Source code for ants.core.ants_image



__all__ = ['ANTsImage',
           'LabelImage',
           'copy_image_info',
           'set_origin',
           'get_origin',
           'set_direction',
           'get_direction',
           'set_spacing',
           'get_spacing',
           'image_physical_space_consistency',
           'image_type_cast',
           'allclose']

import os

import numpy as np
import pandas as pd

try:
    from functools import partialmethod
    HAS_PY3 = True
except:
    HAS_PY3 = False

import inspect

from .. import registration, segmentation, utils, viz
from . import ants_image_io as iio2

_supported_ptypes = {'unsigned char', 'unsigned int', 'float', 'double'}
_supported_dtypes = {'uint8', 'uint32', 'float32', 'float64'}
_itk_to_npy_map = {
    'unsigned char': 'uint8',
    'unsigned int': 'uint32',
    'float': 'float32',
    'double': 'float64'}
_npy_to_itk_map = {
    'uint8': 'unsigned char',
    'uint32':'unsigned int',
    'float32': 'float',
    'float64': 'double'}


[docs]class ANTsImage(object): def __init__(self, pixeltype='float', dimension=3, components=1, pointer=None, is_rgb=False, label_image=None): """ Initialize an ANTsImage Arguments --------- pixeltype : string ITK pixeltype of image dimension : integer number of image dimension. Does NOT include components dimension components : integer number of pixel components in the image pointer : py::capsule (optional) pybind11 capsule holding the pointer to the underlying ITK image object label_image : LabelImage a discrete label image for mapping locations to atlas regions """ ## Attributes which cant change without creating a new ANTsImage object self.pointer = pointer self.pixeltype = pixeltype self.dimension = dimension self.components = components self.has_components = self.components > 1 self.dtype = _itk_to_npy_map[self.pixeltype] self.is_rgb = is_rgb self._pixelclass = 'vector' if self.has_components else 'scalar' self._shortpclass = 'V' if self._pixelclass == 'vector' else '' if is_rgb: self._pixelclass = 'rgb' self._shortpclass = 'RGB' self._libsuffix = '%s%s%i' % (self._shortpclass, utils.short_ptype(self.pixeltype), self.dimension) self.shape = utils.get_lib_fn('getShape%s'%self._libsuffix)(self.pointer) self.physical_shape = tuple([round(sh*sp,3) for sh,sp in zip(self.shape, self.spacing)]) if label_image is not None: if not isinstance(label_image, LabelImage): raise ValueError('label_image argument must be a LabelImage type') self.label_image = label_image self._array = None @property def spacing(self): """ Get image spacing Returns ------- tuple """ libfn = utils.get_lib_fn('getSpacing%s'%self._libsuffix) return libfn(self.pointer)
[docs] def set_spacing(self, new_spacing): """ Set image spacing Arguments --------- new_spacing : tuple or list updated spacing for the image. should have one value for each dimension Returns ------- None """ if not isinstance(new_spacing, (tuple, list)): raise ValueError('arg must be tuple or list') if len(new_spacing) != self.dimension: raise ValueError('must give a spacing value for each dimension (%i)' % self.dimension) libfn = utils.get_lib_fn('setSpacing%s'%self._libsuffix) libfn(self.pointer, new_spacing)
@property def origin(self): """ Get image origin Returns ------- tuple """ libfn = utils.get_lib_fn('getOrigin%s'%self._libsuffix) return libfn(self.pointer)
[docs] def set_origin(self, new_origin): """ Set image origin Arguments --------- new_origin : tuple or list updated origin for the image. should have one value for each dimension Returns ------- None """ if not isinstance(new_origin, (tuple, list)): raise ValueError('arg must be tuple or list') if len(new_origin) != self.dimension: raise ValueError('must give a origin value for each dimension (%i)' % self.dimension) libfn = utils.get_lib_fn('setOrigin%s'%self._libsuffix) libfn(self.pointer, new_origin)
@property def direction(self): """ Get image direction Returns ------- tuple """ libfn = utils.get_lib_fn('getDirection%s'%self._libsuffix) return libfn(self.pointer)
[docs] def set_direction(self, new_direction): """ Set image direction Arguments --------- new_direction : numpy.ndarray or tuple or list updated direction for the image. should have one value for each dimension Returns ------- None """ if isinstance(new_direction, (tuple,list)): new_direction = np.asarray(new_direction) if not isinstance(new_direction, np.ndarray): raise ValueError('arg must be np.ndarray or tuple or list') if len(new_direction) != self.dimension: raise ValueError('must give a origin value for each dimension (%i)' % self.dimension) libfn = utils.get_lib_fn('setDirection%s'%self._libsuffix) libfn(self.pointer, new_direction)
@property def orientation(self): if self.dimension == 3: return self.get_orientation() else: return None
[docs] def view(self, single_components=False): """ Geet a numpy array providing direct, shared access to the image data. IMPORTANT: If you alter the view, then the underlying image data will also be altered. Arguments --------- single_components : boolean (default is False) if True, keep the extra component dimension in returned array even if image only has one component (i.e. self.has_components == False) Returns ------- ndarray """ if self.is_rgb: img = self.rgb_to_vector() else: img = self dtype = img.dtype shape = img.shape[::-1] if img.has_components or (single_components == True): shape = list(shape) + [img.components] libfn = utils.get_lib_fn('toNumpy%s'%img._libsuffix) memview = libfn(img.pointer) return np.asarray(memview).view(dtype = dtype).reshape(shape).view(np.ndarray).T
[docs] def numpy(self, single_components=False): """ Get a numpy array copy representing the underlying image data. Altering this ndarray will have NO effect on the underlying image data. Arguments --------- single_components : boolean (default is False) if True, keep the extra component dimension in returned array even if image only has one component (i.e. self.has_components == False) Returns ------- ndarray """ array = np.array(self.view(single_components=single_components), copy=True, dtype=self.dtype) if self.has_components or (single_components == True): array = np.rollaxis(array, 0, self.dimension+1) return array
[docs] def clone(self, pixeltype=None): """ Create a copy of the given ANTsImage with the same data and info, possibly with a different data type for the image data. Only supports casting to uint8 (unsigned char), uint32 (unsigned int), float32 (float), and float64 (double) Arguments --------- dtype: string (optional) if None, the dtype will be the same as the cloned ANTsImage. Otherwise, the data will be cast to this type. This can be a numpy type or an ITK type. Options: 'unsigned char' or 'uint8', 'unsigned int' or 'uint32', 'float' or 'float32', 'double' or 'float64' Returns ------- ANTsImage """ if pixeltype is None: pixeltype = self.pixeltype if pixeltype not in _supported_ptypes: raise ValueError('Pixeltype %s not supported. Supported types are %s' % (pixeltype, _supported_ptypes)) if self.has_components and (not self.is_rgb): comp_imgs = utils.split_channels(self) comp_imgs_cloned = [comp_img.clone(pixeltype) for comp_img in comp_imgs] return utils.merge_channels(comp_imgs_cloned) else: p1_short = utils.short_ptype(self.pixeltype) p2_short = utils.short_ptype(pixeltype) ndim = self.dimension fn_suffix = '%s%i%s%i' % (p1_short,ndim,p2_short,ndim) libfn = utils.get_lib_fn('antsImageClone%s'%fn_suffix) pointer_cloned = libfn(self.pointer) return ANTsImage(pixeltype=pixeltype, dimension=self.dimension, components=self.components, is_rgb=self.is_rgb, pointer=pointer_cloned)
# pythonic alias for `clone` is `copy` copy = clone
[docs] def astype(self, dtype): """ Cast & clone an ANTsImage to a given numpy datatype. Map: uint8 : unsigned char uint32 : unsigned int float32 : float float64 : double """ if dtype not in _supported_dtypes: raise ValueError('Datatype %s not supported. Supported types are %s' % (dtype, _supported_dtypes)) pixeltype = _npy_to_itk_map[dtype] return self.clone(pixeltype)
[docs] def new_image_like(self, data): """ Create a new ANTsImage with the same header information, but with a new image array. Arguments --------- data : ndarray or py::capsule New array or pointer for the image. It must have the same shape as the current image data. Returns ------- ANTsImage """ if not isinstance(data, np.ndarray): raise ValueError('data must be a numpy array') if not self.has_components: if data.shape != self.shape: raise ValueError('given array shape (%s) and image array shape (%s) do not match' % (data.shape, self.shape)) else: if (data.shape[-1] != self.components) or (data.shape[:-1] != self.shape): raise ValueError('given array shape (%s) and image array shape (%s) do not match' % (data.shape[1:], self.shape)) return iio2.from_numpy(data, origin=self.origin, spacing=self.spacing, direction=self.direction, has_components=self.has_components)
[docs] def to_file(self, filename): """ Write the ANTsImage to file Args ---- filename : string filepath to which the image will be written """ filename = os.path.expanduser(filename) libfn = utils.get_lib_fn('toFile%s'%self._libsuffix) libfn(self.pointer, filename)
to_filename = to_file
[docs] def apply(self, fn): """ Apply an arbitrary function to ANTsImage. Args ---- fn : python function or lambda function to apply to ENTIRE image at once Returns ------- ANTsImage image with function applied to it """ this_array = self.numpy() new_array = fn(this_array) return self.new_image_like(new_array)
[docs] def as_label_image(self, label_info=None): return LabelImage(image=self, label_info=label_info)
## NUMPY FUNCTIONS ##
[docs] def abs(self, axis=None): """ Return absolute value of image """ return np.abs(self.numpy())
[docs] def mean(self, axis=None): """ Return mean along specified axis """ return self.numpy().mean(axis=axis)
[docs] def median(self, axis=None): """ Return median along specified axis """ return np.median(self.numpy(), axis=axis)
[docs] def std(self, axis=None): """ Return std along specified axis """ return self.numpy().std(axis=axis)
[docs] def sum(self, axis=None, keepdims=False): """ Return sum along specified axis """ return self.numpy().sum(axis=axis, keepdims=keepdims)
[docs] def min(self, axis=None): """ Return min along specified axis """ return self.numpy().min(axis=axis)
[docs] def max(self, axis=None): """ Return max along specified axis """ return self.numpy().max(axis=axis)
[docs] def range(self, axis=None): """ Return range tuple along specified axis """ return (self.min(axis=axis), self.max(axis=axis))
[docs] def argmin(self, axis=None): """ Return argmin along specified axis """ return self.numpy().argmin(axis=axis)
[docs] def argmax(self, axis=None): """ Return argmax along specified axis """ return self.numpy().argmax(axis=axis)
[docs] def argrange(self, axis=None): """ Return argrange along specified axis """ amin = self.argmin(axis=axis) amax = self.argmax(axis=axis) if axis is None: return (amin, amax) else: return np.stack([amin, amax]).T
[docs] def flatten(self): """ Flatten image data """ return self.numpy().flatten()
[docs] def nonzero(self): """ Return non-zero indices of image """ return self.numpy().nonzero()
[docs] def unique(self, sort=False): """ Return unique set of values in image """ unique_vals = np.unique(self.numpy()) if sort: unique_vals = np.sort(unique_vals) return unique_vals
## OVERLOADED OPERATORS ## def __add__(self, other): this_array = self.numpy() if isinstance(other, ANTsImage): if not image_physical_space_consistency(self, other): raise ValueError('images do not occupy same physical space') other = other.numpy() new_array = this_array + other return self.new_image_like(new_array) __radd__ = __add__ def __sub__(self, other): this_array = self.numpy() if isinstance(other, ANTsImage): if not image_physical_space_consistency(self, other): raise ValueError('images do not occupy same physical space') other = other.numpy() new_array = this_array - other return self.new_image_like(new_array) def __rsub__(self, other): this_array = self.numpy() if isinstance(other, ANTsImage): if not image_physical_space_consistency(self, other): raise ValueError('images do not occupy same physical space') other = other.numpy() new_array = other - this_array return self.new_image_like(new_array) def __mul__(self, other): this_array = self.numpy() if isinstance(other, ANTsImage): if not image_physical_space_consistency(self, other): raise ValueError('images do not occupy same physical space') other = other.numpy() new_array = this_array * other return self.new_image_like(new_array) __rmul__ = __mul__ def __truediv__(self, other): this_array = self.numpy() if isinstance(other, ANTsImage): if not image_physical_space_consistency(self, other): raise ValueError('images do not occupy same physical space') other = other.numpy() new_array = this_array / other return self.new_image_like(new_array) def __pow__(self, other): this_array = self.numpy() if isinstance(other, ANTsImage): if not image_physical_space_consistency(self, other): raise ValueError('images do not occupy same physical space') other = other.numpy() new_array = this_array ** other return self.new_image_like(new_array) def __gt__(self, other): this_array = self.numpy() if isinstance(other, ANTsImage): if not image_physical_space_consistency(self, other): raise ValueError('images do not occupy same physical space') other = other.numpy() new_array = this_array > other return self.new_image_like(new_array.astype('uint8')) def __ge__(self, other): this_array = self.numpy() if isinstance(other, ANTsImage): if not image_physical_space_consistency(self, other): raise ValueError('images do not occupy same physical space') other = other.numpy() new_array = this_array >= other return self.new_image_like(new_array.astype('uint8')) def __lt__(self, other): this_array = self.numpy() if isinstance(other, ANTsImage): if not image_physical_space_consistency(self, other): raise ValueError('images do not occupy same physical space') other = other.numpy() new_array = this_array < other return self.new_image_like(new_array.astype('uint8')) def __le__(self, other): this_array = self.numpy() if isinstance(other, ANTsImage): if not image_physical_space_consistency(self, other): raise ValueError('images do not occupy same physical space') other = other.numpy() new_array = this_array <= other return self.new_image_like(new_array.astype('uint8')) def __eq__(self, other): this_array = self.numpy() if isinstance(other, ANTsImage): if not image_physical_space_consistency(self, other): raise ValueError('images do not occupy same physical space') other = other.numpy() new_array = this_array == other return self.new_image_like(new_array.astype('uint8')) def __ne__(self, other): this_array = self.numpy() if isinstance(other, ANTsImage): if not image_physical_space_consistency(self, other): raise ValueError('images do not occupy same physical space') other = other.numpy() new_array = this_array != other return self.new_image_like(new_array.astype('uint8')) def __getitem__(self, idx): if self._array is None: self._array = self.numpy() if isinstance(idx, ANTsImage): if not image_physical_space_consistency(self, idx): raise ValueError('images do not occupy same physical space') return self._array.__getitem__(idx.numpy().astype('bool')) else: return self._array.__getitem__(idx) def __setitem__(self, idx, value): arr = self.view() if isinstance(idx, ANTsImage): if not image_physical_space_consistency(self, idx): raise ValueError('images do not occupy same physical space') arr.__setitem__(idx.numpy().astype('bool'), value) else: arr.__setitem__(idx, value) def __repr__(self): if self.dimension == 3: s = 'ANTsImage ({})\n'.format(self.orientation) else: s = 'ANTsImage\n' s = s +\ '\t {:<10} : {} ({})\n'.format('Pixel Type', self.pixeltype, self.dtype)+\ '\t {:<10} : {}{}\n'.format('Components', self.components, ' (RGB)' if 'RGB' in self._libsuffix else '')+\ '\t {:<10} : {}\n'.format('Dimensions', self.shape)+\ '\t {:<10} : {}\n'.format('Spacing', tuple([round(s,4) for s in self.spacing]))+\ '\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
if HAS_PY3: # Set partial class methods for any functions which take an ANTsImage as the first argument for k, v in utils.__dict__.items(): if callable(v): args = inspect.getfullargspec(getattr(utils,k)).args if (len(args) > 0) and (args[0] in {'img','image','cropped_image'}): setattr(ANTsImage, k, partialmethod(v)) for k, v in registration.__dict__.items(): if callable(v): args = inspect.getfullargspec(getattr(registration,k)).args if (len(args) > 0) and (args[0] in {'img','image'}): setattr(ANTsImage, k, partialmethod(v)) for k, v in segmentation.__dict__.items(): if callable(v): args = inspect.getfullargspec(getattr(segmentation,k)).args if (len(args) > 0) and (args[0] in {'img','image'}): setattr(ANTsImage, k, partialmethod(v)) for k, v in viz.__dict__.items(): if callable(v): args = inspect.getfullargspec(getattr(viz,k)).args if (len(args) > 0) and (args[0] in {'img','image'}): setattr(ANTsImage, k, partialmethod(v)) class Dictlist(dict): def __setitem__(self, key, value): try: self[key] except KeyError: super(Dictlist, self).__setitem__(key, []) self[key].append(value)
[docs]class LabelImage(ANTsImage): """ A LabelImage is a special class of ANTsImage which has discrete values and string labels or other metadata (e.g. another string label such as the "lobe" of the region) associated with each of the discrete values. A canonical example of a LabelImage is a brain label_image or parcellation. This class provides convenient functionality for manipulating and visualizing images where you have real values associated with aggregated image regions (e.g. if you have cortical thickness values associated with brain regions) Commonly-used functionality for LabelImage types: - create publication-quality figures of an label_image Nomenclature ------------ - key : a string representing the name of the associated index in the atlas image - e.g. if the index is 1001 and the key may be InferiorTemporalGyrus` - value : an integer value in the atlas image - metakey : a string representing one of the possible sets of label key - e.g. 'Lobes' or 'Regions' Notes ----- - indexing works by creating a separate dict for each metakey, where """ def __init__(self, label_image, label_info=None, template=None): """ Initialize a LabelImage ANTsR function: N/A Arguments --------- label_image : ANTsImage discrete (integer) image as label_image label_info : dict or pandas.DataFrame mapping between discrete values in `image` and string names - if dict, the keys should be the discrete integer label values and the values should be the label names or another dict with any metadata - if pd.DataFrame, the index (df.index) should be the discrete integer label values and the other column(s) should be the label names and any metadata template : ANTsImage default real-valued image to use for plotting or creating new images. This image should be in the same space as the `label_image` image and the two should be aligned. Example ------- >>> import ants >>> square = np.zeros((20,20)) >>> square[:10,:10] = 0 >>> square[:10,10:] = 1 >>> square[10:,:10] = 2 >>> square[10:,10:] = 3 >>> img = ants.from_numpy(square).astype('uint8') >>> label_image = ants.LabelImage(label_image=img, label_info=label_dict) """ if label_image.pixeltype not in {'unsigned char', 'unsigned int'}: raise ValueError('Label images must have discrete pixeltype - got %s' % label_image.pixeltype) if label_image.components > 1: raise ValueError('Label images must have only one component - got %i' % label_image.components) if label_info is None: label_info = {k:'Label%i'%k for k in range(len(label_image.unique()))} if isinstance(label_info, pd.DataFrame): pass elif isinstance(label_info, dict): if isinstance(label_info[list(label_info.keys())[0]], dict): label_info = pd.DataFrame(label_info).T.to_dict() else: label_info = pd.DataFrame(label_info, index=np.arange(len(label_info))).T.to_dict() else: raise ValueError('label_label_info argument must be pd.DataFrame') self.label_info = label_info self.label_image = label_image self.template = template self.generate_data() super(LabelImage, self).__init__(pixeltype=label_image.pixeltype, dimension=label_image.dimension, components=label_image.components, pointer=label_image.pointer)
[docs] def generate_data(self): self._metakeys = list(self.label_info.columns) self._uniquekeys = {mk:list(np.unique(self.label_info[mk])) for mk in self._metakeys} self._keys = {mk:list(self.label_info[mk]) for mk in self._metakeys} self._values = list(self.label_info.index) self._n_values = len(self._values) items = {} for mk in self._metakeys: items[mk] = {} for k, v in zip(self.keys(mk), self.values()): if k in items[mk]: if isinstance(items[mk][k], list): items[mk][k].append(v) else: items[mk][k] = [items[mk][k]] + [v] else: items[mk][k] = v self._items = items
[docs] def uniquekeys(self, metakey=None): """ Get keys for a given metakey """ if metakey is None: return self._uniquekeys else: if metakey not in self.metakeys(): raise ValueError('metakey %s does not exist' % metakey) return self._uniquekeys[metakey]
[docs] def keys(self, metakey=None): if metakey is None: return self._keys else: if metakey not in self.metakeys(): raise ValueError('metakey %s does not exist' % metakey) return self._keys[metakey]
[docs] def metakeys(self): return self._metakeys
[docs] def parentkey(self, key): parent = None for mk in self.metakeys(): if key in self.keys(mk): parent = mk if parent is None: raise ValueError('key does not have a parent') return parent
[docs] def values(self): return self._values
[docs] def items(self, metakey): if metakey not in self.metakeys(): raise ValueError('metakey %s does not exist' % metakey) return self._items[metakey]
[docs] def n_values(self): return self._n_values
def __getitem__(self, key): # get metakey of key metakey = self.parentkey(key) # get key,value pairs for metakey items = self.items(metakey) # return value at the given key return items[key] def __setitem__(self, key, value): label_value = self.__getitem__(key) if isinstance(label_value, list): if isinstance(value, list): if len(value) != len(label_value): raise ValueError('must give either single value or one value '+\ 'for each index (got %i, expected %i)' % (len(value), len(label_value))) for old_val, new_val in zip(label_value, value): self.label_image[self.label_image==old_val] = new_val else: for lv in label_value: self.label_image[self.label_image==lv] = value else: self.label_image[self.label_image==label_value] = value def __repr__(self): s = 'LabelImage\n' +\ '\t {:<10} : {} ({})\n'.format('Pixel Type', self.pixeltype, self.dtype)+\ '\t {:<10} : {}\n'.format('Components', self.components)+\ '\t {:<10} : {}\n'.format('Dimensions', self.shape)+\ '\t {:<10} : {}\n'.format('Spacing', self.spacing)+\ '\t {:<10} : {}\n'.format('Origin', self.origin)+\ '\t {:<10} : {}\n'.format('Direction', self.direction.flatten())+\ '\t {:<10} : {}\n'.format('Num Values', self.n_values()) return s
[docs]def copy_image_info(reference, target): """ Copy origin, direction, and spacing from one antsImage to another ANTsR function: `antsCopyImageInfo` Arguments --------- reference : ANTsImage Image to get values from. target : ANTsImAGE Image to copy values to Returns ------- ANTsImage Target image with reference header information """ target.set_origin(reference.origin) target.set_direction(reference.direction) target.set_spacing(reference.spacing) return target
[docs]def set_origin(image, origin): """ Set origin of ANTsImage ANTsR function: `antsSetOrigin` """ image.set_origin(origin)
[docs]def get_origin(image): """ Get origin of ANTsImage ANTsR function: `antsGetOrigin` """ return image.origin
[docs]def set_direction(image, direction): """ Set direction of ANTsImage ANTsR function: `antsSetDirection` """ image.set_direction(direction)
[docs]def get_direction(image): """ Get direction of ANTsImage ANTsR function: `antsGetDirection` """ return image.direction
[docs]def set_spacing(image, spacing): """ Set spacing of ANTsImage ANTsR function: `antsSetSpacing` """ image.set_spacing(spacing)
[docs]def get_spacing(image): """ Get spacing of ANTsImage ANTsR function: `antsGetSpacing` """ return image.spacing
[docs]def image_physical_space_consistency(image1, image2, tolerance=1e-2, datatype=False): """ Check if two or more ANTsImage objects occupy the same physical space ANTsR function: `antsImagePhysicalSpaceConsistency` Arguments --------- *images : ANTsImages images to compare tolerance : float tolerance when checking origin and spacing data_type : boolean If true, also check that the image data types are the same Returns ------- boolean true if images share same physical space, false otherwise """ images = [image1, image2] img1 = images[0] for img2 in images[1:]: if (not isinstance(img1, ANTsImage)) or (not isinstance(img2, ANTsImage)): raise ValueError('Both images must be of class `AntsImage`') # image dimension check if img1.dimension != img2.dimension: return False # image spacing check space_diffs = sum([abs(s1-s2)>tolerance for s1, s2 in zip(img1.spacing, img2.spacing)]) if space_diffs > 0: return False # image origin check origin_diffs = sum([abs(s1-s2)>tolerance for s1, s2 in zip(img1.origin, img2.origin)]) if origin_diffs > 0: return False # image direction check origin_diff = np.allclose(img1.direction, img2.direction, atol=tolerance) if not origin_diff: return False # data type if datatype == True: if img1.pixeltype != img2.pixeltype: return False if img1.components != img2.components: return False return True
[docs]def image_type_cast(image_list, pixeltype=None): """ Cast a list of images to the highest pixeltype present in the list or all to a specified type ANTsR function: `antsImageTypeCast` Arguments --------- image_list : list/tuple images to cast pixeltype : string (optional) pixeltype to cast to. If None, images will be cast to the highest precision pixeltype found in image_list Returns ------- list of ANTsImages given images casted to new type """ if not isinstance(image_list, (list,tuple)): raise ValueError('image_list must be list of ANTsImage types') pixtypes = [] for img in image_list: pixtypes.append(img.pixeltype) if pixeltype is None: pixeltype = 'unsigned char' for p in pixtypes: if p == 'double': pixeltype = 'double' elif (p=='float') and (pixeltype!='double'): pixeltype = 'float' elif (p=='unsigned int') and (pixeltype!='float') and (pixeltype!='double'): pixeltype = 'unsigned int' out_images = [] for img in image_list: if img.pixeltype == pixeltype: out_images.append(img) else: out_images.append(img.clone(pixeltype)) return out_images
[docs]def allclose(image1, image2): """ Check if two images have the same array values """ return np.allclose(image1.numpy(), image2.numpy())