def dense_patch_slices( image_size: Sequence[int], patch_size: Sequence[int], scan_interval: Sequence[int], ) -> List[Tuple[slice, ...]]: """ Enumerate all slices defining ND patches of size `patch_size` from an `image_size` input image. Args: image_size: dimensions of image to iterate over patch_size: size of patches to generate slices scan_interval: dense patch sampling interval Returns: a list of slice objects defining each patch """ num_spatial_dims = len(image_size) patch_size = get_valid_patch_size(image_size, patch_size) scan_interval = ensure_tuple_size(scan_interval, num_spatial_dims) scan_num = [] for i in range(num_spatial_dims): if scan_interval[i] == 0: scan_num.append(1) else: num = int(math.ceil(float(image_size[i]) / scan_interval[i])) scan_dim = first(d for d in range(num) if d * scan_interval[i] + patch_size[i] >= image_size[i]) scan_num.append(scan_dim + 1 if scan_dim is not None else 1) starts = [] for dim in range(num_spatial_dims): dim_starts = [] for idx in range(scan_num[dim]): start_idx = idx * scan_interval[dim] start_idx -= max(start_idx + patch_size[dim] - image_size[dim], 0) dim_starts.append(start_idx) starts.append(dim_starts) out = np.asarray([x.flatten() for x in np.meshgrid(*starts, indexing="ij")]).T return [tuple(slice(s, s + patch_size[d]) for d, s in enumerate(x)) for x in out]
def iter_patch(arr, patch_size, start_pos=(), copy_back=True, pad_mode="wrap", **pad_opts): """ Yield successive patches from `arr` of size `patch_size`. The iteration can start from position `start_pos` in `arr` but drawing from a padded array extended by the `patch_size` in each dimension (so these coordinates can be negative to start in the padded region). If `copy_back` is True the values from each patch are written back to `arr`. Args: arr (np.ndarray): array to iterate over patch_size (tuple of int or None): size of patches to generate slices for, 0 or None selects whole dimension start_pos (tuple of it, optional): starting position in the array, default is 0 for each dimension copy_back (bool): if True data from the yielded patches is copied back to `arr` once the generator completes pad_mode (str, optional): padding mode, see `numpy.pad` pad_opts (dict, optional): padding options, see `numpy.pad` Yields: Patches of array data from `arr` which are views into a padded array which can be modified, if `copy_back` is True these changes will be reflected in `arr` once the iteration completes. """ # ensure patchSize and startPos are the right length patch_size = get_valid_patch_size(arr.shape, patch_size) start_pos = ensure_tuple_size(start_pos, arr.ndim) # pad image by maximum values needed to ensure patches are taken from inside an image arrpad = np.pad(arr, tuple((p, p) for p in patch_size), pad_mode, **pad_opts) # choose a start position in the padded image start_pos_padded = tuple(s + p for s, p in zip(start_pos, patch_size)) # choose a size to iterate over which is smaller than the actual padded image to prevent producing # patches which are only in the padded regions iter_size = tuple(s + p for s, p in zip(arr.shape, patch_size)) for slices in iter_patch_slices(iter_size, patch_size, start_pos_padded): yield arrpad[slices] # copy back data from the padded image if required if copy_back: slices = tuple(slice(p, p + s) for p, s in zip(patch_size, arr.shape)) arr[...] = arrpad[slices]
def iter_patch( arr: np.ndarray, patch_size: Union[Sequence[int], int] = 0, start_pos: Sequence[int] = (), copy_back: bool = True, mode: Union[NumpyPadMode, str] = NumpyPadMode.WRAP, **pad_opts: Dict, ): """ Yield successive patches from `arr` of size `patch_size`. The iteration can start from position `start_pos` in `arr` but drawing from a padded array extended by the `patch_size` in each dimension (so these coordinates can be negative to start in the padded region). If `copy_back` is True the values from each patch are written back to `arr`. Args: arr: array to iterate over patch_size: size of patches to generate slices for, 0 or None selects whole dimension start_pos: starting position in the array, default is 0 for each dimension copy_back: if True data from the yielded patches is copied back to `arr` once the generator completes 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 ``"wrap"``. See also: https://numpy.org/doc/1.18/reference/generated/numpy.pad.html pad_opts: padding options, see `numpy.pad` Yields: Patches of array data from `arr` which are views into a padded array which can be modified, if `copy_back` is True these changes will be reflected in `arr` once the iteration completes. Note: coordinate format is: [1st_dim_start, 1st_dim_end, 2nd_dim_start, 2nd_dim_end, ..., Nth_dim_start, Nth_dim_end]] """ # ensure patchSize and startPos are the right length patch_size_ = get_valid_patch_size(arr.shape, patch_size) start_pos = ensure_tuple_size(start_pos, arr.ndim) # pad image by maximum values needed to ensure patches are taken from inside an image arrpad = np.pad(arr, tuple((p, p) for p in patch_size_), look_up_option(mode, NumpyPadMode).value, **pad_opts) # choose a start position in the padded image start_pos_padded = tuple(s + p for s, p in zip(start_pos, patch_size_)) # choose a size to iterate over which is smaller than the actual padded image to prevent producing # patches which are only in the padded regions iter_size = tuple(s + p for s, p in zip(arr.shape, patch_size_)) for slices in iter_patch_slices(iter_size, patch_size_, start_pos_padded): # compensate original image padding coords_no_pad = tuple((coord.start - p, coord.stop - p) for coord, p in zip(slices, patch_size_)) yield arrpad[slices], np.asarray( coords_no_pad ) # data and coords (in numpy; works with torch loader) # copy back data from the padded image if required if copy_back: slices = tuple(slice(p, p + s) for p, s in zip(patch_size_, arr.shape)) arr[...] = arrpad[slices]
def __call__(self, img: np.ndarray) -> np.ndarray: self.randomize() if not self._do_transform: return img sigma = ensure_tuple_size(tup=(self.x, self.y, self.z), dim=img.ndim - 1) return GaussianSmooth(sigma=sigma, approx=self.approx)(img)
def dense_patch_slices( image_size: Sequence[int], patch_size: Sequence[int], scan_interval: Sequence[int], ) -> List[Tuple[slice, ...]]: """ Enumerate all slices defining 2D/3D patches of size `patch_size` from an `image_size` input image. Args: image_size: dimensions of image to iterate over patch_size: size of patches to generate slices scan_interval: dense patch sampling interval Raises: ValueError: When ``image_size`` length is not one of [2, 3]. Returns: a list of slice objects defining each patch """ num_spatial_dims = len(image_size) if num_spatial_dims not in (2, 3): raise ValueError( f"Unsupported image_size length: {len(image_size)}, available options are [2, 3]" ) patch_size = get_valid_patch_size(image_size, patch_size) scan_interval = ensure_tuple_size(scan_interval, num_spatial_dims) scan_num = list() for i in range(num_spatial_dims): if scan_interval[i] == 0: scan_num.append(1) else: num = int(math.ceil(float(image_size[i]) / scan_interval[i])) scan_dim = first( d for d in range(num) if d * scan_interval[i] + patch_size[i] >= image_size[i]) scan_num.append(scan_dim + 1) slices: List[Tuple[slice, ...]] = [] if num_spatial_dims == 3: for i in range(scan_num[0]): start_i = i * scan_interval[0] start_i -= max(start_i + patch_size[0] - image_size[0], 0) slice_i = slice(start_i, start_i + patch_size[0]) for j in range(scan_num[1]): start_j = j * scan_interval[1] start_j -= max(start_j + patch_size[1] - image_size[1], 0) slice_j = slice(start_j, start_j + patch_size[1]) for k in range(0, scan_num[2]): start_k = k * scan_interval[2] start_k -= max(start_k + patch_size[2] - image_size[2], 0) slice_k = slice(start_k, start_k + patch_size[2]) slices.append((slice_i, slice_j, slice_k)) else: for i in range(scan_num[0]): start_i = i * scan_interval[0] start_i -= max(start_i + patch_size[0] - image_size[0], 0) slice_i = slice(start_i, start_i + patch_size[0]) for j in range(scan_num[1]): start_j = j * scan_interval[1] start_j -= max(start_j + patch_size[1] - image_size[1], 0) slice_j = slice(start_j, start_j + patch_size[1]) slices.append((slice_i, slice_j)) return slices