Add files using upload-large-folder tool
Browse filesThis view is limited to 50 files because it contains too many changes. See raw diff
- MindEyeV2/antspy/ants/contrib/__init__.py +4 -0
- MindEyeV2/antspy/ants/contrib/sampling/__init__.py +4 -0
- MindEyeV2/antspy/ants/contrib/sampling/affine2d.py +654 -0
- MindEyeV2/antspy/ants/contrib/sampling/affine3d.py +802 -0
- MindEyeV2/antspy/ants/contrib/sampling/transforms.py +766 -0
- MindEyeV2/antspy/ants/contrib/sklearn_interface/__init__.py +3 -0
- MindEyeV2/antspy/ants/contrib/sklearn_interface/sklearn_registration.py +149 -0
- MindEyeV2/antspy/ants/plotting/__init__.py +8 -0
- MindEyeV2/antspy/ants/plotting/movie.py +87 -0
- MindEyeV2/antspy/ants/plotting/plot.py +486 -0
- MindEyeV2/antspy/ants/plotting/plot_directory.py +118 -0
- MindEyeV2/antspy/ants/plotting/plot_grid.py +355 -0
- MindEyeV2/antspy/ants/plotting/plot_hist.py +73 -0
- MindEyeV2/antspy/ants/plotting/plot_ortho.py +612 -0
- MindEyeV2/antspy/ants/plotting/plot_ortho_stack.py +505 -0
- MindEyeV2/antspy/ants/utils/__init__.py +15 -0
- MindEyeV2/antspy/ants/utils/channels.py +95 -0
- MindEyeV2/antspy/ants/utils/consistency.py +73 -0
- MindEyeV2/antspy/ants/utils/get_ants_data.py +108 -0
- MindEyeV2/antspy/ants/utils/matrix_image.py +202 -0
- MindEyeV2/antspy/ants/utils/mni2tal.py +59 -0
- MindEyeV2/antspy/ants/utils/ndimage_to_list.py +111 -0
- MindEyeV2/antspy/ants/utils/nifti_to_ants.py +39 -0
- MindEyeV2/antspy/ants/utils/scalar_rgb_vector.py +92 -0
- MindEyeV2/antspy/ants/utils/sitk_to_ants.py +63 -0
- MindEyeV2/antspy/docs/Makefile +27 -0
- MindEyeV2/antspy/docs/make.bat +36 -0
- MindEyeV2/antspy/docs/other/ANTsPy Tutorial.ipynb +0 -0
- MindEyeV2/antspy/docs/other/ANTsR_Comparison.md +16 -0
- MindEyeV2/antspy/docs/other/All_Functions.md +180 -0
- MindEyeV2/antspy/docs/other/_config.yml +1 -0
- MindEyeV2/antspy/docs/requirements.txt +2 -0
- MindEyeV2/antspy/docs/source/ants.core.rst +62 -0
- MindEyeV2/antspy/docs/source/ants.learn.rst +22 -0
- MindEyeV2/antspy/docs/source/ants.lib.rst +10 -0
- MindEyeV2/antspy/docs/source/ants.rst +35 -0
- MindEyeV2/antspy/docs/source/ants.segmentation.rst +78 -0
- MindEyeV2/antspy/docs/source/conf.py +233 -0
- MindEyeV2/antspy/docs/source/core.rst +57 -0
- MindEyeV2/antspy/docs/source/modules.rst +8 -0
- MindEyeV2/antspy/docs/source/registration.rst +16 -0
- MindEyeV2/antspy/docs/source/setup.rst +7 -0
- MindEyeV2/antspy/docs/source/vis.rst +8 -0
- MindEyeV2/antspy/src/WRAP_KellyKapowski.cxx +19 -0
- MindEyeV2/antspy/src/WRAP_N4BiasFieldCorrection.cxx +19 -0
- MindEyeV2/antspy/src/WRAP_ResampleImage.cxx +19 -0
- MindEyeV2/antspy/src/antsImage.h +233 -0
- MindEyeV2/antspy/src/antsImageClone.cxx +115 -0
- MindEyeV2/antspy/src/antsTransform.cxx +312 -0
- MindEyeV2/antspy/src/fsl2antstransform.cxx +172 -0
MindEyeV2/antspy/ants/contrib/__init__.py
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
|
| 2 |
+
from .sampling import *
|
| 3 |
+
# from .sklearn_interface import *
|
| 4 |
+
|
MindEyeV2/antspy/ants/contrib/sampling/__init__.py
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
|
| 2 |
+
from .transforms import *
|
| 3 |
+
from .affine2d import *
|
| 4 |
+
from .affine3d import *
|
MindEyeV2/antspy/ants/contrib/sampling/affine2d.py
ADDED
|
@@ -0,0 +1,654 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
Affine transforms
|
| 3 |
+
|
| 4 |
+
See http://www.cs.cornell.edu/courses/cs4620/2010fa/lectures/03transforms3D.pdf
|
| 5 |
+
"""
|
| 6 |
+
|
| 7 |
+
__all__ = [
|
| 8 |
+
"Zoom2D",
|
| 9 |
+
"RandomZoom2D",
|
| 10 |
+
"Rotate2D",
|
| 11 |
+
"RandomRotate2D",
|
| 12 |
+
"Shear2D",
|
| 13 |
+
"RandomShear2D",
|
| 14 |
+
"Translate2D",
|
| 15 |
+
"RandomTranslate2D",
|
| 16 |
+
]
|
| 17 |
+
|
| 18 |
+
import random
|
| 19 |
+
import math
|
| 20 |
+
import numpy as np
|
| 21 |
+
|
| 22 |
+
from ...core import ants_transform as tio
|
| 23 |
+
|
| 24 |
+
|
| 25 |
+
class Translate2D(object):
|
| 26 |
+
"""
|
| 27 |
+
Create an ANTs Affine Transform with a specified translation.
|
| 28 |
+
"""
|
| 29 |
+
|
| 30 |
+
def __init__(self, translation, reference=None, lazy=False):
|
| 31 |
+
"""
|
| 32 |
+
Initialize a Translate2D object
|
| 33 |
+
|
| 34 |
+
Arguments
|
| 35 |
+
---------
|
| 36 |
+
translation : list or tuple
|
| 37 |
+
translation values for each axis, in degrees.
|
| 38 |
+
Negative values can be used for translation in the
|
| 39 |
+
other direction
|
| 40 |
+
|
| 41 |
+
reference : ANTsImage (optional but recommended)
|
| 42 |
+
image providing the reference space for the transform.
|
| 43 |
+
this will also set the transform fixed parameters.
|
| 44 |
+
|
| 45 |
+
lazy : boolean (default = False)
|
| 46 |
+
if True, calling the `transform` method only returns
|
| 47 |
+
the randomly generated transform and does not actually
|
| 48 |
+
transform the image
|
| 49 |
+
"""
|
| 50 |
+
if (not isinstance(translation, (list, tuple))) or (len(translation) != 2):
|
| 51 |
+
raise ValueError("translation argument must be list/tuple with two values!")
|
| 52 |
+
|
| 53 |
+
self.translation = translation
|
| 54 |
+
self.lazy = lazy
|
| 55 |
+
self.reference = reference
|
| 56 |
+
|
| 57 |
+
self.tx = tio.ANTsTransform(
|
| 58 |
+
precision="float", dimension=2, transform_type="AffineTransform"
|
| 59 |
+
)
|
| 60 |
+
if self.reference is not None:
|
| 61 |
+
self.tx.set_fixed_parameters(self.reference.get_center_of_mass())
|
| 62 |
+
|
| 63 |
+
def transform(self, X=None, y=None):
|
| 64 |
+
"""
|
| 65 |
+
Transform an image using an Affine transform with the given
|
| 66 |
+
translation parameters. Return the transform if X=None.
|
| 67 |
+
|
| 68 |
+
Arguments
|
| 69 |
+
---------
|
| 70 |
+
X : ANTsImage
|
| 71 |
+
Image to transform
|
| 72 |
+
|
| 73 |
+
y : ANTsImage (optional)
|
| 74 |
+
Another image to transform
|
| 75 |
+
|
| 76 |
+
Returns
|
| 77 |
+
-------
|
| 78 |
+
ANTsImage if y is None, else a tuple of ANTsImage types
|
| 79 |
+
|
| 80 |
+
Examples
|
| 81 |
+
--------
|
| 82 |
+
>>> import ants
|
| 83 |
+
>>> img = ants.image_read(ants.get_data('r16'))
|
| 84 |
+
>>> tx = ants.contrib.Translate2D(translation=(10,0))
|
| 85 |
+
>>> img2_x = tx.transform(img)
|
| 86 |
+
>>> tx = ants.contrib.Translate2D(translation=(-10,0)) # other direction
|
| 87 |
+
>>> img2_x = tx.transform(img)
|
| 88 |
+
>>> tx = ants.contrib.Translate2D(translation=(0,10))
|
| 89 |
+
>>> img2_z = tx.transform(img)
|
| 90 |
+
>>> tx = ants.contrib.Translate2D(translation=(10,10))
|
| 91 |
+
>>> img2 = tx.transform(img)
|
| 92 |
+
"""
|
| 93 |
+
# convert to radians and unpack
|
| 94 |
+
translation_x, translation_y = self.translation
|
| 95 |
+
|
| 96 |
+
translation_matrix = np.array([[1, 0, translation_x], [0, 1, translation_y]])
|
| 97 |
+
self.tx.set_parameters(translation_matrix)
|
| 98 |
+
if self.lazy or X is None:
|
| 99 |
+
return self.tx
|
| 100 |
+
else:
|
| 101 |
+
if y is None:
|
| 102 |
+
return self.tx.apply_to_image(X, reference=self.reference)
|
| 103 |
+
else:
|
| 104 |
+
return (
|
| 105 |
+
self.tx.apply_to_image(X, reference=self.reference),
|
| 106 |
+
self.tx.apply_to_image(y, reference=self.reference),
|
| 107 |
+
)
|
| 108 |
+
|
| 109 |
+
|
| 110 |
+
class RandomTranslate2D(object):
|
| 111 |
+
"""
|
| 112 |
+
Apply a Translate2D transform to an image, but with the
|
| 113 |
+
parameters randomly generated from a user-specified range.
|
| 114 |
+
The range is determined by a mean (first parameter) and standard deviation
|
| 115 |
+
(second parameter) via calls to random.gauss.
|
| 116 |
+
"""
|
| 117 |
+
|
| 118 |
+
def __init__(self, translation_range, reference=None, lazy=False):
|
| 119 |
+
"""
|
| 120 |
+
Initialize a RandomTranslate2D object
|
| 121 |
+
|
| 122 |
+
Arguments
|
| 123 |
+
---------
|
| 124 |
+
translation_range : list or tuple
|
| 125 |
+
Lower and Upper bounds on rotation parameter, in degrees.
|
| 126 |
+
e.g. translation_range = (-10,10) will result in a random
|
| 127 |
+
draw of the rotation parameters between -10 and 10 degrees
|
| 128 |
+
|
| 129 |
+
reference : ANTsImage (optional but recommended)
|
| 130 |
+
image providing the reference space for the transform.
|
| 131 |
+
this will also set the transform fixed parameters.
|
| 132 |
+
|
| 133 |
+
lazy : boolean (default = False)
|
| 134 |
+
if True, calling the `transform` method only returns
|
| 135 |
+
the randomly generated transform and does not actually
|
| 136 |
+
transform the image
|
| 137 |
+
"""
|
| 138 |
+
if (not isinstance(translation_range, (list, tuple))) or (
|
| 139 |
+
len(translation_range) != 2
|
| 140 |
+
):
|
| 141 |
+
raise ValueError("shear_range argument must be list/tuple with two values!")
|
| 142 |
+
|
| 143 |
+
self.translation_range = translation_range
|
| 144 |
+
self.reference = reference
|
| 145 |
+
self.lazy = lazy
|
| 146 |
+
|
| 147 |
+
def transform(self, X=None, y=None):
|
| 148 |
+
"""
|
| 149 |
+
Transform an image using an Affine transform with
|
| 150 |
+
translation parameters randomly generated from the user-specified
|
| 151 |
+
range. Return the transform if X=None.
|
| 152 |
+
|
| 153 |
+
Arguments
|
| 154 |
+
---------
|
| 155 |
+
X : ANTsImage
|
| 156 |
+
Image to transform
|
| 157 |
+
|
| 158 |
+
y : ANTsImage (optional)
|
| 159 |
+
Another image to transform
|
| 160 |
+
|
| 161 |
+
Returns
|
| 162 |
+
-------
|
| 163 |
+
ANTsImage if y is None, else a tuple of ANTsImage types
|
| 164 |
+
|
| 165 |
+
Examples
|
| 166 |
+
--------
|
| 167 |
+
>>> import ants
|
| 168 |
+
>>> img = ants.image_read(ants.get_data('r16'))
|
| 169 |
+
>>> tx = ants.contrib.RandomShear2D(translation_range=(-10,10))
|
| 170 |
+
>>> img2 = tx.transform(img)
|
| 171 |
+
"""
|
| 172 |
+
# random draw in translation range
|
| 173 |
+
translation_x = random.gauss(
|
| 174 |
+
self.translation_range[0], self.translation_range[1]
|
| 175 |
+
)
|
| 176 |
+
translation_y = random.gauss(
|
| 177 |
+
self.translation_range[0], self.translation_range[1]
|
| 178 |
+
)
|
| 179 |
+
self.params = (translation_x, translation_y)
|
| 180 |
+
|
| 181 |
+
tx = Translate2D(
|
| 182 |
+
(translation_x, translation_y), reference=self.reference, lazy=self.lazy
|
| 183 |
+
)
|
| 184 |
+
|
| 185 |
+
return tx.transform(X, y)
|
| 186 |
+
|
| 187 |
+
|
| 188 |
+
class Shear2D(object):
|
| 189 |
+
"""
|
| 190 |
+
Create an ANTs Affine Transform with a specified shear.
|
| 191 |
+
"""
|
| 192 |
+
|
| 193 |
+
def __init__(self, shear, reference=None, lazy=False):
|
| 194 |
+
"""
|
| 195 |
+
Initialize a Shear2D object
|
| 196 |
+
|
| 197 |
+
Arguments
|
| 198 |
+
---------
|
| 199 |
+
shear : list or tuple
|
| 200 |
+
shear values for each axis, in degrees.
|
| 201 |
+
Negative values can be used for shear in the
|
| 202 |
+
other direction
|
| 203 |
+
|
| 204 |
+
reference : ANTsImage (optional but recommended)
|
| 205 |
+
image providing the reference space for the transform.
|
| 206 |
+
this will also set the transform fixed parameters.
|
| 207 |
+
|
| 208 |
+
lazy : boolean (default = False)
|
| 209 |
+
if True, calling the `transform` method only returns
|
| 210 |
+
the randomly generated transform and does not actually
|
| 211 |
+
transform the image
|
| 212 |
+
"""
|
| 213 |
+
if (not isinstance(shear, (list, tuple))) or (len(shear) != 2):
|
| 214 |
+
raise ValueError("shear argument must be list/tuple with two values!")
|
| 215 |
+
|
| 216 |
+
self.shear = shear
|
| 217 |
+
self.lazy = lazy
|
| 218 |
+
self.reference = reference
|
| 219 |
+
|
| 220 |
+
self.tx = tio.ANTsTransform(
|
| 221 |
+
precision="float", dimension=2, transform_type="AffineTransform"
|
| 222 |
+
)
|
| 223 |
+
if self.reference is not None:
|
| 224 |
+
self.tx.set_fixed_parameters(self.reference.get_center_of_mass())
|
| 225 |
+
|
| 226 |
+
def transform(self, X=None, y=None):
|
| 227 |
+
"""
|
| 228 |
+
Transform an image using an Affine transform with the given
|
| 229 |
+
shear parameters. Return the transform if X=None.
|
| 230 |
+
|
| 231 |
+
Arguments
|
| 232 |
+
---------
|
| 233 |
+
X : ANTsImage
|
| 234 |
+
Image to transform
|
| 235 |
+
|
| 236 |
+
y : ANTsImage (optional)
|
| 237 |
+
Another image to transform
|
| 238 |
+
|
| 239 |
+
Returns
|
| 240 |
+
-------
|
| 241 |
+
ANTsImage if y is None, else a tuple of ANTsImage types
|
| 242 |
+
|
| 243 |
+
Examples
|
| 244 |
+
--------
|
| 245 |
+
>>> import ants
|
| 246 |
+
>>> img = ants.image_read(ants.get_data('r16'))
|
| 247 |
+
>>> tx = ants.contrib.Shear2D(shear=(10,0,0))
|
| 248 |
+
>>> img2_x = tx.transform(img)# x axis stays same
|
| 249 |
+
>>> tx = ants.contrib.Shear2D(shear=(-10,0,0)) # other direction
|
| 250 |
+
>>> img2_x = tx.transform(img)# x axis stays same
|
| 251 |
+
>>> tx = ants.contrib.Shear2D(shear=(0,10,0))
|
| 252 |
+
>>> img2_y = tx.transform(img) # y axis stays same
|
| 253 |
+
>>> tx = ants.contrib.Shear2D(shear=(0,0,10))
|
| 254 |
+
>>> img2_z = tx.transform(img) # z axis stays same
|
| 255 |
+
>>> tx = ants.contrib.Shear2D(shear=(10,10,10))
|
| 256 |
+
>>> img2 = tx.transform(img)
|
| 257 |
+
"""
|
| 258 |
+
# convert to radians and unpack
|
| 259 |
+
shear = [math.pi / 180 * s for s in self.shear]
|
| 260 |
+
shear_x, shear_y = shear
|
| 261 |
+
|
| 262 |
+
shear_matrix = np.array([[1, shear_x, 0], [shear_y, 1, 0]])
|
| 263 |
+
self.tx.set_parameters(shear_matrix)
|
| 264 |
+
if self.lazy or X is None:
|
| 265 |
+
return self.tx
|
| 266 |
+
else:
|
| 267 |
+
if y is None:
|
| 268 |
+
return self.tx.apply_to_image(X, reference=self.reference)
|
| 269 |
+
else:
|
| 270 |
+
return (
|
| 271 |
+
self.tx.apply_to_image(X, reference=self.reference),
|
| 272 |
+
self.tx.apply_to_image(y, reference=self.reference),
|
| 273 |
+
)
|
| 274 |
+
|
| 275 |
+
|
| 276 |
+
class RandomShear2D(object):
|
| 277 |
+
"""
|
| 278 |
+
Apply a Shear2D transform to an image, but with the shear
|
| 279 |
+
parameters randomly generated from a user-specified range.
|
| 280 |
+
The range is determined by a mean (first parameter) and standard deviation
|
| 281 |
+
(second parameter) via calls to random.gauss.
|
| 282 |
+
"""
|
| 283 |
+
|
| 284 |
+
def __init__(self, shear_range, reference=None, lazy=False):
|
| 285 |
+
"""
|
| 286 |
+
Initialize a RandomShear2D object
|
| 287 |
+
|
| 288 |
+
Arguments
|
| 289 |
+
---------
|
| 290 |
+
shear_range : list or tuple
|
| 291 |
+
Lower and Upper bounds on rotation parameter, in degrees.
|
| 292 |
+
e.g. shear_range = (-10,10) will result in a random
|
| 293 |
+
draw of the rotation parameters between -10 and 10 degrees
|
| 294 |
+
|
| 295 |
+
reference : ANTsImage (optional but recommended)
|
| 296 |
+
image providing the reference space for the transform.
|
| 297 |
+
this will also set the transform fixed parameters.
|
| 298 |
+
|
| 299 |
+
lazy : boolean (default = False)
|
| 300 |
+
if True, calling the `transform` method only returns
|
| 301 |
+
the randomly generated transform and does not actually
|
| 302 |
+
transform the image
|
| 303 |
+
"""
|
| 304 |
+
if (not isinstance(shear_range, (list, tuple))) or (len(shear_range) != 2):
|
| 305 |
+
raise ValueError("shear_range argument must be list/tuple with two values!")
|
| 306 |
+
|
| 307 |
+
self.shear_range = shear_range
|
| 308 |
+
self.reference = reference
|
| 309 |
+
self.lazy = lazy
|
| 310 |
+
|
| 311 |
+
def transform(self, X=None, y=None):
|
| 312 |
+
"""
|
| 313 |
+
Transform an image using an Affine transform with
|
| 314 |
+
shear parameters randomly generated from the user-specified
|
| 315 |
+
range. Return the transform if X=None.
|
| 316 |
+
|
| 317 |
+
Arguments
|
| 318 |
+
---------
|
| 319 |
+
X : ANTsImage
|
| 320 |
+
Image to transform
|
| 321 |
+
|
| 322 |
+
y : ANTsImage (optional)
|
| 323 |
+
Another image to transform
|
| 324 |
+
|
| 325 |
+
Returns
|
| 326 |
+
-------
|
| 327 |
+
ANTsImage if y is None, else a tuple of ANTsImage types
|
| 328 |
+
|
| 329 |
+
Examples
|
| 330 |
+
--------
|
| 331 |
+
>>> import ants
|
| 332 |
+
>>> img = ants.image_read(ants.get_data('r16'))
|
| 333 |
+
>>> tx = ants.contrib.RandomShear2D(shear_range=(-10,10))
|
| 334 |
+
>>> img2 = tx.transform(img)
|
| 335 |
+
"""
|
| 336 |
+
# random draw in shear range
|
| 337 |
+
shear_x = random.gauss(self.shear_range[0], self.shear_range[1])
|
| 338 |
+
shear_y = random.gauss(self.shear_range[0], self.shear_range[1])
|
| 339 |
+
self.params = (shear_x, shear_y)
|
| 340 |
+
|
| 341 |
+
tx = Shear2D((shear_x, shear_y), reference=self.reference, lazy=self.lazy)
|
| 342 |
+
|
| 343 |
+
return tx.transform(X, y)
|
| 344 |
+
|
| 345 |
+
|
| 346 |
+
class Rotate2D(object):
|
| 347 |
+
"""
|
| 348 |
+
Create an ANTs Affine Transform with a specified level
|
| 349 |
+
of rotation.
|
| 350 |
+
"""
|
| 351 |
+
|
| 352 |
+
def __init__(self, rotation, reference=None, lazy=False):
|
| 353 |
+
"""
|
| 354 |
+
Initialize a Rotate2D object
|
| 355 |
+
|
| 356 |
+
Arguments
|
| 357 |
+
---------
|
| 358 |
+
rotation : scalar
|
| 359 |
+
rotation value in degrees.
|
| 360 |
+
Negative values can be used for rotation in the
|
| 361 |
+
other direction
|
| 362 |
+
|
| 363 |
+
reference : ANTsImage (optional but recommended)
|
| 364 |
+
image providing the reference space for the transform.
|
| 365 |
+
this will also set the transform fixed parameters.
|
| 366 |
+
|
| 367 |
+
lazy : boolean (default = False)
|
| 368 |
+
if True, calling the `transform` method only returns
|
| 369 |
+
the randomly generated transform and does not actually
|
| 370 |
+
transform the image
|
| 371 |
+
"""
|
| 372 |
+
self.rotation = rotation
|
| 373 |
+
self.lazy = lazy
|
| 374 |
+
self.reference = reference
|
| 375 |
+
|
| 376 |
+
self.tx = tio.ANTsTransform(
|
| 377 |
+
precision="float", dimension=2, transform_type="AffineTransform"
|
| 378 |
+
)
|
| 379 |
+
if self.reference is not None:
|
| 380 |
+
self.tx.set_fixed_parameters(self.reference.get_center_of_mass())
|
| 381 |
+
|
| 382 |
+
def transform(self, X=None, y=None):
|
| 383 |
+
"""
|
| 384 |
+
Transform an image using an Affine transform with the given
|
| 385 |
+
rotation parameters. Return the transform if X=None.
|
| 386 |
+
|
| 387 |
+
Arguments
|
| 388 |
+
---------
|
| 389 |
+
X : ANTsImage
|
| 390 |
+
Image to transform
|
| 391 |
+
|
| 392 |
+
y : ANTsImage (optional)
|
| 393 |
+
Another image to transform
|
| 394 |
+
|
| 395 |
+
Returns
|
| 396 |
+
-------
|
| 397 |
+
ANTsImage if y is None, else a tuple of ANTsImage types
|
| 398 |
+
|
| 399 |
+
Examples
|
| 400 |
+
--------
|
| 401 |
+
>>> import ants
|
| 402 |
+
>>> img = ants.image_read(ants.get_data('r16'))
|
| 403 |
+
>>> tx = ants.contrib.Rotate2D(rotation=(10,-5,12))
|
| 404 |
+
>>> img2 = tx.transform(img)
|
| 405 |
+
"""
|
| 406 |
+
# unpack zoom range
|
| 407 |
+
rotation = self.rotation
|
| 408 |
+
|
| 409 |
+
# Rotation about X axis
|
| 410 |
+
theta = math.pi / 180 * rotation
|
| 411 |
+
rotation_matrix = np.array(
|
| 412 |
+
[[np.cos(theta), -np.sin(theta), 0], [np.sin(theta), np.cos(theta), 0]]
|
| 413 |
+
)
|
| 414 |
+
|
| 415 |
+
self.tx.set_parameters(rotation_matrix)
|
| 416 |
+
if self.lazy or X is None:
|
| 417 |
+
return self.tx
|
| 418 |
+
else:
|
| 419 |
+
if y is None:
|
| 420 |
+
return self.tx.apply_to_image(X, reference=self.reference)
|
| 421 |
+
else:
|
| 422 |
+
return (
|
| 423 |
+
self.tx.apply_to_image(X, reference=self.reference),
|
| 424 |
+
self.tx.apply_to_image(y, reference=self.reference),
|
| 425 |
+
)
|
| 426 |
+
|
| 427 |
+
|
| 428 |
+
class RandomRotate2D(object):
|
| 429 |
+
"""
|
| 430 |
+
Apply a Rotated2D transform to an image, but with the zoom
|
| 431 |
+
parameters randomly generated from a user-specified range.
|
| 432 |
+
The range is determined by a mean (first parameter) and standard deviation
|
| 433 |
+
(second parameter) via calls to random.gauss.
|
| 434 |
+
"""
|
| 435 |
+
|
| 436 |
+
def __init__(self, rotation_range, reference=None, lazy=False):
|
| 437 |
+
"""
|
| 438 |
+
Initialize a RandomRotate2D object
|
| 439 |
+
|
| 440 |
+
Arguments
|
| 441 |
+
---------
|
| 442 |
+
rotation_range : list or tuple
|
| 443 |
+
Lower and Upper bounds on rotation parameter, in degrees.
|
| 444 |
+
e.g. rotation_range = (-10,10) will result in a random
|
| 445 |
+
draw of the rotation parameters between -10 and 10 degrees
|
| 446 |
+
|
| 447 |
+
reference : ANTsImage (optional but recommended)
|
| 448 |
+
image providing the reference space for the transform.
|
| 449 |
+
this will also set the transform fixed parameters.
|
| 450 |
+
|
| 451 |
+
lazy : boolean (default = False)
|
| 452 |
+
if True, calling the `transform` method only returns
|
| 453 |
+
the randomly generated transform and does not actually
|
| 454 |
+
transform the image
|
| 455 |
+
"""
|
| 456 |
+
if (not isinstance(rotation_range, (list, tuple))) or (
|
| 457 |
+
len(rotation_range) != 2
|
| 458 |
+
):
|
| 459 |
+
raise ValueError(
|
| 460 |
+
"rotation_range argument must be list/tuple with two values!"
|
| 461 |
+
)
|
| 462 |
+
|
| 463 |
+
self.rotation_range = rotation_range
|
| 464 |
+
self.reference = reference
|
| 465 |
+
self.lazy = lazy
|
| 466 |
+
|
| 467 |
+
def transform(self, X=None, y=None):
|
| 468 |
+
"""
|
| 469 |
+
Transform an image using an Affine transform with
|
| 470 |
+
rotation parameters randomly generated from the user-specified
|
| 471 |
+
range. Return the transform if X=None.
|
| 472 |
+
|
| 473 |
+
Arguments
|
| 474 |
+
---------
|
| 475 |
+
X : ANTsImage
|
| 476 |
+
Image to transform
|
| 477 |
+
|
| 478 |
+
y : ANTsImage (optional)
|
| 479 |
+
Another image to transform
|
| 480 |
+
|
| 481 |
+
Returns
|
| 482 |
+
-------
|
| 483 |
+
ANTsImage if y is None, else a tuple of ANTsImage types
|
| 484 |
+
|
| 485 |
+
Examples
|
| 486 |
+
--------
|
| 487 |
+
>>> import ants
|
| 488 |
+
>>> img = ants.image_read(ants.get_data('r16'))
|
| 489 |
+
>>> tx = ants.contrib.RandomRotate2D(rotation_range=(-10,10))
|
| 490 |
+
>>> img2 = tx.transform(img)
|
| 491 |
+
"""
|
| 492 |
+
# random draw in rotation range
|
| 493 |
+
rotation = random.gauss(self.rotation_range[0], self.rotation_range[1])
|
| 494 |
+
self.params = rotation
|
| 495 |
+
|
| 496 |
+
tx = Rotate2D(rotation, reference=self.reference, lazy=self.lazy)
|
| 497 |
+
|
| 498 |
+
return tx.transform(X, y)
|
| 499 |
+
|
| 500 |
+
|
| 501 |
+
class Zoom2D(object):
|
| 502 |
+
"""
|
| 503 |
+
Create an ANTs Affine Transform with a specified level
|
| 504 |
+
of zoom. Any value greater than 1 implies a "zoom-out" and anything
|
| 505 |
+
less than 1 implies a "zoom-in".
|
| 506 |
+
"""
|
| 507 |
+
|
| 508 |
+
def __init__(self, zoom, reference=None, lazy=False):
|
| 509 |
+
"""
|
| 510 |
+
Initialize a Zoom2D object
|
| 511 |
+
|
| 512 |
+
Arguments
|
| 513 |
+
---------
|
| 514 |
+
zoom_range : list or tuple
|
| 515 |
+
Lower and Upper bounds on zoom parameter.
|
| 516 |
+
e.g. zoom_range = (0.7,0.9) will result in a random
|
| 517 |
+
draw of the zoom parameters between 0.7 and 0.9
|
| 518 |
+
|
| 519 |
+
reference : ANTsImage (optional but recommended)
|
| 520 |
+
image providing the reference space for the transform.
|
| 521 |
+
this will also set the transform fixed parameters.
|
| 522 |
+
|
| 523 |
+
lazy : boolean (default = False)
|
| 524 |
+
if True, calling the `transform` method only returns
|
| 525 |
+
the randomly generated transform and does not actually
|
| 526 |
+
transform the image
|
| 527 |
+
"""
|
| 528 |
+
if (not isinstance(zoom, (list, tuple))) or (len(zoom) != 2):
|
| 529 |
+
raise ValueError("zoom_range argument must be list/tuple with two values!")
|
| 530 |
+
|
| 531 |
+
self.zoom = zoom
|
| 532 |
+
self.lazy = lazy
|
| 533 |
+
self.reference = reference
|
| 534 |
+
|
| 535 |
+
self.tx = tio.ANTsTransform(
|
| 536 |
+
precision="float", dimension=2, transform_type="AffineTransform"
|
| 537 |
+
)
|
| 538 |
+
if self.reference is not None:
|
| 539 |
+
self.tx.set_fixed_parameters(self.reference.get_center_of_mass())
|
| 540 |
+
|
| 541 |
+
def transform(self, X=None, y=None):
|
| 542 |
+
"""
|
| 543 |
+
Transform an image using an Affine transform with the given
|
| 544 |
+
zoom parameters. Return the transform if X=None.
|
| 545 |
+
|
| 546 |
+
Arguments
|
| 547 |
+
---------
|
| 548 |
+
X : ANTsImage
|
| 549 |
+
Image to transform
|
| 550 |
+
|
| 551 |
+
y : ANTsImage (optional)
|
| 552 |
+
Another image to transform
|
| 553 |
+
|
| 554 |
+
Returns
|
| 555 |
+
-------
|
| 556 |
+
ANTsImage if y is None, else a tuple of ANTsImage types
|
| 557 |
+
|
| 558 |
+
Examples
|
| 559 |
+
--------
|
| 560 |
+
>>> import ants
|
| 561 |
+
>>> img = ants.image_read(ants.get_data('r16'))
|
| 562 |
+
>>> tx = ants.contrib.Zoom2D(zoom=(0.8,0.8,0.8))
|
| 563 |
+
>>> img2 = tx.transform(img)
|
| 564 |
+
"""
|
| 565 |
+
# unpack zoom range
|
| 566 |
+
zoom_x, zoom_y = self.zoom
|
| 567 |
+
|
| 568 |
+
self.params = (zoom_x, zoom_y)
|
| 569 |
+
zoom_matrix = np.array([[zoom_x, 0, 0], [0, zoom_y, 0]])
|
| 570 |
+
self.tx.set_parameters(zoom_matrix)
|
| 571 |
+
if self.lazy or X is None:
|
| 572 |
+
return self.tx
|
| 573 |
+
else:
|
| 574 |
+
if y is None:
|
| 575 |
+
return self.tx.apply_to_image(X, reference=self.reference)
|
| 576 |
+
else:
|
| 577 |
+
return (
|
| 578 |
+
self.tx.apply_to_image(X, reference=self.reference),
|
| 579 |
+
self.tx.apply_to_image(y, reference=self.reference),
|
| 580 |
+
)
|
| 581 |
+
|
| 582 |
+
|
| 583 |
+
class RandomZoom2D(object):
|
| 584 |
+
"""
|
| 585 |
+
Apply a Zoom2D transform to an image, but with the zoom
|
| 586 |
+
parameters randomly generated from a user-specified range.
|
| 587 |
+
The range is determined by a mean (first parameter) and standard deviation
|
| 588 |
+
(second parameter) via calls to random.gauss.
|
| 589 |
+
"""
|
| 590 |
+
|
| 591 |
+
def __init__(self, zoom_range, reference=None, lazy=False):
|
| 592 |
+
"""
|
| 593 |
+
Initialize a RandomZoom2D object
|
| 594 |
+
|
| 595 |
+
Arguments
|
| 596 |
+
---------
|
| 597 |
+
zoom_range : list or tuple
|
| 598 |
+
Lower and Upper bounds on zoom parameter.
|
| 599 |
+
e.g. zoom_range = (0.7,0.9) will result in a random
|
| 600 |
+
draw of the zoom parameters between 0.7 and 0.9
|
| 601 |
+
|
| 602 |
+
reference : ANTsImage (optional but recommended)
|
| 603 |
+
image providing the reference space for the transform.
|
| 604 |
+
this will also set the transform fixed parameters.
|
| 605 |
+
|
| 606 |
+
lazy : boolean (default = False)
|
| 607 |
+
if True, calling the `transform` method only returns
|
| 608 |
+
the randomly generated transform and does not actually
|
| 609 |
+
transform the image
|
| 610 |
+
"""
|
| 611 |
+
if (not isinstance(zoom_range, (list, tuple))) or (len(zoom_range) != 2):
|
| 612 |
+
raise ValueError("zoom_range argument must be list/tuple with two values!")
|
| 613 |
+
|
| 614 |
+
self.zoom_range = zoom_range
|
| 615 |
+
self.reference = reference
|
| 616 |
+
self.lazy = lazy
|
| 617 |
+
|
| 618 |
+
def transform(self, X=None, y=None):
|
| 619 |
+
"""
|
| 620 |
+
Transform an image using an Affine transform with
|
| 621 |
+
zoom parameters randomly generated from the user-specified
|
| 622 |
+
range. Return the transform if X=None.
|
| 623 |
+
|
| 624 |
+
Arguments
|
| 625 |
+
---------
|
| 626 |
+
X : ANTsImage
|
| 627 |
+
Image to transform
|
| 628 |
+
|
| 629 |
+
y : ANTsImage (optional)
|
| 630 |
+
Another image to transform
|
| 631 |
+
|
| 632 |
+
Returns
|
| 633 |
+
-------
|
| 634 |
+
ANTsImage if y is None, else a tuple of ANTsImage types
|
| 635 |
+
|
| 636 |
+
Examples
|
| 637 |
+
--------
|
| 638 |
+
>>> import ants
|
| 639 |
+
>>> img = ants.image_read(ants.get_data('r16'))
|
| 640 |
+
>>> tx = ants.contrib.RandomZoom2D(zoom_range=(0.8,0.9))
|
| 641 |
+
>>> img2 = tx.transform(img)
|
| 642 |
+
"""
|
| 643 |
+
# random draw in zoom range
|
| 644 |
+
zoom_x = np.exp(
|
| 645 |
+
random.gauss(np.log(self.zoom_range[0]), np.log(self.zoom_range[1]))
|
| 646 |
+
)
|
| 647 |
+
zoom_y = np.exp(
|
| 648 |
+
random.gauss(np.log(self.zoom_range[0]), np.log(self.zoom_range[1]))
|
| 649 |
+
)
|
| 650 |
+
self.params = (zoom_x, zoom_y)
|
| 651 |
+
|
| 652 |
+
tx = Zoom2D((zoom_x, zoom_y), reference=self.reference, lazy=self.lazy)
|
| 653 |
+
|
| 654 |
+
return tx.transform(X, y)
|
MindEyeV2/antspy/ants/contrib/sampling/affine3d.py
ADDED
|
@@ -0,0 +1,802 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
Affine transforms
|
| 3 |
+
|
| 4 |
+
See http://www.cs.cornell.edu/courses/cs4620/2010fa/lectures/03transforms3d.pdf
|
| 5 |
+
"""
|
| 6 |
+
|
| 7 |
+
__all__ = [
|
| 8 |
+
"Zoom3D",
|
| 9 |
+
"RandomZoom3D",
|
| 10 |
+
"Rotate3D",
|
| 11 |
+
"RandomRotate3D",
|
| 12 |
+
"Shear3D",
|
| 13 |
+
"RandomShear3D",
|
| 14 |
+
"Translate3D",
|
| 15 |
+
"RandomTranslate3D",
|
| 16 |
+
"Affine3D",
|
| 17 |
+
]
|
| 18 |
+
|
| 19 |
+
import random
|
| 20 |
+
import math
|
| 21 |
+
import numpy as np
|
| 22 |
+
|
| 23 |
+
from ...core import ants_transform as tio
|
| 24 |
+
|
| 25 |
+
class Affine3D(object):
|
| 26 |
+
"""
|
| 27 |
+
Create a specified ANTs Affine Transform
|
| 28 |
+
"""
|
| 29 |
+
|
| 30 |
+
def __init__(self, transformation, reference=None, lazy=False):
|
| 31 |
+
"""
|
| 32 |
+
Initialize a Affine object
|
| 33 |
+
|
| 34 |
+
Arguments
|
| 35 |
+
---------
|
| 36 |
+
transformation : array
|
| 37 |
+
affine transformation array (3x4)
|
| 38 |
+
|
| 39 |
+
reference : ANTsImage (optional but recommended)
|
| 40 |
+
image providing the reference space for the transform.
|
| 41 |
+
this will also set the transform fixed parameters.
|
| 42 |
+
|
| 43 |
+
lazy : boolean (default = False)
|
| 44 |
+
if True, calling the `transform` method only returns
|
| 45 |
+
the randomly generated transform and does not actually
|
| 46 |
+
transform the image
|
| 47 |
+
"""
|
| 48 |
+
if (not isinstance(transformation, np.ndarray) or transformation.shape != (3,4)):
|
| 49 |
+
raise ValueError(
|
| 50 |
+
"transformation argument must be 3x4 Numpy array!"
|
| 51 |
+
)
|
| 52 |
+
|
| 53 |
+
self.transformation = transformation
|
| 54 |
+
self.lazy = lazy
|
| 55 |
+
self.reference = reference
|
| 56 |
+
|
| 57 |
+
self.tx = tio.ANTsTransform(
|
| 58 |
+
precision="float", dimension=3, transform_type="AffineTransform"
|
| 59 |
+
)
|
| 60 |
+
if self.reference is not None:
|
| 61 |
+
self.tx.set_fixed_parameters(self.reference.get_center_of_mass())
|
| 62 |
+
|
| 63 |
+
def transform(self, X=None, y=None):
|
| 64 |
+
"""
|
| 65 |
+
Transform an image using an Affine transform with the given
|
| 66 |
+
translation parameters. Return the transform if X=None.
|
| 67 |
+
|
| 68 |
+
Arguments
|
| 69 |
+
---------
|
| 70 |
+
X : ANTsImage
|
| 71 |
+
Image to transform
|
| 72 |
+
|
| 73 |
+
y : ANTsImage (optional)
|
| 74 |
+
Another image to transform
|
| 75 |
+
|
| 76 |
+
Returns
|
| 77 |
+
-------
|
| 78 |
+
ANTsImage if y is None, else a tuple of ANTsImage types
|
| 79 |
+
|
| 80 |
+
Examples
|
| 81 |
+
--------
|
| 82 |
+
>>> import ants
|
| 83 |
+
>>> img = ants.image_read(ants.get_data('ch2'))
|
| 84 |
+
>>> tx = ants.contrib.Affine3D(transformation=np.array([[1, 0, 0, dx], [0, 1, 0, dy],[0, 0, 1, dz]])
|
| 85 |
+
>>> img2_x = tx.transform(img)# image translated by (dx, dy, dz)
|
| 86 |
+
"""
|
| 87 |
+
# unpack
|
| 88 |
+
|
| 89 |
+
transformation_matrix = self.transformation
|
| 90 |
+
|
| 91 |
+
|
| 92 |
+
self.tx.set_parameters(transformation_matrix)
|
| 93 |
+
if self.lazy or X is None:
|
| 94 |
+
return self.tx
|
| 95 |
+
else:
|
| 96 |
+
if y is None:
|
| 97 |
+
return self.tx.apply_to_image(X, reference=self.reference)
|
| 98 |
+
else:
|
| 99 |
+
return (
|
| 100 |
+
self.tx.apply_to_image(X, reference=self.reference),
|
| 101 |
+
self.tx.apply_to_image(y, reference=self.reference),
|
| 102 |
+
)
|
| 103 |
+
|
| 104 |
+
|
| 105 |
+
class Translate3D(object):
|
| 106 |
+
"""
|
| 107 |
+
Create an ANTs Affine Transform with a specified translation.
|
| 108 |
+
"""
|
| 109 |
+
|
| 110 |
+
def __init__(self, translation, reference=None, lazy=False):
|
| 111 |
+
"""
|
| 112 |
+
Initialize a Translate3D object
|
| 113 |
+
|
| 114 |
+
Arguments
|
| 115 |
+
---------
|
| 116 |
+
translation : list or tuple
|
| 117 |
+
translation values for each axis, in degrees.
|
| 118 |
+
Negative values can be used for translation in the
|
| 119 |
+
other direction
|
| 120 |
+
|
| 121 |
+
reference : ANTsImage (optional but recommended)
|
| 122 |
+
image providing the reference space for the transform.
|
| 123 |
+
this will also set the transform fixed parameters.
|
| 124 |
+
|
| 125 |
+
lazy : boolean (default = False)
|
| 126 |
+
if True, calling the `transform` method only returns
|
| 127 |
+
the randomly generated transform and does not actually
|
| 128 |
+
transform the image
|
| 129 |
+
"""
|
| 130 |
+
if (not isinstance(translation, (list, tuple))) or (len(translation) != 3):
|
| 131 |
+
raise ValueError(
|
| 132 |
+
"translation argument must be list/tuple with three values!"
|
| 133 |
+
)
|
| 134 |
+
|
| 135 |
+
self.translation = translation
|
| 136 |
+
self.lazy = lazy
|
| 137 |
+
self.reference = reference
|
| 138 |
+
|
| 139 |
+
self.tx = tio.ANTsTransform(
|
| 140 |
+
precision="float", dimension=3, transform_type="AffineTransform"
|
| 141 |
+
)
|
| 142 |
+
if self.reference is not None:
|
| 143 |
+
self.tx.set_fixed_parameters(self.reference.get_center_of_mass())
|
| 144 |
+
|
| 145 |
+
def transform(self, X=None, y=None):
|
| 146 |
+
"""
|
| 147 |
+
Transform an image using an Affine transform with the given
|
| 148 |
+
translation parameters. Return the transform if X=None.
|
| 149 |
+
|
| 150 |
+
Arguments
|
| 151 |
+
---------
|
| 152 |
+
X : ANTsImage
|
| 153 |
+
Image to transform
|
| 154 |
+
|
| 155 |
+
y : ANTsImage (optional)
|
| 156 |
+
Another image to transform
|
| 157 |
+
|
| 158 |
+
Returns
|
| 159 |
+
-------
|
| 160 |
+
ANTsImage if y is None, else a tuple of ANTsImage types
|
| 161 |
+
|
| 162 |
+
Examples
|
| 163 |
+
--------
|
| 164 |
+
>>> import ants
|
| 165 |
+
>>> img = ants.image_read(ants.get_data('ch2'))
|
| 166 |
+
>>> tx = ants.contrib.Translate3D(translation=(10,0,0))
|
| 167 |
+
>>> img2_x = tx.transform(img)# x axis stays same
|
| 168 |
+
>>> tx = ants.contrib.Translate3D(translation=(-10,0,0)) # other direction
|
| 169 |
+
>>> img2_x = tx.transform(img)# x axis stays same
|
| 170 |
+
>>> tx = ants.contrib.Translate3D(translation=(0,10,0))
|
| 171 |
+
>>> img2_y = tx.transform(img) # y axis stays same
|
| 172 |
+
>>> tx = ants.contrib.Translate3D(translation=(0,0,10))
|
| 173 |
+
>>> img2_z = tx.transform(img) # z axis stays same
|
| 174 |
+
>>> tx = ants.contrib.Translate3D(translation=(10,10,10))
|
| 175 |
+
>>> img2 = tx.transform(img)
|
| 176 |
+
"""
|
| 177 |
+
# unpack
|
| 178 |
+
translation_x, translation_y, translation_z = self.translation
|
| 179 |
+
|
| 180 |
+
translation_matrix = np.array(
|
| 181 |
+
[
|
| 182 |
+
[1, 0, 0, translation_x],
|
| 183 |
+
[0, 1, 0, translation_y],
|
| 184 |
+
[0, 0, 1, translation_z],
|
| 185 |
+
]
|
| 186 |
+
)
|
| 187 |
+
self.tx.set_parameters(translation_matrix)
|
| 188 |
+
if self.lazy or X is None:
|
| 189 |
+
return self.tx
|
| 190 |
+
else:
|
| 191 |
+
if y is None:
|
| 192 |
+
return self.tx.apply_to_image(X, reference=self.reference)
|
| 193 |
+
else:
|
| 194 |
+
return (
|
| 195 |
+
self.tx.apply_to_image(X, reference=self.reference),
|
| 196 |
+
self.tx.apply_to_image(y, reference=self.reference),
|
| 197 |
+
)
|
| 198 |
+
|
| 199 |
+
|
| 200 |
+
class RandomTranslate3D(object):
|
| 201 |
+
"""
|
| 202 |
+
Apply a Translate3D transform to an image, but with the shear
|
| 203 |
+
parameters randomly generated from a user-specified range.
|
| 204 |
+
The range is determined by a mean (first parameter) and standard deviation
|
| 205 |
+
(second parameter) via calls to random.gauss.
|
| 206 |
+
"""
|
| 207 |
+
|
| 208 |
+
def __init__(self, translation_range, reference=None, lazy=False):
|
| 209 |
+
"""
|
| 210 |
+
Initialize a RandomTranslate3D object
|
| 211 |
+
|
| 212 |
+
Arguments
|
| 213 |
+
---------
|
| 214 |
+
translation_range : list or tuple
|
| 215 |
+
Lower and Upper bounds on rotation parameter, in degrees.
|
| 216 |
+
e.g. translation_range = (-10,10) will result in a random
|
| 217 |
+
draw of the rotation parameters between -10 and 10 degrees
|
| 218 |
+
|
| 219 |
+
reference : ANTsImage (optional but recommended)
|
| 220 |
+
image providing the reference space for the transform.
|
| 221 |
+
this will also set the transform fixed parameters.
|
| 222 |
+
|
| 223 |
+
lazy : boolean (default = False)
|
| 224 |
+
if True, calling the `transform` method only returns
|
| 225 |
+
the randomly generated transform and does not actually
|
| 226 |
+
transform the image
|
| 227 |
+
"""
|
| 228 |
+
if (not isinstance(translation_range, (list, tuple))) or (
|
| 229 |
+
len(translation_range) != 2
|
| 230 |
+
):
|
| 231 |
+
raise ValueError("shear_range argument must be list/tuple with two values!")
|
| 232 |
+
|
| 233 |
+
self.translation_range = translation_range
|
| 234 |
+
self.reference = reference
|
| 235 |
+
self.lazy = lazy
|
| 236 |
+
|
| 237 |
+
def transform(self, X=None, y=None):
|
| 238 |
+
"""
|
| 239 |
+
Transform an image using an Affine transform with
|
| 240 |
+
translation parameters randomly generated from the user-specified
|
| 241 |
+
range. Return the transform if X=None.
|
| 242 |
+
|
| 243 |
+
Arguments
|
| 244 |
+
---------
|
| 245 |
+
X : ANTsImage
|
| 246 |
+
Image to transform
|
| 247 |
+
|
| 248 |
+
y : ANTsImage (optional)
|
| 249 |
+
Another image to transform
|
| 250 |
+
|
| 251 |
+
Returns
|
| 252 |
+
-------
|
| 253 |
+
ANTsImage if y is None, else a tuple of ANTsImage types
|
| 254 |
+
|
| 255 |
+
Examples
|
| 256 |
+
--------
|
| 257 |
+
>>> import ants
|
| 258 |
+
>>> img = ants.image_read(ants.get_data('ch2'))
|
| 259 |
+
>>> tx = ants.contrib.RandomShear3D(translation_range=(-10,10))
|
| 260 |
+
>>> img2 = tx.transform(img)
|
| 261 |
+
"""
|
| 262 |
+
# random draw in translation range
|
| 263 |
+
translation_x = random.gauss(
|
| 264 |
+
self.translation_range[0], self.translation_range[1]
|
| 265 |
+
)
|
| 266 |
+
translation_y = random.gauss(
|
| 267 |
+
self.translation_range[0], self.translation_range[1]
|
| 268 |
+
)
|
| 269 |
+
translation_z = random.gauss(
|
| 270 |
+
self.translation_range[0], self.translation_range[1]
|
| 271 |
+
)
|
| 272 |
+
self.params = (translation_x, translation_y, translation_z)
|
| 273 |
+
|
| 274 |
+
tx = Translate3D(
|
| 275 |
+
(translation_x, translation_y, translation_z),
|
| 276 |
+
reference=self.reference,
|
| 277 |
+
lazy=self.lazy,
|
| 278 |
+
)
|
| 279 |
+
|
| 280 |
+
return tx.transform(X, y)
|
| 281 |
+
|
| 282 |
+
|
| 283 |
+
class Shear3D(object):
|
| 284 |
+
"""
|
| 285 |
+
Create an ANTs Affine Transform with a specified shear.
|
| 286 |
+
"""
|
| 287 |
+
|
| 288 |
+
def __init__(self, shear, reference=None, lazy=False):
|
| 289 |
+
"""
|
| 290 |
+
Initialize a Shear3D object
|
| 291 |
+
|
| 292 |
+
Arguments
|
| 293 |
+
---------
|
| 294 |
+
shear : list or tuple
|
| 295 |
+
shear values for each axis, in degrees.
|
| 296 |
+
Negative values can be used for shear in the
|
| 297 |
+
other direction
|
| 298 |
+
|
| 299 |
+
reference : ANTsImage (optional but recommended)
|
| 300 |
+
image providing the reference space for the transform.
|
| 301 |
+
this will also set the transform fixed parameters.
|
| 302 |
+
|
| 303 |
+
lazy : boolean (default = False)
|
| 304 |
+
if True, calling the `transform` method only returns
|
| 305 |
+
the randomly generated transform and does not actually
|
| 306 |
+
transform the image
|
| 307 |
+
"""
|
| 308 |
+
if (not isinstance(shear, (list, tuple))) or (len(shear) != 3):
|
| 309 |
+
raise ValueError("shear argument must be list/tuple with three values!")
|
| 310 |
+
|
| 311 |
+
self.shear = shear
|
| 312 |
+
self.lazy = lazy
|
| 313 |
+
self.reference = reference
|
| 314 |
+
|
| 315 |
+
self.tx = tio.ANTsTransform(
|
| 316 |
+
precision="float", dimension=3, transform_type="AffineTransform"
|
| 317 |
+
)
|
| 318 |
+
if self.reference is not None:
|
| 319 |
+
self.tx.set_fixed_parameters(self.reference.get_center_of_mass())
|
| 320 |
+
|
| 321 |
+
def transform(self, X=None, y=None):
|
| 322 |
+
"""
|
| 323 |
+
Transform an image using an Affine transform with the given
|
| 324 |
+
shear parameters. Return the transform if X=None.
|
| 325 |
+
|
| 326 |
+
Arguments
|
| 327 |
+
---------
|
| 328 |
+
X : ANTsImage
|
| 329 |
+
Image to transform
|
| 330 |
+
|
| 331 |
+
y : ANTsImage (optional)
|
| 332 |
+
Another image to transform
|
| 333 |
+
|
| 334 |
+
Returns
|
| 335 |
+
-------
|
| 336 |
+
ANTsImage if y is None, else a tuple of ANTsImage types
|
| 337 |
+
|
| 338 |
+
Examples
|
| 339 |
+
--------
|
| 340 |
+
>>> import ants
|
| 341 |
+
>>> img = ants.image_read(ants.get_data('ch2'))
|
| 342 |
+
>>> tx = ants.contrib.Shear3D(shear=(10,0,0))
|
| 343 |
+
>>> img2_x = tx.transform(img)# x axis stays same
|
| 344 |
+
>>> tx = ants.contrib.Shear3D(shear=(-10,0,0)) # other direction
|
| 345 |
+
>>> img2_x = tx.transform(img)# x axis stays same
|
| 346 |
+
>>> tx = ants.contrib.Shear3D(shear=(0,10,0))
|
| 347 |
+
>>> img2_y = tx.transform(img) # y axis stays same
|
| 348 |
+
>>> tx = ants.contrib.Shear3D(shear=(0,0,10))
|
| 349 |
+
>>> img2_z = tx.transform(img) # z axis stays same
|
| 350 |
+
>>> tx = ants.contrib.Shear3D(shear=(10,10,10))
|
| 351 |
+
>>> img2 = tx.transform(img)
|
| 352 |
+
"""
|
| 353 |
+
# convert to radians and unpack
|
| 354 |
+
shear = [math.pi / 180 * s for s in self.shear]
|
| 355 |
+
shear_x, shear_y, shear_z = shear
|
| 356 |
+
|
| 357 |
+
shear_matrix = np.array(
|
| 358 |
+
[
|
| 359 |
+
[1, shear_x, shear_x, 0],
|
| 360 |
+
[shear_y, 1, shear_y, 0],
|
| 361 |
+
[shear_z, shear_z, 1, 0],
|
| 362 |
+
]
|
| 363 |
+
)
|
| 364 |
+
self.tx.set_parameters(shear_matrix)
|
| 365 |
+
if self.lazy or X is None:
|
| 366 |
+
return self.tx
|
| 367 |
+
else:
|
| 368 |
+
if y is None:
|
| 369 |
+
return self.tx.apply_to_image(X, reference=self.reference)
|
| 370 |
+
else:
|
| 371 |
+
return (
|
| 372 |
+
self.tx.apply_to_image(X, reference=self.reference),
|
| 373 |
+
self.tx.apply_to_image(y, reference=self.reference),
|
| 374 |
+
)
|
| 375 |
+
|
| 376 |
+
|
| 377 |
+
class RandomShear3D(object):
|
| 378 |
+
"""
|
| 379 |
+
Apply a Shear3D transform to an image, but with the shear
|
| 380 |
+
parameters randomly generated from a user-specified range.
|
| 381 |
+
The range is determined by a mean (first parameter) and standard deviation
|
| 382 |
+
(second parameter) via calls to random.gauss.
|
| 383 |
+
"""
|
| 384 |
+
|
| 385 |
+
def __init__(self, shear_range, reference=None, lazy=False):
|
| 386 |
+
"""
|
| 387 |
+
Initialize a RandomShear3D object
|
| 388 |
+
|
| 389 |
+
Arguments
|
| 390 |
+
---------
|
| 391 |
+
shear_range : list or tuple
|
| 392 |
+
Lower and Upper bounds on rotation parameter, in degrees.
|
| 393 |
+
e.g. shear_range = (-10,10) will result in a random
|
| 394 |
+
draw of the rotation parameters between -10 and 10 degrees
|
| 395 |
+
|
| 396 |
+
reference : ANTsImage (optional but recommended)
|
| 397 |
+
image providing the reference space for the transform.
|
| 398 |
+
this will also set the transform fixed parameters.
|
| 399 |
+
|
| 400 |
+
lazy : boolean (default = False)
|
| 401 |
+
if True, calling the `transform` method only returns
|
| 402 |
+
the randomly generated transform and does not actually
|
| 403 |
+
transform the image
|
| 404 |
+
"""
|
| 405 |
+
if (not isinstance(shear_range, (list, tuple))) or (len(shear_range) != 2):
|
| 406 |
+
raise ValueError("shear_range argument must be list/tuple with two values!")
|
| 407 |
+
|
| 408 |
+
self.shear_range = shear_range
|
| 409 |
+
self.reference = reference
|
| 410 |
+
self.lazy = lazy
|
| 411 |
+
|
| 412 |
+
def transform(self, X=None, y=None):
|
| 413 |
+
"""
|
| 414 |
+
Transform an image using an Affine transform with
|
| 415 |
+
shear parameters randomly generated from the user-specified
|
| 416 |
+
range. Return the transform if X=None.
|
| 417 |
+
|
| 418 |
+
Arguments
|
| 419 |
+
---------
|
| 420 |
+
X : ANTsImage
|
| 421 |
+
Image to transform
|
| 422 |
+
|
| 423 |
+
y : ANTsImage (optional)
|
| 424 |
+
Another image to transform
|
| 425 |
+
|
| 426 |
+
Returns
|
| 427 |
+
-------
|
| 428 |
+
ANTsImage if y is None, else a tuple of ANTsImage types
|
| 429 |
+
|
| 430 |
+
Examples
|
| 431 |
+
--------
|
| 432 |
+
>>> import ants
|
| 433 |
+
>>> img = ants.image_read(ants.get_data('ch2'))
|
| 434 |
+
>>> tx = ants.contrib.RandomShear3D(shear_range=(-10,10))
|
| 435 |
+
>>> img2 = tx.transform(img)
|
| 436 |
+
"""
|
| 437 |
+
# random draw in shear range
|
| 438 |
+
shear_x = random.gauss(self.shear_range[0], self.shear_range[1])
|
| 439 |
+
shear_y = random.gauss(self.shear_range[0], self.shear_range[1])
|
| 440 |
+
shear_z = random.gauss(self.shear_range[0], self.shear_range[1])
|
| 441 |
+
self.params = (shear_x, shear_y, shear_z)
|
| 442 |
+
|
| 443 |
+
tx = Shear3D(
|
| 444 |
+
(shear_x, shear_y, shear_z), reference=self.reference, lazy=self.lazy
|
| 445 |
+
)
|
| 446 |
+
|
| 447 |
+
return tx.transform(X, y)
|
| 448 |
+
|
| 449 |
+
|
| 450 |
+
class Rotate3D(object):
|
| 451 |
+
"""
|
| 452 |
+
Create an ANTs Affine Transform with a specified level
|
| 453 |
+
of rotation.
|
| 454 |
+
"""
|
| 455 |
+
|
| 456 |
+
def __init__(self, rotation, reference=None, lazy=False):
|
| 457 |
+
"""
|
| 458 |
+
Initialize a Rotate3D object
|
| 459 |
+
|
| 460 |
+
Arguments
|
| 461 |
+
---------
|
| 462 |
+
rotation : list or tuple
|
| 463 |
+
rotation values for each axis, in degrees.
|
| 464 |
+
Negative values can be used for rotation in the
|
| 465 |
+
other direction
|
| 466 |
+
|
| 467 |
+
reference : ANTsImage (optional but recommended)
|
| 468 |
+
image providing the reference space for the transform.
|
| 469 |
+
this will also set the transform fixed parameters.
|
| 470 |
+
|
| 471 |
+
lazy : boolean (default = False)
|
| 472 |
+
if True, calling the `transform` method only returns
|
| 473 |
+
the randomly generated transform and does not actually
|
| 474 |
+
transform the image
|
| 475 |
+
"""
|
| 476 |
+
if (not isinstance(rotation, (list, tuple))) or (len(rotation) != 3):
|
| 477 |
+
raise ValueError("rotation argument must be list/tuple with three values!")
|
| 478 |
+
|
| 479 |
+
self.rotation = rotation
|
| 480 |
+
self.lazy = lazy
|
| 481 |
+
self.reference = reference
|
| 482 |
+
|
| 483 |
+
self.tx = tio.ANTsTransform(
|
| 484 |
+
precision="float", dimension=3, transform_type="AffineTransform"
|
| 485 |
+
)
|
| 486 |
+
if self.reference is not None:
|
| 487 |
+
self.tx.set_fixed_parameters(self.reference.get_center_of_mass())
|
| 488 |
+
|
| 489 |
+
def transform(self, X=None, y=None):
|
| 490 |
+
"""
|
| 491 |
+
Transform an image using an Affine transform with the given
|
| 492 |
+
rotation parameters. Return the transform if X=None.
|
| 493 |
+
|
| 494 |
+
Arguments
|
| 495 |
+
---------
|
| 496 |
+
X : ANTsImage
|
| 497 |
+
Image to transform
|
| 498 |
+
|
| 499 |
+
y : ANTsImage (optional)
|
| 500 |
+
Another image to transform
|
| 501 |
+
|
| 502 |
+
Returns
|
| 503 |
+
-------
|
| 504 |
+
ANTsImage if y is None, else a tuple of ANTsImage types
|
| 505 |
+
|
| 506 |
+
Examples
|
| 507 |
+
--------
|
| 508 |
+
>>> import ants
|
| 509 |
+
>>> img = ants.image_read(ants.get_data('ch2'))
|
| 510 |
+
>>> tx = ants.contrib.Rotate3D(rotation=(10,-5,12))
|
| 511 |
+
>>> img2 = tx.transform(img)
|
| 512 |
+
"""
|
| 513 |
+
# unpack zoom range
|
| 514 |
+
rotation_x, rotation_y, rotation_z = self.rotation
|
| 515 |
+
|
| 516 |
+
# Rotation about X axis
|
| 517 |
+
theta_x = math.pi / 180 * rotation_x
|
| 518 |
+
rotate_matrix_x = np.array(
|
| 519 |
+
[
|
| 520 |
+
[1, 0, 0, 0],
|
| 521 |
+
[0, math.cos(theta_x), -math.sin(theta_x), 0],
|
| 522 |
+
[0, math.sin(theta_x), math.cos(theta_x), 0],
|
| 523 |
+
[0, 0, 0, 1],
|
| 524 |
+
]
|
| 525 |
+
)
|
| 526 |
+
|
| 527 |
+
# Rotation about Y axis
|
| 528 |
+
theta_y = math.pi / 180 * rotation_y
|
| 529 |
+
rotate_matrix_y = np.array(
|
| 530 |
+
[
|
| 531 |
+
[math.cos(theta_y), 0, math.sin(theta_y), 0],
|
| 532 |
+
[0, 1, 0, 0],
|
| 533 |
+
[-math.sin(theta_y), 0, math.cos(theta_y), 0],
|
| 534 |
+
[0, 0, 0, 1],
|
| 535 |
+
]
|
| 536 |
+
)
|
| 537 |
+
|
| 538 |
+
# Rotation about Z axis
|
| 539 |
+
theta_z = math.pi / 180 * rotation_z
|
| 540 |
+
rotate_matrix_z = np.array(
|
| 541 |
+
[
|
| 542 |
+
[math.cos(theta_z), -math.sin(theta_z), 0, 0],
|
| 543 |
+
[math.sin(theta_z), math.cos(theta_z), 0, 0],
|
| 544 |
+
[0, 0, 1, 0],
|
| 545 |
+
[0, 0, 0, 1],
|
| 546 |
+
]
|
| 547 |
+
)
|
| 548 |
+
rotate_matrix = rotate_matrix_x.dot(rotate_matrix_y).dot(rotate_matrix_z)[:3, :]
|
| 549 |
+
|
| 550 |
+
self.tx.set_parameters(rotate_matrix)
|
| 551 |
+
if self.lazy or X is None:
|
| 552 |
+
return self.tx
|
| 553 |
+
else:
|
| 554 |
+
if y is None:
|
| 555 |
+
return self.tx.apply_to_image(X, reference=self.reference)
|
| 556 |
+
else:
|
| 557 |
+
return (
|
| 558 |
+
self.tx.apply_to_image(X, reference=self.reference),
|
| 559 |
+
self.tx.apply_to_image(y, reference=self.reference),
|
| 560 |
+
)
|
| 561 |
+
|
| 562 |
+
|
| 563 |
+
class RandomRotate3D(object):
|
| 564 |
+
"""
|
| 565 |
+
Apply a Rotate3D transform to an image, but with the zoom
|
| 566 |
+
parameters randomly generated from a user-specified range.
|
| 567 |
+
The range is determined by a mean (first parameter) and standard deviation
|
| 568 |
+
(second parameter) via calls to random.gauss.
|
| 569 |
+
"""
|
| 570 |
+
|
| 571 |
+
def __init__(self, rotation_range, reference=None, lazy=False):
|
| 572 |
+
"""
|
| 573 |
+
Initialize a RandomRotate3D object
|
| 574 |
+
|
| 575 |
+
Arguments
|
| 576 |
+
---------
|
| 577 |
+
rotation_range : list or tuple
|
| 578 |
+
Lower and Upper bounds on rotation parameter, in degrees.
|
| 579 |
+
e.g. rotation_range = (-10,10) will result in a random
|
| 580 |
+
draw of the rotation parameters between -10 and 10 degrees
|
| 581 |
+
|
| 582 |
+
reference : ANTsImage (optional but recommended)
|
| 583 |
+
image providing the reference space for the transform.
|
| 584 |
+
this will also set the transform fixed parameters.
|
| 585 |
+
|
| 586 |
+
lazy : boolean (default = False)
|
| 587 |
+
if True, calling the `transform` method only returns
|
| 588 |
+
the randomly generated transform and does not actually
|
| 589 |
+
transform the image
|
| 590 |
+
"""
|
| 591 |
+
if (not isinstance(rotation_range, (list, tuple))) or (
|
| 592 |
+
len(rotation_range) != 2
|
| 593 |
+
):
|
| 594 |
+
raise ValueError(
|
| 595 |
+
"rotation_range argument must be list/tuple with two values!"
|
| 596 |
+
)
|
| 597 |
+
|
| 598 |
+
self.rotation_range = rotation_range
|
| 599 |
+
self.reference = reference
|
| 600 |
+
self.lazy = lazy
|
| 601 |
+
|
| 602 |
+
def transform(self, X=None, y=None):
|
| 603 |
+
"""
|
| 604 |
+
Transform an image using an Affine transform with
|
| 605 |
+
rotation parameters randomly generated from the user-specified
|
| 606 |
+
range. Return the transform if X=None.
|
| 607 |
+
|
| 608 |
+
Arguments
|
| 609 |
+
---------
|
| 610 |
+
X : ANTsImage
|
| 611 |
+
Image to transform
|
| 612 |
+
|
| 613 |
+
y : ANTsImage (optional)
|
| 614 |
+
Another image to transform
|
| 615 |
+
|
| 616 |
+
Returns
|
| 617 |
+
-------
|
| 618 |
+
ANTsImage if y is None, else a tuple of ANTsImage types
|
| 619 |
+
|
| 620 |
+
Examples
|
| 621 |
+
--------
|
| 622 |
+
>>> import ants
|
| 623 |
+
>>> img = ants.image_read(ants.get_data('ch2'))
|
| 624 |
+
>>> tx = ants.contrib.RandomRotate3D(rotation_range=(-10,10))
|
| 625 |
+
>>> img2 = tx.transform(img)
|
| 626 |
+
"""
|
| 627 |
+
# random draw in rotation range
|
| 628 |
+
rotation_x = random.gauss(self.rotation_range[0], self.rotation_range[1])
|
| 629 |
+
rotation_y = random.gauss(self.rotation_range[0], self.rotation_range[1])
|
| 630 |
+
rotation_z = random.gauss(self.rotation_range[0], self.rotation_range[1])
|
| 631 |
+
self.params = (rotation_x, rotation_y, rotation_z)
|
| 632 |
+
|
| 633 |
+
tx = Rotate3D(
|
| 634 |
+
(rotation_x, rotation_y, rotation_z),
|
| 635 |
+
reference=self.reference,
|
| 636 |
+
lazy=self.lazy,
|
| 637 |
+
)
|
| 638 |
+
|
| 639 |
+
return tx.transform(X, y)
|
| 640 |
+
|
| 641 |
+
|
| 642 |
+
class Zoom3D(object):
|
| 643 |
+
"""
|
| 644 |
+
Create an ANTs Affine Transform with a specified level
|
| 645 |
+
of zoom. Any value greater than 1 implies a "zoom-out" and anything
|
| 646 |
+
less than 1 implies a "zoom-in".
|
| 647 |
+
"""
|
| 648 |
+
|
| 649 |
+
def __init__(self, zoom, reference=None, lazy=False):
|
| 650 |
+
"""
|
| 651 |
+
Initialize a Zoom3D object
|
| 652 |
+
|
| 653 |
+
Arguments
|
| 654 |
+
---------
|
| 655 |
+
zoom_range : list or tuple
|
| 656 |
+
Lower and Upper bounds on zoom parameter.
|
| 657 |
+
e.g. zoom_range = (0.7,0.9) will result in a random
|
| 658 |
+
draw of the zoom parameters between 0.7 and 0.9
|
| 659 |
+
|
| 660 |
+
reference : ANTsImage (optional but recommended)
|
| 661 |
+
image providing the reference space for the transform.
|
| 662 |
+
this will also set the transform fixed parameters.
|
| 663 |
+
|
| 664 |
+
lazy : boolean (default = False)
|
| 665 |
+
if True, calling the `transform` method only returns
|
| 666 |
+
the randomly generated transform and does not actually
|
| 667 |
+
transform the image
|
| 668 |
+
"""
|
| 669 |
+
if (not isinstance(zoom, (list, tuple))) or (len(zoom) != 3):
|
| 670 |
+
raise ValueError(
|
| 671 |
+
"zoom_range argument must be list/tuple with three values!"
|
| 672 |
+
)
|
| 673 |
+
|
| 674 |
+
self.zoom = zoom
|
| 675 |
+
self.lazy = lazy
|
| 676 |
+
self.reference = reference
|
| 677 |
+
|
| 678 |
+
self.tx = tio.ANTsTransform(
|
| 679 |
+
precision="float", dimension=3, transform_type="AffineTransform"
|
| 680 |
+
)
|
| 681 |
+
if self.reference is not None:
|
| 682 |
+
self.tx.set_fixed_parameters(self.reference.get_center_of_mass())
|
| 683 |
+
|
| 684 |
+
def transform(self, X=None, y=None):
|
| 685 |
+
"""
|
| 686 |
+
Transform an image using an Affine transform with the given
|
| 687 |
+
zoom parameters. Return the transform if X=None.
|
| 688 |
+
|
| 689 |
+
Arguments
|
| 690 |
+
---------
|
| 691 |
+
X : ANTsImage
|
| 692 |
+
Image to transform
|
| 693 |
+
|
| 694 |
+
y : ANTsImage (optional)
|
| 695 |
+
Another image to transform
|
| 696 |
+
|
| 697 |
+
Returns
|
| 698 |
+
-------
|
| 699 |
+
ANTsImage if y is None, else a tuple of ANTsImage types
|
| 700 |
+
|
| 701 |
+
Examples
|
| 702 |
+
--------
|
| 703 |
+
>>> import ants
|
| 704 |
+
>>> img = ants.image_read(ants.get_data('ch2'))
|
| 705 |
+
>>> tx = ants.contrib.Zoom3D(zoom=(0.8,0.8,0.8))
|
| 706 |
+
>>> img2 = tx.transform(img)
|
| 707 |
+
"""
|
| 708 |
+
# unpack zoom range
|
| 709 |
+
zoom_x, zoom_y, zoom_z = self.zoom
|
| 710 |
+
|
| 711 |
+
self.params = (zoom_x, zoom_y, zoom_z)
|
| 712 |
+
zoom_matrix = np.array(
|
| 713 |
+
[[zoom_x, 0, 0, 0], [0, zoom_y, 0, 0], [0, 0, zoom_z, 0]]
|
| 714 |
+
)
|
| 715 |
+
self.tx.set_parameters(zoom_matrix)
|
| 716 |
+
if self.lazy or X is None:
|
| 717 |
+
return self.tx
|
| 718 |
+
else:
|
| 719 |
+
if y is None:
|
| 720 |
+
return self.tx.apply_to_image(X, reference=self.reference)
|
| 721 |
+
else:
|
| 722 |
+
return (
|
| 723 |
+
self.tx.apply_to_image(X, reference=self.reference),
|
| 724 |
+
self.tx.apply_to_image(y, reference=self.reference),
|
| 725 |
+
)
|
| 726 |
+
|
| 727 |
+
|
| 728 |
+
class RandomZoom3D(object):
|
| 729 |
+
"""
|
| 730 |
+
Apply a Zoom3D transform to an image, but with the zoom
|
| 731 |
+
parameters randomly generated from a user-specified range.
|
| 732 |
+
The range is determined by a mean (first parameter) and standard deviation
|
| 733 |
+
(second parameter) via calls to random.gauss.
|
| 734 |
+
"""
|
| 735 |
+
|
| 736 |
+
def __init__(self, zoom_range, reference=None, lazy=False):
|
| 737 |
+
"""
|
| 738 |
+
Initialize a RandomZoom3D object
|
| 739 |
+
|
| 740 |
+
Arguments
|
| 741 |
+
---------
|
| 742 |
+
zoom_range : list or tuple
|
| 743 |
+
Lower and Upper bounds on zoom parameter.
|
| 744 |
+
e.g. zoom_range = (0.7,0.9) will result in a random
|
| 745 |
+
draw of the zoom parameters between 0.7 and 0.9
|
| 746 |
+
|
| 747 |
+
reference : ANTsImage (optional but recommended)
|
| 748 |
+
image providing the reference space for the transform
|
| 749 |
+
this will also set the transform fixed parameters.
|
| 750 |
+
|
| 751 |
+
lazy : boolean (default = False)
|
| 752 |
+
if True, calling the `transform` method only returns
|
| 753 |
+
the randomly generated transform and does not actually
|
| 754 |
+
transform the image
|
| 755 |
+
"""
|
| 756 |
+
if (not isinstance(zoom_range, (list, tuple))) or (len(zoom_range) != 2):
|
| 757 |
+
raise ValueError("zoom_range argument must be list/tuple with two values!")
|
| 758 |
+
|
| 759 |
+
self.zoom_range = zoom_range
|
| 760 |
+
self.reference = reference
|
| 761 |
+
self.lazy = lazy
|
| 762 |
+
|
| 763 |
+
def transform(self, X=None, y=None):
|
| 764 |
+
"""
|
| 765 |
+
Transform an image using an Affine transform with
|
| 766 |
+
zoom parameters randomly generated from the user-specified
|
| 767 |
+
range. Return the transform if X=None.
|
| 768 |
+
|
| 769 |
+
Arguments
|
| 770 |
+
---------
|
| 771 |
+
X : ANTsImage
|
| 772 |
+
Image to transform
|
| 773 |
+
|
| 774 |
+
y : ANTsImage (optional)
|
| 775 |
+
Another image to transform
|
| 776 |
+
|
| 777 |
+
Returns
|
| 778 |
+
-------
|
| 779 |
+
ANTsImage if y is None, else a tuple of ANTsImage types
|
| 780 |
+
|
| 781 |
+
Examples
|
| 782 |
+
--------
|
| 783 |
+
>>> import ants
|
| 784 |
+
>>> img = ants.image_read(ants.get_data('ch2'))
|
| 785 |
+
>>> tx = ants.contrib.RandomZoom3D(zoom_range=(0.8,0.9))
|
| 786 |
+
>>> img2 = tx.transform(img)
|
| 787 |
+
"""
|
| 788 |
+
# random draw in zoom range
|
| 789 |
+
zoom_x = np.exp(
|
| 790 |
+
random.gauss(np.log(self.zoom_range[0]), np.log(self.zoom_range[1]))
|
| 791 |
+
)
|
| 792 |
+
zoom_y = np.exp(
|
| 793 |
+
random.gauss(np.log(self.zoom_range[0]), np.log(self.zoom_range[1]))
|
| 794 |
+
)
|
| 795 |
+
zoom_z = np.exp(
|
| 796 |
+
random.gauss(np.log(self.zoom_range[0]), np.log(self.zoom_range[1]))
|
| 797 |
+
)
|
| 798 |
+
self.params = (zoom_x, zoom_y, zoom_z)
|
| 799 |
+
|
| 800 |
+
tx = Zoom3D((zoom_x, zoom_y, zoom_z), reference=self.reference, lazy=self.lazy)
|
| 801 |
+
|
| 802 |
+
return tx.transform(X, y)
|
MindEyeV2/antspy/ants/contrib/sampling/transforms.py
ADDED
|
@@ -0,0 +1,766 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
Various data augmentation transforms for ANTsImage types
|
| 3 |
+
|
| 4 |
+
List of Transformations:
|
| 5 |
+
======================
|
| 6 |
+
- CastIntensity
|
| 7 |
+
- BlurIntensity
|
| 8 |
+
- NormalizeIntensity
|
| 9 |
+
- RescaleIntensity
|
| 10 |
+
- ShiftScaleIntensity
|
| 11 |
+
- SigmoidIntensity
|
| 12 |
+
======================
|
| 13 |
+
- FlipImage
|
| 14 |
+
- TranslateImage
|
| 15 |
+
|
| 16 |
+
TODO
|
| 17 |
+
----
|
| 18 |
+
- RotateImage
|
| 19 |
+
- ShearImage
|
| 20 |
+
- ScaleImage
|
| 21 |
+
- DeformImage
|
| 22 |
+
- PadImage
|
| 23 |
+
- HistogramEqualizeIntensity
|
| 24 |
+
- TruncateIntensity
|
| 25 |
+
- SharpenIntensity
|
| 26 |
+
- MorpholigicalIntensity
|
| 27 |
+
- MD
|
| 28 |
+
- ME
|
| 29 |
+
- MO
|
| 30 |
+
- MC
|
| 31 |
+
- GD
|
| 32 |
+
- GE
|
| 33 |
+
- GO
|
| 34 |
+
- GC
|
| 35 |
+
"""
|
| 36 |
+
__all__ = ['CastIntensity',
|
| 37 |
+
'BlurIntensity',
|
| 38 |
+
'LocallyBlurIntensity',
|
| 39 |
+
'NormalizeIntensity',
|
| 40 |
+
'RescaleIntensity',
|
| 41 |
+
'ShiftScaleIntensity',
|
| 42 |
+
'SigmoidIntensity',
|
| 43 |
+
'FlipImage',
|
| 44 |
+
'ScaleImage',
|
| 45 |
+
'TranslateImage',
|
| 46 |
+
'MultiResolutionImage']
|
| 47 |
+
|
| 48 |
+
from ... import utils
|
| 49 |
+
from ...core import ants_image as iio
|
| 50 |
+
|
| 51 |
+
|
| 52 |
+
class MultiResolutionImage(object):
|
| 53 |
+
"""
|
| 54 |
+
Generate a set of images at multiple resolutions from an original image
|
| 55 |
+
"""
|
| 56 |
+
def __init__(self, levels=4, keep_shape=False):
|
| 57 |
+
self.levels = levels
|
| 58 |
+
self.keep_shape = keep_shape
|
| 59 |
+
|
| 60 |
+
def transform(self, X, y=None):
|
| 61 |
+
"""
|
| 62 |
+
Generate a set of multi-resolution ANTsImage types
|
| 63 |
+
|
| 64 |
+
Arguments
|
| 65 |
+
---------
|
| 66 |
+
X : ANTsImage
|
| 67 |
+
image to transform
|
| 68 |
+
|
| 69 |
+
y : ANTsImage (optional)
|
| 70 |
+
another image to transform
|
| 71 |
+
|
| 72 |
+
Example
|
| 73 |
+
-------
|
| 74 |
+
>>> import ants
|
| 75 |
+
>>> multires = ants.contrib.MultiResolutionImage(levels=4)
|
| 76 |
+
>>> img = ants.image_read(ants.get_data('r16'))
|
| 77 |
+
>>> imgs = multires.transform(img)
|
| 78 |
+
"""
|
| 79 |
+
insuffix = X._libsuffix
|
| 80 |
+
multires_fn = utils.get_lib_fn('multiResolutionAntsImage%s' % (insuffix))
|
| 81 |
+
casted_ptrs = multires_fn(X.pointer, self.levels)
|
| 82 |
+
|
| 83 |
+
imgs = []
|
| 84 |
+
for casted_ptr in casted_ptrs:
|
| 85 |
+
img = iio.ANTsImage(pixeltype=X.pixeltype, dimension=X.dimension,
|
| 86 |
+
components=X.components, pointer=casted_ptr)
|
| 87 |
+
if self.keep_shape:
|
| 88 |
+
img = img.resample_image_to_target(X)
|
| 89 |
+
imgs.append(img)
|
| 90 |
+
|
| 91 |
+
return imgs
|
| 92 |
+
|
| 93 |
+
|
| 94 |
+
## Intensity Transforms ##
|
| 95 |
+
|
| 96 |
+
class CastIntensity(object):
|
| 97 |
+
"""
|
| 98 |
+
Cast the pixeltype of an ANTsImage to a given type.
|
| 99 |
+
This code uses the C++ ITK library directly, so it is fast.
|
| 100 |
+
|
| 101 |
+
NOTE: This offers a ~2.5x speedup over using img.clone(pixeltype):
|
| 102 |
+
|
| 103 |
+
Timings vs Cloning
|
| 104 |
+
------------------
|
| 105 |
+
>>> import ants
|
| 106 |
+
>>> import time
|
| 107 |
+
>>> caster = ants.contrib.CastIntensity('float')
|
| 108 |
+
>>> img = ants.image_read(ants.get_data('mni')).clone('unsigned int')
|
| 109 |
+
>>> s = time.time()
|
| 110 |
+
>>> for i in range(1000):
|
| 111 |
+
... img_float = caster.transform(img)
|
| 112 |
+
>>> e = time.time()
|
| 113 |
+
>>> print(e - s) # 9.6s
|
| 114 |
+
>>> s = time.time()
|
| 115 |
+
>>> for i in range(1000):
|
| 116 |
+
... img_float = img.clone('float')
|
| 117 |
+
>>> e = time.time()
|
| 118 |
+
>>> print(e - s) # 25.3s
|
| 119 |
+
"""
|
| 120 |
+
def __init__(self, pixeltype):
|
| 121 |
+
"""
|
| 122 |
+
Initialize a CastIntensity transform
|
| 123 |
+
|
| 124 |
+
Arguments
|
| 125 |
+
---------
|
| 126 |
+
pixeltype : string
|
| 127 |
+
pixeltype to which images will be casted
|
| 128 |
+
|
| 129 |
+
Example
|
| 130 |
+
-------
|
| 131 |
+
>>> import ants
|
| 132 |
+
>>> caster = ants.contrib.CastIntensity('float')
|
| 133 |
+
"""
|
| 134 |
+
self.pixeltype = pixeltype
|
| 135 |
+
|
| 136 |
+
def transform(self, X, y=None):
|
| 137 |
+
"""
|
| 138 |
+
Transform an image by casting its type
|
| 139 |
+
|
| 140 |
+
Arguments
|
| 141 |
+
---------
|
| 142 |
+
X : ANTsImage
|
| 143 |
+
image to cast
|
| 144 |
+
|
| 145 |
+
y : ANTsImage (optional)
|
| 146 |
+
another image to cast.
|
| 147 |
+
|
| 148 |
+
Example
|
| 149 |
+
-------
|
| 150 |
+
>>> import ants
|
| 151 |
+
>>> caster = ants.contrib.CastIntensity('float')
|
| 152 |
+
>>> img2d = ants.image_read(ants.get_data('r16')).clone('unsigned int')
|
| 153 |
+
>>> img2d_float = caster.transform(img2d)
|
| 154 |
+
>>> print(img2d.pixeltype, '- ', img2d_float.pixeltype)
|
| 155 |
+
>>> img3d = ants.image_read(ants.get_data('mni')).clone('unsigned int')
|
| 156 |
+
>>> img3d_float = caster.transform(img3d)
|
| 157 |
+
>>> print(img3d.pixeltype, ' - ' , img3d_float.pixeltype)
|
| 158 |
+
"""
|
| 159 |
+
insuffix = X._libsuffix
|
| 160 |
+
outsuffix = '%s%i' % (utils.short_ptype(self.pixeltype), X.dimension)
|
| 161 |
+
cast_fn = utils.get_lib_fn('castAntsImage%s%s' % (insuffix, outsuffix))
|
| 162 |
+
casted_ptr = cast_fn(X.pointer)
|
| 163 |
+
return iio.ANTsImage(pixeltype=self.pixeltype, dimension=X.dimension,
|
| 164 |
+
components=X.components, pointer=casted_ptr)
|
| 165 |
+
|
| 166 |
+
|
| 167 |
+
class BlurIntensity(object):
|
| 168 |
+
"""
|
| 169 |
+
Transform for blurring the intensity of an ANTsImage
|
| 170 |
+
using a Gaussian Filter
|
| 171 |
+
"""
|
| 172 |
+
def __init__(self, sigma, width):
|
| 173 |
+
"""
|
| 174 |
+
Initialize a BlurIntensity transform
|
| 175 |
+
|
| 176 |
+
Arguments
|
| 177 |
+
---------
|
| 178 |
+
sigma : float
|
| 179 |
+
variance of gaussian kernel intensity
|
| 180 |
+
increasing this value increasing the amount
|
| 181 |
+
of blur
|
| 182 |
+
|
| 183 |
+
width : int
|
| 184 |
+
width of gaussian kernel shape
|
| 185 |
+
increasing this value increase the number of
|
| 186 |
+
neighboring voxels which are used for blurring
|
| 187 |
+
|
| 188 |
+
Example
|
| 189 |
+
-------
|
| 190 |
+
>>> import ants
|
| 191 |
+
>>> blur = ants.contrib.BlurIntensity(2,3)
|
| 192 |
+
"""
|
| 193 |
+
self.sigma = sigma
|
| 194 |
+
self.width = width
|
| 195 |
+
|
| 196 |
+
def transform(self, X, y=None):
|
| 197 |
+
"""
|
| 198 |
+
Blur an image by applying a gaussian filter.
|
| 199 |
+
|
| 200 |
+
Arguments
|
| 201 |
+
---------
|
| 202 |
+
X : ANTsImage
|
| 203 |
+
image to transform
|
| 204 |
+
|
| 205 |
+
y : ANTsImage (optional)
|
| 206 |
+
another image to transform.
|
| 207 |
+
|
| 208 |
+
Example
|
| 209 |
+
-------
|
| 210 |
+
>>> import ants
|
| 211 |
+
>>> blur = ants.contrib.BlurIntensity(2,3)
|
| 212 |
+
>>> img2d = ants.image_read(ants.get_data('r16'))
|
| 213 |
+
>>> img2d_b = blur.transform(img2d)
|
| 214 |
+
>>> ants.plot(img2d)
|
| 215 |
+
>>> ants.plot(img2d_b)
|
| 216 |
+
>>> img3d = ants.image_read(ants.get_data('mni'))
|
| 217 |
+
>>> img3d_b = blur.transform(img3d)
|
| 218 |
+
>>> ants.plot(img3d)
|
| 219 |
+
>>> ants.plot(img3d_b)
|
| 220 |
+
"""
|
| 221 |
+
if X.pixeltype != 'float':
|
| 222 |
+
raise ValueError('image.pixeltype must be float ... use TypeCast transform or clone to float')
|
| 223 |
+
|
| 224 |
+
insuffix = X._libsuffix
|
| 225 |
+
cast_fn = utils.get_lib_fn('blurAntsImage%s' % (insuffix))
|
| 226 |
+
casted_ptr = cast_fn(X.pointer, self.sigma, self.width)
|
| 227 |
+
return iio.ANTsImage(pixeltype=X.pixeltype, dimension=X.dimension,
|
| 228 |
+
components=X.components, pointer=casted_ptr,
|
| 229 |
+
origin=X.origin)
|
| 230 |
+
|
| 231 |
+
|
| 232 |
+
class LocallyBlurIntensity(object):
|
| 233 |
+
"""
|
| 234 |
+
Blur an ANTsImage locally using a gradient anisotropic
|
| 235 |
+
diffusion filter, thereby preserving the sharpeness of edges as best
|
| 236 |
+
as possible.
|
| 237 |
+
"""
|
| 238 |
+
def __init__(self, conductance=1, iters=5):
|
| 239 |
+
self.conductance = conductance
|
| 240 |
+
self.iters = iters
|
| 241 |
+
|
| 242 |
+
def transform(self, X, y=None):
|
| 243 |
+
"""
|
| 244 |
+
Locally blur an image by applying a gradient anisotropic diffusion filter.
|
| 245 |
+
|
| 246 |
+
Arguments
|
| 247 |
+
---------
|
| 248 |
+
X : ANTsImage
|
| 249 |
+
image to transform
|
| 250 |
+
|
| 251 |
+
y : ANTsImage (optional)
|
| 252 |
+
another image to transform.
|
| 253 |
+
|
| 254 |
+
Example
|
| 255 |
+
-------
|
| 256 |
+
>>> import ants
|
| 257 |
+
>>> blur = ants.contrib.LocallyBlurIntensity(1,5)
|
| 258 |
+
>>> img2d = ants.image_read(ants.get_data('r16'))
|
| 259 |
+
>>> img2d_b = blur.transform(img2d)
|
| 260 |
+
>>> ants.plot(img2d)
|
| 261 |
+
>>> ants.plot(img2d_b)
|
| 262 |
+
>>> img3d = ants.image_read(ants.get_data('mni'))
|
| 263 |
+
>>> img3d_b = blur.transform(img3d)
|
| 264 |
+
>>> ants.plot(img3d)
|
| 265 |
+
>>> ants.plot(img3d_b)
|
| 266 |
+
"""
|
| 267 |
+
#if X.pixeltype != 'float':
|
| 268 |
+
# raise ValueError('image.pixeltype must be float ... use TypeCast transform or clone to float')
|
| 269 |
+
insuffix = X._libsuffix
|
| 270 |
+
cast_fn = utils.get_lib_fn('locallyBlurAntsImage%s' % (insuffix))
|
| 271 |
+
casted_ptr = cast_fn(X.pointer, self.iters, self.conductance)
|
| 272 |
+
return iio.ANTsImage(pixeltype=X.pixeltype, dimension=X.dimension,
|
| 273 |
+
components=X.components, pointer=casted_ptr)
|
| 274 |
+
|
| 275 |
+
|
| 276 |
+
class NormalizeIntensity(object):
|
| 277 |
+
"""
|
| 278 |
+
Normalize the intensity values of an ANTsImage to have
|
| 279 |
+
zero mean and unit variance
|
| 280 |
+
|
| 281 |
+
NOTE: this transform is more-or-less the same in speed
|
| 282 |
+
as an equivalent numpy+scikit-learn solution.
|
| 283 |
+
|
| 284 |
+
Timing vs Numpy+Scikit-Learn
|
| 285 |
+
----------------------------
|
| 286 |
+
>>> import ants
|
| 287 |
+
>>> import numpy as np
|
| 288 |
+
>>> from sklearn.preprocessing import StandardScaler
|
| 289 |
+
>>> import time
|
| 290 |
+
>>> img = ants.image_read(ants.get_data('mni'))
|
| 291 |
+
>>> arr = img.numpy().reshape(1,-1)
|
| 292 |
+
>>> normalizer = ants.contrib.NormalizeIntensity()
|
| 293 |
+
>>> normalizer2 = StandardScaler()
|
| 294 |
+
>>> s = time.time()
|
| 295 |
+
>>> for i in range(100):
|
| 296 |
+
... img_scaled = normalizer.transform(img)
|
| 297 |
+
>>> e = time.time()
|
| 298 |
+
>>> print(e - s) # 3.3s
|
| 299 |
+
>>> s = time.time()
|
| 300 |
+
>>> for i in range(100):
|
| 301 |
+
... arr_scaled = normalizer2.fit_transform(arr)
|
| 302 |
+
>>> e = time.time()
|
| 303 |
+
>>> print(e - s) # 3.5s
|
| 304 |
+
"""
|
| 305 |
+
def __init__(self):
|
| 306 |
+
"""
|
| 307 |
+
Initialize a NormalizeIntensity transform
|
| 308 |
+
"""
|
| 309 |
+
pass
|
| 310 |
+
|
| 311 |
+
def transform(self, X, y=None):
|
| 312 |
+
"""
|
| 313 |
+
Transform an image by normalizing its intensity values to
|
| 314 |
+
have zero mean and unit variance.
|
| 315 |
+
|
| 316 |
+
Arguments
|
| 317 |
+
---------
|
| 318 |
+
X : ANTsImage
|
| 319 |
+
image to transform
|
| 320 |
+
|
| 321 |
+
y : ANTsImage (optional)
|
| 322 |
+
another image to transform.
|
| 323 |
+
|
| 324 |
+
Example
|
| 325 |
+
-------
|
| 326 |
+
>>> import ants
|
| 327 |
+
>>> normalizer = ants.contrib.NormalizeIntensity()
|
| 328 |
+
>>> img2d = ants.image_read(ants.get_data('r16'))
|
| 329 |
+
>>> img2d_r = normalizer.transform(img2d)
|
| 330 |
+
>>> print(img2d.mean(), ',', img2d.std(), ' -> ', img2d_r.mean(), ',', img2d_r.std())
|
| 331 |
+
>>> img3d = ants.image_read(ants.get_data('mni'))
|
| 332 |
+
>>> img3d_r = normalizer.transform(img3d)
|
| 333 |
+
>>> print(img3d.mean(), ',' , img3d.std(), ',', ' -> ', img3d_r.mean(), ',' , img3d_r.std())
|
| 334 |
+
"""
|
| 335 |
+
if X.pixeltype != 'float':
|
| 336 |
+
raise ValueError('image.pixeltype must be float ... use TypeCast transform or clone to float')
|
| 337 |
+
|
| 338 |
+
insuffix = X._libsuffix
|
| 339 |
+
cast_fn = utils.get_lib_fn('normalizeAntsImage%s' % (insuffix))
|
| 340 |
+
casted_ptr = cast_fn(X.pointer)
|
| 341 |
+
return iio.ANTsImage(pixeltype=X.pixeltype, dimension=X.dimension,
|
| 342 |
+
components=X.components, pointer=casted_ptr)
|
| 343 |
+
|
| 344 |
+
|
| 345 |
+
class RescaleIntensity(object):
|
| 346 |
+
"""
|
| 347 |
+
Rescale the pixeltype of an ANTsImage linearly to be between a given
|
| 348 |
+
minimum and maximum value.
|
| 349 |
+
This code uses the C++ ITK library directly, so it is fast.
|
| 350 |
+
|
| 351 |
+
NOTE: this offered a ~5x speedup over using built-in arithmetic operations in ANTs.
|
| 352 |
+
It is also more-or-less the same in speed as an equivalent numpy+scikit-learn
|
| 353 |
+
solution.
|
| 354 |
+
|
| 355 |
+
Timing vs Built-in Operations
|
| 356 |
+
-----------------------------
|
| 357 |
+
>>> import ants
|
| 358 |
+
>>> import time
|
| 359 |
+
>>> rescaler = ants.contrib.RescaleIntensity(0,1)
|
| 360 |
+
>>> img = ants.image_read(ants.get_data('mni'))
|
| 361 |
+
>>> s = time.time()
|
| 362 |
+
>>> for i in range(100):
|
| 363 |
+
... img_float = rescaler.transform(img)
|
| 364 |
+
>>> e = time.time()
|
| 365 |
+
>>> print(e - s) # 2.8s
|
| 366 |
+
>>> s = time.time()
|
| 367 |
+
>>> for i in range(100):
|
| 368 |
+
... maxval = img.max()
|
| 369 |
+
... img_float = (img - maxval) / (maxval - img.min())
|
| 370 |
+
>>> e = time.time()
|
| 371 |
+
>>> print(e - s) # 13.9s
|
| 372 |
+
|
| 373 |
+
Timing vs Numpy+Scikit-Learn
|
| 374 |
+
----------------------------
|
| 375 |
+
>>> import ants
|
| 376 |
+
>>> import numpy as np
|
| 377 |
+
>>> from sklearn.preprocessing import MinMaxScaler
|
| 378 |
+
>>> import time
|
| 379 |
+
>>> img = ants.image_read(ants.get_data('mni'))
|
| 380 |
+
>>> arr = img.numpy().reshape(1,-1)
|
| 381 |
+
>>> rescaler = ants.contrib.RescaleIntensity(-1,1)
|
| 382 |
+
>>> rescaler2 = MinMaxScaler((-1,1)).fit(arr)
|
| 383 |
+
>>> s = time.time()
|
| 384 |
+
>>> for i in range(100):
|
| 385 |
+
... img_scaled = rescaler.transform(img)
|
| 386 |
+
>>> e = time.time()
|
| 387 |
+
>>> print(e - s) # 2.8s
|
| 388 |
+
>>> s = time.time()
|
| 389 |
+
>>> for i in range(100):
|
| 390 |
+
... arr_scaled = rescaler2.transform(arr)
|
| 391 |
+
>>> e = time.time()
|
| 392 |
+
>>> print(e - s) # 3s
|
| 393 |
+
"""
|
| 394 |
+
|
| 395 |
+
def __init__(self, min_val, max_val):
|
| 396 |
+
"""
|
| 397 |
+
Initialize a RescaleIntensity transform.
|
| 398 |
+
|
| 399 |
+
Arguments
|
| 400 |
+
---------
|
| 401 |
+
min_val : float
|
| 402 |
+
minimum value to which image(s) will be rescaled
|
| 403 |
+
|
| 404 |
+
max_val : float
|
| 405 |
+
maximum value to which image(s) will be rescaled
|
| 406 |
+
|
| 407 |
+
Example
|
| 408 |
+
-------
|
| 409 |
+
>>> import ants
|
| 410 |
+
>>> rescaler = ants.contrib.RescaleIntensity(0,1)
|
| 411 |
+
"""
|
| 412 |
+
self.min_val = min_val
|
| 413 |
+
self.max_val = max_val
|
| 414 |
+
|
| 415 |
+
def transform(self, X, y=None):
|
| 416 |
+
"""
|
| 417 |
+
Transform an image by linearly rescaling its intensity to
|
| 418 |
+
be between a minimum and maximum value
|
| 419 |
+
|
| 420 |
+
Arguments
|
| 421 |
+
---------
|
| 422 |
+
X : ANTsImage
|
| 423 |
+
image to transform
|
| 424 |
+
|
| 425 |
+
y : ANTsImage (optional)
|
| 426 |
+
another image to transform.
|
| 427 |
+
|
| 428 |
+
Example
|
| 429 |
+
-------
|
| 430 |
+
>>> import ants
|
| 431 |
+
>>> rescaler = ants.contrib.RescaleIntensity(0,1)
|
| 432 |
+
>>> img2d = ants.image_read(ants.get_data('r16'))
|
| 433 |
+
>>> img2d_r = rescaler.transform(img2d)
|
| 434 |
+
>>> print(img2d.min(), ',', img2d.max(), ' -> ', img2d_r.min(), ',', img2d_r.max())
|
| 435 |
+
>>> img3d = ants.image_read(ants.get_data('mni'))
|
| 436 |
+
>>> img3d_r = rescaler.transform(img3d)
|
| 437 |
+
>>> print(img3d.min(), ',' , img3d.max(), ' -> ', img3d_r.min(), ',' , img3d_r.max())
|
| 438 |
+
"""
|
| 439 |
+
if X.pixeltype != 'float':
|
| 440 |
+
raise ValueError('image.pixeltype must be float ... use TypeCast transform or clone to float')
|
| 441 |
+
|
| 442 |
+
insuffix = X._libsuffix
|
| 443 |
+
cast_fn = utils.get_lib_fn('rescaleAntsImage%s' % (insuffix))
|
| 444 |
+
casted_ptr = cast_fn(X.pointer, self.min_val, self.max_val)
|
| 445 |
+
return iio.ANTsImage(pixeltype=X.pixeltype, dimension=X.dimension,
|
| 446 |
+
components=X.components, pointer=casted_ptr)
|
| 447 |
+
|
| 448 |
+
|
| 449 |
+
class ShiftScaleIntensity(object):
|
| 450 |
+
"""
|
| 451 |
+
Shift and scale the intensity of an ANTsImage
|
| 452 |
+
"""
|
| 453 |
+
def __init__(self, shift, scale):
|
| 454 |
+
"""
|
| 455 |
+
Initialize a ShiftScaleIntensity transform
|
| 456 |
+
|
| 457 |
+
Arguments
|
| 458 |
+
---------
|
| 459 |
+
shift : float
|
| 460 |
+
shift all of the intensity values by the given amount through addition.
|
| 461 |
+
For example, if the minimum image value is 0.0 and the shift
|
| 462 |
+
is 10.0, then the new minimum value (before scaling) will be 10.0
|
| 463 |
+
|
| 464 |
+
scale : float
|
| 465 |
+
scale all the intensity values by the given amount through multiplication.
|
| 466 |
+
For example, if the min/max image values are 10/20 and the scale
|
| 467 |
+
is 2.0, then then new min/max values will be 20/40
|
| 468 |
+
|
| 469 |
+
Example
|
| 470 |
+
-------
|
| 471 |
+
>>> import ants
|
| 472 |
+
>>> shiftscaler = ants.contrib.ShiftScaleIntensity(shift=10, scale=2)
|
| 473 |
+
"""
|
| 474 |
+
self.shift = shift
|
| 475 |
+
self.scale = scale
|
| 476 |
+
|
| 477 |
+
def transform(self, X, y=None):
|
| 478 |
+
"""
|
| 479 |
+
Transform an image by shifting and scaling its intensity values.
|
| 480 |
+
|
| 481 |
+
Arguments
|
| 482 |
+
---------
|
| 483 |
+
X : ANTsImage
|
| 484 |
+
image to transform
|
| 485 |
+
|
| 486 |
+
y : ANTsImage (optional)
|
| 487 |
+
another image to transform.
|
| 488 |
+
|
| 489 |
+
Example
|
| 490 |
+
-------
|
| 491 |
+
>>> import ants
|
| 492 |
+
>>> shiftscaler = ants.contrib.ShiftScaleIntensity(10,2.)
|
| 493 |
+
>>> img2d = ants.image_read(ants.get_data('r16'))
|
| 494 |
+
>>> img2d_r = shiftscaler.transform(img2d)
|
| 495 |
+
>>> print(img2d.min(), ',', img2d.max(), ' -> ', img2d_r.min(), ',', img2d_r.max())
|
| 496 |
+
>>> img3d = ants.image_read(ants.get_data('mni'))
|
| 497 |
+
>>> img3d_r = shiftscaler.transform(img3d)
|
| 498 |
+
>>> print(img3d.min(), ',' , img3d.max(), ',', ' -> ', img3d_r.min(), ',' , img3d_r.max())
|
| 499 |
+
"""
|
| 500 |
+
if X.pixeltype != 'float':
|
| 501 |
+
raise ValueError('image.pixeltype must be float ... use TypeCast transform or clone to float')
|
| 502 |
+
|
| 503 |
+
insuffix = X._libsuffix
|
| 504 |
+
cast_fn = utils.get_lib_fn('shiftScaleAntsImage%s' % (insuffix))
|
| 505 |
+
casted_ptr = cast_fn(X.pointer, self.scale, self.shift)
|
| 506 |
+
return iio.ANTsImage(pixeltype=X.pixeltype, dimension=X.dimension,
|
| 507 |
+
components=X.components, pointer=casted_ptr)
|
| 508 |
+
|
| 509 |
+
|
| 510 |
+
class SigmoidIntensity(object):
|
| 511 |
+
"""
|
| 512 |
+
Transform an image using a sigmoid function
|
| 513 |
+
"""
|
| 514 |
+
def __init__(self, min_val, max_val, alpha, beta):
|
| 515 |
+
"""
|
| 516 |
+
Initialize a SigmoidIntensity transform
|
| 517 |
+
|
| 518 |
+
Arguments
|
| 519 |
+
---------
|
| 520 |
+
min_val : float
|
| 521 |
+
minimum value
|
| 522 |
+
|
| 523 |
+
max_val : float
|
| 524 |
+
maximum value
|
| 525 |
+
|
| 526 |
+
alpha : float
|
| 527 |
+
alpha value for sigmoid
|
| 528 |
+
|
| 529 |
+
beta : flaot
|
| 530 |
+
beta value for sigmoid
|
| 531 |
+
|
| 532 |
+
Example
|
| 533 |
+
-------
|
| 534 |
+
>>> import ants
|
| 535 |
+
>>> sigscaler = ants.contrib.SigmoidIntensity(0,1,1,1)
|
| 536 |
+
"""
|
| 537 |
+
self.min_val = min_val
|
| 538 |
+
self.max_val = max_val
|
| 539 |
+
self.alpha = alpha
|
| 540 |
+
self.beta = beta
|
| 541 |
+
|
| 542 |
+
def transform(self, X, y=None):
|
| 543 |
+
"""
|
| 544 |
+
Transform an image by applying a sigmoid function.
|
| 545 |
+
|
| 546 |
+
Arguments
|
| 547 |
+
---------
|
| 548 |
+
X : ANTsImage
|
| 549 |
+
image to transform
|
| 550 |
+
|
| 551 |
+
y : ANTsImage (optional)
|
| 552 |
+
another image to transform.
|
| 553 |
+
|
| 554 |
+
Example
|
| 555 |
+
-------
|
| 556 |
+
>>> import ants
|
| 557 |
+
>>> sigscaler = ants.contrib.SigmoidIntensity(0,1,1,1)
|
| 558 |
+
>>> img2d = ants.image_read(ants.get_data('r16'))
|
| 559 |
+
>>> img2d_r = sigscaler.transform(img2d)
|
| 560 |
+
>>> img3d = ants.image_read(ants.get_data('mni'))
|
| 561 |
+
>>> img3d_r = sigscaler.transform(img3d)
|
| 562 |
+
"""
|
| 563 |
+
if X.pixeltype != 'float':
|
| 564 |
+
raise ValueError('image.pixeltype must be float ... use TypeCast transform or clone to float')
|
| 565 |
+
|
| 566 |
+
insuffix = X._libsuffix
|
| 567 |
+
cast_fn = utils.get_lib_fn('sigmoidAntsImage%s' % (insuffix))
|
| 568 |
+
casted_ptr = cast_fn(X.pointer, self.min_val, self.max_val, self.alpha, self.beta)
|
| 569 |
+
return iio.ANTsImage(pixeltype=X.pixeltype, dimension=X.dimension,
|
| 570 |
+
components=X.components, pointer=casted_ptr)
|
| 571 |
+
|
| 572 |
+
|
| 573 |
+
## Physical Transforms ##
|
| 574 |
+
|
| 575 |
+
class FlipImage(object):
|
| 576 |
+
"""
|
| 577 |
+
Transform an image by flipping two axes.
|
| 578 |
+
"""
|
| 579 |
+
def __init__(self, axis1, axis2):
|
| 580 |
+
"""
|
| 581 |
+
Initialize a SigmoidIntensity transform
|
| 582 |
+
|
| 583 |
+
Arguments
|
| 584 |
+
---------
|
| 585 |
+
axis1 : int
|
| 586 |
+
axis to flip
|
| 587 |
+
|
| 588 |
+
axis2 : int
|
| 589 |
+
other axis to flip
|
| 590 |
+
|
| 591 |
+
Example
|
| 592 |
+
-------
|
| 593 |
+
>>> import ants
|
| 594 |
+
>>> flipper = ants.contrib.FlipImage(0,1)
|
| 595 |
+
"""
|
| 596 |
+
self.axis1 = axis1
|
| 597 |
+
self.axis2 = axis2
|
| 598 |
+
|
| 599 |
+
def transform(self, X, y=None):
|
| 600 |
+
"""
|
| 601 |
+
Transform an image by applying a sigmoid function.
|
| 602 |
+
|
| 603 |
+
Arguments
|
| 604 |
+
---------
|
| 605 |
+
X : ANTsImage
|
| 606 |
+
image to transform
|
| 607 |
+
|
| 608 |
+
y : ANTsImage (optional)
|
| 609 |
+
another image to transform.
|
| 610 |
+
|
| 611 |
+
Example
|
| 612 |
+
-------
|
| 613 |
+
>>> import ants
|
| 614 |
+
>>> flipper = ants.contrib.FlipImage(0,1)
|
| 615 |
+
>>> img2d = ants.image_read(ants.get_data('r16'))
|
| 616 |
+
>>> img2d_r = flipper.transform(img2d)
|
| 617 |
+
>>> ants.plot(img2d)
|
| 618 |
+
>>> ants.plot(img2d_r)
|
| 619 |
+
>>> flipper2 = ants.contrib.FlipImage(1,0)
|
| 620 |
+
>>> img2d = ants.image_read(ants.get_data('r16'))
|
| 621 |
+
>>> img2d_r = flipper2.transform(img2d)
|
| 622 |
+
>>> ants.plot(img2d)
|
| 623 |
+
>>> ants.plot(img2d_r)
|
| 624 |
+
"""
|
| 625 |
+
if X.pixeltype != 'float':
|
| 626 |
+
raise ValueError('image.pixeltype must be float ... use TypeCast transform or clone to float')
|
| 627 |
+
|
| 628 |
+
insuffix = X._libsuffix
|
| 629 |
+
cast_fn = utils.get_lib_fn('flipAntsImage%s' % (insuffix))
|
| 630 |
+
casted_ptr = cast_fn(X.pointer, self.axis1, self.axis2)
|
| 631 |
+
return iio.ANTsImage(pixeltype=X.pixeltype, dimension=X.dimension,
|
| 632 |
+
components=X.components, pointer=casted_ptr,
|
| 633 |
+
origin=X.origin)
|
| 634 |
+
|
| 635 |
+
|
| 636 |
+
class TranslateImage(object):
|
| 637 |
+
"""
|
| 638 |
+
Translate an image in physical space. This function calls
|
| 639 |
+
highly optimized ITK/C++ code.
|
| 640 |
+
"""
|
| 641 |
+
def __init__(self, translation, reference=None, interp='linear'):
|
| 642 |
+
"""
|
| 643 |
+
Initialize a TranslateImage transform
|
| 644 |
+
|
| 645 |
+
Arguments
|
| 646 |
+
---------
|
| 647 |
+
translation : list, tuple, or numpy.ndarray
|
| 648 |
+
absolute pixel transformation in each axis
|
| 649 |
+
|
| 650 |
+
reference : ANTsImage (optional)
|
| 651 |
+
image which provides the reference physical space in which
|
| 652 |
+
to perform the transform
|
| 653 |
+
|
| 654 |
+
interp : string
|
| 655 |
+
type of interpolation to use
|
| 656 |
+
options: linear, nearest
|
| 657 |
+
|
| 658 |
+
Example
|
| 659 |
+
-------
|
| 660 |
+
>>> import ants
|
| 661 |
+
>>> translater = ants.contrib.TranslateImage((10,10), interp='linear')
|
| 662 |
+
"""
|
| 663 |
+
if interp not in {'linear', 'nearest'}:
|
| 664 |
+
raise ValueError('interp must be one of {linear, nearest}')
|
| 665 |
+
|
| 666 |
+
self.translation = list(translation)
|
| 667 |
+
self.reference = reference
|
| 668 |
+
self.interp = interp
|
| 669 |
+
|
| 670 |
+
def transform(self, X, y=None):
|
| 671 |
+
"""
|
| 672 |
+
Example
|
| 673 |
+
-------
|
| 674 |
+
>>> import ants
|
| 675 |
+
>>> translater = ants.contrib.TranslateImage((40,0))
|
| 676 |
+
>>> img2d = ants.image_read(ants.get_data('r16'))
|
| 677 |
+
>>> img2d_r = translater.transform(img2d)
|
| 678 |
+
>>> ants.plot(img2d, img2d_r)
|
| 679 |
+
>>> translater = ants.contrib.TranslateImage((40,0,0))
|
| 680 |
+
>>> img3d = ants.image_read(ants.get_data('mni'))
|
| 681 |
+
>>> img3d_r = translater.transform(img3d)
|
| 682 |
+
>>> ants.plot(img3d, img3d_r, axis=2)
|
| 683 |
+
"""
|
| 684 |
+
if X.pixeltype != 'float':
|
| 685 |
+
raise ValueError('image.pixeltype must be float ... use TypeCast transform or clone to float')
|
| 686 |
+
|
| 687 |
+
if len(self.translation) != X.dimension:
|
| 688 |
+
raise ValueError('must give a translation value for each image dimension')
|
| 689 |
+
|
| 690 |
+
if self.reference is None:
|
| 691 |
+
reference = X
|
| 692 |
+
else:
|
| 693 |
+
reference = self.reference
|
| 694 |
+
|
| 695 |
+
insuffix = X._libsuffix
|
| 696 |
+
cast_fn = utils.get_lib_fn('translateAntsImage%s_%s' % (insuffix, self.interp))
|
| 697 |
+
casted_ptr = cast_fn(X.pointer, reference.pointer, self.translation)
|
| 698 |
+
return iio.ANTsImage(pixeltype=X.pixeltype, dimension=X.dimension,
|
| 699 |
+
components=X.components, pointer=casted_ptr)
|
| 700 |
+
|
| 701 |
+
|
| 702 |
+
class ScaleImage(object):
|
| 703 |
+
"""
|
| 704 |
+
Scale an image in physical space. This function calls
|
| 705 |
+
highly optimized ITK/C++ code.
|
| 706 |
+
"""
|
| 707 |
+
def __init__(self, scale, reference=None, interp='linear'):
|
| 708 |
+
"""
|
| 709 |
+
Initialize a TranslateImage transform
|
| 710 |
+
|
| 711 |
+
Arguments
|
| 712 |
+
---------
|
| 713 |
+
scale : list, tuple, or numpy.ndarray
|
| 714 |
+
relative scaling along each axis
|
| 715 |
+
|
| 716 |
+
reference : ANTsImage (optional)
|
| 717 |
+
image which provides the reference physical space in which
|
| 718 |
+
to perform the transform
|
| 719 |
+
|
| 720 |
+
interp : string
|
| 721 |
+
type of interpolation to use
|
| 722 |
+
options: linear, nearest
|
| 723 |
+
|
| 724 |
+
Example
|
| 725 |
+
-------
|
| 726 |
+
>>> import ants
|
| 727 |
+
>>> translater = ants.contrib.TranslateImage((10,10), interp='linear')
|
| 728 |
+
"""
|
| 729 |
+
if interp not in {'linear', 'nearest'}:
|
| 730 |
+
raise ValueError('interp must be one of {linear, nearest}')
|
| 731 |
+
|
| 732 |
+
self.scale = list(scale)
|
| 733 |
+
self.reference = reference
|
| 734 |
+
self.interp = interp
|
| 735 |
+
|
| 736 |
+
def transform(self, X, y=None):
|
| 737 |
+
"""
|
| 738 |
+
Example
|
| 739 |
+
-------
|
| 740 |
+
>>> import ants
|
| 741 |
+
>>> scaler = ants.contrib.ScaleImage((1.2,1.2))
|
| 742 |
+
>>> img2d = ants.image_read(ants.get_data('r16'))
|
| 743 |
+
>>> img2d_r = scaler.transform(img2d)
|
| 744 |
+
>>> ants.plot(img2d, img2d_r)
|
| 745 |
+
>>> scaler = ants.contrib.ScaleImage((1.2,1.2,1.2))
|
| 746 |
+
>>> img3d = ants.image_read(ants.get_data('mni'))
|
| 747 |
+
>>> img3d_r = scaler.transform(img3d)
|
| 748 |
+
>>> ants.plot(img3d, img3d_r)
|
| 749 |
+
"""
|
| 750 |
+
if X.pixeltype != 'float':
|
| 751 |
+
raise ValueError('image.pixeltype must be float ... use TypeCast transform or clone to float')
|
| 752 |
+
|
| 753 |
+
if len(self.scale) != X.dimension:
|
| 754 |
+
raise ValueError('must give a scale value for each image dimension')
|
| 755 |
+
|
| 756 |
+
if self.reference is None:
|
| 757 |
+
reference = X
|
| 758 |
+
else:
|
| 759 |
+
reference = self.reference
|
| 760 |
+
|
| 761 |
+
insuffix = X._libsuffix
|
| 762 |
+
cast_fn = utils.get_lib_fn('scaleAntsImage%s_%s' % (insuffix, self.interp))
|
| 763 |
+
casted_ptr = cast_fn(X.pointer, reference.pointer, self.scale)
|
| 764 |
+
return iio.ANTsImage(pixeltype=X.pixeltype, dimension=X.dimension,
|
| 765 |
+
components=X.components, pointer=casted_ptr)
|
| 766 |
+
|
MindEyeV2/antspy/ants/contrib/sklearn_interface/__init__.py
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
|
| 2 |
+
|
| 3 |
+
from .sklearn_registration import *
|
MindEyeV2/antspy/ants/contrib/sklearn_interface/sklearn_registration.py
ADDED
|
@@ -0,0 +1,149 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
|
| 2 |
+
|
| 3 |
+
__all__ = ['RigidRegistration']
|
| 4 |
+
|
| 5 |
+
from ...registration import interface, apply_transforms
|
| 6 |
+
|
| 7 |
+
|
| 8 |
+
class Registration(object):
|
| 9 |
+
"""
|
| 10 |
+
How would it work:
|
| 11 |
+
|
| 12 |
+
# Co-registration within-visit
|
| 13 |
+
reg = Registration('Rigid', fixed_image=t1_template,
|
| 14 |
+
save_dir=save_dir, save_suffix='_coreg')
|
| 15 |
+
for img in other_imgs:
|
| 16 |
+
reg.fit(img)
|
| 17 |
+
|
| 18 |
+
for img in [flair_img, t2_img]:
|
| 19 |
+
reg = Registration('Rigid', fixed_image=t1_template,
|
| 20 |
+
save_dir=save_dir, save_suffix='_coreg')
|
| 21 |
+
reg.fit(img)
|
| 22 |
+
|
| 23 |
+
# Co-registration across-visit
|
| 24 |
+
reg = Registration('Rigid', fixed_image=t1)
|
| 25 |
+
reg.fit(moving=t1_followup)
|
| 26 |
+
|
| 27 |
+
# now align all followups with first visit
|
| 28 |
+
for img in [flair_follwup, t2_followup]:
|
| 29 |
+
img_reg = reg.transform(img)
|
| 30 |
+
|
| 31 |
+
# conversly, align all first visits with followups
|
| 32 |
+
for img in [flair, t2]:
|
| 33 |
+
img_reg = reg.inverse_transform(img)
|
| 34 |
+
"""
|
| 35 |
+
|
| 36 |
+
def __init__(self, type_of_transform, fixed_image):
|
| 37 |
+
"""
|
| 38 |
+
Properties:
|
| 39 |
+
type_of_transform
|
| 40 |
+
fixed_image (template)
|
| 41 |
+
save_dir (where to save outputs)
|
| 42 |
+
save_suffix (what to append to saved outputs)
|
| 43 |
+
save_prefix (what to preppend to saved outputs)
|
| 44 |
+
"""
|
| 45 |
+
self.type_of_transform = type_of_transform
|
| 46 |
+
self.fixed_image = fixed_image
|
| 47 |
+
|
| 48 |
+
def fit(self, X, y=None):
|
| 49 |
+
"""
|
| 50 |
+
X : ANTsImage | string | list of ANTsImage types | list of strings
|
| 51 |
+
images to register to fixed image
|
| 52 |
+
|
| 53 |
+
y : string | list of strings
|
| 54 |
+
labels for images
|
| 55 |
+
"""
|
| 56 |
+
moving_images = X if isinstance(X, (list,tuple)) else [X]
|
| 57 |
+
moving_labels = y if y is not None else [i for i in range(len(moving_images))]
|
| 58 |
+
fixed_image = self.fixed_image
|
| 59 |
+
|
| 60 |
+
self.fwdtransforms_ = {}
|
| 61 |
+
self.invtransforms_ = {}
|
| 62 |
+
self.warpedmovout_ = {}
|
| 63 |
+
self.warpedfixout_ = {}
|
| 64 |
+
|
| 65 |
+
for moving_image, moving_label in zip(moving_images, moving_labels):
|
| 66 |
+
fit_result = interface.registration(fixed_image,
|
| 67 |
+
moving_image,
|
| 68 |
+
type_of_transform=self.type_of_transform,
|
| 69 |
+
initial_transform=None,
|
| 70 |
+
outprefix='',
|
| 71 |
+
mask=None,
|
| 72 |
+
grad_step=0.2,
|
| 73 |
+
flow_sigma=3,
|
| 74 |
+
total_sigma=0,
|
| 75 |
+
aff_metric='mattes',
|
| 76 |
+
aff_sampling=32,
|
| 77 |
+
syn_metric='mattes',
|
| 78 |
+
syn_sampling=32,
|
| 79 |
+
reg_iterations=(40,20,0),
|
| 80 |
+
verbose=False)
|
| 81 |
+
|
| 82 |
+
self.fwdtransforms_[moving_label] = fit_result['fwdtransforms']
|
| 83 |
+
self.invtransforms_[moving_label] = fit_result['invtransforms']
|
| 84 |
+
self.warpedmovout_[moving_label] = fit_result['warpedmovout']
|
| 85 |
+
self.warpedfixout_[moving_label] = fit_result['warpedfixout']
|
| 86 |
+
|
| 87 |
+
return self
|
| 88 |
+
|
| 89 |
+
def transform(self, X, y=None):
|
| 90 |
+
pass
|
| 91 |
+
|
| 92 |
+
|
| 93 |
+
class RigidRegistration(object):
|
| 94 |
+
"""
|
| 95 |
+
Rigid Registration as a Scikit-Learn compatible transform class
|
| 96 |
+
|
| 97 |
+
Example
|
| 98 |
+
-------
|
| 99 |
+
>>> import ants
|
| 100 |
+
>>> import ants.extra as extrants
|
| 101 |
+
>>> fi = ants.image_read(ants.get_data('r16'))
|
| 102 |
+
>>> mi = ants.image_read(ants.get_data('r64'))
|
| 103 |
+
>>> regtx = extrants.RigidRegistration()
|
| 104 |
+
>>> regtx.fit(fi, mi)
|
| 105 |
+
>>> mi_r = regtx.transform(mi)
|
| 106 |
+
>>> ants.plot(fi, mi_r.iMath_Canny(1, 2, 4).iMath('MD',1))
|
| 107 |
+
"""
|
| 108 |
+
def __init__(self, fixed_image=None):
|
| 109 |
+
self.type_of_transform = 'Rigid'
|
| 110 |
+
self.fixed_image = fixed_image
|
| 111 |
+
|
| 112 |
+
def fit(self, moving_image, fixed_image=None):
|
| 113 |
+
if fixed_image is None:
|
| 114 |
+
if self.fixed_image is None:
|
| 115 |
+
raise ValueError('must give fixed_image in fit() or set it in __init__')
|
| 116 |
+
fixed_image = self.fixed_image
|
| 117 |
+
|
| 118 |
+
fit_result = interface.registration(fixed_image,
|
| 119 |
+
moving_image,
|
| 120 |
+
type_of_transform=self.type_of_transform,
|
| 121 |
+
initial_transform=None,
|
| 122 |
+
outprefix='',
|
| 123 |
+
mask=None,
|
| 124 |
+
grad_step=0.2,
|
| 125 |
+
flow_sigma=3,
|
| 126 |
+
total_sigma=0,
|
| 127 |
+
aff_metric='mattes',
|
| 128 |
+
aff_sampling=32,
|
| 129 |
+
syn_metric='mattes',
|
| 130 |
+
syn_sampling=32,
|
| 131 |
+
reg_iterations=(40,20,0),
|
| 132 |
+
verbose=False)
|
| 133 |
+
self._fit_result = fit_result
|
| 134 |
+
self.fwdtransforms_ = fit_result['fwdtransforms']
|
| 135 |
+
self.invtransforms_ = fit_result['invtransforms']
|
| 136 |
+
self.warpedmovout_ = fit_result['warpedmovout']
|
| 137 |
+
self.warpedfiout_ = fit_result['warpedfixout']
|
| 138 |
+
|
| 139 |
+
def transform(self, moving_image, fixed_image=None):
|
| 140 |
+
result = apply_transforms(fixed=fixed_image, moving=moving_image,
|
| 141 |
+
transformlist=self.fwdtransforms)
|
| 142 |
+
return result
|
| 143 |
+
|
| 144 |
+
def inverse_transform(self, moving_image, fixed_image=None):
|
| 145 |
+
result = apply_transforms(fixed=fixed_image, moving=moving_image,
|
| 146 |
+
transformlist=self.invtransforms)
|
| 147 |
+
return result
|
| 148 |
+
|
| 149 |
+
|
MindEyeV2/antspy/ants/plotting/__init__.py
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
|
| 2 |
+
from .plot import plot
|
| 3 |
+
from .movie import movie
|
| 4 |
+
from .plot_hist import plot_hist
|
| 5 |
+
from .plot_grid import plot_grid
|
| 6 |
+
from .plot_ortho import plot_ortho
|
| 7 |
+
from .plot_ortho_stack import plot_ortho_stack
|
| 8 |
+
from .plot_directory import plot_directory
|
MindEyeV2/antspy/ants/plotting/movie.py
ADDED
|
@@ -0,0 +1,87 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
Functions for plotting ants images
|
| 3 |
+
"""
|
| 4 |
+
|
| 5 |
+
|
| 6 |
+
__all__ = [
|
| 7 |
+
"movie"
|
| 8 |
+
]
|
| 9 |
+
|
| 10 |
+
import fnmatch
|
| 11 |
+
import math
|
| 12 |
+
import os
|
| 13 |
+
import warnings
|
| 14 |
+
|
| 15 |
+
from matplotlib import gridspec
|
| 16 |
+
import matplotlib.pyplot as plt
|
| 17 |
+
import matplotlib.patheffects as path_effects
|
| 18 |
+
import matplotlib.lines as mlines
|
| 19 |
+
import matplotlib.patches as patches
|
| 20 |
+
import matplotlib.mlab as mlab
|
| 21 |
+
import matplotlib.animation as animation
|
| 22 |
+
from mpl_toolkits.axes_grid1.inset_locator import inset_axes
|
| 23 |
+
|
| 24 |
+
|
| 25 |
+
import numpy as np
|
| 26 |
+
from ants.decorators import image_method
|
| 27 |
+
|
| 28 |
+
@image_method
|
| 29 |
+
def movie(image, filename=None, writer=None, fps=30):
|
| 30 |
+
"""
|
| 31 |
+
Create and save a movie - mp4, gif, etc - of the various
|
| 32 |
+
2D slices of a 3D ants image
|
| 33 |
+
|
| 34 |
+
Try this:
|
| 35 |
+
conda install -c conda-forge ffmpeg
|
| 36 |
+
|
| 37 |
+
Example
|
| 38 |
+
-------
|
| 39 |
+
>>> import ants
|
| 40 |
+
>>> mni = ants.image_read(ants.get_data('mni'))
|
| 41 |
+
>>> ants.movie(mni, filename='~/desktop/movie.mp4')
|
| 42 |
+
"""
|
| 43 |
+
|
| 44 |
+
image = image.pad_image()
|
| 45 |
+
img_arr = image.numpy()
|
| 46 |
+
|
| 47 |
+
minidx = max(0, np.where(image > 0)[0][0] - 5)
|
| 48 |
+
maxidx = max(image.shape[0], np.where(image > 0)[0][-1] + 5)
|
| 49 |
+
|
| 50 |
+
# Creare your figure and axes
|
| 51 |
+
fig, ax = plt.subplots(1)
|
| 52 |
+
|
| 53 |
+
im = ax.imshow(
|
| 54 |
+
img_arr[minidx, :, :],
|
| 55 |
+
animated=True,
|
| 56 |
+
cmap="Greys_r",
|
| 57 |
+
vmin=image.quantile(0.05),
|
| 58 |
+
vmax=image.quantile(0.95),
|
| 59 |
+
)
|
| 60 |
+
|
| 61 |
+
ax.axis("off")
|
| 62 |
+
|
| 63 |
+
def init():
|
| 64 |
+
fig.axes("off")
|
| 65 |
+
return (im,)
|
| 66 |
+
|
| 67 |
+
def updatefig(frame):
|
| 68 |
+
im.set_array(img_arr[frame, :, :])
|
| 69 |
+
return (im,)
|
| 70 |
+
|
| 71 |
+
ani = animation.FuncAnimation(
|
| 72 |
+
fig,
|
| 73 |
+
updatefig,
|
| 74 |
+
frames=np.arange(minidx, maxidx),
|
| 75 |
+
# init_func=init,
|
| 76 |
+
interval=50,
|
| 77 |
+
blit=True,
|
| 78 |
+
)
|
| 79 |
+
|
| 80 |
+
if writer is None:
|
| 81 |
+
writer = animation.FFMpegWriter(fps=fps)
|
| 82 |
+
|
| 83 |
+
if filename is not None:
|
| 84 |
+
filename = os.path.expanduser(filename)
|
| 85 |
+
ani.save(filename, writer=writer)
|
| 86 |
+
else:
|
| 87 |
+
plt.show()
|
MindEyeV2/antspy/ants/plotting/plot.py
ADDED
|
@@ -0,0 +1,486 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
Functions for plotting ants images
|
| 3 |
+
"""
|
| 4 |
+
|
| 5 |
+
|
| 6 |
+
__all__ = [
|
| 7 |
+
"plot"
|
| 8 |
+
]
|
| 9 |
+
|
| 10 |
+
import fnmatch
|
| 11 |
+
import math
|
| 12 |
+
import os
|
| 13 |
+
import warnings
|
| 14 |
+
|
| 15 |
+
from matplotlib import gridspec
|
| 16 |
+
import matplotlib.pyplot as plt
|
| 17 |
+
import matplotlib.patheffects as path_effects
|
| 18 |
+
import matplotlib.lines as mlines
|
| 19 |
+
import matplotlib.patches as patches
|
| 20 |
+
import matplotlib.mlab as mlab
|
| 21 |
+
import matplotlib.animation as animation
|
| 22 |
+
from mpl_toolkits.axes_grid1.inset_locator import inset_axes
|
| 23 |
+
|
| 24 |
+
import numpy as np
|
| 25 |
+
import ants
|
| 26 |
+
from ants.decorators import image_method
|
| 27 |
+
|
| 28 |
+
@image_method
|
| 29 |
+
def plot(
|
| 30 |
+
image,
|
| 31 |
+
overlay=None,
|
| 32 |
+
blend=False,
|
| 33 |
+
alpha=1,
|
| 34 |
+
cmap="Greys_r",
|
| 35 |
+
overlay_cmap="turbo",
|
| 36 |
+
overlay_alpha=0.9,
|
| 37 |
+
vminol=None,
|
| 38 |
+
vmaxol=None,
|
| 39 |
+
cbar=False,
|
| 40 |
+
cbar_length=0.8,
|
| 41 |
+
cbar_dx=0.0,
|
| 42 |
+
cbar_vertical=True,
|
| 43 |
+
axis=0,
|
| 44 |
+
nslices=12,
|
| 45 |
+
slices=None,
|
| 46 |
+
ncol=None,
|
| 47 |
+
slice_buffer=None,
|
| 48 |
+
black_bg=True,
|
| 49 |
+
bg_thresh_quant=0.01,
|
| 50 |
+
bg_val_quant=0.99,
|
| 51 |
+
domain_image_map=None,
|
| 52 |
+
crop=False,
|
| 53 |
+
scale=False,
|
| 54 |
+
reverse=False,
|
| 55 |
+
title=None,
|
| 56 |
+
title_fontsize=20,
|
| 57 |
+
title_dx=0.0,
|
| 58 |
+
title_dy=0.0,
|
| 59 |
+
filename=None,
|
| 60 |
+
dpi=500,
|
| 61 |
+
figsize=1.5,
|
| 62 |
+
reorient=True,
|
| 63 |
+
resample=True,
|
| 64 |
+
):
|
| 65 |
+
"""
|
| 66 |
+
Plot an ANTsImage.
|
| 67 |
+
|
| 68 |
+
Use mask_image and/or threshold_image to preprocess images to be be
|
| 69 |
+
overlaid and display the overlays in a given range. See the wiki examples.
|
| 70 |
+
|
| 71 |
+
By default, images will be reoriented to 'LAI' orientation before plotting.
|
| 72 |
+
So, if axis == 0, the images will be ordered from the
|
| 73 |
+
left side of the brain to the right side of the brain. If axis == 1,
|
| 74 |
+
the images will be ordered from the anterior (front) of the brain to
|
| 75 |
+
the posterior (back) of the brain. And if axis == 2, the images will
|
| 76 |
+
be ordered from the inferior (bottom) of the brain to the superior (top)
|
| 77 |
+
of the brain.
|
| 78 |
+
|
| 79 |
+
ANTsR function: `plot.antsImage`
|
| 80 |
+
|
| 81 |
+
Arguments
|
| 82 |
+
---------
|
| 83 |
+
image : ANTsImage
|
| 84 |
+
image to plot
|
| 85 |
+
|
| 86 |
+
overlay : ANTsImage
|
| 87 |
+
image to overlay on base image
|
| 88 |
+
|
| 89 |
+
cmap : string
|
| 90 |
+
colormap to use for base image. See matplotlib.
|
| 91 |
+
|
| 92 |
+
overlay_cmap : string
|
| 93 |
+
colormap to use for overlay images, if applicable. See matplotlib.
|
| 94 |
+
|
| 95 |
+
overlay_alpha : float
|
| 96 |
+
level of transparency for any overlays. Smaller value means
|
| 97 |
+
the overlay is more transparent. See matplotlib.
|
| 98 |
+
|
| 99 |
+
axis : integer
|
| 100 |
+
which axis to plot along if image is 3D
|
| 101 |
+
|
| 102 |
+
nslices : integer
|
| 103 |
+
number of slices to plot if image is 3D
|
| 104 |
+
|
| 105 |
+
slices : list or tuple of integers
|
| 106 |
+
specific slice indices to plot if image is 3D.
|
| 107 |
+
If given, this will override `nslices`.
|
| 108 |
+
This can be absolute array indices (e.g. (80,100,120)), or
|
| 109 |
+
this can be relative array indices (e.g. (0.4,0.5,0.6))
|
| 110 |
+
|
| 111 |
+
ncol : integer
|
| 112 |
+
Number of columns to have on the plot if image is 3D.
|
| 113 |
+
|
| 114 |
+
slice_buffer : integer
|
| 115 |
+
how many slices to buffer when finding the non-zero slices of
|
| 116 |
+
a 3D images. So, if slice_buffer = 10, then the first slice
|
| 117 |
+
in a 3D image will be the first non-zero slice index plus 10 more
|
| 118 |
+
slices.
|
| 119 |
+
|
| 120 |
+
black_bg : boolean
|
| 121 |
+
if True, the background of the image(s) will be black.
|
| 122 |
+
if False, the background of the image(s) will be determined by the
|
| 123 |
+
values `bg_thresh_quant` and `bg_val_quant`.
|
| 124 |
+
|
| 125 |
+
bg_thresh_quant : float
|
| 126 |
+
if white_bg=True, the background will be determined by thresholding
|
| 127 |
+
the image at the `bg_thresh` quantile value and setting the background
|
| 128 |
+
intensity to the `bg_val` quantile value.
|
| 129 |
+
This value should be in [0, 1] - somewhere around 0.01 is recommended.
|
| 130 |
+
- equal to 1 will threshold the entire image
|
| 131 |
+
- equal to 0 will threshold none of the image
|
| 132 |
+
|
| 133 |
+
bg_val_quant : float
|
| 134 |
+
if white_bg=True, the background will be determined by thresholding
|
| 135 |
+
the image at the `bg_thresh` quantile value and setting the background
|
| 136 |
+
intensity to the `bg_val` quantile value.
|
| 137 |
+
This value should be in [0, 1]
|
| 138 |
+
- equal to 1 is pure white
|
| 139 |
+
- equal to 0 is pure black
|
| 140 |
+
- somewhere in between is gray
|
| 141 |
+
|
| 142 |
+
domain_image_map : ANTsImage
|
| 143 |
+
this input ANTsImage or list of ANTsImage types contains a reference image
|
| 144 |
+
`domain_image` and optional reference mapping named `domainMap`.
|
| 145 |
+
If supplied, the image(s) to be plotted will be mapped to the domain
|
| 146 |
+
image space before plotting - useful for non-standard image orientations.
|
| 147 |
+
|
| 148 |
+
crop : boolean
|
| 149 |
+
if true, the image(s) will be cropped to their bounding boxes, resulting
|
| 150 |
+
in a potentially smaller image size.
|
| 151 |
+
if false, the image(s) will not be cropped
|
| 152 |
+
|
| 153 |
+
scale : boolean or 2-tuple
|
| 154 |
+
if true, nothing will happen to intensities of image(s) and overlay(s)
|
| 155 |
+
if false, dynamic range will be maximized when visualizing overlays
|
| 156 |
+
if 2-tuple, the image will be dynamically scaled between these quantiles
|
| 157 |
+
|
| 158 |
+
reverse : boolean
|
| 159 |
+
if true, the order in which the slices are plotted will be reversed.
|
| 160 |
+
This is useful if you want to plot from the front of the brain first
|
| 161 |
+
to the back of the brain, or vice-versa
|
| 162 |
+
|
| 163 |
+
title : string
|
| 164 |
+
add a title to the plot
|
| 165 |
+
|
| 166 |
+
filename : string
|
| 167 |
+
if given, the resulting image will be saved to this file
|
| 168 |
+
|
| 169 |
+
dpi : integer
|
| 170 |
+
determines resolution of image if saved to file. Higher values
|
| 171 |
+
result in higher resolution images, but at a cost of having a
|
| 172 |
+
larger file size
|
| 173 |
+
|
| 174 |
+
resample : bool
|
| 175 |
+
if true, resample image if spacing is very unbalanced.
|
| 176 |
+
|
| 177 |
+
Example
|
| 178 |
+
-------
|
| 179 |
+
>>> import ants
|
| 180 |
+
>>> import numpy as np
|
| 181 |
+
>>> img = ants.image_read(ants.get_data('r16'))
|
| 182 |
+
>>> segs = img.kmeans_segmentation(k=3)['segmentation']
|
| 183 |
+
>>> ants.plot(img, segs*(segs==1), crop=True)
|
| 184 |
+
>>> ants.plot(img, segs*(segs==1), crop=False)
|
| 185 |
+
>>> mni = ants.image_read(ants.get_data('mni'))
|
| 186 |
+
>>> segs = mni.kmeans_segmentation(k=3)['segmentation']
|
| 187 |
+
>>> ants.plot(mni, segs*(segs==1), crop=False)
|
| 188 |
+
"""
|
| 189 |
+
if (axis == "x") or (axis == "saggittal"):
|
| 190 |
+
axis = 0
|
| 191 |
+
if (axis == "y") or (axis == "coronal"):
|
| 192 |
+
axis = 1
|
| 193 |
+
if (axis == "z") or (axis == "axial"):
|
| 194 |
+
axis = 2
|
| 195 |
+
|
| 196 |
+
def mirror_matrix(x):
|
| 197 |
+
return x[::-1, :]
|
| 198 |
+
|
| 199 |
+
def rotate270_matrix(x):
|
| 200 |
+
return mirror_matrix(x.T)
|
| 201 |
+
|
| 202 |
+
def rotate180_matrix(x):
|
| 203 |
+
return x[::-1, ::-1]
|
| 204 |
+
|
| 205 |
+
def rotate90_matrix(x):
|
| 206 |
+
return x.T
|
| 207 |
+
|
| 208 |
+
def reorient_slice(x, axis):
|
| 209 |
+
if axis != 2:
|
| 210 |
+
x = rotate90_matrix(x)
|
| 211 |
+
if axis == 2:
|
| 212 |
+
x = rotate270_matrix(x)
|
| 213 |
+
x = mirror_matrix(x)
|
| 214 |
+
return x
|
| 215 |
+
|
| 216 |
+
|
| 217 |
+
# handle `image` argument
|
| 218 |
+
if isinstance(image, str):
|
| 219 |
+
image = ants.image_read(image)
|
| 220 |
+
if not ants.is_image(image):
|
| 221 |
+
raise ValueError("image argument must be an ANTsImage")
|
| 222 |
+
|
| 223 |
+
if np.all(np.equal(image.numpy(), 0.0)):
|
| 224 |
+
warnings.warn("Image must be non-zero. will not plot.")
|
| 225 |
+
return
|
| 226 |
+
|
| 227 |
+
# need this hack because of a weird NaN warning from matplotlib with overlays
|
| 228 |
+
warnings.simplefilter("ignore")
|
| 229 |
+
|
| 230 |
+
if (image.pixeltype not in {"float", "double"}) or (image.is_rgb):
|
| 231 |
+
scale = False # turn off scaling if image is discrete
|
| 232 |
+
|
| 233 |
+
# handle `overlay` argument
|
| 234 |
+
if overlay is not None:
|
| 235 |
+
if isinstance(overlay, str):
|
| 236 |
+
overlay = ants.image_read(overlay)
|
| 237 |
+
if vminol is None:
|
| 238 |
+
vminol = overlay.min()
|
| 239 |
+
if vmaxol is None:
|
| 240 |
+
vmaxol = overlay.max()
|
| 241 |
+
if not ants.is_image(overlay):
|
| 242 |
+
raise ValueError("overlay argument must be an ANTsImage")
|
| 243 |
+
if overlay.components > 1:
|
| 244 |
+
raise ValueError("overlay cannot have more than one voxel component")
|
| 245 |
+
|
| 246 |
+
if not ants.image_physical_space_consistency(image, overlay):
|
| 247 |
+
overlay = ants.resample_image_to_target(overlay, image, interp_type="nearestNeighbor")
|
| 248 |
+
|
| 249 |
+
if blend:
|
| 250 |
+
if alpha == 1:
|
| 251 |
+
alpha = 0.5
|
| 252 |
+
image = image * alpha + overlay * (1 - alpha)
|
| 253 |
+
overlay = None
|
| 254 |
+
alpha = 1.0
|
| 255 |
+
|
| 256 |
+
# handle `domain_image_map` argument
|
| 257 |
+
if domain_image_map is not None:
|
| 258 |
+
tx = ants.new_ants_transform(
|
| 259 |
+
precision="float",
|
| 260 |
+
transform_type="AffineTransform",
|
| 261 |
+
dimension=image.dimension,
|
| 262 |
+
)
|
| 263 |
+
image = ants.apply_ants_transform_to_image(tx, image, domain_image_map)
|
| 264 |
+
if overlay is not None:
|
| 265 |
+
overlay = ants.apply_ants_transform_to_image(
|
| 266 |
+
tx, overlay, domain_image_map, interpolation="nearestNeighbor"
|
| 267 |
+
)
|
| 268 |
+
|
| 269 |
+
## single-channel images ##
|
| 270 |
+
if image.components == 1:
|
| 271 |
+
|
| 272 |
+
# potentially crop image
|
| 273 |
+
if crop:
|
| 274 |
+
plotmask = image.get_mask(cleanup=0)
|
| 275 |
+
if plotmask.max() == 0:
|
| 276 |
+
plotmask += 1
|
| 277 |
+
image = image.crop_image(plotmask)
|
| 278 |
+
if overlay is not None:
|
| 279 |
+
overlay = overlay.crop_image(plotmask)
|
| 280 |
+
|
| 281 |
+
# potentially find dynamic range
|
| 282 |
+
if scale == True:
|
| 283 |
+
vmin, vmax = image.quantile((0.05, 0.95))
|
| 284 |
+
elif isinstance(scale, (list, tuple)):
|
| 285 |
+
if len(scale) != 2:
|
| 286 |
+
raise ValueError(
|
| 287 |
+
"scale argument must be boolean or list/tuple with two values"
|
| 288 |
+
)
|
| 289 |
+
vmin, vmax = image.quantile(scale)
|
| 290 |
+
else:
|
| 291 |
+
vmin = None
|
| 292 |
+
vmax = None
|
| 293 |
+
|
| 294 |
+
# Plot 2D image
|
| 295 |
+
if image.dimension == 2:
|
| 296 |
+
|
| 297 |
+
img_arr = image.numpy()
|
| 298 |
+
img_arr = rotate90_matrix(img_arr)
|
| 299 |
+
|
| 300 |
+
if not black_bg:
|
| 301 |
+
img_arr[img_arr < image.quantile(bg_thresh_quant)] = image.quantile(
|
| 302 |
+
bg_val_quant
|
| 303 |
+
)
|
| 304 |
+
|
| 305 |
+
if overlay is not None:
|
| 306 |
+
ov_arr = overlay.numpy()
|
| 307 |
+
mask = ov_arr == 0
|
| 308 |
+
mask = np.ma.masked_where(mask == 0, mask)
|
| 309 |
+
ov_arr = np.ma.masked_array(ov_arr, mask)
|
| 310 |
+
ov_arr = rotate90_matrix(ov_arr)
|
| 311 |
+
|
| 312 |
+
fig = plt.figure()
|
| 313 |
+
if title is not None:
|
| 314 |
+
fig.suptitle(
|
| 315 |
+
title, fontsize=title_fontsize, x=0.5 + title_dx, y=0.95 + title_dy
|
| 316 |
+
)
|
| 317 |
+
|
| 318 |
+
ax = plt.subplot(111)
|
| 319 |
+
|
| 320 |
+
# plot main image
|
| 321 |
+
im = ax.imshow(img_arr, cmap=cmap, alpha=alpha, vmin=vmin, vmax=vmax)
|
| 322 |
+
|
| 323 |
+
if overlay is not None:
|
| 324 |
+
im = ax.imshow(ov_arr, alpha=overlay_alpha, cmap=overlay_cmap,
|
| 325 |
+
vmin=vminol, vmax=vmaxol )
|
| 326 |
+
|
| 327 |
+
if cbar:
|
| 328 |
+
cbar_orient = "vertical" if cbar_vertical else "horizontal"
|
| 329 |
+
fig.colorbar(im, orientation=cbar_orient)
|
| 330 |
+
|
| 331 |
+
plt.axis("off")
|
| 332 |
+
|
| 333 |
+
# Plot 3D image
|
| 334 |
+
elif image.dimension == 3:
|
| 335 |
+
# resample image if spacing is very unbalanced
|
| 336 |
+
spacing = [s for i, s in enumerate(image.spacing) if i != axis]
|
| 337 |
+
was_resampled = False
|
| 338 |
+
if (max(spacing) / min(spacing)) > 3.0 and resample:
|
| 339 |
+
was_resampled = True
|
| 340 |
+
new_spacing = (1, 1, 1)
|
| 341 |
+
image = image.resample_image(tuple(new_spacing))
|
| 342 |
+
if overlay is not None:
|
| 343 |
+
overlay = overlay.resample_image(tuple(new_spacing))
|
| 344 |
+
|
| 345 |
+
if reorient:
|
| 346 |
+
image = image.reorient_image2("LAI")
|
| 347 |
+
img_arr = image.numpy()
|
| 348 |
+
# reorder dims so that chosen axis is first
|
| 349 |
+
img_arr = np.rollaxis(img_arr, axis)
|
| 350 |
+
|
| 351 |
+
if overlay is not None:
|
| 352 |
+
if reorient:
|
| 353 |
+
overlay = overlay.reorient_image2("LAI")
|
| 354 |
+
ov_arr = overlay.numpy()
|
| 355 |
+
mask = ov_arr == 0
|
| 356 |
+
mask = np.ma.masked_where(mask == 0, mask)
|
| 357 |
+
ov_arr = np.ma.masked_array(ov_arr, mask)
|
| 358 |
+
ov_arr = np.rollaxis(ov_arr, axis)
|
| 359 |
+
|
| 360 |
+
if slices is None:
|
| 361 |
+
if not isinstance(slice_buffer, (list, tuple)):
|
| 362 |
+
if slice_buffer is None:
|
| 363 |
+
slice_buffer = (
|
| 364 |
+
int(img_arr.shape[1] * 0.1),
|
| 365 |
+
int(img_arr.shape[2] * 0.1),
|
| 366 |
+
)
|
| 367 |
+
else:
|
| 368 |
+
slice_buffer = (slice_buffer, slice_buffer)
|
| 369 |
+
nonzero = np.where(img_arr.sum(axis=(1, 2)) > 0.01)[0]
|
| 370 |
+
min_idx = nonzero[0] + slice_buffer[0]
|
| 371 |
+
max_idx = nonzero[-1] - slice_buffer[1]
|
| 372 |
+
if min_idx > max_idx:
|
| 373 |
+
temp = min_idx
|
| 374 |
+
min_idx = max_idx
|
| 375 |
+
max_idx = temp
|
| 376 |
+
if max_idx > nonzero.max():
|
| 377 |
+
max_idx = nonzero.max()
|
| 378 |
+
if min_idx < 0:
|
| 379 |
+
min_idx = 0
|
| 380 |
+
slice_idxs = np.linspace(min_idx, max_idx, nslices).astype("int")
|
| 381 |
+
if reverse:
|
| 382 |
+
slice_idxs = np.array(list(reversed(slice_idxs)))
|
| 383 |
+
else:
|
| 384 |
+
if isinstance(slices, (int, float)):
|
| 385 |
+
slices = [slices]
|
| 386 |
+
# if all slices are less than 1, infer that they are relative slices
|
| 387 |
+
if sum([s > 1 for s in slices]) == 0:
|
| 388 |
+
slices = [int(s * img_arr.shape[0]) for s in slices]
|
| 389 |
+
slice_idxs = slices
|
| 390 |
+
nslices = len(slices)
|
| 391 |
+
|
| 392 |
+
if was_resampled:
|
| 393 |
+
# re-calculate slices to account for new image shape
|
| 394 |
+
slice_idxs = np.unique(
|
| 395 |
+
np.array(
|
| 396 |
+
[
|
| 397 |
+
int(s * (image.shape[axis] / img_arr.shape[0]))
|
| 398 |
+
for s in slice_idxs
|
| 399 |
+
]
|
| 400 |
+
)
|
| 401 |
+
)
|
| 402 |
+
|
| 403 |
+
# only have one row if nslices <= 6 and user didnt specify ncol
|
| 404 |
+
if ncol is None:
|
| 405 |
+
if nslices <= 6:
|
| 406 |
+
ncol = nslices
|
| 407 |
+
else:
|
| 408 |
+
ncol = int(round(math.sqrt(nslices)))
|
| 409 |
+
|
| 410 |
+
# calculate grid size
|
| 411 |
+
nrow = math.ceil(nslices / ncol)
|
| 412 |
+
xdim = img_arr.shape[2]
|
| 413 |
+
ydim = img_arr.shape[1]
|
| 414 |
+
|
| 415 |
+
dim_ratio = ydim / xdim
|
| 416 |
+
fig = plt.figure(
|
| 417 |
+
figsize=((ncol + 1) * figsize * dim_ratio, (nrow + 1) * figsize)
|
| 418 |
+
)
|
| 419 |
+
if title is not None:
|
| 420 |
+
fig.suptitle(
|
| 421 |
+
title, fontsize=title_fontsize, x=0.5 + title_dx, y=0.95 + title_dy
|
| 422 |
+
)
|
| 423 |
+
|
| 424 |
+
gs = gridspec.GridSpec(
|
| 425 |
+
nrow,
|
| 426 |
+
ncol,
|
| 427 |
+
wspace=0.0,
|
| 428 |
+
hspace=0.0,
|
| 429 |
+
top=1.0 - 0.5 / (nrow + 1),
|
| 430 |
+
bottom=0.5 / (nrow + 1),
|
| 431 |
+
left=0.5 / (ncol + 1),
|
| 432 |
+
right=1 - 0.5 / (ncol + 1),
|
| 433 |
+
)
|
| 434 |
+
|
| 435 |
+
slice_idx_idx = 0
|
| 436 |
+
for i in range(nrow):
|
| 437 |
+
for j in range(ncol):
|
| 438 |
+
if slice_idx_idx < len(slice_idxs):
|
| 439 |
+
imslice = img_arr[slice_idxs[slice_idx_idx]]
|
| 440 |
+
imslice = reorient_slice(imslice, axis)
|
| 441 |
+
if not black_bg:
|
| 442 |
+
imslice[
|
| 443 |
+
imslice < image.quantile(bg_thresh_quant)
|
| 444 |
+
] = image.quantile(bg_val_quant)
|
| 445 |
+
else:
|
| 446 |
+
imslice = np.zeros_like(img_arr[0])
|
| 447 |
+
imslice = reorient_slice(imslice, axis)
|
| 448 |
+
|
| 449 |
+
ax = plt.subplot(gs[i, j])
|
| 450 |
+
im = ax.imshow(imslice, cmap=cmap, vmin=vmin, vmax=vmax)
|
| 451 |
+
|
| 452 |
+
if overlay is not None:
|
| 453 |
+
if slice_idx_idx < len(slice_idxs):
|
| 454 |
+
ovslice = ov_arr[slice_idxs[slice_idx_idx]]
|
| 455 |
+
ovslice = reorient_slice(ovslice, axis)
|
| 456 |
+
im = ax.imshow(
|
| 457 |
+
ovslice, alpha=overlay_alpha, cmap=overlay_cmap,
|
| 458 |
+
vmin=vminol, vmax=vmaxol )
|
| 459 |
+
ax.axis("off")
|
| 460 |
+
slice_idx_idx += 1
|
| 461 |
+
|
| 462 |
+
if cbar:
|
| 463 |
+
cbar_start = (1 - cbar_length) / 2
|
| 464 |
+
if cbar_vertical:
|
| 465 |
+
cax = fig.add_axes([0.9 + cbar_dx, cbar_start, 0.03, cbar_length])
|
| 466 |
+
cbar_orient = "vertical"
|
| 467 |
+
else:
|
| 468 |
+
cax = fig.add_axes([cbar_start, 0.08 + cbar_dx, cbar_length, 0.03])
|
| 469 |
+
cbar_orient = "horizontal"
|
| 470 |
+
fig.colorbar(im, cax=cax, orientation=cbar_orient)
|
| 471 |
+
|
| 472 |
+
## multi-channel images ##
|
| 473 |
+
elif image.has_components:
|
| 474 |
+
raise Exception('Plotting images with components is not currently supported.')
|
| 475 |
+
|
| 476 |
+
if filename is not None:
|
| 477 |
+
filename = os.path.expanduser(filename)
|
| 478 |
+
plt.savefig(filename, dpi=dpi, transparent=True, bbox_inches="tight")
|
| 479 |
+
plt.close(fig)
|
| 480 |
+
else:
|
| 481 |
+
plt.show()
|
| 482 |
+
|
| 483 |
+
# turn warnings back to default
|
| 484 |
+
warnings.simplefilter("default")
|
| 485 |
+
|
| 486 |
+
|
MindEyeV2/antspy/ants/plotting/plot_directory.py
ADDED
|
@@ -0,0 +1,118 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
Functions for plotting ants images
|
| 3 |
+
"""
|
| 4 |
+
|
| 5 |
+
|
| 6 |
+
__all__ = [
|
| 7 |
+
"plot_directory"
|
| 8 |
+
]
|
| 9 |
+
|
| 10 |
+
import fnmatch
|
| 11 |
+
import math
|
| 12 |
+
import os
|
| 13 |
+
import warnings
|
| 14 |
+
|
| 15 |
+
from matplotlib import gridspec
|
| 16 |
+
import matplotlib.pyplot as plt
|
| 17 |
+
import matplotlib.patheffects as path_effects
|
| 18 |
+
import matplotlib.lines as mlines
|
| 19 |
+
import matplotlib.patches as patches
|
| 20 |
+
import matplotlib.mlab as mlab
|
| 21 |
+
import matplotlib.animation as animation
|
| 22 |
+
from mpl_toolkits.axes_grid1.inset_locator import inset_axes
|
| 23 |
+
|
| 24 |
+
|
| 25 |
+
import numpy as np
|
| 26 |
+
|
| 27 |
+
import ants
|
| 28 |
+
|
| 29 |
+
|
| 30 |
+
def plot_directory(
|
| 31 |
+
directory,
|
| 32 |
+
recursive=False,
|
| 33 |
+
regex="*",
|
| 34 |
+
save_prefix="",
|
| 35 |
+
save_suffix="",
|
| 36 |
+
axis=None,
|
| 37 |
+
**kwargs
|
| 38 |
+
):
|
| 39 |
+
"""
|
| 40 |
+
Create and save an ANTsPy plot for every image matching a given regular
|
| 41 |
+
expression in a directory, optionally recursively. This is a good function
|
| 42 |
+
for quick visualize exploration of all of images in a directory
|
| 43 |
+
|
| 44 |
+
ANTsR function: N/A
|
| 45 |
+
|
| 46 |
+
Arguments
|
| 47 |
+
---------
|
| 48 |
+
directory : string
|
| 49 |
+
directory in which to search for images and plot them
|
| 50 |
+
|
| 51 |
+
recursive : boolean
|
| 52 |
+
If true, this function will search through all directories under
|
| 53 |
+
the given directory recursively to make plots.
|
| 54 |
+
If false, this function will only create plots for images in the
|
| 55 |
+
given directory
|
| 56 |
+
|
| 57 |
+
regex : string
|
| 58 |
+
regular expression used to filter out certain filenames or suffixes
|
| 59 |
+
|
| 60 |
+
save_prefix : string
|
| 61 |
+
sub-string that will be appended to the beginning of all saved plot filenames.
|
| 62 |
+
Default is to add nothing.
|
| 63 |
+
|
| 64 |
+
save_suffix : string
|
| 65 |
+
sub-string that will be appended to the end of all saved plot filenames.
|
| 66 |
+
Default is add nothing.
|
| 67 |
+
|
| 68 |
+
kwargs : keyword arguments
|
| 69 |
+
any additional arguments to pass onto the `ants.plot` function.
|
| 70 |
+
e.g. overlay, alpha, cmap, etc. See `ants.plot` for more options.
|
| 71 |
+
|
| 72 |
+
Example
|
| 73 |
+
-------
|
| 74 |
+
>>> import ants
|
| 75 |
+
>>> ants.plot_directory(directory='~/desktop/testdir',
|
| 76 |
+
recursive=False, regex='*')
|
| 77 |
+
"""
|
| 78 |
+
|
| 79 |
+
def has_acceptable_suffix(fname):
|
| 80 |
+
suffixes = {".nii.gz"}
|
| 81 |
+
return sum([fname.endswith(sx) for sx in suffixes]) > 0
|
| 82 |
+
|
| 83 |
+
if directory.startswith("~"):
|
| 84 |
+
directory = os.path.expanduser(directory)
|
| 85 |
+
|
| 86 |
+
if not os.path.isdir(directory):
|
| 87 |
+
raise ValueError("directory %s does not exist!" % directory)
|
| 88 |
+
|
| 89 |
+
for root, dirnames, fnames in os.walk(directory):
|
| 90 |
+
for fname in fnames:
|
| 91 |
+
if fnmatch.fnmatch(fname, regex) and has_acceptable_suffix(fname):
|
| 92 |
+
load_fname = os.path.join(root, fname)
|
| 93 |
+
fname = fname.replace(".".join(fname.split(".")[1:]), "png")
|
| 94 |
+
fname = fname.replace(".png", "%s.png" % save_suffix)
|
| 95 |
+
fname = "%s%s" % (save_prefix, fname)
|
| 96 |
+
save_fname = os.path.join(root, fname)
|
| 97 |
+
img = ants.image_read(load_fname)
|
| 98 |
+
|
| 99 |
+
if axis is None:
|
| 100 |
+
axis_range = [i for i in range(img.dimension)]
|
| 101 |
+
else:
|
| 102 |
+
axis_range = axis if isinstance(axis, (list, tuple)) else [axis]
|
| 103 |
+
|
| 104 |
+
if img.dimension > 2:
|
| 105 |
+
for axis_idx in axis_range:
|
| 106 |
+
filename = save_fname.replace(".png", "_axis%i.png" % axis_idx)
|
| 107 |
+
ncol = int(math.sqrt(img.shape[axis_idx]))
|
| 108 |
+
ants.plot(
|
| 109 |
+
img,
|
| 110 |
+
axis=axis_idx,
|
| 111 |
+
nslices=img.shape[axis_idx],
|
| 112 |
+
ncol=ncol,
|
| 113 |
+
filename=filename,
|
| 114 |
+
**kwargs
|
| 115 |
+
)
|
| 116 |
+
else:
|
| 117 |
+
filename = save_fname
|
| 118 |
+
ants.plot(img, filename=filename, **kwargs)
|
MindEyeV2/antspy/ants/plotting/plot_grid.py
ADDED
|
@@ -0,0 +1,355 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
Functions for plotting ants images
|
| 3 |
+
"""
|
| 4 |
+
|
| 5 |
+
|
| 6 |
+
__all__ = [
|
| 7 |
+
"plot_grid"
|
| 8 |
+
]
|
| 9 |
+
|
| 10 |
+
import fnmatch
|
| 11 |
+
import math
|
| 12 |
+
import os
|
| 13 |
+
import warnings
|
| 14 |
+
|
| 15 |
+
from matplotlib import gridspec
|
| 16 |
+
import matplotlib.pyplot as plt
|
| 17 |
+
import matplotlib.patheffects as path_effects
|
| 18 |
+
import matplotlib.lines as mlines
|
| 19 |
+
import matplotlib.patches as patches
|
| 20 |
+
import matplotlib.mlab as mlab
|
| 21 |
+
import matplotlib.animation as animation
|
| 22 |
+
from mpl_toolkits.axes_grid1.inset_locator import inset_axes
|
| 23 |
+
|
| 24 |
+
|
| 25 |
+
import numpy as np
|
| 26 |
+
|
| 27 |
+
|
| 28 |
+
def plot_grid(
|
| 29 |
+
images,
|
| 30 |
+
slices=None,
|
| 31 |
+
axes=2,
|
| 32 |
+
# general figure arguments
|
| 33 |
+
figsize=1.0,
|
| 34 |
+
rpad=0,
|
| 35 |
+
cpad=0,
|
| 36 |
+
vmin=None,
|
| 37 |
+
vmax=None,
|
| 38 |
+
colorbar=True,
|
| 39 |
+
cmap="Greys_r",
|
| 40 |
+
# title arguments
|
| 41 |
+
title=None,
|
| 42 |
+
tfontsize=20,
|
| 43 |
+
title_dx=0,
|
| 44 |
+
title_dy=0,
|
| 45 |
+
# row arguments
|
| 46 |
+
rlabels=None,
|
| 47 |
+
rfontsize=14,
|
| 48 |
+
rfontcolor="white",
|
| 49 |
+
rfacecolor="black",
|
| 50 |
+
# column arguments
|
| 51 |
+
clabels=None,
|
| 52 |
+
cfontsize=14,
|
| 53 |
+
cfontcolor="white",
|
| 54 |
+
cfacecolor="black",
|
| 55 |
+
# save arguments
|
| 56 |
+
filename=None,
|
| 57 |
+
dpi=400,
|
| 58 |
+
transparent=True,
|
| 59 |
+
# other args
|
| 60 |
+
**kwargs
|
| 61 |
+
):
|
| 62 |
+
"""
|
| 63 |
+
Plot a collection of images in an arbitrarily-defined grid
|
| 64 |
+
|
| 65 |
+
Matplotlib named colors: https://matplotlib.org/examples/color/named_colors.html
|
| 66 |
+
|
| 67 |
+
Arguments
|
| 68 |
+
---------
|
| 69 |
+
images : list of ANTsImage types
|
| 70 |
+
image(s) to plot.
|
| 71 |
+
if one image, this image will be used for all grid locations.
|
| 72 |
+
if multiple images, they should be arrange in a list the same
|
| 73 |
+
shape as the `gridsize` argument.
|
| 74 |
+
|
| 75 |
+
slices : integer or list of integers
|
| 76 |
+
slice indices to plot
|
| 77 |
+
if one integer, this slice index will be used for all images
|
| 78 |
+
if multiple integers, they should be arranged in a list the same
|
| 79 |
+
shape as the `gridsize` argument
|
| 80 |
+
|
| 81 |
+
axes : integer or list of integers
|
| 82 |
+
axis or axes along which to plot image slices
|
| 83 |
+
if one integer, this axis will be used for all images
|
| 84 |
+
if multiple integers, they should be arranged in a list the same
|
| 85 |
+
shape as the `gridsize` argument
|
| 86 |
+
|
| 87 |
+
Example
|
| 88 |
+
-------
|
| 89 |
+
>>> import ants
|
| 90 |
+
>>> import numpy as np
|
| 91 |
+
>>> mni1 = ants.image_read(ants.get_data('mni'))
|
| 92 |
+
>>> mni2 = mni1.smooth_image(1.)
|
| 93 |
+
>>> mni3 = mni1.smooth_image(2.)
|
| 94 |
+
>>> mni4 = mni1.smooth_image(3.)
|
| 95 |
+
>>> images = np.asarray([[mni1, mni2],
|
| 96 |
+
... [mni3, mni4]])
|
| 97 |
+
>>> slices = np.asarray([[100, 100],
|
| 98 |
+
... [100, 100]])
|
| 99 |
+
>>> ants.plot_grid(images=images, slices=slices, title='2x2 Grid')
|
| 100 |
+
>>> images2d = np.asarray([[mni1.slice_image(2,100), mni2.slice_image(2,100)],
|
| 101 |
+
... [mni3.slice_image(2,100), mni4.slice_image(2,100)]])
|
| 102 |
+
>>> ants.plot_grid(images=images2d, title='2x2 Grid Pre-Sliced')
|
| 103 |
+
>>> ants.plot_grid(images.reshape(1,4), slices.reshape(1,4), title='1x4 Grid')
|
| 104 |
+
>>> ants.plot_grid(images.reshape(4,1), slices.reshape(4,1), title='4x1 Grid')
|
| 105 |
+
|
| 106 |
+
>>> # Padding between rows and/or columns
|
| 107 |
+
>>> ants.plot_grid(images, slices, cpad=0.02, title='Col Padding')
|
| 108 |
+
>>> ants.plot_grid(images, slices, rpad=0.02, title='Row Padding')
|
| 109 |
+
>>> ants.plot_grid(images, slices, rpad=0.02, cpad=0.02, title='Row and Col Padding')
|
| 110 |
+
|
| 111 |
+
>>> # Adding plain row and/or column labels
|
| 112 |
+
>>> ants.plot_grid(images, slices, title='Adding Row Labels', rlabels=['Row #1', 'Row #2'])
|
| 113 |
+
>>> ants.plot_grid(images, slices, title='Adding Col Labels', clabels=['Col #1', 'Col #2'])
|
| 114 |
+
>>> ants.plot_grid(images, slices, title='Row and Col Labels',
|
| 115 |
+
rlabels=['Row 1', 'Row 2'], clabels=['Col 1', 'Col 2'])
|
| 116 |
+
|
| 117 |
+
>>> # Making a publication-quality image
|
| 118 |
+
>>> images = np.asarray([[mni1, mni2, mni2],
|
| 119 |
+
... [mni3, mni4, mni4]])
|
| 120 |
+
>>> slices = np.asarray([[100, 100, 100],
|
| 121 |
+
... [100, 100, 100]])
|
| 122 |
+
>>> axes = np.asarray([[0, 1, 2],
|
| 123 |
+
[0, 1, 2]])
|
| 124 |
+
>>> ants.plot_grid(images, slices, axes, title='Publication Figures with ANTsPy',
|
| 125 |
+
tfontsize=20, title_dy=0.03, title_dx=-0.04,
|
| 126 |
+
rlabels=['Row 1', 'Row 2'],
|
| 127 |
+
clabels=['Col 1', 'Col 2', 'Col 3'],
|
| 128 |
+
rfontsize=16, cfontsize=16)
|
| 129 |
+
"""
|
| 130 |
+
|
| 131 |
+
def mirror_matrix(x):
|
| 132 |
+
return x[::-1, :]
|
| 133 |
+
|
| 134 |
+
def rotate270_matrix(x):
|
| 135 |
+
return mirror_matrix(x.T)
|
| 136 |
+
|
| 137 |
+
def rotate180_matrix(x):
|
| 138 |
+
return x[::-1, ::-1]
|
| 139 |
+
|
| 140 |
+
def rotate90_matrix(x):
|
| 141 |
+
return mirror_matrix(x).T
|
| 142 |
+
|
| 143 |
+
def flip_matrix(x):
|
| 144 |
+
return mirror_matrix(rotate180_matrix(x))
|
| 145 |
+
|
| 146 |
+
def reorient_slice(x, axis):
|
| 147 |
+
if axis != 1:
|
| 148 |
+
x = rotate90_matrix(x)
|
| 149 |
+
if axis == 1:
|
| 150 |
+
x = rotate90_matrix(x)
|
| 151 |
+
x = mirror_matrix(x)
|
| 152 |
+
return x
|
| 153 |
+
|
| 154 |
+
def slice_image(img, axis, idx):
|
| 155 |
+
if axis == 0:
|
| 156 |
+
return img[idx, :, :].numpy()
|
| 157 |
+
elif axis == 1:
|
| 158 |
+
return img[:, idx, :].numpy()
|
| 159 |
+
elif axis == 2:
|
| 160 |
+
return img[:, :, idx].numpy()
|
| 161 |
+
elif axis == -1:
|
| 162 |
+
return img[:, :, idx].numpy()
|
| 163 |
+
elif axis == -2:
|
| 164 |
+
return img[:, idx, :].numpy()
|
| 165 |
+
elif axis == -3:
|
| 166 |
+
return img[idx, :, :].numpy()
|
| 167 |
+
else:
|
| 168 |
+
raise ValueError("axis %i not valid" % axis)
|
| 169 |
+
|
| 170 |
+
if isinstance(images, np.ndarray):
|
| 171 |
+
images = images.tolist()
|
| 172 |
+
if not isinstance(images, list):
|
| 173 |
+
raise ValueError("images argument must be of type list")
|
| 174 |
+
if not isinstance(images[0], list):
|
| 175 |
+
images = [images]
|
| 176 |
+
|
| 177 |
+
if slices is None:
|
| 178 |
+
one_slice = True
|
| 179 |
+
if isinstance(slices, int):
|
| 180 |
+
one_slice = True
|
| 181 |
+
if isinstance(slices, np.ndarray):
|
| 182 |
+
slices = slices.tolist()
|
| 183 |
+
if isinstance(slices, list):
|
| 184 |
+
one_slice = False
|
| 185 |
+
if not isinstance(slices[0], list):
|
| 186 |
+
slices = [slices]
|
| 187 |
+
nslicerow = len(slices)
|
| 188 |
+
nslicecol = len(slices[0])
|
| 189 |
+
|
| 190 |
+
nrow = len(images)
|
| 191 |
+
ncol = len(images[0])
|
| 192 |
+
|
| 193 |
+
if rlabels is None:
|
| 194 |
+
rlabels = [None] * nrow
|
| 195 |
+
if clabels is None:
|
| 196 |
+
clabels = [None] * ncol
|
| 197 |
+
|
| 198 |
+
if not one_slice:
|
| 199 |
+
if (nrow != nslicerow) or (ncol != nslicecol):
|
| 200 |
+
raise ValueError(
|
| 201 |
+
"`images` arg shape (%i,%i) must equal `slices` arg shape (%i,%i)!"
|
| 202 |
+
% (nrow, ncol, nslicerow, nslicecol)
|
| 203 |
+
)
|
| 204 |
+
|
| 205 |
+
fig = plt.figure(figsize=((ncol + 1) * 2.5 * figsize, (nrow + 1) * 2.5 * figsize))
|
| 206 |
+
|
| 207 |
+
if title is not None:
|
| 208 |
+
basex = 0.5
|
| 209 |
+
basey = 0.9 if clabels[0] is None else 0.95
|
| 210 |
+
fig.suptitle(title, fontsize=tfontsize, x=basex + title_dx, y=basey + title_dy)
|
| 211 |
+
|
| 212 |
+
if (cpad > 0) and (rpad > 0):
|
| 213 |
+
bothgridpad = max(cpad, rpad)
|
| 214 |
+
cpad = 0
|
| 215 |
+
rpad = 0
|
| 216 |
+
else:
|
| 217 |
+
bothgridpad = 0.0
|
| 218 |
+
|
| 219 |
+
gs = gridspec.GridSpec(
|
| 220 |
+
nrow,
|
| 221 |
+
ncol,
|
| 222 |
+
wspace=bothgridpad,
|
| 223 |
+
hspace=0.0,
|
| 224 |
+
top=1.0 - 0.5 / (nrow + 1),
|
| 225 |
+
bottom=0.5 / (nrow + 1) + cpad,
|
| 226 |
+
left=0.5 / (ncol + 1) + rpad,
|
| 227 |
+
right=1 - 0.5 / (ncol + 1),
|
| 228 |
+
)
|
| 229 |
+
|
| 230 |
+
if isinstance(vmin, (int, float)):
|
| 231 |
+
vmins = [vmin] * nrow
|
| 232 |
+
elif vmin is None:
|
| 233 |
+
vmins = [None] * nrow
|
| 234 |
+
else:
|
| 235 |
+
vmins = vmin
|
| 236 |
+
|
| 237 |
+
if isinstance(vmax, (int, float)):
|
| 238 |
+
vmaxs = [vmax] * nrow
|
| 239 |
+
elif vmax is None:
|
| 240 |
+
vmaxs = [None] * nrow
|
| 241 |
+
else:
|
| 242 |
+
vmaxs = vmax
|
| 243 |
+
|
| 244 |
+
if isinstance(cmap, str):
|
| 245 |
+
cmaps = [cmap] * nrow
|
| 246 |
+
elif cmap is None:
|
| 247 |
+
cmaps = [None] * nrow
|
| 248 |
+
else:
|
| 249 |
+
cmaps = cmap
|
| 250 |
+
|
| 251 |
+
for rowidx, rvmin, rvmax, rcmap in zip(range(nrow), vmins, vmaxs, cmaps):
|
| 252 |
+
for colidx in range(ncol):
|
| 253 |
+
ax = plt.subplot(gs[rowidx, colidx])
|
| 254 |
+
|
| 255 |
+
if colidx == 0:
|
| 256 |
+
if rlabels[rowidx] is not None:
|
| 257 |
+
bottom, height = 0.25, 0.5
|
| 258 |
+
top = bottom + height
|
| 259 |
+
# add label text
|
| 260 |
+
ax.text(
|
| 261 |
+
-0.07,
|
| 262 |
+
0.5 * (bottom + top),
|
| 263 |
+
rlabels[rowidx],
|
| 264 |
+
horizontalalignment="right",
|
| 265 |
+
verticalalignment="center",
|
| 266 |
+
rotation="vertical",
|
| 267 |
+
transform=ax.transAxes,
|
| 268 |
+
color=rfontcolor,
|
| 269 |
+
fontsize=rfontsize,
|
| 270 |
+
)
|
| 271 |
+
|
| 272 |
+
# add label background
|
| 273 |
+
extra = 0.3 if rowidx == 0 else 0.0
|
| 274 |
+
|
| 275 |
+
rect = patches.Rectangle(
|
| 276 |
+
(-0.3, 0),
|
| 277 |
+
0.3,
|
| 278 |
+
1.0 + extra,
|
| 279 |
+
facecolor=rfacecolor,
|
| 280 |
+
alpha=1.0,
|
| 281 |
+
transform=ax.transAxes,
|
| 282 |
+
clip_on=False,
|
| 283 |
+
)
|
| 284 |
+
ax.add_patch(rect)
|
| 285 |
+
|
| 286 |
+
if rowidx == 0:
|
| 287 |
+
if clabels[colidx] is not None:
|
| 288 |
+
bottom, height = 0.25, 0.5
|
| 289 |
+
left, width = 0.25, 0.5
|
| 290 |
+
right = left + width
|
| 291 |
+
top = bottom + height
|
| 292 |
+
ax.text(
|
| 293 |
+
0.5 * (left + right),
|
| 294 |
+
0.09 + top + bottom,
|
| 295 |
+
clabels[colidx],
|
| 296 |
+
horizontalalignment="center",
|
| 297 |
+
verticalalignment="center",
|
| 298 |
+
rotation="horizontal",
|
| 299 |
+
transform=ax.transAxes,
|
| 300 |
+
color=cfontcolor,
|
| 301 |
+
fontsize=cfontsize,
|
| 302 |
+
)
|
| 303 |
+
|
| 304 |
+
# add label background
|
| 305 |
+
rect = patches.Rectangle(
|
| 306 |
+
(0, 1.0),
|
| 307 |
+
1.0,
|
| 308 |
+
0.3,
|
| 309 |
+
facecolor=cfacecolor,
|
| 310 |
+
alpha=1.0,
|
| 311 |
+
transform=ax.transAxes,
|
| 312 |
+
clip_on=False,
|
| 313 |
+
)
|
| 314 |
+
ax.add_patch(rect)
|
| 315 |
+
|
| 316 |
+
tmpimg = images[rowidx][colidx]
|
| 317 |
+
if isinstance(axes, int):
|
| 318 |
+
tmpaxis = axes
|
| 319 |
+
else:
|
| 320 |
+
tmpaxis = axes[rowidx][colidx]
|
| 321 |
+
|
| 322 |
+
if tmpimg.dimension == 2:
|
| 323 |
+
tmpslice = tmpimg.numpy()
|
| 324 |
+
tmpslice = reorient_slice(tmpslice, tmpaxis)
|
| 325 |
+
else:
|
| 326 |
+
sliceidx = slices[rowidx][colidx] if not one_slice else slices
|
| 327 |
+
if sliceidx is None:
|
| 328 |
+
sliceidx = math.ceil(tmpimg.shape[tmpaxis] / 2)
|
| 329 |
+
tmpslice = slice_image(tmpimg, tmpaxis, sliceidx)
|
| 330 |
+
tmpslice = reorient_slice(tmpslice, tmpaxis)
|
| 331 |
+
|
| 332 |
+
im = ax.imshow(tmpslice, cmap=rcmap, aspect="auto", vmin=rvmin, vmax=rvmax)
|
| 333 |
+
ax.axis("off")
|
| 334 |
+
|
| 335 |
+
# A colorbar solution with make_axes_locatable will not allow y-scaling of the colorbar.
|
| 336 |
+
# from mpl_toolkits.axes_grid1 import make_axes_locatable
|
| 337 |
+
# divider = make_axes_locatable(ax)
|
| 338 |
+
# cax = divider.append_axes('right', size='5%', pad=0.05)
|
| 339 |
+
if colorbar:
|
| 340 |
+
axins = inset_axes(ax,
|
| 341 |
+
width="5%", # width = 5% of parent_bbox width
|
| 342 |
+
height="90%", # height : 50%
|
| 343 |
+
loc='center left',
|
| 344 |
+
bbox_to_anchor=(1.03, 0., 1, 1),
|
| 345 |
+
bbox_transform=ax.transAxes,
|
| 346 |
+
borderpad=0,
|
| 347 |
+
)
|
| 348 |
+
fig.colorbar(im, cax=axins, orientation='vertical')
|
| 349 |
+
|
| 350 |
+
if filename is not None:
|
| 351 |
+
filename = os.path.expanduser(filename)
|
| 352 |
+
plt.savefig(filename, dpi=dpi, transparent=transparent, bbox_inches="tight")
|
| 353 |
+
plt.close(fig)
|
| 354 |
+
else:
|
| 355 |
+
plt.show()
|
MindEyeV2/antspy/ants/plotting/plot_hist.py
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
Functions for plotting ants images
|
| 3 |
+
"""
|
| 4 |
+
|
| 5 |
+
|
| 6 |
+
__all__ = [
|
| 7 |
+
"plot_hist"
|
| 8 |
+
]
|
| 9 |
+
|
| 10 |
+
import fnmatch
|
| 11 |
+
import math
|
| 12 |
+
import os
|
| 13 |
+
import warnings
|
| 14 |
+
|
| 15 |
+
from matplotlib import gridspec
|
| 16 |
+
import matplotlib.pyplot as plt
|
| 17 |
+
import matplotlib.patheffects as path_effects
|
| 18 |
+
import matplotlib.lines as mlines
|
| 19 |
+
import matplotlib.patches as patches
|
| 20 |
+
import matplotlib.mlab as mlab
|
| 21 |
+
import matplotlib.animation as animation
|
| 22 |
+
from mpl_toolkits.axes_grid1.inset_locator import inset_axes
|
| 23 |
+
|
| 24 |
+
|
| 25 |
+
import numpy as np
|
| 26 |
+
from ants.decorators import image_method
|
| 27 |
+
|
| 28 |
+
@image_method
|
| 29 |
+
def plot_hist(
|
| 30 |
+
image,
|
| 31 |
+
threshold=0.0,
|
| 32 |
+
fit_line=False,
|
| 33 |
+
normfreq=True,
|
| 34 |
+
## plot label arguments
|
| 35 |
+
title=None,
|
| 36 |
+
grid=True,
|
| 37 |
+
xlabel=None,
|
| 38 |
+
ylabel=None,
|
| 39 |
+
## other plot arguments
|
| 40 |
+
facecolor="green",
|
| 41 |
+
alpha=0.75,
|
| 42 |
+
):
|
| 43 |
+
"""
|
| 44 |
+
Plot a histogram from an ANTsImage
|
| 45 |
+
|
| 46 |
+
Arguments
|
| 47 |
+
---------
|
| 48 |
+
image : ANTsImage
|
| 49 |
+
image from which histogram will be created
|
| 50 |
+
"""
|
| 51 |
+
img_arr = image.numpy().flatten()
|
| 52 |
+
img_arr = img_arr[np.abs(img_arr) > threshold]
|
| 53 |
+
|
| 54 |
+
if normfreq != False:
|
| 55 |
+
normfreq = 1.0 if normfreq == True else normfreq
|
| 56 |
+
n, bins, patches = plt.hist(
|
| 57 |
+
img_arr, 50, facecolor=facecolor, alpha=alpha
|
| 58 |
+
)
|
| 59 |
+
|
| 60 |
+
if fit_line:
|
| 61 |
+
# add a 'best fit' line
|
| 62 |
+
y = mlab.normpdf(bins, img_arr.mean(), img_arr.std())
|
| 63 |
+
l = plt.plot(bins, y, "r--", linewidth=1)
|
| 64 |
+
|
| 65 |
+
if xlabel is not None:
|
| 66 |
+
plt.xlabel(xlabel)
|
| 67 |
+
if ylabel is not None:
|
| 68 |
+
plt.ylabel(ylabel)
|
| 69 |
+
if title is not None:
|
| 70 |
+
plt.title(title)
|
| 71 |
+
|
| 72 |
+
plt.grid(grid)
|
| 73 |
+
plt.show()
|
MindEyeV2/antspy/ants/plotting/plot_ortho.py
ADDED
|
@@ -0,0 +1,612 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
Functions for plotting ants images
|
| 3 |
+
"""
|
| 4 |
+
|
| 5 |
+
|
| 6 |
+
__all__ = [
|
| 7 |
+
"plot_ortho"
|
| 8 |
+
]
|
| 9 |
+
|
| 10 |
+
import fnmatch
|
| 11 |
+
import math
|
| 12 |
+
import os
|
| 13 |
+
import warnings
|
| 14 |
+
|
| 15 |
+
from matplotlib import gridspec
|
| 16 |
+
import matplotlib.pyplot as plt
|
| 17 |
+
import matplotlib.patheffects as path_effects
|
| 18 |
+
import matplotlib.lines as mlines
|
| 19 |
+
import matplotlib.patches as patches
|
| 20 |
+
import matplotlib.mlab as mlab
|
| 21 |
+
import matplotlib.animation as animation
|
| 22 |
+
from mpl_toolkits.axes_grid1.inset_locator import inset_axes
|
| 23 |
+
|
| 24 |
+
|
| 25 |
+
import numpy as np
|
| 26 |
+
import ants
|
| 27 |
+
from ants.decorators import image_method
|
| 28 |
+
|
| 29 |
+
@image_method
|
| 30 |
+
def plot_ortho(
|
| 31 |
+
image,
|
| 32 |
+
overlay=None,
|
| 33 |
+
reorient=True,
|
| 34 |
+
blend=False,
|
| 35 |
+
# xyz arguments
|
| 36 |
+
xyz=None,
|
| 37 |
+
xyz_lines=True,
|
| 38 |
+
xyz_color="red",
|
| 39 |
+
xyz_alpha=0.6,
|
| 40 |
+
xyz_linewidth=2,
|
| 41 |
+
xyz_pad=5,
|
| 42 |
+
orient_labels=True,
|
| 43 |
+
# base image arguments
|
| 44 |
+
alpha=1,
|
| 45 |
+
cmap="Greys_r",
|
| 46 |
+
# overlay arguments
|
| 47 |
+
overlay_cmap="jet",
|
| 48 |
+
overlay_alpha=0.9,
|
| 49 |
+
cbar=False,
|
| 50 |
+
cbar_length=0.8,
|
| 51 |
+
cbar_dx=0.0,
|
| 52 |
+
cbar_vertical=True,
|
| 53 |
+
# background arguments
|
| 54 |
+
black_bg=True,
|
| 55 |
+
bg_thresh_quant=0.01,
|
| 56 |
+
bg_val_quant=0.99,
|
| 57 |
+
# scale/crop/domain arguments
|
| 58 |
+
crop=False,
|
| 59 |
+
scale=False,
|
| 60 |
+
domain_image_map=None,
|
| 61 |
+
# title arguments
|
| 62 |
+
title=None,
|
| 63 |
+
titlefontsize=24,
|
| 64 |
+
title_dx=0,
|
| 65 |
+
title_dy=0,
|
| 66 |
+
# 4th panel text arguemnts
|
| 67 |
+
text=None,
|
| 68 |
+
textfontsize=24,
|
| 69 |
+
textfontcolor="white",
|
| 70 |
+
text_dx=0,
|
| 71 |
+
text_dy=0,
|
| 72 |
+
# save & size arguments
|
| 73 |
+
filename=None,
|
| 74 |
+
dpi=500,
|
| 75 |
+
figsize=1.0,
|
| 76 |
+
flat=False,
|
| 77 |
+
transparent=True,
|
| 78 |
+
resample=False,
|
| 79 |
+
allow_xyz_change=True,
|
| 80 |
+
):
|
| 81 |
+
"""
|
| 82 |
+
Plot an orthographic view of a 3D image
|
| 83 |
+
|
| 84 |
+
Use mask_image and/or threshold_image to preprocess images to be be
|
| 85 |
+
overlaid and display the overlays in a given range. See the wiki examples.
|
| 86 |
+
|
| 87 |
+
ANTsR function: N/A
|
| 88 |
+
|
| 89 |
+
Arguments
|
| 90 |
+
---------
|
| 91 |
+
image : ANTsImage
|
| 92 |
+
image to plot
|
| 93 |
+
|
| 94 |
+
overlay : ANTsImage
|
| 95 |
+
image to overlay on base image
|
| 96 |
+
|
| 97 |
+
xyz : list or tuple of 3 integers
|
| 98 |
+
selects index location on which to center display
|
| 99 |
+
if given, solid lines will be drawn to converge at this coordinate.
|
| 100 |
+
This is useful for pinpointing a specific location in the image.
|
| 101 |
+
|
| 102 |
+
flat : boolean
|
| 103 |
+
if true, the ortho image will be plot in one row
|
| 104 |
+
if false, the ortho image will be a 2x2 grid with the bottom
|
| 105 |
+
left corner blank
|
| 106 |
+
|
| 107 |
+
cmap : string
|
| 108 |
+
colormap to use for base image. See matplotlib.
|
| 109 |
+
|
| 110 |
+
overlay_cmap : string
|
| 111 |
+
colormap to use for overlay images, if applicable. See matplotlib.
|
| 112 |
+
|
| 113 |
+
overlay_alpha : float
|
| 114 |
+
level of transparency for any overlays. Smaller value means
|
| 115 |
+
the overlay is more transparent. See matplotlib.
|
| 116 |
+
|
| 117 |
+
cbar: boolean
|
| 118 |
+
if true, a colorbar will be added to the plot
|
| 119 |
+
|
| 120 |
+
cbar_length: float
|
| 121 |
+
length of the colorbar relative to the image
|
| 122 |
+
|
| 123 |
+
cbar_dx: float
|
| 124 |
+
horizontal shift of the colorbar relative to the image
|
| 125 |
+
|
| 126 |
+
cbar_vertical: boolean
|
| 127 |
+
if true, the colorbar will be vertical, if false, it will be
|
| 128 |
+
horizontal underneath the image
|
| 129 |
+
|
| 130 |
+
axis : integer
|
| 131 |
+
which axis to plot along if image is 3D
|
| 132 |
+
|
| 133 |
+
black_bg : boolean
|
| 134 |
+
if True, the background of the image(s) will be black.
|
| 135 |
+
if False, the background of the image(s) will be determined by the
|
| 136 |
+
values `bg_thresh_quant` and `bg_val_quant`.
|
| 137 |
+
|
| 138 |
+
bg_thresh_quant : float
|
| 139 |
+
if white_bg=True, the background will be determined by thresholding
|
| 140 |
+
the image at the `bg_thresh` quantile value and setting the background
|
| 141 |
+
intensity to the `bg_val` quantile value.
|
| 142 |
+
This value should be in [0, 1] - somewhere around 0.01 is recommended.
|
| 143 |
+
- equal to 1 will threshold the entire image
|
| 144 |
+
- equal to 0 will threshold none of the image
|
| 145 |
+
|
| 146 |
+
bg_val_quant : float
|
| 147 |
+
if white_bg=True, the background will be determined by thresholding
|
| 148 |
+
the image at the `bg_thresh` quantile value and setting the background
|
| 149 |
+
intensity to the `bg_val` quantile value.
|
| 150 |
+
This value should be in [0, 1]
|
| 151 |
+
- equal to 1 is pure white
|
| 152 |
+
- equal to 0 is pure black
|
| 153 |
+
- somewhere in between is gray
|
| 154 |
+
|
| 155 |
+
domain_image_map : ANTsImage
|
| 156 |
+
this input ANTsImage or list of ANTsImage types contains a reference image
|
| 157 |
+
`domain_image` and optional reference mapping named `domainMap`.
|
| 158 |
+
If supplied, the image(s) to be plotted will be mapped to the domain
|
| 159 |
+
image space before plotting - useful for non-standard image orientations.
|
| 160 |
+
|
| 161 |
+
crop : boolean
|
| 162 |
+
if true, the image(s) will be cropped to their bounding boxes, resulting
|
| 163 |
+
in a potentially smaller image size.
|
| 164 |
+
if false, the image(s) will not be cropped
|
| 165 |
+
|
| 166 |
+
scale : boolean or 2-tuple
|
| 167 |
+
if true, nothing will happen to intensities of image(s) and overlay(s)
|
| 168 |
+
if false, dynamic range will be maximized when visualizing overlays
|
| 169 |
+
if 2-tuple, the image will be dynamically scaled between these quantiles
|
| 170 |
+
|
| 171 |
+
title : string
|
| 172 |
+
add a title to the plot
|
| 173 |
+
|
| 174 |
+
filename : string
|
| 175 |
+
if given, the resulting image will be saved to this file
|
| 176 |
+
|
| 177 |
+
dpi : integer
|
| 178 |
+
determines resolution of image if saved to file. Higher values
|
| 179 |
+
result in higher resolution images, but at a cost of having a
|
| 180 |
+
larger file size
|
| 181 |
+
|
| 182 |
+
resample : resample image in case of unbalanced spacing
|
| 183 |
+
|
| 184 |
+
allow_xyz_change : boolean will attempt to adjust xyz after padding
|
| 185 |
+
|
| 186 |
+
Example
|
| 187 |
+
-------
|
| 188 |
+
>>> import ants
|
| 189 |
+
>>> mni = ants.image_read(ants.get_data('mni'))
|
| 190 |
+
>>> ants.plot_ortho(mni, xyz=(100,100,100))
|
| 191 |
+
>>> mni2 = mni.threshold_image(7000, mni.max())
|
| 192 |
+
>>> ants.plot_ortho(mni, overlay=mni2)
|
| 193 |
+
>>> ants.plot_ortho(mni, overlay=mni2, flat=True)
|
| 194 |
+
>>> ants.plot_ortho(mni, overlay=mni2, xyz=(110,110,110), xyz_lines=False,
|
| 195 |
+
text='Lines Turned Off', textfontsize=22)
|
| 196 |
+
>>> ants.plot_ortho(mni, mni2, xyz=(120,100,100),
|
| 197 |
+
text=' Example \nOrtho Text', textfontsize=26,
|
| 198 |
+
title='Example Ortho Title', titlefontsize=26)
|
| 199 |
+
"""
|
| 200 |
+
|
| 201 |
+
def mirror_matrix(x):
|
| 202 |
+
return x[::-1, :]
|
| 203 |
+
|
| 204 |
+
def rotate270_matrix(x):
|
| 205 |
+
return mirror_matrix(x.T)
|
| 206 |
+
|
| 207 |
+
def reorient_slice(x, axis):
|
| 208 |
+
return rotate270_matrix(x)
|
| 209 |
+
|
| 210 |
+
# need this hack because of a weird NaN warning from matplotlib with overlays
|
| 211 |
+
warnings.simplefilter("ignore")
|
| 212 |
+
|
| 213 |
+
# handle `image` argument
|
| 214 |
+
if isinstance(image, str):
|
| 215 |
+
image = ants.image_read(image)
|
| 216 |
+
if not ants.is_image(image):
|
| 217 |
+
raise ValueError("image argument must be an ANTsImage")
|
| 218 |
+
if image.dimension != 3:
|
| 219 |
+
raise ValueError("Input image must have 3 dimensions!")
|
| 220 |
+
|
| 221 |
+
# handle `overlay` argument
|
| 222 |
+
if overlay is not None:
|
| 223 |
+
if isinstance(overlay, str):
|
| 224 |
+
overlay = ants.image_read(overlay)
|
| 225 |
+
vminol = overlay.min()
|
| 226 |
+
vmaxol = overlay.max()
|
| 227 |
+
if not ants.is_image(overlay):
|
| 228 |
+
raise ValueError("overlay argument must be an ANTsImage")
|
| 229 |
+
if overlay.components > 1:
|
| 230 |
+
raise ValueError("overlay cannot have more than one voxel component")
|
| 231 |
+
if overlay.dimension != 3:
|
| 232 |
+
raise ValueError("Overlay image must have 3 dimensions!")
|
| 233 |
+
|
| 234 |
+
if not ants.image_physical_space_consistency(image, overlay):
|
| 235 |
+
overlay = ants.resample_image_to_target(overlay, image, interp_type="linear")
|
| 236 |
+
|
| 237 |
+
if blend:
|
| 238 |
+
if alpha == 1:
|
| 239 |
+
alpha = 0.5
|
| 240 |
+
image = image * alpha + overlay * (1 - alpha)
|
| 241 |
+
overlay = None
|
| 242 |
+
alpha = 1.0
|
| 243 |
+
|
| 244 |
+
if image.pixeltype not in {"float", "double"}:
|
| 245 |
+
scale = False # turn off scaling if image is discrete
|
| 246 |
+
|
| 247 |
+
# reorient images
|
| 248 |
+
if reorient != False:
|
| 249 |
+
if reorient == True:
|
| 250 |
+
reorient = "RPI"
|
| 251 |
+
image = image.reorient_image2("RPI")
|
| 252 |
+
if overlay is not None:
|
| 253 |
+
overlay = overlay.reorient_image2("RPI")
|
| 254 |
+
|
| 255 |
+
# handle `slices` argument
|
| 256 |
+
if xyz is None:
|
| 257 |
+
xyz = [int(s / 2) for s in image.shape]
|
| 258 |
+
for i in range(3):
|
| 259 |
+
if xyz[i] is None:
|
| 260 |
+
xyz[i] = int(image.shape[i] / 2)
|
| 261 |
+
|
| 262 |
+
# resample image if spacing is very unbalanced
|
| 263 |
+
spacing = [s for i, s in enumerate(image.spacing)]
|
| 264 |
+
if (max(spacing) / min(spacing)) > 3.0 and resample:
|
| 265 |
+
new_spacing = (1, 1, 1)
|
| 266 |
+
image = image.resample_image(tuple(new_spacing))
|
| 267 |
+
if overlay is not None:
|
| 268 |
+
overlay = overlay.resample_image(tuple(new_spacing))
|
| 269 |
+
xyz = [
|
| 270 |
+
int(sl * (sold / snew)) for sl, sold, snew in zip(xyz, spacing, new_spacing)
|
| 271 |
+
]
|
| 272 |
+
|
| 273 |
+
|
| 274 |
+
# potentially crop image
|
| 275 |
+
if crop:
|
| 276 |
+
plotmask = image.get_mask(cleanup=0)
|
| 277 |
+
if plotmask.max() == 0:
|
| 278 |
+
plotmask += 1
|
| 279 |
+
image = image.crop_image(plotmask)
|
| 280 |
+
if overlay is not None:
|
| 281 |
+
overlay = overlay.crop_image(plotmask)
|
| 282 |
+
|
| 283 |
+
# pad images
|
| 284 |
+
if True:
|
| 285 |
+
image, lowpad, uppad = image.pad_image(return_padvals=True)
|
| 286 |
+
if allow_xyz_change:
|
| 287 |
+
xyz = [v + l for v, l in zip(xyz, lowpad)]
|
| 288 |
+
if overlay is not None:
|
| 289 |
+
overlay = overlay.pad_image()
|
| 290 |
+
|
| 291 |
+
|
| 292 |
+
# handle `domain_image_map` argument
|
| 293 |
+
if domain_image_map is not None:
|
| 294 |
+
if ants.is_image(domain_image_map):
|
| 295 |
+
tx = ants.new_ants_transform(
|
| 296 |
+
precision="float",
|
| 297 |
+
transform_type="AffineTransform",
|
| 298 |
+
dimension=image.dimension,
|
| 299 |
+
)
|
| 300 |
+
image = ants.apply_ants_transform_to_image(tx, image, domain_image_map)
|
| 301 |
+
if overlay is not None:
|
| 302 |
+
overlay = ants.apply_ants_transform_to_image(
|
| 303 |
+
tx, overlay, domain_image_map, interpolation="linear"
|
| 304 |
+
)
|
| 305 |
+
else:
|
| 306 |
+
raise Exception('The domain_image_map must be an image.')
|
| 307 |
+
|
| 308 |
+
## single-channel images ##
|
| 309 |
+
if image.components == 1:
|
| 310 |
+
|
| 311 |
+
# potentially find dynamic range
|
| 312 |
+
if scale == True:
|
| 313 |
+
vmin, vmax = image.quantile((0.05, 0.95))
|
| 314 |
+
elif isinstance(scale, (list, tuple)):
|
| 315 |
+
if len(scale) != 2:
|
| 316 |
+
raise ValueError(
|
| 317 |
+
"scale argument must be boolean or list/tuple with two values"
|
| 318 |
+
)
|
| 319 |
+
vmin, vmax = image.quantile(scale)
|
| 320 |
+
else:
|
| 321 |
+
vmin = None
|
| 322 |
+
vmax = None
|
| 323 |
+
|
| 324 |
+
if not flat:
|
| 325 |
+
nrow = 2
|
| 326 |
+
ncol = 2
|
| 327 |
+
else:
|
| 328 |
+
nrow = 1
|
| 329 |
+
ncol = 3
|
| 330 |
+
|
| 331 |
+
fig = plt.figure(figsize=(9 * figsize, 9 * figsize))
|
| 332 |
+
if title is not None:
|
| 333 |
+
basey = 0.88 if not flat else 0.66
|
| 334 |
+
basex = 0.5
|
| 335 |
+
fig.suptitle(
|
| 336 |
+
title, fontsize=titlefontsize, color=textfontcolor, x=basex + title_dx, y=basey + title_dy
|
| 337 |
+
)
|
| 338 |
+
|
| 339 |
+
gs = gridspec.GridSpec(
|
| 340 |
+
nrow,
|
| 341 |
+
ncol,
|
| 342 |
+
wspace=0.0,
|
| 343 |
+
hspace=0.0,
|
| 344 |
+
top=1.0 - 0.5 / (nrow + 1),
|
| 345 |
+
bottom=0.5 / (nrow + 1),
|
| 346 |
+
left=0.5 / (ncol + 1),
|
| 347 |
+
right=1 - 0.5 / (ncol + 1),
|
| 348 |
+
)
|
| 349 |
+
|
| 350 |
+
# pad image to have isotropic array dimensions
|
| 351 |
+
imageReturn = image.clone()
|
| 352 |
+
image = image.numpy()
|
| 353 |
+
overlayReturn = None
|
| 354 |
+
if overlay is not None:
|
| 355 |
+
overlayReturn = overlay.clone()
|
| 356 |
+
overlay = overlay.numpy()
|
| 357 |
+
if overlay.dtype not in ["uint8", "uint32"]:
|
| 358 |
+
overlay = np.ma.masked_where( np.abs(overlay) <= 1e-16, overlay)
|
| 359 |
+
# overlay[np.abs(overlay) == 0] = np.nan
|
| 360 |
+
|
| 361 |
+
yz_slice = reorient_slice(image[xyz[0], :, :], 0)
|
| 362 |
+
ax = plt.subplot(gs[0, 0])
|
| 363 |
+
ax.imshow(yz_slice, cmap=cmap, vmin=vmin, vmax=vmax)
|
| 364 |
+
if overlay is not None:
|
| 365 |
+
yz_overlay = reorient_slice(overlay[xyz[0], :, :], 0)
|
| 366 |
+
ax.imshow(yz_overlay, alpha=overlay_alpha, cmap=overlay_cmap, vmin=vminol, vmax=vmaxol )
|
| 367 |
+
if xyz_lines:
|
| 368 |
+
# add lines
|
| 369 |
+
l = mlines.Line2D(
|
| 370 |
+
[yz_slice.shape[0] - xyz[1], yz_slice.shape[0] - xyz[1]],
|
| 371 |
+
[xyz_pad, yz_slice.shape[0] - xyz_pad],
|
| 372 |
+
color=xyz_color,
|
| 373 |
+
alpha=xyz_alpha,
|
| 374 |
+
linewidth=xyz_linewidth,
|
| 375 |
+
)
|
| 376 |
+
ax.add_line(l)
|
| 377 |
+
l = mlines.Line2D(
|
| 378 |
+
[xyz_pad, yz_slice.shape[1] - xyz_pad],
|
| 379 |
+
[yz_slice.shape[1] - xyz[2], yz_slice.shape[1] - xyz[2]],
|
| 380 |
+
color=xyz_color,
|
| 381 |
+
alpha=xyz_alpha,
|
| 382 |
+
linewidth=xyz_linewidth,
|
| 383 |
+
)
|
| 384 |
+
ax.add_line(l)
|
| 385 |
+
if orient_labels:
|
| 386 |
+
ax.text(
|
| 387 |
+
0.5,
|
| 388 |
+
0.98,
|
| 389 |
+
"S",
|
| 390 |
+
horizontalalignment="center",
|
| 391 |
+
verticalalignment="top",
|
| 392 |
+
fontsize=20 * figsize,
|
| 393 |
+
color=textfontcolor,
|
| 394 |
+
transform=ax.transAxes,
|
| 395 |
+
)
|
| 396 |
+
ax.text(
|
| 397 |
+
0.5,
|
| 398 |
+
0.02,
|
| 399 |
+
"I",
|
| 400 |
+
horizontalalignment="center",
|
| 401 |
+
verticalalignment="bottom",
|
| 402 |
+
fontsize=20 * figsize,
|
| 403 |
+
color=textfontcolor,
|
| 404 |
+
transform=ax.transAxes,
|
| 405 |
+
)
|
| 406 |
+
ax.text(
|
| 407 |
+
0.98,
|
| 408 |
+
0.5,
|
| 409 |
+
"A",
|
| 410 |
+
horizontalalignment="right",
|
| 411 |
+
verticalalignment="center",
|
| 412 |
+
fontsize=20 * figsize,
|
| 413 |
+
color=textfontcolor,
|
| 414 |
+
transform=ax.transAxes,
|
| 415 |
+
)
|
| 416 |
+
ax.text(
|
| 417 |
+
0.02,
|
| 418 |
+
0.5,
|
| 419 |
+
"P",
|
| 420 |
+
horizontalalignment="left",
|
| 421 |
+
verticalalignment="center",
|
| 422 |
+
fontsize=20 * figsize,
|
| 423 |
+
color=textfontcolor,
|
| 424 |
+
transform=ax.transAxes,
|
| 425 |
+
)
|
| 426 |
+
ax.axis("off")
|
| 427 |
+
|
| 428 |
+
xz_slice = reorient_slice(image[:, xyz[1], :], 1)
|
| 429 |
+
ax = plt.subplot(gs[0, 1])
|
| 430 |
+
ax.imshow(xz_slice, cmap=cmap, vmin=vmin, vmax=vmax)
|
| 431 |
+
if overlay is not None:
|
| 432 |
+
xz_overlay = reorient_slice(overlay[:, xyz[1], :], 1)
|
| 433 |
+
ax.imshow(xz_overlay, alpha=overlay_alpha, cmap=overlay_cmap, vmin=vminol, vmax=vmaxol )
|
| 434 |
+
|
| 435 |
+
if xyz_lines:
|
| 436 |
+
# add lines
|
| 437 |
+
l = mlines.Line2D(
|
| 438 |
+
[xz_slice.shape[0] - xyz[0], xz_slice.shape[0] - xyz[0]],
|
| 439 |
+
[xyz_pad, xz_slice.shape[0] - xyz_pad],
|
| 440 |
+
color=xyz_color,
|
| 441 |
+
alpha=xyz_alpha,
|
| 442 |
+
linewidth=xyz_linewidth,
|
| 443 |
+
)
|
| 444 |
+
ax.add_line(l)
|
| 445 |
+
l = mlines.Line2D(
|
| 446 |
+
[xyz_pad, xz_slice.shape[1] - xyz_pad],
|
| 447 |
+
[xz_slice.shape[1] - xyz[2], xz_slice.shape[1] - xyz[2]],
|
| 448 |
+
color=xyz_color,
|
| 449 |
+
alpha=xyz_alpha,
|
| 450 |
+
linewidth=xyz_linewidth,
|
| 451 |
+
)
|
| 452 |
+
ax.add_line(l)
|
| 453 |
+
if orient_labels:
|
| 454 |
+
ax.text(
|
| 455 |
+
0.5,
|
| 456 |
+
0.98,
|
| 457 |
+
"S",
|
| 458 |
+
horizontalalignment="center",
|
| 459 |
+
verticalalignment="top",
|
| 460 |
+
fontsize=20 * figsize,
|
| 461 |
+
color=textfontcolor,
|
| 462 |
+
transform=ax.transAxes,
|
| 463 |
+
)
|
| 464 |
+
ax.text(
|
| 465 |
+
0.5,
|
| 466 |
+
0.02,
|
| 467 |
+
"I",
|
| 468 |
+
horizontalalignment="center",
|
| 469 |
+
verticalalignment="bottom",
|
| 470 |
+
fontsize=20 * figsize,
|
| 471 |
+
color=textfontcolor,
|
| 472 |
+
transform=ax.transAxes,
|
| 473 |
+
)
|
| 474 |
+
ax.text(
|
| 475 |
+
0.98,
|
| 476 |
+
0.5,
|
| 477 |
+
"L",
|
| 478 |
+
horizontalalignment="right",
|
| 479 |
+
verticalalignment="center",
|
| 480 |
+
fontsize=20 * figsize,
|
| 481 |
+
color=textfontcolor,
|
| 482 |
+
transform=ax.transAxes,
|
| 483 |
+
)
|
| 484 |
+
ax.text(
|
| 485 |
+
0.02,
|
| 486 |
+
0.5,
|
| 487 |
+
"R",
|
| 488 |
+
horizontalalignment="left",
|
| 489 |
+
verticalalignment="center",
|
| 490 |
+
fontsize=20 * figsize,
|
| 491 |
+
color=textfontcolor,
|
| 492 |
+
transform=ax.transAxes,
|
| 493 |
+
)
|
| 494 |
+
ax.axis("off")
|
| 495 |
+
|
| 496 |
+
xy_slice = reorient_slice(image[:, :, xyz[2]], 2)
|
| 497 |
+
if not flat:
|
| 498 |
+
ax = plt.subplot(gs[1, 1])
|
| 499 |
+
else:
|
| 500 |
+
ax = plt.subplot(gs[0, 2])
|
| 501 |
+
im = ax.imshow(xy_slice, cmap=cmap, vmin=vmin, vmax=vmax)
|
| 502 |
+
if overlay is not None:
|
| 503 |
+
xy_overlay = reorient_slice(overlay[:, :, xyz[2]], 2)
|
| 504 |
+
im = ax.imshow(xy_overlay, alpha=overlay_alpha, cmap=overlay_cmap, vmin=vminol, vmax=vmaxol)
|
| 505 |
+
|
| 506 |
+
if xyz_lines:
|
| 507 |
+
# add lines
|
| 508 |
+
l = mlines.Line2D(
|
| 509 |
+
[xy_slice.shape[0] - xyz[0], xy_slice.shape[0] - xyz[0]],
|
| 510 |
+
[xyz_pad, xy_slice.shape[0] - xyz_pad],
|
| 511 |
+
color=xyz_color,
|
| 512 |
+
alpha=xyz_alpha,
|
| 513 |
+
linewidth=xyz_linewidth,
|
| 514 |
+
)
|
| 515 |
+
ax.add_line(l)
|
| 516 |
+
l = mlines.Line2D(
|
| 517 |
+
[xyz_pad, xy_slice.shape[1] - xyz_pad],
|
| 518 |
+
[xy_slice.shape[1] - xyz[1], xy_slice.shape[1] - xyz[1]],
|
| 519 |
+
color=xyz_color,
|
| 520 |
+
alpha=xyz_alpha,
|
| 521 |
+
linewidth=xyz_linewidth,
|
| 522 |
+
)
|
| 523 |
+
ax.add_line(l)
|
| 524 |
+
if orient_labels:
|
| 525 |
+
ax.text(
|
| 526 |
+
0.5,
|
| 527 |
+
0.98,
|
| 528 |
+
"A",
|
| 529 |
+
horizontalalignment="center",
|
| 530 |
+
verticalalignment="top",
|
| 531 |
+
fontsize=20 * figsize,
|
| 532 |
+
color=textfontcolor,
|
| 533 |
+
transform=ax.transAxes,
|
| 534 |
+
)
|
| 535 |
+
ax.text(
|
| 536 |
+
0.5,
|
| 537 |
+
0.02,
|
| 538 |
+
"P",
|
| 539 |
+
horizontalalignment="center",
|
| 540 |
+
verticalalignment="bottom",
|
| 541 |
+
fontsize=20 * figsize,
|
| 542 |
+
color=textfontcolor,
|
| 543 |
+
transform=ax.transAxes,
|
| 544 |
+
)
|
| 545 |
+
ax.text(
|
| 546 |
+
0.98,
|
| 547 |
+
0.5,
|
| 548 |
+
"L",
|
| 549 |
+
horizontalalignment="right",
|
| 550 |
+
verticalalignment="center",
|
| 551 |
+
fontsize=20 * figsize,
|
| 552 |
+
color=textfontcolor,
|
| 553 |
+
transform=ax.transAxes,
|
| 554 |
+
)
|
| 555 |
+
ax.text(
|
| 556 |
+
0.02,
|
| 557 |
+
0.5,
|
| 558 |
+
"R",
|
| 559 |
+
horizontalalignment="left",
|
| 560 |
+
verticalalignment="center",
|
| 561 |
+
fontsize=20 * figsize,
|
| 562 |
+
color=textfontcolor,
|
| 563 |
+
transform=ax.transAxes,
|
| 564 |
+
)
|
| 565 |
+
ax.axis("off")
|
| 566 |
+
|
| 567 |
+
if not flat:
|
| 568 |
+
# empty corner
|
| 569 |
+
ax = plt.subplot(gs[1, 0])
|
| 570 |
+
if text is not None:
|
| 571 |
+
# add text
|
| 572 |
+
left, width = 0.25, 0.5
|
| 573 |
+
bottom, height = 0.25, 0.5
|
| 574 |
+
right = left + width
|
| 575 |
+
top = bottom + height
|
| 576 |
+
ax.text(
|
| 577 |
+
0.5 * (left + right) + text_dx,
|
| 578 |
+
0.5 * (bottom + top) + text_dy,
|
| 579 |
+
text,
|
| 580 |
+
horizontalalignment="center",
|
| 581 |
+
verticalalignment="center",
|
| 582 |
+
fontsize=textfontsize,
|
| 583 |
+
color=textfontcolor,
|
| 584 |
+
transform=ax.transAxes,
|
| 585 |
+
)
|
| 586 |
+
# ax.text(0.5, 0.5)
|
| 587 |
+
ax.imshow(np.zeros(image.shape[:-1]), cmap="Greys_r")
|
| 588 |
+
ax.axis("off")
|
| 589 |
+
|
| 590 |
+
if cbar:
|
| 591 |
+
cbar_start = (1 - cbar_length) / 2
|
| 592 |
+
if cbar_vertical:
|
| 593 |
+
cax = fig.add_axes([0.9 + cbar_dx, cbar_start, 0.03, cbar_length])
|
| 594 |
+
cbar_orient = "vertical"
|
| 595 |
+
else:
|
| 596 |
+
cax = fig.add_axes([cbar_start, 0.08 + cbar_dx, cbar_length, 0.03])
|
| 597 |
+
cbar_orient = "horizontal"
|
| 598 |
+
fig.colorbar(im, cax=cax, orientation=cbar_orient)
|
| 599 |
+
|
| 600 |
+
## multi-channel images ##
|
| 601 |
+
elif image.components > 1:
|
| 602 |
+
raise ValueError("Multi-channel images not currently supported!")
|
| 603 |
+
|
| 604 |
+
if filename is not None:
|
| 605 |
+
plt.savefig(filename, dpi=dpi, transparent=transparent)
|
| 606 |
+
plt.close(fig)
|
| 607 |
+
else:
|
| 608 |
+
plt.show()
|
| 609 |
+
|
| 610 |
+
# turn warnings back to default
|
| 611 |
+
warnings.simplefilter("default")
|
| 612 |
+
|
MindEyeV2/antspy/ants/plotting/plot_ortho_stack.py
ADDED
|
@@ -0,0 +1,505 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
Functions for plotting ants images
|
| 3 |
+
"""
|
| 4 |
+
|
| 5 |
+
|
| 6 |
+
__all__ = [
|
| 7 |
+
"plot_ortho_stack"
|
| 8 |
+
]
|
| 9 |
+
|
| 10 |
+
import fnmatch
|
| 11 |
+
import math
|
| 12 |
+
import os
|
| 13 |
+
import warnings
|
| 14 |
+
|
| 15 |
+
from matplotlib import gridspec
|
| 16 |
+
import matplotlib.pyplot as plt
|
| 17 |
+
import matplotlib.patheffects as path_effects
|
| 18 |
+
import matplotlib.lines as mlines
|
| 19 |
+
import matplotlib.patches as patches
|
| 20 |
+
import matplotlib.mlab as mlab
|
| 21 |
+
import matplotlib.animation as animation
|
| 22 |
+
from mpl_toolkits.axes_grid1.inset_locator import inset_axes
|
| 23 |
+
|
| 24 |
+
|
| 25 |
+
import numpy as np
|
| 26 |
+
import ants
|
| 27 |
+
|
| 28 |
+
|
| 29 |
+
|
| 30 |
+
|
| 31 |
+
def plot_ortho_stack(
|
| 32 |
+
images,
|
| 33 |
+
overlays=None,
|
| 34 |
+
reorient=True,
|
| 35 |
+
# xyz arguments
|
| 36 |
+
xyz=None,
|
| 37 |
+
xyz_lines=False,
|
| 38 |
+
xyz_color="red",
|
| 39 |
+
xyz_alpha=0.6,
|
| 40 |
+
xyz_linewidth=2,
|
| 41 |
+
xyz_pad=5,
|
| 42 |
+
# base image arguments
|
| 43 |
+
cmap="Greys_r",
|
| 44 |
+
alpha=1,
|
| 45 |
+
# overlay arguments
|
| 46 |
+
overlay_cmap="jet",
|
| 47 |
+
overlay_alpha=0.9,
|
| 48 |
+
# background arguments
|
| 49 |
+
black_bg=True,
|
| 50 |
+
bg_thresh_quant=0.01,
|
| 51 |
+
bg_val_quant=0.99,
|
| 52 |
+
# scale/crop/domain arguments
|
| 53 |
+
crop=False,
|
| 54 |
+
scale=False,
|
| 55 |
+
domain_image_map=None,
|
| 56 |
+
# title arguments
|
| 57 |
+
title=None,
|
| 58 |
+
titlefontsize=24,
|
| 59 |
+
title_dx=0,
|
| 60 |
+
title_dy=0,
|
| 61 |
+
# 4th panel text arguemnts
|
| 62 |
+
text=None,
|
| 63 |
+
textfontsize=24,
|
| 64 |
+
textfontcolor="white",
|
| 65 |
+
text_dx=0,
|
| 66 |
+
text_dy=0,
|
| 67 |
+
# save & size arguments
|
| 68 |
+
filename=None,
|
| 69 |
+
dpi=500,
|
| 70 |
+
figsize=1.0,
|
| 71 |
+
colpad=0,
|
| 72 |
+
rowpad=0,
|
| 73 |
+
transpose=False,
|
| 74 |
+
transparent=True,
|
| 75 |
+
orient_labels=True,
|
| 76 |
+
):
|
| 77 |
+
"""
|
| 78 |
+
Create a stack of orthographic plots with optional overlays.
|
| 79 |
+
|
| 80 |
+
Use mask_image and/or threshold_image to preprocess images to be be
|
| 81 |
+
overlaid and display the overlays in a given range. See the wiki examples.
|
| 82 |
+
|
| 83 |
+
Example
|
| 84 |
+
-------
|
| 85 |
+
>>> import ants
|
| 86 |
+
>>> mni = ants.image_read(ants.get_data('mni'))
|
| 87 |
+
>>> ch2 = ants.image_read(ants.get_data('ch2'))
|
| 88 |
+
>>> ants.plot_ortho_stack([mni,mni,mni])
|
| 89 |
+
"""
|
| 90 |
+
|
| 91 |
+
def mirror_matrix(x):
|
| 92 |
+
return x[::-1, :]
|
| 93 |
+
|
| 94 |
+
def rotate270_matrix(x):
|
| 95 |
+
return mirror_matrix(x.T)
|
| 96 |
+
|
| 97 |
+
def reorient_slice(x, axis):
|
| 98 |
+
return rotate270_matrix(x)
|
| 99 |
+
|
| 100 |
+
# need this hack because of a weird NaN warning from matplotlib with overlays
|
| 101 |
+
warnings.simplefilter("ignore")
|
| 102 |
+
|
| 103 |
+
n_images = len(images)
|
| 104 |
+
|
| 105 |
+
# handle `image` argument
|
| 106 |
+
for i in range(n_images):
|
| 107 |
+
if isinstance(images[i], str):
|
| 108 |
+
images[i] = ants.image_read(images[i])
|
| 109 |
+
if not ants.is_image(images[i]):
|
| 110 |
+
raise ValueError("image argument must be an ANTsImage")
|
| 111 |
+
if images[i].dimension != 3:
|
| 112 |
+
raise ValueError("Input image must have 3 dimensions!")
|
| 113 |
+
|
| 114 |
+
if overlays is None:
|
| 115 |
+
overlays = [None] * n_images
|
| 116 |
+
# handle `overlay` argument
|
| 117 |
+
for i in range(n_images):
|
| 118 |
+
if overlays[i] is not None:
|
| 119 |
+
if isinstance(overlays[i], str):
|
| 120 |
+
overlays[i] = ants.image_read(overlays[i])
|
| 121 |
+
if not ants.is_image(overlays[i]):
|
| 122 |
+
raise ValueError("overlay argument must be an ANTsImage")
|
| 123 |
+
if overlays[i].components > 1:
|
| 124 |
+
raise ValueError("overlays[i] cannot have more than one voxel component")
|
| 125 |
+
if overlays[i].dimension != 3:
|
| 126 |
+
raise ValueError("Overlay image must have 3 dimensions!")
|
| 127 |
+
|
| 128 |
+
if not ants.image_physical_space_consistency(images[i], overlays[i]):
|
| 129 |
+
overlays[i] = ants.resample_image_to_target(
|
| 130 |
+
overlays[i], images[i], interp_type="linear"
|
| 131 |
+
)
|
| 132 |
+
|
| 133 |
+
for i in range(1, n_images):
|
| 134 |
+
if not ants.image_physical_space_consistency(images[0], images[i]):
|
| 135 |
+
images[i] = ants.resample_image_to_target(
|
| 136 |
+
images[0], images[i], interp_type="linear"
|
| 137 |
+
)
|
| 138 |
+
|
| 139 |
+
# reorient images
|
| 140 |
+
if reorient != False:
|
| 141 |
+
if reorient == True:
|
| 142 |
+
reorient = "RPI"
|
| 143 |
+
|
| 144 |
+
for i in range(n_images):
|
| 145 |
+
images[i] = images[i].reorient_image2(reorient)
|
| 146 |
+
|
| 147 |
+
if overlays[i] is not None:
|
| 148 |
+
overlays[i] = overlays[i].reorient_image2(reorient)
|
| 149 |
+
|
| 150 |
+
# handle `slices` argument
|
| 151 |
+
if xyz is None:
|
| 152 |
+
xyz = [int(s / 2) for s in images[0].shape]
|
| 153 |
+
for i in range(3):
|
| 154 |
+
if xyz[i] is None:
|
| 155 |
+
xyz[i] = int(images[0].shape[i] / 2)
|
| 156 |
+
|
| 157 |
+
# resample image if spacing is very unbalanced
|
| 158 |
+
spacing = [s for i, s in enumerate(images[0].spacing)]
|
| 159 |
+
if (max(spacing) / min(spacing)) > 3.0:
|
| 160 |
+
new_spacing = (1, 1, 1)
|
| 161 |
+
for i in range(n_images):
|
| 162 |
+
images[i] = images[i].resample_image(tuple(new_spacing))
|
| 163 |
+
if overlays[i] is not None:
|
| 164 |
+
overlays[i] = overlays[i].resample_image(tuple(new_spacing))
|
| 165 |
+
xyz = [
|
| 166 |
+
int(sl * (sold / snew)) for sl, sold, snew in zip(xyz, spacing, new_spacing)
|
| 167 |
+
]
|
| 168 |
+
|
| 169 |
+
# potentially crop image
|
| 170 |
+
if crop:
|
| 171 |
+
for i in range(n_images):
|
| 172 |
+
plotmask = images[i].get_mask(cleanup=0)
|
| 173 |
+
if plotmask.max() == 0:
|
| 174 |
+
plotmask += 1
|
| 175 |
+
images[i] = images[i].crop_image(plotmask)
|
| 176 |
+
if overlays[i] is not None:
|
| 177 |
+
overlays[i] = overlays[i].crop_image(plotmask)
|
| 178 |
+
|
| 179 |
+
# pad images
|
| 180 |
+
for i in range(n_images):
|
| 181 |
+
if i == 0:
|
| 182 |
+
images[i], lowpad, uppad = images[i].pad_image(return_padvals=True)
|
| 183 |
+
else:
|
| 184 |
+
images[i] = images[i].pad_image()
|
| 185 |
+
if overlays[i] is not None:
|
| 186 |
+
overlays[i] = overlays[i].pad_image()
|
| 187 |
+
xyz = [v + l for v, l in zip(xyz, lowpad)]
|
| 188 |
+
|
| 189 |
+
# handle `domain_image_map` argument
|
| 190 |
+
if domain_image_map is not None:
|
| 191 |
+
if ants.is_image(domain_image_map):
|
| 192 |
+
tx = ants.new_ants_transform(
|
| 193 |
+
precision="float", transform_type="AffineTransform", dimension=3
|
| 194 |
+
)
|
| 195 |
+
for i in range(n_images):
|
| 196 |
+
images[i] = ants.apply_ants_transform_to_image(
|
| 197 |
+
tx, images[i], domain_image_map
|
| 198 |
+
)
|
| 199 |
+
|
| 200 |
+
if overlays[i] is not None:
|
| 201 |
+
overlays[i] = ants.apply_ants_transform_to_image(
|
| 202 |
+
tx, overlays[i], domain_image_map, interpolation="linear"
|
| 203 |
+
)
|
| 204 |
+
else:
|
| 205 |
+
raise Exception('The domain_image_map must be an ants image.')
|
| 206 |
+
|
| 207 |
+
# potentially find dynamic range
|
| 208 |
+
if scale == True:
|
| 209 |
+
vmins = []
|
| 210 |
+
vmaxs = []
|
| 211 |
+
for i in range(n_images):
|
| 212 |
+
vmin, vmax = images[i].quantile((0.05, 0.95))
|
| 213 |
+
vmins.append(vmin)
|
| 214 |
+
vmaxs.append(vmax)
|
| 215 |
+
elif isinstance(scale, (list, tuple)):
|
| 216 |
+
if len(scale) != 2:
|
| 217 |
+
raise ValueError(
|
| 218 |
+
"scale argument must be boolean or list/tuple with two values"
|
| 219 |
+
)
|
| 220 |
+
vmins = []
|
| 221 |
+
vmaxs = []
|
| 222 |
+
for i in range(n_images):
|
| 223 |
+
vmin, vmax = images[i].quantile(scale)
|
| 224 |
+
vmins.append(vmin)
|
| 225 |
+
vmaxs.append(vmax)
|
| 226 |
+
else:
|
| 227 |
+
vmin = None
|
| 228 |
+
vmax = None
|
| 229 |
+
|
| 230 |
+
if not transpose:
|
| 231 |
+
nrow = n_images
|
| 232 |
+
ncol = 3
|
| 233 |
+
else:
|
| 234 |
+
nrow = 3
|
| 235 |
+
ncol = n_images
|
| 236 |
+
|
| 237 |
+
fig = plt.figure(figsize=((ncol + 1) * 2.5 * figsize, (nrow + 1) * 2.5 * figsize))
|
| 238 |
+
if title is not None:
|
| 239 |
+
basey = 0.93
|
| 240 |
+
basex = 0.5
|
| 241 |
+
fig.suptitle(
|
| 242 |
+
title, fontsize=titlefontsize, color=textfontcolor, x=basex + title_dx, y=basey + title_dy
|
| 243 |
+
)
|
| 244 |
+
|
| 245 |
+
if (colpad > 0) and (rowpad > 0):
|
| 246 |
+
bothgridpad = max(colpad, rowpad)
|
| 247 |
+
colpad = 0
|
| 248 |
+
rowpad = 0
|
| 249 |
+
else:
|
| 250 |
+
bothgridpad = 0.0
|
| 251 |
+
|
| 252 |
+
gs = gridspec.GridSpec(
|
| 253 |
+
nrow,
|
| 254 |
+
ncol,
|
| 255 |
+
wspace=bothgridpad,
|
| 256 |
+
hspace=0.0,
|
| 257 |
+
top=1.0 - 0.5 / (nrow + 1),
|
| 258 |
+
bottom=0.5 / (nrow + 1) + colpad,
|
| 259 |
+
left=0.5 / (ncol + 1) + rowpad,
|
| 260 |
+
right=1 - 0.5 / (ncol + 1),
|
| 261 |
+
)
|
| 262 |
+
|
| 263 |
+
# pad image to have isotropic array dimensions
|
| 264 |
+
vminols=[]
|
| 265 |
+
vmaxols=[]
|
| 266 |
+
for i in range(n_images):
|
| 267 |
+
images[i] = images[i].numpy()
|
| 268 |
+
if overlays[i] is not None:
|
| 269 |
+
vminols.append( overlays[i].min() )
|
| 270 |
+
vmaxols.append( overlays[i].max() )
|
| 271 |
+
overlays[i] = overlays[i].numpy()
|
| 272 |
+
if overlays[i].dtype not in ["uint8", "uint32"]:
|
| 273 |
+
overlays[i][np.abs(overlays[i]) == 0] = np.nan
|
| 274 |
+
|
| 275 |
+
####################
|
| 276 |
+
####################
|
| 277 |
+
for i in range(n_images):
|
| 278 |
+
yz_slice = reorient_slice(images[i][xyz[0], :, :], 0)
|
| 279 |
+
if not transpose:
|
| 280 |
+
ax = plt.subplot(gs[i, 0])
|
| 281 |
+
else:
|
| 282 |
+
ax = plt.subplot(gs[0, i])
|
| 283 |
+
ax.imshow(yz_slice, cmap=cmap, vmin=vmin, vmax=vmax)
|
| 284 |
+
if overlays[i] is not None:
|
| 285 |
+
yz_overlay = reorient_slice(overlays[i][xyz[0], :, :], 0)
|
| 286 |
+
ax.imshow(yz_overlay, alpha=overlay_alpha, cmap=overlay_cmap,
|
| 287 |
+
vmin=vminols[i], vmax=vmaxols[i])
|
| 288 |
+
if xyz_lines:
|
| 289 |
+
# add lines
|
| 290 |
+
l = mlines.Line2D(
|
| 291 |
+
[yz_slice.shape[0] - xyz[1], yz_slice.shape[0] - xyz[1]],
|
| 292 |
+
[xyz_pad, yz_slice.shape[0] - xyz_pad],
|
| 293 |
+
color=xyz_color,
|
| 294 |
+
alpha=xyz_alpha,
|
| 295 |
+
linewidth=xyz_linewidth,
|
| 296 |
+
)
|
| 297 |
+
ax.add_line(l)
|
| 298 |
+
l = mlines.Line2D(
|
| 299 |
+
[xyz_pad, yz_slice.shape[1] - xyz_pad],
|
| 300 |
+
[yz_slice.shape[1] - xyz[2], yz_slice.shape[1] - xyz[2]],
|
| 301 |
+
color=xyz_color,
|
| 302 |
+
alpha=xyz_alpha,
|
| 303 |
+
linewidth=xyz_linewidth,
|
| 304 |
+
)
|
| 305 |
+
ax.add_line(l)
|
| 306 |
+
if orient_labels:
|
| 307 |
+
ax.text(
|
| 308 |
+
0.5,
|
| 309 |
+
0.98,
|
| 310 |
+
"S",
|
| 311 |
+
horizontalalignment="center",
|
| 312 |
+
verticalalignment="top",
|
| 313 |
+
fontsize=20 * figsize,
|
| 314 |
+
color=textfontcolor,
|
| 315 |
+
transform=ax.transAxes,
|
| 316 |
+
)
|
| 317 |
+
ax.text(
|
| 318 |
+
0.5,
|
| 319 |
+
0.02,
|
| 320 |
+
"I",
|
| 321 |
+
horizontalalignment="center",
|
| 322 |
+
verticalalignment="bottom",
|
| 323 |
+
fontsize=20 * figsize,
|
| 324 |
+
color=textfontcolor,
|
| 325 |
+
transform=ax.transAxes,
|
| 326 |
+
)
|
| 327 |
+
ax.text(
|
| 328 |
+
0.98,
|
| 329 |
+
0.5,
|
| 330 |
+
"A",
|
| 331 |
+
horizontalalignment="right",
|
| 332 |
+
verticalalignment="center",
|
| 333 |
+
fontsize=20 * figsize,
|
| 334 |
+
color=textfontcolor,
|
| 335 |
+
transform=ax.transAxes,
|
| 336 |
+
)
|
| 337 |
+
ax.text(
|
| 338 |
+
0.02,
|
| 339 |
+
0.5,
|
| 340 |
+
"P",
|
| 341 |
+
horizontalalignment="left",
|
| 342 |
+
verticalalignment="center",
|
| 343 |
+
fontsize=20 * figsize,
|
| 344 |
+
color=textfontcolor,
|
| 345 |
+
transform=ax.transAxes,
|
| 346 |
+
)
|
| 347 |
+
ax.axis("off")
|
| 348 |
+
####################
|
| 349 |
+
####################
|
| 350 |
+
|
| 351 |
+
xz_slice = reorient_slice(images[i][:, xyz[1], :], 1)
|
| 352 |
+
if not transpose:
|
| 353 |
+
ax = plt.subplot(gs[i, 1])
|
| 354 |
+
else:
|
| 355 |
+
ax = plt.subplot(gs[1, i])
|
| 356 |
+
ax.imshow(xz_slice, cmap=cmap, vmin=vmin, vmax=vmax)
|
| 357 |
+
if overlays[i] is not None:
|
| 358 |
+
xz_overlay = reorient_slice(overlays[i][:, xyz[1], :], 1)
|
| 359 |
+
ax.imshow(xz_overlay, alpha=overlay_alpha, cmap=overlay_cmap,
|
| 360 |
+
vmin=vminols[i], vmax=vmaxols[i])
|
| 361 |
+
if xyz_lines:
|
| 362 |
+
# add lines
|
| 363 |
+
l = mlines.Line2D(
|
| 364 |
+
[xz_slice.shape[0] - xyz[0], xz_slice.shape[0] - xyz[0]],
|
| 365 |
+
[xyz_pad, xz_slice.shape[0] - xyz_pad],
|
| 366 |
+
color=xyz_color,
|
| 367 |
+
alpha=xyz_alpha,
|
| 368 |
+
linewidth=xyz_linewidth,
|
| 369 |
+
)
|
| 370 |
+
ax.add_line(l)
|
| 371 |
+
l = mlines.Line2D(
|
| 372 |
+
[xyz_pad, xz_slice.shape[1] - xyz_pad],
|
| 373 |
+
[xz_slice.shape[1] - xyz[2], xz_slice.shape[1] - xyz[2]],
|
| 374 |
+
color=xyz_color,
|
| 375 |
+
alpha=xyz_alpha,
|
| 376 |
+
linewidth=xyz_linewidth,
|
| 377 |
+
)
|
| 378 |
+
ax.add_line(l)
|
| 379 |
+
if orient_labels:
|
| 380 |
+
ax.text(
|
| 381 |
+
0.5,
|
| 382 |
+
0.98,
|
| 383 |
+
"I",
|
| 384 |
+
horizontalalignment="center",
|
| 385 |
+
verticalalignment="top",
|
| 386 |
+
fontsize=20 * figsize,
|
| 387 |
+
color=textfontcolor,
|
| 388 |
+
transform=ax.transAxes,
|
| 389 |
+
)
|
| 390 |
+
ax.text(
|
| 391 |
+
0.5,
|
| 392 |
+
0.02,
|
| 393 |
+
"S",
|
| 394 |
+
horizontalalignment="center",
|
| 395 |
+
verticalalignment="bottom",
|
| 396 |
+
fontsize=20 * figsize,
|
| 397 |
+
color=textfontcolor,
|
| 398 |
+
transform=ax.transAxes,
|
| 399 |
+
)
|
| 400 |
+
ax.text(
|
| 401 |
+
0.98,
|
| 402 |
+
0.5,
|
| 403 |
+
"L",
|
| 404 |
+
horizontalalignment="right",
|
| 405 |
+
verticalalignment="center",
|
| 406 |
+
fontsize=20 * figsize,
|
| 407 |
+
color=textfontcolor,
|
| 408 |
+
transform=ax.transAxes,
|
| 409 |
+
)
|
| 410 |
+
ax.text(
|
| 411 |
+
0.02,
|
| 412 |
+
0.5,
|
| 413 |
+
"R",
|
| 414 |
+
horizontalalignment="left",
|
| 415 |
+
verticalalignment="center",
|
| 416 |
+
fontsize=20 * figsize,
|
| 417 |
+
color=textfontcolor,
|
| 418 |
+
transform=ax.transAxes,
|
| 419 |
+
)
|
| 420 |
+
ax.axis("off")
|
| 421 |
+
|
| 422 |
+
####################
|
| 423 |
+
####################
|
| 424 |
+
xy_slice = reorient_slice(images[i][:, :, xyz[2]], 2)
|
| 425 |
+
if not transpose:
|
| 426 |
+
ax = plt.subplot(gs[i, 2])
|
| 427 |
+
else:
|
| 428 |
+
ax = plt.subplot(gs[2, i])
|
| 429 |
+
ax.imshow(xy_slice, cmap=cmap, vmin=vmin, vmax=vmax)
|
| 430 |
+
if overlays[i] is not None:
|
| 431 |
+
xy_overlay = reorient_slice(overlays[i][:, :, xyz[2]], 2)
|
| 432 |
+
ax.imshow(xy_overlay, alpha=overlay_alpha, cmap=overlay_cmap,
|
| 433 |
+
vmin=vminols[i], vmax=vmaxols[i])
|
| 434 |
+
if xyz_lines:
|
| 435 |
+
# add lines
|
| 436 |
+
l = mlines.Line2D(
|
| 437 |
+
[xy_slice.shape[0] - xyz[0], xy_slice.shape[0] - xyz[0]],
|
| 438 |
+
[xyz_pad, xy_slice.shape[0] - xyz_pad],
|
| 439 |
+
color=xyz_color,
|
| 440 |
+
alpha=xyz_alpha,
|
| 441 |
+
linewidth=xyz_linewidth,
|
| 442 |
+
)
|
| 443 |
+
ax.add_line(l)
|
| 444 |
+
l = mlines.Line2D(
|
| 445 |
+
[xyz_pad, xy_slice.shape[1] - xyz_pad],
|
| 446 |
+
[xy_slice.shape[1] - xyz[1], xy_slice.shape[1] - xyz[1]],
|
| 447 |
+
color=xyz_color,
|
| 448 |
+
alpha=xyz_alpha,
|
| 449 |
+
linewidth=xyz_linewidth,
|
| 450 |
+
)
|
| 451 |
+
ax.add_line(l)
|
| 452 |
+
if orient_labels:
|
| 453 |
+
ax.text(
|
| 454 |
+
0.5,
|
| 455 |
+
0.98,
|
| 456 |
+
"A",
|
| 457 |
+
horizontalalignment="center",
|
| 458 |
+
verticalalignment="top",
|
| 459 |
+
fontsize=20 * figsize,
|
| 460 |
+
color=textfontcolor,
|
| 461 |
+
transform=ax.transAxes,
|
| 462 |
+
)
|
| 463 |
+
ax.text(
|
| 464 |
+
0.5,
|
| 465 |
+
0.02,
|
| 466 |
+
"P",
|
| 467 |
+
horizontalalignment="center",
|
| 468 |
+
verticalalignment="bottom",
|
| 469 |
+
fontsize=20 * figsize,
|
| 470 |
+
color=textfontcolor,
|
| 471 |
+
transform=ax.transAxes,
|
| 472 |
+
)
|
| 473 |
+
ax.text(
|
| 474 |
+
0.98,
|
| 475 |
+
0.5,
|
| 476 |
+
"L",
|
| 477 |
+
horizontalalignment="right",
|
| 478 |
+
verticalalignment="center",
|
| 479 |
+
fontsize=20 * figsize,
|
| 480 |
+
color=textfontcolor,
|
| 481 |
+
transform=ax.transAxes,
|
| 482 |
+
)
|
| 483 |
+
ax.text(
|
| 484 |
+
0.02,
|
| 485 |
+
0.5,
|
| 486 |
+
"R",
|
| 487 |
+
horizontalalignment="left",
|
| 488 |
+
verticalalignment="center",
|
| 489 |
+
fontsize=20 * figsize,
|
| 490 |
+
color=textfontcolor,
|
| 491 |
+
transform=ax.transAxes,
|
| 492 |
+
)
|
| 493 |
+
ax.axis("off")
|
| 494 |
+
|
| 495 |
+
####################
|
| 496 |
+
####################
|
| 497 |
+
|
| 498 |
+
if filename is not None:
|
| 499 |
+
plt.savefig(filename, dpi=dpi, transparent=transparent)
|
| 500 |
+
plt.close(fig)
|
| 501 |
+
else:
|
| 502 |
+
plt.show()
|
| 503 |
+
|
| 504 |
+
# turn warnings back to default
|
| 505 |
+
warnings.simplefilter("default")
|
MindEyeV2/antspy/ants/utils/__init__.py
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from .channels import merge_channels, split_channels
|
| 2 |
+
from .consistency import image_physical_space_consistency, allclose
|
| 3 |
+
from .get_ants_data import get_ants_data, get_data
|
| 4 |
+
from .matrix_image import (matrix_to_images,
|
| 5 |
+
images_from_matrix,
|
| 6 |
+
image_list_to_matrix,
|
| 7 |
+
images_to_matrix,
|
| 8 |
+
matrix_from_images,
|
| 9 |
+
timeseries_to_matrix,
|
| 10 |
+
matrix_to_timeseries)
|
| 11 |
+
from .mni2tal import mni2tal
|
| 12 |
+
from .ndimage_to_list import ndimage_to_list, list_to_ndimage
|
| 13 |
+
from .nifti_to_ants import nifti_to_ants
|
| 14 |
+
from .scalar_rgb_vector import rgb_to_vector, vector_to_rgb, scalar_to_rgb
|
| 15 |
+
from .sitk_to_ants import from_sitk, to_sitk
|
MindEyeV2/antspy/ants/utils/channels.py
ADDED
|
@@ -0,0 +1,95 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
|
| 2 |
+
|
| 3 |
+
|
| 4 |
+
__all__ = ['merge_channels',
|
| 5 |
+
'split_channels']
|
| 6 |
+
|
| 7 |
+
|
| 8 |
+
|
| 9 |
+
|
| 10 |
+
import ants
|
| 11 |
+
from ants.internal import get_lib_fn
|
| 12 |
+
from ants.decorators import image_method
|
| 13 |
+
|
| 14 |
+
|
| 15 |
+
def merge_channels(image_list, channels_first=False):
|
| 16 |
+
"""
|
| 17 |
+
Merge channels of multiple scalar ANTsImage types into one
|
| 18 |
+
multi-channel ANTsImage
|
| 19 |
+
|
| 20 |
+
ANTsR function: `mergeChannels`
|
| 21 |
+
|
| 22 |
+
Arguments
|
| 23 |
+
---------
|
| 24 |
+
image_list : list/tuple of ANTsImage types
|
| 25 |
+
scalar images to merge
|
| 26 |
+
|
| 27 |
+
Returns
|
| 28 |
+
-------
|
| 29 |
+
ANTsImage
|
| 30 |
+
|
| 31 |
+
Example
|
| 32 |
+
-------
|
| 33 |
+
>>> import ants
|
| 34 |
+
>>> image = ants.image_read(ants.get_ants_data('r16'))
|
| 35 |
+
>>> image2 = ants.image_read(ants.get_ants_data('r16'))
|
| 36 |
+
>>> image3 = ants.merge_channels([image,image2])
|
| 37 |
+
>>> image3 = ants.merge_channels([image,image2], channels_first=True)
|
| 38 |
+
>>> image3.numpy()
|
| 39 |
+
>>> image3.components == 2
|
| 40 |
+
"""
|
| 41 |
+
inpixeltype = image_list[0].pixeltype
|
| 42 |
+
dimension = image_list[0].dimension
|
| 43 |
+
components = len(image_list)
|
| 44 |
+
|
| 45 |
+
for image in image_list:
|
| 46 |
+
if not ants.is_image(image):
|
| 47 |
+
raise ValueError('list may only contain ANTsImage objects')
|
| 48 |
+
if image.pixeltype != inpixeltype:
|
| 49 |
+
raise ValueError('all images must have the same pixeltype')
|
| 50 |
+
|
| 51 |
+
libfn = get_lib_fn('mergeChannels')
|
| 52 |
+
image_ptr = libfn([image.pointer for image in image_list])
|
| 53 |
+
|
| 54 |
+
image = ants.from_pointer(image_ptr)
|
| 55 |
+
image.channels_first = channels_first
|
| 56 |
+
return image
|
| 57 |
+
|
| 58 |
+
@image_method
|
| 59 |
+
def split_channels(image):
|
| 60 |
+
"""
|
| 61 |
+
Split channels of a multi-channel ANTsImage into a collection
|
| 62 |
+
of scalar ANTsImage types
|
| 63 |
+
|
| 64 |
+
Arguments
|
| 65 |
+
---------
|
| 66 |
+
image : ANTsImage
|
| 67 |
+
multi-channel image to split
|
| 68 |
+
|
| 69 |
+
Returns
|
| 70 |
+
-------
|
| 71 |
+
list of ANTsImage types
|
| 72 |
+
|
| 73 |
+
Example
|
| 74 |
+
-------
|
| 75 |
+
>>> import ants
|
| 76 |
+
>>> image = ants.image_read(ants.get_ants_data('r16'), 'float')
|
| 77 |
+
>>> image2 = ants.image_read(ants.get_ants_data('r16'), 'float')
|
| 78 |
+
>>> imagemerge = ants.merge_channels([image,image2])
|
| 79 |
+
>>> imagemerge.components == 2
|
| 80 |
+
>>> images_unmerged = ants.split_channels(imagemerge)
|
| 81 |
+
>>> len(images_unmerged) == 2
|
| 82 |
+
>>> images_unmerged[0].components == 1
|
| 83 |
+
"""
|
| 84 |
+
inpixeltype = image.pixeltype
|
| 85 |
+
dimension = image.dimension
|
| 86 |
+
components = 1
|
| 87 |
+
|
| 88 |
+
libfn = get_lib_fn('splitChannels')
|
| 89 |
+
itkimages = libfn(image.pointer)
|
| 90 |
+
antsimages = [ants.from_pointer(itkimage) for itkimage in itkimages]
|
| 91 |
+
return antsimages
|
| 92 |
+
|
| 93 |
+
|
| 94 |
+
|
| 95 |
+
|
MindEyeV2/antspy/ants/utils/consistency.py
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import numpy as np
|
| 2 |
+
import ants
|
| 3 |
+
from ants.decorators import image_method
|
| 4 |
+
|
| 5 |
+
__all__ = ['image_physical_space_consistency',
|
| 6 |
+
'allclose']
|
| 7 |
+
|
| 8 |
+
@image_method
|
| 9 |
+
def image_physical_space_consistency(image1, image2, tolerance=1e-2, datatype=False):
|
| 10 |
+
"""
|
| 11 |
+
Check if two or more ANTsImage objects occupy the same physical space
|
| 12 |
+
|
| 13 |
+
ANTsR function: `antsImagePhysicalSpaceConsistency`
|
| 14 |
+
|
| 15 |
+
Arguments
|
| 16 |
+
---------
|
| 17 |
+
*images : ANTsImages
|
| 18 |
+
images to compare
|
| 19 |
+
|
| 20 |
+
tolerance : float
|
| 21 |
+
tolerance when checking origin and spacing
|
| 22 |
+
|
| 23 |
+
data_type : boolean
|
| 24 |
+
If true, also check that the image data types are the same
|
| 25 |
+
|
| 26 |
+
Returns
|
| 27 |
+
-------
|
| 28 |
+
boolean
|
| 29 |
+
true if images share same physical space, false otherwise
|
| 30 |
+
"""
|
| 31 |
+
images = [image1, image2]
|
| 32 |
+
|
| 33 |
+
img1 = images[0]
|
| 34 |
+
for img2 in images[1:]:
|
| 35 |
+
if (not ants.is_image(img1)) or (not ants.is_image(img2)):
|
| 36 |
+
raise ValueError('Both images must be of class `AntsImage`')
|
| 37 |
+
|
| 38 |
+
# image dimension check
|
| 39 |
+
if img1.dimension != img2.dimension:
|
| 40 |
+
return False
|
| 41 |
+
|
| 42 |
+
# image spacing check
|
| 43 |
+
space_diffs = sum([abs(s1-s2)>tolerance for s1, s2 in zip(img1.spacing, img2.spacing)])
|
| 44 |
+
if space_diffs > 0:
|
| 45 |
+
return False
|
| 46 |
+
|
| 47 |
+
# image origin check
|
| 48 |
+
origin_diffs = sum([abs(s1-s2)>tolerance for s1, s2 in zip(img1.origin, img2.origin)])
|
| 49 |
+
if origin_diffs > 0:
|
| 50 |
+
return False
|
| 51 |
+
|
| 52 |
+
# image direction check
|
| 53 |
+
origin_diff = np.allclose(img1.direction, img2.direction, atol=tolerance)
|
| 54 |
+
if not origin_diff:
|
| 55 |
+
return False
|
| 56 |
+
|
| 57 |
+
# data type
|
| 58 |
+
if datatype == True:
|
| 59 |
+
if img1.pixeltype != img2.pixeltype:
|
| 60 |
+
return False
|
| 61 |
+
|
| 62 |
+
if img1.components != img2.components:
|
| 63 |
+
return False
|
| 64 |
+
|
| 65 |
+
return True
|
| 66 |
+
|
| 67 |
+
|
| 68 |
+
@image_method
|
| 69 |
+
def allclose(image1, image2):
|
| 70 |
+
"""
|
| 71 |
+
Check if two images have the same array values
|
| 72 |
+
"""
|
| 73 |
+
return np.allclose(image1.numpy(), image2.numpy())
|
MindEyeV2/antspy/ants/utils/get_ants_data.py
ADDED
|
@@ -0,0 +1,108 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
Get local ANTsPy data
|
| 3 |
+
"""
|
| 4 |
+
|
| 5 |
+
__all__ = ['get_ants_data',
|
| 6 |
+
'get_data']
|
| 7 |
+
|
| 8 |
+
import os
|
| 9 |
+
import requests
|
| 10 |
+
import tempfile
|
| 11 |
+
|
| 12 |
+
def get_data(file_id=None, target_file_name=None, antsx_cache_directory=None):
|
| 13 |
+
"""
|
| 14 |
+
Get ANTsPy test data file
|
| 15 |
+
|
| 16 |
+
ANTsR function: `getANTsRData`
|
| 17 |
+
|
| 18 |
+
Arguments
|
| 19 |
+
---------
|
| 20 |
+
name : string
|
| 21 |
+
name of test image tag to retrieve
|
| 22 |
+
Options:
|
| 23 |
+
- 'r16'
|
| 24 |
+
- 'r27'
|
| 25 |
+
- 'r30'
|
| 26 |
+
- 'r62'
|
| 27 |
+
- 'r64'
|
| 28 |
+
- 'r85'
|
| 29 |
+
- 'ch2'
|
| 30 |
+
- 'mni'
|
| 31 |
+
- 'surf'
|
| 32 |
+
- 'pcasl'
|
| 33 |
+
Returns
|
| 34 |
+
-------
|
| 35 |
+
string
|
| 36 |
+
filepath of test image
|
| 37 |
+
|
| 38 |
+
Example
|
| 39 |
+
-------
|
| 40 |
+
>>> import ants
|
| 41 |
+
>>> mnipath = ants.get_ants_data('mni')
|
| 42 |
+
"""
|
| 43 |
+
|
| 44 |
+
def switch_data(argument):
|
| 45 |
+
switcher = {
|
| 46 |
+
"r16": "https://ndownloader.figshare.com/files/28726512",
|
| 47 |
+
"r27": "https://ndownloader.figshare.com/files/28726515",
|
| 48 |
+
"r30": "https://ndownloader.figshare.com/files/28726518",
|
| 49 |
+
"r62": "https://ndownloader.figshare.com/files/28726521",
|
| 50 |
+
"r64": "https://ndownloader.figshare.com/files/28726524",
|
| 51 |
+
"r85": "https://ndownloader.figshare.com/files/28726527",
|
| 52 |
+
"ch2": "https://ndownloader.figshare.com/files/28726494",
|
| 53 |
+
"mni": "https://ndownloader.figshare.com/files/28726500",
|
| 54 |
+
"surf": "https://ndownloader.figshare.com/files/28726530",
|
| 55 |
+
"pcasl": "http://files.figshare.com/1862041/101_pcasl.nii.gz",
|
| 56 |
+
}
|
| 57 |
+
return(switcher.get(argument, "Invalid argument."))
|
| 58 |
+
|
| 59 |
+
if antsx_cache_directory is None:
|
| 60 |
+
antsx_cache_directory = os.path.expanduser('~/.antspy/')
|
| 61 |
+
os.makedirs(antsx_cache_directory, exist_ok=True)
|
| 62 |
+
|
| 63 |
+
if os.path.isdir(antsx_cache_directory) == False:
|
| 64 |
+
antsx_cache_directory = tempfile.TemporaryDirectory()
|
| 65 |
+
|
| 66 |
+
valid_list = ("r16",
|
| 67 |
+
"r27",
|
| 68 |
+
"r30",
|
| 69 |
+
"r62",
|
| 70 |
+
"r64",
|
| 71 |
+
"r85",
|
| 72 |
+
"ch2",
|
| 73 |
+
"mni",
|
| 74 |
+
"surf",
|
| 75 |
+
"pcasl",
|
| 76 |
+
"show")
|
| 77 |
+
|
| 78 |
+
if file_id == "show" or file_id is None:
|
| 79 |
+
return(valid_list)
|
| 80 |
+
|
| 81 |
+
url = switch_data(file_id)
|
| 82 |
+
|
| 83 |
+
if target_file_name == None:
|
| 84 |
+
if file_id == "pcasl":
|
| 85 |
+
target_file_name = antsx_cache_directory + "pcasl.nii.gz"
|
| 86 |
+
else:
|
| 87 |
+
extension = ".jpg"
|
| 88 |
+
if file_id == "ch2" or file_id == "mni" or file_id == "surf":
|
| 89 |
+
extension = ".nii.gz"
|
| 90 |
+
if extension == ".jpg":
|
| 91 |
+
target_file_name = antsx_cache_directory + file_id + "slice" + extension
|
| 92 |
+
else:
|
| 93 |
+
target_file_name = antsx_cache_directory + file_id + extension
|
| 94 |
+
|
| 95 |
+
target_file_name_path = target_file_name
|
| 96 |
+
if target_file_name == None:
|
| 97 |
+
target_file = tempfile.NamedTemporaryFile(prefix=target_file_name, dir=antsx_cache_directory)
|
| 98 |
+
target_file_name_path = target_file.name
|
| 99 |
+
target_file.close()
|
| 100 |
+
|
| 101 |
+
if not os.path.exists(target_file_name_path):
|
| 102 |
+
r = requests.get(url)
|
| 103 |
+
with open(target_file_name_path, 'wb') as f:
|
| 104 |
+
f.write(r.content)
|
| 105 |
+
|
| 106 |
+
return(target_file_name_path)
|
| 107 |
+
|
| 108 |
+
get_ants_data = get_data
|
MindEyeV2/antspy/ants/utils/matrix_image.py
ADDED
|
@@ -0,0 +1,202 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
|
| 2 |
+
__all__ = [
|
| 3 |
+
"matrix_to_images",
|
| 4 |
+
"images_from_matrix",
|
| 5 |
+
"image_list_to_matrix",
|
| 6 |
+
"images_to_matrix",
|
| 7 |
+
"matrix_from_images",
|
| 8 |
+
"timeseries_to_matrix",
|
| 9 |
+
"matrix_to_timeseries"
|
| 10 |
+
]
|
| 11 |
+
|
| 12 |
+
import os
|
| 13 |
+
import json
|
| 14 |
+
import numpy as np
|
| 15 |
+
import warnings
|
| 16 |
+
|
| 17 |
+
import ants
|
| 18 |
+
from ants.decorators import image_method
|
| 19 |
+
|
| 20 |
+
@image_method
|
| 21 |
+
def matrix_to_timeseries(image, matrix, mask=None):
|
| 22 |
+
"""
|
| 23 |
+
converts a matrix to a ND image.
|
| 24 |
+
|
| 25 |
+
ANTsR function: `matrix2timeseries`
|
| 26 |
+
|
| 27 |
+
Arguments
|
| 28 |
+
---------
|
| 29 |
+
|
| 30 |
+
image: reference ND image
|
| 31 |
+
|
| 32 |
+
matrix: matrix to convert to image
|
| 33 |
+
|
| 34 |
+
mask: mask image defining voxels of interest
|
| 35 |
+
|
| 36 |
+
|
| 37 |
+
Returns
|
| 38 |
+
-------
|
| 39 |
+
ANTsImage
|
| 40 |
+
|
| 41 |
+
Example
|
| 42 |
+
-------
|
| 43 |
+
>>> import ants
|
| 44 |
+
>>> img = ants.make_image( (10,10,10,5 ) )
|
| 45 |
+
>>> mask = ants.ndimage_to_list( img )[0] * 0
|
| 46 |
+
>>> mask[ 4:8, 4:8, 4:8 ] = 1
|
| 47 |
+
>>> mat = ants.timeseries_to_matrix( img, mask = mask )
|
| 48 |
+
>>> img2 = ants.matrix_to_timeseries( img, mat, mask)
|
| 49 |
+
"""
|
| 50 |
+
|
| 51 |
+
if mask is None:
|
| 52 |
+
mask = temp[0] * 0 + 1
|
| 53 |
+
temp = matrix_to_images(matrix, mask)
|
| 54 |
+
newImage = ants.list_to_ndimage(image, temp)
|
| 55 |
+
ants.copy_image_info(image, newImage)
|
| 56 |
+
return newImage
|
| 57 |
+
|
| 58 |
+
|
| 59 |
+
def matrix_to_images(data_matrix, mask):
|
| 60 |
+
"""
|
| 61 |
+
Unmasks rows of a matrix and writes as images
|
| 62 |
+
|
| 63 |
+
ANTsR function: `matrixToImages`
|
| 64 |
+
|
| 65 |
+
Arguments
|
| 66 |
+
---------
|
| 67 |
+
data_matrix : numpy.ndarray
|
| 68 |
+
each row corresponds to an image
|
| 69 |
+
array should have number of columns equal to non-zero voxels in the mask
|
| 70 |
+
|
| 71 |
+
mask : ANTsImage
|
| 72 |
+
image containing a binary mask. Rows of the matrix are
|
| 73 |
+
unmasked and written as images. The mask defines the output image space
|
| 74 |
+
|
| 75 |
+
Returns
|
| 76 |
+
-------
|
| 77 |
+
list of ANTsImage types
|
| 78 |
+
|
| 79 |
+
Example
|
| 80 |
+
-------
|
| 81 |
+
>>> import ants
|
| 82 |
+
>>> img = ants.image_read(ants.get_ants_data('r16'))
|
| 83 |
+
>>> msk = ants.get_mask( img )
|
| 84 |
+
>>> img2 = ants.image_read(ants.get_ants_data('r16'))
|
| 85 |
+
>>> img3 = ants.image_read(ants.get_ants_data('r16'))
|
| 86 |
+
>>> mat = ants.image_list_to_matrix([img,img2,img3], msk )
|
| 87 |
+
>>> ilist = ants.matrix_to_images( mat, msk )
|
| 88 |
+
"""
|
| 89 |
+
|
| 90 |
+
if data_matrix.ndim > 2:
|
| 91 |
+
data_matrix = data_matrix.reshape(data_matrix.shape[0], -1)
|
| 92 |
+
|
| 93 |
+
numimages = len(data_matrix)
|
| 94 |
+
numVoxelsInMatrix = data_matrix.shape[1]
|
| 95 |
+
numVoxelsInMask = (mask >= 0.5).sum()
|
| 96 |
+
if numVoxelsInMask != numVoxelsInMatrix:
|
| 97 |
+
raise ValueError(
|
| 98 |
+
"Num masked voxels %i must match data matrix %i"
|
| 99 |
+
% (numVoxelsInMask, numVoxelsInMatrix)
|
| 100 |
+
)
|
| 101 |
+
|
| 102 |
+
imagelist = []
|
| 103 |
+
for i in range(numimages):
|
| 104 |
+
img = mask.clone()
|
| 105 |
+
img[mask >= 0.5] = data_matrix[i, :]
|
| 106 |
+
imagelist.append(img)
|
| 107 |
+
return imagelist
|
| 108 |
+
|
| 109 |
+
|
| 110 |
+
images_from_matrix = matrix_to_images
|
| 111 |
+
|
| 112 |
+
|
| 113 |
+
def images_to_matrix(image_list, mask=None, sigma=None, epsilon=0.5):
|
| 114 |
+
"""
|
| 115 |
+
Read images into rows of a matrix, given a mask - much faster for
|
| 116 |
+
large datasets as it is based on C++ implementations.
|
| 117 |
+
|
| 118 |
+
ANTsR function: `imagesToMatrix`
|
| 119 |
+
|
| 120 |
+
Arguments
|
| 121 |
+
---------
|
| 122 |
+
image_list : list of ANTsImage types
|
| 123 |
+
images to convert to ndarray
|
| 124 |
+
|
| 125 |
+
mask : ANTsImage (optional)
|
| 126 |
+
Mask image, voxels in the mask (>= epsilon) are placed in the matrix. If None,
|
| 127 |
+
the first image in image_list is thresholded at its mean value to create a mask.
|
| 128 |
+
|
| 129 |
+
sigma : scaler (optional)
|
| 130 |
+
smoothing factor
|
| 131 |
+
|
| 132 |
+
epsilon : scalar
|
| 133 |
+
threshold for mask, values >= epsilon are included in the mask.
|
| 134 |
+
|
| 135 |
+
Returns
|
| 136 |
+
-------
|
| 137 |
+
ndarray
|
| 138 |
+
array with a row for each image
|
| 139 |
+
shape = (N_IMAGES, N_VOXELS)
|
| 140 |
+
|
| 141 |
+
Example
|
| 142 |
+
-------
|
| 143 |
+
>>> import ants
|
| 144 |
+
>>> img = ants.image_read(ants.get_ants_data('r16'))
|
| 145 |
+
>>> img2 = ants.image_read(ants.get_ants_data('r16'))
|
| 146 |
+
>>> img3 = ants.image_read(ants.get_ants_data('r16'))
|
| 147 |
+
>>> mat = ants.image_list_to_matrix([img,img2,img3])
|
| 148 |
+
"""
|
| 149 |
+
if mask is None:
|
| 150 |
+
mask = ants.get_mask(image_list[0])
|
| 151 |
+
|
| 152 |
+
num_images = len(image_list)
|
| 153 |
+
mask_thresh = mask.clone() >= epsilon
|
| 154 |
+
mask_arr = mask.numpy() >= epsilon
|
| 155 |
+
num_voxels = np.sum(mask_arr)
|
| 156 |
+
|
| 157 |
+
data_matrix = np.empty((num_images, num_voxels))
|
| 158 |
+
do_smooth = sigma is not None
|
| 159 |
+
for i, img in enumerate(image_list):
|
| 160 |
+
if do_smooth:
|
| 161 |
+
img = ants.smooth_image(img, sigma, sigma_in_physical_coordinates=True)
|
| 162 |
+
if np.sum(np.array(img.shape) - np.array(mask_thresh.shape)) != 0:
|
| 163 |
+
img = ants.resample_image_to_target(img, mask_thresh, 2)
|
| 164 |
+
data_matrix[i, :] = img[mask_thresh]
|
| 165 |
+
return data_matrix
|
| 166 |
+
|
| 167 |
+
|
| 168 |
+
image_list_to_matrix = images_to_matrix
|
| 169 |
+
matrix_from_images = images_to_matrix
|
| 170 |
+
|
| 171 |
+
@image_method
|
| 172 |
+
def timeseries_to_matrix(image, mask=None):
|
| 173 |
+
"""
|
| 174 |
+
Convert a timeseries image into a matrix.
|
| 175 |
+
|
| 176 |
+
ANTsR function: `timeseries2matrix`
|
| 177 |
+
|
| 178 |
+
Arguments
|
| 179 |
+
---------
|
| 180 |
+
image : image whose slices we convert to a matrix. E.g. a 3D image of size
|
| 181 |
+
x by y by z will convert to a z by x*y sized matrix
|
| 182 |
+
|
| 183 |
+
mask : ANTsImage (optional)
|
| 184 |
+
image containing binary mask. voxels in the mask are placed in the matrix
|
| 185 |
+
|
| 186 |
+
Returns
|
| 187 |
+
-------
|
| 188 |
+
ndarray
|
| 189 |
+
array with a row for each image
|
| 190 |
+
shape = (N_IMAGES, N_VOXELS)
|
| 191 |
+
|
| 192 |
+
Example
|
| 193 |
+
-------
|
| 194 |
+
>>> import ants
|
| 195 |
+
>>> img = ants.make_image( (10,10,10,5 ) )
|
| 196 |
+
>>> mat = ants.timeseries_to_matrix( img )
|
| 197 |
+
"""
|
| 198 |
+
temp = ants.ndimage_to_list(image)
|
| 199 |
+
if mask is None:
|
| 200 |
+
mask = temp[0] * 0 + 1
|
| 201 |
+
return image_list_to_matrix(temp, mask)
|
| 202 |
+
|
MindEyeV2/antspy/ants/utils/mni2tal.py
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
|
| 2 |
+
__all__ = ['mni2tal']
|
| 3 |
+
|
| 4 |
+
def mni2tal(xin):
|
| 5 |
+
"""
|
| 6 |
+
mni2tal for converting from ch2/mni space to tal - very approximate.
|
| 7 |
+
|
| 8 |
+
This is a standard approach but it's not very accurate.
|
| 9 |
+
|
| 10 |
+
ANTsR function: `mni2tal`
|
| 11 |
+
|
| 12 |
+
Arguments
|
| 13 |
+
---------
|
| 14 |
+
xin : tuple
|
| 15 |
+
point in mni152 space.
|
| 16 |
+
|
| 17 |
+
Returns
|
| 18 |
+
-------
|
| 19 |
+
tuple
|
| 20 |
+
|
| 21 |
+
Example
|
| 22 |
+
-------
|
| 23 |
+
>>> import ants
|
| 24 |
+
>>> ants.mni2tal( (10,12,14) )
|
| 25 |
+
|
| 26 |
+
References
|
| 27 |
+
----------
|
| 28 |
+
http://bioimagesuite.yale.edu/mni2tal/501_95733_More\\%20Accurate\\%20Talairach\\%20Coordinates\\%20SLIDES.pdf
|
| 29 |
+
http://imaging.mrc-cbu.cam.ac.uk/imaging/MniTalairach
|
| 30 |
+
"""
|
| 31 |
+
if (not isinstance(xin, (tuple,list))) or (len(xin) != 3):
|
| 32 |
+
raise ValueError('xin must be tuple/list with 3 coordinates')
|
| 33 |
+
|
| 34 |
+
x = list(xin)
|
| 35 |
+
# The input image is in RAS coordinates but we use ITK which returns LPS
|
| 36 |
+
# coordinates. So we need to flip the coordinates such that L => R and P => A to
|
| 37 |
+
# get RAS (MNI) coordinates
|
| 38 |
+
x[0] = x[0] * (-1) # flip X
|
| 39 |
+
x[1] = x[1] * (-1) # flip Y
|
| 40 |
+
|
| 41 |
+
xout = x
|
| 42 |
+
|
| 43 |
+
if (x[2] >= 0):
|
| 44 |
+
xout[0] = x[0] * 0.99
|
| 45 |
+
xout[1] = x[1] * 0.9688 + 0.046 * x[2]
|
| 46 |
+
xout[2] = x[1] * (-0.0485) + 0.9189 * x[2]
|
| 47 |
+
|
| 48 |
+
if (x[2] < 0):
|
| 49 |
+
xout[0] = x[0] * 0.99
|
| 50 |
+
xout[1] = x[1] * 0.9688 + 0.042 * x[2]
|
| 51 |
+
xout[2] = x[1] * (-0.0485) + 0.839 * x[2]
|
| 52 |
+
|
| 53 |
+
return(xout)
|
| 54 |
+
|
| 55 |
+
|
| 56 |
+
|
| 57 |
+
|
| 58 |
+
|
| 59 |
+
|
MindEyeV2/antspy/ants/utils/ndimage_to_list.py
ADDED
|
@@ -0,0 +1,111 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
__all__ = ['ndimage_to_list',
|
| 2 |
+
'list_to_ndimage']
|
| 3 |
+
|
| 4 |
+
|
| 5 |
+
import numpy as np
|
| 6 |
+
|
| 7 |
+
import ants
|
| 8 |
+
from ants.decorators import image_method
|
| 9 |
+
|
| 10 |
+
@image_method
|
| 11 |
+
def list_to_ndimage( image, image_list ):
|
| 12 |
+
"""
|
| 13 |
+
Merge list of multiple scalar ANTsImage types of dimension into one
|
| 14 |
+
ANTsImage of dimension plus one
|
| 15 |
+
|
| 16 |
+
ANTsR function: `mergeListToNDImage`
|
| 17 |
+
|
| 18 |
+
Arguments
|
| 19 |
+
---------
|
| 20 |
+
image : target image space
|
| 21 |
+
image_list : list/tuple of ANTsImage types
|
| 22 |
+
scalar images to merge into target image space
|
| 23 |
+
|
| 24 |
+
Returns
|
| 25 |
+
-------
|
| 26 |
+
ANTsImage
|
| 27 |
+
|
| 28 |
+
Example
|
| 29 |
+
-------
|
| 30 |
+
>>> import ants
|
| 31 |
+
>>> image = ants.image_read(ants.get_ants_data('r16'))
|
| 32 |
+
>>> image2 = ants.image_read(ants.get_ants_data('r16'))
|
| 33 |
+
>>> imageTar = ants.make_image( ( *image2.shape, 2 ) )
|
| 34 |
+
>>> image3 = ants.list_to_ndimage( imageTar, [image,image2])
|
| 35 |
+
>>> image3.dimension == 3
|
| 36 |
+
"""
|
| 37 |
+
inpixeltype = image_list[0].pixeltype
|
| 38 |
+
dimension = image_list[0].dimension
|
| 39 |
+
components = len(image_list)
|
| 40 |
+
|
| 41 |
+
for imageL in image_list:
|
| 42 |
+
if not ants.is_image(imageL):
|
| 43 |
+
raise ValueError('list may only contain ANTsImage objects')
|
| 44 |
+
if image.pixeltype != inpixeltype:
|
| 45 |
+
raise ValueError('all images must have the same pixeltype')
|
| 46 |
+
|
| 47 |
+
dimensionout = ( *image_list[0].shape, len( image_list ) )
|
| 48 |
+
newImage = ants.make_image(
|
| 49 |
+
dimensionout,
|
| 50 |
+
spacing = ants.get_spacing( image ),
|
| 51 |
+
origin = ants.get_origin( image ),
|
| 52 |
+
direction = ants.get_direction( image ),
|
| 53 |
+
pixeltype = inpixeltype
|
| 54 |
+
)
|
| 55 |
+
# FIXME - should implement paste image filter from ITK
|
| 56 |
+
for x in range( len( image_list ) ):
|
| 57 |
+
if dimension == 2:
|
| 58 |
+
newImage[:,:,x] = image_list[x][:,:]
|
| 59 |
+
if dimension == 3:
|
| 60 |
+
newImage[:,:,:,x] = image_list[x][:,:,:]
|
| 61 |
+
return newImage
|
| 62 |
+
|
| 63 |
+
|
| 64 |
+
@image_method
|
| 65 |
+
def ndimage_to_list(image):
|
| 66 |
+
"""
|
| 67 |
+
Split a n dimensional ANTsImage into a list
|
| 68 |
+
of n-1 dimensional ANTsImages
|
| 69 |
+
|
| 70 |
+
Arguments
|
| 71 |
+
---------
|
| 72 |
+
image : ANTsImage
|
| 73 |
+
n-dimensional image to split
|
| 74 |
+
|
| 75 |
+
Returns
|
| 76 |
+
-------
|
| 77 |
+
list of ANTsImage types
|
| 78 |
+
|
| 79 |
+
Example
|
| 80 |
+
-------
|
| 81 |
+
>>> import ants
|
| 82 |
+
>>> image = ants.image_read(ants.get_ants_data('r16'))
|
| 83 |
+
>>> image2 = ants.image_read(ants.get_ants_data('r16'))
|
| 84 |
+
>>> imageTar = ants.make_image( ( *image2.shape, 2 ) )
|
| 85 |
+
>>> image3 = ants.list_to_ndimage( imageTar, [image,image2])
|
| 86 |
+
>>> image3.dimension == 3
|
| 87 |
+
>>> images_unmerged = ants.ndimage_to_list( image3 )
|
| 88 |
+
>>> len(images_unmerged) == 2
|
| 89 |
+
>>> images_unmerged[0].dimension == 2
|
| 90 |
+
"""
|
| 91 |
+
inpixeltype = image.pixeltype
|
| 92 |
+
dimension = image.dimension
|
| 93 |
+
components = 1
|
| 94 |
+
imageShape = image.shape
|
| 95 |
+
nSections = imageShape[ dimension - 1 ]
|
| 96 |
+
subdimension = dimension - 1
|
| 97 |
+
suborigin = ants.get_origin( image )[0:subdimension]
|
| 98 |
+
subspacing = ants.get_spacing( image )[0:subdimension]
|
| 99 |
+
subdirection = np.eye( subdimension )
|
| 100 |
+
for i in range( subdimension ):
|
| 101 |
+
subdirection[i,:] = ants.get_direction( image )[i,0:subdimension]
|
| 102 |
+
subdim = image.shape[ 0:subdimension ]
|
| 103 |
+
imagelist = []
|
| 104 |
+
for i in range( nSections ):
|
| 105 |
+
img = ants.slice_image( image, axis = subdimension, idx = i )
|
| 106 |
+
ants.set_spacing( img, subspacing )
|
| 107 |
+
ants.set_origin( img, suborigin )
|
| 108 |
+
ants.set_direction( img, subdirection )
|
| 109 |
+
imagelist.append( img )
|
| 110 |
+
|
| 111 |
+
return imagelist
|
MindEyeV2/antspy/ants/utils/nifti_to_ants.py
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
__all__ = ["nifti_to_ants"]
|
| 2 |
+
|
| 3 |
+
import numpy as np
|
| 4 |
+
import ants
|
| 5 |
+
|
| 6 |
+
def nifti_to_ants( nib_image ):
|
| 7 |
+
"""
|
| 8 |
+
Converts a given Nifti image into an ANTsPy image
|
| 9 |
+
|
| 10 |
+
Parameters
|
| 11 |
+
----------
|
| 12 |
+
img: NiftiImage
|
| 13 |
+
|
| 14 |
+
Returns
|
| 15 |
+
-------
|
| 16 |
+
ants_image: ANTsImage
|
| 17 |
+
"""
|
| 18 |
+
ndim = nib_image.ndim
|
| 19 |
+
|
| 20 |
+
if ndim < 3:
|
| 21 |
+
print("Dimensionality is less than 3.")
|
| 22 |
+
return None
|
| 23 |
+
|
| 24 |
+
q_form = nib_image.get_qform()
|
| 25 |
+
spacing = nib_image.header["pixdim"][1 : ndim + 1]
|
| 26 |
+
|
| 27 |
+
origin = np.zeros((ndim))
|
| 28 |
+
origin[:3] = q_form[:3, 3]
|
| 29 |
+
|
| 30 |
+
direction = np.diag(np.ones(ndim))
|
| 31 |
+
direction[:3, :3] = q_form[:3, :3] / spacing[:3]
|
| 32 |
+
|
| 33 |
+
ants_img = ants.from_numpy(
|
| 34 |
+
data = nib_image.get_data().astype( np.float ),
|
| 35 |
+
origin = origin.tolist(),
|
| 36 |
+
spacing = spacing.tolist(),
|
| 37 |
+
direction = direction )
|
| 38 |
+
|
| 39 |
+
return ants_img
|
MindEyeV2/antspy/ants/utils/scalar_rgb_vector.py
ADDED
|
@@ -0,0 +1,92 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
|
| 2 |
+
|
| 3 |
+
__all__ = ['rgb_to_vector',
|
| 4 |
+
'vector_to_rgb',
|
| 5 |
+
'scalar_to_rgb']
|
| 6 |
+
|
| 7 |
+
import os
|
| 8 |
+
from tempfile import mktemp
|
| 9 |
+
|
| 10 |
+
import numpy as np
|
| 11 |
+
|
| 12 |
+
import ants
|
| 13 |
+
from ants.internal import get_lib_fn, process_arguments
|
| 14 |
+
from ants.decorators import image_method
|
| 15 |
+
|
| 16 |
+
def scalar_to_rgb(image, mask=None, filename=None, cmap='red', custom_colormap_file=None,
|
| 17 |
+
min_input=None, max_input=None, min_rgb_output=None, max_rgb_output=None,
|
| 18 |
+
vtk_lookup_table=None):
|
| 19 |
+
"""
|
| 20 |
+
Usage: ConvertScalarImageToRGB imageDimension inputImage outputImage mask colormap
|
| 21 |
+
[customColormapFile] [minimumInput] [maximumInput] [minimumRGBOutput=0]
|
| 22 |
+
[maximumRGBOutput=255] <vtkLookupTable>
|
| 23 |
+
Possible colormaps: grey, red, green, blue, copper, jet, hsv, spring, summer, autumn, winter, hot, cool, overunder, custom
|
| 24 |
+
|
| 25 |
+
Example
|
| 26 |
+
-------
|
| 27 |
+
>>> import ants
|
| 28 |
+
>>> img = ants.image_read(ants.get_data('r16'))
|
| 29 |
+
>>> img_color = ants.scalar_to_rgb(img, cmap='jet')
|
| 30 |
+
"""
|
| 31 |
+
raise Exception('This function is currently not supported.')
|
| 32 |
+
|
| 33 |
+
@image_method
|
| 34 |
+
def rgb_to_vector(image):
|
| 35 |
+
"""
|
| 36 |
+
Convert an RGB ANTsImage to a Vector ANTsImage
|
| 37 |
+
|
| 38 |
+
Arguments
|
| 39 |
+
---------
|
| 40 |
+
image : ANTsImage
|
| 41 |
+
RGB image to be converted
|
| 42 |
+
|
| 43 |
+
Returns
|
| 44 |
+
-------
|
| 45 |
+
ANTsImage
|
| 46 |
+
|
| 47 |
+
Example
|
| 48 |
+
-------
|
| 49 |
+
>>> import ants
|
| 50 |
+
>>> mni = ants.image_read(ants.get_data('mni'))
|
| 51 |
+
>>> mni_rgb = ants.scalar_to_rgb(mni)
|
| 52 |
+
>>> mni_vector = mni.rgb_to_vector()
|
| 53 |
+
>>> mni_rgb2 = mni.vector_to_rgb()
|
| 54 |
+
"""
|
| 55 |
+
if image.pixeltype != 'unsigned char':
|
| 56 |
+
image = image.clone('unsigned char')
|
| 57 |
+
idim = image.dimension
|
| 58 |
+
libfn = get_lib_fn('RgbToVector%i' % idim)
|
| 59 |
+
new_ptr = libfn(image.pointer)
|
| 60 |
+
new_img = ants.from_pointer(new_ptr)
|
| 61 |
+
return new_img
|
| 62 |
+
|
| 63 |
+
@image_method
|
| 64 |
+
def vector_to_rgb(image):
|
| 65 |
+
"""
|
| 66 |
+
Convert an Vector ANTsImage to a RGB ANTsImage
|
| 67 |
+
|
| 68 |
+
Arguments
|
| 69 |
+
---------
|
| 70 |
+
image : ANTsImage
|
| 71 |
+
RGB image to be converted
|
| 72 |
+
|
| 73 |
+
Returns
|
| 74 |
+
-------
|
| 75 |
+
ANTsImage
|
| 76 |
+
|
| 77 |
+
Example
|
| 78 |
+
-------
|
| 79 |
+
>>> import ants
|
| 80 |
+
>>> img = ants.image_read(ants.get_data('r16'), pixeltype='unsigned char')
|
| 81 |
+
>>> img_rgb = ants.scalar_to_rgb(img.clone())
|
| 82 |
+
>>> img_vec = img_rgb.rgb_to_vector()
|
| 83 |
+
>>> img_rgb2 = img_vec.vector_to_rgb()
|
| 84 |
+
"""
|
| 85 |
+
if image.pixeltype != 'unsigned char':
|
| 86 |
+
image = image.clone('unsigned char')
|
| 87 |
+
idim = image.dimension
|
| 88 |
+
libfn = get_lib_fn('VectorToRgb%i' % idim)
|
| 89 |
+
new_ptr = libfn(image.pointer)
|
| 90 |
+
new_img = ants.from_pointer(new_ptr)
|
| 91 |
+
return new_img
|
| 92 |
+
|
MindEyeV2/antspy/ants/utils/sitk_to_ants.py
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import numpy as np
|
| 2 |
+
import ants
|
| 3 |
+
|
| 4 |
+
|
| 5 |
+
def from_sitk(sitk_image: "SimpleITK.Image") -> ants.ANTsImage:
|
| 6 |
+
"""
|
| 7 |
+
Converts a given SimpleITK image into an ANTsPy image
|
| 8 |
+
|
| 9 |
+
Parameters
|
| 10 |
+
----------
|
| 11 |
+
img: SimpleITK.Image
|
| 12 |
+
|
| 13 |
+
Returns
|
| 14 |
+
-------
|
| 15 |
+
ants_image: ANTsImage
|
| 16 |
+
"""
|
| 17 |
+
import SimpleITK as sitk
|
| 18 |
+
|
| 19 |
+
ndim = sitk_image.GetDimension()
|
| 20 |
+
|
| 21 |
+
if ndim < 3:
|
| 22 |
+
print("Dimensionality is less than 3.")
|
| 23 |
+
return None
|
| 24 |
+
|
| 25 |
+
direction = np.asarray(sitk_image.GetDirection()).reshape((3, 3))
|
| 26 |
+
spacing = list(sitk_image.GetSpacing())
|
| 27 |
+
origin = list(sitk_image.GetOrigin())
|
| 28 |
+
|
| 29 |
+
data = sitk.GetArrayViewFromImage(sitk_image)
|
| 30 |
+
|
| 31 |
+
ants_img: ants.ANTsImage = ants.from_numpy(
|
| 32 |
+
data=data.ravel(order="F").reshape(data.shape[::-1]),
|
| 33 |
+
origin=origin,
|
| 34 |
+
spacing=spacing,
|
| 35 |
+
direction=direction,
|
| 36 |
+
)
|
| 37 |
+
|
| 38 |
+
return ants_img
|
| 39 |
+
|
| 40 |
+
|
| 41 |
+
def to_sitk(ants_image: ants.ANTsImage) -> "SimpleITK.Image":
|
| 42 |
+
"""
|
| 43 |
+
Converts a given ANTsPy image into an SimpleITK image
|
| 44 |
+
|
| 45 |
+
Parameters
|
| 46 |
+
----------
|
| 47 |
+
ants_image: ANTsImage
|
| 48 |
+
|
| 49 |
+
Returns
|
| 50 |
+
-------
|
| 51 |
+
img: SimpleITK.Image
|
| 52 |
+
"""
|
| 53 |
+
|
| 54 |
+
import SimpleITK as sitk
|
| 55 |
+
|
| 56 |
+
data = ants_image.view()
|
| 57 |
+
shape = ants_image.shape
|
| 58 |
+
|
| 59 |
+
sitk_img = sitk.GetImageFromArray(data.ravel(order="F").reshape(shape[::-1]))
|
| 60 |
+
sitk_img.SetOrigin(ants_image.origin)
|
| 61 |
+
sitk_img.SetSpacing(ants_image.spacing)
|
| 62 |
+
sitk_img.SetDirection(ants_image.direction.flatten())
|
| 63 |
+
return sitk_img
|
MindEyeV2/antspy/docs/Makefile
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Minimal makefile for Sphinx documentation
|
| 2 |
+
#
|
| 3 |
+
|
| 4 |
+
# You can set these variables from the command line.
|
| 5 |
+
SPHINXOPTS =
|
| 6 |
+
SPHINXBUILD = sphinx-build
|
| 7 |
+
SPHINXPROJ = ANTsPy
|
| 8 |
+
SOURCEDIR = source
|
| 9 |
+
BUILDDIR = build
|
| 10 |
+
|
| 11 |
+
# Put it first so that "make" without argument is like "make help".
|
| 12 |
+
help:
|
| 13 |
+
@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
|
| 14 |
+
|
| 15 |
+
docset: html
|
| 16 |
+
doc2dash --name $(SPHINXPROJ) --icon $(SOURCEDIR)/_static/img/antspy-logo-icon.png --enable-js --online-redirect-url http://ncullen.github.io/docs/ --force $(BUILDDIR)/html/
|
| 17 |
+
|
| 18 |
+
# Manually fix because Zeal doesn't deal well with `icon.png`-only at 2x resolution.
|
| 19 |
+
cp $(SPHINXPROJ).docset/icon.png $(SPHINXPROJ).docset/icon@2x.png
|
| 20 |
+
convert $(SPHINXPROJ).docset/icon@2x.png -resize 16x16 $(SPHINXPROJ).docset/icon.png
|
| 21 |
+
|
| 22 |
+
.PHONY: help Makefile docset
|
| 23 |
+
|
| 24 |
+
# Catch-all target: route all unknown targets to Sphinx using the new
|
| 25 |
+
# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
|
| 26 |
+
%: Makefile
|
| 27 |
+
@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
|
MindEyeV2/antspy/docs/make.bat
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
@ECHO OFF
|
| 2 |
+
|
| 3 |
+
pushd %~dp0
|
| 4 |
+
|
| 5 |
+
REM Command file for Sphinx documentation
|
| 6 |
+
|
| 7 |
+
if "%SPHINXBUILD%" == "" (
|
| 8 |
+
set SPHINXBUILD=sphinx-build
|
| 9 |
+
)
|
| 10 |
+
set SOURCEDIR=source
|
| 11 |
+
set BUILDDIR=build
|
| 12 |
+
set SPHINXPROJ=ANTsPy
|
| 13 |
+
|
| 14 |
+
if "%1" == "" goto help
|
| 15 |
+
|
| 16 |
+
%SPHINXBUILD% >NUL 2>NUL
|
| 17 |
+
if errorlevel 9009 (
|
| 18 |
+
echo.
|
| 19 |
+
echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
|
| 20 |
+
echo.installed, then set the SPHINXBUILD environment variable to point
|
| 21 |
+
echo.to the full path of the 'sphinx-build' executable. Alternatively you
|
| 22 |
+
echo.may add the Sphinx directory to PATH.
|
| 23 |
+
echo.
|
| 24 |
+
echo.If you don't have Sphinx installed, grab it from
|
| 25 |
+
echo.http://sphinx-doc.org/
|
| 26 |
+
exit /b 1
|
| 27 |
+
)
|
| 28 |
+
|
| 29 |
+
%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS%
|
| 30 |
+
goto end
|
| 31 |
+
|
| 32 |
+
:help
|
| 33 |
+
%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS%
|
| 34 |
+
|
| 35 |
+
:end
|
| 36 |
+
popd
|
MindEyeV2/antspy/docs/other/ANTsPy Tutorial.ipynb
ADDED
|
The diff for this file is too large to render.
See raw diff
|
|
|
MindEyeV2/antspy/docs/other/ANTsR_Comparison.md
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
|
| 2 |
+
```R
|
| 3 |
+
library(ANTsR)
|
| 4 |
+
img <- antsImageRead( getANTsRData("r16") , 2 )
|
| 5 |
+
img <- resampleImage( img, c(64,64), 1, 0 )
|
| 6 |
+
mask <- getMask(img)
|
| 7 |
+
segs1 <- atropos( a = img, m = '[0.2,1x1]', c = '[2,0]', i = 'kmeans[3]', x = mask )
|
| 8 |
+
```
|
| 9 |
+
|
| 10 |
+
```python
|
| 11 |
+
import ants
|
| 12 |
+
img = ants.image_read(ants.get_ants_data('r16'))
|
| 13 |
+
img = ants.resample_image(img, (64,64), 1, 0)
|
| 14 |
+
mask = ants.get_mask(img)
|
| 15 |
+
ants.atropos(a = img, m = '[0.2,1x1]', c = '[2,0]', i = 'kmeans[3]', x = mask )
|
| 16 |
+
```
|
MindEyeV2/antspy/docs/other/All_Functions.md
ADDED
|
@@ -0,0 +1,180 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
|
| 2 |
+
ANTsR_Name ANTsPy_Name Description
|
| 3 |
+
abpN4 | |
|
| 4 |
+
affineInitializer | | a multi-start optimizer for affine registration
|
| 5 |
+
mean | |,antsImage-method arith.antsImage
|
| 6 |
+
antsImageClone | |
|
| 7 |
+
antsMotionCalculation | | Correct 4D time-series data for motion.
|
| 8 |
+
antsRegistration | |
|
| 9 |
+
antsImagePhysicalSpaceConsistency | | Check for physical space consistency
|
| 10 |
+
antsImageRead | |
|
| 11 |
+
antsrimpute | |
|
| 12 |
+
antsImageHeaderInfo | | Read file info from image header
|
| 13 |
+
antsMotionCorr | | Motion Correction
|
| 14 |
+
antsImageGetSet | | antsImageGetSet
|
| 15 |
+
antsImageMutualInformation | | mutual information between two images
|
| 16 |
+
antsImageWrite | |
|
| 17 |
+
antsSetPixels | | Set a pixel value at an index
|
| 18 |
+
aslDenoiseR | |
|
| 19 |
+
aslPerfusion | |
|
| 20 |
+
antsTransformIndexToPhysicalPoint | | Get Spatial Point from Index
|
| 21 |
+
as.antsImage | |
|
| 22 |
+
antsTransformPhysicalPointToIndex | | Get Index from Spatial Point
|
| 23 |
+
antsSpatialICAfMRI | |
|
| 24 |
+
aslOutlierRejection | | Pair-wise subtraction based outlier rejection.
|
| 25 |
+
atropos | | FMM Segmentation
|
| 26 |
+
as.antsMatrix | | as.antsMatrix
|
| 27 |
+
combineNuisancePredictors | | Combine and reduce dimensionality of nuisance predictors.
|
| 28 |
+
clusterTimeSeries | | Split time series image into k distinct images
|
| 29 |
+
bigLMStats | |
|
| 30 |
+
bayesianlm | | Simple bayesian regression function.
|
| 31 |
+
computeDVARS | | computeDVARS
|
| 32 |
+
compcor | |
|
| 33 |
+
basicInPaint | | Inpaints missing imaging data from boundary data
|
| 34 |
+
bayesianCBF | | Uses probabilistic segmentation to constrain pcasl-based cbf computation.
|
| 35 |
+
blockStimulus | |
|
| 36 |
+
bold_correlation_matrix | | bold_correlation_matrix
|
| 37 |
+
crossvalidatedR2 | | Cross-Validated R^2 value
|
| 38 |
+
createJacobianDeterminantImage | | createJacobianDeterminantImage
|
| 39 |
+
DesikanKillianyTourville | | DesikanKillianyTourville
|
| 40 |
+
eigSeg | |
|
| 41 |
+
cropImage | | crop a sub-image via a mask
|
| 42 |
+
cropIndices | | crop a sub-image by image indices
|
| 43 |
+
convolveImage | | convolve one image with another
|
| 44 |
+
corw | |
|
| 45 |
+
cvEigenanatomy | |
|
| 46 |
+
decropImage | | decrop a sub-image back into the full image
|
| 47 |
+
getMask | |
|
| 48 |
+
getfMRInuisanceVariables | |
|
| 49 |
+
getNeighborhoodInMask | | Get neighborhoods for voxels within mask
|
| 50 |
+
getPixels | | Get Pixels
|
| 51 |
+
icawhiten | |
|
| 52 |
+
image2ClusterImages | |
|
| 53 |
+
imageFileNames2ImageList | |
|
| 54 |
+
imageListToMatrix | |
|
| 55 |
+
iMath | | iMath
|
| 56 |
+
iMathOps | | iMathOps
|
| 57 |
+
maskImage | |
|
| 58 |
+
matrix2timeseries | | Simple matrix2timeseries function.
|
| 59 |
+
getAverageOfTimeSeries | |
|
| 60 |
+
getCentroids | |
|
| 61 |
+
hemodynamicRF | | Linear Model for FMRI Data
|
| 62 |
+
iBind | | iBind
|
| 63 |
+
initializeEigenanatomy | |
|
| 64 |
+
interleaveMatrixWithItself | |
|
| 65 |
+
labelStats | | labelStats
|
| 66 |
+
lappend | |
|
| 67 |
+
makeGraph | |
|
| 68 |
+
makeImage | |
|
| 69 |
+
partialVolumeCorrection | |
|
| 70 |
+
projectImageAlongAxis | |
|
| 71 |
+
perfusionregression | |
|
| 72 |
+
quantifyCBF | |
|
| 73 |
+
sparseDecom | |
|
| 74 |
+
sparseDecom2 | |
|
| 75 |
+
usePkg | | Use any package. If package is not installed, this will install from CRAN.
|
| 76 |
+
vwnrfs | | voxelwise neighborhood random forest segmentation and prediction
|
| 77 |
+
antsApplyTransforms | |
|
| 78 |
+
n4BiasFieldCorrection | | Bias Field Correction
|
| 79 |
+
reorientImage | | reorient image by its principal axis
|
| 80 |
+
renderSurfaceFunction | |
|
| 81 |
+
rfSegmentationPredict | |
|
| 82 |
+
subjectDataToGroupDataFrame | |
|
| 83 |
+
timeseries2matrix | |
|
| 84 |
+
rsfDenoise | |
|
| 85 |
+
taskFMRI | |
|
| 86 |
+
timeseriesN3 | | Run N3 on slices of timeseries.
|
| 87 |
+
filterfMRIforNetworkAnalysis | |
|
| 88 |
+
getNeighborhoodAtVoxel | | Get a hypercube neighborhood at a voxel
|
| 89 |
+
imagesToMatrix | |
|
| 90 |
+
getMultivariateTemplateCoordinates | |
|
| 91 |
+
lowrankRowMatrix | | Produces a low rank version of the input matrix
|
| 92 |
+
imageMath | | R access to the ANTs program ImageMath
|
| 93 |
+
labelGeometryMeasures | | labelGeometryMeasures
|
| 94 |
+
labelImageCentroids | | labelImageCentroids
|
| 95 |
+
frequencyFilterfMRI | |
|
| 96 |
+
antsBOLDNetworkAnalysis | | a basic framework for network analysis that produces graph metrics
|
| 97 |
+
antsCopyImageInfo | | Copy header info
|
| 98 |
+
getANTsRData | | getANTsRData
|
| 99 |
+
plotBasicNetwork | |
|
| 100 |
+
getASLNoisePredictors | | Get nuisance predictors from ASL images
|
| 101 |
+
is.antsImage | | is.antsImage
|
| 102 |
+
joinEigenanatomy | |
|
| 103 |
+
invariantImageSimilarity | | similarity metrics between two images as a function of geometry
|
| 104 |
+
jointIntensityFusion | | joint intensity fusion
|
| 105 |
+
aal | | aal
|
| 106 |
+
plot.antsImage | | Plotting an image slice or multi-slice with optional color overlay.
|
| 107 |
+
abpBrainExtraction | |
|
| 108 |
+
getTemplateCoordinates | |
|
| 109 |
+
extractSlice | | extract a slice from an image
|
| 110 |
+
%>% | | Pipe an object forward
|
| 111 |
+
exemplarInpainting | | Uses example images to inpaint or approximate an existing image.
|
| 112 |
+
quantifySNPs | | Simple quantifySNPs function.
|
| 113 |
+
rapidlyInspectImageData | | Simple rapidlyInspectImageData function.
|
| 114 |
+
whiten | |
|
| 115 |
+
temporalwhiten | |
|
| 116 |
+
thresholdImage | | Threshold Image
|
| 117 |
+
jointIntensityFusion3D | | jointIntensityFusion3D
|
| 118 |
+
antsAverageImages | | Computes average of image list
|
| 119 |
+
kellyKapowski | | Compute cortical thickness using the DiReCT algorithm.
|
| 120 |
+
mrvnrfs | | multi-res voxelwise neighborhood random forest segmentation learning
|
| 121 |
+
mrvnrfs.predict | | multi-res voxelwise neighborhood random forest segmentation
|
| 122 |
+
n3BiasFieldCorrection | | Bias Field Correction
|
| 123 |
+
plotPrettyGraph | |
|
| 124 |
+
preprocessfMRI | | Preprocess BOLD fMRI image data.
|
| 125 |
+
reflectImage | | reflectImage
|
| 126 |
+
sparseDecom2boot | |
|
| 127 |
+
sparseDecomboot | |
|
| 128 |
+
regressionNetworkViz | |
|
| 129 |
+
timeserieswindow2matrix | |
|
| 130 |
+
tracts | | tracts
|
| 131 |
+
save.ANTsR | | save.ANTsR
|
| 132 |
+
segmentShapeFromImage | | convolution-based shape identification
|
| 133 |
+
kmeansSegmentation | | k means image segmentation.
|
| 134 |
+
labelClusters | |
|
| 135 |
+
matrixToImages | |
|
| 136 |
+
networkEiganat | |
|
| 137 |
+
mni2tal | |
|
| 138 |
+
pairwiseImageDistanceMatrix | |
|
| 139 |
+
regressProjections | |
|
| 140 |
+
renderImageLabels | |
|
| 141 |
+
sliceTimingCorrection | | slice timing correction for fMRI.
|
| 142 |
+
sparseRegression | |
|
| 143 |
+
smoothImage | | Smooth image
|
| 144 |
+
spatialbayesianlm | | spatially constrained bayesian regression function.
|
| 145 |
+
make3ViewPNG | |
|
| 146 |
+
resampleImage | | resampleImage
|
| 147 |
+
rfSegmentation | |
|
| 148 |
+
splitData | |
|
| 149 |
+
subgradientL1Regression | |
|
| 150 |
+
antsPreprocessfMRI | |
|
| 151 |
+
as.matrix | |
|
| 152 |
+
as.numeric | |
|
| 153 |
+
perfusion-predictors | |
|
| 154 |
+
getROIValues | |
|
| 155 |
+
phantom_population_study | |
|
| 156 |
+
fastwhiten | |
|
| 157 |
+
Motion-Correction | |
|
| 158 |
+
sccan | |
|
| 159 |
+
as.antsImage | |
|
| 160 |
+
as.array | |
|
| 161 |
+
MeasureMinMaxMean | |
|
| 162 |
+
plotANTsImage | |
|
| 163 |
+
renderNetwork | |
|
| 164 |
+
visualizeBlob | |
|
| 165 |
+
inspectImageData3D | |
|
| 166 |
+
antsImage-class | |
|
| 167 |
+
CBF | |
|
| 168 |
+
simple_roi_analysis | |
|
| 169 |
+
simple_voxel_based_analysis | |
|
| 170 |
+
KellyKapowski | |
|
| 171 |
+
Atropos | |
|
| 172 |
+
Extract | |
|
| 173 |
+
SmoothImage | |
|
| 174 |
+
N3BiasFieldCorrection | |
|
| 175 |
+
ImageMath | |
|
| 176 |
+
Comparison | |
|
| 177 |
+
ThresholdImage | |
|
| 178 |
+
antsMatrix-class | | An S4 class to hold an antsMatrix imported from ITK types
|
| 179 |
+
antsRegion-class | | An S4 class to hold a region of an antsImage
|
| 180 |
+
%>% | | Pipe an object forward
|
MindEyeV2/antspy/docs/other/_config.yml
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
theme: jekyll-theme-minimal
|
MindEyeV2/antspy/docs/requirements.txt
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
|
|
|
|
|
|
|
| 1 |
+
sphinx
|
| 2 |
+
-e git+https://github.com/snide/sphinx_rtd_theme.git#egg=sphinx_rtd_theme
|
MindEyeV2/antspy/docs/source/ants.core.rst
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
ants.core package
|
| 2 |
+
=================
|
| 3 |
+
|
| 4 |
+
Submodules
|
| 5 |
+
----------
|
| 6 |
+
|
| 7 |
+
ants.core.ants\_image module
|
| 8 |
+
----------------------------
|
| 9 |
+
|
| 10 |
+
.. automodule:: ants.core.ants_image
|
| 11 |
+
:members:
|
| 12 |
+
:undoc-members:
|
| 13 |
+
:show-inheritance:
|
| 14 |
+
|
| 15 |
+
ants.core.ants\_image\_io module
|
| 16 |
+
--------------------------------
|
| 17 |
+
|
| 18 |
+
.. automodule:: ants.core.ants_image_io
|
| 19 |
+
:members:
|
| 20 |
+
:undoc-members:
|
| 21 |
+
:show-inheritance:
|
| 22 |
+
|
| 23 |
+
ants.core.ants\_metric module
|
| 24 |
+
-----------------------------
|
| 25 |
+
|
| 26 |
+
.. automodule:: ants.core.ants_metric
|
| 27 |
+
:members:
|
| 28 |
+
:undoc-members:
|
| 29 |
+
:show-inheritance:
|
| 30 |
+
|
| 31 |
+
ants.core.ants\_metric\_io module
|
| 32 |
+
---------------------------------
|
| 33 |
+
|
| 34 |
+
.. automodule:: ants.core.ants_metric_io
|
| 35 |
+
:members:
|
| 36 |
+
:undoc-members:
|
| 37 |
+
:show-inheritance:
|
| 38 |
+
|
| 39 |
+
ants.core.ants\_transform module
|
| 40 |
+
--------------------------------
|
| 41 |
+
|
| 42 |
+
.. automodule:: ants.core.ants_transform
|
| 43 |
+
:members:
|
| 44 |
+
:undoc-members:
|
| 45 |
+
:show-inheritance:
|
| 46 |
+
|
| 47 |
+
ants.core.ants\_transform\_io module
|
| 48 |
+
------------------------------------
|
| 49 |
+
|
| 50 |
+
.. automodule:: ants.core.ants_transform_io
|
| 51 |
+
:members:
|
| 52 |
+
:undoc-members:
|
| 53 |
+
:show-inheritance:
|
| 54 |
+
|
| 55 |
+
|
| 56 |
+
Module contents
|
| 57 |
+
---------------
|
| 58 |
+
|
| 59 |
+
.. automodule:: ants.core
|
| 60 |
+
:members:
|
| 61 |
+
:undoc-members:
|
| 62 |
+
:show-inheritance:
|
MindEyeV2/antspy/docs/source/ants.learn.rst
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
ants.learn package
|
| 2 |
+
==================
|
| 3 |
+
|
| 4 |
+
Submodules
|
| 5 |
+
----------
|
| 6 |
+
|
| 7 |
+
ants.learn.decomposition module
|
| 8 |
+
-------------------------------
|
| 9 |
+
|
| 10 |
+
.. automodule:: ants.learn.decomposition
|
| 11 |
+
:members:
|
| 12 |
+
:undoc-members:
|
| 13 |
+
:show-inheritance:
|
| 14 |
+
|
| 15 |
+
|
| 16 |
+
Module contents
|
| 17 |
+
---------------
|
| 18 |
+
|
| 19 |
+
.. automodule:: ants.learn
|
| 20 |
+
:members:
|
| 21 |
+
:undoc-members:
|
| 22 |
+
:show-inheritance:
|
MindEyeV2/antspy/docs/source/ants.lib.rst
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
ants.lib package
|
| 2 |
+
================
|
| 3 |
+
|
| 4 |
+
Module contents
|
| 5 |
+
---------------
|
| 6 |
+
|
| 7 |
+
.. automodule:: ants.lib
|
| 8 |
+
:members:
|
| 9 |
+
:undoc-members:
|
| 10 |
+
:show-inheritance:
|
MindEyeV2/antspy/docs/source/ants.rst
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
ants package
|
| 2 |
+
============
|
| 3 |
+
|
| 4 |
+
Subpackages
|
| 5 |
+
-----------
|
| 6 |
+
|
| 7 |
+
.. toctree::
|
| 8 |
+
|
| 9 |
+
ants.core
|
| 10 |
+
ants.learn
|
| 11 |
+
ants.lib
|
| 12 |
+
ants.registration
|
| 13 |
+
ants.segmentation
|
| 14 |
+
ants.utils
|
| 15 |
+
ants.viz
|
| 16 |
+
|
| 17 |
+
Submodules
|
| 18 |
+
----------
|
| 19 |
+
|
| 20 |
+
ants.version module
|
| 21 |
+
-------------------
|
| 22 |
+
|
| 23 |
+
.. automodule:: ants.version
|
| 24 |
+
:members:
|
| 25 |
+
:undoc-members:
|
| 26 |
+
:show-inheritance:
|
| 27 |
+
|
| 28 |
+
|
| 29 |
+
Module contents
|
| 30 |
+
---------------
|
| 31 |
+
|
| 32 |
+
.. automodule:: ants
|
| 33 |
+
:members:
|
| 34 |
+
:undoc-members:
|
| 35 |
+
:show-inheritance:
|
MindEyeV2/antspy/docs/source/ants.segmentation.rst
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
ants.segmentation package
|
| 2 |
+
=========================
|
| 3 |
+
|
| 4 |
+
Submodules
|
| 5 |
+
----------
|
| 6 |
+
|
| 7 |
+
ants.segmentation.anti\_alias module
|
| 8 |
+
------------------------------------
|
| 9 |
+
|
| 10 |
+
.. automodule:: ants.segmentation.anti_alias
|
| 11 |
+
:members:
|
| 12 |
+
:undoc-members:
|
| 13 |
+
:show-inheritance:
|
| 14 |
+
|
| 15 |
+
ants.segmentation.atropos module
|
| 16 |
+
--------------------------------
|
| 17 |
+
|
| 18 |
+
.. automodule:: ants.segmentation.atropos
|
| 19 |
+
:members:
|
| 20 |
+
:undoc-members:
|
| 21 |
+
:show-inheritance:
|
| 22 |
+
|
| 23 |
+
ants.segmentation.joint\_label\_fusion module
|
| 24 |
+
---------------------------------------------
|
| 25 |
+
|
| 26 |
+
.. automodule:: ants.segmentation.joint_label_fusion
|
| 27 |
+
:members:
|
| 28 |
+
:undoc-members:
|
| 29 |
+
:show-inheritance:
|
| 30 |
+
|
| 31 |
+
ants.segmentation.kelly\_kapowski module
|
| 32 |
+
----------------------------------------
|
| 33 |
+
|
| 34 |
+
.. automodule:: ants.segmentation.kelly_kapowski
|
| 35 |
+
:members:
|
| 36 |
+
:undoc-members:
|
| 37 |
+
:show-inheritance:
|
| 38 |
+
|
| 39 |
+
ants.segmentation.kmeans module
|
| 40 |
+
-------------------------------
|
| 41 |
+
|
| 42 |
+
.. automodule:: ants.segmentation.kmeans
|
| 43 |
+
:members:
|
| 44 |
+
:undoc-members:
|
| 45 |
+
:show-inheritance:
|
| 46 |
+
|
| 47 |
+
ants.segmentation.label\_geometry\_measures module
|
| 48 |
+
--------------------------------------------------
|
| 49 |
+
|
| 50 |
+
.. automodule:: ants.segmentation.label_geometry_measures
|
| 51 |
+
:members:
|
| 52 |
+
:undoc-members:
|
| 53 |
+
:show-inheritance:
|
| 54 |
+
|
| 55 |
+
ants.segmentation.otsu module
|
| 56 |
+
-----------------------------
|
| 57 |
+
|
| 58 |
+
.. automodule:: ants.segmentation.otsu
|
| 59 |
+
:members:
|
| 60 |
+
:undoc-members:
|
| 61 |
+
:show-inheritance:
|
| 62 |
+
|
| 63 |
+
ants.segmentation.prior\_based\_segmentation module
|
| 64 |
+
---------------------------------------------------
|
| 65 |
+
|
| 66 |
+
.. automodule:: ants.segmentation.prior_based_segmentation
|
| 67 |
+
:members:
|
| 68 |
+
:undoc-members:
|
| 69 |
+
:show-inheritance:
|
| 70 |
+
|
| 71 |
+
|
| 72 |
+
Module contents
|
| 73 |
+
---------------
|
| 74 |
+
|
| 75 |
+
.. automodule:: ants.segmentation
|
| 76 |
+
:members:
|
| 77 |
+
:undoc-members:
|
| 78 |
+
:show-inheritance:
|
MindEyeV2/antspy/docs/source/conf.py
ADDED
|
@@ -0,0 +1,233 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env python3
|
| 2 |
+
# -*- coding: utf-8 -*-
|
| 3 |
+
#
|
| 4 |
+
# ANTsPy documentation build configuration file, created by
|
| 5 |
+
# sphinx-quickstart on Fri Dec 23 13:31:47 2016.
|
| 6 |
+
#
|
| 7 |
+
# This file is execfile()d with the current directory set to its
|
| 8 |
+
# containing dir.
|
| 9 |
+
#
|
| 10 |
+
# Note that not all possible configuration values are present in this
|
| 11 |
+
# autogenerated file.
|
| 12 |
+
#
|
| 13 |
+
# All configuration values have a default; values that are commented out
|
| 14 |
+
# serve to show the default.
|
| 15 |
+
|
| 16 |
+
# If extensions (or modules to document with autodoc) are in another directory,
|
| 17 |
+
# add these directories to sys.path here. If the directory is relative to the
|
| 18 |
+
# documentation root, use os.path.abspath to make it absolute, like shown here.
|
| 19 |
+
#
|
| 20 |
+
|
| 21 |
+
import os
|
| 22 |
+
import sys
|
| 23 |
+
import shutil
|
| 24 |
+
|
| 25 |
+
on_rtd = os.environ.get('READTHEDOCS') == 'True'
|
| 26 |
+
|
| 27 |
+
# add package to local path
|
| 28 |
+
sys.path.insert(0, os.path.abspath('../../'))
|
| 29 |
+
autodoc_mock_imports = ['_tkinter', 'matplotlib']
|
| 30 |
+
if on_rtd:
|
| 31 |
+
# replace lib __init__ with empty init file since RTD cant handle C++ extensions
|
| 32 |
+
os.makedirs('../../ants/lib', exist_ok=True)
|
| 33 |
+
shutil.copyfile('emptyinit.py', '../../ants/lib/__init__.py')
|
| 34 |
+
|
| 35 |
+
import ants
|
| 36 |
+
import numpy as np
|
| 37 |
+
import sphinx_rtd_theme
|
| 38 |
+
|
| 39 |
+
|
| 40 |
+
# -- General configuration ------------------------------------------------
|
| 41 |
+
|
| 42 |
+
# If your documentation needs a minimal Sphinx version, state it here.
|
| 43 |
+
#
|
| 44 |
+
# needs_sphinx = '1.0'
|
| 45 |
+
|
| 46 |
+
# Add any Sphinx extension module names here, as strings. They can be
|
| 47 |
+
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
|
| 48 |
+
# ones.
|
| 49 |
+
extensions = [
|
| 50 |
+
'sphinx.ext.autodoc',
|
| 51 |
+
'sphinx.ext.autosummary',
|
| 52 |
+
'sphinx.ext.doctest',
|
| 53 |
+
'sphinx.ext.intersphinx',
|
| 54 |
+
'sphinx.ext.todo',
|
| 55 |
+
'sphinx.ext.coverage',
|
| 56 |
+
'sphinx.ext.mathjax',
|
| 57 |
+
'sphinx.ext.napoleon',
|
| 58 |
+
'sphinx.ext.viewcode',
|
| 59 |
+
]
|
| 60 |
+
|
| 61 |
+
napoleon_use_ivar = True
|
| 62 |
+
|
| 63 |
+
# Add any paths that contain templates here, relative to this directory.
|
| 64 |
+
templates_path = ['_templates']
|
| 65 |
+
|
| 66 |
+
# The suffix(es) of source filenames.
|
| 67 |
+
# You can specify multiple suffix as a list of string:
|
| 68 |
+
#
|
| 69 |
+
# source_suffix = ['.rst', '.md']
|
| 70 |
+
source_suffix = '.rst'
|
| 71 |
+
|
| 72 |
+
# The master toctree document.
|
| 73 |
+
master_doc = 'index'
|
| 74 |
+
|
| 75 |
+
# General information about the project.
|
| 76 |
+
project = 'ANTsPy'
|
| 77 |
+
copyright = '2017, ANTs Contributors'
|
| 78 |
+
author = 'ANTs Contributors'
|
| 79 |
+
|
| 80 |
+
# The version info for the project you're documenting, acts as replacement for
|
| 81 |
+
# |version| and |release|, also used in various other places throughout the
|
| 82 |
+
# built documents.
|
| 83 |
+
#
|
| 84 |
+
# The short X.Y version.
|
| 85 |
+
# TODO: change to [:2] at v1.0
|
| 86 |
+
version = 'master (0.1.3)'
|
| 87 |
+
# The full version, including alpha/beta/rc tags.
|
| 88 |
+
# TODO: verify this works as expected
|
| 89 |
+
release = 'master'
|
| 90 |
+
|
| 91 |
+
# The language for content autogenerated by Sphinx. Refer to documentation
|
| 92 |
+
# for a list of supported languages.
|
| 93 |
+
#
|
| 94 |
+
# This is also used if you do content translation via gettext catalogs.
|
| 95 |
+
# Usually you set "language" from the command line for these cases.
|
| 96 |
+
language = None
|
| 97 |
+
|
| 98 |
+
# List of patterns, relative to source directory, that match files and
|
| 99 |
+
# directories to ignore when looking for source files.
|
| 100 |
+
# This patterns also effect to html_static_path and html_extra_path
|
| 101 |
+
exclude_patterns = []
|
| 102 |
+
|
| 103 |
+
# The name of the Pygments (syntax highlighting) style to use.
|
| 104 |
+
pygments_style = 'sphinx'
|
| 105 |
+
|
| 106 |
+
# If true, `todo` and `todoList` produce output, else they produce nothing.
|
| 107 |
+
todo_include_todos = True
|
| 108 |
+
|
| 109 |
+
|
| 110 |
+
# -- Options for HTML output ----------------------------------------------
|
| 111 |
+
|
| 112 |
+
# The theme to use for HTML and HTML Help pages. See the documentation for
|
| 113 |
+
# a list of builtin themes.
|
| 114 |
+
#
|
| 115 |
+
html_theme = 'alabaster'
|
| 116 |
+
#html_theme_path = [sphinx_rtd_theme.get_html_theme_path()]
|
| 117 |
+
|
| 118 |
+
# Theme options are theme-specific and customize the look and feel of a theme
|
| 119 |
+
# further. For a list of options available for each theme, see the
|
| 120 |
+
# documentation.
|
| 121 |
+
#
|
| 122 |
+
#html_theme_options = {
|
| 123 |
+
# 'collapse_navigation': False,
|
| 124 |
+
# 'display_version': True,
|
| 125 |
+
# 'logo_only': True,
|
| 126 |
+
#}
|
| 127 |
+
|
| 128 |
+
html_logo = '_static/img/antspy-logo.png'
|
| 129 |
+
|
| 130 |
+
# Add any paths that contain custom static files (such as style sheets) here,
|
| 131 |
+
# relative to this directory. They are copied after the builtin static files,
|
| 132 |
+
# so a file named "default.css" will overwrite the builtin "default.css".
|
| 133 |
+
html_static_path = ['_static']
|
| 134 |
+
|
| 135 |
+
#html_style_path = '_static/css/ants_theme.css'
|
| 136 |
+
#html_context = {
|
| 137 |
+
# 'css_files': [
|
| 138 |
+
# 'https://fonts.googleapis.com/css?family=Lato',
|
| 139 |
+
# '_static/css/ants_theme.css'
|
| 140 |
+
# ],
|
| 141 |
+
#}
|
| 142 |
+
|
| 143 |
+
|
| 144 |
+
# -- Options for HTMLHelp output ------------------------------------------
|
| 145 |
+
|
| 146 |
+
# Output file base name for HTML help builder.
|
| 147 |
+
htmlhelp_basename = 'ANTsPydoc'
|
| 148 |
+
|
| 149 |
+
|
| 150 |
+
# -- Options for LaTeX output ---------------------------------------------
|
| 151 |
+
|
| 152 |
+
|
| 153 |
+
|
| 154 |
+
# -- Options for manual page output ---------------------------------------
|
| 155 |
+
|
| 156 |
+
# One entry per manual page. List of tuples
|
| 157 |
+
# (source start file, name, description, authors, manual section).
|
| 158 |
+
man_pages = [
|
| 159 |
+
(master_doc, 'ANTsPy', 'ANTsPy Documentation',
|
| 160 |
+
[author], 1)
|
| 161 |
+
]
|
| 162 |
+
|
| 163 |
+
|
| 164 |
+
# -- Options for Texinfo output -------------------------------------------
|
| 165 |
+
|
| 166 |
+
# Grouping the document tree into Texinfo files. List of tuples
|
| 167 |
+
# (source start file, target name, title, author,
|
| 168 |
+
# dir menu entry, description, category)
|
| 169 |
+
texinfo_documents = [
|
| 170 |
+
(master_doc, 'ANTsPy', 'ANTsPy Documentation',
|
| 171 |
+
author, 'ANTsPy', 'One line description of project.',
|
| 172 |
+
'Miscellaneous'),
|
| 173 |
+
]
|
| 174 |
+
|
| 175 |
+
|
| 176 |
+
# Example configuration for intersphinx: refer to the Python standard library.
|
| 177 |
+
intersphinx_mapping = {
|
| 178 |
+
'python': ('https://docs.python.org/', None),
|
| 179 |
+
'numpy': ('http://docs.scipy.org/doc/numpy/', None),
|
| 180 |
+
}
|
| 181 |
+
|
| 182 |
+
# -- A patch that prevents Sphinx from cross-referencing ivar tags -------
|
| 183 |
+
# See http://stackoverflow.com/a/41184353/3343043
|
| 184 |
+
|
| 185 |
+
from docutils import nodes
|
| 186 |
+
from sphinx.util.docfields import TypedField
|
| 187 |
+
from sphinx import addnodes
|
| 188 |
+
|
| 189 |
+
|
| 190 |
+
def patched_make_field(self, types, domain, items, **kw):
|
| 191 |
+
# `kw` catches `env=None` needed for newer sphinx while maingaining
|
| 192 |
+
# backwards compatibility when passed along further down!
|
| 193 |
+
|
| 194 |
+
# type: (List, unicode, Tuple) -> nodes.field
|
| 195 |
+
def handle_item(fieldarg, content):
|
| 196 |
+
par = nodes.paragraph()
|
| 197 |
+
par += addnodes.literal_strong('', fieldarg) # Patch: this line added
|
| 198 |
+
# par.extend(self.make_xrefs(self.rolename, domain, fieldarg,
|
| 199 |
+
# addnodes.literal_strong))
|
| 200 |
+
if fieldarg in types:
|
| 201 |
+
par += nodes.Text(' (')
|
| 202 |
+
# NOTE: using .pop() here to prevent a single type node to be
|
| 203 |
+
# inserted twice into the doctree, which leads to
|
| 204 |
+
# inconsistencies later when references are resolved
|
| 205 |
+
fieldtype = types.pop(fieldarg)
|
| 206 |
+
if len(fieldtype) == 1 and isinstance(fieldtype[0], nodes.Text):
|
| 207 |
+
typename = u''.join(n.astext() for n in fieldtype)
|
| 208 |
+
typename = typename.replace('int', 'python:int')
|
| 209 |
+
typename = typename.replace('long', 'python:long')
|
| 210 |
+
typename = typename.replace('float', 'python:float')
|
| 211 |
+
typename = typename.replace('type', 'python:type')
|
| 212 |
+
par.extend(self.make_xrefs(self.typerolename, domain, typename,
|
| 213 |
+
addnodes.literal_emphasis, **kw))
|
| 214 |
+
else:
|
| 215 |
+
par += fieldtype
|
| 216 |
+
par += nodes.Text(')')
|
| 217 |
+
par += nodes.Text(' -- ')
|
| 218 |
+
par += content
|
| 219 |
+
return par
|
| 220 |
+
|
| 221 |
+
fieldname = nodes.field_name('', self.label)
|
| 222 |
+
if len(items) == 1 and self.can_collapse:
|
| 223 |
+
fieldarg, content = items[0]
|
| 224 |
+
bodynode = handle_item(fieldarg, content)
|
| 225 |
+
else:
|
| 226 |
+
bodynode = self.list_type()
|
| 227 |
+
for fieldarg, content in items:
|
| 228 |
+
bodynode += nodes.list_item('', handle_item(fieldarg, content))
|
| 229 |
+
fieldbody = nodes.field_body('', bodynode)
|
| 230 |
+
return nodes.field('', fieldname, fieldbody)
|
| 231 |
+
|
| 232 |
+
TypedField.make_field = patched_make_field
|
| 233 |
+
|
MindEyeV2/antspy/docs/source/core.rst
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
Core
|
| 2 |
+
===================================
|
| 3 |
+
.. automodule:: ants
|
| 4 |
+
|
| 5 |
+
Images
|
| 6 |
+
----------------------------------
|
| 7 |
+
|
| 8 |
+
ANTsImage
|
| 9 |
+
~~~~~~~~~~~~~~~~~~~~~~
|
| 10 |
+
.. autoclass:: ants.core.ants_image.ANTsImage
|
| 11 |
+
:members:
|
| 12 |
+
|
| 13 |
+
ANTsImage IO
|
| 14 |
+
~~~~~~~~~~~~~~~~~~~~~~
|
| 15 |
+
.. autofunction:: image_clone
|
| 16 |
+
.. autofunction:: image_header_info
|
| 17 |
+
.. autofunction:: image_read
|
| 18 |
+
.. autofunction:: image_write
|
| 19 |
+
.. autofunction:: make_image
|
| 20 |
+
.. autofunction:: from_numpy
|
| 21 |
+
.. autofunction:: matrix_to_images
|
| 22 |
+
.. autofunction:: images_from_matrix
|
| 23 |
+
.. autofunction:: image_list_to_matrix
|
| 24 |
+
.. autofunction:: images_to_matrix
|
| 25 |
+
.. autofunction:: matrix_from_images
|
| 26 |
+
|
| 27 |
+
Transforms
|
| 28 |
+
----------------------------------
|
| 29 |
+
|
| 30 |
+
ANTsTransform
|
| 31 |
+
~~~~~~~~~~~~~~~~~~~~~~
|
| 32 |
+
.. autoclass:: ants.core.ants_transform.ANTsTransform
|
| 33 |
+
:members:
|
| 34 |
+
|
| 35 |
+
ANTsTransform IO
|
| 36 |
+
~~~~~~~~~~~~~~~~~~~~~~
|
| 37 |
+
.. autofunction:: create_ants_transform
|
| 38 |
+
.. autofunction:: new_ants_transform
|
| 39 |
+
.. autofunction:: read_transform
|
| 40 |
+
.. autofunction:: write_transform
|
| 41 |
+
.. autofunction:: transform_from_displacement_field
|
| 42 |
+
|
| 43 |
+
Metrics
|
| 44 |
+
----------------------------------
|
| 45 |
+
|
| 46 |
+
ANTsMetric
|
| 47 |
+
~~~~~~~~~~~~~~~~~~~~~~
|
| 48 |
+
.. autoclass:: ants.core.ants_metric.ANTsImageToImageMetric
|
| 49 |
+
:members:
|
| 50 |
+
|
| 51 |
+
ANTsMetric IO
|
| 52 |
+
~~~~~~~~~~~~~~~~~~~~~~
|
| 53 |
+
.. autofunction:: new_ants_metric
|
| 54 |
+
.. autofunction:: create_ants_metric
|
| 55 |
+
.. autofunction:: supported_metrics
|
| 56 |
+
|
| 57 |
+
|
MindEyeV2/antspy/docs/source/modules.rst
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
ANTsPy
|
| 2 |
+
======
|
| 3 |
+
|
| 4 |
+
.. toctree::
|
| 5 |
+
:maxdepth: 4
|
| 6 |
+
|
| 7 |
+
ants
|
| 8 |
+
setup
|
MindEyeV2/antspy/docs/source/registration.rst
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
Registration
|
| 2 |
+
===================================
|
| 3 |
+
.. automodule:: ants
|
| 4 |
+
|
| 5 |
+
.. autofunction:: registration
|
| 6 |
+
.. autofunction:: affine_initializer
|
| 7 |
+
.. autofunction:: apply_transforms
|
| 8 |
+
.. autofunction:: create_jacobian_determinant_image
|
| 9 |
+
.. autofunction:: create_warped_grid
|
| 10 |
+
.. autofunction:: fsl2antstransform
|
| 11 |
+
.. autofunction:: image_mutual_information
|
| 12 |
+
.. autofunction:: reflect_image
|
| 13 |
+
.. autofunction:: reorient_image
|
| 14 |
+
.. autofunction:: get_center_of_mass
|
| 15 |
+
.. autofunction:: resample_image
|
| 16 |
+
.. autofunction:: resample_image_to_target
|
MindEyeV2/antspy/docs/source/setup.rst
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
setup module
|
| 2 |
+
============
|
| 3 |
+
|
| 4 |
+
.. automodule:: setup
|
| 5 |
+
:members:
|
| 6 |
+
:undoc-members:
|
| 7 |
+
:show-inheritance:
|
MindEyeV2/antspy/docs/source/vis.rst
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
Visualization
|
| 2 |
+
===================================
|
| 3 |
+
.. automodule:: ants
|
| 4 |
+
|
| 5 |
+
.. autofunction:: plot
|
| 6 |
+
.. autofunction:: surf
|
| 7 |
+
.. autofunction:: vol
|
| 8 |
+
.. autofunction:: render_surface_function
|
MindEyeV2/antspy/src/WRAP_KellyKapowski.cxx
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#include <nanobind/nanobind.h>
|
| 2 |
+
#include <nanobind/stl/vector.h>
|
| 3 |
+
#include <nanobind/stl/string.h>
|
| 4 |
+
|
| 5 |
+
#include "antscore/KellyKapowski.h"
|
| 6 |
+
|
| 7 |
+
namespace nb = nanobind;
|
| 8 |
+
using namespace nb::literals;
|
| 9 |
+
|
| 10 |
+
using StrVector = std::vector<std::string>;
|
| 11 |
+
|
| 12 |
+
int KellyKapowski( StrVector instring )
|
| 13 |
+
{
|
| 14 |
+
return ants::KellyKapowski(instring, NULL);
|
| 15 |
+
}
|
| 16 |
+
|
| 17 |
+
void wrap_KellyKapowski(nb::module_ &m) {
|
| 18 |
+
m.def("KellyKapowski", &KellyKapowski);
|
| 19 |
+
}
|
MindEyeV2/antspy/src/WRAP_N4BiasFieldCorrection.cxx
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#include <nanobind/nanobind.h>
|
| 2 |
+
#include <nanobind/stl/vector.h>
|
| 3 |
+
#include <nanobind/stl/string.h>
|
| 4 |
+
|
| 5 |
+
#include "antscore/N4BiasFieldCorrection.h"
|
| 6 |
+
|
| 7 |
+
namespace nb = nanobind;
|
| 8 |
+
using namespace nb::literals;
|
| 9 |
+
|
| 10 |
+
using StrVector = std::vector<std::string>;
|
| 11 |
+
|
| 12 |
+
int N4BiasFieldCorrection( StrVector instring )
|
| 13 |
+
{
|
| 14 |
+
return ants::N4BiasFieldCorrection(instring, NULL);
|
| 15 |
+
}
|
| 16 |
+
|
| 17 |
+
void wrap_N4BiasFieldCorrection(nb::module_ &m) {
|
| 18 |
+
m.def("N4BiasFieldCorrection", &N4BiasFieldCorrection);
|
| 19 |
+
}
|
MindEyeV2/antspy/src/WRAP_ResampleImage.cxx
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#include <nanobind/nanobind.h>
|
| 2 |
+
#include <nanobind/stl/vector.h>
|
| 3 |
+
#include <nanobind/stl/string.h>
|
| 4 |
+
|
| 5 |
+
#include "antscore/ResampleImage.h"
|
| 6 |
+
|
| 7 |
+
namespace nb = nanobind;
|
| 8 |
+
using namespace nb::literals;
|
| 9 |
+
|
| 10 |
+
using StrVector = std::vector<std::string>;
|
| 11 |
+
|
| 12 |
+
int ResampleImage( StrVector instring )
|
| 13 |
+
{
|
| 14 |
+
return ants::ResampleImage(instring, NULL);
|
| 15 |
+
}
|
| 16 |
+
|
| 17 |
+
void wrap_ResampleImage(nb::module_ &m) {
|
| 18 |
+
m.def("ResampleImage", &ResampleImage);
|
| 19 |
+
}
|
MindEyeV2/antspy/src/antsImage.h
ADDED
|
@@ -0,0 +1,233 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#ifndef __ANTSPYIMAGE_H
|
| 2 |
+
#define __ANTSPYIMAGE_H
|
| 3 |
+
|
| 4 |
+
#include <nanobind/nanobind.h>
|
| 5 |
+
#include <nanobind/stl/vector.h>
|
| 6 |
+
#include <nanobind/stl/string.h>
|
| 7 |
+
#include <nanobind/stl/tuple.h>
|
| 8 |
+
#include <nanobind/stl/list.h>
|
| 9 |
+
#include <nanobind/ndarray.h>
|
| 10 |
+
#include <nanobind/stl/shared_ptr.h>
|
| 11 |
+
|
| 12 |
+
#include "itkImageIOBase.h"
|
| 13 |
+
|
| 14 |
+
#include "itkImage.h"
|
| 15 |
+
#include "itkImageFileReader.h"
|
| 16 |
+
#include "itkImageFileWriter.h"
|
| 17 |
+
#include "itkPyBuffer.h"
|
| 18 |
+
#include "itkVectorImage.h"
|
| 19 |
+
#include "itkChangeInformationImageFilter.h"
|
| 20 |
+
|
| 21 |
+
#include "itkMath.h"
|
| 22 |
+
#include "itkPyVnl.h"
|
| 23 |
+
#include "itkMatrix.h"
|
| 24 |
+
#include "vnl/vnl_matrix_fixed.hxx"
|
| 25 |
+
#include "vnl/vnl_transpose.h"
|
| 26 |
+
#include "vnl/algo/vnl_matrix_inverse.h"
|
| 27 |
+
#include "vnl/vnl_matrix.h"
|
| 28 |
+
#include "vnl/algo/vnl_determinant.h"
|
| 29 |
+
|
| 30 |
+
namespace nb = nanobind;
|
| 31 |
+
using namespace nb::literals;
|
| 32 |
+
|
| 33 |
+
|
| 34 |
+
template <typename ImageType>
|
| 35 |
+
typename ImageType::Pointer as( void * ptr )
|
| 36 |
+
{
|
| 37 |
+
typename ImageType::Pointer * real = static_cast<typename ImageType::Pointer *>(ptr); // static_cast or reinterpret_cast ??
|
| 38 |
+
return *real;
|
| 39 |
+
}
|
| 40 |
+
|
| 41 |
+
template <typename ImageType>
|
| 42 |
+
void * wrap( const typename ImageType::Pointer &image )
|
| 43 |
+
{
|
| 44 |
+
typedef typename ImageType::Pointer ImagePointerType;
|
| 45 |
+
ImagePointerType * ptr = new ImagePointerType( image );
|
| 46 |
+
return ptr;
|
| 47 |
+
}
|
| 48 |
+
|
| 49 |
+
template <typename ImageType>
|
| 50 |
+
typename ImageType::Pointer asImage( void * ptr ) {
|
| 51 |
+
typename ImageType::Pointer itkImage = ImageType::New();
|
| 52 |
+
itkImage = as<ImageType>( ptr );
|
| 53 |
+
return itkImage;
|
| 54 |
+
}
|
| 55 |
+
|
| 56 |
+
|
| 57 |
+
template <typename ImageType>
|
| 58 |
+
struct AntsImage {
|
| 59 |
+
typename ImageType::Pointer ptr;
|
| 60 |
+
};
|
| 61 |
+
|
| 62 |
+
|
| 63 |
+
template <typename ImageType>
|
| 64 |
+
void toFile( AntsImage<ImageType> & myPointer, std::string filename )
|
| 65 |
+
{
|
| 66 |
+
typename ImageType::Pointer image = myPointer.ptr;
|
| 67 |
+
|
| 68 |
+
typedef itk::ImageFileWriter< ImageType > ImageWriterType ;
|
| 69 |
+
typename ImageWriterType::Pointer image_writer = ImageWriterType::New() ;
|
| 70 |
+
image_writer->SetFileName( filename.c_str() ) ;
|
| 71 |
+
image_writer->SetInput( image );
|
| 72 |
+
image_writer->Update();
|
| 73 |
+
}
|
| 74 |
+
|
| 75 |
+
|
| 76 |
+
template <typename ImageType>
|
| 77 |
+
std::list<int> getShape( AntsImage<ImageType> & myPointer )
|
| 78 |
+
{
|
| 79 |
+
typename ImageType::Pointer image = myPointer.ptr;
|
| 80 |
+
unsigned int ndim = ImageType::GetImageDimension();
|
| 81 |
+
image->UpdateOutputInformation();
|
| 82 |
+
typename ImageType::SizeType shape = image->GetBufferedRegion().GetSize();
|
| 83 |
+
std::list<int> shapelist;
|
| 84 |
+
for (int i = 0; i < ndim; i++)
|
| 85 |
+
{
|
| 86 |
+
shapelist.push_back( shape[i] );
|
| 87 |
+
}
|
| 88 |
+
return shapelist;
|
| 89 |
+
}
|
| 90 |
+
|
| 91 |
+
|
| 92 |
+
template <typename ImageType>
|
| 93 |
+
int getComponents( AntsImage<ImageType> & myPointer )
|
| 94 |
+
{
|
| 95 |
+
typename ImageType::Pointer image = myPointer.ptr;
|
| 96 |
+
return image->GetNumberOfComponentsPerPixel();
|
| 97 |
+
}
|
| 98 |
+
|
| 99 |
+
template <typename ImageType>
|
| 100 |
+
std::vector<double> getOrigin( AntsImage<ImageType> & myPointer )
|
| 101 |
+
{
|
| 102 |
+
typename ImageType::Pointer image = myPointer.ptr;
|
| 103 |
+
typename ImageType::PointType origin = image->GetOrigin();
|
| 104 |
+
unsigned int ndim = ImageType::GetImageDimension();
|
| 105 |
+
|
| 106 |
+
std::vector<double> originlist;
|
| 107 |
+
for (int i = 0; i < ndim; i++)
|
| 108 |
+
{
|
| 109 |
+
originlist.push_back( origin[i] );
|
| 110 |
+
}
|
| 111 |
+
|
| 112 |
+
return originlist;
|
| 113 |
+
}
|
| 114 |
+
|
| 115 |
+
template <typename ImageType>
|
| 116 |
+
void setOrigin( AntsImage<ImageType> & myPointer, std::vector<double> new_origin)
|
| 117 |
+
{
|
| 118 |
+
typename ImageType::Pointer itkImage = myPointer.ptr;
|
| 119 |
+
unsigned int nvals = new_origin.size();
|
| 120 |
+
typename ImageType::PointType origin = itkImage->GetOrigin();
|
| 121 |
+
for (int i = 0; i < nvals; i++)
|
| 122 |
+
{
|
| 123 |
+
origin[i] = new_origin[i];
|
| 124 |
+
}
|
| 125 |
+
itkImage->SetOrigin( origin );
|
| 126 |
+
}
|
| 127 |
+
|
| 128 |
+
|
| 129 |
+
template <typename ImageType>
|
| 130 |
+
std::vector<double> getDirection( AntsImage<ImageType> & myPointer )
|
| 131 |
+
{
|
| 132 |
+
typename ImageType::Pointer image = myPointer.ptr;
|
| 133 |
+
typedef typename ImageType::DirectionType ImageDirectionType;
|
| 134 |
+
ImageDirectionType direction = image->GetDirection();
|
| 135 |
+
|
| 136 |
+
typedef typename ImageDirectionType::InternalMatrixType DirectionInternalMatrixType;
|
| 137 |
+
DirectionInternalMatrixType fixed_matrix = direction.GetVnlMatrix();
|
| 138 |
+
|
| 139 |
+
vnl_matrix<double> vnlmat1 = fixed_matrix.as_matrix();
|
| 140 |
+
|
| 141 |
+
const unsigned int ndim = ImageType::SizeType::GetSizeDimension();
|
| 142 |
+
|
| 143 |
+
std::vector<double> dvec;
|
| 144 |
+
|
| 145 |
+
for (int i = 0; i < ndim; i++)
|
| 146 |
+
{
|
| 147 |
+
for (int j = 0; j < ndim; j++)
|
| 148 |
+
{
|
| 149 |
+
dvec.push_back(vnlmat1(i,j));
|
| 150 |
+
}
|
| 151 |
+
}
|
| 152 |
+
return dvec;
|
| 153 |
+
|
| 154 |
+
}
|
| 155 |
+
|
| 156 |
+
|
| 157 |
+
template <typename ImageType>
|
| 158 |
+
void setDirection( AntsImage<ImageType> & myPointer, std::vector<std::vector<double>> new_direction)
|
| 159 |
+
{
|
| 160 |
+
|
| 161 |
+
typename ImageType::Pointer itkImage = myPointer.ptr;
|
| 162 |
+
|
| 163 |
+
typename ImageType::DirectionType new_matrix2 = itkImage->GetDirection( );
|
| 164 |
+
for ( std::size_t i = 0; i < new_direction.size(); i++ )
|
| 165 |
+
for ( std::size_t j = 0; j < new_direction[0].size(); j++ ) {
|
| 166 |
+
new_matrix2(i,j) = new_direction[i][j];
|
| 167 |
+
}
|
| 168 |
+
itkImage->SetDirection( new_matrix2 );
|
| 169 |
+
}
|
| 170 |
+
|
| 171 |
+
template <typename ImageType>
|
| 172 |
+
void setSpacing( AntsImage<ImageType> & myPointer, std::vector<double> new_spacing)
|
| 173 |
+
{
|
| 174 |
+
typename ImageType::Pointer itkImage = myPointer.ptr;
|
| 175 |
+
unsigned int nvals = new_spacing.size();
|
| 176 |
+
typename ImageType::SpacingType spacing = itkImage->GetSpacing();
|
| 177 |
+
|
| 178 |
+
for (int i = 0; i < nvals; i++)
|
| 179 |
+
{
|
| 180 |
+
spacing[i] = new_spacing[i];
|
| 181 |
+
}
|
| 182 |
+
itkImage->SetSpacing( spacing );
|
| 183 |
+
}
|
| 184 |
+
|
| 185 |
+
template <typename ImageType>
|
| 186 |
+
std::vector<double> getSpacing( AntsImage<ImageType> & myPointer )
|
| 187 |
+
{
|
| 188 |
+
typename ImageType::Pointer image = myPointer.ptr;
|
| 189 |
+
typename ImageType::SpacingType spacing = image->GetSpacing();
|
| 190 |
+
unsigned int ndim = ImageType::GetImageDimension();
|
| 191 |
+
|
| 192 |
+
std::vector<double> spacinglist;
|
| 193 |
+
for (int i = 0; i < ndim; i++)
|
| 194 |
+
{
|
| 195 |
+
spacinglist.push_back( spacing[i] );
|
| 196 |
+
}
|
| 197 |
+
|
| 198 |
+
return spacinglist;
|
| 199 |
+
}
|
| 200 |
+
|
| 201 |
+
/*
|
| 202 |
+
This function resets the region of an image to index from zero if needed. This
|
| 203 |
+
keeps the voxel indices in the numpy matrix consistent with the ITK image, and
|
| 204 |
+
also keeps the origin of physical space of the consistent with how it will be
|
| 205 |
+
saved as NIFTI.
|
| 206 |
+
*/
|
| 207 |
+
template <typename ImageType>
|
| 208 |
+
static void FixNonZeroIndex( typename ImageType::Pointer img )
|
| 209 |
+
{
|
| 210 |
+
assert(img);
|
| 211 |
+
|
| 212 |
+
typename ImageType::RegionType r = img->GetLargestPossibleRegion();
|
| 213 |
+
typename ImageType::IndexType idx = r.GetIndex();
|
| 214 |
+
|
| 215 |
+
for (unsigned int i = 0; i < ImageType::ImageDimension; ++i)
|
| 216 |
+
{
|
| 217 |
+
// if any index is non-zero, reset the origin and region
|
| 218 |
+
if ( idx[i] != 0 )
|
| 219 |
+
{
|
| 220 |
+
typename ImageType::PointType o;
|
| 221 |
+
img->TransformIndexToPhysicalPoint( idx, o );
|
| 222 |
+
img->SetOrigin( o );
|
| 223 |
+
|
| 224 |
+
idx.Fill( 0 );
|
| 225 |
+
r.SetIndex( idx );
|
| 226 |
+
img->SetRegions( r );
|
| 227 |
+
|
| 228 |
+
return;
|
| 229 |
+
}
|
| 230 |
+
}
|
| 231 |
+
}
|
| 232 |
+
|
| 233 |
+
#endif
|
MindEyeV2/antspy/src/antsImageClone.cxx
ADDED
|
@@ -0,0 +1,115 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
|
| 2 |
+
#include <nanobind/nanobind.h>
|
| 3 |
+
#include <nanobind/stl/vector.h>
|
| 4 |
+
#include <nanobind/stl/string.h>
|
| 5 |
+
|
| 6 |
+
#include <exception>
|
| 7 |
+
#include <vector>
|
| 8 |
+
#include <string>
|
| 9 |
+
|
| 10 |
+
#include "itkImage.h"
|
| 11 |
+
#include "itkImageFileWriter.h"
|
| 12 |
+
|
| 13 |
+
#include "antsImage.h"
|
| 14 |
+
|
| 15 |
+
namespace nb = nanobind;
|
| 16 |
+
using namespace nb::literals;
|
| 17 |
+
|
| 18 |
+
template<typename InImageType, typename OutImageType>
|
| 19 |
+
AntsImage<OutImageType> antsImageClone( AntsImage<InImageType> & myPointer )
|
| 20 |
+
{
|
| 21 |
+
typename InImageType::Pointer in_image = myPointer.ptr;
|
| 22 |
+
|
| 23 |
+
typename OutImageType::Pointer out_image = OutImageType::New() ;
|
| 24 |
+
out_image->SetRegions( in_image->GetLargestPossibleRegion() ) ;
|
| 25 |
+
out_image->SetSpacing( in_image->GetSpacing() ) ;
|
| 26 |
+
out_image->SetOrigin( in_image->GetOrigin() ) ;
|
| 27 |
+
out_image->SetDirection( in_image->GetDirection() );
|
| 28 |
+
//out_image->CopyInformation( in_image );
|
| 29 |
+
out_image->AllocateInitialized();
|
| 30 |
+
|
| 31 |
+
itk::ImageRegionConstIterator< InImageType > in_iterator( in_image , in_image->GetLargestPossibleRegion() ) ;
|
| 32 |
+
itk::ImageRegionIterator< OutImageType > out_iterator( out_image , out_image->GetLargestPossibleRegion() ) ;
|
| 33 |
+
for( in_iterator.GoToBegin() , out_iterator.GoToBegin() ; !in_iterator.IsAtEnd() ; ++in_iterator , ++out_iterator )
|
| 34 |
+
{
|
| 35 |
+
out_iterator.Set( static_cast< typename OutImageType::PixelType >( in_iterator.Get() ) ) ;
|
| 36 |
+
}
|
| 37 |
+
AntsImage<OutImageType> outImage = { out_image };
|
| 38 |
+
return outImage;
|
| 39 |
+
}
|
| 40 |
+
void local_antsImageClone(nb::module_ &m) {
|
| 41 |
+
|
| 42 |
+
// call the function based on the image type you are converting TO.
|
| 43 |
+
// the image type you are converting FROM should be automatically inferred by the template
|
| 44 |
+
|
| 45 |
+
// dim = 2
|
| 46 |
+
m.def("antsImageCloneUC2", &antsImageClone<itk::Image<unsigned char,2>,itk::Image<unsigned char,2>>);
|
| 47 |
+
m.def("antsImageCloneUC2", &antsImageClone<itk::Image<unsigned int,2>,itk::Image<unsigned char,2>>);
|
| 48 |
+
m.def("antsImageCloneUC2", &antsImageClone<itk::Image<float,2>,itk::Image<unsigned char,2>>);
|
| 49 |
+
m.def("antsImageCloneUC2", &antsImageClone<itk::Image<double,2>,itk::Image<unsigned char,2>>);
|
| 50 |
+
|
| 51 |
+
m.def("antsImageCloneUI2", &antsImageClone<itk::Image<unsigned char,2>,itk::Image<unsigned int,2>>);
|
| 52 |
+
m.def("antsImageCloneUI2", &antsImageClone<itk::Image<unsigned int,2>,itk::Image<unsigned int,2>>);
|
| 53 |
+
m.def("antsImageCloneUI2", &antsImageClone<itk::Image<float,2>,itk::Image<unsigned int,2>>);
|
| 54 |
+
m.def("antsImageCloneUI2", &antsImageClone<itk::Image<double,2>,itk::Image<unsigned int,2>>);
|
| 55 |
+
|
| 56 |
+
m.def("antsImageCloneF2", &antsImageClone<itk::Image<unsigned char,2>,itk::Image<float,2>>);
|
| 57 |
+
m.def("antsImageCloneF2", &antsImageClone<itk::Image<unsigned int,2>,itk::Image<float,2>>);
|
| 58 |
+
m.def("antsImageCloneF2", &antsImageClone<itk::Image<float,2>,itk::Image<float,2>>);
|
| 59 |
+
m.def("antsImageCloneF2", &antsImageClone<itk::Image<double,2>,itk::Image<float,2>>);
|
| 60 |
+
|
| 61 |
+
m.def("antsImageCloneD2", &antsImageClone<itk::Image<unsigned char,2>,itk::Image<double,2>>);
|
| 62 |
+
m.def("antsImageCloneD2", &antsImageClone<itk::Image<unsigned int,2>,itk::Image<double,2>>);
|
| 63 |
+
m.def("antsImageCloneD2", &antsImageClone<itk::Image<float,2>,itk::Image<double,2>>);
|
| 64 |
+
m.def("antsImageCloneD2", &antsImageClone<itk::Image<double,2>,itk::Image<double,2>>);
|
| 65 |
+
|
| 66 |
+
m.def("antsImageCloneRGBUC2", &antsImageClone<itk::Image<itk::RGBPixel<unsigned char>,2>,itk::Image<itk::RGBPixel<unsigned char>,2>>);
|
| 67 |
+
|
| 68 |
+
// dim = 3
|
| 69 |
+
|
| 70 |
+
m.def("antsImageCloneUC3", &antsImageClone<itk::Image<unsigned char,3>,itk::Image<unsigned char,3>>);
|
| 71 |
+
m.def("antsImageCloneUC3", &antsImageClone<itk::Image<unsigned int,3>,itk::Image<unsigned char,3>>);
|
| 72 |
+
m.def("antsImageCloneUC3", &antsImageClone<itk::Image<float,3>,itk::Image<unsigned char,3>>);
|
| 73 |
+
m.def("antsImageCloneUC3", &antsImageClone<itk::Image<double,3>,itk::Image<unsigned char,3>>);
|
| 74 |
+
|
| 75 |
+
m.def("antsImageCloneUI3", &antsImageClone<itk::Image<unsigned char,3>,itk::Image<unsigned int,3>>);
|
| 76 |
+
m.def("antsImageCloneUI3", &antsImageClone<itk::Image<unsigned int,3>,itk::Image<unsigned int,3>>);
|
| 77 |
+
m.def("antsImageCloneUI3", &antsImageClone<itk::Image<float,3>,itk::Image<unsigned int,3>>);
|
| 78 |
+
m.def("antsImageCloneUI3", &antsImageClone<itk::Image<double,3>,itk::Image<unsigned int,3>>);
|
| 79 |
+
|
| 80 |
+
m.def("antsImageCloneF3", &antsImageClone<itk::Image<unsigned char,3>,itk::Image<float,3>>);
|
| 81 |
+
m.def("antsImageCloneF3", &antsImageClone<itk::Image<unsigned int,3>,itk::Image<float,3>>);
|
| 82 |
+
m.def("antsImageCloneF3", &antsImageClone<itk::Image<float,3>,itk::Image<float,3>>);
|
| 83 |
+
m.def("antsImageCloneF3", &antsImageClone<itk::Image<double,3>,itk::Image<float,3>>);
|
| 84 |
+
|
| 85 |
+
m.def("antsImageCloneD3", &antsImageClone<itk::Image<unsigned char,3>,itk::Image<double,3>>);
|
| 86 |
+
m.def("antsImageCloneD3", &antsImageClone<itk::Image<unsigned int,3>,itk::Image<double,3>>);
|
| 87 |
+
m.def("antsImageCloneD3", &antsImageClone<itk::Image<float,3>,itk::Image<double,3>>);
|
| 88 |
+
m.def("antsImageCloneD3", &antsImageClone<itk::Image<double,3>,itk::Image<double,3>>);
|
| 89 |
+
|
| 90 |
+
m.def("antsImageCloneRGBUC3", &antsImageClone<itk::Image<itk::RGBPixel<unsigned char>,3>,itk::Image<itk::RGBPixel<unsigned char>,3>>);
|
| 91 |
+
|
| 92 |
+
// dim = 4
|
| 93 |
+
|
| 94 |
+
m.def("antsImageCloneUC4", &antsImageClone<itk::Image<unsigned char,4>,itk::Image<unsigned char,4>>);
|
| 95 |
+
m.def("antsImageCloneUC4", &antsImageClone<itk::Image<unsigned int,4>,itk::Image<unsigned char,4>>);
|
| 96 |
+
m.def("antsImageCloneUC4", &antsImageClone<itk::Image<float,4>,itk::Image<unsigned char,4>>);
|
| 97 |
+
m.def("antsImageCloneUC4", &antsImageClone<itk::Image<double,4>,itk::Image<unsigned char,4>>);
|
| 98 |
+
|
| 99 |
+
m.def("antsImageCloneUI4", &antsImageClone<itk::Image<unsigned char,4>,itk::Image<unsigned int,4>>);
|
| 100 |
+
m.def("antsImageCloneUI4", &antsImageClone<itk::Image<unsigned int,4>,itk::Image<unsigned int,4>>);
|
| 101 |
+
m.def("antsImageCloneUI4", &antsImageClone<itk::Image<float,4>,itk::Image<unsigned int,4>>);
|
| 102 |
+
m.def("antsImageCloneUI4", &antsImageClone<itk::Image<double,4>,itk::Image<unsigned int,4>>);
|
| 103 |
+
|
| 104 |
+
m.def("antsImageCloneF4", &antsImageClone<itk::Image<unsigned char,4>,itk::Image<float,4>>);
|
| 105 |
+
m.def("antsImageCloneF4", &antsImageClone<itk::Image<unsigned int,4>,itk::Image<float,4>>);
|
| 106 |
+
m.def("antsImageCloneF4", &antsImageClone<itk::Image<float,4>,itk::Image<float,4>>);
|
| 107 |
+
m.def("antsImageCloneF4", &antsImageClone<itk::Image<double,4>,itk::Image<float,4>>);
|
| 108 |
+
|
| 109 |
+
m.def("antsImageCloneD4", &antsImageClone<itk::Image<unsigned char,4>,itk::Image<double,4>>);
|
| 110 |
+
m.def("antsImageCloneD4", &antsImageClone<itk::Image<unsigned int,4>,itk::Image<double,4>>);
|
| 111 |
+
m.def("antsImageCloneD4", &antsImageClone<itk::Image<float,4>,itk::Image<double,4>>);
|
| 112 |
+
m.def("antsImageCloneD4", &antsImageClone<itk::Image<double,4>,itk::Image<double,4>>);
|
| 113 |
+
|
| 114 |
+
m.def("antsImageCloneRGBUC4", &antsImageClone<itk::Image<itk::RGBPixel<unsigned char>,4>,itk::Image<itk::RGBPixel<unsigned char>,4>>);
|
| 115 |
+
}
|
MindEyeV2/antspy/src/antsTransform.cxx
ADDED
|
@@ -0,0 +1,312 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
|
| 2 |
+
#include <nanobind/nanobind.h>
|
| 3 |
+
#include <nanobind/stl/vector.h>
|
| 4 |
+
#include <nanobind/stl/string.h>
|
| 5 |
+
#include <nanobind/stl/tuple.h>
|
| 6 |
+
#include <nanobind/stl/list.h>
|
| 7 |
+
#include <nanobind/ndarray.h>
|
| 8 |
+
#include <nanobind/stl/shared_ptr.h>
|
| 9 |
+
|
| 10 |
+
#include <algorithm>
|
| 11 |
+
#include <vector>
|
| 12 |
+
#include <string>
|
| 13 |
+
|
| 14 |
+
#include "itkMacro.h"
|
| 15 |
+
#include "itkImage.h"
|
| 16 |
+
#include "itkVectorImage.h"
|
| 17 |
+
#include "itkVector.h"
|
| 18 |
+
#include "itkImageRegionIteratorWithIndex.h"
|
| 19 |
+
#include "vnl/vnl_vector_ref.h"
|
| 20 |
+
#include "itkTransform.h"
|
| 21 |
+
#include "itkAffineTransform.h"
|
| 22 |
+
|
| 23 |
+
#include "itkAffineTransform.h"
|
| 24 |
+
#include "itkAffineTransform.h"
|
| 25 |
+
#include "itkCenteredAffineTransform.h"
|
| 26 |
+
#include "itkEuler2DTransform.h"
|
| 27 |
+
#include "itkEuler3DTransform.h"
|
| 28 |
+
#include "itkRigid2DTransform.h"
|
| 29 |
+
#include "itkRigid3DTransform.h"
|
| 30 |
+
#include "itkCenteredRigid2DTransform.h"
|
| 31 |
+
#include "itkCenteredEuler3DTransform.h"
|
| 32 |
+
#include "itkSimilarity2DTransform.h"
|
| 33 |
+
#include "itkCenteredSimilarity2DTransform.h"
|
| 34 |
+
#include "itkSimilarity3DTransform.h"
|
| 35 |
+
#include "itkQuaternionRigidTransform.h"
|
| 36 |
+
#include "itkTranslationTransform.h"
|
| 37 |
+
#include "itkResampleImageFilter.h"
|
| 38 |
+
#include "itkTransformFileReader.h"
|
| 39 |
+
#include "itkCompositeTransform.h"
|
| 40 |
+
#include "itkMatrixOffsetTransformBase.h"
|
| 41 |
+
#include "itkDisplacementFieldTransform.h"
|
| 42 |
+
#include "itkConstantBoundaryCondition.h"
|
| 43 |
+
|
| 44 |
+
#include "itkBSplineInterpolateImageFunction.h"
|
| 45 |
+
#include "itkLinearInterpolateImageFunction.h"
|
| 46 |
+
#include "itkGaussianInterpolateImageFunction.h"
|
| 47 |
+
#include "itkInterpolateImageFunction.h"
|
| 48 |
+
#include "itkNearestNeighborInterpolateImageFunction.h"
|
| 49 |
+
#include "itkWindowedSincInterpolateImageFunction.h"
|
| 50 |
+
#include "itkLabelImageGaussianInterpolateImageFunction.h"
|
| 51 |
+
#include "itkTransformFileWriter.h"
|
| 52 |
+
|
| 53 |
+
#include "itkMacro.h"
|
| 54 |
+
#include "itkImage.h"
|
| 55 |
+
#include "itkVectorImage.h"
|
| 56 |
+
#include "itkVector.h"
|
| 57 |
+
#include "itkImageRegionIteratorWithIndex.h"
|
| 58 |
+
#include "vnl/vnl_vector_ref.h"
|
| 59 |
+
#include "itkTransform.h"
|
| 60 |
+
#include "itkAffineTransform.h"
|
| 61 |
+
|
| 62 |
+
#include "antscore/antsUtilities.h"
|
| 63 |
+
|
| 64 |
+
#include "antsTransform.h"
|
| 65 |
+
#include "antsImage.h"
|
| 66 |
+
|
| 67 |
+
namespace nb = nanobind;
|
| 68 |
+
using namespace nb::literals;
|
| 69 |
+
|
| 70 |
+
template <typename TransformType, typename VectorImageType, typename PrecisionType, unsigned int Dimension>
|
| 71 |
+
AntsTransform<TransformType> antsTransformFromDisplacementField( AntsImage<VectorImageType> & field )
|
| 72 |
+
{
|
| 73 |
+
//typedef itk::Transform<PrecisionType,Dimension,Dimension> TransformType;
|
| 74 |
+
typedef typename TransformType::Pointer TransformPointerType;
|
| 75 |
+
typedef typename itk::DisplacementFieldTransform<PrecisionType, Dimension> DisplacementFieldTransformType;
|
| 76 |
+
typedef typename DisplacementFieldTransformType::DisplacementFieldType DisplacementFieldType;
|
| 77 |
+
typedef typename DisplacementFieldType::PixelType VectorType;
|
| 78 |
+
|
| 79 |
+
// Displacement field is an itk::Image with vector pixels, while in ANTsR we use the
|
| 80 |
+
// itk::VectorImage class for multichannel data. So we must copy the field
|
| 81 |
+
// and pass it to the transform
|
| 82 |
+
//typedef itk::VectorImage<PrecisionType, Dimension> AntsrFieldType;
|
| 83 |
+
//typedef typename AntsrFieldType::Pointer AntsrFieldPointerType;
|
| 84 |
+
typedef typename VectorImageType::Pointer VectorImagePointerType;
|
| 85 |
+
VectorImagePointerType antsrField = field.ptr;
|
| 86 |
+
|
| 87 |
+
typename DisplacementFieldType::Pointer itkField = DisplacementFieldType::New();
|
| 88 |
+
itkField->SetRegions( antsrField->GetLargestPossibleRegion() );
|
| 89 |
+
itkField->SetSpacing( antsrField->GetSpacing() );
|
| 90 |
+
itkField->SetOrigin( antsrField->GetOrigin() );
|
| 91 |
+
itkField->SetDirection( antsrField->GetDirection() );
|
| 92 |
+
itkField->AllocateInitialized();
|
| 93 |
+
|
| 94 |
+
typedef itk::ImageRegionIteratorWithIndex<DisplacementFieldType> IteratorType;
|
| 95 |
+
IteratorType it( itkField, itkField->GetLargestPossibleRegion() );
|
| 96 |
+
while ( !it.IsAtEnd() )
|
| 97 |
+
{
|
| 98 |
+
typename VectorImageType::PixelType vec = antsrField->GetPixel( it.GetIndex() );
|
| 99 |
+
VectorType dvec;
|
| 100 |
+
for ( unsigned int i=0; i<Dimension; i++)
|
| 101 |
+
{
|
| 102 |
+
dvec[i] = vec[i];
|
| 103 |
+
}
|
| 104 |
+
itkField->SetPixel(it.GetIndex(), dvec);
|
| 105 |
+
++it;
|
| 106 |
+
}
|
| 107 |
+
|
| 108 |
+
typename DisplacementFieldTransformType::Pointer displacementTransform =
|
| 109 |
+
DisplacementFieldTransformType::New();
|
| 110 |
+
displacementTransform->SetDisplacementField( itkField );
|
| 111 |
+
|
| 112 |
+
/*
|
| 113 |
+
TransformPointerType transform = dynamic_cast<TransformType *>( displacementTransform.GetPointer() );
|
| 114 |
+
|
| 115 |
+
Rcpp::S4 antsrTransform( "antsrTransform" );
|
| 116 |
+
antsrTransform.slot("dimension") = Dimension;
|
| 117 |
+
antsrTransform.slot("precision") = precision;
|
| 118 |
+
std::string type = displacementTransform->GetNameOfClass();
|
| 119 |
+
antsrTransform.slot("type") = type;
|
| 120 |
+
TransformPointerType * rawPointer = new TransformPointerType( transform );
|
| 121 |
+
Rcpp::XPtr<TransformPointerType> xptr( rawPointer, true );
|
| 122 |
+
antsrTransform.slot("pointer") = xptr;
|
| 123 |
+
|
| 124 |
+
return antsrTransform;
|
| 125 |
+
*/
|
| 126 |
+
AntsTransform<TransformType> outTransform = { displacementTransform.GetPointer() };
|
| 127 |
+
return outTransform;
|
| 128 |
+
}
|
| 129 |
+
|
| 130 |
+
template <typename TransformType, typename VectorImageType, typename PrecisionType, unsigned int Dimension>
|
| 131 |
+
AntsImage<VectorImageType> antsTransformToDisplacementField( AntsTransform<TransformType> & xfrm,
|
| 132 |
+
AntsImage<itk::Image<PrecisionType, Dimension>> & ref )
|
| 133 |
+
{
|
| 134 |
+
//typedef itk::Transform<PrecisionType,Dimension,Dimension> TransformType;
|
| 135 |
+
using ImageType = typename itk::Image<PrecisionType, Dimension>;
|
| 136 |
+
using ImagePointerType = typename ImageType::Pointer;
|
| 137 |
+
using TransformPointerType = typename TransformType::Pointer;
|
| 138 |
+
using DisplacementFieldTransformType = typename itk::DisplacementFieldTransform<PrecisionType, VectorImageType::ImageDimension>;
|
| 139 |
+
using DisplacementFieldTransformPointerType = typename DisplacementFieldTransformType::Pointer;
|
| 140 |
+
using DisplacementFieldType = typename DisplacementFieldTransformType::DisplacementFieldType;
|
| 141 |
+
using VectorType = typename DisplacementFieldType::PixelType;
|
| 142 |
+
|
| 143 |
+
TransformPointerType itkTransform = xfrm.ptr;
|
| 144 |
+
DisplacementFieldTransformPointerType warp = dynamic_cast<DisplacementFieldTransformType *>( itkTransform.GetPointer() ) ;
|
| 145 |
+
|
| 146 |
+
ImagePointerType domainImage = ref.ptr;
|
| 147 |
+
|
| 148 |
+
typedef typename VectorImageType::Pointer VectorImagePointerType;
|
| 149 |
+
VectorImagePointerType antsrField = VectorImageType::New();
|
| 150 |
+
antsrField->CopyInformation( domainImage );
|
| 151 |
+
antsrField->SetRegions( domainImage->GetLargestPossibleRegion() );
|
| 152 |
+
antsrField->SetNumberOfComponentsPerPixel( Dimension );
|
| 153 |
+
antsrField->AllocateInitialized();
|
| 154 |
+
|
| 155 |
+
typedef itk::ImageRegionIteratorWithIndex<ImageType> IteratorType;
|
| 156 |
+
IteratorType it( domainImage, domainImage->GetLargestPossibleRegion() );
|
| 157 |
+
while ( !it.IsAtEnd() )
|
| 158 |
+
{
|
| 159 |
+
VectorType vec = warp->GetDisplacementField()->GetPixel( it.GetIndex() );
|
| 160 |
+
typename VectorImageType::PixelType dvec;
|
| 161 |
+
dvec.SetSize( Dimension );
|
| 162 |
+
for( unsigned int i = 0; i < Dimension; i++ )
|
| 163 |
+
{
|
| 164 |
+
dvec[i] = vec[i];
|
| 165 |
+
}
|
| 166 |
+
antsrField->SetPixel( it.GetIndex(), dvec );
|
| 167 |
+
++it;
|
| 168 |
+
}
|
| 169 |
+
|
| 170 |
+
AntsImage<VectorImageType> outImage = { antsrField };
|
| 171 |
+
return outImage;
|
| 172 |
+
}
|
| 173 |
+
|
| 174 |
+
void local_antsTransform(nb::module_ &m) {
|
| 175 |
+
|
| 176 |
+
m.def("getTransformParameters", &getTransformParameters<itk::Transform<float, 2, 2>>);
|
| 177 |
+
m.def("getTransformParameters", &getTransformParameters<itk::Transform<float, 3, 3>>);
|
| 178 |
+
m.def("getTransformParameters", &getTransformParameters<itk::Transform<float, 4, 4>>);
|
| 179 |
+
m.def("getTransformParameters", &getTransformParameters<itk::Transform<double,2, 2>>);
|
| 180 |
+
m.def("getTransformParameters", &getTransformParameters<itk::Transform<double,3, 3>>);
|
| 181 |
+
m.def("getTransformParameters", &getTransformParameters<itk::Transform<double,4, 4>>);
|
| 182 |
+
|
| 183 |
+
m.def("setTransformParameters", &setTransformParameters<itk::Transform<float, 2, 2>>);
|
| 184 |
+
m.def("setTransformParameters", &setTransformParameters<itk::Transform<float, 3, 3>>);
|
| 185 |
+
m.def("setTransformParameters", &setTransformParameters<itk::Transform<float, 4, 4>>);
|
| 186 |
+
m.def("setTransformParameters", &setTransformParameters<itk::Transform<double,2, 2>>);
|
| 187 |
+
m.def("setTransformParameters", &setTransformParameters<itk::Transform<double,3, 3>>);
|
| 188 |
+
m.def("setTransformParameters", &setTransformParameters<itk::Transform<double,4, 4>>);
|
| 189 |
+
|
| 190 |
+
m.def("getTransformFixedParameters", &getTransformFixedParameters<itk::Transform<float, 2, 2>>);
|
| 191 |
+
m.def("getTransformFixedParameters", &getTransformFixedParameters<itk::Transform<float, 3, 3>>);
|
| 192 |
+
m.def("getTransformFixedParameters", &getTransformFixedParameters<itk::Transform<float, 4, 4>>);
|
| 193 |
+
m.def("getTransformFixedParameters", &getTransformFixedParameters<itk::Transform<double,2, 2>>);
|
| 194 |
+
m.def("getTransformFixedParameters", &getTransformFixedParameters<itk::Transform<double,3, 3>>);
|
| 195 |
+
m.def("getTransformFixedParameters", &getTransformFixedParameters<itk::Transform<double,4, 4>>);
|
| 196 |
+
|
| 197 |
+
m.def("setTransformFixedParameters", &setTransformFixedParameters<itk::Transform<float, 2, 2>>);
|
| 198 |
+
m.def("setTransformFixedParameters", &setTransformFixedParameters<itk::Transform<float, 3, 3>>);
|
| 199 |
+
m.def("setTransformFixedParameters", &setTransformFixedParameters<itk::Transform<float, 4, 4>>);
|
| 200 |
+
m.def("setTransformFixedParameters", &setTransformFixedParameters<itk::Transform<double,2, 2>>);
|
| 201 |
+
m.def("setTransformFixedParameters", &setTransformFixedParameters<itk::Transform<double,3, 3>>);
|
| 202 |
+
m.def("setTransformFixedParameters", &setTransformFixedParameters<itk::Transform<double,4, 4>>);
|
| 203 |
+
|
| 204 |
+
|
| 205 |
+
m.def("transformPoint", &transformPoint<itk::DisplacementFieldTransform<float, 2>>);
|
| 206 |
+
m.def("transformPoint", &transformPoint<itk::DisplacementFieldTransform<float, 3>>);
|
| 207 |
+
m.def("transformPoint", &transformPoint<itk::Transform<float, 2, 2>>);
|
| 208 |
+
m.def("transformPoint", &transformPoint<itk::Transform<float, 3, 3>>);
|
| 209 |
+
m.def("transformPoint", &transformPoint<itk::Transform<float, 4, 4>>);
|
| 210 |
+
m.def("transformPoint", &transformPoint<itk::Transform<double,2, 2>>);
|
| 211 |
+
m.def("transformPoint", &transformPoint<itk::Transform<double,3, 3>>);
|
| 212 |
+
m.def("transformPoint", &transformPoint<itk::Transform<double,4, 4>>);
|
| 213 |
+
|
| 214 |
+
m.def("transformVector", &transformVector<itk::Transform<float, 2, 2>>);
|
| 215 |
+
m.def("transformVector", &transformVector<itk::Transform<float, 3, 3>>);
|
| 216 |
+
m.def("transformVector", &transformVector<itk::Transform<float, 4, 4>>);
|
| 217 |
+
m.def("transformVector", &transformVector<itk::Transform<double,2, 2>>);
|
| 218 |
+
m.def("transformVector", &transformVector<itk::Transform<double,3, 3>>);
|
| 219 |
+
m.def("transformVector", &transformVector<itk::Transform<double,4, 4>>);
|
| 220 |
+
|
| 221 |
+
m.def("transformImage", &transformImage<itk::Transform<float, 2, 2>, itk::Image<unsigned char, 2>>);
|
| 222 |
+
m.def("transformImage", &transformImage<itk::Transform<float, 3, 3>, itk::Image<unsigned char, 3>>);
|
| 223 |
+
m.def("transformImage", &transformImage<itk::Transform<float, 4, 4>, itk::Image<unsigned char, 4>>);
|
| 224 |
+
m.def("transformImage", &transformImage<itk::Transform<double,2, 2>, itk::Image<unsigned char, 2>>);
|
| 225 |
+
m.def("transformImage", &transformImage<itk::Transform<double,3, 3>, itk::Image<unsigned char, 3>>);
|
| 226 |
+
m.def("transformImage", &transformImage<itk::Transform<double,4, 4>, itk::Image<unsigned char, 4>>);
|
| 227 |
+
|
| 228 |
+
m.def("transformImage", &transformImage<itk::Transform<float, 2, 2>, itk::Image<unsigned int, 2>>);
|
| 229 |
+
m.def("transformImage", &transformImage<itk::Transform<float, 3, 3>, itk::Image<unsigned int, 3>>);
|
| 230 |
+
m.def("transformImage", &transformImage<itk::Transform<float, 4, 4>, itk::Image<unsigned int, 4>>);
|
| 231 |
+
m.def("transformImage", &transformImage<itk::Transform<double,2, 2>, itk::Image<unsigned int, 2>>);
|
| 232 |
+
m.def("transformImage", &transformImage<itk::Transform<double,3, 3>, itk::Image<unsigned int, 3>>);
|
| 233 |
+
m.def("transformImage", &transformImage<itk::Transform<double,4, 4>, itk::Image<unsigned int, 4>>);
|
| 234 |
+
|
| 235 |
+
m.def("transformImage", &transformImage<itk::Transform<float, 2, 2>, itk::Image<float, 2>>);
|
| 236 |
+
m.def("transformImage", &transformImage<itk::Transform<float, 3, 3>, itk::Image<float, 3>>);
|
| 237 |
+
m.def("transformImage", &transformImage<itk::Transform<float, 4, 4>, itk::Image<float, 4>>);
|
| 238 |
+
m.def("transformImage", &transformImage<itk::Transform<double,2, 2>, itk::Image<float, 2>>);
|
| 239 |
+
m.def("transformImage", &transformImage<itk::Transform<double,3, 3>, itk::Image<float, 3>>);
|
| 240 |
+
m.def("transformImage", &transformImage<itk::Transform<double,4, 4>, itk::Image<float, 4>>);
|
| 241 |
+
|
| 242 |
+
m.def("transformImage", &transformImage<itk::Transform<float, 2, 2>, itk::Image<double, 2>>);
|
| 243 |
+
m.def("transformImage", &transformImage<itk::Transform<float, 3, 3>, itk::Image<double, 3>>);
|
| 244 |
+
m.def("transformImage", &transformImage<itk::Transform<float, 4, 4>, itk::Image<double, 4>>);
|
| 245 |
+
m.def("transformImage", &transformImage<itk::Transform<double,2, 2>, itk::Image<double, 2>>);
|
| 246 |
+
m.def("transformImage", &transformImage<itk::Transform<double,3, 3>, itk::Image<double, 3>>);
|
| 247 |
+
m.def("transformImage", &transformImage<itk::Transform<double,4, 4>, itk::Image<double, 4>>);
|
| 248 |
+
|
| 249 |
+
// displacement field transforms
|
| 250 |
+
m.def("transformImage", &transformImage<itk::DisplacementFieldTransform<float, 2>, itk::Image<unsigned char, 2>>);
|
| 251 |
+
m.def("transformImage", &transformImage<itk::DisplacementFieldTransform<float, 3>, itk::Image<unsigned char, 3>>);
|
| 252 |
+
m.def("transformImage", &transformImage<itk::DisplacementFieldTransform<float, 2>, itk::Image<unsigned int, 2>>);
|
| 253 |
+
m.def("transformImage", &transformImage<itk::DisplacementFieldTransform<float, 3>, itk::Image<unsigned int, 3>>);
|
| 254 |
+
m.def("transformImage", &transformImage<itk::DisplacementFieldTransform<float, 2>, itk::Image<float, 2>>);
|
| 255 |
+
m.def("transformImage", &transformImage<itk::DisplacementFieldTransform<float, 3>, itk::Image<float, 3>>);
|
| 256 |
+
m.def("transformImage", &transformImage<itk::DisplacementFieldTransform<float, 2>, itk::Image<double, 2>>);
|
| 257 |
+
m.def("transformImage", &transformImage<itk::DisplacementFieldTransform<float, 3>, itk::Image<double, 3>>);
|
| 258 |
+
|
| 259 |
+
m.def("inverseTransform", &inverseTransform<itk::Transform<float, 2, 2>, itk::Transform<float, 2, 2>>);
|
| 260 |
+
m.def("inverseTransform", &inverseTransform<itk::Transform<float, 3, 3>, itk::Transform<float, 3, 3>>);
|
| 261 |
+
m.def("inverseTransform", &inverseTransform<itk::Transform<float, 4, 4>, itk::Transform<float, 4, 4>>);
|
| 262 |
+
m.def("inverseTransform", &inverseTransform<itk::Transform<double,2, 2>, itk::Transform<double,2, 2>>);
|
| 263 |
+
m.def("inverseTransform", &inverseTransform<itk::Transform<double,3, 3>, itk::Transform<double,3, 3>>);
|
| 264 |
+
m.def("inverseTransform", &inverseTransform<itk::Transform<double,4, 4>, itk::Transform<double,4, 4>>);
|
| 265 |
+
|
| 266 |
+
m.def("composeTransformsF2", &composeTransforms<itk::Transform<float, 2, 2>, float, 2>);
|
| 267 |
+
m.def("composeTransformsF3", &composeTransforms<itk::Transform<float, 3, 3>, float, 3>);
|
| 268 |
+
m.def("composeTransformsF4", &composeTransforms<itk::Transform<float, 4, 4>, float, 4>);
|
| 269 |
+
m.def("composeTransformsD2", &composeTransforms<itk::Transform<double,2, 2>, double,2> );
|
| 270 |
+
m.def("composeTransformsD3", &composeTransforms<itk::Transform<double,3, 3>, double,3> );
|
| 271 |
+
m.def("composeTransformsD4", &composeTransforms<itk::Transform<double,4, 4>, double,4> );
|
| 272 |
+
|
| 273 |
+
m.def("readTransformF2", &readTransform<itk::Transform<float, 2, 2>, float, 2>);
|
| 274 |
+
m.def("readTransformF3", &readTransform<itk::Transform<float, 3, 3>, float, 3>);
|
| 275 |
+
m.def("readTransformF4", &readTransform<itk::Transform<float, 4, 4>, float, 4>);
|
| 276 |
+
m.def("readTransformD2", &readTransform<itk::Transform<double,2, 2>, double,2> );
|
| 277 |
+
m.def("readTransformD3", &readTransform<itk::Transform<double,3, 3>, double,3> );
|
| 278 |
+
m.def("readTransformD4", &readTransform<itk::Transform<double,4, 4>, double,4> );
|
| 279 |
+
|
| 280 |
+
m.def("writeTransform", &writeTransform<itk::Transform<float, 2, 2>>);
|
| 281 |
+
m.def("writeTransform", &writeTransform<itk::Transform<float, 3, 3>>);
|
| 282 |
+
m.def("writeTransform", &writeTransform<itk::Transform<float, 4, 4>>);
|
| 283 |
+
m.def("writeTransform", &writeTransform<itk::Transform<double,2, 2>>);
|
| 284 |
+
m.def("writeTransform", &writeTransform<itk::Transform<double,3, 3>>);
|
| 285 |
+
m.def("writeTransform", &writeTransform<itk::Transform<double,4, 4>>);
|
| 286 |
+
|
| 287 |
+
m.def("matrixOffsetF2", &matrixOffset<itk::Transform<float, 2, 2>, float, 2>);
|
| 288 |
+
m.def("matrixOffsetF3", &matrixOffset<itk::Transform<float, 3, 3>, float, 3>);
|
| 289 |
+
m.def("matrixOffsetF4", &matrixOffset<itk::Transform<float, 4, 4>, float, 4>);
|
| 290 |
+
m.def("matrixOffsetD2", &matrixOffset<itk::Transform<double,2, 2>, double,2>);
|
| 291 |
+
m.def("matrixOffsetD3", &matrixOffset<itk::Transform<double,3, 3>, double,3>);
|
| 292 |
+
m.def("matrixOffsetD4", &matrixOffset<itk::Transform<double,4, 4>, double,4>);
|
| 293 |
+
|
| 294 |
+
m.def("antsTransformFromDisplacementField", &antsTransformFromDisplacementField<itk::DisplacementFieldTransform<float,2>, itk::VectorImage<float,2>,float,2>);
|
| 295 |
+
m.def("antsTransformFromDisplacementField", &antsTransformFromDisplacementField<itk::DisplacementFieldTransform<float,3>, itk::VectorImage<float,3>,float,3>);
|
| 296 |
+
m.def("antsTransformToDisplacementField", &antsTransformToDisplacementField<itk::DisplacementFieldTransform<float,2>, itk::VectorImage<float,2>,float,2>);
|
| 297 |
+
m.def("antsTransformToDisplacementField", &antsTransformToDisplacementField<itk::DisplacementFieldTransform<float,3>, itk::VectorImage<float,3>,float,3>);
|
| 298 |
+
|
| 299 |
+
|
| 300 |
+
nb::class_<AntsTransform<itk::DisplacementFieldTransform<float,2>>>(m, "AntsTransformDF2");
|
| 301 |
+
nb::class_<AntsTransform<itk::DisplacementFieldTransform<float,3>>>(m, "AntsTransformDF3");
|
| 302 |
+
nb::class_<AntsTransform<itk::Transform<float, 2, 2>>>(m, "AntsTransformF22");
|
| 303 |
+
nb::class_<AntsTransform<itk::Transform<float, 3, 3>>>(m, "AntsTransformF33");
|
| 304 |
+
nb::class_<AntsTransform<itk::Transform<float, 4, 4>>>(m, "AntsTransformF44");
|
| 305 |
+
nb::class_<AntsTransform<itk::Transform<double,2, 2>>>(m, "AntsTransformD22");
|
| 306 |
+
nb::class_<AntsTransform<itk::Transform<double,3, 3>>>(m, "AntsTransformD33");
|
| 307 |
+
nb::class_<AntsTransform<itk::Transform<double,4, 4>>>(m, "AntsTransformD44");
|
| 308 |
+
|
| 309 |
+
}
|
| 310 |
+
|
| 311 |
+
|
| 312 |
+
|
MindEyeV2/antspy/src/fsl2antstransform.cxx
ADDED
|
@@ -0,0 +1,172 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
|
| 2 |
+
#include <nanobind/nanobind.h>
|
| 3 |
+
#include <nanobind/stl/vector.h>
|
| 4 |
+
#include <nanobind/stl/string.h>
|
| 5 |
+
#include <nanobind/stl/tuple.h>
|
| 6 |
+
#include <nanobind/stl/list.h>
|
| 7 |
+
#include <nanobind/ndarray.h>
|
| 8 |
+
#include <nanobind/stl/shared_ptr.h>
|
| 9 |
+
|
| 10 |
+
#include <algorithm>
|
| 11 |
+
#include <vector>
|
| 12 |
+
#include <string>
|
| 13 |
+
|
| 14 |
+
#include "itkImage.h"
|
| 15 |
+
#include "itkMatrixOffsetTransformBase.h"
|
| 16 |
+
#include "itkCastImageFilter.h"
|
| 17 |
+
#include "vnl/vnl_matrix_fixed.h"
|
| 18 |
+
#include "vnl/vnl_diag_matrix.h"
|
| 19 |
+
#include "vnl/vnl_vector.h"
|
| 20 |
+
#include "vnl/vnl_det.h"
|
| 21 |
+
#include "vnl/vnl_inverse.h"
|
| 22 |
+
#include "vnl/algo/vnl_real_eigensystem.h"
|
| 23 |
+
#include "vnl/algo/vnl_qr.h"
|
| 24 |
+
|
| 25 |
+
#include "antsTransform.h"
|
| 26 |
+
#include "antsImage.h"
|
| 27 |
+
|
| 28 |
+
#define RAS_TO_FSL 0
|
| 29 |
+
#define FSL_TO_RAS 1
|
| 30 |
+
|
| 31 |
+
namespace nb = nanobind;
|
| 32 |
+
using namespace nb::literals;
|
| 33 |
+
|
| 34 |
+
/**
|
| 35 |
+
* Get a matrix that maps points voxel coordinates to RAS coordinates
|
| 36 |
+
*/
|
| 37 |
+
template< class ImageType, class TransformMatrixType >
|
| 38 |
+
TransformMatrixType GetVoxelSpaceToRASPhysicalSpaceMatrix(typename ImageType::Pointer image)
|
| 39 |
+
{
|
| 40 |
+
// Generate intermediate terms
|
| 41 |
+
vnl_matrix_fixed<double, 3U, 3U> m_dir, m_ras_matrix;
|
| 42 |
+
vnl_diag_matrix_fixed<double, 3U> m_scale, m_lps_to_ras;
|
| 43 |
+
vnl_vector_fixed<double, 3U> v_origin, v_ras_offset;
|
| 44 |
+
|
| 45 |
+
// Compute the matrix
|
| 46 |
+
m_dir = image->GetDirection().GetVnlMatrix();
|
| 47 |
+
m_scale.set(image->GetSpacing().GetVnlVector());
|
| 48 |
+
m_lps_to_ras.set(vnl_vector<double>(ImageType::ImageDimension, 1.0));
|
| 49 |
+
m_lps_to_ras[0] = -1;
|
| 50 |
+
m_lps_to_ras[1] = -1;
|
| 51 |
+
m_ras_matrix = m_lps_to_ras * m_dir * m_scale;
|
| 52 |
+
|
| 53 |
+
// Compute the vector
|
| 54 |
+
v_origin = image->GetOrigin().GetVnlVector();
|
| 55 |
+
v_ras_offset = m_lps_to_ras * v_origin;
|
| 56 |
+
|
| 57 |
+
// Create the larger matrix
|
| 58 |
+
TransformMatrixType mat;
|
| 59 |
+
vnl_vector<double> vcol(ImageType::ImageDimension+1, 1.0);
|
| 60 |
+
vcol.update(v_ras_offset);
|
| 61 |
+
mat.SetIdentity();
|
| 62 |
+
mat.GetVnlMatrix().update(m_ras_matrix);
|
| 63 |
+
mat.GetVnlMatrix().set_column(ImageType::ImageDimension, vcol);
|
| 64 |
+
|
| 65 |
+
return mat;
|
| 66 |
+
}
|
| 67 |
+
|
| 68 |
+
|
| 69 |
+
template< class PixelType, unsigned int Dimension >
|
| 70 |
+
AntsTransform<itk::Transform<double,3,3>> fsl2antstransform( std::vector<std::vector<float> > matrix,
|
| 71 |
+
AntsImage<itk::Image<PixelType, Dimension>> & ants_reference,
|
| 72 |
+
AntsImage<itk::Image<PixelType, Dimension>> & ants_moving,
|
| 73 |
+
int flag )
|
| 74 |
+
{
|
| 75 |
+
typedef vnl_matrix_fixed<double, 4, 4> MatrixType;
|
| 76 |
+
typedef itk::Image<PixelType, Dimension> ImageType;
|
| 77 |
+
typedef itk::Matrix<double, 4,4> TransformMatrixType;
|
| 78 |
+
|
| 79 |
+
typedef itk::AffineTransform<double, 3> AffTran;
|
| 80 |
+
|
| 81 |
+
typedef typename ImageType::Pointer ImagePointerType;
|
| 82 |
+
|
| 83 |
+
typedef itk::Transform<double,3,3> TransformBaseType;
|
| 84 |
+
typedef typename TransformBaseType::Pointer TransformBasePointerType;
|
| 85 |
+
|
| 86 |
+
ImagePointerType ref = ants_reference.ptr;
|
| 87 |
+
ImagePointerType mov = ants_moving.ptr;
|
| 88 |
+
|
| 89 |
+
MatrixType m_fsl, m_spcref, m_spcmov, m_swpref, m_swpmov, mat, m_ref, m_mov;
|
| 90 |
+
|
| 91 |
+
//Rcpp::NumericMatrix matrix(r_matrix);
|
| 92 |
+
for ( unsigned int i=0; i<matrix.size(); i++)
|
| 93 |
+
for ( unsigned int j=0; j<matrix[0].size(); j++)
|
| 94 |
+
m_fsl(i,j) = matrix[i][j];
|
| 95 |
+
|
| 96 |
+
// Set the ref/mov matrices
|
| 97 |
+
m_ref = GetVoxelSpaceToRASPhysicalSpaceMatrix<ImageType, TransformMatrixType>( ref ).GetVnlMatrix();
|
| 98 |
+
m_mov = GetVoxelSpaceToRASPhysicalSpaceMatrix<ImageType, TransformMatrixType>( mov ).GetVnlMatrix();
|
| 99 |
+
|
| 100 |
+
// Set the swap matrices
|
| 101 |
+
m_swpref.set_identity();
|
| 102 |
+
if(vnl_det(m_ref) > 0)
|
| 103 |
+
{
|
| 104 |
+
m_swpref(0,0) = -1.0;
|
| 105 |
+
m_swpref(0,3) = (ref->GetBufferedRegion().GetSize(0) - 1) * ref->GetSpacing()[0];
|
| 106 |
+
}
|
| 107 |
+
|
| 108 |
+
m_swpmov.set_identity();
|
| 109 |
+
if(vnl_det(m_mov) > 0)
|
| 110 |
+
{
|
| 111 |
+
m_swpmov(0,0) = -1.0;
|
| 112 |
+
m_swpmov(0,3) = (mov->GetBufferedRegion().GetSize(0) - 1) * mov->GetSpacing()[0];
|
| 113 |
+
}
|
| 114 |
+
|
| 115 |
+
// Set the spacing matrices
|
| 116 |
+
m_spcref.set_identity();
|
| 117 |
+
m_spcmov.set_identity();
|
| 118 |
+
for(size_t i = 0; i < 3; i++)
|
| 119 |
+
{
|
| 120 |
+
m_spcref(i,i) = ref->GetSpacing()[i];
|
| 121 |
+
m_spcmov(i,i) = mov->GetSpacing()[i];
|
| 122 |
+
}
|
| 123 |
+
|
| 124 |
+
// Compute the output matrix
|
| 125 |
+
//if (flag == FSL_TO_RAS)
|
| 126 |
+
mat = m_mov * vnl_inverse(m_spcmov) * m_swpmov * vnl_inverse(m_fsl) * m_swpref * m_spcref * vnl_inverse(m_ref);
|
| 127 |
+
|
| 128 |
+
// Add access to this
|
| 129 |
+
// NOTE: m_fsl is really m_ras here
|
| 130 |
+
//if (flag == RAS_TO_FSL)
|
| 131 |
+
// mat =
|
| 132 |
+
// vnl_inverse(vnl_inverse(m_swpmov) * m_spcmov* vnl_inverse(m_mov) *
|
| 133 |
+
// m_fsl *
|
| 134 |
+
// m_ref*vnl_inverse(m_spcref)*vnl_inverse(m_swpref));
|
| 135 |
+
|
| 136 |
+
///////////////
|
| 137 |
+
|
| 138 |
+
// Flip the entries that must be flipped
|
| 139 |
+
mat(2,0) *= -1; mat(2,1) *= -1;
|
| 140 |
+
mat(0,2) *= -1; mat(1,2) *= -1;
|
| 141 |
+
mat(0,3) *= -1; mat(1,3) *= -1;
|
| 142 |
+
|
| 143 |
+
// Create an ITK affine transform
|
| 144 |
+
AffTran::Pointer atran = AffTran::New();
|
| 145 |
+
|
| 146 |
+
// Populate its matrix
|
| 147 |
+
AffTran::MatrixType amat = atran->GetMatrix();
|
| 148 |
+
AffTran::OffsetType aoff = atran->GetOffset();
|
| 149 |
+
|
| 150 |
+
for(size_t r = 0; r < 3; r++)
|
| 151 |
+
{
|
| 152 |
+
for(size_t c = 0; c < 3; c++)
|
| 153 |
+
{
|
| 154 |
+
amat(r,c) = mat(r,c);
|
| 155 |
+
}
|
| 156 |
+
aoff[r] = mat(r,3);
|
| 157 |
+
}
|
| 158 |
+
|
| 159 |
+
atran->SetMatrix(amat);
|
| 160 |
+
atran->SetOffset(aoff);
|
| 161 |
+
|
| 162 |
+
TransformBasePointerType itkTransform = dynamic_cast<TransformBaseType*>( atran.GetPointer() );
|
| 163 |
+
|
| 164 |
+
AntsTransform<TransformBaseType> out_ants_tx = { itkTransform };
|
| 165 |
+
return out_ants_tx;
|
| 166 |
+
}
|
| 167 |
+
|
| 168 |
+
|
| 169 |
+
void local_fsl2antstransform(nb::module_ &m)
|
| 170 |
+
{
|
| 171 |
+
m.def("fsl2antstransformF3", &fsl2antstransform<float,3>);
|
| 172 |
+
}
|