ckadirt commited on
Commit
c2de783
·
verified ·
1 Parent(s): 8284ae2

Add files using upload-large-folder tool

Browse files
This view is limited to 50 files because it contains too many changes.   See raw diff
Files changed (50) hide show
  1. MindEyeV2/antspy/ants/contrib/__init__.py +4 -0
  2. MindEyeV2/antspy/ants/contrib/sampling/__init__.py +4 -0
  3. MindEyeV2/antspy/ants/contrib/sampling/affine2d.py +654 -0
  4. MindEyeV2/antspy/ants/contrib/sampling/affine3d.py +802 -0
  5. MindEyeV2/antspy/ants/contrib/sampling/transforms.py +766 -0
  6. MindEyeV2/antspy/ants/contrib/sklearn_interface/__init__.py +3 -0
  7. MindEyeV2/antspy/ants/contrib/sklearn_interface/sklearn_registration.py +149 -0
  8. MindEyeV2/antspy/ants/plotting/__init__.py +8 -0
  9. MindEyeV2/antspy/ants/plotting/movie.py +87 -0
  10. MindEyeV2/antspy/ants/plotting/plot.py +486 -0
  11. MindEyeV2/antspy/ants/plotting/plot_directory.py +118 -0
  12. MindEyeV2/antspy/ants/plotting/plot_grid.py +355 -0
  13. MindEyeV2/antspy/ants/plotting/plot_hist.py +73 -0
  14. MindEyeV2/antspy/ants/plotting/plot_ortho.py +612 -0
  15. MindEyeV2/antspy/ants/plotting/plot_ortho_stack.py +505 -0
  16. MindEyeV2/antspy/ants/utils/__init__.py +15 -0
  17. MindEyeV2/antspy/ants/utils/channels.py +95 -0
  18. MindEyeV2/antspy/ants/utils/consistency.py +73 -0
  19. MindEyeV2/antspy/ants/utils/get_ants_data.py +108 -0
  20. MindEyeV2/antspy/ants/utils/matrix_image.py +202 -0
  21. MindEyeV2/antspy/ants/utils/mni2tal.py +59 -0
  22. MindEyeV2/antspy/ants/utils/ndimage_to_list.py +111 -0
  23. MindEyeV2/antspy/ants/utils/nifti_to_ants.py +39 -0
  24. MindEyeV2/antspy/ants/utils/scalar_rgb_vector.py +92 -0
  25. MindEyeV2/antspy/ants/utils/sitk_to_ants.py +63 -0
  26. MindEyeV2/antspy/docs/Makefile +27 -0
  27. MindEyeV2/antspy/docs/make.bat +36 -0
  28. MindEyeV2/antspy/docs/other/ANTsPy Tutorial.ipynb +0 -0
  29. MindEyeV2/antspy/docs/other/ANTsR_Comparison.md +16 -0
  30. MindEyeV2/antspy/docs/other/All_Functions.md +180 -0
  31. MindEyeV2/antspy/docs/other/_config.yml +1 -0
  32. MindEyeV2/antspy/docs/requirements.txt +2 -0
  33. MindEyeV2/antspy/docs/source/ants.core.rst +62 -0
  34. MindEyeV2/antspy/docs/source/ants.learn.rst +22 -0
  35. MindEyeV2/antspy/docs/source/ants.lib.rst +10 -0
  36. MindEyeV2/antspy/docs/source/ants.rst +35 -0
  37. MindEyeV2/antspy/docs/source/ants.segmentation.rst +78 -0
  38. MindEyeV2/antspy/docs/source/conf.py +233 -0
  39. MindEyeV2/antspy/docs/source/core.rst +57 -0
  40. MindEyeV2/antspy/docs/source/modules.rst +8 -0
  41. MindEyeV2/antspy/docs/source/registration.rst +16 -0
  42. MindEyeV2/antspy/docs/source/setup.rst +7 -0
  43. MindEyeV2/antspy/docs/source/vis.rst +8 -0
  44. MindEyeV2/antspy/src/WRAP_KellyKapowski.cxx +19 -0
  45. MindEyeV2/antspy/src/WRAP_N4BiasFieldCorrection.cxx +19 -0
  46. MindEyeV2/antspy/src/WRAP_ResampleImage.cxx +19 -0
  47. MindEyeV2/antspy/src/antsImage.h +233 -0
  48. MindEyeV2/antspy/src/antsImageClone.cxx +115 -0
  49. MindEyeV2/antspy/src/antsTransform.cxx +312 -0
  50. 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
+ }