def __init__( self, rotate_range: Optional[Union[Sequence[float], float]] = None, shear_range: Optional[Union[Sequence[float], float]] = None, translate_range: Optional[Union[Sequence[float], float]] = None, scale_range: Optional[Union[Sequence[float], float]] = None, as_tensor_output: bool = True, device: Optional[torch.device] = None, ) -> None: """ Args: rotate_range: angle range in radians. rotate_range[0] with be used to generate the 1st rotation parameter from `uniform[-rotate_range[0], rotate_range[0])`. Similarly, `rotate_range[1]` and `rotate_range[2]` are used in 3D affine for the range of 2nd and 3rd axes. shear_range: shear_range[0] with be used to generate the 1st shearing parameter from `uniform[-shear_range[0], shear_range[0])`. Similarly, `shear_range[1]` to `shear_range[N]` controls the range of the uniform distribution used to generate the 2nd to N-th parameter. translate_range : translate_range[0] with be used to generate the 1st shift parameter from `uniform[-translate_range[0], translate_range[0])`. Similarly, `translate_range[1]` to `translate_range[N]` controls the range of the uniform distribution used to generate the 2nd to N-th parameter. scale_range: scaling_range[0] with be used to generate the 1st scaling factor from `uniform[-scale_range[0], scale_range[0]) + 1.0`. Similarly, `scale_range[1]` to `scale_range[N]` controls the range of the uniform distribution used to generate the 2nd to N-th parameter. as_tensor_output: whether to output tensor instead of numpy array. defaults to True. device: device to store the output grid data. See also: - :py:meth:`monai.transforms.utils.create_rotate` - :py:meth:`monai.transforms.utils.create_shear` - :py:meth:`monai.transforms.utils.create_translate` - :py:meth:`monai.transforms.utils.create_scale` """ self.rotate_range = ensure_tuple(rotate_range) self.shear_range = ensure_tuple(shear_range) self.translate_range = ensure_tuple(translate_range) self.scale_range = ensure_tuple(scale_range) self.rotate_params: Optional[List[float]] = None self.shear_params: Optional[List[float]] = None self.translate_params: Optional[List[float]] = None self.scale_params: Optional[List[float]] = None self.as_tensor_output = as_tensor_output self.device = device
def __init__( self, spatial_size: Union[Sequence[int], int], label: Optional[np.ndarray] = None, pos: float = 1.0, neg: float = 1.0, num_samples: int = 1, image: Optional[np.ndarray] = None, image_threshold: float = 0.0, fg_indices: Optional[np.ndarray] = None, bg_indices: Optional[np.ndarray] = None, ) -> None: self.spatial_size = ensure_tuple(spatial_size) self.label = label if pos < 0 or neg < 0: raise ValueError( f"pos and neg must be nonnegative, got pos={pos} neg={neg}.") if pos + neg == 0: raise ValueError("Incompatible values: pos=0 and neg=0.") self.pos_ratio = pos / (pos + neg) self.num_samples = num_samples self.image = image self.image_threshold = image_threshold self.centers: Optional[List[List[np.ndarray]]] = None self.fg_indices = fg_indices self.bg_indices = bg_indices
def generate_spatial_bounding_box( img: np.ndarray, select_fn: Callable = lambda x: x > 0, channel_indices: Optional[IndexSelection] = None, margin: int = 0, ) -> Tuple[List[int], List[int]]: """ generate the spatial bounding box of foreground in the image with start-end positions. Users can define arbitrary function to select expected foreground from the whole image or specified channels. And it can also add margin to every dim of the bounding box. Args: img: source image to generate bounding box from. select_fn: function to select expected foreground, default is to select values > 0. channel_indices: if defined, select foreground only on the specified channels of image. if None, select foreground on the whole image. margin: add margin to all dims of the bounding box. """ assert isinstance(margin, int), "margin must be int type." data = img[[*(ensure_tuple(channel_indices)) ]] if channel_indices is not None else img data = np.any(select_fn(data), axis=0) nonzero_idx = np.nonzero(data) box_start = list() box_end = list() for i in range(data.ndim): assert len(nonzero_idx[i] ) > 0, f"did not find nonzero index at spatial dim {i}" box_start.append(max(0, np.min(nonzero_idx[i]) - margin)) box_end.append(min(data.shape[i], np.max(nonzero_idx[i]) + margin + 1)) return box_start, box_end
def __init__( self, transforms: Optional[Union[Sequence[Callable], Callable]] = None) -> None: if transforms is None: transforms = [] self.transforms = ensure_tuple(transforms) self.set_random_state(seed=get_seed())
def __init__(self, keys: KeysCollection) -> None: self.keys: Tuple[Hashable, ...] = ensure_tuple(keys) if not self.keys: raise ValueError("keys must be non empty.") for key in self.keys: if not isinstance(key, Hashable): raise TypeError( f"keys must be one of (Hashable, Iterable[Hashable]) but is {type(keys).__name__}." )
def create_translate(spatial_dims: int, shift: Union[Sequence[float], float]) -> np.ndarray: """ create a translation matrix Args: spatial_dims: spatial rank shift: translate factors, defaults to 0. """ shift = ensure_tuple(shift) affine = np.eye(spatial_dims + 1) for i, a in enumerate(shift[:spatial_dims]): affine[i, spatial_dims] = a return affine
def __init__(self, select_fn: Callable = lambda x: x > 0, channel_indices: Optional[IndexSelection] = None, margin: int = 0) -> None: """ Args: select_fn: function to select expected foreground, default is to select values > 0. channel_indices: if defined, select foreground only on the specified channels of image. if None, select foreground on the whole image. margin: add margin to all dims of the bounding box. """ self.select_fn = select_fn self.channel_indices = ensure_tuple( channel_indices) if channel_indices is not None else None self.margin = margin
def create_rotate(spatial_dims: int, radians: Union[Sequence[float], float]) -> np.ndarray: """ create a 2D or 3D rotation matrix Args: spatial_dims: {``2``, ``3``} spatial rank radians: rotation radians when spatial_dims == 3, the `radians` sequence corresponds to rotation in the 1st, 2nd, and 3rd dim respectively. Raises: ValueError: When ``radians`` is empty. ValueError: When ``spatial_dims`` is not one of [2, 3]. """ radians = ensure_tuple(radians) if spatial_dims == 2: if len(radians) >= 1: sin_, cos_ = np.sin(radians[0]), np.cos(radians[0]) return np.array([[cos_, -sin_, 0.0], [sin_, cos_, 0.0], [0.0, 0.0, 1.0]]) raise ValueError("radians must be non empty.") if spatial_dims == 3: affine = None if len(radians) >= 1: sin_, cos_ = np.sin(radians[0]), np.cos(radians[0]) affine = np.array([[1.0, 0.0, 0.0, 0.0], [0.0, cos_, -sin_, 0.0], [0.0, sin_, cos_, 0.0], [0.0, 0.0, 0.0, 1.0]]) if len(radians) >= 2: sin_, cos_ = np.sin(radians[1]), np.cos(radians[1]) affine = affine @ np.array( [[cos_, 0.0, sin_, 0.0], [0.0, 1.0, 0.0, 0.0], [-sin_, 0.0, cos_, 0.0], [0.0, 0.0, 0.0, 1.0]]) if len(radians) >= 3: sin_, cos_ = np.sin(radians[2]), np.cos(radians[2]) affine = affine @ np.array( [[cos_, -sin_, 0.0, 0.0], [sin_, cos_, 0.0, 0.0], [0.0, 0.0, 1.0, 0.0], [0.0, 0.0, 0.0, 1.0]]) if affine is None: raise ValueError("radians must be non empty.") return affine raise ValueError( f"Unsupported spatial_dims: {spatial_dims}, available options are [2, 3]." )
def __call__( self, img: np.ndarray, mode: Optional[Union[NumpyPadMode, str]] = None) -> np.ndarray: """ Args: img: data to be transformed, assuming `img` is channel-first and padding doesn't apply to the channel dim. mode: {``"constant"``, ``"edge"``, ``"linear_ramp"``, ``"maximum"``, ``"mean"``, ``"median"``, ``"minimum"``, ``"reflect"``, ``"symmetric"``, ``"wrap"``, ``"empty"``} One of the listed string values or a user supplied function. Defaults to ``self.mode``. See also: https://numpy.org/doc/1.18/reference/generated/numpy.pad.html Raises: ValueError: When ``self.spatial_border`` contains a nonnegative int. ValueError: When ``self.spatial_border`` length is not one of [1, len(spatial_shape), 2*len(spatial_shape)]. """ spatial_shape = img.shape[1:] spatial_border = ensure_tuple(self.spatial_border) for b in spatial_border: if not isinstance(b, int) or b < 0: raise ValueError( f"self.spatial_border must contain only nonnegative ints, got {spatial_border}." ) if len(spatial_border) == 1: data_pad_width = [(spatial_border[0], spatial_border[0]) for _ in range(len(spatial_shape))] elif len(spatial_border) == len(spatial_shape): data_pad_width = [(spatial_border[i], spatial_border[i]) for i in range(len(spatial_shape))] elif len(spatial_border) == len(spatial_shape) * 2: data_pad_width = [(spatial_border[2 * i], spatial_border[2 * i + 1]) for i in range(len(spatial_shape))] else: raise ValueError( f"Unsupported spatial_border length: {len(spatial_border)}, available options are " f"[1, len(spatial_shape)={len(spatial_shape)}, 2*len(spatial_shape)={2*len(spatial_shape)}]." ) return np.pad( img, [(0, 0)] + data_pad_width, mode=self.mode.value if mode is None else NumpyPadMode(mode).value)
def __call__( self, img: Union[np.ndarray, torch.Tensor], spatial_size: Optional[Union[Tuple[int, int], int]] = None, mode: Optional[Union[GridSampleMode, str]] = None, padding_mode: Optional[Union[GridSamplePadMode, str]] = None, ) -> Union[np.ndarray, torch.Tensor]: """ Args: img: shape must be (num_channels, H, W), spatial_size: specifying output image spatial size [h, w]. if `spatial_size` and `self.spatial_size` are not defined, or smaller than 1, the transform will use the spatial size of `img`. mode: {``"bilinear"``, ``"nearest"``} Interpolation mode to calculate output values. Defaults to ``self.mode``. See also: https://pytorch.org/docs/stable/nn.functional.html#grid-sample padding_mode: {``"zeros"``, ``"border"``, ``"reflection"``} Padding mode for outside grid values. Defaults to ``self.padding_mode``. See also: https://pytorch.org/docs/stable/nn.functional.html#grid-sample """ sp_size = fall_back_tuple(spatial_size or self.spatial_size, img.shape[1:]) self.randomize(spatial_size=sp_size) if self.do_transform: grid = self.deform_grid(spatial_size=sp_size) grid = self.rand_affine_grid(grid=grid) grid = _torch_interp( input=grid.unsqueeze(0), scale_factor=list(ensure_tuple(self.deform_grid.spacing)), mode=InterpolateMode.BICUBIC.value, align_corners=False, ) grid = CenterSpatialCrop(roi_size=sp_size)(grid[0]) else: grid = create_grid(spatial_size=sp_size) return self.resampler(img, grid, mode=mode or self.mode, padding_mode=padding_mode or self.padding_mode)