mirror of
https://github.com/wassname/scikit-image.git
synced 2026-07-13 17:45:20 +08:00
Merge pull request #1302 from ahojnnes/warp
Clip to min and max range of input image, add missing clip parameter to ...
This commit is contained in:
@@ -32,7 +32,7 @@ def test_binary_descriptors_lena_rotation_crosscheck_false():
|
||||
img = data.lena()
|
||||
img = rgb2gray(img)
|
||||
tform = tf.SimilarityTransform(scale=1, rotation=0.15, translation=(0, 0))
|
||||
rotated_img = tf.warp(img, tform)
|
||||
rotated_img = tf.warp(img, tform, clip=False)
|
||||
|
||||
extractor = BRIEF(descriptor_size=512)
|
||||
|
||||
@@ -65,7 +65,7 @@ def test_binary_descriptors_lena_rotation_crosscheck_true():
|
||||
img = data.lena()
|
||||
img = rgb2gray(img)
|
||||
tform = tf.SimilarityTransform(scale=1, rotation=0.15, translation=(0, 0))
|
||||
rotated_img = tf.warp(img, tform)
|
||||
rotated_img = tf.warp(img, tform, clip=False)
|
||||
|
||||
extractor = BRIEF(descriptor_size=512)
|
||||
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import numpy as np
|
||||
from numpy.testing import assert_array_equal, assert_almost_equal
|
||||
from numpy.testing import assert_equal, assert_almost_equal, run_module_suite
|
||||
from skimage.feature import ORB
|
||||
from skimage.data import lena
|
||||
from skimage import data
|
||||
from skimage.color import rgb2gray
|
||||
|
||||
|
||||
img = rgb2gray(lena())
|
||||
img = rgb2gray(data.lena())
|
||||
|
||||
|
||||
def test_keypoints_orb_desired_no_of_keypoints():
|
||||
@@ -42,7 +42,6 @@ def test_keypoints_orb_desired_no_of_keypoints():
|
||||
|
||||
|
||||
def test_keypoints_orb_less_than_desired_no_of_keypoints():
|
||||
img = rgb2gray(lena())
|
||||
detector_extractor = ORB(n_keypoints=15, fast_n=12,
|
||||
fast_threshold=0.33, downscale=2, n_scales=2)
|
||||
detector_extractor.detect(img)
|
||||
@@ -102,14 +101,13 @@ def test_descriptor_orb():
|
||||
detector_extractor.extract(img, detector_extractor.keypoints,
|
||||
detector_extractor.scales,
|
||||
detector_extractor.orientations)
|
||||
assert_array_equal(exp_descriptors,
|
||||
detector_extractor.descriptors[100:120, 10:20])
|
||||
assert_equal(exp_descriptors,
|
||||
detector_extractor.descriptors[100:120, 10:20])
|
||||
|
||||
detector_extractor.detect_and_extract(img)
|
||||
assert_array_equal(exp_descriptors,
|
||||
detector_extractor.descriptors[100:120, 10:20])
|
||||
assert_equal(exp_descriptors,
|
||||
detector_extractor.descriptors[100:120, 10:20])
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
from numpy import testing
|
||||
testing.run_module_suite()
|
||||
run_module_suite()
|
||||
|
||||
@@ -353,8 +353,9 @@ class Picture(object):
|
||||
if (value[0] != self.width) or (value[1] != self.height):
|
||||
# skimage dimensions are flipped: y, x
|
||||
new_size = (int(value[1]), int(value[0]))
|
||||
new_array = resize(self.array, new_size, order=0)
|
||||
self.array = img_as_ubyte(new_array)
|
||||
new_array = resize(self.array, new_size, order=0,
|
||||
preserve_range=True)
|
||||
self.array = new_array.astype(np.uint8)
|
||||
|
||||
self._array_modified()
|
||||
|
||||
|
||||
@@ -4,8 +4,9 @@ import warnings
|
||||
import numpy as np
|
||||
from scipy import ndimage, spatial
|
||||
|
||||
from skimage._shared.utils import get_bound_method_class, safe_as_int
|
||||
from skimage.util import img_as_float
|
||||
from .._shared.utils import get_bound_method_class, safe_as_int
|
||||
from ..util import img_as_float
|
||||
from ..exposure import rescale_intensity
|
||||
from ._warps_cy import _warp_fast
|
||||
|
||||
|
||||
@@ -994,8 +995,64 @@ def warp_coords(coord_map, shape, dtype=np.float64):
|
||||
return coords
|
||||
|
||||
|
||||
def _convert_warp_input(image, preserve_range):
|
||||
"""Convert input image to double image with the appropriate range."""
|
||||
if preserve_range:
|
||||
image = image.astype(np.double)
|
||||
else:
|
||||
image = img_as_float(image)
|
||||
return image
|
||||
|
||||
|
||||
def _clip_warp_output(input_image, output_image, order, mode, cval, clip):
|
||||
"""Clip output image to range of values of input image.
|
||||
|
||||
Note that this function modifies the values of `output_image` in-place
|
||||
and it is only modified if ``clip=True``.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
input_image : ndarray
|
||||
Input image.
|
||||
output_image : ndarray
|
||||
Output image, which is modified in-place.
|
||||
|
||||
Other parameters
|
||||
----------------
|
||||
order : int, optional
|
||||
The order of the spline interpolation, default is 1. The order has to
|
||||
be in the range 0-5. See `skimage.transform.warp` for detail.
|
||||
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 range of values of the input image.
|
||||
This is enabled by default, since higher order interpolation may
|
||||
produce values outside the given input range.
|
||||
|
||||
"""
|
||||
|
||||
if clip and order != 0:
|
||||
min_val = input_image.min()
|
||||
max_val = input_image.max()
|
||||
|
||||
preserve_cval = mode == 'constant' and not \
|
||||
(min_val <= cval <= max_val)
|
||||
|
||||
if preserve_cval:
|
||||
cval_mask = output_image == cval
|
||||
|
||||
np.clip(output_image, min_val, max_val, out=output_image)
|
||||
|
||||
if preserve_cval:
|
||||
output_image[cval_mask] = cval
|
||||
|
||||
|
||||
def warp(image, inverse_map=None, map_args={}, output_shape=None, order=1,
|
||||
mode='constant', cval=0., clip=True):
|
||||
mode='constant', cval=0., clip=True, preserve_range=False):
|
||||
"""Warp an image according to a given coordinate transformation.
|
||||
|
||||
Parameters
|
||||
@@ -1055,17 +1112,25 @@ def warp(image, inverse_map=None, map_args={}, output_shape=None, order=1,
|
||||
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.
|
||||
Whether to clip the output to the range of values of the input image.
|
||||
This is enabled by default, since higher order interpolation may
|
||||
produce values outside the given input range.
|
||||
preserve_range : bool, optional
|
||||
Whether to keep the original range of values. Otherwise, the input
|
||||
image is converted according to the conventions of `img_as_float`.
|
||||
|
||||
Returns
|
||||
-------
|
||||
warped : double ndarray
|
||||
The warped input image.
|
||||
|
||||
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.
|
||||
- The input image is converted to a `double` image.
|
||||
- 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
|
||||
--------
|
||||
@@ -1124,7 +1189,8 @@ def warp(image, inverse_map=None, map_args={}, output_shape=None, order=1,
|
||||
|
||||
"""
|
||||
|
||||
image = img_as_float(image)
|
||||
image = _convert_warp_input(image, preserve_range)
|
||||
|
||||
input_shape = np.array(image.shape)
|
||||
|
||||
if output_shape is None:
|
||||
@@ -1132,7 +1198,7 @@ def warp(image, inverse_map=None, map_args={}, output_shape=None, order=1,
|
||||
else:
|
||||
output_shape = safe_as_int(output_shape)
|
||||
|
||||
out = None
|
||||
warped = None
|
||||
|
||||
if order == 2:
|
||||
# When fixing this issue, make sure to fix the branches further
|
||||
@@ -1168,7 +1234,7 @@ def warp(image, inverse_map=None, map_args={}, output_shape=None, order=1,
|
||||
if matrix is not None:
|
||||
matrix = matrix.astype(np.double)
|
||||
if image.ndim == 2:
|
||||
out = _warp_fast(image, matrix,
|
||||
warped = _warp_fast(image, matrix,
|
||||
output_shape=output_shape,
|
||||
order=order, mode=mode, cval=cval)
|
||||
elif image.ndim == 3:
|
||||
@@ -1177,9 +1243,9 @@ def warp(image, inverse_map=None, map_args={}, output_shape=None, order=1,
|
||||
dims.append(_warp_fast(image[..., dim], matrix,
|
||||
output_shape=output_shape,
|
||||
order=order, mode=mode, cval=cval))
|
||||
out = np.dstack(dims)
|
||||
warped = np.dstack(dims)
|
||||
|
||||
if out is None:
|
||||
if warped is None:
|
||||
# use ndimage.map_coordinates
|
||||
|
||||
if (isinstance(inverse_map, np.ndarray)
|
||||
@@ -1216,24 +1282,10 @@ def warp(image, inverse_map=None, map_args={}, output_shape=None, order=1,
|
||||
# Pre-filtering not necessary for order 0, 1 interpolation
|
||||
prefilter = order > 1
|
||||
|
||||
out = ndimage.map_coordinates(image, coords, prefilter=prefilter,
|
||||
warped = 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
|
||||
_clip_warp_output(image, warped, order, mode, cval, clip)
|
||||
|
||||
clipped = np.clip(out, min_val, max_val)
|
||||
|
||||
if mode == 'constant' and not (0 <= cval <= 1):
|
||||
clipped[out == cval] = cval
|
||||
|
||||
out = clipped
|
||||
|
||||
return out
|
||||
return warped
|
||||
|
||||
+55
-19
@@ -1,12 +1,13 @@
|
||||
import numpy as np
|
||||
from scipy import ndimage
|
||||
|
||||
from skimage.transform._geometric import (warp, SimilarityTransform,
|
||||
AffineTransform)
|
||||
from skimage.measure import block_reduce
|
||||
from ..measure import block_reduce
|
||||
from ._geometric import (warp, SimilarityTransform, AffineTransform,
|
||||
_convert_warp_input, _clip_warp_output)
|
||||
|
||||
|
||||
def resize(image, output_shape, order=1, mode='constant', cval=0.):
|
||||
def resize(image, output_shape, order=1, mode='constant', cval=0, clip=True,
|
||||
preserve_range=False):
|
||||
"""Resize image to match a certain size.
|
||||
|
||||
Performs interpolation to up-size or down-size images. For down-sampling
|
||||
@@ -40,6 +41,13 @@ def resize(image, output_shape, order=1, mode='constant', cval=0.):
|
||||
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 range of values of the input image.
|
||||
This is enabled by default, since higher order interpolation may
|
||||
produce values outside the given input range.
|
||||
preserve_range : bool, optional
|
||||
Whether to keep the original range of values. Otherwise, the input
|
||||
image is converted according to the conventions of `img_as_float`.
|
||||
|
||||
Examples
|
||||
--------
|
||||
@@ -71,8 +79,12 @@ def resize(image, output_shape, order=1, mode='constant', cval=0.):
|
||||
|
||||
coord_map = np.array([map_rows, map_cols, map_dims])
|
||||
|
||||
out = ndimage.map_coordinates(image, coord_map, order=order, mode=mode,
|
||||
cval=cval)
|
||||
image = _convert_warp_input(image, preserve_range)
|
||||
|
||||
out = ndimage.map_coordinates(image, coord_map, order=order,
|
||||
mode=mode, cval=cval)
|
||||
|
||||
_clip_warp_output(image, out, order, mode, cval, clip)
|
||||
|
||||
else: # 2-dimensional interpolation
|
||||
|
||||
@@ -87,12 +99,13 @@ def resize(image, output_shape, order=1, mode='constant', cval=0.):
|
||||
tform.estimate(src_corners, dst_corners)
|
||||
|
||||
out = warp(image, tform, output_shape=output_shape, order=order,
|
||||
mode=mode, cval=cval)
|
||||
mode=mode, cval=cval, clip=clip, preserve_range=preserve_range)
|
||||
|
||||
return out
|
||||
|
||||
|
||||
def rescale(image, scale, order=1, mode='constant', cval=0.):
|
||||
def rescale(image, scale, order=1, mode='constant', cval=0, clip=True,
|
||||
preserve_range=False):
|
||||
"""Scale image by a certain factor.
|
||||
|
||||
Performs interpolation to upscale or down-scale images. For down-sampling
|
||||
@@ -124,6 +137,13 @@ def rescale(image, scale, order=1, mode='constant', cval=0.):
|
||||
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 range of values of the input image.
|
||||
This is enabled by default, since higher order interpolation may
|
||||
produce values outside the given input range.
|
||||
preserve_range : bool, optional
|
||||
Whether to keep the original range of values. Otherwise, the input
|
||||
image is converted according to the conventions of `img_as_float`.
|
||||
|
||||
Examples
|
||||
--------
|
||||
@@ -147,11 +167,12 @@ def rescale(image, scale, order=1, mode='constant', cval=0.):
|
||||
cols = np.round(col_scale * orig_cols)
|
||||
output_shape = (rows, cols)
|
||||
|
||||
return resize(image, output_shape, order=order, mode=mode, cval=cval)
|
||||
return resize(image, output_shape, order=order, mode=mode, cval=cval,
|
||||
clip=clip, preserve_range=preserve_range)
|
||||
|
||||
|
||||
def rotate(image, angle, resize=False, order=1, mode='constant', cval=0.,
|
||||
center=None):
|
||||
def rotate(image, angle, resize=False, center=None, order=1, mode='constant',
|
||||
cval=0, clip=True, preserve_range=False):
|
||||
"""Rotate image by a certain angle around its center.
|
||||
|
||||
Parameters
|
||||
@@ -164,6 +185,9 @@ def rotate(image, angle, resize=False, order=1, mode='constant', cval=0.,
|
||||
Determine whether the shape of the output image will be automatically
|
||||
calculated, so the complete rotated image exactly fits. Default is
|
||||
False.
|
||||
center : iterable of length 2
|
||||
The rotation center. If ``center=None``, the image is rotated around
|
||||
its center, i.e. ``center=(rows / 2 - 0.5, cols / 2 - 0.5)``.
|
||||
|
||||
Returns
|
||||
-------
|
||||
@@ -181,9 +205,13 @@ def rotate(image, angle, resize=False, order=1, mode='constant', cval=0.,
|
||||
cval : float, optional
|
||||
Used in conjunction with mode 'constant', the value outside
|
||||
the image boundaries.
|
||||
center : iterable of length 2
|
||||
The rotation center. If ``center=None``, the image is rotated around
|
||||
its center, i.e. ``center=(rows / 2 - 0.5, cols / 2 - 0.5)``.
|
||||
clip : bool, optional
|
||||
Whether to clip the output to the range of values of the input image.
|
||||
This is enabled by default, since higher order interpolation may
|
||||
produce values outside the given input range.
|
||||
preserve_range : bool, optional
|
||||
Whether to keep the original range of values. Otherwise, the input
|
||||
image is converted according to the conventions of `img_as_float`.
|
||||
|
||||
Examples
|
||||
--------
|
||||
@@ -230,10 +258,10 @@ def rotate(image, angle, resize=False, order=1, mode='constant', cval=0.,
|
||||
tform = tform4 + tform
|
||||
|
||||
return warp(image, tform, output_shape=output_shape, order=order,
|
||||
mode=mode, cval=cval)
|
||||
mode=mode, cval=cval, clip=clip, preserve_range=preserve_range)
|
||||
|
||||
|
||||
def downscale_local_mean(image, factors, cval=0):
|
||||
def downscale_local_mean(image, factors, cval=0, clip=True):
|
||||
"""Down-sample N-dimensional image by local averaging.
|
||||
|
||||
The image is padded with `cval` if it is not perfectly divisible by the
|
||||
@@ -294,7 +322,8 @@ def _swirl_mapping(xy, center, rotation, strength, radius):
|
||||
|
||||
|
||||
def swirl(image, center=None, strength=1, radius=100, rotation=0,
|
||||
output_shape=None, order=1, mode='constant', cval=0):
|
||||
output_shape=None, order=1, mode='constant', cval=0, clip=True,
|
||||
preserve_range=False):
|
||||
"""Perform a swirl transformation.
|
||||
|
||||
Parameters
|
||||
@@ -330,6 +359,13 @@ def swirl(image, center=None, strength=1, radius=100, rotation=0,
|
||||
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 range of values of the input image.
|
||||
This is enabled by default, since higher order interpolation may
|
||||
produce values outside the given input range.
|
||||
preserve_range : bool, optional
|
||||
Whether to keep the original range of values. Otherwise, the input
|
||||
image is converted according to the conventions of `img_as_float`.
|
||||
|
||||
"""
|
||||
|
||||
@@ -342,5 +378,5 @@ def swirl(image, center=None, strength=1, radius=100, rotation=0,
|
||||
'radius': radius}
|
||||
|
||||
return warp(image, _swirl_mapping, map_args=warp_args,
|
||||
output_shape=output_shape,
|
||||
order=order, mode=mode, cval=cval)
|
||||
output_shape=output_shape, order=order, mode=mode, cval=cval,
|
||||
clip=clip, preserve_range=preserve_range)
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
#cython: nonecheck=False
|
||||
#cython: wraparound=False
|
||||
import numpy as np
|
||||
|
||||
cimport numpy as cnp
|
||||
from skimage._shared.interpolation cimport (nearest_neighbour_interpolation,
|
||||
bilinear_interpolation,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
from numpy.testing import (assert_almost_equal, run_module_suite,
|
||||
assert_array_equal, assert_raises)
|
||||
assert_equal, assert_raises)
|
||||
import numpy as np
|
||||
from scipy.ndimage import map_coordinates
|
||||
|
||||
@@ -75,14 +75,15 @@ def test_warp_nd():
|
||||
|
||||
|
||||
def test_warp_clip():
|
||||
x = 2 * np.ones((5, 5), dtype=np.double)
|
||||
matrix = np.eye(3)
|
||||
x = np.zeros((5, 5), dtype=np.double)
|
||||
x[2, 2] = 1
|
||||
|
||||
outx = warp(x, matrix, order=0, clip=False)
|
||||
assert_almost_equal(x, outx)
|
||||
outx = rescale(x, 3, order=3, clip=False)
|
||||
assert outx.min() < 0
|
||||
|
||||
outx = warp(x, matrix, order=0, clip=True)
|
||||
assert_almost_equal(x / 2, outx)
|
||||
outx = rescale(x, 3, order=3, clip=True)
|
||||
assert_almost_equal(outx.min(), 0)
|
||||
assert_almost_equal(outx.max(), 1)
|
||||
|
||||
|
||||
def test_homography():
|
||||
@@ -235,17 +236,16 @@ def test_downscale_local_mean():
|
||||
out1 = downscale_local_mean(image1, (2, 3))
|
||||
expected1 = np.array([[ 4., 7.],
|
||||
[ 16., 19.]])
|
||||
assert_array_equal(expected1, out1)
|
||||
assert_equal(expected1, out1)
|
||||
|
||||
image2 = np.arange(5 * 8).reshape(5, 8)
|
||||
out2 = downscale_local_mean(image2, (4, 5))
|
||||
expected2 = np.array([[ 14. , 10.8],
|
||||
[ 8.5, 5.7]])
|
||||
assert_array_equal(expected2, out2)
|
||||
assert_equal(expected2, out2)
|
||||
|
||||
|
||||
def test_invalid():
|
||||
assert_raises(ValueError, warp, np.ones((4, )), SimilarityTransform())
|
||||
assert_raises(ValueError, warp, np.ones((4, 3, 3, 3)),
|
||||
SimilarityTransform())
|
||||
|
||||
@@ -254,7 +254,7 @@ def test_inverse():
|
||||
tform = SimilarityTransform(scale=0.5, rotation=0.1)
|
||||
inverse_tform = SimilarityTransform(matrix=np.linalg.inv(tform.params))
|
||||
image = np.arange(10 * 10).reshape(10, 10).astype(np.double)
|
||||
assert_array_equal(warp(image, inverse_tform), warp(image, tform.inverse))
|
||||
assert_equal(warp(image, inverse_tform), warp(image, tform.inverse))
|
||||
|
||||
|
||||
def test_slow_warp_nonint_oshape():
|
||||
@@ -266,5 +266,23 @@ def test_slow_warp_nonint_oshape():
|
||||
warp(image, lambda xy: xy, output_shape=(13.0001, 19.9999))
|
||||
|
||||
|
||||
def test_keep_range():
|
||||
image = np.linspace(0, 2, 25).reshape(5, 5)
|
||||
|
||||
out = rescale(image, 2, preserve_range=False, clip=True, order=0)
|
||||
assert out.min() == 0
|
||||
assert out.max() == 2
|
||||
|
||||
out = rescale(image, 2, preserve_range=True, clip=True, order=0)
|
||||
assert out.min() == 0
|
||||
assert out.max() == 2
|
||||
|
||||
out = rescale(image.astype(np.uint8), 2, preserve_range=False,
|
||||
clip=True, order=0)
|
||||
assert out.min() == 0
|
||||
assert out.max() == 2 / 255.0
|
||||
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
run_module_suite()
|
||||
|
||||
Reference in New Issue
Block a user