diff --git a/ants/__init__.py b/ants/__init__.py index 9ecccac6..3458583c 100644 --- a/ants/__init__.py +++ b/ants/__init__.py @@ -6,6 +6,4 @@ from .segmentation import * from .registration import * from .learn import * -from .viz import * - -from . import contrib \ No newline at end of file +from .viz import * \ No newline at end of file diff --git a/ants/contrib/__init__.py b/ants/contrib/__init__.py deleted file mode 100644 index 48635c4d..00000000 --- a/ants/contrib/__init__.py +++ /dev/null @@ -1,4 +0,0 @@ - -from .sampling import * -from .sklearn_interface import * - diff --git a/ants/contrib/sampling/__init__.py b/ants/contrib/sampling/__init__.py deleted file mode 100644 index b5083171..00000000 --- a/ants/contrib/sampling/__init__.py +++ /dev/null @@ -1,4 +0,0 @@ - -from .transforms import * -from .affine2d import * -from .affine3d import * diff --git a/ants/contrib/sampling/affine2d.py b/ants/contrib/sampling/affine2d.py deleted file mode 100644 index 25dae0b8..00000000 --- a/ants/contrib/sampling/affine2d.py +++ /dev/null @@ -1,654 +0,0 @@ -""" -Affine transforms - -See http://www.cs.cornell.edu/courses/cs4620/2010fa/lectures/03transforms3D.pdf -""" - -__all__ = [ - "Zoom2D", - "RandomZoom2D", - "Rotate2D", - "RandomRotate2D", - "Shear2D", - "RandomShear2D", - "Translate2D", - "RandomTranslate2D", -] - -import random -import math -import numpy as np - -from ...core import ants_transform as tio - - -class Translate2D(object): - """ - Create an ANTs Affine Transform with a specified translation. - """ - - def __init__(self, translation, reference=None, lazy=False): - """ - Initialize a Translate2D object - - Arguments - --------- - translation : list or tuple - translation values for each axis, in degrees. - Negative values can be used for translation in the - other direction - - reference : ANTsImage (optional but recommended) - image providing the reference space for the transform. - this will also set the transform fixed parameters. - - lazy : boolean (default = False) - if True, calling the `transform` method only returns - the randomly generated transform and does not actually - transform the image - """ - if (not isinstance(translation, (list, tuple))) or (len(translation) != 2): - raise ValueError("translation argument must be list/tuple with two values!") - - self.translation = translation - self.lazy = lazy - self.reference = reference - - self.tx = tio.ANTsTransform( - precision="float", dimension=2, transform_type="AffineTransform" - ) - if self.reference is not None: - self.tx.set_fixed_parameters(self.reference.get_center_of_mass()) - - def transform(self, X=None, y=None): - """ - Transform an image using an Affine transform with the given - translation parameters. Return the transform if X=None. - - Arguments - --------- - X : ANTsImage - Image to transform - - y : ANTsImage (optional) - Another image to transform - - Returns - ------- - ANTsImage if y is None, else a tuple of ANTsImage types - - Examples - -------- - >>> import ants - >>> img = ants.image_read(ants.get_data('r16')) - >>> tx = ants.contrib.Translate2D(translation=(10,0)) - >>> img2_x = tx.transform(img) - >>> tx = ants.contrib.Translate2D(translation=(-10,0)) # other direction - >>> img2_x = tx.transform(img) - >>> tx = ants.contrib.Translate2D(translation=(0,10)) - >>> img2_z = tx.transform(img) - >>> tx = ants.contrib.Translate2D(translation=(10,10)) - >>> img2 = tx.transform(img) - """ - # convert to radians and unpack - translation_x, translation_y = self.translation - - translation_matrix = np.array([[1, 0, translation_x], [0, 1, translation_y]]) - self.tx.set_parameters(translation_matrix) - if self.lazy or X is None: - return self.tx - else: - if y is None: - return self.tx.apply_to_image(X, reference=self.reference) - else: - return ( - self.tx.apply_to_image(X, reference=self.reference), - self.tx.apply_to_image(y, reference=self.reference), - ) - - -class RandomTranslate2D(object): - """ - Apply a Translate2D transform to an image, but with the - parameters randomly generated from a user-specified range. - The range is determined by a mean (first parameter) and standard deviation - (second parameter) via calls to random.gauss. - """ - - def __init__(self, translation_range, reference=None, lazy=False): - """ - Initialize a RandomTranslate2D object - - Arguments - --------- - translation_range : list or tuple - Lower and Upper bounds on rotation parameter, in degrees. - e.g. translation_range = (-10,10) will result in a random - draw of the rotation parameters between -10 and 10 degrees - - reference : ANTsImage (optional but recommended) - image providing the reference space for the transform. - this will also set the transform fixed parameters. - - lazy : boolean (default = False) - if True, calling the `transform` method only returns - the randomly generated transform and does not actually - transform the image - """ - if (not isinstance(translation_range, (list, tuple))) or ( - len(translation_range) != 2 - ): - raise ValueError("shear_range argument must be list/tuple with two values!") - - self.translation_range = translation_range - self.reference = reference - self.lazy = lazy - - def transform(self, X=None, y=None): - """ - Transform an image using an Affine transform with - translation parameters randomly generated from the user-specified - range. Return the transform if X=None. - - Arguments - --------- - X : ANTsImage - Image to transform - - y : ANTsImage (optional) - Another image to transform - - Returns - ------- - ANTsImage if y is None, else a tuple of ANTsImage types - - Examples - -------- - >>> import ants - >>> img = ants.image_read(ants.get_data('r16')) - >>> tx = ants.contrib.RandomShear2D(translation_range=(-10,10)) - >>> img2 = tx.transform(img) - """ - # random draw in translation range - translation_x = random.gauss( - self.translation_range[0], self.translation_range[1] - ) - translation_y = random.gauss( - self.translation_range[0], self.translation_range[1] - ) - self.params = (translation_x, translation_y) - - tx = Translate2D( - (translation_x, translation_y), reference=self.reference, lazy=self.lazy - ) - - return tx.transform(X, y) - - -class Shear2D(object): - """ - Create an ANTs Affine Transform with a specified shear. - """ - - def __init__(self, shear, reference=None, lazy=False): - """ - Initialize a Shear2D object - - Arguments - --------- - shear : list or tuple - shear values for each axis, in degrees. - Negative values can be used for shear in the - other direction - - reference : ANTsImage (optional but recommended) - image providing the reference space for the transform. - this will also set the transform fixed parameters. - - lazy : boolean (default = False) - if True, calling the `transform` method only returns - the randomly generated transform and does not actually - transform the image - """ - if (not isinstance(shear, (list, tuple))) or (len(shear) != 2): - raise ValueError("shear argument must be list/tuple with two values!") - - self.shear = shear - self.lazy = lazy - self.reference = reference - - self.tx = tio.ANTsTransform( - precision="float", dimension=2, transform_type="AffineTransform" - ) - if self.reference is not None: - self.tx.set_fixed_parameters(self.reference.get_center_of_mass()) - - def transform(self, X=None, y=None): - """ - Transform an image using an Affine transform with the given - shear parameters. Return the transform if X=None. - - Arguments - --------- - X : ANTsImage - Image to transform - - y : ANTsImage (optional) - Another image to transform - - Returns - ------- - ANTsImage if y is None, else a tuple of ANTsImage types - - Examples - -------- - >>> import ants - >>> img = ants.image_read(ants.get_data('r16')) - >>> tx = ants.contrib.Shear2D(shear=(10,0,0)) - >>> img2_x = tx.transform(img)# x axis stays same - >>> tx = ants.contrib.Shear2D(shear=(-10,0,0)) # other direction - >>> img2_x = tx.transform(img)# x axis stays same - >>> tx = ants.contrib.Shear2D(shear=(0,10,0)) - >>> img2_y = tx.transform(img) # y axis stays same - >>> tx = ants.contrib.Shear2D(shear=(0,0,10)) - >>> img2_z = tx.transform(img) # z axis stays same - >>> tx = ants.contrib.Shear2D(shear=(10,10,10)) - >>> img2 = tx.transform(img) - """ - # convert to radians and unpack - shear = [math.pi / 180 * s for s in self.shear] - shear_x, shear_y = shear - - shear_matrix = np.array([[1, shear_x, 0], [shear_y, 1, 0]]) - self.tx.set_parameters(shear_matrix) - if self.lazy or X is None: - return self.tx - else: - if y is None: - return self.tx.apply_to_image(X, reference=self.reference) - else: - return ( - self.tx.apply_to_image(X, reference=self.reference), - self.tx.apply_to_image(y, reference=self.reference), - ) - - -class RandomShear2D(object): - """ - Apply a Shear2D transform to an image, but with the shear - parameters randomly generated from a user-specified range. - The range is determined by a mean (first parameter) and standard deviation - (second parameter) via calls to random.gauss. - """ - - def __init__(self, shear_range, reference=None, lazy=False): - """ - Initialize a RandomShear2D object - - Arguments - --------- - shear_range : list or tuple - Lower and Upper bounds on rotation parameter, in degrees. - e.g. shear_range = (-10,10) will result in a random - draw of the rotation parameters between -10 and 10 degrees - - reference : ANTsImage (optional but recommended) - image providing the reference space for the transform. - this will also set the transform fixed parameters. - - lazy : boolean (default = False) - if True, calling the `transform` method only returns - the randomly generated transform and does not actually - transform the image - """ - if (not isinstance(shear_range, (list, tuple))) or (len(shear_range) != 2): - raise ValueError("shear_range argument must be list/tuple with two values!") - - self.shear_range = shear_range - self.reference = reference - self.lazy = lazy - - def transform(self, X=None, y=None): - """ - Transform an image using an Affine transform with - shear parameters randomly generated from the user-specified - range. Return the transform if X=None. - - Arguments - --------- - X : ANTsImage - Image to transform - - y : ANTsImage (optional) - Another image to transform - - Returns - ------- - ANTsImage if y is None, else a tuple of ANTsImage types - - Examples - -------- - >>> import ants - >>> img = ants.image_read(ants.get_data('r16')) - >>> tx = ants.contrib.RandomShear2D(shear_range=(-10,10)) - >>> img2 = tx.transform(img) - """ - # random draw in shear range - shear_x = random.gauss(self.shear_range[0], self.shear_range[1]) - shear_y = random.gauss(self.shear_range[0], self.shear_range[1]) - self.params = (shear_x, shear_y) - - tx = Shear2D((shear_x, shear_y), reference=self.reference, lazy=self.lazy) - - return tx.transform(X, y) - - -class Rotate2D(object): - """ - Create an ANTs Affine Transform with a specified level - of rotation. - """ - - def __init__(self, rotation, reference=None, lazy=False): - """ - Initialize a Rotate2D object - - Arguments - --------- - rotation : scalar - rotation value in degrees. - Negative values can be used for rotation in the - other direction - - reference : ANTsImage (optional but recommended) - image providing the reference space for the transform. - this will also set the transform fixed parameters. - - lazy : boolean (default = False) - if True, calling the `transform` method only returns - the randomly generated transform and does not actually - transform the image - """ - self.rotation = rotation - self.lazy = lazy - self.reference = reference - - self.tx = tio.ANTsTransform( - precision="float", dimension=2, transform_type="AffineTransform" - ) - if self.reference is not None: - self.tx.set_fixed_parameters(self.reference.get_center_of_mass()) - - def transform(self, X=None, y=None): - """ - Transform an image using an Affine transform with the given - rotation parameters. Return the transform if X=None. - - Arguments - --------- - X : ANTsImage - Image to transform - - y : ANTsImage (optional) - Another image to transform - - Returns - ------- - ANTsImage if y is None, else a tuple of ANTsImage types - - Examples - -------- - >>> import ants - >>> img = ants.image_read(ants.get_data('r16')) - >>> tx = ants.contrib.Rotate2D(rotation=(10,-5,12)) - >>> img2 = tx.transform(img) - """ - # unpack zoom range - rotation = self.rotation - - # Rotation about X axis - theta = math.pi / 180 * rotation - rotation_matrix = np.array( - [[np.cos(theta), -np.sin(theta), 0], [np.sin(theta), np.cos(theta), 0]] - ) - - self.tx.set_parameters(rotation_matrix) - if self.lazy or X is None: - return self.tx - else: - if y is None: - return self.tx.apply_to_image(X, reference=self.reference) - else: - return ( - self.tx.apply_to_image(X, reference=self.reference), - self.tx.apply_to_image(y, reference=self.reference), - ) - - -class RandomRotate2D(object): - """ - Apply a Rotated2D transform to an image, but with the zoom - parameters randomly generated from a user-specified range. - The range is determined by a mean (first parameter) and standard deviation - (second parameter) via calls to random.gauss. - """ - - def __init__(self, rotation_range, reference=None, lazy=False): - """ - Initialize a RandomRotate2D object - - Arguments - --------- - rotation_range : list or tuple - Lower and Upper bounds on rotation parameter, in degrees. - e.g. rotation_range = (-10,10) will result in a random - draw of the rotation parameters between -10 and 10 degrees - - reference : ANTsImage (optional but recommended) - image providing the reference space for the transform. - this will also set the transform fixed parameters. - - lazy : boolean (default = False) - if True, calling the `transform` method only returns - the randomly generated transform and does not actually - transform the image - """ - if (not isinstance(rotation_range, (list, tuple))) or ( - len(rotation_range) != 2 - ): - raise ValueError( - "rotation_range argument must be list/tuple with two values!" - ) - - self.rotation_range = rotation_range - self.reference = reference - self.lazy = lazy - - def transform(self, X=None, y=None): - """ - Transform an image using an Affine transform with - rotation parameters randomly generated from the user-specified - range. Return the transform if X=None. - - Arguments - --------- - X : ANTsImage - Image to transform - - y : ANTsImage (optional) - Another image to transform - - Returns - ------- - ANTsImage if y is None, else a tuple of ANTsImage types - - Examples - -------- - >>> import ants - >>> img = ants.image_read(ants.get_data('r16')) - >>> tx = ants.contrib.RandomRotate2D(rotation_range=(-10,10)) - >>> img2 = tx.transform(img) - """ - # random draw in rotation range - rotation = random.gauss(self.rotation_range[0], self.rotation_range[1]) - self.params = rotation - - tx = Rotate2D(rotation, reference=self.reference, lazy=self.lazy) - - return tx.transform(X, y) - - -class Zoom2D(object): - """ - Create an ANTs Affine Transform with a specified level - of zoom. Any value greater than 1 implies a "zoom-out" and anything - less than 1 implies a "zoom-in". - """ - - def __init__(self, zoom, reference=None, lazy=False): - """ - Initialize a Zoom2D object - - Arguments - --------- - zoom_range : list or tuple - Lower and Upper bounds on zoom parameter. - e.g. zoom_range = (0.7,0.9) will result in a random - draw of the zoom parameters between 0.7 and 0.9 - - reference : ANTsImage (optional but recommended) - image providing the reference space for the transform. - this will also set the transform fixed parameters. - - lazy : boolean (default = False) - if True, calling the `transform` method only returns - the randomly generated transform and does not actually - transform the image - """ - if (not isinstance(zoom, (list, tuple))) or (len(zoom) != 2): - raise ValueError("zoom_range argument must be list/tuple with two values!") - - self.zoom = zoom - self.lazy = lazy - self.reference = reference - - self.tx = tio.ANTsTransform( - precision="float", dimension=2, transform_type="AffineTransform" - ) - if self.reference is not None: - self.tx.set_fixed_parameters(self.reference.get_center_of_mass()) - - def transform(self, X=None, y=None): - """ - Transform an image using an Affine transform with the given - zoom parameters. Return the transform if X=None. - - Arguments - --------- - X : ANTsImage - Image to transform - - y : ANTsImage (optional) - Another image to transform - - Returns - ------- - ANTsImage if y is None, else a tuple of ANTsImage types - - Examples - -------- - >>> import ants - >>> img = ants.image_read(ants.get_data('r16')) - >>> tx = ants.contrib.Zoom2D(zoom=(0.8,0.8,0.8)) - >>> img2 = tx.transform(img) - """ - # unpack zoom range - zoom_x, zoom_y = self.zoom - - self.params = (zoom_x, zoom_y) - zoom_matrix = np.array([[zoom_x, 0, 0], [0, zoom_y, 0]]) - self.tx.set_parameters(zoom_matrix) - if self.lazy or X is None: - return self.tx - else: - if y is None: - return self.tx.apply_to_image(X, reference=self.reference) - else: - return ( - self.tx.apply_to_image(X, reference=self.reference), - self.tx.apply_to_image(y, reference=self.reference), - ) - - -class RandomZoom2D(object): - """ - Apply a Zoom2D transform to an image, but with the zoom - parameters randomly generated from a user-specified range. - The range is determined by a mean (first parameter) and standard deviation - (second parameter) via calls to random.gauss. - """ - - def __init__(self, zoom_range, reference=None, lazy=False): - """ - Initialize a RandomZoom2D object - - Arguments - --------- - zoom_range : list or tuple - Lower and Upper bounds on zoom parameter. - e.g. zoom_range = (0.7,0.9) will result in a random - draw of the zoom parameters between 0.7 and 0.9 - - reference : ANTsImage (optional but recommended) - image providing the reference space for the transform. - this will also set the transform fixed parameters. - - lazy : boolean (default = False) - if True, calling the `transform` method only returns - the randomly generated transform and does not actually - transform the image - """ - if (not isinstance(zoom_range, (list, tuple))) or (len(zoom_range) != 2): - raise ValueError("zoom_range argument must be list/tuple with two values!") - - self.zoom_range = zoom_range - self.reference = reference - self.lazy = lazy - - def transform(self, X=None, y=None): - """ - Transform an image using an Affine transform with - zoom parameters randomly generated from the user-specified - range. Return the transform if X=None. - - Arguments - --------- - X : ANTsImage - Image to transform - - y : ANTsImage (optional) - Another image to transform - - Returns - ------- - ANTsImage if y is None, else a tuple of ANTsImage types - - Examples - -------- - >>> import ants - >>> img = ants.image_read(ants.get_data('r16')) - >>> tx = ants.contrib.RandomZoom2D(zoom_range=(0.8,0.9)) - >>> img2 = tx.transform(img) - """ - # random draw in zoom range - zoom_x = np.exp( - random.gauss(np.log(self.zoom_range[0]), np.log(self.zoom_range[1])) - ) - zoom_y = np.exp( - random.gauss(np.log(self.zoom_range[0]), np.log(self.zoom_range[1])) - ) - self.params = (zoom_x, zoom_y) - - tx = Zoom2D((zoom_x, zoom_y), reference=self.reference, lazy=self.lazy) - - return tx.transform(X, y) diff --git a/ants/contrib/sampling/affine3d.py b/ants/contrib/sampling/affine3d.py deleted file mode 100644 index 98b93b53..00000000 --- a/ants/contrib/sampling/affine3d.py +++ /dev/null @@ -1,802 +0,0 @@ -""" -Affine transforms - -See http://www.cs.cornell.edu/courses/cs4620/2010fa/lectures/03transforms3d.pdf -""" - -__all__ = [ - "Zoom3D", - "RandomZoom3D", - "Rotate3D", - "RandomRotate3D", - "Shear3D", - "RandomShear3D", - "Translate3D", - "RandomTranslate3D", - "Affine3D", -] - -import random -import math -import numpy as np - -from ...core import ants_transform as tio - -class Affine3D(object): - """ - Create a specified ANTs Affine Transform - """ - - def __init__(self, transformation, reference=None, lazy=False): - """ - Initialize a Affine object - - Arguments - --------- - transformation : array - affine transformation array (3x4) - - reference : ANTsImage (optional but recommended) - image providing the reference space for the transform. - this will also set the transform fixed parameters. - - lazy : boolean (default = False) - if True, calling the `transform` method only returns - the randomly generated transform and does not actually - transform the image - """ - if (not isinstance(transformation, np.ndarray) or transformation.shape != (3,4)): - raise ValueError( - "transformation argument must be 3x4 Numpy array!" - ) - - self.transformation = transformation - self.lazy = lazy - self.reference = reference - - self.tx = tio.ANTsTransform( - precision="float", dimension=3, transform_type="AffineTransform" - ) - if self.reference is not None: - self.tx.set_fixed_parameters(self.reference.get_center_of_mass()) - - def transform(self, X=None, y=None): - """ - Transform an image using an Affine transform with the given - translation parameters. Return the transform if X=None. - - Arguments - --------- - X : ANTsImage - Image to transform - - y : ANTsImage (optional) - Another image to transform - - Returns - ------- - ANTsImage if y is None, else a tuple of ANTsImage types - - Examples - -------- - >>> import ants - >>> img = ants.image_read(ants.get_data('ch2')) - >>> tx = ants.contrib.Affine3D(transformation=np.array([[1, 0, 0, dx], [0, 1, 0, dy],[0, 0, 1, dz]]) - >>> img2_x = tx.transform(img)# image translated by (dx, dy, dz) - """ - # unpack - - transformation_matrix = self.transformation - - - self.tx.set_parameters(transformation_matrix) - if self.lazy or X is None: - return self.tx - else: - if y is None: - return self.tx.apply_to_image(X, reference=self.reference) - else: - return ( - self.tx.apply_to_image(X, reference=self.reference), - self.tx.apply_to_image(y, reference=self.reference), - ) - - -class Translate3D(object): - """ - Create an ANTs Affine Transform with a specified translation. - """ - - def __init__(self, translation, reference=None, lazy=False): - """ - Initialize a Translate3D object - - Arguments - --------- - translation : list or tuple - translation values for each axis, in degrees. - Negative values can be used for translation in the - other direction - - reference : ANTsImage (optional but recommended) - image providing the reference space for the transform. - this will also set the transform fixed parameters. - - lazy : boolean (default = False) - if True, calling the `transform` method only returns - the randomly generated transform and does not actually - transform the image - """ - if (not isinstance(translation, (list, tuple))) or (len(translation) != 3): - raise ValueError( - "translation argument must be list/tuple with three values!" - ) - - self.translation = translation - self.lazy = lazy - self.reference = reference - - self.tx = tio.ANTsTransform( - precision="float", dimension=3, transform_type="AffineTransform" - ) - if self.reference is not None: - self.tx.set_fixed_parameters(self.reference.get_center_of_mass()) - - def transform(self, X=None, y=None): - """ - Transform an image using an Affine transform with the given - translation parameters. Return the transform if X=None. - - Arguments - --------- - X : ANTsImage - Image to transform - - y : ANTsImage (optional) - Another image to transform - - Returns - ------- - ANTsImage if y is None, else a tuple of ANTsImage types - - Examples - -------- - >>> import ants - >>> img = ants.image_read(ants.get_data('ch2')) - >>> tx = ants.contrib.Translate3D(translation=(10,0,0)) - >>> img2_x = tx.transform(img)# x axis stays same - >>> tx = ants.contrib.Translate3D(translation=(-10,0,0)) # other direction - >>> img2_x = tx.transform(img)# x axis stays same - >>> tx = ants.contrib.Translate3D(translation=(0,10,0)) - >>> img2_y = tx.transform(img) # y axis stays same - >>> tx = ants.contrib.Translate3D(translation=(0,0,10)) - >>> img2_z = tx.transform(img) # z axis stays same - >>> tx = ants.contrib.Translate3D(translation=(10,10,10)) - >>> img2 = tx.transform(img) - """ - # unpack - translation_x, translation_y, translation_z = self.translation - - translation_matrix = np.array( - [ - [1, 0, 0, translation_x], - [0, 1, 0, translation_y], - [0, 0, 1, translation_z], - ] - ) - self.tx.set_parameters(translation_matrix) - if self.lazy or X is None: - return self.tx - else: - if y is None: - return self.tx.apply_to_image(X, reference=self.reference) - else: - return ( - self.tx.apply_to_image(X, reference=self.reference), - self.tx.apply_to_image(y, reference=self.reference), - ) - - -class RandomTranslate3D(object): - """ - Apply a Translate3D transform to an image, but with the shear - parameters randomly generated from a user-specified range. - The range is determined by a mean (first parameter) and standard deviation - (second parameter) via calls to random.gauss. - """ - - def __init__(self, translation_range, reference=None, lazy=False): - """ - Initialize a RandomTranslate3D object - - Arguments - --------- - translation_range : list or tuple - Lower and Upper bounds on rotation parameter, in degrees. - e.g. translation_range = (-10,10) will result in a random - draw of the rotation parameters between -10 and 10 degrees - - reference : ANTsImage (optional but recommended) - image providing the reference space for the transform. - this will also set the transform fixed parameters. - - lazy : boolean (default = False) - if True, calling the `transform` method only returns - the randomly generated transform and does not actually - transform the image - """ - if (not isinstance(translation_range, (list, tuple))) or ( - len(translation_range) != 2 - ): - raise ValueError("shear_range argument must be list/tuple with two values!") - - self.translation_range = translation_range - self.reference = reference - self.lazy = lazy - - def transform(self, X=None, y=None): - """ - Transform an image using an Affine transform with - translation parameters randomly generated from the user-specified - range. Return the transform if X=None. - - Arguments - --------- - X : ANTsImage - Image to transform - - y : ANTsImage (optional) - Another image to transform - - Returns - ------- - ANTsImage if y is None, else a tuple of ANTsImage types - - Examples - -------- - >>> import ants - >>> img = ants.image_read(ants.get_data('ch2')) - >>> tx = ants.contrib.RandomShear3D(translation_range=(-10,10)) - >>> img2 = tx.transform(img) - """ - # random draw in translation range - translation_x = random.gauss( - self.translation_range[0], self.translation_range[1] - ) - translation_y = random.gauss( - self.translation_range[0], self.translation_range[1] - ) - translation_z = random.gauss( - self.translation_range[0], self.translation_range[1] - ) - self.params = (translation_x, translation_y, translation_z) - - tx = Translate3D( - (translation_x, translation_y, translation_z), - reference=self.reference, - lazy=self.lazy, - ) - - return tx.transform(X, y) - - -class Shear3D(object): - """ - Create an ANTs Affine Transform with a specified shear. - """ - - def __init__(self, shear, reference=None, lazy=False): - """ - Initialize a Shear3D object - - Arguments - --------- - shear : list or tuple - shear values for each axis, in degrees. - Negative values can be used for shear in the - other direction - - reference : ANTsImage (optional but recommended) - image providing the reference space for the transform. - this will also set the transform fixed parameters. - - lazy : boolean (default = False) - if True, calling the `transform` method only returns - the randomly generated transform and does not actually - transform the image - """ - if (not isinstance(shear, (list, tuple))) or (len(shear) != 3): - raise ValueError("shear argument must be list/tuple with three values!") - - self.shear = shear - self.lazy = lazy - self.reference = reference - - self.tx = tio.ANTsTransform( - precision="float", dimension=3, transform_type="AffineTransform" - ) - if self.reference is not None: - self.tx.set_fixed_parameters(self.reference.get_center_of_mass()) - - def transform(self, X=None, y=None): - """ - Transform an image using an Affine transform with the given - shear parameters. Return the transform if X=None. - - Arguments - --------- - X : ANTsImage - Image to transform - - y : ANTsImage (optional) - Another image to transform - - Returns - ------- - ANTsImage if y is None, else a tuple of ANTsImage types - - Examples - -------- - >>> import ants - >>> img = ants.image_read(ants.get_data('ch2')) - >>> tx = ants.contrib.Shear3D(shear=(10,0,0)) - >>> img2_x = tx.transform(img)# x axis stays same - >>> tx = ants.contrib.Shear3D(shear=(-10,0,0)) # other direction - >>> img2_x = tx.transform(img)# x axis stays same - >>> tx = ants.contrib.Shear3D(shear=(0,10,0)) - >>> img2_y = tx.transform(img) # y axis stays same - >>> tx = ants.contrib.Shear3D(shear=(0,0,10)) - >>> img2_z = tx.transform(img) # z axis stays same - >>> tx = ants.contrib.Shear3D(shear=(10,10,10)) - >>> img2 = tx.transform(img) - """ - # convert to radians and unpack - shear = [math.pi / 180 * s for s in self.shear] - shear_x, shear_y, shear_z = shear - - shear_matrix = np.array( - [ - [1, shear_x, shear_x, 0], - [shear_y, 1, shear_y, 0], - [shear_z, shear_z, 1, 0], - ] - ) - self.tx.set_parameters(shear_matrix) - if self.lazy or X is None: - return self.tx - else: - if y is None: - return self.tx.apply_to_image(X, reference=self.reference) - else: - return ( - self.tx.apply_to_image(X, reference=self.reference), - self.tx.apply_to_image(y, reference=self.reference), - ) - - -class RandomShear3D(object): - """ - Apply a Shear3D transform to an image, but with the shear - parameters randomly generated from a user-specified range. - The range is determined by a mean (first parameter) and standard deviation - (second parameter) via calls to random.gauss. - """ - - def __init__(self, shear_range, reference=None, lazy=False): - """ - Initialize a RandomShear3D object - - Arguments - --------- - shear_range : list or tuple - Lower and Upper bounds on rotation parameter, in degrees. - e.g. shear_range = (-10,10) will result in a random - draw of the rotation parameters between -10 and 10 degrees - - reference : ANTsImage (optional but recommended) - image providing the reference space for the transform. - this will also set the transform fixed parameters. - - lazy : boolean (default = False) - if True, calling the `transform` method only returns - the randomly generated transform and does not actually - transform the image - """ - if (not isinstance(shear_range, (list, tuple))) or (len(shear_range) != 2): - raise ValueError("shear_range argument must be list/tuple with two values!") - - self.shear_range = shear_range - self.reference = reference - self.lazy = lazy - - def transform(self, X=None, y=None): - """ - Transform an image using an Affine transform with - shear parameters randomly generated from the user-specified - range. Return the transform if X=None. - - Arguments - --------- - X : ANTsImage - Image to transform - - y : ANTsImage (optional) - Another image to transform - - Returns - ------- - ANTsImage if y is None, else a tuple of ANTsImage types - - Examples - -------- - >>> import ants - >>> img = ants.image_read(ants.get_data('ch2')) - >>> tx = ants.contrib.RandomShear3D(shear_range=(-10,10)) - >>> img2 = tx.transform(img) - """ - # random draw in shear range - shear_x = random.gauss(self.shear_range[0], self.shear_range[1]) - shear_y = random.gauss(self.shear_range[0], self.shear_range[1]) - shear_z = random.gauss(self.shear_range[0], self.shear_range[1]) - self.params = (shear_x, shear_y, shear_z) - - tx = Shear3D( - (shear_x, shear_y, shear_z), reference=self.reference, lazy=self.lazy - ) - - return tx.transform(X, y) - - -class Rotate3D(object): - """ - Create an ANTs Affine Transform with a specified level - of rotation. - """ - - def __init__(self, rotation, reference=None, lazy=False): - """ - Initialize a Rotate3D object - - Arguments - --------- - rotation : list or tuple - rotation values for each axis, in degrees. - Negative values can be used for rotation in the - other direction - - reference : ANTsImage (optional but recommended) - image providing the reference space for the transform. - this will also set the transform fixed parameters. - - lazy : boolean (default = False) - if True, calling the `transform` method only returns - the randomly generated transform and does not actually - transform the image - """ - if (not isinstance(rotation, (list, tuple))) or (len(rotation) != 3): - raise ValueError("rotation argument must be list/tuple with three values!") - - self.rotation = rotation - self.lazy = lazy - self.reference = reference - - self.tx = tio.ANTsTransform( - precision="float", dimension=3, transform_type="AffineTransform" - ) - if self.reference is not None: - self.tx.set_fixed_parameters(self.reference.get_center_of_mass()) - - def transform(self, X=None, y=None): - """ - Transform an image using an Affine transform with the given - rotation parameters. Return the transform if X=None. - - Arguments - --------- - X : ANTsImage - Image to transform - - y : ANTsImage (optional) - Another image to transform - - Returns - ------- - ANTsImage if y is None, else a tuple of ANTsImage types - - Examples - -------- - >>> import ants - >>> img = ants.image_read(ants.get_data('ch2')) - >>> tx = ants.contrib.Rotate3D(rotation=(10,-5,12)) - >>> img2 = tx.transform(img) - """ - # unpack zoom range - rotation_x, rotation_y, rotation_z = self.rotation - - # Rotation about X axis - theta_x = math.pi / 180 * rotation_x - rotate_matrix_x = np.array( - [ - [1, 0, 0, 0], - [0, math.cos(theta_x), -math.sin(theta_x), 0], - [0, math.sin(theta_x), math.cos(theta_x), 0], - [0, 0, 0, 1], - ] - ) - - # Rotation about Y axis - theta_y = math.pi / 180 * rotation_y - rotate_matrix_y = np.array( - [ - [math.cos(theta_y), 0, math.sin(theta_y), 0], - [0, 1, 0, 0], - [-math.sin(theta_y), 0, math.cos(theta_y), 0], - [0, 0, 0, 1], - ] - ) - - # Rotation about Z axis - theta_z = math.pi / 180 * rotation_z - rotate_matrix_z = np.array( - [ - [math.cos(theta_z), -math.sin(theta_z), 0, 0], - [math.sin(theta_z), math.cos(theta_z), 0, 0], - [0, 0, 1, 0], - [0, 0, 0, 1], - ] - ) - rotate_matrix = rotate_matrix_x.dot(rotate_matrix_y).dot(rotate_matrix_z)[:3, :] - - self.tx.set_parameters(rotate_matrix) - if self.lazy or X is None: - return self.tx - else: - if y is None: - return self.tx.apply_to_image(X, reference=self.reference) - else: - return ( - self.tx.apply_to_image(X, reference=self.reference), - self.tx.apply_to_image(y, reference=self.reference), - ) - - -class RandomRotate3D(object): - """ - Apply a Rotate3D transform to an image, but with the zoom - parameters randomly generated from a user-specified range. - The range is determined by a mean (first parameter) and standard deviation - (second parameter) via calls to random.gauss. - """ - - def __init__(self, rotation_range, reference=None, lazy=False): - """ - Initialize a RandomRotate3D object - - Arguments - --------- - rotation_range : list or tuple - Lower and Upper bounds on rotation parameter, in degrees. - e.g. rotation_range = (-10,10) will result in a random - draw of the rotation parameters between -10 and 10 degrees - - reference : ANTsImage (optional but recommended) - image providing the reference space for the transform. - this will also set the transform fixed parameters. - - lazy : boolean (default = False) - if True, calling the `transform` method only returns - the randomly generated transform and does not actually - transform the image - """ - if (not isinstance(rotation_range, (list, tuple))) or ( - len(rotation_range) != 2 - ): - raise ValueError( - "rotation_range argument must be list/tuple with two values!" - ) - - self.rotation_range = rotation_range - self.reference = reference - self.lazy = lazy - - def transform(self, X=None, y=None): - """ - Transform an image using an Affine transform with - rotation parameters randomly generated from the user-specified - range. Return the transform if X=None. - - Arguments - --------- - X : ANTsImage - Image to transform - - y : ANTsImage (optional) - Another image to transform - - Returns - ------- - ANTsImage if y is None, else a tuple of ANTsImage types - - Examples - -------- - >>> import ants - >>> img = ants.image_read(ants.get_data('ch2')) - >>> tx = ants.contrib.RandomRotate3D(rotation_range=(-10,10)) - >>> img2 = tx.transform(img) - """ - # random draw in rotation range - rotation_x = random.gauss(self.rotation_range[0], self.rotation_range[1]) - rotation_y = random.gauss(self.rotation_range[0], self.rotation_range[1]) - rotation_z = random.gauss(self.rotation_range[0], self.rotation_range[1]) - self.params = (rotation_x, rotation_y, rotation_z) - - tx = Rotate3D( - (rotation_x, rotation_y, rotation_z), - reference=self.reference, - lazy=self.lazy, - ) - - return tx.transform(X, y) - - -class Zoom3D(object): - """ - Create an ANTs Affine Transform with a specified level - of zoom. Any value greater than 1 implies a "zoom-out" and anything - less than 1 implies a "zoom-in". - """ - - def __init__(self, zoom, reference=None, lazy=False): - """ - Initialize a Zoom3D object - - Arguments - --------- - zoom_range : list or tuple - Lower and Upper bounds on zoom parameter. - e.g. zoom_range = (0.7,0.9) will result in a random - draw of the zoom parameters between 0.7 and 0.9 - - reference : ANTsImage (optional but recommended) - image providing the reference space for the transform. - this will also set the transform fixed parameters. - - lazy : boolean (default = False) - if True, calling the `transform` method only returns - the randomly generated transform and does not actually - transform the image - """ - if (not isinstance(zoom, (list, tuple))) or (len(zoom) != 3): - raise ValueError( - "zoom_range argument must be list/tuple with three values!" - ) - - self.zoom = zoom - self.lazy = lazy - self.reference = reference - - self.tx = tio.ANTsTransform( - precision="float", dimension=3, transform_type="AffineTransform" - ) - if self.reference is not None: - self.tx.set_fixed_parameters(self.reference.get_center_of_mass()) - - def transform(self, X=None, y=None): - """ - Transform an image using an Affine transform with the given - zoom parameters. Return the transform if X=None. - - Arguments - --------- - X : ANTsImage - Image to transform - - y : ANTsImage (optional) - Another image to transform - - Returns - ------- - ANTsImage if y is None, else a tuple of ANTsImage types - - Examples - -------- - >>> import ants - >>> img = ants.image_read(ants.get_data('ch2')) - >>> tx = ants.contrib.Zoom3D(zoom=(0.8,0.8,0.8)) - >>> img2 = tx.transform(img) - """ - # unpack zoom range - zoom_x, zoom_y, zoom_z = self.zoom - - self.params = (zoom_x, zoom_y, zoom_z) - zoom_matrix = np.array( - [[zoom_x, 0, 0, 0], [0, zoom_y, 0, 0], [0, 0, zoom_z, 0]] - ) - self.tx.set_parameters(zoom_matrix) - if self.lazy or X is None: - return self.tx - else: - if y is None: - return self.tx.apply_to_image(X, reference=self.reference) - else: - return ( - self.tx.apply_to_image(X, reference=self.reference), - self.tx.apply_to_image(y, reference=self.reference), - ) - - -class RandomZoom3D(object): - """ - Apply a Zoom3D transform to an image, but with the zoom - parameters randomly generated from a user-specified range. - The range is determined by a mean (first parameter) and standard deviation - (second parameter) via calls to random.gauss. - """ - - def __init__(self, zoom_range, reference=None, lazy=False): - """ - Initialize a RandomZoom3D object - - Arguments - --------- - zoom_range : list or tuple - Lower and Upper bounds on zoom parameter. - e.g. zoom_range = (0.7,0.9) will result in a random - draw of the zoom parameters between 0.7 and 0.9 - - reference : ANTsImage (optional but recommended) - image providing the reference space for the transform - this will also set the transform fixed parameters. - - lazy : boolean (default = False) - if True, calling the `transform` method only returns - the randomly generated transform and does not actually - transform the image - """ - if (not isinstance(zoom_range, (list, tuple))) or (len(zoom_range) != 2): - raise ValueError("zoom_range argument must be list/tuple with two values!") - - self.zoom_range = zoom_range - self.reference = reference - self.lazy = lazy - - def transform(self, X=None, y=None): - """ - Transform an image using an Affine transform with - zoom parameters randomly generated from the user-specified - range. Return the transform if X=None. - - Arguments - --------- - X : ANTsImage - Image to transform - - y : ANTsImage (optional) - Another image to transform - - Returns - ------- - ANTsImage if y is None, else a tuple of ANTsImage types - - Examples - -------- - >>> import ants - >>> img = ants.image_read(ants.get_data('ch2')) - >>> tx = ants.contrib.RandomZoom3D(zoom_range=(0.8,0.9)) - >>> img2 = tx.transform(img) - """ - # random draw in zoom range - zoom_x = np.exp( - random.gauss(np.log(self.zoom_range[0]), np.log(self.zoom_range[1])) - ) - zoom_y = np.exp( - random.gauss(np.log(self.zoom_range[0]), np.log(self.zoom_range[1])) - ) - zoom_z = np.exp( - random.gauss(np.log(self.zoom_range[0]), np.log(self.zoom_range[1])) - ) - self.params = (zoom_x, zoom_y, zoom_z) - - tx = Zoom3D((zoom_x, zoom_y, zoom_z), reference=self.reference, lazy=self.lazy) - - return tx.transform(X, y) diff --git a/ants/contrib/sampling/transforms.py b/ants/contrib/sampling/transforms.py deleted file mode 100644 index 753542ed..00000000 --- a/ants/contrib/sampling/transforms.py +++ /dev/null @@ -1,766 +0,0 @@ -""" -Various data augmentation transforms for ANTsImage types - -List of Transformations: -====================== -- CastIntensity -- BlurIntensity -- NormalizeIntensity -- RescaleIntensity -- ShiftScaleIntensity -- SigmoidIntensity -====================== -- FlipImage -- TranslateImage - -TODO ----- -- RotateImage -- ShearImage -- ScaleImage -- DeformImage -- PadImage -- HistogramEqualizeIntensity -- TruncateIntensity -- SharpenIntensity -- MorpholigicalIntensity - - MD - - ME - - MO - - MC - - GD - - GE - - GO - - GC -""" -__all__ = ['CastIntensity', - 'BlurIntensity', - 'LocallyBlurIntensity', - 'NormalizeIntensity', - 'RescaleIntensity', - 'ShiftScaleIntensity', - 'SigmoidIntensity', - 'FlipImage', - 'ScaleImage', - 'TranslateImage', - 'MultiResolutionImage'] - -from ... import utils -from ...core import ants_image as iio - - -class MultiResolutionImage(object): - """ - Generate a set of images at multiple resolutions from an original image - """ - def __init__(self, levels=4, keep_shape=False): - self.levels = levels - self.keep_shape = keep_shape - - def transform(self, X, y=None): - """ - Generate a set of multi-resolution ANTsImage types - - Arguments - --------- - X : ANTsImage - image to transform - - y : ANTsImage (optional) - another image to transform - - Example - ------- - >>> import ants - >>> multires = ants.contrib.MultiResolutionImage(levels=4) - >>> img = ants.image_read(ants.get_data('r16')) - >>> imgs = multires.transform(img) - """ - insuffix = X._libsuffix - multires_fn = utils.get_lib_fn('multiResolutionAntsImage%s' % (insuffix)) - casted_ptrs = multires_fn(X.pointer, self.levels) - - imgs = [] - for casted_ptr in casted_ptrs: - img = iio.ANTsImage(pixeltype=X.pixeltype, dimension=X.dimension, - components=X.components, pointer=casted_ptr) - if self.keep_shape: - img = img.resample_image_to_target(X) - imgs.append(img) - - return imgs - - -## Intensity Transforms ## - -class CastIntensity(object): - """ - Cast the pixeltype of an ANTsImage to a given type. - This code uses the C++ ITK library directly, so it is fast. - - NOTE: This offers a ~2.5x speedup over using img.clone(pixeltype): - - Timings vs Cloning - ------------------ - >>> import ants - >>> import time - >>> caster = ants.contrib.CastIntensity('float') - >>> img = ants.image_read(ants.get_data('mni')).clone('unsigned int') - >>> s = time.time() - >>> for i in range(1000): - ... img_float = caster.transform(img) - >>> e = time.time() - >>> print(e - s) # 9.6s - >>> s = time.time() - >>> for i in range(1000): - ... img_float = img.clone('float') - >>> e = time.time() - >>> print(e - s) # 25.3s - """ - def __init__(self, pixeltype): - """ - Initialize a CastIntensity transform - - Arguments - --------- - pixeltype : string - pixeltype to which images will be casted - - Example - ------- - >>> import ants - >>> caster = ants.contrib.CastIntensity('float') - """ - self.pixeltype = pixeltype - - def transform(self, X, y=None): - """ - Transform an image by casting its type - - Arguments - --------- - X : ANTsImage - image to cast - - y : ANTsImage (optional) - another image to cast. - - Example - ------- - >>> import ants - >>> caster = ants.contrib.CastIntensity('float') - >>> img2d = ants.image_read(ants.get_data('r16')).clone('unsigned int') - >>> img2d_float = caster.transform(img2d) - >>> print(img2d.pixeltype, '- ', img2d_float.pixeltype) - >>> img3d = ants.image_read(ants.get_data('mni')).clone('unsigned int') - >>> img3d_float = caster.transform(img3d) - >>> print(img3d.pixeltype, ' - ' , img3d_float.pixeltype) - """ - insuffix = X._libsuffix - outsuffix = '%s%i' % (utils.short_ptype(self.pixeltype), X.dimension) - cast_fn = utils.get_lib_fn('castAntsImage%s%s' % (insuffix, outsuffix)) - casted_ptr = cast_fn(X.pointer) - return iio.ANTsImage(pixeltype=self.pixeltype, dimension=X.dimension, - components=X.components, pointer=casted_ptr) - - -class BlurIntensity(object): - """ - Transform for blurring the intensity of an ANTsImage - using a Gaussian Filter - """ - def __init__(self, sigma, width): - """ - Initialize a BlurIntensity transform - - Arguments - --------- - sigma : float - variance of gaussian kernel intensity - increasing this value increasing the amount - of blur - - width : int - width of gaussian kernel shape - increasing this value increase the number of - neighboring voxels which are used for blurring - - Example - ------- - >>> import ants - >>> blur = ants.contrib.BlurIntensity(2,3) - """ - self.sigma = sigma - self.width = width - - def transform(self, X, y=None): - """ - Blur an image by applying a gaussian filter. - - Arguments - --------- - X : ANTsImage - image to transform - - y : ANTsImage (optional) - another image to transform. - - Example - ------- - >>> import ants - >>> blur = ants.contrib.BlurIntensity(2,3) - >>> img2d = ants.image_read(ants.get_data('r16')) - >>> img2d_b = blur.transform(img2d) - >>> ants.plot(img2d) - >>> ants.plot(img2d_b) - >>> img3d = ants.image_read(ants.get_data('mni')) - >>> img3d_b = blur.transform(img3d) - >>> ants.plot(img3d) - >>> ants.plot(img3d_b) - """ - if X.pixeltype != 'float': - raise ValueError('image.pixeltype must be float ... use TypeCast transform or clone to float') - - insuffix = X._libsuffix - cast_fn = utils.get_lib_fn('blurAntsImage%s' % (insuffix)) - casted_ptr = cast_fn(X.pointer, self.sigma, self.width) - return iio.ANTsImage(pixeltype=X.pixeltype, dimension=X.dimension, - components=X.components, pointer=casted_ptr, - origin=X.origin) - - -class LocallyBlurIntensity(object): - """ - Blur an ANTsImage locally using a gradient anisotropic - diffusion filter, thereby preserving the sharpeness of edges as best - as possible. - """ - def __init__(self, conductance=1, iters=5): - self.conductance = conductance - self.iters = iters - - def transform(self, X, y=None): - """ - Locally blur an image by applying a gradient anisotropic diffusion filter. - - Arguments - --------- - X : ANTsImage - image to transform - - y : ANTsImage (optional) - another image to transform. - - Example - ------- - >>> import ants - >>> blur = ants.contrib.LocallyBlurIntensity(1,5) - >>> img2d = ants.image_read(ants.get_data('r16')) - >>> img2d_b = blur.transform(img2d) - >>> ants.plot(img2d) - >>> ants.plot(img2d_b) - >>> img3d = ants.image_read(ants.get_data('mni')) - >>> img3d_b = blur.transform(img3d) - >>> ants.plot(img3d) - >>> ants.plot(img3d_b) - """ - #if X.pixeltype != 'float': - # raise ValueError('image.pixeltype must be float ... use TypeCast transform or clone to float') - insuffix = X._libsuffix - cast_fn = utils.get_lib_fn('locallyBlurAntsImage%s' % (insuffix)) - casted_ptr = cast_fn(X.pointer, self.iters, self.conductance) - return iio.ANTsImage(pixeltype=X.pixeltype, dimension=X.dimension, - components=X.components, pointer=casted_ptr) - - -class NormalizeIntensity(object): - """ - Normalize the intensity values of an ANTsImage to have - zero mean and unit variance - - NOTE: this transform is more-or-less the same in speed - as an equivalent numpy+scikit-learn solution. - - Timing vs Numpy+Scikit-Learn - ---------------------------- - >>> import ants - >>> import numpy as np - >>> from sklearn.preprocessing import StandardScaler - >>> import time - >>> img = ants.image_read(ants.get_data('mni')) - >>> arr = img.numpy().reshape(1,-1) - >>> normalizer = ants.contrib.NormalizeIntensity() - >>> normalizer2 = StandardScaler() - >>> s = time.time() - >>> for i in range(100): - ... img_scaled = normalizer.transform(img) - >>> e = time.time() - >>> print(e - s) # 3.3s - >>> s = time.time() - >>> for i in range(100): - ... arr_scaled = normalizer2.fit_transform(arr) - >>> e = time.time() - >>> print(e - s) # 3.5s - """ - def __init__(self): - """ - Initialize a NormalizeIntensity transform - """ - pass - - def transform(self, X, y=None): - """ - Transform an image by normalizing its intensity values to - have zero mean and unit variance. - - Arguments - --------- - X : ANTsImage - image to transform - - y : ANTsImage (optional) - another image to transform. - - Example - ------- - >>> import ants - >>> normalizer = ants.contrib.NormalizeIntensity() - >>> img2d = ants.image_read(ants.get_data('r16')) - >>> img2d_r = normalizer.transform(img2d) - >>> print(img2d.mean(), ',', img2d.std(), ' -> ', img2d_r.mean(), ',', img2d_r.std()) - >>> img3d = ants.image_read(ants.get_data('mni')) - >>> img3d_r = normalizer.transform(img3d) - >>> print(img3d.mean(), ',' , img3d.std(), ',', ' -> ', img3d_r.mean(), ',' , img3d_r.std()) - """ - if X.pixeltype != 'float': - raise ValueError('image.pixeltype must be float ... use TypeCast transform or clone to float') - - insuffix = X._libsuffix - cast_fn = utils.get_lib_fn('normalizeAntsImage%s' % (insuffix)) - casted_ptr = cast_fn(X.pointer) - return iio.ANTsImage(pixeltype=X.pixeltype, dimension=X.dimension, - components=X.components, pointer=casted_ptr) - - -class RescaleIntensity(object): - """ - Rescale the pixeltype of an ANTsImage linearly to be between a given - minimum and maximum value. - This code uses the C++ ITK library directly, so it is fast. - - NOTE: this offered a ~5x speedup over using built-in arithmetic operations in ANTs. - It is also more-or-less the same in speed as an equivalent numpy+scikit-learn - solution. - - Timing vs Built-in Operations - ----------------------------- - >>> import ants - >>> import time - >>> rescaler = ants.contrib.RescaleIntensity(0,1) - >>> img = ants.image_read(ants.get_data('mni')) - >>> s = time.time() - >>> for i in range(100): - ... img_float = rescaler.transform(img) - >>> e = time.time() - >>> print(e - s) # 2.8s - >>> s = time.time() - >>> for i in range(100): - ... maxval = img.max() - ... img_float = (img - maxval) / (maxval - img.min()) - >>> e = time.time() - >>> print(e - s) # 13.9s - - Timing vs Numpy+Scikit-Learn - ---------------------------- - >>> import ants - >>> import numpy as np - >>> from sklearn.preprocessing import MinMaxScaler - >>> import time - >>> img = ants.image_read(ants.get_data('mni')) - >>> arr = img.numpy().reshape(1,-1) - >>> rescaler = ants.contrib.RescaleIntensity(-1,1) - >>> rescaler2 = MinMaxScaler((-1,1)).fit(arr) - >>> s = time.time() - >>> for i in range(100): - ... img_scaled = rescaler.transform(img) - >>> e = time.time() - >>> print(e - s) # 2.8s - >>> s = time.time() - >>> for i in range(100): - ... arr_scaled = rescaler2.transform(arr) - >>> e = time.time() - >>> print(e - s) # 3s - """ - - def __init__(self, min_val, max_val): - """ - Initialize a RescaleIntensity transform. - - Arguments - --------- - min_val : float - minimum value to which image(s) will be rescaled - - max_val : float - maximum value to which image(s) will be rescaled - - Example - ------- - >>> import ants - >>> rescaler = ants.contrib.RescaleIntensity(0,1) - """ - self.min_val = min_val - self.max_val = max_val - - def transform(self, X, y=None): - """ - Transform an image by linearly rescaling its intensity to - be between a minimum and maximum value - - Arguments - --------- - X : ANTsImage - image to transform - - y : ANTsImage (optional) - another image to transform. - - Example - ------- - >>> import ants - >>> rescaler = ants.contrib.RescaleIntensity(0,1) - >>> img2d = ants.image_read(ants.get_data('r16')) - >>> img2d_r = rescaler.transform(img2d) - >>> print(img2d.min(), ',', img2d.max(), ' -> ', img2d_r.min(), ',', img2d_r.max()) - >>> img3d = ants.image_read(ants.get_data('mni')) - >>> img3d_r = rescaler.transform(img3d) - >>> print(img3d.min(), ',' , img3d.max(), ' -> ', img3d_r.min(), ',' , img3d_r.max()) - """ - if X.pixeltype != 'float': - raise ValueError('image.pixeltype must be float ... use TypeCast transform or clone to float') - - insuffix = X._libsuffix - cast_fn = utils.get_lib_fn('rescaleAntsImage%s' % (insuffix)) - casted_ptr = cast_fn(X.pointer, self.min_val, self.max_val) - return iio.ANTsImage(pixeltype=X.pixeltype, dimension=X.dimension, - components=X.components, pointer=casted_ptr) - - -class ShiftScaleIntensity(object): - """ - Shift and scale the intensity of an ANTsImage - """ - def __init__(self, shift, scale): - """ - Initialize a ShiftScaleIntensity transform - - Arguments - --------- - shift : float - shift all of the intensity values by the given amount through addition. - For example, if the minimum image value is 0.0 and the shift - is 10.0, then the new minimum value (before scaling) will be 10.0 - - scale : float - scale all the intensity values by the given amount through multiplication. - For example, if the min/max image values are 10/20 and the scale - is 2.0, then then new min/max values will be 20/40 - - Example - ------- - >>> import ants - >>> shiftscaler = ants.contrib.ShiftScaleIntensity(shift=10, scale=2) - """ - self.shift = shift - self.scale = scale - - def transform(self, X, y=None): - """ - Transform an image by shifting and scaling its intensity values. - - Arguments - --------- - X : ANTsImage - image to transform - - y : ANTsImage (optional) - another image to transform. - - Example - ------- - >>> import ants - >>> shiftscaler = ants.contrib.ShiftScaleIntensity(10,2.) - >>> img2d = ants.image_read(ants.get_data('r16')) - >>> img2d_r = shiftscaler.transform(img2d) - >>> print(img2d.min(), ',', img2d.max(), ' -> ', img2d_r.min(), ',', img2d_r.max()) - >>> img3d = ants.image_read(ants.get_data('mni')) - >>> img3d_r = shiftscaler.transform(img3d) - >>> print(img3d.min(), ',' , img3d.max(), ',', ' -> ', img3d_r.min(), ',' , img3d_r.max()) - """ - if X.pixeltype != 'float': - raise ValueError('image.pixeltype must be float ... use TypeCast transform or clone to float') - - insuffix = X._libsuffix - cast_fn = utils.get_lib_fn('shiftScaleAntsImage%s' % (insuffix)) - casted_ptr = cast_fn(X.pointer, self.scale, self.shift) - return iio.ANTsImage(pixeltype=X.pixeltype, dimension=X.dimension, - components=X.components, pointer=casted_ptr) - - -class SigmoidIntensity(object): - """ - Transform an image using a sigmoid function - """ - def __init__(self, min_val, max_val, alpha, beta): - """ - Initialize a SigmoidIntensity transform - - Arguments - --------- - min_val : float - minimum value - - max_val : float - maximum value - - alpha : float - alpha value for sigmoid - - beta : flaot - beta value for sigmoid - - Example - ------- - >>> import ants - >>> sigscaler = ants.contrib.SigmoidIntensity(0,1,1,1) - """ - self.min_val = min_val - self.max_val = max_val - self.alpha = alpha - self.beta = beta - - def transform(self, X, y=None): - """ - Transform an image by applying a sigmoid function. - - Arguments - --------- - X : ANTsImage - image to transform - - y : ANTsImage (optional) - another image to transform. - - Example - ------- - >>> import ants - >>> sigscaler = ants.contrib.SigmoidIntensity(0,1,1,1) - >>> img2d = ants.image_read(ants.get_data('r16')) - >>> img2d_r = sigscaler.transform(img2d) - >>> img3d = ants.image_read(ants.get_data('mni')) - >>> img3d_r = sigscaler.transform(img3d) - """ - if X.pixeltype != 'float': - raise ValueError('image.pixeltype must be float ... use TypeCast transform or clone to float') - - insuffix = X._libsuffix - cast_fn = utils.get_lib_fn('sigmoidAntsImage%s' % (insuffix)) - casted_ptr = cast_fn(X.pointer, self.min_val, self.max_val, self.alpha, self.beta) - return iio.ANTsImage(pixeltype=X.pixeltype, dimension=X.dimension, - components=X.components, pointer=casted_ptr) - - -## Physical Transforms ## - -class FlipImage(object): - """ - Transform an image by flipping two axes. - """ - def __init__(self, axis1, axis2): - """ - Initialize a SigmoidIntensity transform - - Arguments - --------- - axis1 : int - axis to flip - - axis2 : int - other axis to flip - - Example - ------- - >>> import ants - >>> flipper = ants.contrib.FlipImage(0,1) - """ - self.axis1 = axis1 - self.axis2 = axis2 - - def transform(self, X, y=None): - """ - Transform an image by applying a sigmoid function. - - Arguments - --------- - X : ANTsImage - image to transform - - y : ANTsImage (optional) - another image to transform. - - Example - ------- - >>> import ants - >>> flipper = ants.contrib.FlipImage(0,1) - >>> img2d = ants.image_read(ants.get_data('r16')) - >>> img2d_r = flipper.transform(img2d) - >>> ants.plot(img2d) - >>> ants.plot(img2d_r) - >>> flipper2 = ants.contrib.FlipImage(1,0) - >>> img2d = ants.image_read(ants.get_data('r16')) - >>> img2d_r = flipper2.transform(img2d) - >>> ants.plot(img2d) - >>> ants.plot(img2d_r) - """ - if X.pixeltype != 'float': - raise ValueError('image.pixeltype must be float ... use TypeCast transform or clone to float') - - insuffix = X._libsuffix - cast_fn = utils.get_lib_fn('flipAntsImage%s' % (insuffix)) - casted_ptr = cast_fn(X.pointer, self.axis1, self.axis2) - return iio.ANTsImage(pixeltype=X.pixeltype, dimension=X.dimension, - components=X.components, pointer=casted_ptr, - origin=X.origin) - - -class TranslateImage(object): - """ - Translate an image in physical space. This function calls - highly optimized ITK/C++ code. - """ - def __init__(self, translation, reference=None, interp='linear'): - """ - Initialize a TranslateImage transform - - Arguments - --------- - translation : list, tuple, or numpy.ndarray - absolute pixel transformation in each axis - - reference : ANTsImage (optional) - image which provides the reference physical space in which - to perform the transform - - interp : string - type of interpolation to use - options: linear, nearest - - Example - ------- - >>> import ants - >>> translater = ants.contrib.TranslateImage((10,10), interp='linear') - """ - if interp not in {'linear', 'nearest'}: - raise ValueError('interp must be one of {linear, nearest}') - - self.translation = list(translation) - self.reference = reference - self.interp = interp - - def transform(self, X, y=None): - """ - Example - ------- - >>> import ants - >>> translater = ants.contrib.TranslateImage((40,0)) - >>> img2d = ants.image_read(ants.get_data('r16')) - >>> img2d_r = translater.transform(img2d) - >>> ants.plot(img2d, img2d_r) - >>> translater = ants.contrib.TranslateImage((40,0,0)) - >>> img3d = ants.image_read(ants.get_data('mni')) - >>> img3d_r = translater.transform(img3d) - >>> ants.plot(img3d, img3d_r, axis=2) - """ - if X.pixeltype != 'float': - raise ValueError('image.pixeltype must be float ... use TypeCast transform or clone to float') - - if len(self.translation) != X.dimension: - raise ValueError('must give a translation value for each image dimension') - - if self.reference is None: - reference = X - else: - reference = self.reference - - insuffix = X._libsuffix - cast_fn = utils.get_lib_fn('translateAntsImage%s_%s' % (insuffix, self.interp)) - casted_ptr = cast_fn(X.pointer, reference.pointer, self.translation) - return iio.ANTsImage(pixeltype=X.pixeltype, dimension=X.dimension, - components=X.components, pointer=casted_ptr) - - -class ScaleImage(object): - """ - Scale an image in physical space. This function calls - highly optimized ITK/C++ code. - """ - def __init__(self, scale, reference=None, interp='linear'): - """ - Initialize a TranslateImage transform - - Arguments - --------- - scale : list, tuple, or numpy.ndarray - relative scaling along each axis - - reference : ANTsImage (optional) - image which provides the reference physical space in which - to perform the transform - - interp : string - type of interpolation to use - options: linear, nearest - - Example - ------- - >>> import ants - >>> translater = ants.contrib.TranslateImage((10,10), interp='linear') - """ - if interp not in {'linear', 'nearest'}: - raise ValueError('interp must be one of {linear, nearest}') - - self.scale = list(scale) - self.reference = reference - self.interp = interp - - def transform(self, X, y=None): - """ - Example - ------- - >>> import ants - >>> scaler = ants.contrib.ScaleImage((1.2,1.2)) - >>> img2d = ants.image_read(ants.get_data('r16')) - >>> img2d_r = scaler.transform(img2d) - >>> ants.plot(img2d, img2d_r) - >>> scaler = ants.contrib.ScaleImage((1.2,1.2,1.2)) - >>> img3d = ants.image_read(ants.get_data('mni')) - >>> img3d_r = scaler.transform(img3d) - >>> ants.plot(img3d, img3d_r) - """ - if X.pixeltype != 'float': - raise ValueError('image.pixeltype must be float ... use TypeCast transform or clone to float') - - if len(self.scale) != X.dimension: - raise ValueError('must give a scale value for each image dimension') - - if self.reference is None: - reference = X - else: - reference = self.reference - - insuffix = X._libsuffix - cast_fn = utils.get_lib_fn('scaleAntsImage%s_%s' % (insuffix, self.interp)) - casted_ptr = cast_fn(X.pointer, reference.pointer, self.scale) - return iio.ANTsImage(pixeltype=X.pixeltype, dimension=X.dimension, - components=X.components, pointer=casted_ptr) - diff --git a/ants/contrib/sklearn_interface/__init__.py b/ants/contrib/sklearn_interface/__init__.py deleted file mode 100644 index 68cc3fd3..00000000 --- a/ants/contrib/sklearn_interface/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ - - -from .sklearn_registration import * \ No newline at end of file diff --git a/ants/contrib/sklearn_interface/sklearn_registration.py b/ants/contrib/sklearn_interface/sklearn_registration.py deleted file mode 100644 index 2621a79e..00000000 --- a/ants/contrib/sklearn_interface/sklearn_registration.py +++ /dev/null @@ -1,149 +0,0 @@ - - -__all__ = ['RigidRegistration'] - -from ...registration import interface, apply_transforms - - -class Registration(object): - """ - How would it work: - - # Co-registration within-visit - reg = Registration('Rigid', fixed_image=t1_template, - save_dir=save_dir, save_suffix='_coreg') - for img in other_imgs: - reg.fit(img) - - for img in [flair_img, t2_img]: - reg = Registration('Rigid', fixed_image=t1_template, - save_dir=save_dir, save_suffix='_coreg') - reg.fit(img) - - # Co-registration across-visit - reg = Registration('Rigid', fixed_image=t1) - reg.fit(moving=t1_followup) - - # now align all followups with first visit - for img in [flair_follwup, t2_followup]: - img_reg = reg.transform(img) - - # conversly, align all first visits with followups - for img in [flair, t2]: - img_reg = reg.inverse_transform(img) - """ - - def __init__(self, type_of_transform, fixed_image): - """ - Properties: - type_of_transform - fixed_image (template) - save_dir (where to save outputs) - save_suffix (what to append to saved outputs) - save_prefix (what to preppend to saved outputs) - """ - self.type_of_transform = type_of_transform - self.fixed_image = fixed_image - - def fit(self, X, y=None): - """ - X : ANTsImage | string | list of ANTsImage types | list of strings - images to register to fixed image - - y : string | list of strings - labels for images - """ - moving_images = X if isinstance(X, (list,tuple)) else [X] - moving_labels = y if y is not None else [i for i in range(len(moving_images))] - fixed_image = self.fixed_image - - self.fwdtransforms_ = {} - self.invtransforms_ = {} - self.warpedmovout_ = {} - self.warpedfixout_ = {} - - for moving_image, moving_label in zip(moving_images, moving_labels): - fit_result = interface.registration(fixed_image, - moving_image, - type_of_transform=self.type_of_transform, - initial_transform=None, - outprefix='', - mask=None, - grad_step=0.2, - flow_sigma=3, - total_sigma=0, - aff_metric='mattes', - aff_sampling=32, - syn_metric='mattes', - syn_sampling=32, - reg_iterations=(40,20,0), - verbose=False) - - self.fwdtransforms_[moving_label] = fit_result['fwdtransforms'] - self.invtransforms_[moving_label] = fit_result['invtransforms'] - self.warpedmovout_[moving_label] = fit_result['warpedmovout'] - self.warpedfixout_[moving_label] = fit_result['warpedfixout'] - - return self - - def transform(self, X, y=None): - pass - - -class RigidRegistration(object): - """ - Rigid Registration as a Scikit-Learn compatible transform class - - Example - ------- - >>> import ants - >>> import ants.extra as extrants - >>> fi = ants.image_read(ants.get_data('r16')) - >>> mi = ants.image_read(ants.get_data('r64')) - >>> regtx = extrants.RigidRegistration() - >>> regtx.fit(fi, mi) - >>> mi_r = regtx.transform(mi) - >>> ants.plot(fi, mi_r.iMath_Canny(1, 2, 4).iMath('MD',1)) - """ - def __init__(self, fixed_image=None): - self.type_of_transform = 'Rigid' - self.fixed_image = fixed_image - - def fit(self, moving_image, fixed_image=None): - if fixed_image is None: - if self.fixed_image is None: - raise ValueError('must give fixed_image in fit() or set it in __init__') - fixed_image = self.fixed_image - - fit_result = interface.registration(fixed_image, - moving_image, - type_of_transform=self.type_of_transform, - initial_transform=None, - outprefix='', - mask=None, - grad_step=0.2, - flow_sigma=3, - total_sigma=0, - aff_metric='mattes', - aff_sampling=32, - syn_metric='mattes', - syn_sampling=32, - reg_iterations=(40,20,0), - verbose=False) - self._fit_result = fit_result - self.fwdtransforms_ = fit_result['fwdtransforms'] - self.invtransforms_ = fit_result['invtransforms'] - self.warpedmovout_ = fit_result['warpedmovout'] - self.warpedfiout_ = fit_result['warpedfixout'] - - def transform(self, moving_image, fixed_image=None): - result = apply_transforms(fixed=fixed_image, moving=moving_image, - transformlist=self.fwdtransforms) - return result - - def inverse_transform(self, moving_image, fixed_image=None): - result = apply_transforms(fixed=fixed_image, moving=moving_image, - transformlist=self.invtransforms) - return result - - diff --git a/ants/segmentation/functional_lung_segmentation.py b/ants/segmentation/functional_lung_segmentation.py index dcc22541..01d30856 100644 --- a/ants/segmentation/functional_lung_segmentation.py +++ b/ants/segmentation/functional_lung_segmentation.py @@ -61,9 +61,12 @@ def functional_lung_segmentation(image, Example ------- >>> import ants - >>> image = ants.image_read("lung_image.nii.gz") - >>> mask = ants.image_read("lung_mask.nii.gz") - >>> seg = functional_lung_segmentation(image, mask, verbose=True) + >>> image = ants.image_read(ants.get_data("mni")).resample_image((4,4,4)) + >>> mask = image.get_mask() + >>> seg = ants.functional_lung_segmentation(image, mask, verbose=True, + number_of_iterations=1, + number_of_clusters=2, + number_of_atropos_iterations=1) """ if image.dimension != 3: diff --git a/ants/utils/fit_bspline_displacement_field.py b/ants/utils/fit_bspline_displacement_field.py index 84ee431b..3ff69fbc 100644 --- a/ants/utils/fit_bspline_displacement_field.py +++ b/ants/utils/fit_bspline_displacement_field.py @@ -96,13 +96,10 @@ def fit_bspline_displacement_field(displacement_field=None, Example ------- - >>> # Perform 2-D fitting - >>> - >>> import ants, numpy - >>> + >>> import ants + >>> import numpy >>> points = numpy.array([[-50, -50]]) >>> deltas = numpy.array([[10, 10]]) - >>> >>> bspline_field = ants.fit_bspline_displacement_field( >>> displacement_origins=points, displacements=deltas, >>> origin=[0.0, 0.0], spacing=[1.0, 1.0], size=[100, 100], diff --git a/ants/utils/fit_thin_plate_spline_displacement_field.py b/ants/utils/fit_thin_plate_spline_displacement_field.py index a1f37dfe..53d8399a 100644 --- a/ants/utils/fit_thin_plate_spline_displacement_field.py +++ b/ants/utils/fit_thin_plate_spline_displacement_field.py @@ -53,17 +53,14 @@ def fit_thin_plate_spline_displacement_field(displacement_origins=None, Example ------- - >>> # Perform 2-D fitting - >>> - >>> import ants, numpy - >>> - >>> points = numpy.array([[-50, -50]]) - >>> deltas = numpy.array([[10, 10]]) - >>> + >>> import ants + >>> import numpy as np + >>> points = np.array([[-50, -50]]) + >>> deltas = np.array([[10, 10]]) >>> tps_field = ants.fit_thin_plate_spline_displacement_field( >>> displacement_origins=points, displacements=deltas, >>> origin=[0.0, 0.0], spacing=[1.0, 1.0], size=[100, 100], - >>> direction=numpy.array([[-1, 0], [0, -1]])) + >>> direction=np.array([[-1, 0], [0, -1]])) """ dimensionality = displacement_origins.shape[1] diff --git a/ants/utils/histogram_match_image.py b/ants/utils/histogram_match_image.py index fb418380..44f309f6 100644 --- a/ants/utils/histogram_match_image.py +++ b/ants/utils/histogram_match_image.py @@ -4,7 +4,8 @@ import numpy as np -from ..core import ants_image_io as iio +from ..core import ants_image as iio +from ..core import ants_image_io as iio2 from .. import utils from ..utils import fit_bspline_object_to_scattered_data @@ -94,7 +95,7 @@ def histogram_match_image2(source_image, reference_image, >>> import ants >>> src_img = ants.image_read(ants.get_data('r16')) >>> ref_img = ants.image_read(ants.get_data('r64')) - >>> src_ref = ants.histogram_match_image(src_img, ref_img) + >>> src_ref = ants.histogram_match_image2(src_img, ref_img) """ if not isinstance(match_points, int): @@ -147,7 +148,7 @@ def histogram_match_image2(source_image, reference_image, xfrm = alpha * (bspline_histogram_transform[i+1] - bspline_histogram_transform[i]) + bspline_histogram_transform[i] transformed_source_array[indices] = intensities + xfrm - transformed_source_image = iio.from_numpy(transformed_source_array, origin=source_image.origin, + transformed_source_image = iio2.from_numpy(transformed_source_array, origin=source_image.origin, spacing=source_image.spacing, direction=source_image.direction) transformed_source_image[source_mask == 0] = source_image[source_mask == 0] diff --git a/ants/utils/quantile.py b/ants/utils/quantile.py index c4fc3e90..ae94434e 100644 --- a/ants/utils/quantile.py +++ b/ants/utils/quantile.py @@ -50,7 +50,8 @@ def rank_intensity( x, mask=None, get_mask=True, method='max', ): Example ------- - >>> rank_intensity( some_image ) + >>> img = ants.image_read(ants.get_data('r16')) + >>> ants.rank_intensity(img) """ if mask is not None: fir = rankdata( (x*mask).numpy(), method=method ) @@ -122,7 +123,7 @@ def ilr( data_frame, voxmats, ilr_formula, verbose = False ): >>> df = pd.DataFrame( data ) >>> vlist = { "mat1": mat, "mat2": mat2 } >>> myform = " outcome ~ covar * mat1 " - >>> result = ilr( df, vlist, myform) + >>> result = ants.ilr( df, vlist, myform) >>> myform = " mat2 ~ covar + mat1 " >>> result = ants.ilr( df, vlist, myform) @@ -202,6 +203,12 @@ def ilr( data_frame, voxmats, ilr_formula, verbose = False ): def quantile(image, q, nonzero=True): """ Get the quantile values from an ANTsImage + + Examples + -------- + >>> img = ants.image_read(ants.get_data('r16')) + >>> ants.quantile(img, 0.5) + >>> ants.quantile(img, (0.5, 0.75)) """ img_arr = image.numpy() if isinstance(q, (list,tuple)): diff --git a/tests/run_tests.sh b/tests/run_tests.sh index a875a532..61319cf5 100755 --- a/tests/run_tests.sh +++ b/tests/run_tests.sh @@ -1,7 +1,7 @@ #!/usr/bin/env bash set -e -PYCMD=${PYCMD:="python3"} +PYCMD=${PYCMD:="python"} COVERAGE=0 while [[ "$#" -gt 0 ]]; do case "$1" in diff --git a/tests/test_contrib.py b/tests/test_contrib.py deleted file mode 100644 index c657c924..00000000 --- a/tests/test_contrib.py +++ /dev/null @@ -1,89 +0,0 @@ -""" -Test ants.contrib module -""" - - -import os -import unittest - -from common import run_tests - -import numpy as np -import numpy.testing as nptest - -import ants - - -class TestModule_transforms(unittest.TestCase): - - def setUp(self): - img2d = ants.image_read(ants.get_ants_data('r16')) - img3d = ants.image_read(ants.get_ants_data('ch2')).resample_image((2,2,2)) - self.imgs_2d = [img2d] - self.imgs_3d = [img3d] - - def tearDown(self): - pass - - # ---- Zoom ---- # - - def test_Zoom3D_example(self): - img = ants.image_read(ants.get_data('ch2')) - tx = ants.contrib.Zoom3D(zoom=(0.8,0.8,0.8)) - img2 = tx.transform(img) - - def test_Zoom3D(self): - for img in self.imgs_3d: - imgclone = img.clone() - tx = ants.contrib.Zoom3D((0.8,0.8,0.8)) - img_zoom = tx.transform(img) - - # physical space shouldnt change .. ? - self.assertTrue(ants.image_physical_space_consistency(img, img_zoom)) - # assert no unintended changes to passed-in img - self.assertTrue(ants.image_physical_space_consistency(img, imgclone)) - self.assertTrue(ants.allclose(img, imgclone)) - - # apply to cloned image to ensure deterministic nature - img_zoom2 = tx.transform(imgclone) - self.assertTrue(ants.image_physical_space_consistency(img_zoom, img_zoom2)) - self.assertTrue(ants.allclose(img_zoom,img_zoom2)) - - def test_RandomZoom3D_example(self): - img = ants.image_read(ants.get_data('ch2')) - tx = ants.contrib.RandomZoom3D(zoom_range=(0.8,0.9)) - img2 = tx.transform(img) - - # ---- Rotation ---- # - - def test_Rotate3D_example(self): - img = ants.image_read(ants.get_data('ch2')) - tx = ants.contrib.Rotate3D(rotation=(10,-5,12)) - img2 = tx.transform(img) - - def test_Rotate3D(self): - for img in self.imgs_3d: - imgclone = img.clone() - tx = ants.contrib.Rotate3D((10,-5,12)) - img_zoom = tx.transform(img) - - # physical space shouldnt change .. ? - self.assertTrue(ants.image_physical_space_consistency(img, img_zoom)) - # assert no unintended changes to passed-in img - self.assertTrue(ants.image_physical_space_consistency(img, imgclone)) - self.assertTrue(ants.allclose(img, imgclone)) - - # apply to cloned image to ensure deterministic nature - img_zoom2 = tx.transform(imgclone) - self.assertTrue(ants.image_physical_space_consistency(img_zoom, img_zoom2)) - self.assertTrue(ants.allclose(img_zoom,img_zoom2)) - - def test_RandomRotate3D_example(self): - img = ants.image_read(ants.get_data('ch2')) - tx = ants.contrib.RandomRotate3D(rotation_range=(-10,10)) - img2 = tx.transform(img) - - -if __name__ == '__main__': - run_tests() - diff --git a/tests/test_core_ants_image.py b/tests/test_core_ants_image.py index 57038990..aa11b061 100644 --- a/tests/test_core_ants_image.py +++ b/tests/test_core_ants_image.py @@ -339,6 +339,7 @@ def test__rmul__(self): def test__div__(self): #self.setUp() for img in self.imgs: + img = img + 10 # op on constant img2 = img / 6.9 self.assertTrue(ants.image_physical_space_consistency(img, img2)) @@ -358,6 +359,7 @@ def test__div__(self): def test__pow__(self): #self.setUp() for img in self.imgs: + img = img + 10 # op on constant img2 = img ** 6.9 self.assertTrue(ants.image_physical_space_consistency(img, img2)) diff --git a/tests/test_core_ants_image_io.py b/tests/test_core_ants_image_io.py index 9b50940c..df592160 100644 --- a/tests/test_core_ants_image_io.py +++ b/tests/test_core_ants_image_io.py @@ -188,6 +188,15 @@ def test_images_to_matrix(self): mask2 = mask2 > mask2.mean() imgmat = ants.images_to_matrix(imglist, mask=mask2) + def timeseries_to_matrix(self): + img = ants.make_image( (10,10,10,5 ) ) + mat = ants.timeseries_to_matrix( img ) + + img = ants.make_image( (10,10,10,5 ) ) + mask = ants.ndimage_to_list( img )[0] * 0 + mask[ 4:8, 4:8, 4:8 ] = 1 + mat = ants.timeseries_to_matrix( img, mask = mask ) + img2 = ants.matrix_to_timeseries( img, mat, mask) def test_image_header_info(self): # def image_header_info(filename): diff --git a/tests/test_registration.py b/tests/test_registration.py index a2cf3268..71704aed 100644 --- a/tests/test_registration.py +++ b/tests/test_registration.py @@ -13,6 +13,7 @@ import numpy as np import numpy.testing as nptest +import pandas as pd import ants @@ -249,20 +250,16 @@ def test_example(self): asym = asym - fi -# class TestModule_reorient_image(unittest.TestCase): -# def setUp(self): -# pass -# -# def tearDown(self): -# pass -# -# def test_reorient_image(self): -# image = ants.image_read(ants.get_ants_data("r16")) -# ants.reorient_image(image, (1, 0)) -# -# image = ants.image_read(ants.get_ants_data("r16")) -# image = image.clone("unsigned int") -# ants.reorient_image(image, (1, 0)) +class TestModule_reorient_image(unittest.TestCase): + def setUp(self): + pass + + def tearDown(self): + pass + + def test_reorient_image(self): + mni = ants.image_read(ants.get_data('mni')) + mni2 = mni.reorient_image2() def test_get_center_of_mass(self): fi = ants.image_read(ants.get_ants_data("r16")) @@ -362,6 +359,64 @@ def test_example(self): image, image2, "SyNOnly", multivariate_extras=metrics, verbose=True ) +class TestModule_random(unittest.TestCase): + def setUp(self): + pass + + def tearDown(self): + pass + + def test_landmark_transforms(self): + fixed = np.array([[50.0,50.0],[200.0,50.0],[200.0,200.0]]) + moving = np.array([[50.0,50.0],[50.0,200.0],[200.0,200.0]]) + xfrm = ants.fit_transform_to_paired_points(moving, fixed, transform_type="affine") + xfrm = ants.fit_transform_to_paired_points(moving, fixed, transform_type="rigid") + xfrm = ants.fit_transform_to_paired_points(moving, fixed, transform_type="similarity") + domain_image = ants.image_read(ants.get_ants_data("r16")) + xfrm = ants.fit_transform_to_paired_points(moving, fixed, transform_type="bspline", domain_image=domain_image, number_of_fitting_levels=5) + xfrm = ants.fit_transform_to_paired_points(moving, fixed, transform_type="diffeo", domain_image=domain_image, number_of_fitting_levels=6) + + def test_deformation_gradient(self): + fi = ants.image_read( ants.get_ants_data('r16')) + mi = ants.image_read( ants.get_ants_data('r64')) + fi = ants.resample_image(fi,(128,128),1,0) + mi = ants.resample_image(mi,(128,128),1,0) + mytx = ants.registration(fixed=fi , moving=mi, type_of_transform = ('SyN') ) + dg = ants.deformation_gradient( ants.image_read( mytx['fwdtransforms'][0] ) ) + + def test_jacobian(self): + fi = ants.image_read( ants.get_ants_data('r16')) + mi = ants.image_read( ants.get_ants_data('r64')) + fi = ants.resample_image(fi,(128,128),1,0) + mi = ants.resample_image(mi,(128,128),1,0) + mytx = ants.registration(fixed=fi , moving=mi, type_of_transform = ('SyN') ) + jac = ants.create_jacobian_determinant_image(fi,mytx['fwdtransforms'][0],1) + + def test_apply_transforms(self): + fixed = ants.image_read( ants.get_ants_data('r16') ) + moving = ants.image_read( ants.get_ants_data('r64') ) + fixed = ants.resample_image(fixed, (64,64), 1, 0) + moving = ants.resample_image(moving, (64,64), 1, 0) + mytx = ants.registration(fixed=fixed , moving=moving , + type_of_transform = 'SyN' ) + mywarpedimage = ants.apply_transforms( fixed=fixed, moving=moving, + transformlist=mytx['fwdtransforms'] ) + + def test_apply_transforms_to_points(self): + fixed = ants.image_read( ants.get_ants_data('r16') ) + moving = ants.image_read( ants.get_ants_data('r27') ) + reg = ants.registration( fixed, moving, 'Affine' ) + d = {'x': [128, 127], 'y': [101, 111]} + pts = pd.DataFrame(data=d) + ptsw = ants.apply_transforms_to_points( 2, pts, reg['fwdtransforms']) + + def test_warped_grid(self): + fi = ants.image_read( ants.get_ants_data( 'r16' ) ) + mi = ants.image_read( ants.get_ants_data( 'r64' ) ) + mygr = ants.create_warped_grid( mi ) + mytx = ants.registration(fixed=fi, moving=mi, type_of_transform = ('SyN') ) + mywarpedgrid = ants.create_warped_grid( mi, grid_directions=(False,True), + transform=mytx['fwdtransforms'], fixed_reference_image=fi ) if __name__ == "__main__": run_tests() diff --git a/tests/test_segmentation.py b/tests/test_segmentation.py index dcf40bb3..1a39a332 100644 --- a/tests/test_segmentation.py +++ b/tests/test_segmentation.py @@ -168,5 +168,25 @@ def test_example(self): priorseg = ants.prior_based_segmentation(fi, seg['probabilityimages'], mask, 0.25, 0.1, 3) +class TestModule_random(unittest.TestCase): + + def setUp(self): + pass + def tearDown(self): + pass + + def test_fuzzy_cmeans(self): + image = ants.image_read(ants.get_ants_data('r16')) + mask = ants.get_mask(image) + fuzzy = ants.fuzzy_spatial_cmeans_segmentation(image, mask, number_of_clusters=3) + + def test_functional_lung(self): + image = ants.image_read(ants.get_data("mni")).resample_image((4,4,4)) + mask = image.get_mask() + seg = ants.functional_lung_segmentation(image, mask, verbose=True, + number_of_iterations=1, + number_of_clusters=2, + number_of_atropos_iterations=1) + if __name__ == '__main__': run_tests() diff --git a/tests/test_utils.py b/tests/test_utils.py index dfa1fefd..46dfce47 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -1,12 +1,25 @@ """ Test utils module +""" + +import os +import unittest + +from common import run_tests + +import numpy.testing as nptest +import numpy as np +import pandas as pd + +import ants + class TestModule_iMath(unittest.TestCase): def setUp(self): img2d = ants.image_read(ants.get_ants_data('r16')) - img3d = ants.image_read(ants.get_ants_data('mni')).resample_image((2,2,2)) - self.imgs = [img2d, img3d] + #img3d = ants.image_read(ants.get_ants_data('mni')).resample_image((2,2,2)) + self.imgs = [img2d] def tearDown(self): pass @@ -25,17 +38,17 @@ def test_iMath_options(self): # Grayscale erosion img_tx = ants.iMath(img, 'GE', 3) self.assertTrue(ants.image_physical_space_consistency(img_tx, img)) - self.assertTrue(not ants.allclose(img_tx, img)) + self.assertFalse(ants.allclose(img_tx, img)) # Morphological dilation img_tx = ants.iMath(img, 'MD', 3) self.assertTrue(ants.image_physical_space_consistency(img_tx, img)) - self.assertTrue(not ants.allclose(img_tx, img)) + self.assertFalse(ants.allclose(img_tx, img)) # Morphological erosion img_tx = ants.iMath(img, 'ME', 3) self.assertTrue(ants.image_physical_space_consistency(img_tx, img)) - self.assertTrue(not ants.allclose(img_tx, img)) + self.assertFalse(ants.allclose(img_tx, img)) # Morphological closing img_tx = ants.iMath(img, 'MC', 3) @@ -73,19 +86,6 @@ def test_iMath_options(self): self.assertTrue(ants.image_physical_space_consistency(img_tx, img)) self.assertTrue(not ants.allclose(img_tx, img)) -""" - -import os -import unittest - -from common import run_tests - -import numpy as np -import numpy.testing as nptest - -import ants - - class TestModule_bias_correction(unittest.TestCase): def setUp(self): img2d = ants.image_read(ants.get_ants_data("r16")) @@ -880,5 +880,105 @@ def test2(self): print(ants.allclose(rgb_img, vec_img)) +class TestRandom(unittest.TestCase): + def setUp(self): + pass + def tearDown(self): + pass + + def test_bspline_field(self): + points = np.array([[-50, -50]]) + deltas = np.array([[10, 10]]) + bspline_field = ants.fit_bspline_displacement_field( + displacement_origins=points, displacements=deltas, + origin=[0.0, 0.0], spacing=[1.0, 1.0], size=[100, 100], + direction=np.array([[-1, 0], [0, -1]]), + number_of_fitting_levels=4, mesh_size=(1, 1)) + + def test_quantile(self): + img = ants.image_read(ants.get_data('r16')) + ants.rank_intensity(img) + + def test_ilr(self): + nsub = 20 + mu, sigma = 0, 1 + outcome = np.random.normal( mu, sigma, nsub ) + covar = np.random.normal( mu, sigma, nsub ) + mat = np.random.normal( mu, sigma, (nsub, 500 ) ) + mat2 = np.random.normal( mu, sigma, (nsub, 500 ) ) + data = {'covar':covar,'outcome':outcome} + df = pd.DataFrame( data ) + vlist = { "mat1": mat, "mat2": mat2 } + myform = " outcome ~ covar * mat1 " + result = ants.ilr( df, vlist, myform) + myform = " mat2 ~ covar + mat1 " + result = ants.ilr( df, vlist, myform) + + def test_quantile(self): + img = ants.image_read(ants.get_data('r16')) + ants.quantile(img, 0.5) + ants.quantile(img, (0.5, 0.75)) + + def test_bandpass(self): + brainSignal = np.random.randn( 400, 1000 ) + tr = 1 + filtered = ants.bandpass_filter_matrix( brainSignal, tr = tr ) + + def test_compcorr(self): + cc = ants.compcor( ants.image_read(ants.get_ants_data("ch2")) ) + + def test_histogram_match(self): + src_img = ants.image_read(ants.get_data('r16')) + ref_img = ants.image_read(ants.get_data('r64')) + src_ref = ants.histogram_match_image(src_img, ref_img) + + src_img = ants.image_read(ants.get_data('r16')) + ref_img = ants.image_read(ants.get_data('r64')) + src_ref = ants.histogram_match_image2(src_img, ref_img) + + def test_averaging(self): + x0=[ ants.get_data('r16'), ants.get_data('r27'), ants.get_data('r62'), ants.get_data('r64') ] + x1=[] + for k in range(len(x0)): + x1.append( ants.image_read( x0[k] ) ) + avg=ants.average_images(x0) + avg1=ants.average_images(x1) + avg2=ants.average_images(x1,mask=0) + avg3=ants.average_images(x1,mask=1,normalize=True) + + def test_impute(self): + data = np.random.randn(4,10) + data[2,3] = np.nan + data[3,5] = np.nan + data_imputed = ants.impute(data, 'mean') + + def test_n3_2(self): + image = ants.image_read( ants.get_ants_data('r16') ) + image_n3 = ants.n3_bias_field_correction2(image) + + def test_add_noise(self): + image = ants.image_read(ants.get_ants_data('r16')) + noise_image = ants.add_noise_to_image(image, 'additivegaussian', (0.0, 1.0)) + noise_image = ants.add_noise_to_image(image, 'saltandpepper', (0.1, 0.0, 100.0)) + noise_image = ants.add_noise_to_image(image, 'shot', 1.0) + noise_image = ants.add_noise_to_image(image, 'speckle', 1.0) + + def test_thin_plate_spline(self): + points = np.array([[-50, -50]]) + deltas = np.array([[10, 10]]) + tps_field = ants.fit_thin_plate_spline_displacement_field( + displacement_origins=points, displacements=deltas, + origin=[0.0, 0.0], spacing=[1.0, 1.0], size=[100, 100], + direction=np.array([[-1, 0], [0, -1]])) + + def test_multi_label_morph(self): + img = ants.image_read(ants.get_data('r16')) + labels = ants.get_mask(img,1,150) + ants.get_mask(img,151,225) * 2 + labels_dilated = ants.multi_label_morphology(labels, 'MD', 2) + # should see original label regions preserved in dilated version + # label N should have mean N and 0 variance + print(ants.label_stats(labels_dilated, labels)) + + if __name__ == "__main__": run_tests()