mirror of
https://github.com/wassname/scikit-image.git
synced 2026-09-09 11:33:41 +08:00
ENH: Implement fast coordinate transformations.
This commit is contained in:
@@ -4,3 +4,5 @@ from .finite_radon_transform import *
|
||||
from .project import *
|
||||
from ._project import homography as fast_homography
|
||||
from .integral import *
|
||||
from ._warp import warp
|
||||
from ._swirl import swirl
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
from __future__ import division
|
||||
import numpy as np
|
||||
|
||||
from ._warp import warp
|
||||
|
||||
|
||||
def _swirl_mapping(xy, center, rotation, strength, radius):
|
||||
x, y = xy.T
|
||||
x0, y0 = center
|
||||
radius = radius / 5 * np.log(2)
|
||||
|
||||
rho = np.sqrt((x - x0)**2 + (y - y0)**2)
|
||||
theta = rotation + strength * \
|
||||
np.exp(-rho / radius) + \
|
||||
np.arctan2(y - y0, x - x0)
|
||||
|
||||
xy[..., 0] = x0 + rho * np.cos(theta)
|
||||
xy[..., 1] = y0 + rho * np.sin(theta)
|
||||
|
||||
return xy
|
||||
|
||||
def swirl(image, center=None, strength=1, radius=100, rotation=0,
|
||||
output_shape=None, order=1, mode='constant', cval=0):
|
||||
"""Perform a swirl transformation.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
image : ndarray
|
||||
Input image.
|
||||
center : (x,y) tuple or (2,) ndarray
|
||||
Center coordinate of transformation.
|
||||
strength : float
|
||||
The amount of swirling applied.
|
||||
radius : float
|
||||
The extent of the swirling in pixels. The effect dies out
|
||||
rapidly beyond radius.
|
||||
rotation : float
|
||||
Additional rotation applied to the image.
|
||||
|
||||
Returns
|
||||
-------
|
||||
swirled : ndarray
|
||||
Swirled version of the input.
|
||||
|
||||
Other parameters
|
||||
----------------
|
||||
output_shape : tuple or ndarray
|
||||
Size of the generated output image.
|
||||
order : int
|
||||
Order of splines used in interpolation, passed as-is to ndimage.
|
||||
mode : string
|
||||
How to handle values outside the image borders, passed as-is
|
||||
to ndimage.
|
||||
cval : string
|
||||
Used in conjunction with mode 'constant', the value outside
|
||||
the image boundaries.
|
||||
|
||||
"""
|
||||
|
||||
if center is None:
|
||||
center = np.array(image.shape)[:2] / 2
|
||||
|
||||
warp_args = {'center': center,
|
||||
'rotation': rotation,
|
||||
'strength': strength,
|
||||
'radius': radius}
|
||||
|
||||
return warp(image, _swirl_mapping, tf_args=warp_args,
|
||||
output_shape=output_shape,
|
||||
order=order, mode=mode, cval=cval)
|
||||
@@ -0,0 +1,91 @@
|
||||
__all__ = ['warp']
|
||||
|
||||
import numpy as np
|
||||
from scipy import ndimage
|
||||
from skimage.util import img_as_float
|
||||
|
||||
eps = np.finfo(float).eps
|
||||
|
||||
def _stackcopy(a, b):
|
||||
"""a[:,:,0] = a[:,:,1] = ... = b"""
|
||||
if a.ndim == 3:
|
||||
a.transpose().swapaxes(1, 2)[:] = b
|
||||
else:
|
||||
a[:] = b
|
||||
|
||||
def warp(image, coord_tf, tf_args={},
|
||||
output_shape=None, order=1, mode='constant', cval=0.):
|
||||
"""Warp an image according to a given coordinate transformation.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
image : 2-D array
|
||||
Input image.
|
||||
coord_tf : callable xy = f(xy, **kwargs)
|
||||
Function that transforms an Nx2 array of ``(x, y)`` coordinates
|
||||
in the *output image* into their corresponding coordinates in the
|
||||
*source image*. Note that this is a reverse mapping (also
|
||||
see examples below).
|
||||
tf_args : dict, optional
|
||||
Keyword arguments passed to `coord_tf`.
|
||||
output_shape : tuple (rows, cols)
|
||||
Shape of the output image generated.
|
||||
order : int
|
||||
Order of splines used in interpolation.
|
||||
mode : string
|
||||
How to handle values outside the image borders. Passed as-is
|
||||
to ndimage.
|
||||
cval : string
|
||||
Used in conjunction with mode 'constant', the value outside
|
||||
the image boundaries.
|
||||
|
||||
Examples
|
||||
--------
|
||||
Shift an image to the right:
|
||||
|
||||
>>> from skimage import data
|
||||
>>> image = data.camera()
|
||||
>>>
|
||||
>>> def shift_right(xy):
|
||||
... xy[:, 0] -= 10
|
||||
... return xy
|
||||
>>>
|
||||
>>> warp(image, shift_right)
|
||||
|
||||
"""
|
||||
if image.ndim < 2:
|
||||
raise ValueError("Input must have more than 1 dimension.")
|
||||
|
||||
image = np.atleast_3d(img_as_float(image))
|
||||
ishape = np.array(image.shape)
|
||||
bands = ishape[2]
|
||||
|
||||
if output_shape is None:
|
||||
output_shape = ishape
|
||||
|
||||
coords = np.empty(np.r_[3, output_shape], dtype=float)
|
||||
|
||||
# Construct transformed coordinates
|
||||
rows, cols = output_shape[:2]
|
||||
tf_coords = np.indices((cols, rows), dtype=float).reshape(2, -1).T
|
||||
|
||||
tf_coords = coord_tf(tf_coords, **tf_args)
|
||||
tf_coords = tf_coords.T.reshape((-1, cols, rows)).swapaxes(1, 2)
|
||||
|
||||
# y-coordinate mapping
|
||||
_stackcopy(coords[1, ...], tf_coords[0, ...])
|
||||
|
||||
# x-coordinate mapping
|
||||
_stackcopy(coords[0, ...], tf_coords[1, ...])
|
||||
|
||||
# colour-coordinate mapping
|
||||
coords[2, ...] = range(bands)
|
||||
|
||||
# Prefilter not necessary for order 1 interpolation
|
||||
prefilter = order > 1
|
||||
mapped = 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
|
||||
return np.clip(mapped.squeeze(), 0, 1)
|
||||
@@ -4,18 +4,12 @@
|
||||
|
||||
import numpy as np
|
||||
from scipy.ndimage import interpolation as ndii
|
||||
from .warp import _stackcopy
|
||||
|
||||
__all__ = ['homography']
|
||||
|
||||
eps = np.finfo(float).eps
|
||||
|
||||
def _stackcopy(a, b):
|
||||
"""a[:,:,0] = a[:,:,1] = ... = b"""
|
||||
if a.ndim == 3:
|
||||
a.transpose().swapaxes(1, 2)[:] = b
|
||||
else:
|
||||
a[:] = b
|
||||
|
||||
def homography(image, H, output_shape=None, order=1,
|
||||
mode='constant', cval=0.):
|
||||
"""Perform a projective transformation (homography) on an image.
|
||||
@@ -106,6 +100,8 @@ def homography(image, H, output_shape=None, order=1,
|
||||
|
||||
coords = np.empty(np.r_[3, output_shape], dtype=float)
|
||||
|
||||
# TODO: Refactor this method to use transform.warp instead.
|
||||
|
||||
# Construct transformed coordinates
|
||||
rows, cols = output_shape[:2]
|
||||
rows, cols = np.mgrid[:rows, :cols]
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import numpy as np
|
||||
from numpy.testing import assert_array_almost_equal
|
||||
|
||||
from skimage.transform.project import _stackcopy
|
||||
from skimage.transform._warp import _stackcopy
|
||||
from skimage.transform import homography, fast_homography
|
||||
from skimage import data
|
||||
from skimage.color import rgb2gray
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
import numpy as np
|
||||
from numpy.testing import assert_array_almost_equal
|
||||
|
||||
from skimage import transform as tf, data, img_as_float
|
||||
|
||||
|
||||
def test_roundtrip():
|
||||
image = img_as_float(data.checkerboard())
|
||||
swirl_params = {'radius': 80, 'rotation': 0, 'order': 2, 'mode': 'reflect'}
|
||||
unswirled = tf.swirl(
|
||||
tf.swirl(image, strength=10, **swirl_params),
|
||||
strength=-10, **swirl_params
|
||||
)
|
||||
|
||||
assert np.mean(np.abs(image - unswirled)) < 0.01
|
||||
|
||||
if __name__ == "__main__":
|
||||
np.testing.run_module_suite()
|
||||
Reference in New Issue
Block a user