Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ENH: Add ants.ANTsImage back to namespace #700

Merged
merged 1 commit into from
Aug 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion ants/core/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@
from_numpy,
from_numpy_like,
new_image_like)
from .ants_image import (copy_image_info,
from .ants_image import (ANTsImage,
copy_image_info,
set_origin,
get_origin,
set_direction,
Expand Down
57 changes: 29 additions & 28 deletions ants/core/ants_image.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@


__all__ = ['copy_image_info',
__all__ = ['ANTsImage',
'copy_image_info',
'set_origin',
'get_origin',
'set_direction',
Expand Down Expand Up @@ -40,7 +41,7 @@ class ANTsImage(object):
def __init__(self, pointer):
"""
Initialize an ANTsImage.

Creating an ANTsImage requires a pointer to an underlying ITK image that
is stored via a nanobind class wrapping a AntsImage struct.

Expand All @@ -57,31 +58,31 @@ def __init__(self, pointer):
@property
def _libsuffix(self):
return str(type(self.pointer)).split('AntsImage')[-1].split("'")[0]

@property
def shape(self):
return tuple(get_lib_fn('getShape')(self.pointer))

@property
def physical_shape(self):
return tuple([round(sh*sp,3) for sh,sp in zip(self.shape, self.spacing)])

@property
def is_rgb(self):
return 'RGB' in self._libsuffix

@property
def has_components(self):
suffix = self._libsuffix
return suffix.startswith('V') or suffix.startswith('RGB')

@property
def components(self):
if not self.has_components:
return 1

return get_lib_fn('getComponents')(self.pointer)

@property
def pixeltype(self):
ptype = self._libsuffix[:-1]
Expand All @@ -90,17 +91,17 @@ def pixeltype(self):
ptype = ptype[3:]
else:
ptype = ptype[1:]

ptype_map = {'UC': 'unsigned char',
'UI': 'unsigned int',
'F': 'float',
'D': 'double'}
return ptype_map[ptype]

@property
def dtype(self):
return _itk_to_npy_map[self.pixeltype]

@property
def dimension(self):
return int(self._libsuffix[-1])
Expand Down Expand Up @@ -369,9 +370,9 @@ def __add__(self, other):

new_array = this_array + other
return self.new_image_like(new_array)

__radd__ = __add__

def __sub__(self, other):
this_array = self.numpy()

Expand All @@ -382,7 +383,7 @@ def __sub__(self, other):

new_array = this_array - other
return self.new_image_like(new_array)

def __rsub__(self, other):
this_array = self.numpy()

Expand All @@ -393,7 +394,7 @@ def __rsub__(self, other):

new_array = other - this_array
return self.new_image_like(new_array)

def __mul__(self, other):
this_array = self.numpy()

Expand Down Expand Up @@ -500,16 +501,16 @@ def __getitem__(self, idx):
return ants.merge_channels([
img[idx] for img in ants.split_channels(self)
])

if isinstance(idx, ANTsImage):
if not ants.image_physical_space_consistency(self, idx):
raise ValueError('images do not occupy same physical space')
return self.numpy().__getitem__(idx.numpy().astype('bool'))

ndim = len(idx)
sizes = list(self.shape)
starts = [0] * ndim

for i in range(ndim):
ti = idx[i]
if isinstance(ti, slice):
Expand All @@ -519,32 +520,32 @@ def __getitem__(self, idx):
sizes[i] = ti.stop - starts[i]
else:
sizes[i] = self.shape[i] - starts[i]

if ti.stop and ti.start:
if ti.stop < ti.start:
raise Exception('Reverse indexing is not supported.')

elif isinstance(ti, int):
starts[i] = ti
sizes[i] = 0

if sizes[i] == 0:
ndim -= 1

if ndim < 2:
return self.numpy().__getitem__(idx)

libfn = get_lib_fn('getItem%i' % ndim)
new_ptr = libfn(self.pointer, starts, sizes)
new_image = from_pointer(new_ptr)
return new_image

def __setitem__(self, idx, value):
arr = self.view()

if is_image(value):
value = value.numpy()

if is_image(idx):
if not ants.image_physical_space_consistency(self, idx):
raise ValueError('images do not occupy same physical space')
Expand All @@ -565,7 +566,7 @@ def __repr__(self):
'\t {:<10} : {}\n'.format('Origin', tuple([round(o,4) for o in self.origin]))+\
'\t {:<10} : {}\n'.format('Direction', np.round(self.direction.flatten(),4))
return s

def __getstate__(self):
"""
import ants
Expand All @@ -580,7 +581,7 @@ def __getstate__(self):
print(img.mean(), img3.mean())
"""
return self.numpy(), self.origin, self.spacing, self.direction, self.has_components, self.is_rgb

def __setstate__(self, state):
data, origin, spacing, direction, has_components, is_rgb = state
image = ants.from_numpy(np.copy(data), origin=origin, spacing=spacing, direction=direction, has_components=has_components, is_rgb=is_rgb)
Expand Down