dbdicom 0.2.1__py3-none-any.whl → 0.2.3__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of dbdicom might be problematic. Click here for more details.

Files changed (50) hide show
  1. dbdicom/__init__.py +4 -3
  2. dbdicom/create.py +34 -97
  3. dbdicom/dro.py +174 -0
  4. dbdicom/ds/dataset.py +29 -3
  5. dbdicom/ds/types/mr_image.py +18 -7
  6. dbdicom/extensions/__init__.py +10 -0
  7. dbdicom/{wrappers → extensions}/dipy.py +191 -205
  8. dbdicom/extensions/elastix.py +503 -0
  9. dbdicom/extensions/matplotlib.py +107 -0
  10. dbdicom/extensions/numpy.py +271 -0
  11. dbdicom/{wrappers → extensions}/scipy.py +130 -31
  12. dbdicom/{wrappers → extensions}/skimage.py +1 -1
  13. dbdicom/extensions/sklearn.py +243 -0
  14. dbdicom/extensions/vreg.py +1390 -0
  15. dbdicom/external/dcm4che/bin/emf2sf +57 -57
  16. dbdicom/manager.py +70 -36
  17. dbdicom/pipelines.py +66 -0
  18. dbdicom/record.py +266 -43
  19. dbdicom/types/instance.py +17 -3
  20. dbdicom/types/series.py +1900 -404
  21. dbdicom/utils/image.py +152 -21
  22. dbdicom/utils/vreg.py +327 -135
  23. dbdicom-0.2.3.dist-info/METADATA +88 -0
  24. {dbdicom-0.2.1.dist-info → dbdicom-0.2.3.dist-info}/RECORD +27 -41
  25. {dbdicom-0.2.1.dist-info → dbdicom-0.2.3.dist-info}/WHEEL +1 -1
  26. dbdicom/external/__pycache__/__init__.cpython-310.pyc +0 -0
  27. dbdicom/external/__pycache__/__init__.cpython-37.pyc +0 -0
  28. dbdicom/external/dcm4che/__pycache__/__init__.cpython-310.pyc +0 -0
  29. dbdicom/external/dcm4che/__pycache__/__init__.cpython-37.pyc +0 -0
  30. dbdicom/external/dcm4che/bin/__pycache__/__init__.cpython-310.pyc +0 -0
  31. dbdicom/external/dcm4che/bin/__pycache__/__init__.cpython-37.pyc +0 -0
  32. dbdicom/external/dcm4che/lib/linux-x86/libclib_jiio.so +0 -0
  33. dbdicom/external/dcm4che/lib/linux-x86-64/libclib_jiio.so +0 -0
  34. dbdicom/external/dcm4che/lib/linux-x86-64/libopencv_java.so +0 -0
  35. dbdicom/external/dcm4che/lib/solaris-sparc/libclib_jiio.so +0 -0
  36. dbdicom/external/dcm4che/lib/solaris-sparc/libclib_jiio_vis.so +0 -0
  37. dbdicom/external/dcm4che/lib/solaris-sparc/libclib_jiio_vis2.so +0 -0
  38. dbdicom/external/dcm4che/lib/solaris-sparcv9/libclib_jiio.so +0 -0
  39. dbdicom/external/dcm4che/lib/solaris-sparcv9/libclib_jiio_vis.so +0 -0
  40. dbdicom/external/dcm4che/lib/solaris-sparcv9/libclib_jiio_vis2.so +0 -0
  41. dbdicom/external/dcm4che/lib/solaris-x86/libclib_jiio.so +0 -0
  42. dbdicom/external/dcm4che/lib/solaris-x86-64/libclib_jiio.so +0 -0
  43. dbdicom/wrappers/__init__.py +0 -7
  44. dbdicom/wrappers/elastix.py +0 -855
  45. dbdicom/wrappers/numpy.py +0 -119
  46. dbdicom/wrappers/sklearn.py +0 -151
  47. dbdicom/wrappers/vreg.py +0 -273
  48. dbdicom-0.2.1.dist-info/METADATA +0 -276
  49. {dbdicom-0.2.1.dist-info → dbdicom-0.2.3.dist-info}/LICENSE +0 -0
  50. {dbdicom-0.2.1.dist-info → dbdicom-0.2.3.dist-info}/top_level.txt +0 -0
@@ -1,855 +0,0 @@
1
- # pip install SimpleITK-SimpleElastix
2
-
3
- import numpy as np
4
- import SimpleITK as sitk
5
- import dbdicom.wrappers.scipy as scipy
6
-
7
-
8
- def invert_deformation_field(deformation_field, **kwargs):
9
-
10
- # Get arrays for deformation_field
11
- deform, headers = deformation_field.array('SliceLocation', pixels_first=True)
12
-
13
- # Raise an error if the array is empty
14
- if deform is None:
15
- msg = 'The deformation field is an empty series. \n'
16
- msg += 'Please select a valid series and try again.'
17
- raise ValueError(msg)
18
-
19
- # Calculate the inverse
20
- deformation_field.status.message('Calculating inverse..')
21
- deform_inv = _invert_deformation_field(deform, **kwargs)
22
-
23
- # Return as new series
24
- inv = deformation_field.new_sibling(suffix='inverse')
25
- inv.set_array(deform_inv, headers, pixels_first=True)
26
- return inv
27
-
28
-
29
- def coregister_3d_to_3d(moving, fixed,
30
- transformation = 'Affine',
31
- metric = "NormalizedMutualInformation",
32
- final_grid_spacing = 1.0,
33
- _ignore_empty_slices = False, # Do not use for now
34
- ):
35
-
36
- nan = 2**16-1
37
- fixed_map = scipy.map_to(fixed, moving, cval=nan)
38
-
39
- # Get arrays for fixed and moving series
40
- array_fixed, _ = fixed_map.array('SliceLocation', pixels_first=True, first_volume=True)
41
- array_moving, headers_moving = moving.array('SliceLocation', pixels_first=True, first_volume=True)
42
-
43
- # If one of the datasets is empty do nothing
44
- if array_fixed is None or array_moving is None:
45
- return fixed_map
46
-
47
- # Remove temporary overlay
48
- if fixed_map != fixed:
49
- fixed_map.remove()
50
-
51
- # # If time series consider only first time point
52
- # array_fixed = array_fixed[...,0]
53
- # array_moving = array_moving[...,0]
54
- # headers_moving = headers_moving[...,0]
55
-
56
- # Apply coregistration settings
57
- if transformation == 'Rigid':
58
- pars = _default_rigid()
59
- elif transformation == 'Affine':
60
- pars = _default_affine()
61
- else:
62
- pars = _default_bspline()
63
-
64
- pars["Metric"] = [metric]
65
- pars["FinalGridSpacingInPhysicalUnits"] = [str(final_grid_spacing)]
66
- pars["FixedImageDimension"] = ['3']
67
- pars["MovingImageDimension"] = ['3']
68
-
69
- # Coregister fixed and moving slice-by-slice
70
- moving.status.message('Performing coregistration..')
71
- #deformation = np.empty(array_moving.shape + (2,))
72
- ind_fixed = np.where(array_fixed==nan)
73
- array_fixed[ind_fixed] = 0
74
- array_moving[ind_fixed] = 0
75
-
76
- # Don't use for now
77
- # This may lead to an error of too many samples outside moving image buffer
78
- if _ignore_empty_slices:
79
- fixed_mask = _coregistration_mask_3d(array_fixed)
80
- moving_mask = _coregistration_mask_3d(array_moving)
81
- else:
82
- fixed_mask = None
83
- moving_mask = None
84
-
85
- # get this from series affine instead - more robust
86
- slice_spacing = headers_moving[0].SpacingBetweenSlices # needs a custom keyword slice_spacing
87
- if slice_spacing is None:
88
- slice_spacing = headers_moving[0].SliceThickness
89
- pixel_spacing = headers_moving[0].PixelSpacing + [slice_spacing]
90
-
91
- coregistered, deformation = _coregister_arrays(array_fixed, array_moving, pars, pixel_spacing, pixel_spacing, fixed_mask=fixed_mask, moving_mask=moving_mask)
92
-
93
- # Return new series
94
- coreg = moving.new_sibling(suffix='coregistered')
95
- deform = moving.new_sibling(suffix='deformation field')
96
-
97
- coreg.set_array(coregistered, headers_moving, pixels_first=True)
98
- for dim in range(deformation.shape[-1]):
99
- deform.set_array(deformation[...,dim], headers_moving, pixels_first=True)
100
- # deform_size = moving.new_sibling(SeriesDescription = desc + ' [deformation]')
101
- # deform_size.set_array( np.linalg.norm(deformation, axis=-1), headers_moving, pixels_first=True)
102
- moving.status.message('Finished coregistration..')
103
- return coreg, deform
104
-
105
-
106
- def _coregistration_mask_3d(array):
107
- mask = np.zeros(array.shape, np.uint8)
108
- for z in range(array.shape[2]):
109
- if np.count_nonzero(array[:,:,z]) > 0:
110
- mask[:,:,z] = 1
111
- return mask
112
-
113
-
114
- def coregister_2d_to_2d(moving, fixed,
115
- transformation = 'Affine',
116
- metric = "NormalizedMutualInformation",
117
- final_grid_spacing = 1.0,
118
- ):
119
-
120
- background = 2**16-1
121
- fixed_map = scipy.map_to(fixed, moving, cval=background)
122
-
123
- # Get arrays for fixed and moving series
124
- array_fixed, _ = fixed_map.array('SliceLocation', pixels_first=True, first_volume=True)
125
- array_moving, headers_moving = moving.array('SliceLocation', pixels_first=True, first_volume=True)
126
-
127
- # Raise an error if one of the datasets is empty
128
- if array_fixed is None or array_moving is None:
129
- msg = 'One of the series is empty. \n'
130
- msg += 'Please select a non-empty series and try again.'
131
- raise ValueError(msg)
132
-
133
- # Remove temporary overlay
134
- if fixed_map != fixed:
135
- fixed_map.remove()
136
-
137
- # If time series consider only first time point
138
- # array_fixed = array_fixed[...,0]
139
- # array_moving = array_moving[...,0]
140
- # headers_moving = headers_moving[...,0]
141
-
142
- # Set background pixels to zero for both images
143
- idx = np.where(array_fixed==background)
144
- array_fixed[idx] = 0
145
- array_moving[idx] = 0
146
-
147
- # Get coregistration settings
148
- if transformation == 'Rigid':
149
- pars = _default_rigid()
150
- elif transformation == 'Affine':
151
- pars = _default_affine()
152
- else:
153
- pars = _default_bspline()
154
- pars["Metric"] = [metric]
155
- pars["FinalGridSpacingInPhysicalUnits"] = [str(final_grid_spacing)]
156
- pars["FixedImageDimension"] = ['2']
157
- pars["MovingImageDimension"] = ['2']
158
-
159
- # Coregister fixed and moving slice-by-slice
160
- deformation = np.empty(array_moving.shape + (2,))
161
- pixel_spacing = headers_moving[0].PixelSpacing
162
- for z in range(array_moving.shape[2]):
163
- moving.status.progress(z+1, array_moving.shape[2], 'Performing coregistration..')
164
- coreg, deform = _coregister_arrays(array_fixed[:,:,z], array_moving[:,:,z], pars, pixel_spacing, pixel_spacing)
165
- deformation[:,:,z,:] = deform
166
- array_moving[:,:,z] = coreg
167
-
168
- # Create new series
169
- coreg = moving.new_sibling(suffix='coregistered')
170
- deform = moving.new_sibling(suffix='deformation field')
171
-
172
- # Save data
173
- coreg.set_array(array_moving, headers_moving, pixels_first=True)
174
- for dim in range(deformation.shape[-1]):
175
- deform.set_array(deformation[...,dim], headers_moving, pixels_first=True)
176
- deform[['WindowCenter', 'WindowWidth']] = [0, 10]
177
-
178
- # Return coregistered image and deformation field
179
- return coreg, deform
180
-
181
-
182
- # ONLY TESTED FOR RIGID TRANSFORMATION
183
- # AFFINE AND DEFORMABLE DOES NOT WORK
184
- def coregister_3d_to_2d(moving_3d, fixed_2d, # moving=3D, fixed=2D
185
- transformation = 'Affine',
186
- metric = "NormalizedMutualInformation",
187
- final_grid_spacing = 1.0,
188
- ):
189
-
190
- nan = 2**16-1
191
- moving_map = scipy.map_to(moving_3d, fixed_2d, cval=nan)
192
-
193
- # Get arrays for fixed and moving series
194
- array_fixed, headers_fixed = fixed_2d.array('SliceLocation', pixels_first=True, first_volume=True)
195
- array_moving, _ = moving_map.array('SliceLocation', pixels_first=True, first_volume=True)
196
-
197
- # If one of the datasets is empty do nothing
198
- if array_fixed is None or array_moving is None:
199
- return moving_map
200
-
201
- # Remove temporary overlay
202
- if moving_map != moving_3d:
203
- moving_map.remove()
204
-
205
- # # If time series consider only first time point
206
- # array_fixed = array_fixed[...,0]
207
- # array_moving = array_moving[...,0]
208
- # headers_fixed = headers_fixed[...,0]
209
-
210
- # Get coregistration settings
211
- if transformation == 'Rigid':
212
- pars = _default_rigid()
213
- elif transformation == 'Affine':
214
- pars = _default_affine()
215
- else:
216
- pars = _default_bspline()
217
-
218
- pars["Metric"] = [metric]
219
- pars["FinalGridSpacingInPhysicalUnits"] = [str(final_grid_spacing)]
220
- pars["FixedImageDimension"] = ['3'] # 2D image must be entered as 3D with 3d dimension = 1
221
- pars["MovingImageDimension"] = ['3']
222
-
223
- # Coregister fixed and moving slice-by-slice
224
- deformation = np.empty(array_moving.shape + (3,))
225
- ind_nan = np.where(array_fixed==nan)
226
- array_fixed[ind_nan] = 0
227
- array_moving[ind_nan] = 0
228
- pixel_spacing = headers_fixed[0].PixelSpacing
229
- slice_spacing = headers_fixed[0].SpacingBetweenSlices # needs a custom keyword slice_spacing
230
- if slice_spacing is None:
231
- slice_spacing = headers_fixed[0].SliceThickness
232
- spacing = pixel_spacing + [slice_spacing]
233
- for z in range(array_fixed.shape[2]):
234
- moving_3d.status.progress(z+1, array_fixed.shape[2], 'Performing coregistration..')
235
- fixed = array_fixed[:,:,z].reshape(array_fixed.shape[:2]+(1,)) # enter as 3d with 3d dim=1
236
- coreg, deform = _coregister_arrays(fixed, array_moving, pars, spacing, spacing)
237
- deformation[:,:,z,:] = np.squeeze(deform) # remove z-dimension of 1 again
238
- array_fixed[:,:,z] = np.squeeze(coreg)
239
-
240
- # Return new series
241
- coreg = moving_3d.new_sibling(suffix='coregistered')
242
- deform = moving_3d.new_sibling(suffix='deformation field')
243
-
244
- coreg.set_array(array_fixed, headers_fixed, pixels_first=True)
245
- for dim in range(deformation.shape[-1]):
246
- deform.set_array(deformation[...,dim], headers_fixed, pixels_first=True)
247
- return coreg, deform
248
-
249
-
250
- # THIS DOES NOT WORK
251
- def coregister_2d_to_3d(moving_2d, fixed_3d, # moving=2D, fixed=3D
252
- transformation = 'Affine',
253
- metric = "NormalizedMutualInformation",
254
- final_grid_spacing = 1.0,
255
- ):
256
-
257
- nan = 2**16-1
258
- fixed_map = scipy.map_to(fixed_3d, moving_2d, cval=nan)
259
-
260
- # Get arrays for fixed and moving series
261
- array_fixed, _ = fixed_map.array('SliceLocation', pixels_first=True, first_volume=True)
262
- array_moving, headers_moving = moving_2d.array('SliceLocation', pixels_first=True, first_volume=True)
263
-
264
- # If one of the datasets is empty do nothing
265
- if array_fixed is None or array_moving is None:
266
- return fixed_map
267
-
268
- # Remove temporary overlay
269
- if fixed_map != fixed_3d:
270
- fixed_map.remove()
271
-
272
- # # If time series consider only first time point
273
- # array_fixed = array_fixed[...,0]
274
- # array_moving = array_moving[...,0]
275
- # headers_moving = headers_moving[...,0]
276
-
277
- # Get coregistration settings
278
- if transformation == 'Rigid':
279
- #pars = _default_rigid()
280
- pars = _default_2d_to_3d()
281
- elif transformation == 'Affine':
282
- #pars = _default_affine()
283
- pars = _default_2d_to_3d()
284
- else:
285
- #pars = _default_bspline()
286
- pars = _default_2d_to_3d()
287
-
288
- pars["Metric"] = [metric]
289
- pars["FinalGridSpacingInPhysicalUnits"] = [str(final_grid_spacing)]
290
- pars["FixedImageDimension"] = ['3']
291
- pars["MovingImageDimension"] = ['3'] # 2D image must be entered as 3D with 3d dimension = 1
292
-
293
- # Coregister fixed and moving slice-by-slice
294
- deformation = np.empty(array_moving.shape + (3,))
295
- ind_nan = np.where(array_fixed==nan)
296
- array_fixed[ind_nan] = 0
297
- array_moving[ind_nan] = 0
298
- pixel_spacing = headers_moving[0].PixelSpacing
299
- slice_spacing = headers_moving[0].SpacingBetweenSlices
300
- if slice_spacing is None:
301
- slice_spacing = headers_moving[0].SliceThickness
302
- spacing = pixel_spacing + [slice_spacing]
303
- for z in range(array_moving.shape[2]):
304
- moving_2d.status.progress(z+1, array_moving.shape[2], 'Performing coregistration..')
305
- moving = array_moving[:,:,z].reshape(array_moving.shape[:2]+(1,)) # enter as 3d with 3d dim=1
306
- coreg, deform = _coregister_arrays(array_fixed, moving, pars, spacing, spacing)
307
- deformation[:,:,z,:] = np.squeeze(deform) # remove z-dimension of 1 again
308
- array_fixed[:,:,z] = np.squeeze(coreg)
309
-
310
- # Create new series
311
- coreg = moving_2d.new_sibling(suffix='coregistered')
312
- deform = moving_2d.new_sibling(suffix='deformation field')
313
-
314
- # Set arrays
315
- coreg.set_array(array_fixed, headers_moving, pixels_first=True)
316
- for dim in range(deformation.shape[-1]):
317
- deform.set_array(deformation[...,dim], headers_moving, pixels_first=True)
318
-
319
- # return coregistered image and deformation field
320
- return coreg, deform
321
-
322
-
323
- def warp(image, deformation_field):
324
-
325
- # Get arrays for image and deformation field
326
- array, headers = image.array('SliceLocation', pixels_first=True, first_volume=True)
327
- array_deform, _ = deformation_field.array('SliceLocation', pixels_first=True)
328
-
329
- # Warp array with deformation field
330
- image.message('Warping image with deformation field..')
331
- array = _warp_arrays(array, array_deform)
332
-
333
- # Return as dbdicom series
334
- warped = image.new_sibling(suffix='warped')
335
- warped.set_array(array, headers, pixels_first=True)
336
- return warped
337
-
338
-
339
-
340
-
341
-
342
- # BELOW HERE NON-DICOM FUNCTIONALITY
343
-
344
-
345
- def _invert_deformation_field(deform: np.ndarray, smooth=False) -> np.ndarray:
346
- # deform must have shape (x,y,z,ndim) with ndim=2 or 3
347
- ndim = deform.shape[-1]
348
- if ndim==3:
349
- return _invert_deformation_field_volume(deform, smooth=smooth)
350
- elif ndim==2:
351
- nslices = deform.shape[2]
352
- for z in range(nslices):
353
- deform[:,:,z,:] = _invert_deformation_field_volume(deform[:,:,z,:], smooth=smooth)
354
- return deform
355
- else:
356
- msg = 'The deformation field must have either 2 or 3 dimensions'
357
- raise ValueError(msg)
358
-
359
-
360
- def _invert_deformation_field_volume(deform: np.ndarray, smooth=False) -> np.ndarray:
361
- deform = sitk.GetImageFromArray(deform, isVector=True)
362
- if smooth:
363
- filter = sitk.InvertDisplacementFieldImageFilter()
364
- filter.EnforceBoundaryConditionOn()
365
- deform_inv = filter.Execute(deform)
366
- else:
367
- deform_inv = sitk.InverseDisplacementField(deform,
368
- size = deform.GetSize(),
369
- outputOrigin = (0.0, 0.0),
370
- outputSpacing = (1.0, 1.0))
371
- return sitk.GetArrayFromImage(deform_inv)
372
-
373
-
374
- def _warp_arrays(array, deformation_field):
375
-
376
- # For this function, image and deformation field must be aligned
377
- if array.shape != deformation_field.shape[:-1]:
378
- msg = 'The dimensions of image and deformation field are not matching up. \n'
379
- msg += 'Please select two series with matching dimensions.'
380
- raise ValueError(msg)
381
-
382
- ndim = deformation_field.shape[-1]
383
- if ndim == 3:
384
- return _warp_volume(array, deformation_field)
385
-
386
- # if the deformation field is 2D, then loop over the slices
387
- elif ndim == 2:
388
- nslices = deformation_field.shape[2]
389
- for z in range(nslices):
390
- array[:,:,z] = _warp_volume(array[:,:,z], deformation_field[:,:,z,:])
391
- return array
392
-
393
- # Raise an error if the deformation field does not have 2 or 3 components
394
- else:
395
- msg = 'The deformation field must have either 2 or 3 dimensions'
396
- raise ValueError(msg)
397
-
398
-
399
-
400
- def _warp_volume(volume, displacement):
401
- # Create an image from the volume numpy array
402
- volume_sitk = sitk.GetImageFromArray(volume)
403
-
404
- # Create a displacement field image from the displacement numpy array
405
- displacement_sitk = sitk.GetImageFromArray(displacement, isVector=True)
406
- displacement_sitk = sitk.Cast(displacement_sitk, sitk.sitkVectorFloat64)
407
-
408
- # Set up the transformation object
409
- displacement_transform = sitk.DisplacementFieldTransform(displacement_sitk)
410
-
411
- # Warp the volume using the displacement field transform
412
- warped_sitk = sitk.Resample(volume_sitk, displacement_transform)
413
-
414
- # Convert the warped image to a numpy array
415
- warped = sitk.GetArrayFromImage(warped_sitk)
416
-
417
- return warped
418
-
419
-
420
- # Would prefer to use Transformix for warping but this does not work
421
- def _wip_warp_volume(array, deformation_field):
422
-
423
- # Convert the numpy arrays to a SimpleITK images
424
- input_image = sitk.GetImageFromArray(array)
425
- displacement_sitk = sitk.GetImageFromArray(deformation_field)
426
-
427
- # Define transform parameter map for transformation with displacement field
428
- parameter_map = _default_bspline()
429
- parameter_map['Transform'] = ['DisplacementFieldTransform']
430
- parameter_map['DisplacementField'] = [displacement_sitk]
431
- parameter_map['ResampleInterpolator'] = ['FinalBSplineInterpolator']
432
- parameter_map['FinalBSplineInterpolationOrder'] = ['3']
433
- parameter_map['ResultImagePixelType'] = ['float']
434
-
435
- # Create the Transformix image filter object
436
- transformix = sitk.TransformixImageFilter()
437
- transformix.SetTransformParameterMap(parameter_map)
438
-
439
- # Set the input and deformation images for the Transformix filter
440
- transformix.SetMovingImage(input_image)
441
-
442
- warped = transformix.Execute()
443
-
444
- # Get the warped image as a SimpleITK image
445
- #warped = transformix.GetResultImage()
446
-
447
- # Convert the warped image to a numpy array
448
- warped = sitk.GetArrayFromImage(warped)
449
-
450
- return warped
451
-
452
-
453
-
454
- def _coregister_arrays(fixed, moving, params, fixed_spacing, moving_spacing, fixed_mask=None, moving_mask=None):
455
- """
456
- Coregister two arrays and return coregistered + deformation field
457
- """
458
-
459
- # Convert numpy arrays to sitk images
460
- moving = sitk.GetImageFromArray(moving)
461
- moving.SetSpacing(moving_spacing)
462
- fixed = sitk.GetImageFromArray(fixed)
463
- fixed.SetSpacing(fixed_spacing)
464
- if moving_mask is not None:
465
- moving_mask = sitk.GetImageFromArray(moving_mask)
466
- moving_mask.SetSpacing(moving_spacing)
467
- #moving_mask.__SetPixelAsUInt8__
468
- if fixed_mask is not None:
469
- fixed_mask = sitk.GetImageFromArray(fixed_mask)
470
- fixed_mask.SetSpacing(fixed_spacing)
471
- #fixed_mask.__SetPixelAsUInt8__
472
-
473
- # Perform registration
474
- elastixImageFilter = sitk.ElastixImageFilter()
475
- elastixImageFilter.LogToConsoleOn() # turn on for debugging
476
- #elastixImageFilter.LogToConsoleOff()
477
- elastixImageFilter.SetFixedImage(fixed)
478
- elastixImageFilter.SetMovingImage(moving)
479
- elastixImageFilter.SetParameterMap(params)
480
- if fixed_mask is not None:
481
- elastixImageFilter.SetFixedMask(fixed_mask)
482
- if moving_mask is not None:
483
- elastixImageFilter.SetMovingMask(moving_mask)
484
- elastixImageFilter.Execute()
485
-
486
- # Calculate deformation field
487
- transformParameterMap = elastixImageFilter.GetTransformParameterMap()
488
- transformixImageFilter = sitk.TransformixImageFilter()
489
- #transformixImageFilter.LogToConsoleOn() # turn on for debugging
490
- transformixImageFilter.LogToConsoleOff()
491
- transformixImageFilter.SetTransformParameterMap(transformParameterMap)
492
- # transformixImageFilter.UpdateLargestPossibleRegion()
493
- transformixImageFilter.ComputeDeformationFieldOn()
494
- transformixImageFilter.SetMovingImage(moving)
495
- transformixImageFilter.Execute()
496
-
497
- # Collect return values
498
- # coregistered = transformixImageFilter.GetResultImage() # same result
499
- coregistered = elastixImageFilter.GetResultImage()
500
- deformation_field = transformixImageFilter.GetDeformationField()
501
-
502
- # Convert sitk Images back to numpy arrays
503
- coregistered = sitk.GetArrayFromImage(coregistered)
504
- deformation_field = sitk.GetArrayFromImage(deformation_field)
505
-
506
- return coregistered, deformation_field
507
-
508
-
509
-
510
-
511
- def _default_bspline():
512
-
513
- p = sitk.GetDefaultParameterMap("bspline")
514
-
515
- # *********************
516
- # * ImageTypes
517
- # *********************
518
- p["FixedInternalImagePixelType"] = ["float"]
519
- p["MovingInternalImagePixelType"] = ["float"]
520
- ## selection based on 3D or 2D image data: newest elastix version does not require input image dimension
521
- p["FixedImageDimension"] = ["2"]
522
- p["MovingImageDimension"] = ["2"]
523
- p["UseDirectionCosines"] = ["true"]
524
- # *********************
525
- # * Components
526
- # *********************
527
- p["Registration"] = ["MultiResolutionRegistration"]
528
- # Image intensities are sampled using an ImageSampler, Interpolator and ResampleInterpolator.
529
- # Image sampler is responsible for selecting points in the image to sample.
530
- # The RandomCoordinate simply selects random positions.
531
- p["ImageSampler"] = ["RandomCoordinate"]
532
- # Interpolator is responsible for interpolating off-grid positions during optimization.
533
- # The BSplineInterpolator with BSplineInterpolationOrder = 1 used here is very fast and uses very little memory
534
- p["Interpolator"] = ["BSplineInterpolator"]
535
- # ResampleInterpolator here chosen to be FinalBSplineInterpolator with FinalBSplineInterpolationOrder = 1
536
- # is used to resample the result image from the moving image once the final transformation has been found.
537
- # This is a one-time step so the additional computational complexity is worth the trade-off for higher image quality.
538
- p["ResampleInterpolator"] = ["FinalBSplineInterpolator"]
539
- p["Resampler"] = ["DefaultResampler"]
540
- # Order of B-Spline interpolation used during registration/optimisation.
541
- # It may improve accuracy if you set this to 3. Never use 0.
542
- # An order of 1 gives linear interpolation. This is in most
543
- # applications a good choice.
544
- p["BSplineInterpolationOrder"] = ["1"]
545
- # Order of B-Spline interpolation used for applying the final
546
- # deformation.
547
- # 3 gives good accuracy; recommended in most cases.
548
- # 1 gives worse accuracy (linear interpolation)
549
- # 0 gives worst accuracy, but is appropriate for binary images
550
- # (masks, segmentations); equivalent to nearest neighbor interpolation.
551
- p["FinalBSplineInterpolationOrder"] = ["3"]
552
- # Pyramids found in Elastix:
553
- # 1) Smoothing -> Smoothing: YES, Downsampling: NO
554
- # 2) Recursive -> Smoothing: YES, Downsampling: YES
555
- # If Recursive is chosen and only # of resolutions is given
556
- # then downsamlping by a factor of 2 (default)
557
- # 3) Shrinking -> Smoothing: NO, Downsampling: YES
558
- # p["FixedImagePyramid"] = ["FixedSmoothingImagePyramid"] # Smoothing requires 3d dimension at least 4 pixels
559
- # p["MovingImagePyramid"] = ["MovingSmoothingImagePyramid"]
560
- p["FixedImagePyramid"] = ["FixedRecursiveImagePyramid"]
561
- p["MovingImagePyramid"] = ["MovingRecursiveImagePyramid"]
562
- p["Optimizer"] = ["AdaptiveStochasticGradientDescent"]
563
- # Whether transforms are combined by composition or by addition.
564
- # In generally, Compose is the best option in most cases.
565
- # It does not influence the results very much.
566
- p["HowToCombineTransforms"] = ["Compose"]
567
- p["Transform"] = ["BSplineTransform"]
568
- # Metric
569
- # p["Metric"] = ["AdvancedMeanSquares"]
570
- p["Metric"] = ["NormalizedMutualInformation"]
571
- # Number of grey level bins in each resolution level,
572
- # for the mutual information. 16 or 32 usually works fine.
573
- # You could also employ a hierarchical strategy:
574
- #(NumberOfHistogramBins 16 32 64)
575
- p["NumberOfHistogramBins"] = ["32"]
576
- # *********************
577
- # * Transformation
578
- # *********************
579
- # The control point spacing of the bspline transformation in
580
- # the finest resolution level. Can be specified for each
581
- # dimension differently. Unit: mm.
582
- # The lower this value, the more flexible the deformation.
583
- # Low values may improve the accuracy, but may also cause
584
- # unrealistic deformations.
585
- # By default the grid spacing is halved after every resolution,
586
- # such that the final grid spacing is obtained in the last
587
- # resolution level.
588
- # The grid spacing here is specified in voxel units.
589
- #(FinalGridSpacingInPhysicalUnits 10.0 10.0)
590
- #(FinalGridSpacingInVoxels 8)
591
- #p["FinalGridSpacingInPhysicalUnits"] = ["50.0"]
592
- p["FinalGridSpacingInPhysicalUnits"] = ["25.0", "25.0"]
593
- # *********************
594
- # * Optimizer settings
595
- # *********************
596
- # The number of resolutions. 1 Is only enough if the expected
597
- # deformations are small. 3 or 4 mostly works fine. For large
598
- # images and large deformations, 5 or 6 may even be useful.
599
- p["NumberOfResolutions"] = ["4"]
600
- p["AutomaticParameterEstimation"] = ["true"]
601
- p["ASGDParameterEstimationMethod"] = ["Original"]
602
- p["MaximumNumberOfIterations"] = ["500"]
603
- # The step size of the optimizer, in mm. By default the voxel size is used.
604
- # which usually works well. In case of unusual high-resolution images
605
- # (eg histology) it is necessary to increase this value a bit, to the size
606
- # of the "smallest visible structure" in the image:
607
- # p["MaximumStepLength"] = ["1.0"]
608
- p["MaximumStepLength"] = ["0.1"]
609
- # *********************
610
- # * Pyramid settings
611
- # *********************
612
- # The downsampling/blurring factors for the image pyramids.
613
- # By default, the images are downsampled by a factor of 2
614
- # compared to the next resolution.
615
- #p["ImagePyramidSchedule"] = ["8 8 4 4 2 2 1 1"]
616
- # *********************
617
- # * Sampler parameters
618
- # *********************
619
- # Number of spatial samples used to compute the mutual
620
- # information (and its derivative) in each iteration.
621
- # With an AdaptiveStochasticGradientDescent optimizer,
622
- # in combination with the two options below, around 2000
623
- # samples may already suffice.
624
- p["NumberOfSpatialSamples"] = ["2048"]
625
- # Refresh these spatial samples in every iteration, and select
626
- # them randomly. See the manual for information on other sampling
627
- # strategies.
628
- p["NewSamplesEveryIteration"] = ["true"]
629
- p["CheckNumberOfSamples"] = ["true"]
630
- # *********************
631
- # * Mask settings
632
- # *********************
633
- # If you use a mask, this option is important.
634
- # If the mask serves as region of interest, set it to false.
635
- # If the mask indicates which pixels are valid, then set it to true.
636
- # If you do not use a mask, the option doesn't matter.
637
- p["ErodeMask"] = ["false"]
638
- p["ErodeFixedMask"] = ["false"]
639
- # *********************
640
- # * Output settings
641
- # *********************
642
- #Default pixel value for pixels that come from outside the picture:
643
- p["DefaultPixelValue"] = ["0"]
644
- # Choose whether to generate the deformed moving image.
645
- # You can save some time by setting this to false, if you are
646
- # not interested in the final deformed moving image, but only
647
- # want to analyze the deformation field for example.
648
- p["WriteResultImage"] = ["true"]
649
- # The pixel type and format of the resulting deformed moving image
650
- p["ResultImagePixelType"] = ["float"]
651
- p["ResultImageFormat"] = ["nii"]
652
-
653
- return p
654
-
655
-
656
-
657
- def _default_affine():
658
-
659
- p = sitk.GetDefaultParameterMap("affine")
660
-
661
- # ImageTypes
662
- p["FixedInternalImagePixelType"] = ["float"]
663
- p["MovingInternalImagePixelType"] = ["float"]
664
- p["FixedImageDimension"] = ['2']
665
- p["MovingImageDimension"] = ['2']
666
- p["UseDirectionCosines"] = ["true"]
667
-
668
- # Components
669
- p["Registration"] = ["MultiResolutionRegistration"]
670
- p["ImageSampler"] = ["Random"]
671
- p["Interpolator"] = ["BSplineInterpolator"]
672
- p["ResampleInterpolator"] = ["FinalBSplineInterpolator"]
673
- p["Resampler"] = ["DefaultResampler"]
674
- p["BSplineInterpolationOrder"] = ["3"]
675
- p["FinalBSplineInterpolationOrder"] = ["3"]
676
- # p["FixedImagePyramid"] = ["FixedSmoothingImagePyramid"] # Smoothing requires 3d dimension at least 4 pixels
677
- # p["MovingImagePyramid"] = ["MovingSmoothingImagePyramid"]
678
- p["FixedImagePyramid"] = ["FixedRecursiveImagePyramid"]
679
- p["MovingImagePyramid"] = ["MovingRecursiveImagePyramid"]
680
-
681
- # p["Metric"] = ["AdvancedMeanSquares"]
682
- p["Metric"] = ["AdvancedMattesMutualInformation"]
683
- p["NumberOfHistogramBins"] = ["32"]
684
-
685
- # Transformation
686
- p["Transform"] = ["AffineTransform"]
687
- p["HowToCombineTransforms"] = ["Compose"]
688
- p["AutomaticTransformInitialization"] = ["false"]
689
- p["FinalGridSpacingInPhysicalUnits"] = ["25.0", "25.0"]
690
-
691
- # Optimizer
692
- p["Optimizer"] = ["AdaptiveStochasticGradientDescent"]
693
- p["NumberOfResolutions"] = ["4"]
694
- p["AutomaticParameterEstimation"] = ["true"]
695
- p["ASGDParameterEstimationMethod"] = ["Original"]
696
- p["MaximumNumberOfIterations"] = ["500"]
697
- p["MaximumStepLength"] = ["1.0"]
698
-
699
- # Pyramid settings
700
- #p["ImagePyramidSchedule"] = ["8 8 4 4 2 2 1 1"]
701
-
702
- # Sampler parameters
703
- p["NumberOfSpatialSamples"] = ["2048"]
704
- p["NewSamplesEveryIteration"] = ["true"]
705
- p["CheckNumberOfSamples"] = ["true"]
706
-
707
- # Mask settings
708
- p["ErodeMask"] = ["true"]
709
- p["ErodeFixedMask"] = ["false"]
710
-
711
- # Output settings
712
- p["DefaultPixelValue"] = ["0"]
713
- p["WriteResultImage"] = ["true"]
714
- p["ResultImagePixelType"] = ["float"]
715
- p["ResultImageFormat"] = ["nii"]
716
-
717
- return p
718
-
719
-
720
-
721
- def _default_rigid():
722
- # https://github.com/SuperElastix/ElastixModelZoo/tree/master/models/Par0064
723
-
724
- p = sitk.GetDefaultParameterMap("rigid")
725
-
726
- # ImageTypes
727
- p["FixedInternalImagePixelType"] = ["float"]
728
- p["MovingInternalImagePixelType"] = ["float"]
729
- p["FixedImageDimension"] = ['2']
730
- p["MovingImageDimension"] = ['2']
731
- p["UseDirectionCosines"] = ["true"]
732
-
733
- # Components
734
- p["Registration"] = ["MultiResolutionRegistration"]
735
- p["ImageSampler"] = ["Random"]
736
- p["Interpolator"] = ["BSplineInterpolator"]
737
- p["ResampleInterpolator"] = ["FinalBSplineInterpolator"]
738
- p["Resampler"] = ["DefaultResampler"]
739
- p["BSplineInterpolationOrder"] = ["1"]
740
- p["FinalBSplineInterpolationOrder"] = ["3"]
741
- # p["FixedImagePyramid"] = ["FixedSmoothingImagePyramid"] # Smoothing requires 3d dimension at least 4 pixels
742
- # p["MovingImagePyramid"] = ["MovingSmoothingImagePyramid"]
743
- p["FixedImagePyramid"] = ["FixedRecursiveImagePyramid"]
744
- p["MovingImagePyramid"] = ["MovingRecursiveImagePyramid"]
745
-
746
- # Pyramid settings
747
- #p["ImagePyramidSchedule"] = ["8 8 4 4 4 2 2 2 1"]
748
- p["Optimizer"] = ["AdaptiveStochasticGradientDescent"]
749
-
750
- # Transformation
751
- p["HowToCombineTransforms"] = ["Compose"]
752
- p["Transform"] = ["EulerTransform"]
753
- p["AutomaticTransformInitialization"] = ["true"]
754
- p["AutomaticTransformInitializationMethod"] = ["GeometricalCenter"]
755
- p["AutomaticScalesEstimation"] = ["true"]
756
- p["Metric"] = ["AdvancedMattesMutualInformation"]
757
- p["NumberOfHistogramBins"] = ["32"]
758
-
759
- # Optimizer settings
760
- p["NumberOfResolutions"] = ["3"]
761
- p["AutomaticParameterEstimation"] = ["true"]
762
- # p["ASGDParameterEstimationMethod"] = ["Original"]
763
- p["MaximumNumberOfIterations"] = ["500"]
764
- p["MaximumStepLength"] = ["1.0"]
765
-
766
- # Sampler parameters
767
- p["NumberOfSpatialSamples"] = ["2048"]
768
- p["NewSamplesEveryIteration"] = ["true"]
769
- p["NumberOfSamplesForExactGradient"] = ["1024"]
770
- p["MaximumNumberOfSamplingAttempts"] = ["15"]
771
- p["CheckNumberOfSamples"] = ["true"]
772
-
773
- # Mask settings
774
- p["ErodeMask"] = ["true"]
775
- # p["ErodeFixedMask"] = ["false"]
776
-
777
- # Output settings
778
- p["DefaultPixelValue"] = ["0"]
779
- p["WriteResultImage"] = ["true"]
780
- p["ResultImagePixelType"] = ["float"]
781
- p["ResultImageFormat"] = ["nii"]
782
-
783
- return p
784
-
785
-
786
- def _default_2d_to_3d():
787
- # modified from:
788
- # https://github.com/SuperElastix/ElastixModelZoo/tree/master/models/Par0013
789
-
790
- p = sitk.GetDefaultParameterMap("rigid")
791
-
792
- p['FixedInternalImagePixelType'] = ["float"]
793
- p['MovingInternalImagePixelType'] = ["float"]
794
- p['FixedImageDimension'] = ['3']
795
- p['MovingImageDimension'] = ['3']
796
- p['UseDirectionCosines"'] = ["false"]
797
-
798
- # **************** Main Components **************************
799
-
800
- p['Registration'] = ["MultiResolutionRegistration"]
801
- #p['Interpolator'] = ["RayCastInterpolator"]
802
- #p['ResampleInterpolator'] = ["FinalRayCastInterpolator"]
803
- p['Resampler'] = ["DefaultResampler"]
804
-
805
- p['FixedImagePyramid'] = ["FixedRecursiveImagePyramid"]
806
- p['MovingImagePyramid'] = ["MovingRecursiveImagePyramid"]
807
-
808
- #p['Optimizer'] = ["Powell"]
809
- p['Transform'] = ["EulerTransform"]
810
- p['Metric'] = ["GradientDifference"]
811
-
812
- # ***************** Transformation **************************
813
-
814
- #p['Scales'] = ['57.3']
815
- #p['AutomaticTransformInitialization'] = ["false"]
816
- p['AutomaticTransformInitialization'] = ["true"]
817
- p["AutomaticTransformInitializationMethod"] = ["GeometricalCenter"]
818
- p["AutomaticScalesEstimation"] = ["true"]
819
- p['HowToCombineTransforms'] = ["Compose"]
820
- p['CenterOfRotationPoint'] = ['0.0 0.0 0.0']
821
-
822
- # ******************* Similarity measure *********************
823
-
824
- p['UseNormalization'] = ["true"]
825
-
826
- # ******************** Multiresolution **********************
827
-
828
- p['NumberOfResolutions'] = ['1']
829
-
830
- # ******************* Optimizer ****************************
831
-
832
- p['MaximumNumberOfIterations'] = ['10']
833
- p['MaximumStepLength'] = ['1.0']
834
- p['StepTolerance'] = ['0.0001']
835
- p['ValueTolerance'] = ['0.000001']
836
-
837
- # **************** Image sampling **********************
838
-
839
- #p['ImageSampler'] = ["Full"]
840
- p['NewSamplesEveryIteration'] = ["false"]
841
-
842
- # ************* Interpolation and Resampling ****************
843
-
844
- #p['Origin'] = ['-145.498 -146.889 381.766']
845
- #p['Interpolator0PreParameters'] = ['-0.009475 -0.006807 -0.030067 0.0 0.0 0.0']
846
- #p['ResampleInterpolator0PreParameters'] = ['-0.009475 -0.006807 -0.030067 0.0 0.0 0.0']
847
- #p['Interpolator0FocalPoint'] = ['0.54 -0.85 -813.234']
848
- #p['ResampleInterpolator0FocalPoint'] = ['0.54 -0.85 -813.234']
849
- p['Threshold'] = ['1000']
850
- p['DefaultPixelValue'] = ['0']
851
- p['WriteResultImage'] = ["true"]
852
- #p['WriteTransformParametersEachIteration'] = ["true"]
853
- p['ResultImagePixelType'] = ["float"]
854
-
855
- return p