1- from copy import deepcopy
2-
31import numpy as np
42from nibabel .affines import apply_affine
53
6- from ....data .image import Image
74from ....data .subject import Subject
85from .bounds_transform import BoundsTransform
96from .bounds_transform import TypeBounds
@@ -27,23 +24,15 @@ class Crop(BoundsTransform):
2724 If only one value :math:`n` is provided, then
2825 :math:`w_{ini} = w_{fin} = h_{ini} = h_{fin}
2926 = d_{ini} = d_{fin} = n`.
30- copy: If ``True``, each image will be cropped and the patch copied to a new
31- subject. If ``False``, each image will be cropped in place. This transform
32- overwrites the copy argument of the base transform and copies only the
33- cropped patch instead of the whole image. This can provide a significant
34- speedup when cropping small patches from large images.
3527 **kwargs: See :class:`~torchio.transforms.Transform` for additional
3628 keyword arguments.
3729
3830 .. seealso:: If you want to pass the output shape instead, please use
3931 :class:`~torchio.transforms.CropOrPad` instead.
4032 """
4133
42- def __init__ (self , cropping : TypeBounds , copy = True , ** kwargs ):
43- self ._copy_patch = copy
44- # Transform base class deepcopies whole subject by default
45- # We want to copy only the cropped patch, so we overwrite the functionality
46- super ().__init__ (cropping , copy = False , ** kwargs )
34+ def __init__ (self , cropping : TypeBounds , ** kwargs ):
35+ super ().__init__ (cropping , ** kwargs )
4736 self .cropping = cropping
4837 self .args_names = ['cropping' ]
4938
@@ -52,76 +41,16 @@ def apply_transform(self, subject: Subject) -> Subject:
5241 low = self .bounds_parameters [::2 ]
5342 high = self .bounds_parameters [1 ::2 ]
5443 index_ini = low
55- index_fin = np .asarray (subject .spatial_shape , dtype = int ) - high
56-
57- if self ._copy_patch :
58- # Create a clean new subject to copy the images into
59- # We do this __new__ to avoid calling __init__ so we don't have to specify images immediately
60- cropped_subject = subject .__class__ .__new__ (subject .__class__ )
61- image_keys_to_crop = subject .get_images_dict (
62- intensity_only = False ,
63- include = self .include ,
64- exclude = self .exclude ,
65- ).keys ()
66- keys_to_expose = subject .keys ()
67- # Copy all attributes we don't want to crop
68- # __dict__ returns all attributes, instead of just the images
69- for key , value in subject .__dict__ .items ():
70- if key not in image_keys_to_crop :
71- copied_value = deepcopy (value )
72- # Setting __dict__ does not allow key indexing the attribute
73- # so we set it explicitly if we want to expose it
74- if key in keys_to_expose :
75- cropped_subject [key ] = copied_value
76- cropped_subject .__dict__ [str (key )] = copied_value
77- else :
78- # Images are always exposed, so we don't worry about setting __dict__
79- cropped_subject [key ] = self ._crop_image (
80- value ,
81- index_ini ,
82- index_fin , # type: ignore[arg-type]
83- copy_patch = self ._copy_patch ,
84- )
85-
86- # Update the __dict__ attribute to include the cropped images
87- cropped_subject .update_attributes ()
88- return cropped_subject
89- else :
90- # Crop in place
91- for image in self .get_images (subject ):
92- self ._crop_image (
93- image ,
94- index_ini ,
95- index_fin , # type: ignore[arg-type]
96- copy_patch = self ._copy_patch ,
97- )
98- return subject
99-
100- @staticmethod
101- def _crop_image (
102- image : Image , index_ini : tuple , index_fin : tuple , * , copy_patch : bool
103- ) -> Image :
104- new_origin = apply_affine (image .affine , index_ini )
105- new_affine = image .affine .copy ()
106- new_affine [:3 , 3 ] = new_origin
107- i0 , j0 , k0 = index_ini
108- i1 , j1 , k1 = index_fin
109-
110- # Crop the image data
111- if copy_patch :
112- # Create a new image with the cropped data
113- cropped_data = image .data [:, i0 :i1 , j0 :j1 , k0 :k1 ].clone ()
114- new_image = type (image )(
115- tensor = cropped_data ,
116- affine = new_affine ,
117- type = image .type ,
118- path = image .path ,
119- )
120- return new_image
121- else :
44+ index_fin = np .array (subject .spatial_shape ) - high
45+ for image in self .get_images (subject ):
46+ new_origin = apply_affine (image .affine , index_ini )
47+ new_affine = image .affine .copy ()
48+ new_affine [:3 , 3 ] = new_origin
49+ i0 , j0 , k0 = index_ini
50+ i1 , j1 , k1 = index_fin
12251 image .set_data (image .data [:, i0 :i1 , j0 :j1 , k0 :k1 ].clone ())
12352 image .affine = new_affine
124- return image
53+ return subject
12554
12655 def inverse (self ):
12756 from .pad import Pad
0 commit comments