def warp(image, inverse_map=None, map_args={}, output_shape=None, order=1, mode='constant', cval=0., reverse_map=None): """Warp an image according to a given coordinate transformation. Parameters ---------- image : 2-D or 3-D array Input image. inverse_map : transformation object, callable ``xy = f(xy, **kwargs)``, (3, 3) array Inverse coordinate map. A function that transforms a (N, 2) array of ``(x, y)`` coordinates in the *output image* into their corresponding coordinates in the *source image* (e.g. a transformation object or its inverse). See example section for usage. map_args : dict, optional Keyword arguments passed to `inverse_map`. output_shape : tuple (rows, cols), optional Shape of the output image generated. By default the shape of the input image is preserved. order : int, optional The order of interpolation. The order has to be in the range 0-5: * 0: Nearest-neighbor * 1: Bi-linear (default) * 2: Bi-quadratic * 3: Bi-cubic * 4: Bi-quartic * 5: Bi-quintic mode : string, optional Points outside the boundaries of the input are filled according to the given mode ('constant', 'nearest', 'reflect' or 'wrap'). cval : float, optional Used in conjunction with mode 'constant', the value outside the image boundaries. Notes ----- In case of a `SimilarityTransform`, `AffineTransform` and `ProjectiveTransform` and `order` in [0, 3] this function uses the underlying transformation matrix to warp the image with a much faster routine. Examples -------- >>> from skimage.transform import warp >>> from skimage import data >>> image = data.camera() The following image warps are all equal but differ substantially in execution time. Use a geometric transform to warp an image (fast): >>> from skimage.transform import SimilarityTransform >>> tform = SimilarityTransform(translation=(0, -10)) >>> warp(image, tform) Shift an image to the right with a callable (slow): >>> def shift(xy): ... xy[:, 1] -= 10 ... return xy >>> warp(image, shift_right) Use a transformation matrix to warp an image (fast): >>> matrix = np.array([[1, 0, 0], [0, 1, -10], [0, 0, 1]]) >>> warp(image, matrix) >>> from skimage.transform import ProjectiveTransform >>> warp(image, ProjectiveTransform(matrix=matrix)) You can also use the inverse of a geometric transformation (fast): >>> warp(image, tform.inverse) """ # Backward API compatibility if reverse_map is not None: inverse_map = reverse_map if image.ndim < 2: raise ValueError("Input must have more than 1 dimension.") orig_ndim = image.ndim image = np.atleast_3d(img_as_float(image)) ishape = np.array(image.shape) bands = ishape[2] out = None # use fast Cython version for specific interpolation orders and input if order in range(4) and not map_args: matrix = None # inverse_map is a transformation matrix as numpy array if isinstance(inverse_map, np.ndarray) and inverse_map.shape == (3, 3): matrix = inverse_map # inverse_map is a homography elif isinstance(inverse_map, HOMOGRAPHY_TRANSFORMS): matrix = inverse_map._matrix # inverse_map is the inverse of a homography elif (hasattr(inverse_map, '__name__') and inverse_map.__name__ == 'inverse' and isinstance(get_bound_method_class(inverse_map), HOMOGRAPHY_TRANSFORMS)): matrix = np.linalg.inv(six.get_method_self(inverse_map)._matrix) if matrix is not None: matrix = matrix.astype(np.double) # transform all bands dims = [] for dim in range(image.shape[2]): dims.append(_warp_fast(image[..., dim], matrix, output_shape=output_shape, order=order, mode=mode, cval=cval)) out = np.dstack(dims) if orig_ndim == 2: out = out[..., 0] if out is None: # use ndimage.map_coordinates if output_shape is None: output_shape = ishape rows, cols = output_shape[:2] # inverse_map is a transformation matrix as numpy array if isinstance(inverse_map, np.ndarray) and inverse_map.shape == (3, 3): inverse_map = ProjectiveTransform(matrix=inverse_map) def coord_map(*args): return inverse_map(*args, **map_args) coords = warp_coords(coord_map, (rows, cols, bands)) # Pre-filtering not necessary for order 0, 1 interpolation prefilter = order > 1 out = ndimage.map_coordinates(image, coords, prefilter=prefilter, mode=mode, order=order, cval=cval) # The spline filters sometimes return results outside [0, 1], # so clip to ensure valid data clipped = np.clip(out, 0, 1) if mode == 'constant' and not (0 <= cval <= 1): clipped[out == cval] = cval out = clipped if out.ndim == 3 and orig_ndim == 2: # remove singleton dimension introduced by atleast_3d return out[..., 0] else: return out
def warp(image, inverse_map=None, map_args={}, output_shape=None, order=1, mode='constant', cval=0., reverse_map=None): """Warp an image according to a given coordinate transformation. Parameters ---------- image : 2-D or 3-D array Input image. inverse_map : transformation object, callable ``xy = f(xy, **kwargs)`` Inverse coordinate map. A function that transforms a (N, 2) array of ``(x, y)`` coordinates in the *output image* into their corresponding coordinates in the *source image* (e.g. a transformation object or its inverse). map_args : dict, optional Keyword arguments passed to `inverse_map`. output_shape : tuple (rows, cols), optional Shape of the output image generated. By default the shape of the input image is preserved. order : int, optional The order of the spline interpolation, default is 3. The order has to be in the range 0-5. mode : string, optional Points outside the boundaries of the input are filled according to the given mode ('constant', 'nearest', 'reflect' or 'wrap'). cval : float, optional Used in conjunction with mode 'constant', the value outside the image boundaries. Examples -------- Shift an image to the right: >>> from skimage.transform import warp >>> from skimage import data >>> image = data.camera() >>> >>> def shift_right(xy): ... xy[:, 0] -= 10 ... return xy >>> >>> warp(image, shift_right) Use a geometric transform to warp an image: >>> from skimage.transform import SimilarityTransform >>> tform = SimilarityTransform(scale=0.1, rotation=0.1) >>> warp(image, tform) """ # Backward API compatibility if reverse_map is not None: inverse_map = reverse_map if image.ndim < 2: raise ValueError("Input must have more than 1 dimension.") orig_ndim = image.ndim image = np.atleast_3d(img_as_float(image)) ishape = np.array(image.shape) bands = ishape[2] out = None # use fast Cython version for specific interpolation orders if order in range(4) and not map_args: matrix = None if inverse_map in HOMOGRAPHY_TRANSFORMS: matrix = inverse_map._matrix elif hasattr(inverse_map, '__name__') \ and inverse_map.__name__ == 'inverse' \ and get_bound_method_class(inverse_map) in HOMOGRAPHY_TRANSFORMS: matrix = np.linalg.inv(six.get_method_self(inverse_map)._matrix) if matrix is not None: # transform all bands dims = [] for dim in range(image.shape[2]): dims.append(_warp_fast(image[..., dim], matrix, output_shape=output_shape, order=order, mode=mode, cval=cval)) out = np.dstack(dims) if orig_ndim == 2: out = out[..., 0] if out is None: # use ndimage.map_coordinates if output_shape is None: output_shape = ishape rows, cols = output_shape[:2] def coord_map(*args): return inverse_map(*args, **map_args) coords = warp_coords(coord_map, (rows, cols, bands)) # Prefilter not necessary for order 1 interpolation prefilter = order > 1 out = ndimage.map_coordinates(image, coords, prefilter=prefilter, mode=mode, order=order, cval=cval) # The spline filters sometimes return results outside [0, 1], # so clip to ensure valid data clipped = np.clip(out, 0, 1) if mode == 'constant' and not (0 <= cval <= 1): clipped[out == cval] = cval if clipped.ndim == 3 and orig_ndim == 2: # remove singleton dim introduced by atleast_3d return clipped[..., 0] else: return clipped