Esempio n. 1
0
    def __add__(self, other):
        """Combine this transformation with another.

        """
        if isinstance(other, ProjectiveTransform):
            # combination of the same types result in a transformation of this
            # type again, otherwise use general projective transformation
            if type(self) == type(other):
                tform = self.__class__
            else:
                tform = ProjectiveTransform
            return tform(other.params.dot(self.params))
        elif (hasattr(other, '__name__') and other.__name__ == 'inverse'
              and hasattr(get_bound_method_class(other), '_inv_matrix')):
            return ProjectiveTransform(self._inv_matrix.dot(self.params))
        else:
            raise TypeError("Cannot combine transformations of differing "
                            "types.")
Esempio n. 2
0
    def __add__(self, other):
        """Combine this transformation with another.

        """
        if isinstance(other, ProjectiveTransform):
            # combination of the same types result in a transformation of this
            # type again, otherwise use general projective transformation
            if type(self) == type(other):
                tform = self.__class__
            else:
                tform = ProjectiveTransform
            return tform(other.params.dot(self.params))
        elif (hasattr(other, '__name__')
                and other.__name__ == 'inverse'
                and hasattr(get_bound_method_class(other), '_inv_matrix')):
            return ProjectiveTransform(self._inv_matrix.dot(self.params))
        else:
            raise TypeError("Cannot combine transformations of differing "
                            "types.")
Esempio n. 3
0
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) # doctest: +SKIP

    Shift an image to the right with a callable (slow):

    >>> def shift(xy):
    ...     xy[:, 1] -= 10
    ...     return xy
    >>> warp(image, shift_right) # doctest: +SKIP

    Use a transformation matrix to warp an image (fast):

    >>> matrix = np.array([[1, 0, 0], [0, 1, -10], [0, 0, 1]])
    >>> warp(image, matrix) # doctest: +SKIP
    >>> from skimage.transform import ProjectiveTransform
    >>> warp(image, ProjectiveTransform(matrix=matrix)) # doctest: +SKIP

    You can also use the inverse of a geometric transformation (fast):

    >>> warp(image, tform.inverse) # doctest: +SKIP

    """
    # Backward API compatibility
    if reverse_map is not None:
        warnings.warn('`reverse_map` parameter is deprecated and replaced by '
                      'the `inverse_map` parameter.')
        inverse_map = reverse_map

    if image.ndim < 2 or image.ndim > 3:
        raise ValueError("Input must have 2 or 3 dimensions.")

    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 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:
            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
Esempio n. 4
0
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
Esempio n. 5
0
def warp(image, inverse_map=None, map_args={}, output_shape=None, order=1,
         mode='constant', cval=0., clip=True):
    """Warp an image according to a given coordinate transformation.

    Parameters
    ----------
    image : ndarray
        Input image.
    inverse_map : transformation object, callable ``cr = f(cr, **kwargs)``, or ndarray
        Inverse coordinate map, which transforms coordinates in the output
        images into their corresponding coordinates in the input image.

        There are a number of different options to define this map, depending
        on the dimensionality of the input image. A 2-D image can have 2
        dimensions for gray-scale images, or 3 dimensions with color
        information.

         - For 2-D images, you can directly pass a transformation object,
           e.g. `skimage.transform.SimilarityTransform`, or its inverse.
         - For 2-D images, you can pass a ``(3, 3)`` homogeneous
           transformation matrix, e.g.
           `skimage.transform.SimilarityTransform.params`.
         - For 2-D images, a function that transforms a ``(M, 2)`` array of
           ``(col, row)`` coordinates in the output image to their
           corresponding coordinates in the input image. Extra parameters to
           the function can be specified through `map_args`.
         - For N-D images, you can directly pass an array of coordinates.
           The first dimension specifies the coordinates in the input image,
           while the subsequent dimensions determine the position in the
           output image. E.g. in case of 2-D images, you need to pass an array
           of shape ``(2, rows, cols)``, where `rows` and `cols` determine the
           shape of the output image, and the first dimension contains the
           ``(row, col)`` coordinate in the input image.
           See `scipy.ndimage.map_coordinates` for further documentation.

        Note, that a ``(3, 3)`` matrix is interpreted as a homogeneous
        transformation matrix, so you cannot interpolate values from a 3-D
        input, if the output is of shape ``(3,)``.

        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.  Note that, even for multi-band images, only rows
        and columns need to be specified.
    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.
    clip : bool, optional
        Whether to clip the output to the float range of ``[0, 1]``, or
        ``[-1, 1]`` for input images with negative values. This is enabled by
        default, since  higher order interpolation may produce values outside
        the given input range.

    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. The image is shifted to the bottom.

    Use a geometric transform to warp an image (fast):

    >>> from skimage.transform import SimilarityTransform
    >>> tform = SimilarityTransform(translation=(0, -10))
    >>> warped = warp(image, tform)

    Use a callable (slow):

    >>> def shift_down(xy):
    ...     xy[:, 1] -= 10
    ...     return xy
    >>> warped = warp(image, shift_down)

    Use a transformation matrix to warp an image (fast):

    >>> matrix = np.array([[1, 0, 0], [0, 1, -10], [0, 0, 1]])
    >>> warped = warp(image, matrix)
    >>> from skimage.transform import ProjectiveTransform
    >>> warped = warp(image, ProjectiveTransform(matrix=matrix))

    You can also use the inverse of a geometric transformation (fast):

    >>> warped = warp(image, tform.inverse)

    For N-D images you can pass a coordinate array, that specifies the
    coordinates in the input image for every element in the output image. E.g.
    if you want to rescale a 3-D cube, you can do:

    >>> cube_shape = np.array([30, 30, 30])
    >>> cube = np.random.rand(*cube_shape)

    Setup the coordinate array, that defines the scaling:

    >>> scale = 0.1
    >>> output_shape = (scale * cube_shape).astype(int)
    >>> coords0, coords1, coords2 = np.mgrid[:output_shape[0],
    ...                    :output_shape[1], :output_shape[2]]
    >>> coords = np.array([coords0, coords1, coords2])

    Assume that the cube contains spatial data, where the first array element
    center is at coordinate (0.5, 0.5, 0.5) in real space, i.e. we have to
    account for this extra offset when scaling the image:

    >>> coords = (coords + 0.5) / scale - 0.5
    >>> warped = warp(cube, coords)

    """

    image = img_as_float(image)
    input_shape = np.array(image.shape)

    if output_shape is None:
        output_shape = input_shape
    else:
        output_shape = safe_as_int(output_shape)

    out = None

    if order == 2:
        # When fixing this issue, make sure to fix the branches further
        # below in this function
        warnings.warn("Bi-quadratic interpolation behavior has changed due "
                      "to a bug in the implementation of scikit-image. "
                      "The new version now serves as a wrapper "
                      "around SciPy's interpolation functions, which itself "
                      "is not verified to be a correct implementation. Until "
                      "skimage's implementation is fixed, we recommend "
                      "to use bi-linear or bi-cubic interpolation instead.")

    if order in (0, 1, 3) and not map_args:
        # use fast Cython version for specific interpolation orders and input

        matrix = None

        if isinstance(inverse_map, np.ndarray) and inverse_map.shape == (3, 3):
            # inverse_map is a transformation matrix as numpy array
            matrix = inverse_map

        elif isinstance(inverse_map, HOMOGRAPHY_TRANSFORMS):
            # inverse_map is a homography
            matrix = inverse_map.params

        elif (hasattr(inverse_map, '__name__')
              and inverse_map.__name__ == 'inverse'
              and get_bound_method_class(inverse_map) \
                  in HOMOGRAPHY_TRANSFORMS):
            # inverse_map is the inverse of a homography
            matrix = np.linalg.inv(six.get_method_self(inverse_map).params)

        if matrix is not None:
            matrix = matrix.astype(np.double)
            if image.ndim == 2:
                out = _warp_fast(image, matrix,
                                 output_shape=output_shape,
                                 order=order, mode=mode, cval=cval)
            elif image.ndim == 3:
                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 out is None:
        # use ndimage.map_coordinates

        if (isinstance(inverse_map, np.ndarray)
                and inverse_map.shape == (3, 3)):
            # inverse_map is a transformation matrix as numpy array,
            # this is only used for order >= 4.
            inverse_map = ProjectiveTransform(matrix=inverse_map)

        if isinstance(inverse_map, np.ndarray):
            # inverse_map is directly given as coordinates
            coords = inverse_map
        else:
            # inverse_map is given as function, that transforms (N, 2)
            # destination coordinates to their corresponding source
            # coordinates. This is only supported for 2(+1)-D images.

            if image.ndim < 2 or image.ndim > 3:
                raise ValueError("Only 2-D images (grayscale or color) are "
                                 "supported, when providing a callable "
                                 "`inverse_map`.")

            def coord_map(*args):
                return inverse_map(*args, **map_args)

            if len(input_shape) == 3 and len(output_shape) == 2:
                # Input image is 2D and has color channel, but output_shape is
                # given for 2-D images. Automatically add the color channel
                # dimensionality.
                output_shape = (output_shape[0], output_shape[1],
                                input_shape[2])

            coords = warp_coords(coord_map, output_shape)

        # 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)

    if clip:
        # The spline filters sometimes return results outside [0, 1],
        # so clip to ensure valid data

        if np.min(image) < 0:
            min_val = -1
        else:
            min_val = 0
        max_val = 1

        clipped = np.clip(out, min_val, max_val)

        if mode == 'constant' and not (0 <= cval <= 1):
            clipped[out == cval] = cval

        out = clipped

    return out
Esempio n. 6
0
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
Esempio n. 7
0
def warp(image,
         inverse_map=None,
         map_args={},
         output_shape=None,
         order=1,
         mode='constant',
         cval=0.,
         clip=True):
    """Warp an image according to a given coordinate transformation.

    Parameters
    ----------
    image : ndarray
        Input image.
    inverse_map : transformation object, callable ``cr = f(cr, **kwargs)``, or ndarray
        Inverse coordinate map, which transforms coordinates in the output
        images into their corresponding coordinates in the input image.

        There are a number of different options to define this map, depending
        on the dimensionality of the input image. A 2-D image can have 2
        dimensions for gray-scale images, or 3 dimensions with color
        information.

         - For 2-D images, you can directly pass a transformation object,
           e.g. `skimage.transform.SimilarityTransform`, or its inverse.
         - For 2-D images, you can pass a ``(3, 3)`` homogeneous
           transformation matrix, e.g.
           `skimage.transform.SimilarityTransform.params`.
         - For 2-D images, a function that transforms a ``(M, 2)`` array of
           ``(col, row)`` coordinates in the output image to their
           corresponding coordinates in the input image. Extra parameters to
           the function can be specified through `map_args`.
         - For N-D images, you can directly pass an array of coordinates.
           The first dimension specifies the coordinates in the input image,
           while the subsequent dimensions determine the position in the
           output image. E.g. in case of 2-D images, you need to pass an array
           of shape ``(2, rows, cols)``, where `rows` and `cols` determine the
           shape of the output image, and the first dimension contains the
           ``(row, col)`` coordinate in the input image.
           See `scipy.ndimage.map_coordinates` for further documentation.

        Note, that a ``(3, 3)`` matrix is interpreted as a homogeneous
        transformation matrix, so you cannot interpolate values from a 3-D
        input, if the output is of shape ``(3,)``.

        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.  Note that, even for multi-band images, only rows
        and columns need to be specified.
    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.
    clip : bool, optional
        Whether to clip the output to the float range of ``[0, 1]``, or
        ``[-1, 1]`` for input images with negative values. This is enabled by
        default, since  higher order interpolation may produce values outside
        the given input range.

    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. The image is shifted to the bottom.

    Use a geometric transform to warp an image (fast):

    >>> from skimage.transform import SimilarityTransform
    >>> tform = SimilarityTransform(translation=(0, -10))
    >>> warped = warp(image, tform)

    Use a callable (slow):

    >>> def shift_down(xy):
    ...     xy[:, 1] -= 10
    ...     return xy
    >>> warped = warp(image, shift_down)

    Use a transformation matrix to warp an image (fast):

    >>> matrix = np.array([[1, 0, 0], [0, 1, -10], [0, 0, 1]])
    >>> warped = warp(image, matrix)
    >>> from skimage.transform import ProjectiveTransform
    >>> warped = warp(image, ProjectiveTransform(matrix=matrix))

    You can also use the inverse of a geometric transformation (fast):

    >>> warped = warp(image, tform.inverse)

    For N-D images you can pass a coordinate array, that specifies the
    coordinates in the input image for every element in the output image. E.g.
    if you want to rescale a 3-D cube, you can do:

    >>> cube_shape = np.array([30, 30, 30])
    >>> cube = np.random.rand(*cube_shape)

    Setup the coordinate array, that defines the scaling:

    >>> scale = 0.1
    >>> output_shape = (scale * cube_shape).astype(int)
    >>> coords0, coords1, coords2 = \
    ...     np.mgrid[:output_shape[0], :output_shape[1], :output_shape[2]]
    >>> coords = np.array([coords0, coords1, coords2])

    Assume that the cube contains spatial data, where the first array element
    center is at coordinate (0.5, 0.5, 0.5) in real space, i.e. we have to
    account for this extra offset when scaling the image:

    >>> coords = (coords + 0.5) / scale - 0.5
    >>> warped = warp(cube, coords)

    """

    image = img_as_float(image)
    input_shape = np.array(image.shape)

    if output_shape is None:
        output_shape = input_shape
    else:
        output_shape = safe_as_int(output_shape)

    out = None

    if order in range(4) and not map_args:
        # use fast Cython version for specific interpolation orders and input

        matrix = None

        if isinstance(inverse_map, np.ndarray) and inverse_map.shape == (3, 3):
            # inverse_map is a transformation matrix as numpy array
            matrix = inverse_map

        elif isinstance(inverse_map, HOMOGRAPHY_TRANSFORMS):
            # inverse_map is a homography
            matrix = inverse_map.params

        elif (hasattr(inverse_map, '__name__')
              and inverse_map.__name__ == 'inverse'
              and get_bound_method_class(inverse_map) \
                  in HOMOGRAPHY_TRANSFORMS):
            # inverse_map is the inverse of a homography
            matrix = np.linalg.inv(six.get_method_self(inverse_map).params)

        if matrix is not None:
            matrix = matrix.astype(np.double)
            if image.ndim == 2:
                out = _warp_fast(image,
                                 matrix,
                                 output_shape=output_shape,
                                 order=order,
                                 mode=mode,
                                 cval=cval)
            elif image.ndim == 3:
                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 out is None:
        # use ndimage.map_coordinates

        if (isinstance(inverse_map, np.ndarray)
                and inverse_map.shape == (3, 3)):
            # inverse_map is a transformation matrix as numpy array,
            # this is only used for order >= 4.
            inverse_map = ProjectiveTransform(matrix=inverse_map)

        if isinstance(inverse_map, np.ndarray):
            # inverse_map is directly given as coordinates
            coords = inverse_map
        else:
            # inverse_map is given as function, that transforms (N, 2)
            # destination coordinates to their corresponding source
            # coordinates. This is only supported for 2(+1)-D images.

            if image.ndim < 2 or image.ndim > 3:
                raise ValueError("Only 2-D images (grayscale or color) are "
                                 "supported, when providing a callable "
                                 "`inverse_map`.")

            def coord_map(*args):
                return inverse_map(*args, **map_args)

            if len(input_shape) == 3 and len(output_shape) == 2:
                # Input image is 2D and has color channel, but output_shape is
                # given for 2-D images. Automatically add the color channel
                # dimensionality.
                output_shape = (output_shape[0], output_shape[1],
                                input_shape[2])

            coords = warp_coords(coord_map, output_shape)

        # 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)

    if clip:
        # The spline filters sometimes return results outside [0, 1],
        # so clip to ensure valid data

        if np.min(image) < 0:
            min_val = -1
        else:
            min_val = 0
        max_val = 1

        clipped = np.clip(out, min_val, max_val)

        if mode == 'constant' and not (0 <= cval <= 1):
            clipped[out == cval] = cval

        out = clipped

    return out