def _run_1d_filters(filters, input, args, output, mode, cval, origin=0): """ Runs a series of 1D filters forming an nd filter. The filters must be a list of callables that take input, arg, axis, output, mode, cval, origin. The args is a list of values that are passed for the arg value to the filter. Individual filters can be None causing that axis to be skipped. """ output_orig = output output = _util._get_output(output, input) modes = _util._fix_sequence_arg(mode, input.ndim, 'mode', _util._check_mode) origins = _util._fix_sequence_arg(origin, input.ndim, 'origin', int) n_filters = sum(filter is not None for filter in filters) if n_filters == 0: output[...] = input return output # We can't operate in-place efficiently, so use a 2-buffer system temp = _util._get_output(output.dtype, input) if n_filters > 1 else None first = True iterator = zip(filters, args, modes, origins) for axis, (fltr, arg, mode, origin) in enumerate(iterator): if fltr is None: continue fltr(input, arg, axis, output, mode, cval, origin) input, output = output, temp if first else input first = False if isinstance(output_orig, cupy.ndarray) and input is not output_orig: output_orig[...] = input input = output_orig return input
def map_coordinates(input, coordinates, output=None, order=3, mode='constant', cval=0.0, prefilter=True): """Map the input array to new coordinates by interpolation. The array of coordinates is used to find, for each point in the output, the corresponding coordinates in the input. The value of the input at those coordinates is determined by spline interpolation of the requested order. The shape of the output is derived from that of the coordinate array by dropping the first axis. The values of the array along the first axis are the coordinates in the input array at which the output value is found. Args: input (cupy.ndarray): The input array. coordinates (array_like): The coordinates at which ``input`` is evaluated. output (cupy.ndarray or ~cupy.dtype): The array in which to place the output, or the dtype of the returned array. order (int): The order of the spline interpolation, default is 3. Must be in the range 0-5. mode (str): Points outside the boundaries of the input are filled according to the given mode (``'constant'``, ``'nearest'``, ``'mirror'``, ``'reflect'``, ``'wrap'``, ``'grid-mirror'``, ``'grid-wrap'``, ``'grid-constant'`` or ``'opencv'``). cval (scalar): Value used for points outside the boundaries of the input if ``mode='constant'`` or ``mode='opencv'``. Default is 0.0 prefilter (bool): It is not used yet. It just exists for compatibility with :mod:`scipy.ndimage`. Returns: cupy.ndarray: The result of transforming the input. The shape of the output is derived from that of ``coordinates`` by dropping the first axis. .. seealso:: :func:`scipy.ndimage.map_coordinates` """ _check_parameter('map_coordinates', order, mode) if mode == 'opencv' or mode == '_opencv_edge': input = cupy.pad(input, [(1, 1)] * input.ndim, 'constant', constant_values=cval) coordinates = cupy.add(coordinates, 1) mode = 'constant' ret = _util._get_output(output, input, coordinates.shape[1:]) integer_output = ret.dtype.kind in 'iu' _util._check_cval(mode, cval, integer_output) if input.dtype.kind in 'iu': input = input.astype(cupy.float32) coordinates = _check_coordinates(coordinates, order) filtered, nprepad = _filter_input(input, prefilter, mode, cval, order) large_int = max(_prod(input.shape), coordinates.shape[0]) > 1 << 31 kern = _interp_kernels._get_map_kernel( input.ndim, large_int, yshape=coordinates.shape, mode=mode, cval=cval, order=order, integer_output=integer_output, nprepad=nprepad) kern(filtered, coordinates, ret) return ret
def _call_kernel(kernel, input, weights, output, structure=None, weights_dtype=numpy.float64, structure_dtype=numpy.float64): """ Calls a constructed ElementwiseKernel. The kernel must take an input image, an optional array of weights, an optional array for the structure, and an output array. weights and structure can be given as None (structure defaults to None) in which case they are not passed to the kernel at all. If the output is given as None then it will be allocated in this function. This function deals with making sure that the weights and structure are contiguous and float64 (or bool for weights that are footprints)*, that the output is allocated and appriopately shaped. This also deals with the situation that the input and output arrays overlap in memory. * weights is always cast to float64 or bool in order to get an output compatible with SciPy, though float32 might be sufficient when input dtype is low precision. If weights_dtype is passed as weights.dtype then no dtype conversion will occur. The input and output are never converted. """ args = [input] complex_output = input.dtype.kind == 'c' if weights is not None: weights = cupy.ascontiguousarray(weights, weights_dtype) complex_output = complex_output or weights.dtype.kind == 'c' args.append(weights) if structure is not None: structure = cupy.ascontiguousarray(structure, structure_dtype) args.append(structure) output = _util._get_output(output, input, None, complex_output) needs_temp = cupy.shares_memory(output, input, 'MAY_SHARE_BOUNDS') if needs_temp: output, temp = _util._get_output(output.dtype, input), output args.append(output) kernel(*args) if needs_temp: temp[...] = output[...] output = temp return output
def _binary_erosion(input, structure, iterations, mask, output, border_value, origin, invert, brute_force=True): try: iterations = operator.index(iterations) except TypeError: raise TypeError('iterations parameter should be an integer') if input.dtype.kind == 'c': raise TypeError('Complex type not supported') if structure is None: structure = generate_binary_structure(input.ndim, 1) all_weights_nonzero = input.ndim == 1 center_is_true = True default_structure = True else: structure = structure.astype(dtype=bool, copy=False) # transfer to CPU for use in determining if it is fully dense # structure_cpu = cupy.asnumpy(structure) default_structure = False if structure.ndim != input.ndim: raise RuntimeError('structure and input must have same dimensionality') if not structure.flags.c_contiguous: structure = cupy.ascontiguousarray(structure) if structure.size < 1: raise RuntimeError('structure must not be empty') if mask is not None: if mask.shape != input.shape: raise RuntimeError('mask and input must have equal sizes') if not mask.flags.c_contiguous: mask = cupy.ascontiguousarray(mask) masked = True else: masked = False origin = _util._fix_sequence_arg(origin, input.ndim, 'origin', int) if isinstance(output, cupy.ndarray): if output.dtype.kind == 'c': raise TypeError('Complex output type not supported') else: output = bool output = _util._get_output(output, input) temp_needed = cupy.shares_memory(output, input, 'MAY_SHARE_BOUNDS') if temp_needed: # input and output arrays cannot share memory temp = output output = _util._get_output(output.dtype, input) if structure.ndim == 0: # kernel doesn't handle ndim=0, so special case it here if float(structure): output[...] = cupy.asarray(input, dtype=bool) else: output[...] = ~cupy.asarray(input, dtype=bool) return output origin = tuple(origin) int_type = _util._get_inttype(input) offsets = _filters_core._origins_to_offsets(origin, structure.shape) if not default_structure: # synchronize required to determine if all weights are non-zero nnz = int(cupy.count_nonzero(structure)) all_weights_nonzero = nnz == structure.size if all_weights_nonzero: center_is_true = True else: center_is_true = _center_is_true(structure, origin) erode_kernel = _get_binary_erosion_kernel( structure.shape, int_type, offsets, center_is_true, border_value, invert, masked, all_weights_nonzero, ) if iterations == 1: if masked: output = erode_kernel(input, structure, mask, output) else: output = erode_kernel(input, structure, output) elif center_is_true and not brute_force: raise NotImplementedError( 'only brute_force iteration has been implemented') else: if cupy.shares_memory(output, input, 'MAY_SHARE_BOUNDS'): raise ValueError('output and input may not overlap in memory') tmp_in = cupy.empty_like(input, dtype=output.dtype) tmp_out = output if iterations >= 1 and not iterations & 1: tmp_in, tmp_out = tmp_out, tmp_in if masked: tmp_out = erode_kernel(input, structure, mask, tmp_out) else: tmp_out = erode_kernel(input, structure, tmp_out) # TODO: kernel doesn't return the changed status, so determine it here changed = not (input == tmp_out).all() # synchronize! ii = 1 while ii < iterations or ((iterations < 1) and changed): tmp_in, tmp_out = tmp_out, tmp_in if masked: tmp_out = erode_kernel(tmp_in, structure, mask, tmp_out) else: tmp_out = erode_kernel(tmp_in, structure, tmp_out) changed = not (tmp_in == tmp_out).all() ii += 1 if not changed and (not ii & 1): # synchronize! # can exit early if nothing changed # (only do this after even number of tmp_in/out swaps) break output = tmp_out if temp_needed: temp[...] = output output = temp return output
def zoom(input, zoom, output=None, order=3, mode='constant', cval=0.0, prefilter=True, *, grid_mode=False): """Zoom an array. The array is zoomed using spline interpolation of the requested order. Args: input (cupy.ndarray): The input array. zoom (float or sequence): The zoom factor along the axes. If a float, ``zoom`` is the same for each axis. If a sequence, ``zoom`` should contain one value for each axis. output (cupy.ndarray or ~cupy.dtype): The array in which to place the output, or the dtype of the returned array. order (int): The order of the spline interpolation, default is 3. Must be in the range 0-5. mode (str): Points outside the boundaries of the input are filled according to the given mode (``'constant'``, ``'nearest'``, ``'mirror'``, ``'reflect'``, ``'wrap'``, ``'grid-mirror'``, ``'grid-wrap'``, ``'grid-constant'`` or ``'opencv'``). cval (scalar): Value used for points outside the boundaries of the input if ``mode='constant'`` or ``mode='opencv'``. Default is 0.0 prefilter (bool): It is not used yet. It just exists for compatibility with :mod:`scipy.ndimage`. grid_mode (bool, optional): If False, the distance from the pixel centers is zoomed. Otherwise, the distance including the full pixel extent is used. For example, a 1d signal of length 5 is considered to have length 4 when ``grid_mode`` is False, but length 5 when ``grid_mode`` is True. See the following visual illustration: .. code-block:: text | pixel 1 | pixel 2 | pixel 3 | pixel 4 | pixel 5 | |<-------------------------------------->| vs. |<----------------------------------------------->| The starting point of the arrow in the diagram above corresponds to coordinate location 0 in each mode. Returns: cupy.ndarray or None: The zoomed input. .. seealso:: :func:`scipy.ndimage.zoom` """ _check_parameter('zoom', order, mode) zoom = _util._fix_sequence_arg(zoom, input.ndim, 'zoom', float) output_shape = [] for s, z in zip(input.shape, zoom): output_shape.append(int(round(s * z))) output_shape = tuple(output_shape) if mode == 'opencv': zoom = [] offset = [] for in_size, out_size in zip(input.shape, output_shape): if out_size > 1: zoom.append(float(in_size) / out_size) offset.append((zoom[-1] - 1) / 2.0) else: zoom.append(0) offset.append(0) mode = 'nearest' output = affine_transform( input, cupy.asarray(zoom), offset, output_shape, output, order, mode, cval, prefilter, ) else: if grid_mode: # warn about modes that may have surprising behavior suggest_mode = None if mode == 'constant': suggest_mode = 'grid-constant' elif mode == 'wrap': suggest_mode = 'grid-wrap' if suggest_mode is not None: warnings.warn( f'It is recommended to use mode = {suggest_mode} instead ' f'of {mode} when grid_mode is True.') zoom = [] for in_size, out_size in zip(input.shape, output_shape): if grid_mode and out_size > 0: zoom.append(in_size / out_size) elif out_size > 1: zoom.append((in_size - 1) / (out_size - 1)) else: zoom.append(0) output = _util._get_output(output, input, shape=output_shape) if input.dtype.kind in 'iu': input = input.astype(cupy.float32) filtered, nprepad = _filter_input(input, prefilter, mode, cval, order) integer_output = output.dtype.kind in 'iu' _util._check_cval(mode, cval, integer_output) large_int = max(_prod(input.shape), _prod(output_shape)) > 1 << 31 kern = _interp_kernels._get_zoom_kernel(input.ndim, large_int, output_shape, mode, order=order, integer_output=integer_output, grid_mode=grid_mode, nprepad=nprepad) zoom = cupy.asarray(zoom, dtype=cupy.float64) kern(filtered, zoom, output) return output
def spline_filter1d(input, order=3, axis=-1, output=cupy.float64, mode='mirror'): """ Calculate a 1-D spline filter along the given axis. The lines of the array along the given axis are filtered by a spline filter. The order of the spline must be >= 2 and <= 5. Args: input (cupy.ndarray): The input array. order (int): The order of the spline interpolation, default is 3. Must be in the range 0-5. axis (int): The axis along which the spline filter is applied. Default is the last axis. output (cupy.ndarray or dtype, optional): The array in which to place the output, or the dtype of the returned array. Default is ``numpy.float64``. mode (str): Points outside the boundaries of the input are filled according to the given mode (``'constant'``, ``'nearest'``, ``'mirror'``, ``'reflect'``, ``'wrap'``, ``'grid-mirror'``, ``'grid-wrap'``, ``'grid-constant'`` or ``'opencv'``). Returns: cupy.ndarray: The result of prefiltering the input. .. seealso:: :func:`scipy.spline_filter1d` """ if order < 0 or order > 5: raise RuntimeError('spline order not supported') x = input ndim = x.ndim axis = internal._normalize_axis_index(axis, ndim) # order 0, 1 don't require reshaping as no CUDA kernel will be called # scalar or size 1 arrays also don't need to be filtered run_kernel = not (order < 2 or x.ndim == 0 or x.shape[axis] == 1) if not run_kernel: output = _util._get_output(output, input) output[...] = x[...] return output temp, data_dtype, output_dtype = _get_spline_output(x, output) data_type = cupy._core._scalar.get_typename(temp.dtype) pole_type = cupy._core._scalar.get_typename(temp.real.dtype) index_type = _util._get_inttype(input) index_dtype = cupy.int32 if index_type == 'int' else cupy.int64 n_samples = x.shape[axis] n_signals = x.size // n_samples info = cupy.array((n_signals, n_samples) + x.shape, dtype=index_dtype) # empirical choice of block size that seemed to work well block_size = max(2**math.ceil(numpy.log2(n_samples / 32)), 8) kern = _spline_prefilter_core.get_raw_spline1d_kernel( axis, ndim, mode, order=order, index_type=index_type, data_type=data_type, pole_type=pole_type, block_size=block_size, ) # Due to recursive nature, a given line of data must be processed by a # single thread. n_signals lines will be processed in total. block = (block_size, ) grid = ((n_signals + block[0] - 1) // block[0], ) # apply prefilter gain poles = _spline_prefilter_core.get_poles(order=order) temp *= _spline_prefilter_core.get_gain(poles) # apply caual + anti-causal IIR spline filters kern(grid, block, (temp, info)) if isinstance(output, cupy.ndarray) and temp is not output: # copy kernel output into the user-provided output array output[...] = temp[...] return output return temp.astype(output_dtype, copy=False)
def shift(input, shift, output=None, order=3, mode='constant', cval=0.0, prefilter=True): """Shift an array. The array is shifted using spline interpolation of the requested order. Points outside the boundaries of the input are filled according to the given mode. Args: input (cupy.ndarray): The input array. shift (float or sequence): The shift along the axes. If a float, ``shift`` is the same for each axis. If a sequence, ``shift`` should contain one value for each axis. output (cupy.ndarray or ~cupy.dtype): The array in which to place the output, or the dtype of the returned array. order (int): The order of the spline interpolation, default is 3. Must be in the range 0-5. mode (str): Points outside the boundaries of the input are filled according to the given mode (``'constant'``, ``'nearest'``, ``'mirror'``, ``'reflect'``, ``'wrap'``, ``'grid-mirror'``, ``'grid-wrap'``, ``'grid-constant'`` or ``'opencv'``). cval (scalar): Value used for points outside the boundaries of the input if ``mode='constant'`` or ``mode='opencv'``. Default is 0.0 prefilter (bool): It is not used yet. It just exists for compatibility with :mod:`scipy.ndimage`. Returns: cupy.ndarray or None: The shifted input. .. seealso:: :func:`scipy.ndimage.shift` """ _check_parameter('shift', order, mode) shift = _util._fix_sequence_arg(shift, input.ndim, 'shift', float) if mode == 'opencv': mode = '_opencv_edge' output = affine_transform( input, cupy.ones(input.ndim, input.dtype), cupy.negative(cupy.asarray(shift)), None, output, order, mode, cval, prefilter, ) else: output = _util._get_output(output, input) if input.dtype.kind in 'iu': input = input.astype(cupy.float32) filtered, nprepad = _filter_input(input, prefilter, mode, cval, order) integer_output = output.dtype.kind in 'iu' _util._check_cval(mode, cval, integer_output) large_int = _prod(input.shape) > 1 << 31 kern = _interp_kernels._get_shift_kernel(input.ndim, large_int, input.shape, mode, cval=cval, order=order, integer_output=integer_output, nprepad=nprepad) shift = cupy.asarray(shift, dtype=cupy.float64, order='C') if shift.ndim != 1: raise ValueError('shift must be 1d') if shift.size != filtered.ndim: raise ValueError('len(shift) must equal input.ndim') kern(filtered, shift, output) return output
def affine_transform(input, matrix, offset=0.0, output_shape=None, output=None, order=3, mode='constant', cval=0.0, prefilter=True, *, texture_memory=False): """Apply an affine transformation. Given an output image pixel index vector ``o``, the pixel value is determined from the input image at position ``cupy.dot(matrix, o) + offset``. Args: input (cupy.ndarray): The input array. matrix (cupy.ndarray): The inverse coordinate transformation matrix, mapping output coordinates to input coordinates. If ``ndim`` is the number of dimensions of ``input``, the given matrix must have one of the following shapes: - ``(ndim, ndim)``: the linear transformation matrix for each output coordinate. - ``(ndim,)``: assume that the 2D transformation matrix is diagonal, with the diagonal specified by the given value. - ``(ndim + 1, ndim + 1)``: assume that the transformation is specified using homogeneous coordinates. In this case, any value passed to ``offset`` is ignored. - ``(ndim, ndim + 1)``: as above, but the bottom row of a homogeneous transformation matrix is always ``[0, 0, ..., 1]``, and may be omitted. offset (float or sequence): The offset into the array where the transform is applied. If a float, ``offset`` is the same for each axis. If a sequence, ``offset`` should contain one value for each axis. output_shape (tuple of ints): Shape tuple. output (cupy.ndarray or ~cupy.dtype): The array in which to place the output, or the dtype of the returned array. order (int): The order of the spline interpolation, default is 3. Must be in the range 0-5. mode (str): Points outside the boundaries of the input are filled according to the given mode (``'constant'``, ``'nearest'``, ``'mirror'``, ``'reflect'``, ``'wrap'``, ``'grid-mirror'``, ``'grid-wrap'``, ``'grid-constant'`` or ``'opencv'``). cval (scalar): Value used for points outside the boundaries of the input if ``mode='constant'`` or ``mode='opencv'``. Default is 0.0 prefilter (bool): It is not used yet. It just exists for compatibility with :mod:`scipy.ndimage`. texture_memory (bool): If True, uses GPU texture memory. Supports only: - 2D and 3D float32 arrays as input - ``(ndim + 1, ndim + 1)`` homogeneous float32 transformation matrix - ``mode='constant'`` and ``mode='nearest'`` - ``order=0`` (nearest neighbor) and ``order=1`` (linear interpolation) Returns: cupy.ndarray or None: The transformed input. If ``output`` is given as a parameter, ``None`` is returned. .. seealso:: :func:`scipy.ndimage.affine_transform` """ if texture_memory: tm_interp = 'linear' if order > 0 else 'nearest' return _texture.affine_transformation(data=input, transformation_matrix=matrix, output_shape=output_shape, output=output, interpolation=tm_interp, mode=mode, border_value=cval) _check_parameter('affine_transform', order, mode) offset = _util._fix_sequence_arg(offset, input.ndim, 'offset', float) if matrix.ndim not in [1, 2] or matrix.shape[0] < 1: raise RuntimeError('no proper affine matrix provided') if matrix.ndim == 2: if matrix.shape[0] == matrix.shape[1] - 1: offset = matrix[:, -1] matrix = matrix[:, :-1] elif matrix.shape[0] == input.ndim + 1: offset = matrix[:-1, -1] matrix = matrix[:-1, :-1] if matrix.shape != (input.ndim, input.ndim): raise RuntimeError('improper affine shape') if mode == 'opencv': m = cupy.zeros((input.ndim + 1, input.ndim + 1)) m[:-1, :-1] = matrix m[:-1, -1] = offset m[-1, -1] = 1 m = cupy.linalg.inv(m) m[:2] = cupy.roll(m[:2], 1, axis=0) m[:2, :2] = cupy.roll(m[:2, :2], 1, axis=1) matrix = m[:-1, :-1] offset = m[:-1, -1] if output_shape is None: output_shape = input.shape if mode == 'opencv' or mode == '_opencv_edge': if matrix.ndim == 1: matrix = cupy.diag(matrix) coordinates = cupy.indices(output_shape, dtype=cupy.float64) coordinates = cupy.dot(matrix, coordinates.reshape((input.ndim, -1))) coordinates += cupy.expand_dims(cupy.asarray(offset), -1) ret = _util._get_output(output, input, shape=output_shape) ret[:] = map_coordinates(input, coordinates, ret.dtype, order, mode, cval, prefilter).reshape(output_shape) return ret matrix = matrix.astype(cupy.float64, copy=False) ndim = input.ndim output = _util._get_output(output, input, shape=output_shape) if input.dtype.kind in 'iu': input = input.astype(cupy.float32) filtered, nprepad = _filter_input(input, prefilter, mode, cval, order) integer_output = output.dtype.kind in 'iu' _util._check_cval(mode, cval, integer_output) large_int = max(_prod(input.shape), _prod(output_shape)) > 1 << 31 if matrix.ndim == 1: offset = cupy.asarray(offset, dtype=cupy.float64) offset = -offset / matrix kern = _interp_kernels._get_zoom_shift_kernel( ndim, large_int, output_shape, mode, cval=cval, order=order, integer_output=integer_output, nprepad=nprepad) kern(filtered, offset, matrix, output) else: kern = _interp_kernels._get_affine_kernel( ndim, large_int, output_shape, mode, cval=cval, order=order, integer_output=integer_output, nprepad=nprepad) m = cupy.zeros((ndim, ndim + 1), dtype=cupy.float64) m[:, :-1] = matrix m[:, -1] = cupy.asarray(offset, dtype=cupy.float64) kern(filtered, m, output) return output
def zoom(input, zoom, output=None, order=None, mode='constant', cval=0.0, prefilter=True): """Zoom an array. The array is zoomed using spline interpolation of the requested order. Args: input (cupy.ndarray): The input array. zoom (float or sequence): The zoom factor along the axes. If a float, ``zoom`` is the same for each axis. If a sequence, ``zoom`` should contain one value for each axis. output (cupy.ndarray or ~cupy.dtype): The array in which to place the output, or the dtype of the returned array. order (int): The order of the spline interpolation. If it is not given, order 1 is used. It is different from :mod:`scipy.ndimage` and can change in the future. Currently it supports only order 0 and 1. mode (str): Points outside the boundaries of the input are filled according to the given mode (``'constant'``, ``'nearest'``, ``'mirror'`` or ``'opencv'``). Default is ``'constant'``. cval (scalar): Value used for points outside the boundaries of the input if ``mode='constant'`` or ``mode='opencv'``. Default is 0.0 prefilter (bool): It is not used yet. It just exists for compatibility with :mod:`scipy.ndimage`. Returns: cupy.ndarray or None: The zoomed input. .. seealso:: :func:`scipy.ndimage.zoom` """ _check_parameter('zoom', order, mode) zoom = _util._fix_sequence_arg(zoom, input.ndim, 'zoom', float) output_shape = [] for s, z in zip(input.shape, zoom): output_shape.append(int(round(s * z))) output_shape = tuple(output_shape) if mode == 'opencv': zoom = [] offset = [] for in_size, out_size in zip(input.shape, output_shape): if out_size > 1: zoom.append(float(in_size) / out_size) offset.append((zoom[-1] - 1) / 2.0) else: zoom.append(0) offset.append(0) mode = 'nearest' output = affine_transform( input, cupy.asarray(zoom), offset, output_shape, output, order, mode, cval, prefilter, ) else: if order is None: order = 1 zoom = [] for in_size, out_size in zip(input.shape, output_shape): if out_size > 1: zoom.append(float(in_size - 1) / (out_size - 1)) else: zoom.append(0) output = _util._get_output(output, input, shape=output_shape) if input.dtype.kind in 'iu': input = input.astype(cupy.float32) integer_output = output.dtype.kind in 'iu' large_int = max(_prod(input.shape), _prod(output_shape)) > 1 << 31 kern = _interp_kernels._get_zoom_kernel(input.ndim, large_int, output_shape, mode, order=order, integer_output=integer_output) zoom = cupy.asarray(zoom, dtype=cupy.float64) kern(input, zoom, output) return output
def shift(input, shift, output=None, order=None, mode='constant', cval=0.0, prefilter=True): """Shift an array. The array is shifted using spline interpolation of the requested order. Points outside the boundaries of the input are filled according to the given mode. Args: input (cupy.ndarray): The input array. shift (float or sequence): The shift along the axes. If a float, ``shift`` is the same for each axis. If a sequence, ``shift`` should contain one value for each axis. output (cupy.ndarray or ~cupy.dtype): The array in which to place the output, or the dtype of the returned array. order (int): The order of the spline interpolation. If it is not given, order 1 is used. It is different from :mod:`scipy.ndimage` and can change in the future. Currently it supports only order 0 and 1. mode (str): Points outside the boundaries of the input are filled according to the given mode (``'constant'``, ``'nearest'``, ``'mirror'`` or ``'opencv'``). Default is ``'constant'``. cval (scalar): Value used for points outside the boundaries of the input if ``mode='constant'`` or ``mode='opencv'``. Default is 0.0 prefilter (bool): It is not used yet. It just exists for compatibility with :mod:`scipy.ndimage`. Returns: cupy.ndarray or None: The shifted input. .. seealso:: :func:`scipy.ndimage.shift` """ _check_parameter('shift', order, mode) shift = _util._fix_sequence_arg(shift, input.ndim, 'shift', float) if mode == 'opencv': mode = '_opencv_edge' output = affine_transform( input, cupy.ones(input.ndim, input.dtype), cupy.negative(cupy.asarray(shift)), None, output, order, mode, cval, prefilter, ) else: if order is None: order = 1 output = _util._get_output(output, input) if input.dtype.kind in 'iu': input = input.astype(cupy.float32) integer_output = output.dtype.kind in 'iu' large_int = _prod(input.shape) > 1 << 31 kern = _interp_kernels._get_shift_kernel(input.ndim, large_int, input.shape, mode, cval=cval, order=order, integer_output=integer_output) shift = cupy.asarray(shift, dtype=cupy.float64) kern(input, shift, output) return output
def affine_transform(input, matrix, offset=0.0, output_shape=None, output=None, order=None, mode='constant', cval=0.0, prefilter=True): """Apply an affine transformation. Given an output image pixel index vector ``o``, the pixel value is determined from the input image at position ``cupy.dot(matrix, o) + offset``. Args: input (cupy.ndarray): The input array. matrix (cupy.ndarray): The inverse coordinate transformation matrix, mapping output coordinates to input coordinates. If ``ndim`` is the number of dimensions of ``input``, the given matrix must have one of the following shapes: - ``(ndim, ndim)``: the linear transformation matrix for each output coordinate. - ``(ndim,)``: assume that the 2D transformation matrix is diagonal, with the diagonal specified by the given value. - ``(ndim + 1, ndim + 1)``: assume that the transformation is specified using homogeneous coordinates. In this case, any value passed to ``offset`` is ignored. - ``(ndim, ndim + 1)``: as above, but the bottom row of a homogeneous transformation matrix is always ``[0, 0, ..., 1]``, and may be omitted. offset (float or sequence): The offset into the array where the transform is applied. If a float, ``offset`` is the same for each axis. If a sequence, ``offset`` should contain one value for each axis. output_shape (tuple of ints): Shape tuple. output (cupy.ndarray or ~cupy.dtype): The array in which to place the output, or the dtype of the returned array. order (int): The order of the spline interpolation. If it is not given, order 1 is used. It is different from :mod:`scipy.ndimage` and can change in the future. Currently it supports only order 0 and 1. mode (str): Points outside the boundaries of the input are filled according to the given mode (``'constant'``, ``'nearest'``, ``'mirror'`` or ``'opencv'``). Default is ``'constant'``. cval (scalar): Value used for points outside the boundaries of the input if ``mode='constant'`` or ``mode='opencv'``. Default is 0.0 prefilter (bool): It is not used yet. It just exists for compatibility with :mod:`scipy.ndimage`. Returns: cupy.ndarray or None: The transformed input. If ``output`` is given as a parameter, ``None`` is returned. .. seealso:: :func:`scipy.ndimage.affine_transform` """ _check_parameter('affine_transform', order, mode) offset = _util._fix_sequence_arg(offset, input.ndim, 'offset', float) if matrix.ndim not in [1, 2] or matrix.shape[0] < 1: raise RuntimeError('no proper affine matrix provided') if matrix.ndim == 2: if matrix.shape[0] == matrix.shape[1] - 1: offset = matrix[:, -1] matrix = matrix[:, :-1] elif matrix.shape[0] == input.ndim + 1: offset = matrix[:-1, -1] matrix = matrix[:-1, :-1] if matrix.shape != (input.ndim, input.ndim): raise RuntimeError("improper affine shape") if mode == 'opencv': m = cupy.zeros((input.ndim + 1, input.ndim + 1)) m[:-1, :-1] = matrix m[:-1, -1] = offset m[-1, -1] = 1 m = cupy.linalg.inv(m) m[:2] = cupy.roll(m[:2], 1, axis=0) m[:2, :2] = cupy.roll(m[:2, :2], 1, axis=1) matrix = m[:-1, :-1] offset = m[:-1, -1] if output_shape is None: output_shape = input.shape matrix = matrix.astype(cupy.float64, copy=False) if order is None: order = 1 ndim = input.ndim output = _util._get_output(output, input, shape=output_shape) if input.dtype.kind in 'iu': input = input.astype(cupy.float32) integer_output = output.dtype.kind in 'iu' large_int = max(_prod(input.shape), _prod(output_shape)) > 1 << 31 if matrix.ndim == 1: offset = cupy.asarray(offset, dtype=cupy.float64) offset = -offset / matrix kern = _interp_kernels._get_zoom_shift_kernel( ndim, large_int, output_shape, mode, cval=cval, order=order, integer_output=integer_output) kern(input, offset, matrix, output) else: kern = _interp_kernels._get_affine_kernel( ndim, large_int, output_shape, mode, cval=cval, order=order, integer_output=integer_output) m = cupy.zeros((ndim, ndim + 1), dtype=cupy.float64) m[:, :-1] = matrix m[:, -1] = cupy.asarray(offset, dtype=cupy.float64) kern(input, m, output) return output