From 6620ad7f432462adb274d166ecf86d555f4640bf Mon Sep 17 00:00:00 2001 From: Rich <33289025+rijobro@users.noreply.github.com> Date: Thu, 25 Feb 2021 11:07:05 +0000 Subject: [PATCH 01/13] add randomizable constructor Signed-off-by: Richard Brown <33289025+rijobro@users.noreply.github.com> --- monai/transforms/croppad/dictionary.py | 6 ++- monai/transforms/intensity/array.py | 25 ++++------- monai/transforms/intensity/dictionary.py | 37 ++++++---------- monai/transforms/spatial/array.py | 34 ++++++-------- monai/transforms/spatial/dictionary.py | 56 +++++++++++++----------- monai/transforms/transform.py | 4 ++ 6 files changed, 74 insertions(+), 88 deletions(-) diff --git a/monai/transforms/croppad/dictionary.py b/monai/transforms/croppad/dictionary.py index 20ae6ac1ed..75e4e8cb99 100644 --- a/monai/transforms/croppad/dictionary.py +++ b/monai/transforms/croppad/dictionary.py @@ -283,7 +283,9 @@ def __init__( random_center: bool = True, random_size: bool = True, ) -> None: - super().__init__(keys) + Randomizable.__init__(self, prob=1.0) + MapTransform.__init__(self, keys) + self._do_transform = True self.roi_size = roi_size self.random_center = random_center self.random_size = random_size @@ -534,7 +536,7 @@ def __init__( fg_indices_key: Optional[str] = None, bg_indices_key: Optional[str] = None, ) -> None: - super().__init__(keys) + MapTransform.__init__(self, keys) self.label_key = label_key self.spatial_size: Union[Tuple[int, ...], Sequence[int], int] = spatial_size if pos < 0 or neg < 0: diff --git a/monai/transforms/intensity/array.py b/monai/transforms/intensity/array.py index 40bef064eb..7fc7b23d3c 100644 --- a/monai/transforms/intensity/array.py +++ b/monai/transforms/intensity/array.py @@ -60,10 +60,9 @@ class RandGaussianNoise(Randomizable, Transform): """ def __init__(self, prob: float = 0.1, mean: Union[Sequence[float], float] = 0.0, std: float = 0.1) -> None: - self.prob = prob + Randomizable.__init__(self, prob) self.mean = mean self.std = std - self._do_transform = False self._noise = None def randomize(self, im_shape: Sequence[int]) -> None: @@ -113,6 +112,7 @@ def __init__(self, offsets: Union[Tuple[float, float], float], prob: float = 0.1 if single number, offset value is picked from (-offsets, offsets). prob: probability of shift. """ + Randomizable.__init__(self, prob) if isinstance(offsets, (int, float)): self.offsets = (min(-offsets, offsets), max(-offsets, offsets)) else: @@ -120,9 +120,6 @@ def __init__(self, offsets: Union[Tuple[float, float], float], prob: float = 0.1 raise AssertionError("offsets should be a number or pair of numbers.") self.offsets = (min(offsets), max(offsets)) - self.prob = prob - self._do_transform = False - def randomize(self, data: Optional[Any] = None) -> None: self._offset = self.R.uniform(low=self.offsets[0], high=self.offsets[1]) self._do_transform = self.R.random() < self.prob @@ -186,6 +183,7 @@ def __init__(self, factors: Union[Tuple[float, float], float], prob: float = 0.1 prob: probability of scale. """ + Randomizable.__init__(self, prob) if isinstance(factors, (int, float)): self.factors = (min(-factors, factors), max(-factors, factors)) else: @@ -193,9 +191,6 @@ def __init__(self, factors: Union[Tuple[float, float], float], prob: float = 0.1 raise AssertionError("factors should be a number or pair of numbers.") self.factors = (min(factors), max(factors)) - self.prob = prob - self._do_transform = False - def randomize(self, data: Optional[Any] = None) -> None: self.factor = self.R.uniform(low=self.factors[0], high=self.factors[1]) self._do_transform = self.R.random() < self.prob @@ -243,7 +238,7 @@ def __init__( self.dtype = dtype def _normalize(self, img: np.ndarray, sub=None, div=None) -> np.ndarray: - slices = (img != 0) if self.nonzero else np.ones(img.shape, dtype=np.bool_) + slices = (img != 0) if self.nonzero else np.ones(img.shape, dtype=bool) if not np.any(slices): return img @@ -383,7 +378,7 @@ class RandAdjustContrast(Randomizable, Transform): """ def __init__(self, prob: float = 0.1, gamma: Union[Sequence[float], float] = (0.5, 4.5)) -> None: - self.prob = prob + Randomizable.__init__(self, prob) if isinstance(gamma, (int, float)): if gamma <= 0.5: @@ -396,7 +391,6 @@ def __init__(self, prob: float = 0.1, gamma: Union[Sequence[float], float] = (0. raise AssertionError("gamma should be a number or pair of numbers.") self.gamma = (min(gamma), max(gamma)) - self._do_transform = False self.gamma_value = None def randomize(self, data: Optional[Any] = None) -> None: @@ -679,12 +673,11 @@ def __init__( prob: float = 0.1, approx: str = "erf", ) -> None: + Randomizable.__init__(self, prob) self.sigma_x = sigma_x self.sigma_y = sigma_y self.sigma_z = sigma_z - self.prob = prob self.approx = approx - self._do_transform = False def randomize(self, data: Optional[Any] = None) -> None: self._do_transform = self.R.random_sample() < self.prob @@ -782,6 +775,7 @@ def __init__( approx: str = "erf", prob: float = 0.1, ) -> None: + Randomizable.__init__(self, prob) self.sigma1_x = sigma1_x self.sigma1_y = sigma1_y self.sigma1_z = sigma1_z @@ -790,8 +784,6 @@ def __init__( self.sigma2_z = sigma2_z self.alpha = alpha self.approx = approx - self.prob = prob - self._do_transform = False def randomize(self, data: Optional[Any] = None) -> None: self._do_transform = self.R.random_sample() < self.prob @@ -827,6 +819,7 @@ class RandHistogramShift(Randomizable, Transform): """ def __init__(self, num_control_points: Union[Tuple[int, int], int] = 10, prob: float = 0.1) -> None: + Randomizable.__init__(self, prob) if isinstance(num_control_points, int): if num_control_points <= 2: @@ -838,8 +831,6 @@ def __init__(self, num_control_points: Union[Tuple[int, int], int] = 10, prob: f if min(num_control_points) <= 2: raise AssertionError("num_control_points should be greater than or equal to 3") self.num_control_points = (min(num_control_points), max(num_control_points)) - self.prob = prob - self._do_transform = False def randomize(self, data: Optional[Any] = None) -> None: self._do_transform = self.R.random() < self.prob diff --git a/monai/transforms/intensity/dictionary.py b/monai/transforms/intensity/dictionary.py index 54a85a57b0..deaf6823d6 100644 --- a/monai/transforms/intensity/dictionary.py +++ b/monai/transforms/intensity/dictionary.py @@ -108,11 +108,10 @@ class RandGaussianNoised(Randomizable, MapTransform): def __init__( self, keys: KeysCollection, prob: float = 0.1, mean: Union[Sequence[float], float] = 0.0, std: float = 0.1 ) -> None: - super().__init__(keys) - self.prob = prob + MapTransform.__init__(self, keys) + Randomizable.__init__(self, prob) self.mean = ensure_tuple_rep(mean, len(self.keys)) self.std = std - self._do_transform = False self._noise: List[np.ndarray] = [] def randomize(self, im_shape: Sequence[int]) -> None: @@ -173,7 +172,8 @@ def __init__(self, keys: KeysCollection, offsets: Union[Tuple[float, float], flo prob: probability of rotating. (Default 0.1, with 10% probability it returns a rotated array.) """ - super().__init__(keys) + MapTransform.__init__(self, keys) + Randomizable.__init__(self, prob) if isinstance(offsets, (int, float)): self.offsets = (min(-offsets, offsets), max(-offsets, offsets)) @@ -182,9 +182,6 @@ def __init__(self, keys: KeysCollection, offsets: Union[Tuple[float, float], flo raise AssertionError("offsets should be a number or pair of numbers.") self.offsets = (min(offsets), max(offsets)) - self.prob = prob - self._do_transform = False - def randomize(self, data: Optional[Any] = None) -> None: self._offset = self.R.uniform(low=self.offsets[0], high=self.offsets[1]) self._do_transform = self.R.random() < self.prob @@ -245,7 +242,8 @@ def __init__(self, keys: KeysCollection, factors: Union[Tuple[float, float], flo (Default 0.1, with 10% probability it returns a rotated array.) """ - super().__init__(keys) + MapTransform.__init__(self, keys) + Randomizable.__init__(self, prob) if isinstance(factors, (int, float)): self.factors = (min(-factors, factors), max(-factors, factors)) @@ -254,9 +252,6 @@ def __init__(self, keys: KeysCollection, factors: Union[Tuple[float, float], flo raise AssertionError("factors should be a number or pair of numbers.") self.factors = (min(factors), max(factors)) - self.prob = prob - self._do_transform = False - def randomize(self, data: Optional[Any] = None) -> None: self.factor = self.R.uniform(low=self.factors[0], high=self.factors[1]) self._do_transform = self.R.random() < self.prob @@ -400,8 +395,8 @@ class RandAdjustContrastd(Randomizable, MapTransform): def __init__( self, keys: KeysCollection, prob: float = 0.1, gamma: Union[Tuple[float, float], float] = (0.5, 4.5) ) -> None: - super().__init__(keys) - self.prob: float = prob + MapTransform.__init__(self, keys) + Randomizable.__init__(self, prob) if isinstance(gamma, (int, float)): if gamma <= 0.5: @@ -414,7 +409,6 @@ def __init__( raise AssertionError("gamma should be a number or pair of numbers.") self.gamma = (min(gamma), max(gamma)) - self._do_transform = False self.gamma_value: Optional[float] = None def randomize(self, data: Optional[Any] = None) -> None: @@ -554,13 +548,12 @@ def __init__( approx: str = "erf", prob: float = 0.1, ) -> None: - super().__init__(keys) + MapTransform.__init__(self, keys) + Randomizable.__init__(self, prob) self.sigma_x = sigma_x self.sigma_y = sigma_y self.sigma_z = sigma_z self.approx = approx - self.prob = prob - self._do_transform = False def randomize(self, data: Optional[Any] = None) -> None: self._do_transform = self.R.random_sample() < self.prob @@ -652,7 +645,8 @@ def __init__( approx: str = "erf", prob: float = 0.1, ): - super().__init__(keys) + MapTransform.__init__(self, keys) + Randomizable.__init__(self, prob) self.sigma1_x = sigma1_x self.sigma1_y = sigma1_y self.sigma1_z = sigma1_z @@ -661,8 +655,6 @@ def __init__( self.sigma2_z = sigma2_z self.alpha = alpha self.approx = approx - self.prob = prob - self._do_transform = False def randomize(self, data: Optional[Any] = None) -> None: self._do_transform = self.R.random_sample() < self.prob @@ -706,7 +698,8 @@ class RandHistogramShiftd(Randomizable, MapTransform): def __init__( self, keys: KeysCollection, num_control_points: Union[Tuple[int, int], int] = 10, prob: float = 0.1 ) -> None: - super().__init__(keys) + MapTransform.__init__(self, keys) + Randomizable.__init__(self, prob) if isinstance(num_control_points, int): if num_control_points <= 2: raise AssertionError("num_control_points should be greater than or equal to 3") @@ -717,8 +710,6 @@ def __init__( if min(num_control_points) <= 2: raise AssertionError("num_control_points should be greater than or equal to 3") self.num_control_points = (min(num_control_points), max(num_control_points)) - self.prob = prob - self._do_transform = False def randomize(self, data: Optional[Any] = None) -> None: self._do_transform = self.R.random() < self.prob diff --git a/monai/transforms/spatial/array.py b/monai/transforms/spatial/array.py index 0610982847..db38815c9e 100644 --- a/monai/transforms/spatial/array.py +++ b/monai/transforms/spatial/array.py @@ -616,11 +616,10 @@ def __init__(self, prob: float = 0.1, max_k: int = 3, spatial_axes: Tuple[int, i spatial_axes: 2 int numbers, defines the plane to rotate with 2 spatial axes. Default: (0, 1), this is the first two axis in spatial dimensions. """ - self.prob = min(max(prob, 0.0), 1.0) + Randomizable.__init__(self, min(max(prob, 0.0), 1.0)) self.max_k = max_k self.spatial_axes = spatial_axes - self._do_transform = False self._rand_k = 0 def randomize(self, data: Optional[Any] = None) -> None: @@ -679,6 +678,7 @@ def __init__( align_corners: bool = False, dtype: DtypeLike = np.float64, ) -> None: + Randomizable.__init__(self, prob) self.range_x = ensure_tuple(range_x) if len(self.range_x) == 1: self.range_x = tuple(sorted([-self.range_x[0], self.range_x[0]])) @@ -689,14 +689,12 @@ def __init__( if len(self.range_z) == 1: self.range_z = tuple(sorted([-self.range_z[0], self.range_z[0]])) - self.prob = prob self.keep_size = keep_size self.mode: GridSampleMode = GridSampleMode(mode) self.padding_mode: GridSamplePadMode = GridSamplePadMode(padding_mode) self.align_corners = align_corners self.dtype = dtype - self._do_transform = False self.x = 0.0 self.y = 0.0 self.z = 0.0 @@ -756,9 +754,8 @@ class RandFlip(Randomizable, Transform): """ def __init__(self, prob: float = 0.1, spatial_axis: Optional[Union[Sequence[int], int]] = None) -> None: - self.prob = prob + Randomizable.__init__(self, min(max(prob, 0.0), 1.0)) self.flipper = Flip(spatial_axis=spatial_axis) - self._do_transform = False def randomize(self, data: Optional[Any] = None) -> None: self._do_transform = self.R.random_sample() < self.prob @@ -813,17 +810,16 @@ def __init__( align_corners: Optional[bool] = None, keep_size: bool = True, ) -> None: + Randomizable.__init__(self, prob) self.min_zoom = ensure_tuple(min_zoom) self.max_zoom = ensure_tuple(max_zoom) if len(self.min_zoom) != len(self.max_zoom): raise AssertionError("min_zoom and max_zoom must have same length.") - self.prob = prob self.mode: InterpolateMode = InterpolateMode(mode) self.padding_mode: NumpyPadMode = NumpyPadMode(padding_mode) self.align_corners = align_corners self.keep_size = keep_size - self._do_transform = False self._zoom: Sequence[float] = [1.0] def randomize(self, data: Optional[Any] = None) -> None: @@ -1331,6 +1327,7 @@ def __init__( - :py:class:`RandAffineGrid` for the random affine parameters configurations. - :py:class:`Affine` for the affine transformation parameters configurations. """ + Randomizable.__init__(self, prob) self.rand_affine_grid = RandAffineGrid( rotate_range=rotate_range, @@ -1346,9 +1343,6 @@ def __init__( self.mode: GridSampleMode = GridSampleMode(mode) self.padding_mode: GridSamplePadMode = GridSamplePadMode(padding_mode) - self.do_transform = False - self.prob = prob - def set_random_state( self, seed: Optional[int] = None, state: Optional[np.random.RandomState] = None ) -> "RandAffine": @@ -1357,7 +1351,7 @@ def set_random_state( return self def randomize(self, data: Optional[Any] = None) -> None: - self.do_transform = self.R.rand() < self.prob + self._do_transform = self.R.rand() < self.prob self.rand_affine_grid.randomize() def __call__( @@ -1452,6 +1446,7 @@ def __init__( - :py:class:`RandAffineGrid` for the random affine parameters configurations. - :py:class:`Affine` for the affine transformation parameters configurations. """ + Randomizable.__init__(self, prob) self.deform_grid = RandDeformGrid( spacing=spacing, magnitude_range=magnitude_range, as_tensor_output=True, device=device ) @@ -1468,8 +1463,6 @@ def __init__( self.spatial_size = spatial_size self.mode: GridSampleMode = GridSampleMode(mode) self.padding_mode: GridSamplePadMode = GridSamplePadMode(padding_mode) - self.prob = prob - self.do_transform = False def set_random_state( self, seed: Optional[int] = None, state: Optional[np.random.RandomState] = None @@ -1480,7 +1473,7 @@ def set_random_state( return self def randomize(self, spatial_size: Sequence[int]) -> None: - self.do_transform = self.R.rand() < self.prob + self._do_transform = self.R.rand() < self.prob self.deform_grid.randomize(spatial_size) self.rand_affine_grid.randomize() @@ -1506,7 +1499,7 @@ def __call__( """ sp_size = fall_back_tuple(spatial_size or self.spatial_size, img.shape[1:]) self.randomize(spatial_size=sp_size) - if self.do_transform: + if self._do_transform: grid = self.deform_grid(spatial_size=sp_size) grid = self.rand_affine_grid(grid=grid) grid = torch.nn.functional.interpolate( # type: ignore @@ -1584,6 +1577,7 @@ def __init__( - :py:class:`RandAffineGrid` for the random affine parameters configurations. - :py:class:`Affine` for the affine transformation parameters configurations. """ + Randomizable.__init__(self, prob) self.rand_affine_grid = RandAffineGrid(rotate_range, shear_range, translate_range, scale_range, True, device) self.resampler = Resample(as_tensor_output=as_tensor_output, device=device) @@ -1594,8 +1588,6 @@ def __init__( self.padding_mode: GridSamplePadMode = GridSamplePadMode(padding_mode) self.device = device - self.prob = prob - self.do_transform = False self.rand_offset = None self.magnitude = 1.0 self.sigma = 1.0 @@ -1608,8 +1600,8 @@ def set_random_state( return self def randomize(self, grid_size: Sequence[int]) -> None: - self.do_transform = self.R.rand() < self.prob - if self.do_transform: + self._do_transform = self.R.rand() < self.prob + if self._do_transform: self.rand_offset = self.R.uniform(-1.0, 1.0, [3] + list(grid_size)).astype(np.float32) self.magnitude = self.R.uniform(self.magnitude_range[0], self.magnitude_range[1]) self.sigma = self.R.uniform(self.sigma_range[0], self.sigma_range[1]) @@ -1638,7 +1630,7 @@ def __call__( sp_size = fall_back_tuple(spatial_size or self.spatial_size, img.shape[1:]) self.randomize(grid_size=sp_size) grid = create_grid(spatial_size=sp_size) - if self.do_transform: + if self._do_transform: if self.rand_offset is None: raise AssertionError grid = torch.as_tensor(np.ascontiguousarray(grid), device=self.device) diff --git a/monai/transforms/spatial/dictionary.py b/monai/transforms/spatial/dictionary.py index 8b546e5e97..4d2a905a51 100644 --- a/monai/transforms/spatial/dictionary.py +++ b/monai/transforms/spatial/dictionary.py @@ -299,13 +299,12 @@ def __init__( spatial_axes: 2 int numbers, defines the plane to rotate with 2 spatial axes. Default: (0, 1), this is the first two axis in spatial dimensions. """ - super().__init__(keys) + MapTransform.__init__(self, keys) + Randomizable.__init__(self, min(max(prob, 0.0), 1.0)) - self.prob = min(max(prob, 0.0), 1.0) self.max_k = max_k self.spatial_axes = spatial_axes - self._do_transform = False self._rand_k = 0 def randomize(self, data: Optional[Any] = None) -> None: @@ -314,13 +313,13 @@ def randomize(self, data: Optional[Any] = None) -> None: def __call__(self, data: Mapping[Hashable, np.ndarray]) -> Mapping[Hashable, np.ndarray]: self.randomize() - if not self._do_transform: - return data + d = dict(data) rotator = Rotate90(self._rand_k, self.spatial_axes) - d = dict(data) for key in self.keys: + if self._do_transform: d[key] = rotator(d[key]) + self.append_applied_transforms(d, key, extra_info={"rand_k": self._rand_k}) return d @@ -425,9 +424,10 @@ def __init__( - :py:class:`monai.transforms.compose.MapTransform` - :py:class:`RandAffineGrid` for the random affine parameters configurations. """ - super().__init__(keys) + MapTransform.__init__(self, keys) + Randomizable.__init__(self, prob) self.rand_affine = RandAffine( - prob=prob, + prob=1.0, # because probability handled in this class rotate_range=rotate_range, shear_range=shear_range, translate_range=translate_range, @@ -447,6 +447,7 @@ def set_random_state( return self def randomize(self, data: Optional[Any] = None) -> None: + self._do_transform = self.R.rand() < self.prob self.rand_affine.randomize() def __call__( @@ -456,7 +457,7 @@ def __call__( self.randomize() sp_size = fall_back_tuple(self.rand_affine.spatial_size, data[self.keys[0]].shape[1:]) - if self.rand_affine.do_transform: + if self._do_transform: grid = self.rand_affine.rand_affine_grid(spatial_size=sp_size) else: grid = create_grid(spatial_size=sp_size) @@ -529,11 +530,12 @@ def __init__( - :py:class:`RandAffineGrid` for the random affine parameters configurations. - :py:class:`Affine` for the affine transformation parameters configurations. """ - super().__init__(keys) + MapTransform.__init__(self, keys) + Randomizable.__init__(self, prob) self.rand_2d_elastic = Rand2DElastic( spacing=spacing, magnitude_range=magnitude_range, - prob=prob, + prob=1.0, # because probability controlled by this class rotate_range=rotate_range, shear_range=shear_range, translate_range=translate_range, @@ -553,6 +555,7 @@ def set_random_state( return self def randomize(self, spatial_size: Sequence[int]) -> None: + self._do_transform = self.R.rand() < self.prob self.rand_2d_elastic.randomize(spatial_size) def __call__( @@ -650,11 +653,12 @@ def __init__( - :py:class:`RandAffineGrid` for the random affine parameters configurations. - :py:class:`Affine` for the affine transformation parameters configurations. """ - super().__init__(keys) + MapTransform.__init__(self, keys) + Randomizable.__init__(self, prob) self.rand_3d_elastic = Rand3DElastic( sigma_range=sigma_range, magnitude_range=magnitude_range, - prob=prob, + prob=1.0, # because probability controlled by this class rotate_range=rotate_range, shear_range=shear_range, translate_range=translate_range, @@ -674,6 +678,7 @@ def set_random_state( return self def randomize(self, grid_size: Sequence[int]) -> None: + self._do_transform = self.R.rand() < self.prob self.rand_3d_elastic.randomize(grid_size) def __call__( @@ -684,7 +689,7 @@ def __call__( self.randomize(grid_size=sp_size) grid = create_grid(spatial_size=sp_size) - if self.rand_3d_elastic.do_transform: + if self._do_transform: device = self.rand_3d_elastic.device grid = torch.tensor(grid).to(device) gaussian = GaussianFilter(spatial_dims=3, sigma=self.rand_3d_elastic.sigma, truncated=3.0).to(device) @@ -741,11 +746,10 @@ def __init__( prob: float = 0.1, spatial_axis: Optional[Union[Sequence[int], int]] = None, ) -> None: - super().__init__(keys) + MapTransform.__init__(self, keys) + Randomizable.__init__(self, prob) self.spatial_axis = spatial_axis - self.prob = prob - self._do_transform = False self.flipper = Flip(spatial_axis=spatial_axis) def randomize(self, data: Optional[Any] = None) -> None: @@ -754,9 +758,13 @@ def randomize(self, data: Optional[Any] = None) -> None: def __call__(self, data: Mapping[Hashable, np.ndarray]) -> Dict[Hashable, np.ndarray]: self.randomize() d = dict(data) - if not self._do_transform: - return d for key in self.keys: + if self._do_transform: + d[key] = self.flipper(d[key]) + self.append_applied_transforms(d, key) + + return d + d[key] = self.flipper(d[key]) return d @@ -866,7 +874,8 @@ def __init__( align_corners: Union[Sequence[bool], bool] = False, dtype: Union[Sequence[DtypeLike], DtypeLike] = np.float64, ) -> None: - super().__init__(keys) + MapTransform.__init__(self, keys) + Randomizable.__init__(self, prob) self.range_x = ensure_tuple(range_x) if len(self.range_x) == 1: self.range_x = tuple(sorted([-self.range_x[0], self.range_x[0]])) @@ -877,14 +886,12 @@ def __init__( if len(self.range_z) == 1: self.range_z = tuple(sorted([-self.range_z[0], self.range_z[0]])) - self.prob = prob self.keep_size = keep_size self.mode = ensure_tuple_rep(mode, len(self.keys)) self.padding_mode = ensure_tuple_rep(padding_mode, len(self.keys)) self.align_corners = ensure_tuple_rep(align_corners, len(self.keys)) self.dtype = ensure_tuple_rep(dtype, len(self.keys)) - self._do_transform = False self.x = 0.0 self.y = 0.0 self.z = 0.0 @@ -1009,19 +1016,18 @@ def __init__( align_corners: Union[Sequence[Optional[bool]], Optional[bool]] = None, keep_size: bool = True, ) -> None: - super().__init__(keys) + MapTransform.__init__(self, keys) + Randomizable.__init__(self, prob) self.min_zoom = ensure_tuple(min_zoom) self.max_zoom = ensure_tuple(max_zoom) if len(self.min_zoom) != len(self.max_zoom): raise AssertionError("min_zoom and max_zoom must have same length.") - self.prob = prob self.mode = ensure_tuple_rep(mode, len(self.keys)) self.padding_mode = ensure_tuple_rep(padding_mode, len(self.keys)) self.align_corners = ensure_tuple_rep(align_corners, len(self.keys)) self.keep_size = keep_size - self._do_transform = False self._zoom: Sequence[float] = [1.0] def randomize(self, data: Optional[Any] = None) -> None: diff --git a/monai/transforms/transform.py b/monai/transforms/transform.py index e5841cbe97..4f0b2eca79 100644 --- a/monai/transforms/transform.py +++ b/monai/transforms/transform.py @@ -43,6 +43,10 @@ def __call__(self, img): R: np.random.RandomState = np.random.RandomState() + def __init__(self, prob): + self._do_transform = False + self.prob = prob + def set_random_state( self, seed: Optional[int] = None, state: Optional[np.random.RandomState] = None ) -> "Randomizable": From 21210623f6a22830d5fd9192c87a93ca089b5280 Mon Sep 17 00:00:00 2001 From: Rich <33289025+rijobro@users.noreply.github.com> Date: Thu, 25 Feb 2021 11:09:44 +0000 Subject: [PATCH 02/13] indent Signed-off-by: Richard Brown <33289025+rijobro@users.noreply.github.com> --- monai/transforms/spatial/dictionary.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/monai/transforms/spatial/dictionary.py b/monai/transforms/spatial/dictionary.py index 4d2a905a51..879645b8bb 100644 --- a/monai/transforms/spatial/dictionary.py +++ b/monai/transforms/spatial/dictionary.py @@ -318,8 +318,7 @@ def __call__(self, data: Mapping[Hashable, np.ndarray]) -> Mapping[Hashable, np. rotator = Rotate90(self._rand_k, self.spatial_axes) for key in self.keys: if self._do_transform: - d[key] = rotator(d[key]) - self.append_applied_transforms(d, key, extra_info={"rand_k": self._rand_k}) + d[key] = rotator(d[key]) return d From dd17ad70cc5f1cbd137ff001ae08bebdff250795 Mon Sep 17 00:00:00 2001 From: Rich <33289025+rijobro@users.noreply.github.com> Date: Thu, 25 Feb 2021 11:26:09 +0000 Subject: [PATCH 03/13] bug fix Signed-off-by: Richard Brown <33289025+rijobro@users.noreply.github.com> --- monai/transforms/spatial/dictionary.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/monai/transforms/spatial/dictionary.py b/monai/transforms/spatial/dictionary.py index 879645b8bb..706bfd8091 100644 --- a/monai/transforms/spatial/dictionary.py +++ b/monai/transforms/spatial/dictionary.py @@ -760,11 +760,6 @@ def __call__(self, data: Mapping[Hashable, np.ndarray]) -> Dict[Hashable, np.nda for key in self.keys: if self._do_transform: d[key] = self.flipper(d[key]) - self.append_applied_transforms(d, key) - - return d - - d[key] = self.flipper(d[key]) return d From 893eedc451efa9d568cba897961b7dc669666094 Mon Sep 17 00:00:00 2001 From: Rich <33289025+rijobro@users.noreply.github.com> Date: Thu, 25 Feb 2021 12:01:06 +0000 Subject: [PATCH 04/13] call super().randomize() Signed-off-by: Richard Brown <33289025+rijobro@users.noreply.github.com> --- monai/transforms/intensity/array.py | 14 +++++++------- monai/transforms/intensity/dictionary.py | 14 +++++++------- monai/transforms/spatial/array.py | 15 ++++++--------- monai/transforms/spatial/dictionary.py | 15 ++++++--------- monai/transforms/transform.py | 9 ++------- 5 files changed, 28 insertions(+), 39 deletions(-) diff --git a/monai/transforms/intensity/array.py b/monai/transforms/intensity/array.py index 7fc7b23d3c..cbb21a2f8a 100644 --- a/monai/transforms/intensity/array.py +++ b/monai/transforms/intensity/array.py @@ -66,7 +66,7 @@ def __init__(self, prob: float = 0.1, mean: Union[Sequence[float], float] = 0.0, self._noise = None def randomize(self, im_shape: Sequence[int]) -> None: - self._do_transform = self.R.random() < self.prob + super().randomize() self._noise = self.R.normal(self.mean, self.R.uniform(0, self.std), size=im_shape) def __call__(self, img: Union[torch.Tensor, np.ndarray]) -> Union[torch.Tensor, np.ndarray]: @@ -121,8 +121,8 @@ def __init__(self, offsets: Union[Tuple[float, float], float], prob: float = 0.1 self.offsets = (min(offsets), max(offsets)) def randomize(self, data: Optional[Any] = None) -> None: + super().randomize() self._offset = self.R.uniform(low=self.offsets[0], high=self.offsets[1]) - self._do_transform = self.R.random() < self.prob def __call__(self, img: np.ndarray) -> np.ndarray: """ @@ -192,8 +192,8 @@ def __init__(self, factors: Union[Tuple[float, float], float], prob: float = 0.1 self.factors = (min(factors), max(factors)) def randomize(self, data: Optional[Any] = None) -> None: + super().randomize() self.factor = self.R.uniform(low=self.factors[0], high=self.factors[1]) - self._do_transform = self.R.random() < self.prob def __call__(self, img: np.ndarray) -> np.ndarray: """ @@ -394,7 +394,7 @@ def __init__(self, prob: float = 0.1, gamma: Union[Sequence[float], float] = (0. self.gamma_value = None def randomize(self, data: Optional[Any] = None) -> None: - self._do_transform = self.R.random_sample() < self.prob + super().randomize() self.gamma_value = self.R.uniform(low=self.gamma[0], high=self.gamma[1]) def __call__(self, img: np.ndarray) -> np.ndarray: @@ -680,7 +680,7 @@ def __init__( self.approx = approx def randomize(self, data: Optional[Any] = None) -> None: - self._do_transform = self.R.random_sample() < self.prob + super().randomize() self.x = self.R.uniform(low=self.sigma_x[0], high=self.sigma_x[1]) self.y = self.R.uniform(low=self.sigma_y[0], high=self.sigma_y[1]) self.z = self.R.uniform(low=self.sigma_z[0], high=self.sigma_z[1]) @@ -786,7 +786,7 @@ def __init__( self.approx = approx def randomize(self, data: Optional[Any] = None) -> None: - self._do_transform = self.R.random_sample() < self.prob + super().randomize() self.x1 = self.R.uniform(low=self.sigma1_x[0], high=self.sigma1_x[1]) self.y1 = self.R.uniform(low=self.sigma1_y[0], high=self.sigma1_y[1]) self.z1 = self.R.uniform(low=self.sigma1_z[0], high=self.sigma1_z[1]) @@ -833,7 +833,7 @@ def __init__(self, num_control_points: Union[Tuple[int, int], int] = 10, prob: f self.num_control_points = (min(num_control_points), max(num_control_points)) def randomize(self, data: Optional[Any] = None) -> None: - self._do_transform = self.R.random() < self.prob + super().randomize() num_control_point = self.R.randint(self.num_control_points[0], self.num_control_points[1] + 1) self.reference_control_points = np.linspace(0, 1, num_control_point) self.floating_control_points = np.copy(self.reference_control_points) diff --git a/monai/transforms/intensity/dictionary.py b/monai/transforms/intensity/dictionary.py index deaf6823d6..639f5d908b 100644 --- a/monai/transforms/intensity/dictionary.py +++ b/monai/transforms/intensity/dictionary.py @@ -115,7 +115,7 @@ def __init__( self._noise: List[np.ndarray] = [] def randomize(self, im_shape: Sequence[int]) -> None: - self._do_transform = self.R.random() < self.prob + super().randomize() self._noise.clear() for m in self.mean: self._noise.append(self.R.normal(m, self.R.uniform(0, self.std), size=im_shape)) @@ -183,8 +183,8 @@ def __init__(self, keys: KeysCollection, offsets: Union[Tuple[float, float], flo self.offsets = (min(offsets), max(offsets)) def randomize(self, data: Optional[Any] = None) -> None: + super().randomize() self._offset = self.R.uniform(low=self.offsets[0], high=self.offsets[1]) - self._do_transform = self.R.random() < self.prob def __call__(self, data: Mapping[Hashable, np.ndarray]) -> Dict[Hashable, np.ndarray]: d = dict(data) @@ -253,8 +253,8 @@ def __init__(self, keys: KeysCollection, factors: Union[Tuple[float, float], flo self.factors = (min(factors), max(factors)) def randomize(self, data: Optional[Any] = None) -> None: + super().randomize() self.factor = self.R.uniform(low=self.factors[0], high=self.factors[1]) - self._do_transform = self.R.random() < self.prob def __call__(self, data: Mapping[Hashable, np.ndarray]) -> Dict[Hashable, np.ndarray]: d = dict(data) @@ -412,7 +412,7 @@ def __init__( self.gamma_value: Optional[float] = None def randomize(self, data: Optional[Any] = None) -> None: - self._do_transform = self.R.random_sample() < self.prob + super().randomize() self.gamma_value = self.R.uniform(low=self.gamma[0], high=self.gamma[1]) def __call__(self, data: Mapping[Hashable, np.ndarray]) -> Dict[Hashable, np.ndarray]: @@ -556,7 +556,7 @@ def __init__( self.approx = approx def randomize(self, data: Optional[Any] = None) -> None: - self._do_transform = self.R.random_sample() < self.prob + super().randomize() self.x = self.R.uniform(low=self.sigma_x[0], high=self.sigma_x[1]) self.y = self.R.uniform(low=self.sigma_y[0], high=self.sigma_y[1]) self.z = self.R.uniform(low=self.sigma_z[0], high=self.sigma_z[1]) @@ -657,7 +657,7 @@ def __init__( self.approx = approx def randomize(self, data: Optional[Any] = None) -> None: - self._do_transform = self.R.random_sample() < self.prob + super().randomize() self.x1 = self.R.uniform(low=self.sigma1_x[0], high=self.sigma1_x[1]) self.y1 = self.R.uniform(low=self.sigma1_y[0], high=self.sigma1_y[1]) self.z1 = self.R.uniform(low=self.sigma1_z[0], high=self.sigma1_z[1]) @@ -712,7 +712,7 @@ def __init__( self.num_control_points = (min(num_control_points), max(num_control_points)) def randomize(self, data: Optional[Any] = None) -> None: - self._do_transform = self.R.random() < self.prob + super().randomize() num_control_point = self.R.randint(self.num_control_points[0], self.num_control_points[1] + 1) self.reference_control_points = np.linspace(0, 1, num_control_point) self.floating_control_points = np.copy(self.reference_control_points) diff --git a/monai/transforms/spatial/array.py b/monai/transforms/spatial/array.py index db38815c9e..dd0b618d55 100644 --- a/monai/transforms/spatial/array.py +++ b/monai/transforms/spatial/array.py @@ -623,8 +623,8 @@ def __init__(self, prob: float = 0.1, max_k: int = 3, spatial_axes: Tuple[int, i self._rand_k = 0 def randomize(self, data: Optional[Any] = None) -> None: + super().randomize() self._rand_k = self.R.randint(self.max_k) + 1 - self._do_transform = self.R.random() < self.prob def __call__(self, img: np.ndarray) -> np.ndarray: """ @@ -700,7 +700,7 @@ def __init__( self.z = 0.0 def randomize(self, data: Optional[Any] = None) -> None: - self._do_transform = self.R.random_sample() < self.prob + super().randomize() self.x = self.R.uniform(low=self.range_x[0], high=self.range_x[1]) self.y = self.R.uniform(low=self.range_y[0], high=self.range_y[1]) self.z = self.R.uniform(low=self.range_z[0], high=self.range_z[1]) @@ -757,9 +757,6 @@ def __init__(self, prob: float = 0.1, spatial_axis: Optional[Union[Sequence[int] Randomizable.__init__(self, min(max(prob, 0.0), 1.0)) self.flipper = Flip(spatial_axis=spatial_axis) - def randomize(self, data: Optional[Any] = None) -> None: - self._do_transform = self.R.random_sample() < self.prob - def __call__(self, img: np.ndarray) -> np.ndarray: """ Args: @@ -823,7 +820,7 @@ def __init__( self._zoom: Sequence[float] = [1.0] def randomize(self, data: Optional[Any] = None) -> None: - self._do_transform = self.R.random_sample() < self.prob + super().randomize() self._zoom = [self.R.uniform(l, h) for l, h in zip(self.min_zoom, self.max_zoom)] def __call__( @@ -1351,7 +1348,7 @@ def set_random_state( return self def randomize(self, data: Optional[Any] = None) -> None: - self._do_transform = self.R.rand() < self.prob + super().randomize() self.rand_affine_grid.randomize() def __call__( @@ -1473,7 +1470,7 @@ def set_random_state( return self def randomize(self, spatial_size: Sequence[int]) -> None: - self._do_transform = self.R.rand() < self.prob + super().randomize() self.deform_grid.randomize(spatial_size) self.rand_affine_grid.randomize() @@ -1600,7 +1597,7 @@ def set_random_state( return self def randomize(self, grid_size: Sequence[int]) -> None: - self._do_transform = self.R.rand() < self.prob + super().randomize() if self._do_transform: self.rand_offset = self.R.uniform(-1.0, 1.0, [3] + list(grid_size)).astype(np.float32) self.magnitude = self.R.uniform(self.magnitude_range[0], self.magnitude_range[1]) diff --git a/monai/transforms/spatial/dictionary.py b/monai/transforms/spatial/dictionary.py index 706bfd8091..7f62305f14 100644 --- a/monai/transforms/spatial/dictionary.py +++ b/monai/transforms/spatial/dictionary.py @@ -308,8 +308,8 @@ def __init__( self._rand_k = 0 def randomize(self, data: Optional[Any] = None) -> None: + super().randomize() self._rand_k = self.R.randint(self.max_k) + 1 - self._do_transform = self.R.random() < self.prob def __call__(self, data: Mapping[Hashable, np.ndarray]) -> Mapping[Hashable, np.ndarray]: self.randomize() @@ -446,7 +446,7 @@ def set_random_state( return self def randomize(self, data: Optional[Any] = None) -> None: - self._do_transform = self.R.rand() < self.prob + super().randomize() self.rand_affine.randomize() def __call__( @@ -554,7 +554,7 @@ def set_random_state( return self def randomize(self, spatial_size: Sequence[int]) -> None: - self._do_transform = self.R.rand() < self.prob + super().randomize() self.rand_2d_elastic.randomize(spatial_size) def __call__( @@ -677,7 +677,7 @@ def set_random_state( return self def randomize(self, grid_size: Sequence[int]) -> None: - self._do_transform = self.R.rand() < self.prob + super().randomize() self.rand_3d_elastic.randomize(grid_size) def __call__( @@ -751,9 +751,6 @@ def __init__( self.flipper = Flip(spatial_axis=spatial_axis) - def randomize(self, data: Optional[Any] = None) -> None: - self._do_transform = self.R.random_sample() < self.prob - def __call__(self, data: Mapping[Hashable, np.ndarray]) -> Dict[Hashable, np.ndarray]: self.randomize() d = dict(data) @@ -891,7 +888,7 @@ def __init__( self.z = 0.0 def randomize(self, data: Optional[Any] = None) -> None: - self._do_transform = self.R.random_sample() < self.prob + super().randomize() self.x = self.R.uniform(low=self.range_x[0], high=self.range_x[1]) self.y = self.R.uniform(low=self.range_y[0], high=self.range_y[1]) self.z = self.R.uniform(low=self.range_z[0], high=self.range_z[1]) @@ -1025,7 +1022,7 @@ def __init__( self._zoom: Sequence[float] = [1.0] def randomize(self, data: Optional[Any] = None) -> None: - self._do_transform = self.R.random_sample() < self.prob + super().randomize() self._zoom = [self.R.uniform(l, h) for l, h in zip(self.min_zoom, self.max_zoom)] def __call__(self, data: Mapping[Hashable, np.ndarray]) -> Dict[Hashable, np.ndarray]: diff --git a/monai/transforms/transform.py b/monai/transforms/transform.py index 4f0b2eca79..38231846c4 100644 --- a/monai/transforms/transform.py +++ b/monai/transforms/transform.py @@ -81,8 +81,7 @@ def set_random_state( self.R = np.random.RandomState() return self - @abstractmethod - def randomize(self, data: Any) -> None: + def randomize(self, data: Optional[Any] = None) -> None: """ Within this method, :py:attr:`self.R` should be used, instead of `np.random`, to introduce random factors. @@ -90,12 +89,8 @@ def randomize(self, data: Any) -> None: identify errors of sync the random state. This method can generate the random factors based on properties of the input data. - - Raises: - NotImplementedError: When the subclass does not override this method. - """ - raise NotImplementedError(f"Subclass {self.__class__.__name__} must implement this method.") + self._do_transform = self.R.rand() < self.prob class Transform(ABC): From a2defb705bcb60551894957f2e48ce50296c31e3 Mon Sep 17 00:00:00 2001 From: Rich <33289025+rijobro@users.noreply.github.com> Date: Thu, 25 Feb 2021 12:18:29 +0000 Subject: [PATCH 05/13] undo change in signature Signed-off-by: Richard Brown <33289025+rijobro@users.noreply.github.com> --- monai/transforms/transform.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/monai/transforms/transform.py b/monai/transforms/transform.py index 38231846c4..b210a17372 100644 --- a/monai/transforms/transform.py +++ b/monai/transforms/transform.py @@ -81,7 +81,7 @@ def set_random_state( self.R = np.random.RandomState() return self - def randomize(self, data: Optional[Any] = None) -> None: + def randomize(self, data: Any) -> None: """ Within this method, :py:attr:`self.R` should be used, instead of `np.random`, to introduce random factors. From 1ff74c4494ac73e27a18267f4b912bf69b7ba5c8 Mon Sep 17 00:00:00 2001 From: Rich <33289025+rijobro@users.noreply.github.com> Date: Thu, 25 Feb 2021 12:30:09 +0000 Subject: [PATCH 06/13] make sure all call super parent Signed-off-by: Richard Brown <33289025+rijobro@users.noreply.github.com> --- monai/transforms/croppad/dictionary.py | 10 ++++++---- monai/transforms/spatial/dictionary.py | 2 +- monai/transforms/transform.py | 6 +++--- 3 files changed, 10 insertions(+), 8 deletions(-) diff --git a/monai/transforms/croppad/dictionary.py b/monai/transforms/croppad/dictionary.py index 75e4e8cb99..93780a28a0 100644 --- a/monai/transforms/croppad/dictionary.py +++ b/monai/transforms/croppad/dictionary.py @@ -283,9 +283,8 @@ def __init__( random_center: bool = True, random_size: bool = True, ) -> None: - Randomizable.__init__(self, prob=1.0) + Randomizable.__init__(self) MapTransform.__init__(self, keys) - self._do_transform = True self.roi_size = roi_size self.random_center = random_center self.random_size = random_size @@ -346,7 +345,8 @@ def __init__( random_center: bool = True, random_size: bool = True, ) -> None: - super().__init__(keys) + Randomizable.__init__(self) + MapTransform.__init__(self, keys) if num_samples < 1: raise ValueError(f"num_samples must be positive, got {num_samples}.") self.num_samples = num_samples @@ -448,7 +448,8 @@ def __init__( num_samples: int = 1, center_coord_key: Optional[str] = None, ): - super().__init__(keys) + Randomizable.__init__(self) + MapTransform.__init__(self, keys) self.spatial_size = ensure_tuple(spatial_size) self.w_key = w_key self.num_samples = int(num_samples) @@ -536,6 +537,7 @@ def __init__( fg_indices_key: Optional[str] = None, bg_indices_key: Optional[str] = None, ) -> None: + Randomizable.__init__(self) MapTransform.__init__(self, keys) self.label_key = label_key self.spatial_size: Union[Tuple[int, ...], Sequence[int], int] = spatial_size diff --git a/monai/transforms/spatial/dictionary.py b/monai/transforms/spatial/dictionary.py index 4374e6bb99..ad6c0a0e00 100644 --- a/monai/transforms/spatial/dictionary.py +++ b/monai/transforms/spatial/dictionary.py @@ -300,7 +300,7 @@ def __init__( Default: (0, 1), this is the first two axis in spatial dimensions. """ MapTransform.__init__(self, keys) - Randomizable.__init__(self, min(max(prob, 0.0), 1.0)) + Randomizable.__init__(self, prob) self.max_k = max_k self.spatial_axes = spatial_axes diff --git a/monai/transforms/transform.py b/monai/transforms/transform.py index b210a17372..db3d7a5306 100644 --- a/monai/transforms/transform.py +++ b/monai/transforms/transform.py @@ -43,9 +43,9 @@ def __call__(self, img): R: np.random.RandomState = np.random.RandomState() - def __init__(self, prob): - self._do_transform = False - self.prob = prob + def __init__(self, prob=1.0, do_transform=False): + self._do_transform = do_transform + self.prob = min(max(prob, 0.0), 1.0) def set_random_state( self, seed: Optional[int] = None, state: Optional[np.random.RandomState] = None From 9de8e3cf77a17d0966c286bbfdc5ba6ca1220e1e Mon Sep 17 00:00:00 2001 From: Rich <33289025+rijobro@users.noreply.github.com> Date: Thu, 25 Feb 2021 13:32:07 +0000 Subject: [PATCH 07/13] code format Signed-off-by: Richard Brown <33289025+rijobro@users.noreply.github.com> --- monai/transforms/intensity/array.py | 14 +++++++------- monai/transforms/intensity/dictionary.py | 14 +++++++------- monai/transforms/spatial/array.py | 16 ++++++++-------- monai/transforms/spatial/dictionary.py | 16 ++++++++-------- 4 files changed, 30 insertions(+), 30 deletions(-) diff --git a/monai/transforms/intensity/array.py b/monai/transforms/intensity/array.py index cbb21a2f8a..3b7bf56b1d 100644 --- a/monai/transforms/intensity/array.py +++ b/monai/transforms/intensity/array.py @@ -66,7 +66,7 @@ def __init__(self, prob: float = 0.1, mean: Union[Sequence[float], float] = 0.0, self._noise = None def randomize(self, im_shape: Sequence[int]) -> None: - super().randomize() + super().randomize(None) self._noise = self.R.normal(self.mean, self.R.uniform(0, self.std), size=im_shape) def __call__(self, img: Union[torch.Tensor, np.ndarray]) -> Union[torch.Tensor, np.ndarray]: @@ -121,7 +121,7 @@ def __init__(self, offsets: Union[Tuple[float, float], float], prob: float = 0.1 self.offsets = (min(offsets), max(offsets)) def randomize(self, data: Optional[Any] = None) -> None: - super().randomize() + super().randomize(None) self._offset = self.R.uniform(low=self.offsets[0], high=self.offsets[1]) def __call__(self, img: np.ndarray) -> np.ndarray: @@ -192,7 +192,7 @@ def __init__(self, factors: Union[Tuple[float, float], float], prob: float = 0.1 self.factors = (min(factors), max(factors)) def randomize(self, data: Optional[Any] = None) -> None: - super().randomize() + super().randomize(None) self.factor = self.R.uniform(low=self.factors[0], high=self.factors[1]) def __call__(self, img: np.ndarray) -> np.ndarray: @@ -394,7 +394,7 @@ def __init__(self, prob: float = 0.1, gamma: Union[Sequence[float], float] = (0. self.gamma_value = None def randomize(self, data: Optional[Any] = None) -> None: - super().randomize() + super().randomize(None) self.gamma_value = self.R.uniform(low=self.gamma[0], high=self.gamma[1]) def __call__(self, img: np.ndarray) -> np.ndarray: @@ -680,7 +680,7 @@ def __init__( self.approx = approx def randomize(self, data: Optional[Any] = None) -> None: - super().randomize() + super().randomize(None) self.x = self.R.uniform(low=self.sigma_x[0], high=self.sigma_x[1]) self.y = self.R.uniform(low=self.sigma_y[0], high=self.sigma_y[1]) self.z = self.R.uniform(low=self.sigma_z[0], high=self.sigma_z[1]) @@ -786,7 +786,7 @@ def __init__( self.approx = approx def randomize(self, data: Optional[Any] = None) -> None: - super().randomize() + super().randomize(None) self.x1 = self.R.uniform(low=self.sigma1_x[0], high=self.sigma1_x[1]) self.y1 = self.R.uniform(low=self.sigma1_y[0], high=self.sigma1_y[1]) self.z1 = self.R.uniform(low=self.sigma1_z[0], high=self.sigma1_z[1]) @@ -833,7 +833,7 @@ def __init__(self, num_control_points: Union[Tuple[int, int], int] = 10, prob: f self.num_control_points = (min(num_control_points), max(num_control_points)) def randomize(self, data: Optional[Any] = None) -> None: - super().randomize() + super().randomize(None) num_control_point = self.R.randint(self.num_control_points[0], self.num_control_points[1] + 1) self.reference_control_points = np.linspace(0, 1, num_control_point) self.floating_control_points = np.copy(self.reference_control_points) diff --git a/monai/transforms/intensity/dictionary.py b/monai/transforms/intensity/dictionary.py index 639f5d908b..bca51bcd55 100644 --- a/monai/transforms/intensity/dictionary.py +++ b/monai/transforms/intensity/dictionary.py @@ -115,7 +115,7 @@ def __init__( self._noise: List[np.ndarray] = [] def randomize(self, im_shape: Sequence[int]) -> None: - super().randomize() + super().randomize(None) self._noise.clear() for m in self.mean: self._noise.append(self.R.normal(m, self.R.uniform(0, self.std), size=im_shape)) @@ -183,7 +183,7 @@ def __init__(self, keys: KeysCollection, offsets: Union[Tuple[float, float], flo self.offsets = (min(offsets), max(offsets)) def randomize(self, data: Optional[Any] = None) -> None: - super().randomize() + super().randomize(None) self._offset = self.R.uniform(low=self.offsets[0], high=self.offsets[1]) def __call__(self, data: Mapping[Hashable, np.ndarray]) -> Dict[Hashable, np.ndarray]: @@ -253,7 +253,7 @@ def __init__(self, keys: KeysCollection, factors: Union[Tuple[float, float], flo self.factors = (min(factors), max(factors)) def randomize(self, data: Optional[Any] = None) -> None: - super().randomize() + super().randomize(None) self.factor = self.R.uniform(low=self.factors[0], high=self.factors[1]) def __call__(self, data: Mapping[Hashable, np.ndarray]) -> Dict[Hashable, np.ndarray]: @@ -412,7 +412,7 @@ def __init__( self.gamma_value: Optional[float] = None def randomize(self, data: Optional[Any] = None) -> None: - super().randomize() + super().randomize(None) self.gamma_value = self.R.uniform(low=self.gamma[0], high=self.gamma[1]) def __call__(self, data: Mapping[Hashable, np.ndarray]) -> Dict[Hashable, np.ndarray]: @@ -556,7 +556,7 @@ def __init__( self.approx = approx def randomize(self, data: Optional[Any] = None) -> None: - super().randomize() + super().randomize(None) self.x = self.R.uniform(low=self.sigma_x[0], high=self.sigma_x[1]) self.y = self.R.uniform(low=self.sigma_y[0], high=self.sigma_y[1]) self.z = self.R.uniform(low=self.sigma_z[0], high=self.sigma_z[1]) @@ -657,7 +657,7 @@ def __init__( self.approx = approx def randomize(self, data: Optional[Any] = None) -> None: - super().randomize() + super().randomize(None) self.x1 = self.R.uniform(low=self.sigma1_x[0], high=self.sigma1_x[1]) self.y1 = self.R.uniform(low=self.sigma1_y[0], high=self.sigma1_y[1]) self.z1 = self.R.uniform(low=self.sigma1_z[0], high=self.sigma1_z[1]) @@ -712,7 +712,7 @@ def __init__( self.num_control_points = (min(num_control_points), max(num_control_points)) def randomize(self, data: Optional[Any] = None) -> None: - super().randomize() + super().randomize(None) num_control_point = self.R.randint(self.num_control_points[0], self.num_control_points[1] + 1) self.reference_control_points = np.linspace(0, 1, num_control_point) self.floating_control_points = np.copy(self.reference_control_points) diff --git a/monai/transforms/spatial/array.py b/monai/transforms/spatial/array.py index 7ce78b9496..b50c3cf9ed 100644 --- a/monai/transforms/spatial/array.py +++ b/monai/transforms/spatial/array.py @@ -626,7 +626,7 @@ def __init__(self, prob: float = 0.1, max_k: int = 3, spatial_axes: Tuple[int, i self._rand_k = 0 def randomize(self, data: Optional[Any] = None) -> None: - super().randomize() + super().randomize(None) self._rand_k = self.R.randint(self.max_k) + 1 def __call__(self, img: np.ndarray) -> np.ndarray: @@ -703,7 +703,7 @@ def __init__( self.z = 0.0 def randomize(self, data: Optional[Any] = None) -> None: - super().randomize() + super().randomize(None) self.x = self.R.uniform(low=self.range_x[0], high=self.range_x[1]) self.y = self.R.uniform(low=self.range_y[0], high=self.range_y[1]) self.z = self.R.uniform(low=self.range_z[0], high=self.range_z[1]) @@ -765,7 +765,7 @@ def __call__(self, img: np.ndarray) -> np.ndarray: Args: img: channel first array, must have shape: (num_channels, H[, W, ..., ]), """ - self.randomize() + self.randomize(None) if not self._do_transform: return img return self.flipper(img) @@ -823,7 +823,7 @@ def __init__( self._zoom: Sequence[float] = [1.0] def randomize(self, data: Optional[Any] = None) -> None: - super().randomize() + super().randomize(None) self._zoom = [self.R.uniform(l, h) for l, h in zip(self.min_zoom, self.max_zoom)] def __call__( @@ -1348,7 +1348,7 @@ def set_random_state( return self def randomize(self, data: Optional[Any] = None) -> None: - super().randomize() + super().randomize(None) self.rand_affine_grid.randomize() def __call__( @@ -1376,7 +1376,7 @@ def __call__( self.randomize() sp_size = fall_back_tuple(spatial_size or self.spatial_size, img.shape[1:]) - if self.do_transform: + if self._do_transform: grid = self.rand_affine_grid(spatial_size=sp_size) else: grid = create_grid(spatial_size=sp_size) @@ -1469,7 +1469,7 @@ def set_random_state( return self def randomize(self, spatial_size: Sequence[int]) -> None: - super().randomize() + super().randomize(None) self.deform_grid.randomize(spatial_size) self.rand_affine_grid.randomize() @@ -1593,7 +1593,7 @@ def set_random_state( return self def randomize(self, grid_size: Sequence[int]) -> None: - super().randomize() + super().randomize(None) if self._do_transform: self.rand_offset = self.R.uniform(-1.0, 1.0, [3] + list(grid_size)).astype(np.float32) self.magnitude = self.R.uniform(self.magnitude_range[0], self.magnitude_range[1]) diff --git a/monai/transforms/spatial/dictionary.py b/monai/transforms/spatial/dictionary.py index ad6c0a0e00..ccc09cdb59 100644 --- a/monai/transforms/spatial/dictionary.py +++ b/monai/transforms/spatial/dictionary.py @@ -308,7 +308,7 @@ def __init__( self._rand_k = 0 def randomize(self, data: Optional[Any] = None) -> None: - super().randomize() + super().randomize(None) self._rand_k = self.R.randint(self.max_k) + 1 def __call__(self, data: Mapping[Hashable, np.ndarray]) -> Mapping[Hashable, np.ndarray]: @@ -441,7 +441,7 @@ def set_random_state( return self def randomize(self, data: Optional[Any] = None) -> None: - super().randomize() + super().randomize(None) self.rand_affine.randomize() def __call__( @@ -548,7 +548,7 @@ def set_random_state( return self def randomize(self, spatial_size: Sequence[int]) -> None: - super().randomize() + super().randomize(None) self.rand_2d_elastic.randomize(spatial_size) def __call__( @@ -559,7 +559,7 @@ def __call__( sp_size = fall_back_tuple(self.rand_2d_elastic.spatial_size, data[self.keys[0]].shape[1:]) self.randomize(spatial_size=sp_size) - if self.rand_2d_elastic.do_transform: + if self._do_transform: grid = self.rand_2d_elastic.deform_grid(spatial_size=sp_size) grid = self.rand_2d_elastic.rand_affine_grid(grid=grid) grid = torch.nn.functional.interpolate( # type: ignore @@ -668,7 +668,7 @@ def set_random_state( return self def randomize(self, grid_size: Sequence[int]) -> None: - super().randomize() + super().randomize(None) self.rand_3d_elastic.randomize(grid_size) def __call__( @@ -743,7 +743,7 @@ def __init__( self.flipper = Flip(spatial_axis=spatial_axis) def __call__(self, data: Mapping[Hashable, np.ndarray]) -> Dict[Hashable, np.ndarray]: - self.randomize() + self.randomize(None) d = dict(data) for key in self.keys: if self._do_transform: @@ -879,7 +879,7 @@ def __init__( self.z = 0.0 def randomize(self, data: Optional[Any] = None) -> None: - super().randomize() + super().randomize(None) self.x = self.R.uniform(low=self.range_x[0], high=self.range_x[1]) self.y = self.R.uniform(low=self.range_y[0], high=self.range_y[1]) self.z = self.R.uniform(low=self.range_z[0], high=self.range_z[1]) @@ -1013,7 +1013,7 @@ def __init__( self._zoom: Sequence[float] = [1.0] def randomize(self, data: Optional[Any] = None) -> None: - super().randomize() + super().randomize(None) self._zoom = [self.R.uniform(l, h) for l, h in zip(self.min_zoom, self.max_zoom)] def __call__(self, data: Mapping[Hashable, np.ndarray]) -> Dict[Hashable, np.ndarray]: From 874e9c0ddb90daf7d678137438b7f769f9c6ded1 Mon Sep 17 00:00:00 2001 From: Rich <33289025+rijobro@users.noreply.github.com> Date: Thu, 25 Feb 2021 16:45:49 +0000 Subject: [PATCH 08/13] bug fix Signed-off-by: Richard Brown <33289025+rijobro@users.noreply.github.com> --- monai/transforms/intensity/array.py | 4 ++-- monai/transforms/intensity/dictionary.py | 4 ++-- monai/transforms/spatial/array.py | 2 +- monai/transforms/spatial/dictionary.py | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/monai/transforms/intensity/array.py b/monai/transforms/intensity/array.py index 3b7bf56b1d..f955be305c 100644 --- a/monai/transforms/intensity/array.py +++ b/monai/transforms/intensity/array.py @@ -121,8 +121,8 @@ def __init__(self, offsets: Union[Tuple[float, float], float], prob: float = 0.1 self.offsets = (min(offsets), max(offsets)) def randomize(self, data: Optional[Any] = None) -> None: - super().randomize(None) self._offset = self.R.uniform(low=self.offsets[0], high=self.offsets[1]) + super().randomize(None) def __call__(self, img: np.ndarray) -> np.ndarray: """ @@ -192,8 +192,8 @@ def __init__(self, factors: Union[Tuple[float, float], float], prob: float = 0.1 self.factors = (min(factors), max(factors)) def randomize(self, data: Optional[Any] = None) -> None: - super().randomize(None) self.factor = self.R.uniform(low=self.factors[0], high=self.factors[1]) + super().randomize(None) def __call__(self, img: np.ndarray) -> np.ndarray: """ diff --git a/monai/transforms/intensity/dictionary.py b/monai/transforms/intensity/dictionary.py index bca51bcd55..f271c27274 100644 --- a/monai/transforms/intensity/dictionary.py +++ b/monai/transforms/intensity/dictionary.py @@ -183,8 +183,8 @@ def __init__(self, keys: KeysCollection, offsets: Union[Tuple[float, float], flo self.offsets = (min(offsets), max(offsets)) def randomize(self, data: Optional[Any] = None) -> None: - super().randomize(None) self._offset = self.R.uniform(low=self.offsets[0], high=self.offsets[1]) + super().randomize(None) def __call__(self, data: Mapping[Hashable, np.ndarray]) -> Dict[Hashable, np.ndarray]: d = dict(data) @@ -253,8 +253,8 @@ def __init__(self, keys: KeysCollection, factors: Union[Tuple[float, float], flo self.factors = (min(factors), max(factors)) def randomize(self, data: Optional[Any] = None) -> None: - super().randomize(None) self.factor = self.R.uniform(low=self.factors[0], high=self.factors[1]) + super().randomize(None) def __call__(self, data: Mapping[Hashable, np.ndarray]) -> Dict[Hashable, np.ndarray]: d = dict(data) diff --git a/monai/transforms/spatial/array.py b/monai/transforms/spatial/array.py index b50c3cf9ed..fe773789a4 100644 --- a/monai/transforms/spatial/array.py +++ b/monai/transforms/spatial/array.py @@ -626,8 +626,8 @@ def __init__(self, prob: float = 0.1, max_k: int = 3, spatial_axes: Tuple[int, i self._rand_k = 0 def randomize(self, data: Optional[Any] = None) -> None: - super().randomize(None) self._rand_k = self.R.randint(self.max_k) + 1 + super().randomize(None) def __call__(self, img: np.ndarray) -> np.ndarray: """ diff --git a/monai/transforms/spatial/dictionary.py b/monai/transforms/spatial/dictionary.py index ccc09cdb59..44824c847e 100644 --- a/monai/transforms/spatial/dictionary.py +++ b/monai/transforms/spatial/dictionary.py @@ -308,8 +308,8 @@ def __init__( self._rand_k = 0 def randomize(self, data: Optional[Any] = None) -> None: - super().randomize(None) self._rand_k = self.R.randint(self.max_k) + 1 + super().randomize(None) def __call__(self, data: Mapping[Hashable, np.ndarray]) -> Mapping[Hashable, np.ndarray]: self.randomize() From ad64f4ef10d0ff9e357879c1e954b800d44cc61b Mon Sep 17 00:00:00 2001 From: Richard Brown <33289025+rijobro@users.noreply.github.com> Date: Thu, 25 Feb 2021 17:06:15 +0000 Subject: [PATCH 09/13] fix Signed-off-by: Richard Brown <33289025+rijobro@users.noreply.github.com> --- monai/transforms/utility/dictionary.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/monai/transforms/utility/dictionary.py b/monai/transforms/utility/dictionary.py index c4bd7d4cba..2d18a61409 100644 --- a/monai/transforms/utility/dictionary.py +++ b/monai/transforms/utility/dictionary.py @@ -770,7 +770,7 @@ def __init__( rescale_min: float = -1.0, rescale_max: float = 1.0, ): - super().__init__(keys) + MapTransform.__init__(self, keys) self.background = background self.pert = pert self.points: List[Tuple[int, ...]] = [] From 9edb551c0412f4aa36a6bb2b9a581f520d191df4 Mon Sep 17 00:00:00 2001 From: Rich <33289025+rijobro@users.noreply.github.com> Date: Fri, 26 Feb 2021 11:16:07 +0000 Subject: [PATCH 10/13] add RandomizableTransform Signed-off-by: Richard Brown <33289025+rijobro@users.noreply.github.com> --- docs/source/transforms.rst | 5 +++ monai/apps/deepgrow/transforms.py | 6 +-- monai/data/dataset.py | 13 +++--- monai/data/image_dataset.py | 5 ++- monai/transforms/__init__.py | 2 +- monai/transforms/compose.py | 9 ++-- monai/transforms/croppad/array.py | 10 ++--- monai/transforms/croppad/dictionary.py | 18 ++++---- monai/transforms/intensity/array.py | 30 ++++++------- monai/transforms/intensity/dictionary.py | 30 ++++++------- monai/transforms/spatial/array.py | 34 +++++++------- monai/transforms/spatial/dictionary.py | 30 ++++++------- monai/transforms/transform.py | 57 ++++++++++++++++-------- monai/transforms/utility/array.py | 4 +- monai/transforms/utility/dictionary.py | 8 ++-- tests/test_compose.py | 11 ++--- tests/test_image_dataset.py | 4 +- tests/test_rand_lambdad.py | 4 +- tests/test_randomizable.py | 4 +- 19 files changed, 156 insertions(+), 128 deletions(-) diff --git a/docs/source/transforms.rst b/docs/source/transforms.rst index 5b550f7885..a144c8c138 100644 --- a/docs/source/transforms.rst +++ b/docs/source/transforms.rst @@ -27,6 +27,11 @@ Generic Interfaces .. autoclass:: Randomizable :members: +`RandomizableTransform` +^^^^^^^^^^^^^^^^^^^^^^^ +.. autoclass:: RandomizableTransform + :members: + `Compose` ^^^^^^^^^ .. autoclass:: Compose diff --git a/monai/apps/deepgrow/transforms.py b/monai/apps/deepgrow/transforms.py index 80b0d1648d..3f4031fade 100644 --- a/monai/apps/deepgrow/transforms.py +++ b/monai/apps/deepgrow/transforms.py @@ -17,7 +17,7 @@ from monai.config import IndexSelection, KeysCollection from monai.networks.layers import GaussianFilter from monai.transforms import SpatialCrop -from monai.transforms.transform import MapTransform, Randomizable, Transform +from monai.transforms.transform import MapTransform, RandomizableTransform, Transform from monai.transforms.utils import generate_spatial_bounding_box from monai.utils import min_version, optional_import @@ -62,7 +62,7 @@ def __call__(self, data): return d -class AddInitialSeedPointd(Randomizable, Transform): +class AddInitialSeedPointd(RandomizableTransform): """ Add random guidance as initial seed point for a given label. @@ -279,7 +279,7 @@ def __call__(self, data): return d -class AddRandomGuidanced(Randomizable, Transform): +class AddRandomGuidanced(RandomizableTransform): """ Add random guidance based on discrepancies that were found between label and prediction. diff --git a/monai/data/dataset.py b/monai/data/dataset.py index db1ecc2b1f..bb5a98ba1e 100644 --- a/monai/data/dataset.py +++ b/monai/data/dataset.py @@ -26,6 +26,7 @@ from monai.data.utils import pickle_hashing from monai.transforms import Compose, Randomizable, Transform, apply_transform +from monai.transforms.transform import RandomizableTransform from monai.utils import MAX_SEED, get_seed, min_version, optional_import if TYPE_CHECKING: @@ -161,7 +162,7 @@ def _pre_transform(self, item_transformed): raise ValueError("transform must be an instance of monai.transforms.Compose.") for _transform in self.transform.transforms: # execute all the deterministic transforms - if isinstance(_transform, Randomizable) or not isinstance(_transform, Transform): + if isinstance(_transform, RandomizableTransform) or not isinstance(_transform, Transform): break item_transformed = apply_transform(_transform, item_transformed) return item_transformed @@ -183,7 +184,7 @@ def _post_transform(self, item_transformed): for _transform in self.transform.transforms: if ( start_post_randomize_run - or isinstance(_transform, Randomizable) + or isinstance(_transform, RandomizableTransform) or not isinstance(_transform, Transform) ): start_post_randomize_run = True @@ -522,7 +523,7 @@ def _load_cache_item(self, idx: int): raise ValueError("transform must be an instance of monai.transforms.Compose.") for _transform in self.transform.transforms: # execute all the deterministic transforms - if isinstance(_transform, Randomizable) or not isinstance(_transform, Transform): + if isinstance(_transform, RandomizableTransform) or not isinstance(_transform, Transform): break item = apply_transform(_transform, item) return item @@ -539,7 +540,7 @@ def __getitem__(self, index): if not isinstance(self.transform, Compose): raise ValueError("transform must be an instance of monai.transforms.Compose.") for _transform in self.transform.transforms: - if start_run or isinstance(_transform, Randomizable) or not isinstance(_transform, Transform): + if start_run or isinstance(_transform, RandomizableTransform) or not isinstance(_transform, Transform): start_run = True data = apply_transform(_transform, data) return data @@ -924,9 +925,9 @@ def __getitem__(self, index: int): # set transforms of each zip component for dataset in self.dataset.data: transform = getattr(dataset, "transform", None) - if isinstance(transform, Randomizable): + if isinstance(transform, RandomizableTransform): transform.set_random_state(seed=self._seed) transform = getattr(self.dataset, "transform", None) - if isinstance(transform, Randomizable): + if isinstance(transform, RandomizableTransform): transform.set_random_state(seed=self._seed) return self.dataset[index] diff --git a/monai/data/image_dataset.py b/monai/data/image_dataset.py index 1568e082ee..1074105508 100644 --- a/monai/data/image_dataset.py +++ b/monai/data/image_dataset.py @@ -17,6 +17,7 @@ from monai.config import DtypeLike from monai.data.image_reader import ImageReader from monai.transforms import LoadImage, Randomizable, apply_transform +from monai.transforms.transform import RandomizableTransform from monai.utils import MAX_SEED, get_seed @@ -106,14 +107,14 @@ def __getitem__(self, index: int): label = self.labels[index] if self.transform is not None: - if isinstance(self.transform, Randomizable): + if isinstance(self.transform, RandomizableTransform): self.transform.set_random_state(seed=self._seed) img = apply_transform(self.transform, img) data = [img] if self.seg_transform is not None: - if isinstance(self.seg_transform, Randomizable): + if isinstance(self.seg_transform, RandomizableTransform): self.seg_transform.set_random_state(seed=self._seed) seg = apply_transform(self.seg_transform, seg) diff --git a/monai/transforms/__init__.py b/monai/transforms/__init__.py index f5c7c826e9..8b30d76bec 100644 --- a/monai/transforms/__init__.py +++ b/monai/transforms/__init__.py @@ -237,7 +237,7 @@ ZoomD, ZoomDict, ) -from .transform import MapTransform, Randomizable, Transform +from .transform import MapTransform, Randomizable, RandomizableTransform, Transform from .utility.array import ( AddChannel, AddExtremePointsChannel, diff --git a/monai/transforms/compose.py b/monai/transforms/compose.py index 2d612ad2e3..a9f66b12a0 100644 --- a/monai/transforms/compose.py +++ b/monai/transforms/compose.py @@ -18,15 +18,14 @@ import numpy as np # For backwards compatiblity (so this still works: from monai.transforms.compose import MapTransform) -from monai.transforms.transform import MapTransform # noqa: F401 -from monai.transforms.transform import Randomizable, Transform +from monai.transforms.transform import MapTransform, Randomizable, RandomizableTransform, Transform # noqa: F401 from monai.transforms.utils import apply_transform from monai.utils import MAX_SEED, ensure_tuple, get_seed __all__ = ["Compose"] -class Compose(Randomizable, Transform): +class Compose(RandomizableTransform): """ ``Compose`` provides the ability to chain a series of calls together in a sequence. Each transform in the sequence must take a single argument and @@ -96,14 +95,14 @@ def __init__(self, transforms: Optional[Union[Sequence[Callable], Callable]] = N def set_random_state(self, seed: Optional[int] = None, state: Optional[np.random.RandomState] = None) -> "Compose": super().set_random_state(seed=seed, state=state) for _transform in self.transforms: - if not isinstance(_transform, Randomizable): + if not isinstance(_transform, RandomizableTransform): continue _transform.set_random_state(seed=self.R.randint(MAX_SEED, dtype="uint32")) return self def randomize(self, data: Optional[Any] = None) -> None: for _transform in self.transforms: - if not isinstance(_transform, Randomizable): + if not isinstance(_transform, RandomizableTransform): continue try: _transform.randomize(data) diff --git a/monai/transforms/croppad/array.py b/monai/transforms/croppad/array.py index ef5e0019bd..a3d36ad903 100644 --- a/monai/transforms/croppad/array.py +++ b/monai/transforms/croppad/array.py @@ -20,7 +20,7 @@ from monai.config import IndexSelection from monai.data.utils import get_random_patch, get_valid_patch_size -from monai.transforms.transform import Randomizable, Transform +from monai.transforms.transform import Randomizable, RandomizableTransform, Transform from monai.transforms.utils import ( generate_pos_neg_label_crop_centers, generate_spatial_bounding_box, @@ -276,7 +276,7 @@ def __call__(self, img: np.ndarray): return cropper(img) -class RandSpatialCrop(Randomizable, Transform): +class RandSpatialCrop(RandomizableTransform): """ Crop image with random size or specific size ROI. It can crop at a random position as center or at the image center. And allows to set the minimum size to limit the randomly generated ROI. @@ -321,7 +321,7 @@ def __call__(self, img: np.ndarray): return cropper(img) -class RandSpatialCropSamples(Randomizable, Transform): +class RandSpatialCropSamples(RandomizableTransform): """ Crop image with random size or specific size ROI to generate a list of N samples. It can crop at a random position as center or at the image center. And allows to set @@ -429,7 +429,7 @@ def __call__(self, img: np.ndarray): return cropped -class RandWeightedCrop(Randomizable, Transform): +class RandWeightedCrop(RandomizableTransform): """ Samples a list of `num_samples` image patches according to the provided `weight_map`. @@ -481,7 +481,7 @@ def __call__(self, img: np.ndarray, weight_map: Optional[np.ndarray] = None) -> return results -class RandCropByPosNegLabel(Randomizable, Transform): +class RandCropByPosNegLabel(RandomizableTransform): """ Crop random fixed sized regions with the center being a foreground or background voxel based on the Pos Neg Ratio. diff --git a/monai/transforms/croppad/dictionary.py b/monai/transforms/croppad/dictionary.py index 93780a28a0..9739c6322f 100644 --- a/monai/transforms/croppad/dictionary.py +++ b/monai/transforms/croppad/dictionary.py @@ -30,7 +30,7 @@ SpatialCrop, SpatialPad, ) -from monai.transforms.transform import MapTransform, Randomizable +from monai.transforms.transform import MapTransform, Randomizable, RandomizableTransform from monai.transforms.utils import ( generate_pos_neg_label_crop_centers, generate_spatial_bounding_box, @@ -258,7 +258,7 @@ def __call__(self, data: Mapping[Hashable, np.ndarray]) -> Dict[Hashable, np.nda return d -class RandSpatialCropd(Randomizable, MapTransform): +class RandSpatialCropd(RandomizableTransform, MapTransform): """ Dictionary-based version :py:class:`monai.transforms.RandSpatialCrop`. Crop image with random size or specific size ROI. It can crop at a random position as @@ -283,7 +283,7 @@ def __init__( random_center: bool = True, random_size: bool = True, ) -> None: - Randomizable.__init__(self) + RandomizableTransform.__init__(self) MapTransform.__init__(self, keys) self.roi_size = roi_size self.random_center = random_center @@ -313,7 +313,7 @@ def __call__(self, data: Mapping[Hashable, np.ndarray]) -> Dict[Hashable, np.nda return d -class RandSpatialCropSamplesd(Randomizable, MapTransform): +class RandSpatialCropSamplesd(RandomizableTransform, MapTransform): """ Dictionary-based version :py:class:`monai.transforms.RandSpatialCropSamples`. Crop image with random size or specific size ROI to generate a list of N samples. @@ -345,7 +345,7 @@ def __init__( random_center: bool = True, random_size: bool = True, ) -> None: - Randomizable.__init__(self) + RandomizableTransform.__init__(self) MapTransform.__init__(self, keys) if num_samples < 1: raise ValueError(f"num_samples must be positive, got {num_samples}.") @@ -422,7 +422,7 @@ def __call__(self, data: Mapping[Hashable, np.ndarray]) -> Dict[Hashable, np.nda return d -class RandWeightedCropd(Randomizable, MapTransform): +class RandWeightedCropd(RandomizableTransform, MapTransform): """ Samples a list of `num_samples` image patches according to the provided `weight_map`. @@ -448,7 +448,7 @@ def __init__( num_samples: int = 1, center_coord_key: Optional[str] = None, ): - Randomizable.__init__(self) + RandomizableTransform.__init__(self) MapTransform.__init__(self, keys) self.spatial_size = ensure_tuple(spatial_size) self.w_key = w_key @@ -487,7 +487,7 @@ def __call__(self, data: Mapping[Hashable, np.ndarray]) -> List[Dict[Hashable, n return results -class RandCropByPosNegLabeld(Randomizable, MapTransform): +class RandCropByPosNegLabeld(RandomizableTransform, MapTransform): """ Dictionary-based version :py:class:`monai.transforms.RandCropByPosNegLabel`. Crop random fixed sized regions with the center being a foreground or background voxel @@ -537,7 +537,7 @@ def __init__( fg_indices_key: Optional[str] = None, bg_indices_key: Optional[str] = None, ) -> None: - Randomizable.__init__(self) + RandomizableTransform.__init__(self) MapTransform.__init__(self, keys) self.label_key = label_key self.spatial_size: Union[Tuple[int, ...], Sequence[int], int] = spatial_size diff --git a/monai/transforms/intensity/array.py b/monai/transforms/intensity/array.py index f955be305c..1bddc0137d 100644 --- a/monai/transforms/intensity/array.py +++ b/monai/transforms/intensity/array.py @@ -22,7 +22,7 @@ from monai.config import DtypeLike from monai.networks.layers import GaussianFilter, HilbertTransform, SavitzkyGolayFilter -from monai.transforms.transform import Randomizable, Transform +from monai.transforms.transform import RandomizableTransform, Transform from monai.transforms.utils import rescale_array from monai.utils import PT_BEFORE_1_7, InvalidPyTorchVersionError, dtype_torch_to_numpy, ensure_tuple_size @@ -49,7 +49,7 @@ ] -class RandGaussianNoise(Randomizable, Transform): +class RandGaussianNoise(RandomizableTransform): """ Add Gaussian noise to image. @@ -60,7 +60,7 @@ class RandGaussianNoise(Randomizable, Transform): """ def __init__(self, prob: float = 0.1, mean: Union[Sequence[float], float] = 0.0, std: float = 0.1) -> None: - Randomizable.__init__(self, prob) + RandomizableTransform.__init__(self, prob) self.mean = mean self.std = std self._noise = None @@ -100,7 +100,7 @@ def __call__(self, img: np.ndarray) -> np.ndarray: return np.asarray((img + self.offset), dtype=img.dtype) -class RandShiftIntensity(Randomizable, Transform): +class RandShiftIntensity(RandomizableTransform): """ Randomly shift intensity with randomly picked offset. """ @@ -112,7 +112,7 @@ def __init__(self, offsets: Union[Tuple[float, float], float], prob: float = 0.1 if single number, offset value is picked from (-offsets, offsets). prob: probability of shift. """ - Randomizable.__init__(self, prob) + RandomizableTransform.__init__(self, prob) if isinstance(offsets, (int, float)): self.offsets = (min(-offsets, offsets), max(-offsets, offsets)) else: @@ -169,7 +169,7 @@ def __call__(self, img: np.ndarray) -> np.ndarray: raise ValueError("Incompatible values: minv=None or maxv=None and factor=None.") -class RandScaleIntensity(Randomizable, Transform): +class RandScaleIntensity(RandomizableTransform): """ Randomly scale the intensity of input image by ``v = v * (1 + factor)`` where the `factor` is randomly picked from (-factors[0], factors[0]). @@ -183,7 +183,7 @@ def __init__(self, factors: Union[Tuple[float, float], float], prob: float = 0.1 prob: probability of scale. """ - Randomizable.__init__(self, prob) + RandomizableTransform.__init__(self, prob) if isinstance(factors, (int, float)): self.factors = (min(-factors, factors), max(-factors, factors)) else: @@ -365,7 +365,7 @@ def __call__(self, img: np.ndarray): return np.power(((img - img_min) / float(img_range + epsilon)), self.gamma) * img_range + img_min -class RandAdjustContrast(Randomizable, Transform): +class RandAdjustContrast(RandomizableTransform): """ Randomly changes image intensity by gamma. Each pixel/voxel intensity is updated as:: @@ -378,7 +378,7 @@ class RandAdjustContrast(Randomizable, Transform): """ def __init__(self, prob: float = 0.1, gamma: Union[Sequence[float], float] = (0.5, 4.5)) -> None: - Randomizable.__init__(self, prob) + RandomizableTransform.__init__(self, prob) if isinstance(gamma, (int, float)): if gamma <= 0.5: @@ -651,7 +651,7 @@ def __call__(self, img: np.ndarray): return gaussian_filter(input_data).squeeze(0).detach().numpy() -class RandGaussianSmooth(Randomizable, Transform): +class RandGaussianSmooth(RandomizableTransform): """ Apply Gaussian smooth to the input data based on randomly selected `sigma` parameters. @@ -673,7 +673,7 @@ def __init__( prob: float = 0.1, approx: str = "erf", ) -> None: - Randomizable.__init__(self, prob) + RandomizableTransform.__init__(self, prob) self.sigma_x = sigma_x self.sigma_y = sigma_y self.sigma_z = sigma_z @@ -741,7 +741,7 @@ def __call__(self, img: np.ndarray): return (blurred_f + self.alpha * (blurred_f - filter_blurred_f)).squeeze(0).detach().numpy() -class RandGaussianSharpen(Randomizable, Transform): +class RandGaussianSharpen(RandomizableTransform): """ Sharpen images using the Gaussian Blur filter based on randomly selected `sigma1`, `sigma2` and `alpha`. The algorithm is :py:class:`monai.transforms.GaussianSharpen`. @@ -775,7 +775,7 @@ def __init__( approx: str = "erf", prob: float = 0.1, ) -> None: - Randomizable.__init__(self, prob) + RandomizableTransform.__init__(self, prob) self.sigma1_x = sigma1_x self.sigma1_y = sigma1_y self.sigma1_z = sigma1_z @@ -807,7 +807,7 @@ def __call__(self, img: np.ndarray): return GaussianSharpen(sigma1=sigma1, sigma2=sigma2, alpha=self.a, approx=self.approx)(img) -class RandHistogramShift(Randomizable, Transform): +class RandHistogramShift(RandomizableTransform): """ Apply random nonlinear transform to the image's intensity histogram. @@ -819,7 +819,7 @@ class RandHistogramShift(Randomizable, Transform): """ def __init__(self, num_control_points: Union[Tuple[int, int], int] = 10, prob: float = 0.1) -> None: - Randomizable.__init__(self, prob) + RandomizableTransform.__init__(self, prob) if isinstance(num_control_points, int): if num_control_points <= 2: diff --git a/monai/transforms/intensity/dictionary.py b/monai/transforms/intensity/dictionary.py index f271c27274..7d0d66d2ba 100644 --- a/monai/transforms/intensity/dictionary.py +++ b/monai/transforms/intensity/dictionary.py @@ -34,7 +34,7 @@ ShiftIntensity, ThresholdIntensity, ) -from monai.transforms.transform import MapTransform, Randomizable +from monai.transforms.transform import MapTransform, RandomizableTransform from monai.utils import dtype_torch_to_numpy, ensure_tuple_rep, ensure_tuple_size __all__ = [ @@ -92,7 +92,7 @@ ] -class RandGaussianNoised(Randomizable, MapTransform): +class RandGaussianNoised(RandomizableTransform, MapTransform): """ Dictionary-based version :py:class:`monai.transforms.RandGaussianNoise`. Add Gaussian noise to image. This transform assumes all the expected fields have same shape. @@ -109,7 +109,7 @@ def __init__( self, keys: KeysCollection, prob: float = 0.1, mean: Union[Sequence[float], float] = 0.0, std: float = 0.1 ) -> None: MapTransform.__init__(self, keys) - Randomizable.__init__(self, prob) + RandomizableTransform.__init__(self, prob) self.mean = ensure_tuple_rep(mean, len(self.keys)) self.std = std self._noise: List[np.ndarray] = [] @@ -157,7 +157,7 @@ def __call__(self, data: Mapping[Hashable, np.ndarray]) -> Dict[Hashable, np.nda return d -class RandShiftIntensityd(Randomizable, MapTransform): +class RandShiftIntensityd(RandomizableTransform, MapTransform): """ Dictionary-based version :py:class:`monai.transforms.RandShiftIntensity`. """ @@ -173,7 +173,7 @@ def __init__(self, keys: KeysCollection, offsets: Union[Tuple[float, float], flo (Default 0.1, with 10% probability it returns a rotated array.) """ MapTransform.__init__(self, keys) - Randomizable.__init__(self, prob) + RandomizableTransform.__init__(self, prob) if isinstance(offsets, (int, float)): self.offsets = (min(-offsets, offsets), max(-offsets, offsets)) @@ -226,7 +226,7 @@ def __call__(self, data: Mapping[Hashable, np.ndarray]) -> Dict[Hashable, np.nda return d -class RandScaleIntensityd(Randomizable, MapTransform): +class RandScaleIntensityd(RandomizableTransform, MapTransform): """ Dictionary-based version :py:class:`monai.transforms.RandScaleIntensity`. """ @@ -243,7 +243,7 @@ def __init__(self, keys: KeysCollection, factors: Union[Tuple[float, float], flo """ MapTransform.__init__(self, keys) - Randomizable.__init__(self, prob) + RandomizableTransform.__init__(self, prob) if isinstance(factors, (int, float)): self.factors = (min(-factors, factors), max(-factors, factors)) @@ -377,7 +377,7 @@ def __call__(self, data: Mapping[Hashable, np.ndarray]) -> Dict[Hashable, np.nda return d -class RandAdjustContrastd(Randomizable, MapTransform): +class RandAdjustContrastd(RandomizableTransform, MapTransform): """ Dictionary-based version :py:class:`monai.transforms.RandAdjustContrast`. Randomly changes image intensity by gamma. Each pixel/voxel intensity is updated as: @@ -396,7 +396,7 @@ def __init__( self, keys: KeysCollection, prob: float = 0.1, gamma: Union[Tuple[float, float], float] = (0.5, 4.5) ) -> None: MapTransform.__init__(self, keys) - Randomizable.__init__(self, prob) + RandomizableTransform.__init__(self, prob) if isinstance(gamma, (int, float)): if gamma <= 0.5: @@ -523,7 +523,7 @@ def __call__(self, data: Mapping[Hashable, np.ndarray]) -> Dict[Hashable, np.nda return d -class RandGaussianSmoothd(Randomizable, MapTransform): +class RandGaussianSmoothd(RandomizableTransform, MapTransform): """ Dictionary-based wrapper of :py:class:`monai.transforms.GaussianSmooth`. @@ -549,7 +549,7 @@ def __init__( prob: float = 0.1, ) -> None: MapTransform.__init__(self, keys) - Randomizable.__init__(self, prob) + RandomizableTransform.__init__(self, prob) self.sigma_x = sigma_x self.sigma_y = sigma_y self.sigma_z = sigma_z @@ -609,7 +609,7 @@ def __call__(self, data: Mapping[Hashable, np.ndarray]) -> Dict[Hashable, np.nda return d -class RandGaussianSharpend(Randomizable, MapTransform): +class RandGaussianSharpend(RandomizableTransform, MapTransform): """ Dictionary-based wrapper of :py:class:`monai.transforms.GaussianSharpen`. @@ -646,7 +646,7 @@ def __init__( prob: float = 0.1, ): MapTransform.__init__(self, keys) - Randomizable.__init__(self, prob) + RandomizableTransform.__init__(self, prob) self.sigma1_x = sigma1_x self.sigma1_y = sigma1_y self.sigma1_z = sigma1_z @@ -681,7 +681,7 @@ def __call__(self, data): return d -class RandHistogramShiftd(Randomizable, MapTransform): +class RandHistogramShiftd(RandomizableTransform, MapTransform): """ Dictionary-based version :py:class:`monai.transforms.RandHistogramShift`. Apply random nonlinear transform the the image's intensity histogram. @@ -699,7 +699,7 @@ def __init__( self, keys: KeysCollection, num_control_points: Union[Tuple[int, int], int] = 10, prob: float = 0.1 ) -> None: MapTransform.__init__(self, keys) - Randomizable.__init__(self, prob) + RandomizableTransform.__init__(self, prob) if isinstance(num_control_points, int): if num_control_points <= 2: raise AssertionError("num_control_points should be greater than or equal to 3") diff --git a/monai/transforms/spatial/array.py b/monai/transforms/spatial/array.py index fe773789a4..3559d0eb3c 100644 --- a/monai/transforms/spatial/array.py +++ b/monai/transforms/spatial/array.py @@ -23,7 +23,7 @@ from monai.data.utils import compute_shape_offset, to_affine_nd, zoom_affine from monai.networks.layers import AffineTransform, GaussianFilter, grid_pull from monai.transforms.croppad.array import CenterSpatialCrop -from monai.transforms.transform import Randomizable, Transform +from monai.transforms.transform import RandomizableTransform, Transform from monai.transforms.utils import ( create_control_grid, create_grid, @@ -604,7 +604,7 @@ def __call__(self, img: np.ndarray) -> np.ndarray: return result.astype(img.dtype) -class RandRotate90(Randomizable, Transform): +class RandRotate90(RandomizableTransform): """ With probability `prob`, input arrays are rotated by 90 degrees in the plane specified by `spatial_axes`. @@ -619,7 +619,7 @@ def __init__(self, prob: float = 0.1, max_k: int = 3, spatial_axes: Tuple[int, i spatial_axes: 2 int numbers, defines the plane to rotate with 2 spatial axes. Default: (0, 1), this is the first two axis in spatial dimensions. """ - Randomizable.__init__(self, min(max(prob, 0.0), 1.0)) + RandomizableTransform.__init__(self, min(max(prob, 0.0), 1.0)) self.max_k = max_k self.spatial_axes = spatial_axes @@ -641,7 +641,7 @@ def __call__(self, img: np.ndarray) -> np.ndarray: return rotator(img) -class RandRotate(Randomizable, Transform): +class RandRotate(RandomizableTransform): """ Randomly rotate the input arrays. @@ -681,7 +681,7 @@ def __init__( align_corners: bool = False, dtype: DtypeLike = np.float64, ) -> None: - Randomizable.__init__(self, prob) + RandomizableTransform.__init__(self, prob) self.range_x = ensure_tuple(range_x) if len(self.range_x) == 1: self.range_x = tuple(sorted([-self.range_x[0], self.range_x[0]])) @@ -745,7 +745,7 @@ def __call__( return rotator(img) -class RandFlip(Randomizable, Transform): +class RandFlip(RandomizableTransform): """ Randomly flips the image along axes. Preserves shape. See numpy.flip for additional details. @@ -757,7 +757,7 @@ class RandFlip(Randomizable, Transform): """ def __init__(self, prob: float = 0.1, spatial_axis: Optional[Union[Sequence[int], int]] = None) -> None: - Randomizable.__init__(self, min(max(prob, 0.0), 1.0)) + RandomizableTransform.__init__(self, min(max(prob, 0.0), 1.0)) self.flipper = Flip(spatial_axis=spatial_axis) def __call__(self, img: np.ndarray) -> np.ndarray: @@ -771,7 +771,7 @@ def __call__(self, img: np.ndarray) -> np.ndarray: return self.flipper(img) -class RandZoom(Randomizable, Transform): +class RandZoom(RandomizableTransform): """ Randomly zooms input arrays with given probability within given zoom range. @@ -810,7 +810,7 @@ def __init__( align_corners: Optional[bool] = None, keep_size: bool = True, ) -> None: - Randomizable.__init__(self, prob) + RandomizableTransform.__init__(self, prob) self.min_zoom = ensure_tuple(min_zoom) self.max_zoom = ensure_tuple(max_zoom) if len(self.min_zoom) != len(self.max_zoom): @@ -954,7 +954,7 @@ def __call__( return np.asarray(grid.cpu().numpy()) -class RandAffineGrid(Randomizable, Transform): +class RandAffineGrid(RandomizableTransform): """ Generate randomised affine grid. """ @@ -1043,7 +1043,7 @@ def __call__( return affine_grid(spatial_size, grid) -class RandDeformGrid(Randomizable, Transform): +class RandDeformGrid(RandomizableTransform): """ Generate random deformation grid. """ @@ -1272,7 +1272,7 @@ def __call__( ) -class RandAffine(Randomizable, Transform): +class RandAffine(RandomizableTransform): """ Random affine transform. """ @@ -1324,7 +1324,7 @@ def __init__( - :py:class:`RandAffineGrid` for the random affine parameters configurations. - :py:class:`Affine` for the affine transformation parameters configurations. """ - Randomizable.__init__(self, prob) + RandomizableTransform.__init__(self, prob) self.rand_affine_grid = RandAffineGrid( rotate_range=rotate_range, @@ -1385,7 +1385,7 @@ def __call__( ) -class Rand2DElastic(Randomizable, Transform): +class Rand2DElastic(RandomizableTransform): """ Random elastic deformation and affine in 2D """ @@ -1442,7 +1442,7 @@ def __init__( - :py:class:`RandAffineGrid` for the random affine parameters configurations. - :py:class:`Affine` for the affine transformation parameters configurations. """ - Randomizable.__init__(self, prob) + RandomizableTransform.__init__(self, prob) self.deform_grid = RandDeformGrid( spacing=spacing, magnitude_range=magnitude_range, as_tensor_output=True, device=device ) @@ -1511,7 +1511,7 @@ def __call__( return self.resampler(img, grid, mode=mode or self.mode, padding_mode=padding_mode or self.padding_mode) -class Rand3DElastic(Randomizable, Transform): +class Rand3DElastic(RandomizableTransform): """ Random elastic deformation and affine in 3D """ @@ -1570,7 +1570,7 @@ def __init__( - :py:class:`RandAffineGrid` for the random affine parameters configurations. - :py:class:`Affine` for the affine transformation parameters configurations. """ - Randomizable.__init__(self, prob) + RandomizableTransform.__init__(self, prob) self.rand_affine_grid = RandAffineGrid(rotate_range, shear_range, translate_range, scale_range, True, device) self.resampler = Resample(as_tensor_output=as_tensor_output, device=device) diff --git a/monai/transforms/spatial/dictionary.py b/monai/transforms/spatial/dictionary.py index 44824c847e..6693d75bcd 100644 --- a/monai/transforms/spatial/dictionary.py +++ b/monai/transforms/spatial/dictionary.py @@ -35,7 +35,7 @@ Spacing, Zoom, ) -from monai.transforms.transform import MapTransform, Randomizable +from monai.transforms.transform import MapTransform, RandomizableTransform from monai.transforms.utils import create_grid from monai.utils import ( GridSampleMode, @@ -274,7 +274,7 @@ def __call__(self, data: Mapping[Hashable, np.ndarray]) -> Dict[Hashable, np.nda return d -class RandRotate90d(Randomizable, MapTransform): +class RandRotate90d(RandomizableTransform, MapTransform): """ Dictionary-based version :py:class:`monai.transforms.RandRotate90`. With probability `prob`, input arrays are rotated by 90 degrees @@ -300,7 +300,7 @@ def __init__( Default: (0, 1), this is the first two axis in spatial dimensions. """ MapTransform.__init__(self, keys) - Randomizable.__init__(self, prob) + RandomizableTransform.__init__(self, prob) self.max_k = max_k self.spatial_axes = spatial_axes @@ -362,7 +362,7 @@ def __call__(self, data: Mapping[Hashable, np.ndarray]) -> Dict[Hashable, np.nda return d -class RandAffined(Randomizable, MapTransform): +class RandAffined(RandomizableTransform, MapTransform): """ Dictionary-based wrapper of :py:class:`monai.transforms.RandAffine`. """ @@ -419,7 +419,7 @@ def __init__( - :py:class:`RandAffineGrid` for the random affine parameters configurations. """ MapTransform.__init__(self, keys) - Randomizable.__init__(self, prob) + RandomizableTransform.__init__(self, prob) self.rand_affine = RandAffine( prob=1.0, # because probability handled in this class rotate_range=rotate_range, @@ -461,7 +461,7 @@ def __call__( return d -class Rand2DElasticd(Randomizable, MapTransform): +class Rand2DElasticd(RandomizableTransform, MapTransform): """ Dictionary-based wrapper of :py:class:`monai.transforms.Rand2DElastic`. """ @@ -524,7 +524,7 @@ def __init__( - :py:class:`Affine` for the affine transformation parameters configurations. """ MapTransform.__init__(self, keys) - Randomizable.__init__(self, prob) + RandomizableTransform.__init__(self, prob) self.rand_2d_elastic = Rand2DElastic( spacing=spacing, magnitude_range=magnitude_range, @@ -580,7 +580,7 @@ def __call__( return d -class Rand3DElasticd(Randomizable, MapTransform): +class Rand3DElasticd(RandomizableTransform, MapTransform): """ Dictionary-based wrapper of :py:class:`monai.transforms.Rand3DElastic`. """ @@ -644,7 +644,7 @@ def __init__( - :py:class:`Affine` for the affine transformation parameters configurations. """ MapTransform.__init__(self, keys) - Randomizable.__init__(self, prob) + RandomizableTransform.__init__(self, prob) self.rand_3d_elastic = Rand3DElastic( sigma_range=sigma_range, magnitude_range=magnitude_range, @@ -717,7 +717,7 @@ def __call__(self, data: Mapping[Hashable, np.ndarray]) -> Dict[Hashable, np.nda return d -class RandFlipd(Randomizable, MapTransform): +class RandFlipd(RandomizableTransform, MapTransform): """ Dictionary-based version :py:class:`monai.transforms.RandFlip`. @@ -737,7 +737,7 @@ def __init__( spatial_axis: Optional[Union[Sequence[int], int]] = None, ) -> None: MapTransform.__init__(self, keys) - Randomizable.__init__(self, prob) + RandomizableTransform.__init__(self, prob) self.spatial_axis = spatial_axis self.flipper = Flip(spatial_axis=spatial_axis) @@ -809,7 +809,7 @@ def __call__(self, data: Mapping[Hashable, np.ndarray]) -> Dict[Hashable, np.nda return d -class RandRotated(Randomizable, MapTransform): +class RandRotated(RandomizableTransform, MapTransform): """ Dictionary-based version :py:class:`monai.transforms.RandRotate` Randomly rotates the input arrays. @@ -857,7 +857,7 @@ def __init__( dtype: Union[Sequence[DtypeLike], DtypeLike] = np.float64, ) -> None: MapTransform.__init__(self, keys) - Randomizable.__init__(self, prob) + RandomizableTransform.__init__(self, prob) self.range_x = ensure_tuple(range_x) if len(self.range_x) == 1: self.range_x = tuple(sorted([-self.range_x[0], self.range_x[0]])) @@ -955,7 +955,7 @@ def __call__(self, data: Mapping[Hashable, np.ndarray]) -> Dict[Hashable, np.nda return d -class RandZoomd(Randomizable, MapTransform): +class RandZoomd(RandomizableTransform, MapTransform): """ Dict-based version :py:class:`monai.transforms.RandZoom`. @@ -999,7 +999,7 @@ def __init__( keep_size: bool = True, ) -> None: MapTransform.__init__(self, keys) - Randomizable.__init__(self, prob) + RandomizableTransform.__init__(self, prob) self.min_zoom = ensure_tuple(min_zoom) self.max_zoom = ensure_tuple(max_zoom) if len(self.min_zoom) != len(self.max_zoom): diff --git a/monai/transforms/transform.py b/monai/transforms/transform.py index db3d7a5306..9c9729d250 100644 --- a/monai/transforms/transform.py +++ b/monai/transforms/transform.py @@ -20,33 +20,17 @@ from monai.config import KeysCollection from monai.utils import MAX_SEED, ensure_tuple -__all__ = ["Randomizable", "Transform", "MapTransform"] +__all__ = ["Randomizable", "RandomizableTransform", "Transform", "MapTransform"] class Randomizable(ABC): """ An interface for handling random state locally, currently based on a class variable `R`, which is an instance of `np.random.RandomState`. - This is mainly for randomized data augmentation transforms. For example:: - - class RandShiftIntensity(Randomizable): - def randomize(): - self._offset = self.R.uniform(low=0, high=100) - def __call__(self, img): - self.randomize() - return img + self._offset - - transform = RandShiftIntensity() - transform.set_random_state(seed=0) - """ R: np.random.RandomState = np.random.RandomState() - def __init__(self, prob=1.0, do_transform=False): - self._do_transform = do_transform - self.prob = min(max(prob, 0.0), 1.0) - def set_random_state( self, seed: Optional[int] = None, state: Optional[np.random.RandomState] = None ) -> "Randomizable": @@ -89,8 +73,11 @@ def randomize(self, data: Any) -> None: identify errors of sync the random state. This method can generate the random factors based on properties of the input data. + + Raises: + NotImplementedError: When the subclass does not override this method. """ - self._do_transform = self.R.rand() < self.prob + raise NotImplementedError(f"Subclass {self.__class__.__name__} must implement this method.") class Transform(ABC): @@ -141,6 +128,40 @@ def __call__(self, data: Any): raise NotImplementedError(f"Subclass {self.__class__.__name__} must implement this method.") +class RandomizableTransform(Randomizable, Transform): + """ + An interface for handling random state locally, currently based on a class variable `R`, + which is an instance of `np.random.RandomState`. + This is mainly for randomized data augmentation transforms. For example:: + + class RandShiftIntensity(RandomizableTransform): + def randomize(): + self._offset = self.R.uniform(low=0, high=100) + def __call__(self, img): + self.randomize() + return img + self._offset + + transform = RandShiftIntensity() + transform.set_random_state(seed=0) + + """ + + def __init__(self, prob=1.0, do_transform=False): + self._do_transform = do_transform + self.prob = min(max(prob, 0.0), 1.0) + + def randomize(self, data: Any) -> None: + """ + Within this method, :py:attr:`self.R` should be used, instead of `np.random`, to introduce random factors. + + all :py:attr:`self.R` calls happen here so that we have a better chance to + identify errors of sync the random state. + + This method can generate the random factors based on properties of the input data. + """ + self._do_transform = self.R.rand() < self.prob + + class MapTransform(Transform): """ A subclass of :py:class:`monai.transforms.Transform` with an assumption diff --git a/monai/transforms/utility/array.py b/monai/transforms/utility/array.py index 0ee88e1a6c..24d2feb781 100644 --- a/monai/transforms/utility/array.py +++ b/monai/transforms/utility/array.py @@ -21,7 +21,7 @@ import torch from monai.config import DtypeLike, NdarrayTensor -from monai.transforms.transform import Randomizable, Transform +from monai.transforms.transform import RandomizableTransform, Transform from monai.transforms.utils import extreme_points_to_image, get_extreme_points, map_binary_to_indices from monai.utils import ensure_tuple, min_version, optional_import @@ -631,7 +631,7 @@ def __call__(self, img: np.ndarray) -> np.ndarray: return np.stack(result, axis=0).astype(np.float32) -class AddExtremePointsChannel(Transform, Randomizable): +class AddExtremePointsChannel(RandomizableTransform): """ Add extreme points of label to the image as a new channel. This transform generates extreme point from label and applies a gaussian filter. The pixel values in points image are rescaled diff --git a/monai/transforms/utility/dictionary.py b/monai/transforms/utility/dictionary.py index b50c1af435..e9d923d0fd 100644 --- a/monai/transforms/utility/dictionary.py +++ b/monai/transforms/utility/dictionary.py @@ -23,7 +23,7 @@ import torch from monai.config import DtypeLike, KeysCollection, NdarrayTensor -from monai.transforms.transform import MapTransform, Randomizable +from monai.transforms.transform import MapTransform, RandomizableTransform from monai.transforms.utility.array import ( AddChannel, AsChannelFirst, @@ -682,9 +682,9 @@ def __call__(self, data): return d -class RandLambdad(Lambdad, Randomizable): +class RandLambdad(Lambdad, RandomizableTransform): """ - Randomizable version :py:class:`monai.transforms.Lambdad`, the input `func` contains random logic. + RandomizableTransform version :py:class:`monai.transforms.Lambdad`, the input `func` contains random logic. It's a randomizable transform so `CacheDataset` will not execute it and cache the results. Args: @@ -800,7 +800,7 @@ def __call__(self, data: Mapping[Hashable, np.ndarray]) -> Dict[Hashable, np.nda return d -class AddExtremePointsChanneld(Randomizable, MapTransform): +class AddExtremePointsChanneld(RandomizableTransform, MapTransform): """ Dictionary-based wrapper of :py:class:`monai.transforms.AddExtremePointsChannel`. diff --git a/tests/test_compose.py b/tests/test_compose.py index 3a0a6ea5bb..ac9368cb0f 100644 --- a/tests/test_compose.py +++ b/tests/test_compose.py @@ -13,11 +13,12 @@ import unittest from monai.data import DataLoader, Dataset -from monai.transforms import AddChannel, Compose, Randomizable +from monai.transforms import AddChannel, Compose +from monai.transforms.transform import RandomizableTransform from monai.utils import set_determinism -class _RandXform(Randomizable): +class _RandXform(RandomizableTransform): def randomize(self): self.val = self.R.random_sample() @@ -79,7 +80,7 @@ def c(d): # transform to handle dict data self.assertDictEqual(item, {"a": 2, "b": 1, "c": 2}) def test_random_compose(self): - class _Acc(Randomizable): + class _Acc(RandomizableTransform): self.rand = 0.0 def randomize(self, data=None): @@ -98,7 +99,7 @@ def __call__(self, data): self.assertAlmostEqual(c(1), 1.90734751) def test_randomize_warn(self): - class _RandomClass(Randomizable): + class _RandomClass(RandomizableTransform): def randomize(self, foo1, foo2): pass @@ -168,7 +169,7 @@ def test_flatten_and_len(self): self.assertEqual(len(t1), 8) def test_backwards_compatible_imports(self): - from monai.transforms.compose import MapTransform, Randomizable, Transform # noqa: F401 + from monai.transforms.compose import MapTransform, RandomizableTransform, Transform # noqa: F401 if __name__ == "__main__": diff --git a/tests/test_image_dataset.py b/tests/test_image_dataset.py index d79a7d884c..ec2cf77cd8 100644 --- a/tests/test_image_dataset.py +++ b/tests/test_image_dataset.py @@ -17,12 +17,12 @@ import numpy as np from monai.data import ImageDataset -from monai.transforms import Randomizable +from monai.transforms.transform import RandomizableTransform FILENAMES = ["test1.nii.gz", "test2.nii", "test3.nii.gz"] -class RandTest(Randomizable): +class RandTest(RandomizableTransform): """ randomisable transform for testing. """ diff --git a/tests/test_rand_lambdad.py b/tests/test_rand_lambdad.py index 359da8857a..2ddfeefae0 100644 --- a/tests/test_rand_lambdad.py +++ b/tests/test_rand_lambdad.py @@ -13,11 +13,11 @@ import numpy as np -from monai.transforms import Randomizable +from monai.transforms.transform import RandomizableTransform from monai.transforms.utility.dictionary import RandLambdad -class RandTest(Randomizable): +class RandTest(RandomizableTransform): """ randomisable transform for testing. """ diff --git a/tests/test_randomizable.py b/tests/test_randomizable.py index a7a30124df..9c9e4ddfc0 100644 --- a/tests/test_randomizable.py +++ b/tests/test_randomizable.py @@ -13,10 +13,10 @@ import numpy as np -from monai.transforms import Randomizable +from monai.transforms.transform import RandomizableTransform -class RandTest(Randomizable): +class RandTest(RandomizableTransform): def randomize(self, data=None): pass From a9fe721dd00a21c8f41f1e27b3f6b1dab06f6a84 Mon Sep 17 00:00:00 2001 From: Rich <33289025+rijobro@users.noreply.github.com> Date: Fri, 26 Feb 2021 11:33:02 +0000 Subject: [PATCH 11/13] bug fixes Signed-off-by: Richard Brown <33289025+rijobro@users.noreply.github.com> --- tests/test_compose.py | 4 ++-- tests/test_randomizable.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/test_compose.py b/tests/test_compose.py index ac9368cb0f..e9315eacd2 100644 --- a/tests/test_compose.py +++ b/tests/test_compose.py @@ -14,7 +14,7 @@ from monai.data import DataLoader, Dataset from monai.transforms import AddChannel, Compose -from monai.transforms.transform import RandomizableTransform +from monai.transforms.transform import Randomizable, RandomizableTransform from monai.utils import set_determinism @@ -99,7 +99,7 @@ def __call__(self, data): self.assertAlmostEqual(c(1), 1.90734751) def test_randomize_warn(self): - class _RandomClass(RandomizableTransform): + class _RandomClass(Randomizable): def randomize(self, foo1, foo2): pass diff --git a/tests/test_randomizable.py b/tests/test_randomizable.py index 9c9e4ddfc0..9972bded0f 100644 --- a/tests/test_randomizable.py +++ b/tests/test_randomizable.py @@ -13,10 +13,10 @@ import numpy as np -from monai.transforms.transform import RandomizableTransform +from monai.transforms.transform import Randomizable -class RandTest(RandomizableTransform): +class RandTest(Randomizable): def randomize(self, data=None): pass From d80be1fb69026024454047a3a5e8bfd10efb2501 Mon Sep 17 00:00:00 2001 From: Rich <33289025+rijobro@users.noreply.github.com> Date: Fri, 26 Feb 2021 11:34:51 +0000 Subject: [PATCH 12/13] bug fix2 Signed-off-by: Richard Brown <33289025+rijobro@users.noreply.github.com> --- tests/test_compose.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tests/test_compose.py b/tests/test_compose.py index e9315eacd2..d40dbd23b2 100644 --- a/tests/test_compose.py +++ b/tests/test_compose.py @@ -99,10 +99,13 @@ def __call__(self, data): self.assertAlmostEqual(c(1), 1.90734751) def test_randomize_warn(self): - class _RandomClass(Randomizable): + class _RandomClass(RandomizableTransform): def randomize(self, foo1, foo2): pass + def __call__(self, data): + pass + c = Compose([_RandomClass(), _RandomClass()]) with self.assertWarns(Warning): c.randomize() From 6d791a6da57412aa0ade5b30af111fa48cdcab51 Mon Sep 17 00:00:00 2001 From: Richard Brown <33289025+rijobro@users.noreply.github.com> Date: Fri, 26 Feb 2021 11:51:35 +0000 Subject: [PATCH 13/13] bugfix2 Signed-off-by: Richard Brown <33289025+rijobro@users.noreply.github.com> --- tests/test_compose.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_compose.py b/tests/test_compose.py index d40dbd23b2..bb8a5f08c5 100644 --- a/tests/test_compose.py +++ b/tests/test_compose.py @@ -14,7 +14,7 @@ from monai.data import DataLoader, Dataset from monai.transforms import AddChannel, Compose -from monai.transforms.transform import Randomizable, RandomizableTransform +from monai.transforms.transform import RandomizableTransform from monai.utils import set_determinism