Fixed database typo and removed unnecessary class identifier.
This commit is contained in:
parent
00ad49a143
commit
45fb349a7d
5098 changed files with 952558 additions and 85 deletions
|
@ -0,0 +1,9 @@
|
|||
from ..._shared.testing import setup_test, teardown_test
|
||||
|
||||
|
||||
def setup():
|
||||
setup_test()
|
||||
|
||||
|
||||
def teardown():
|
||||
teardown_test()
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -0,0 +1,18 @@
|
|||
import numpy as np
|
||||
|
||||
from skimage.transform import frt2, ifrt2
|
||||
|
||||
|
||||
def test_frt():
|
||||
SIZE = 59
|
||||
try:
|
||||
import sympy.ntheory as sn
|
||||
assert sn.isprime(SIZE) == True
|
||||
except ImportError:
|
||||
pass
|
||||
|
||||
# Generate a test image
|
||||
L = np.tri(SIZE, dtype=np.int32) + np.tri(SIZE, dtype=np.int32)[::-1]
|
||||
f = frt2(L)
|
||||
fi = ifrt2(f)
|
||||
assert len(np.nonzero(L - fi)[0]) == 0
|
523
venv/Lib/site-packages/skimage/transform/tests/test_geometric.py
Normal file
523
venv/Lib/site-packages/skimage/transform/tests/test_geometric.py
Normal file
|
@ -0,0 +1,523 @@
|
|||
import numpy as np
|
||||
import re
|
||||
from skimage.transform._geometric import GeometricTransform
|
||||
from skimage.transform import (estimate_transform, matrix_transform,
|
||||
EuclideanTransform, SimilarityTransform,
|
||||
AffineTransform, FundamentalMatrixTransform,
|
||||
EssentialMatrixTransform, ProjectiveTransform,
|
||||
PolynomialTransform, PiecewiseAffineTransform)
|
||||
|
||||
from skimage._shared import testing
|
||||
from skimage._shared.testing import assert_equal, assert_almost_equal
|
||||
import textwrap
|
||||
|
||||
|
||||
SRC = np.array([
|
||||
[-12.3705, -10.5075],
|
||||
[-10.7865, 15.4305],
|
||||
[8.6985, 10.8675],
|
||||
[11.4975, -9.5715],
|
||||
[7.8435, 7.4835],
|
||||
[-5.3325, 6.5025],
|
||||
[6.7905, -6.3765],
|
||||
[-6.1695, -0.8235],
|
||||
])
|
||||
DST = np.array([
|
||||
[0, 0],
|
||||
[0, 5800],
|
||||
[4900, 5800],
|
||||
[4900, 0],
|
||||
[4479, 4580],
|
||||
[1176, 3660],
|
||||
[3754, 790],
|
||||
[1024, 1931],
|
||||
])
|
||||
|
||||
|
||||
def test_estimate_transform():
|
||||
for tform in ('euclidean', 'similarity', 'affine', 'projective',
|
||||
'polynomial'):
|
||||
estimate_transform(tform, SRC[:2, :], DST[:2, :])
|
||||
with testing.raises(ValueError):
|
||||
estimate_transform('foobar', SRC[:2, :], DST[:2, :])
|
||||
|
||||
|
||||
def test_matrix_transform():
|
||||
tform = AffineTransform(scale=(0.1, 0.5), rotation=2)
|
||||
assert_equal(tform(SRC), matrix_transform(SRC, tform.params))
|
||||
|
||||
|
||||
def test_euclidean_estimation():
|
||||
# exact solution
|
||||
tform = estimate_transform('euclidean', SRC[:2, :], SRC[:2, :] + 10)
|
||||
assert_almost_equal(tform(SRC[:2, :]), SRC[:2, :] + 10)
|
||||
assert_almost_equal(tform.params[0, 0], tform.params[1, 1])
|
||||
assert_almost_equal(tform.params[0, 1], - tform.params[1, 0])
|
||||
|
||||
# over-determined
|
||||
tform2 = estimate_transform('euclidean', SRC, DST)
|
||||
assert_almost_equal(tform2.inverse(tform2(SRC)), SRC)
|
||||
assert_almost_equal(tform2.params[0, 0], tform2.params[1, 1])
|
||||
assert_almost_equal(tform2.params[0, 1], - tform2.params[1, 0])
|
||||
|
||||
# via estimate method
|
||||
tform3 = EuclideanTransform()
|
||||
tform3.estimate(SRC, DST)
|
||||
assert_almost_equal(tform3.params, tform2.params)
|
||||
|
||||
|
||||
def test_euclidean_init():
|
||||
# init with implicit parameters
|
||||
rotation = 1
|
||||
translation = (1, 1)
|
||||
tform = EuclideanTransform(rotation=rotation, translation=translation)
|
||||
assert_almost_equal(tform.rotation, rotation)
|
||||
assert_almost_equal(tform.translation, translation)
|
||||
|
||||
# init with transformation matrix
|
||||
tform2 = EuclideanTransform(tform.params)
|
||||
assert_almost_equal(tform2.rotation, rotation)
|
||||
assert_almost_equal(tform2.translation, translation)
|
||||
|
||||
# test special case for scale if rotation=0
|
||||
rotation = 0
|
||||
translation = (1, 1)
|
||||
tform = EuclideanTransform(rotation=rotation, translation=translation)
|
||||
assert_almost_equal(tform.rotation, rotation)
|
||||
assert_almost_equal(tform.translation, translation)
|
||||
|
||||
# test special case for scale if rotation=90deg
|
||||
rotation = np.pi / 2
|
||||
translation = (1, 1)
|
||||
tform = EuclideanTransform(rotation=rotation, translation=translation)
|
||||
assert_almost_equal(tform.rotation, rotation)
|
||||
assert_almost_equal(tform.translation, translation)
|
||||
|
||||
|
||||
def test_similarity_estimation():
|
||||
# exact solution
|
||||
tform = estimate_transform('similarity', SRC[:2, :], DST[:2, :])
|
||||
assert_almost_equal(tform(SRC[:2, :]), DST[:2, :])
|
||||
assert_almost_equal(tform.params[0, 0], tform.params[1, 1])
|
||||
assert_almost_equal(tform.params[0, 1], - tform.params[1, 0])
|
||||
|
||||
# over-determined
|
||||
tform2 = estimate_transform('similarity', SRC, DST)
|
||||
assert_almost_equal(tform2.inverse(tform2(SRC)), SRC)
|
||||
assert_almost_equal(tform2.params[0, 0], tform2.params[1, 1])
|
||||
assert_almost_equal(tform2.params[0, 1], - tform2.params[1, 0])
|
||||
|
||||
# via estimate method
|
||||
tform3 = SimilarityTransform()
|
||||
tform3.estimate(SRC, DST)
|
||||
assert_almost_equal(tform3.params, tform2.params)
|
||||
|
||||
|
||||
def test_similarity_init():
|
||||
# init with implicit parameters
|
||||
scale = 0.1
|
||||
rotation = 1
|
||||
translation = (1, 1)
|
||||
tform = SimilarityTransform(scale=scale, rotation=rotation,
|
||||
translation=translation)
|
||||
assert_almost_equal(tform.scale, scale)
|
||||
assert_almost_equal(tform.rotation, rotation)
|
||||
assert_almost_equal(tform.translation, translation)
|
||||
|
||||
# init with transformation matrix
|
||||
tform2 = SimilarityTransform(tform.params)
|
||||
assert_almost_equal(tform2.scale, scale)
|
||||
assert_almost_equal(tform2.rotation, rotation)
|
||||
assert_almost_equal(tform2.translation, translation)
|
||||
|
||||
# test special case for scale if rotation=0
|
||||
scale = 0.1
|
||||
rotation = 0
|
||||
translation = (1, 1)
|
||||
tform = SimilarityTransform(scale=scale, rotation=rotation,
|
||||
translation=translation)
|
||||
assert_almost_equal(tform.scale, scale)
|
||||
assert_almost_equal(tform.rotation, rotation)
|
||||
assert_almost_equal(tform.translation, translation)
|
||||
|
||||
# test special case for scale if rotation=90deg
|
||||
scale = 0.1
|
||||
rotation = np.pi / 2
|
||||
translation = (1, 1)
|
||||
tform = SimilarityTransform(scale=scale, rotation=rotation,
|
||||
translation=translation)
|
||||
assert_almost_equal(tform.scale, scale)
|
||||
assert_almost_equal(tform.rotation, rotation)
|
||||
assert_almost_equal(tform.translation, translation)
|
||||
|
||||
# test special case for scale where the rotation isn't exactly 90deg,
|
||||
# but very close
|
||||
scale = 1.0
|
||||
rotation = np.pi / 2
|
||||
translation = (0, 0)
|
||||
params = np.array([[0, -1, 1.33226763e-15],
|
||||
[1, 2.22044605e-16, -1.33226763e-15],
|
||||
[0, 0, 1]])
|
||||
tform = SimilarityTransform(params)
|
||||
assert_almost_equal(tform.scale, scale)
|
||||
assert_almost_equal(tform.rotation, rotation)
|
||||
assert_almost_equal(tform.translation, translation)
|
||||
|
||||
|
||||
def test_affine_estimation():
|
||||
# exact solution
|
||||
tform = estimate_transform('affine', SRC[:3, :], DST[:3, :])
|
||||
assert_almost_equal(tform(SRC[:3, :]), DST[:3, :])
|
||||
|
||||
# over-determined
|
||||
tform2 = estimate_transform('affine', SRC, DST)
|
||||
assert_almost_equal(tform2.inverse(tform2(SRC)), SRC)
|
||||
|
||||
# via estimate method
|
||||
tform3 = AffineTransform()
|
||||
tform3.estimate(SRC, DST)
|
||||
assert_almost_equal(tform3.params, tform2.params)
|
||||
|
||||
|
||||
def test_affine_init():
|
||||
# init with implicit parameters
|
||||
scale = (0.1, 0.13)
|
||||
rotation = 1
|
||||
shear = 0.1
|
||||
translation = (1, 1)
|
||||
tform = AffineTransform(scale=scale, rotation=rotation, shear=shear,
|
||||
translation=translation)
|
||||
assert_almost_equal(tform.scale, scale)
|
||||
assert_almost_equal(tform.rotation, rotation)
|
||||
assert_almost_equal(tform.shear, shear)
|
||||
assert_almost_equal(tform.translation, translation)
|
||||
|
||||
# init with transformation matrix
|
||||
tform2 = AffineTransform(tform.params)
|
||||
assert_almost_equal(tform2.scale, scale)
|
||||
assert_almost_equal(tform2.rotation, rotation)
|
||||
assert_almost_equal(tform2.shear, shear)
|
||||
assert_almost_equal(tform2.translation, translation)
|
||||
|
||||
# scalar vs. tuple scale arguments
|
||||
assert_almost_equal(AffineTransform(scale=0.5).scale, AffineTransform(scale=(0.5, 0.5)).scale)
|
||||
|
||||
|
||||
def test_piecewise_affine():
|
||||
tform = PiecewiseAffineTransform()
|
||||
tform.estimate(SRC, DST)
|
||||
# make sure each single affine transform is exactly estimated
|
||||
assert_almost_equal(tform(SRC), DST)
|
||||
assert_almost_equal(tform.inverse(DST), SRC)
|
||||
|
||||
|
||||
def test_fundamental_matrix_estimation():
|
||||
src = np.array([1.839035, 1.924743, 0.543582, 0.375221,
|
||||
0.473240, 0.142522, 0.964910, 0.598376,
|
||||
0.102388, 0.140092, 15.994343, 9.622164,
|
||||
0.285901, 0.430055, 0.091150, 0.254594]).reshape(-1, 2)
|
||||
dst = np.array([1.002114, 1.129644, 1.521742, 1.846002,
|
||||
1.084332, 0.275134, 0.293328, 0.588992,
|
||||
0.839509, 0.087290, 1.779735, 1.116857,
|
||||
0.878616, 0.602447, 0.642616, 1.028681]).reshape(-1, 2)
|
||||
|
||||
tform = estimate_transform('fundamental', src, dst)
|
||||
|
||||
# Reference values obtained using COLMAP SfM library.
|
||||
tform_ref = np.array([[-0.217859, 0.419282, -0.0343075],
|
||||
[-0.0717941, 0.0451643, 0.0216073],
|
||||
[0.248062, -0.429478, 0.0221019]])
|
||||
assert_almost_equal(tform.params, tform_ref, 6)
|
||||
|
||||
|
||||
def test_fundamental_matrix_residuals():
|
||||
essential_matrix_tform = EssentialMatrixTransform(
|
||||
rotation=np.eye(3), translation=np.array([1, 0, 0]))
|
||||
tform = FundamentalMatrixTransform()
|
||||
tform.params = essential_matrix_tform.params
|
||||
src = np.array([[0, 0], [0, 0], [0, 0]])
|
||||
dst = np.array([[2, 0], [2, 1], [2, 2]])
|
||||
assert_almost_equal(tform.residuals(src, dst)**2, [0, 0.5, 2])
|
||||
|
||||
|
||||
def test_fundamental_matrix_forward():
|
||||
essential_matrix_tform = EssentialMatrixTransform(
|
||||
rotation=np.eye(3), translation=np.array([1, 0, 0]))
|
||||
tform = FundamentalMatrixTransform()
|
||||
tform.params = essential_matrix_tform.params
|
||||
src = np.array([[0, 0], [0, 1], [1, 1]])
|
||||
assert_almost_equal(tform(src), [[0, -1, 0], [0, -1, 1], [0, -1, 1]])
|
||||
|
||||
|
||||
def test_fundamental_matrix_inverse():
|
||||
essential_matrix_tform = EssentialMatrixTransform(
|
||||
rotation=np.eye(3), translation=np.array([1, 0, 0]))
|
||||
tform = FundamentalMatrixTransform()
|
||||
tform.params = essential_matrix_tform.params
|
||||
src = np.array([[0, 0], [0, 1], [1, 1]])
|
||||
assert_almost_equal(tform.inverse(src),
|
||||
[[0, 1, 0], [0, 1, -1], [0, 1, -1]])
|
||||
|
||||
|
||||
def test_essential_matrix_init():
|
||||
tform = EssentialMatrixTransform(rotation=np.eye(3),
|
||||
translation=np.array([0, 0, 1]))
|
||||
assert_equal(tform.params,
|
||||
np.array([0, -1, 0, 1, 0, 0, 0, 0, 0]).reshape(3, 3))
|
||||
|
||||
|
||||
def test_essential_matrix_estimation():
|
||||
src = np.array([1.839035, 1.924743, 0.543582, 0.375221,
|
||||
0.473240, 0.142522, 0.964910, 0.598376,
|
||||
0.102388, 0.140092, 15.994343, 9.622164,
|
||||
0.285901, 0.430055, 0.091150, 0.254594]).reshape(-1, 2)
|
||||
dst = np.array([1.002114, 1.129644, 1.521742, 1.846002,
|
||||
1.084332, 0.275134, 0.293328, 0.588992,
|
||||
0.839509, 0.087290, 1.779735, 1.116857,
|
||||
0.878616, 0.602447, 0.642616, 1.028681]).reshape(-1, 2)
|
||||
|
||||
tform = estimate_transform('essential', src, dst)
|
||||
|
||||
# Reference values obtained using COLMAP SfM library.
|
||||
tform_ref = np.array([[-0.0811666, 0.255449, -0.0478999],
|
||||
[-0.192392, -0.0531675, 0.119547],
|
||||
[0.177784, -0.22008, -0.015203]])
|
||||
assert_almost_equal(tform.params, tform_ref, 6)
|
||||
|
||||
|
||||
def test_essential_matrix_forward():
|
||||
tform = EssentialMatrixTransform(rotation=np.eye(3),
|
||||
translation=np.array([1, 0, 0]))
|
||||
src = np.array([[0, 0], [0, 1], [1, 1]])
|
||||
assert_almost_equal(tform(src), [[0, -1, 0], [0, -1, 1], [0, -1, 1]])
|
||||
|
||||
|
||||
def test_essential_matrix_inverse():
|
||||
tform = EssentialMatrixTransform(rotation=np.eye(3),
|
||||
translation=np.array([1, 0, 0]))
|
||||
src = np.array([[0, 0], [0, 1], [1, 1]])
|
||||
assert_almost_equal(tform.inverse(src),
|
||||
[[0, 1, 0], [0, 1, -1], [0, 1, -1]])
|
||||
|
||||
|
||||
def test_essential_matrix_residuals():
|
||||
tform = EssentialMatrixTransform(rotation=np.eye(3),
|
||||
translation=np.array([1, 0, 0]))
|
||||
src = np.array([[0, 0], [0, 0], [0, 0]])
|
||||
dst = np.array([[2, 0], [2, 1], [2, 2]])
|
||||
assert_almost_equal(tform.residuals(src, dst)**2, [0, 0.5, 2])
|
||||
|
||||
|
||||
def test_projective_estimation():
|
||||
# exact solution
|
||||
tform = estimate_transform('projective', SRC[:4, :], DST[:4, :])
|
||||
assert_almost_equal(tform(SRC[:4, :]), DST[:4, :])
|
||||
|
||||
# over-determined
|
||||
tform2 = estimate_transform('projective', SRC, DST)
|
||||
assert_almost_equal(tform2.inverse(tform2(SRC)), SRC)
|
||||
|
||||
# via estimate method
|
||||
tform3 = ProjectiveTransform()
|
||||
tform3.estimate(SRC, DST)
|
||||
assert_almost_equal(tform3.params, tform2.params)
|
||||
|
||||
|
||||
def test_projective_init():
|
||||
tform = estimate_transform('projective', SRC, DST)
|
||||
# init with transformation matrix
|
||||
tform2 = ProjectiveTransform(tform.params)
|
||||
assert_almost_equal(tform2.params, tform.params)
|
||||
|
||||
|
||||
def test_polynomial_estimation():
|
||||
# over-determined
|
||||
tform = estimate_transform('polynomial', SRC, DST, order=10)
|
||||
assert_almost_equal(tform(SRC), DST, 6)
|
||||
|
||||
# via estimate method
|
||||
tform2 = PolynomialTransform()
|
||||
tform2.estimate(SRC, DST, order=10)
|
||||
assert_almost_equal(tform2.params, tform.params)
|
||||
|
||||
|
||||
def test_polynomial_init():
|
||||
tform = estimate_transform('polynomial', SRC, DST, order=10)
|
||||
# init with transformation parameters
|
||||
tform2 = PolynomialTransform(tform.params)
|
||||
assert_almost_equal(tform2.params, tform.params)
|
||||
|
||||
|
||||
def test_polynomial_default_order():
|
||||
tform = estimate_transform('polynomial', SRC, DST)
|
||||
tform2 = estimate_transform('polynomial', SRC, DST, order=2)
|
||||
assert_almost_equal(tform2.params, tform.params)
|
||||
|
||||
|
||||
def test_polynomial_inverse():
|
||||
with testing.raises(Exception):
|
||||
PolynomialTransform().inverse(0)
|
||||
|
||||
|
||||
def test_union():
|
||||
tform1 = SimilarityTransform(scale=0.1, rotation=0.3)
|
||||
tform2 = SimilarityTransform(scale=0.1, rotation=0.9)
|
||||
tform3 = SimilarityTransform(scale=0.1 ** 2, rotation=0.3 + 0.9)
|
||||
tform = tform1 + tform2
|
||||
assert_almost_equal(tform.params, tform3.params)
|
||||
|
||||
tform1 = AffineTransform(scale=(0.1, 0.1), rotation=0.3)
|
||||
tform2 = SimilarityTransform(scale=0.1, rotation=0.9)
|
||||
tform3 = SimilarityTransform(scale=0.1 ** 2, rotation=0.3 + 0.9)
|
||||
tform = tform1 + tform2
|
||||
assert_almost_equal(tform.params, tform3.params)
|
||||
assert tform.__class__ == ProjectiveTransform
|
||||
|
||||
tform = AffineTransform(scale=(0.1, 0.1), rotation=0.3)
|
||||
assert_almost_equal((tform + tform.inverse).params, np.eye(3))
|
||||
|
||||
tform1 = SimilarityTransform(scale=0.1, rotation=0.3)
|
||||
tform2 = SimilarityTransform(scale=0.1, rotation=0.9)
|
||||
tform3 = SimilarityTransform(scale=0.1 * 1/0.1, rotation=0.3 - 0.9)
|
||||
tform = tform1 + tform2.inverse
|
||||
assert_almost_equal(tform.params, tform3.params)
|
||||
|
||||
|
||||
def test_union_differing_types():
|
||||
tform1 = SimilarityTransform()
|
||||
tform2 = PolynomialTransform()
|
||||
with testing.raises(TypeError):
|
||||
tform1.__add__(tform2)
|
||||
|
||||
|
||||
def test_geometric_tform():
|
||||
tform = GeometricTransform()
|
||||
with testing.raises(NotImplementedError):
|
||||
tform(0)
|
||||
with testing.raises(NotImplementedError):
|
||||
tform.inverse(0)
|
||||
with testing.raises(NotImplementedError):
|
||||
tform.__add__(0)
|
||||
|
||||
# See gh-3926 for discussion details
|
||||
for i in range(20):
|
||||
# Generate random Homography
|
||||
H = np.random.rand(3, 3) * 100
|
||||
H[2, H[2] == 0] += np.finfo(float).eps
|
||||
H /= H[2, 2]
|
||||
|
||||
# Craft some src coords
|
||||
src = np.array([
|
||||
[(H[2, 1] + 1) / -H[2, 0], 1],
|
||||
[1, (H[2, 0] + 1) / -H[2, 1]],
|
||||
[1, 1],
|
||||
])
|
||||
# Prior to gh-3926, under the above circumstances,
|
||||
# destination coordinates could be returned with nan/inf values.
|
||||
tform = ProjectiveTransform(H) # Construct the transform
|
||||
dst = tform(src) # Obtain the dst coords
|
||||
# Ensure dst coords are finite numeric values
|
||||
assert(np.isfinite(dst).all())
|
||||
|
||||
def test_invalid_input():
|
||||
with testing.raises(ValueError):
|
||||
ProjectiveTransform(np.zeros((2, 3)))
|
||||
with testing.raises(ValueError):
|
||||
AffineTransform(np.zeros((2, 3)))
|
||||
with testing.raises(ValueError):
|
||||
SimilarityTransform(np.zeros((2, 3)))
|
||||
with testing.raises(ValueError):
|
||||
EuclideanTransform(np.zeros((2, 3)))
|
||||
with testing.raises(ValueError):
|
||||
AffineTransform(matrix=np.zeros((2, 3)), scale=1)
|
||||
with testing.raises(ValueError):
|
||||
SimilarityTransform(matrix=np.zeros((2, 3)), scale=1)
|
||||
with testing.raises(ValueError):
|
||||
EuclideanTransform(
|
||||
matrix=np.zeros((2, 3)), translation=(0, 0))
|
||||
with testing.raises(ValueError):
|
||||
PolynomialTransform(np.zeros((3, 3)))
|
||||
with testing.raises(ValueError):
|
||||
FundamentalMatrixTransform(matrix=np.zeros((3, 2)))
|
||||
with testing.raises(ValueError):
|
||||
EssentialMatrixTransform(matrix=np.zeros((3, 2)))
|
||||
|
||||
with testing.raises(ValueError):
|
||||
EssentialMatrixTransform(rotation=np.zeros((3, 2)))
|
||||
with testing.raises(ValueError):
|
||||
EssentialMatrixTransform(
|
||||
rotation=np.zeros((3, 3)))
|
||||
with testing.raises(ValueError):
|
||||
EssentialMatrixTransform(
|
||||
rotation=np.eye(3))
|
||||
with testing.raises(ValueError):
|
||||
EssentialMatrixTransform(rotation=np.eye(3),
|
||||
translation=np.zeros((2,)))
|
||||
with testing.raises(ValueError):
|
||||
EssentialMatrixTransform(rotation=np.eye(3),
|
||||
translation=np.zeros((2,)))
|
||||
with testing.raises(ValueError):
|
||||
EssentialMatrixTransform(
|
||||
rotation=np.eye(3), translation=np.zeros((3,)))
|
||||
|
||||
|
||||
def test_degenerate():
|
||||
src = dst = np.zeros((10, 2))
|
||||
|
||||
tform = SimilarityTransform()
|
||||
tform.estimate(src, dst)
|
||||
assert np.all(np.isnan(tform.params))
|
||||
|
||||
tform = AffineTransform()
|
||||
tform.estimate(src, dst)
|
||||
assert np.all(np.isnan(tform.params))
|
||||
|
||||
tform = ProjectiveTransform()
|
||||
tform.estimate(src, dst)
|
||||
assert np.all(np.isnan(tform.params))
|
||||
|
||||
# See gh-3926 for discussion details
|
||||
tform = ProjectiveTransform()
|
||||
for i in range(20):
|
||||
# Some random coordinates
|
||||
src = np.random.rand(4, 2) * 100
|
||||
dst = np.random.rand(4, 2) * 100
|
||||
|
||||
# Degenerate the case by arranging points on a single line
|
||||
src[:, 1] = np.random.rand()
|
||||
# Prior to gh-3926, under the above circumstances,
|
||||
# a transform could be returned with nan values.
|
||||
assert(not tform.estimate(src, dst) or np.isfinite(tform.params).all())
|
||||
|
||||
|
||||
def test_projective_repr():
|
||||
tform = ProjectiveTransform()
|
||||
want = re.escape(textwrap.dedent(
|
||||
'''
|
||||
<ProjectiveTransform(matrix=
|
||||
[[1., 0., 0.],
|
||||
[0., 1., 0.],
|
||||
[0., 0., 1.]]) at
|
||||
''').strip()) + ' 0x[a-f0-9]+' + re.escape('>')
|
||||
# Hack the escaped regex to allow whitespace before each number for
|
||||
# compatibility with different numpy versions.
|
||||
want = want.replace('0\\.', ' *0\\.')
|
||||
want = want.replace('1\\.', ' *1\\.')
|
||||
assert re.match(want, repr(tform))
|
||||
|
||||
|
||||
def test_projective_str():
|
||||
tform = ProjectiveTransform()
|
||||
want = re.escape(textwrap.dedent(
|
||||
'''
|
||||
<ProjectiveTransform(matrix=
|
||||
[[1., 0., 0.],
|
||||
[0., 1., 0.],
|
||||
[0., 0., 1.]])>
|
||||
''').strip())
|
||||
# Hack the escaped regex to allow whitespace before each number for
|
||||
# compatibility with different numpy versions.
|
||||
want = want.replace('0\\.', ' *0\\.')
|
||||
want = want.replace('1\\.', ' *1\\.')
|
||||
print(want)
|
||||
assert re.match(want, str(tform))
|
|
@ -0,0 +1,549 @@
|
|||
import numpy as np
|
||||
|
||||
from skimage import transform
|
||||
from skimage import data
|
||||
from skimage.feature import canny
|
||||
from skimage.draw import line, circle_perimeter, ellipse_perimeter
|
||||
|
||||
from skimage._shared import testing
|
||||
from skimage._shared.testing import (assert_almost_equal, assert_equal,
|
||||
test_parallel)
|
||||
|
||||
|
||||
@test_parallel()
|
||||
def test_hough_line():
|
||||
# Generate a test image
|
||||
img = np.zeros((100, 150), dtype=int)
|
||||
rr, cc = line(60, 130, 80, 10)
|
||||
img[rr, cc] = 1
|
||||
|
||||
out, angles, d = transform.hough_line(img)
|
||||
|
||||
y, x = np.where(out == out.max())
|
||||
dist = d[y[0]]
|
||||
theta = angles[x[0]]
|
||||
|
||||
assert_almost_equal(dist, 80.723, 1)
|
||||
assert_almost_equal(theta, 1.41, 1)
|
||||
|
||||
|
||||
def test_hough_line_angles():
|
||||
img = np.zeros((10, 10))
|
||||
img[0, 0] = 1
|
||||
|
||||
out, angles, d = transform.hough_line(img, np.linspace(0, 360, 10))
|
||||
|
||||
assert_equal(len(angles), 10)
|
||||
|
||||
|
||||
def test_hough_line_bad_input():
|
||||
img = np.zeros(100)
|
||||
img[10] = 1
|
||||
|
||||
# Expected error, img must be 2D
|
||||
with testing.raises(ValueError):
|
||||
transform.hough_line(img, np.linspace(0, 360, 10))
|
||||
|
||||
|
||||
def test_probabilistic_hough():
|
||||
# Generate a test image
|
||||
img = np.zeros((100, 100), dtype=int)
|
||||
for i in range(25, 75):
|
||||
img[100 - i, i] = 100
|
||||
img[i, i] = 100
|
||||
|
||||
# decrease default theta sampling because similar orientations may confuse
|
||||
# as mentioned in article of Galambos et al
|
||||
theta = np.linspace(0, np.pi, 45)
|
||||
lines = transform.probabilistic_hough_line(
|
||||
img, threshold=10, line_length=10, line_gap=1, theta=theta)
|
||||
# sort the lines according to the x-axis
|
||||
sorted_lines = []
|
||||
for line in lines:
|
||||
line = list(line)
|
||||
line.sort(key=lambda x: x[0])
|
||||
sorted_lines.append(line)
|
||||
|
||||
assert([(25, 75), (74, 26)] in sorted_lines)
|
||||
assert([(25, 25), (74, 74)] in sorted_lines)
|
||||
|
||||
# Execute with default theta
|
||||
transform.probabilistic_hough_line(img, line_length=10, line_gap=3)
|
||||
|
||||
|
||||
def test_probabilistic_hough_seed():
|
||||
# Load image that is likely to give a randomly varying number of lines
|
||||
image = data.checkerboard()
|
||||
|
||||
# Use constant seed to ensure a deterministic output
|
||||
lines = transform.probabilistic_hough_line(image, threshold=50,
|
||||
line_length=50, line_gap=1,
|
||||
seed=1234)
|
||||
assert len(lines) == 65
|
||||
|
||||
|
||||
def test_probabilistic_hough_bad_input():
|
||||
img = np.zeros(100)
|
||||
img[10] = 1
|
||||
|
||||
# Expected error, img must be 2D
|
||||
with testing.raises(ValueError):
|
||||
transform.probabilistic_hough_line(img)
|
||||
|
||||
|
||||
def test_hough_line_peaks():
|
||||
img = np.zeros((100, 150), dtype=int)
|
||||
rr, cc = line(60, 130, 80, 10)
|
||||
img[rr, cc] = 1
|
||||
|
||||
out, angles, d = transform.hough_line(img)
|
||||
|
||||
out, theta, dist = transform.hough_line_peaks(out, angles, d)
|
||||
|
||||
assert_equal(len(dist), 1)
|
||||
assert_almost_equal(dist[0], 80.723, 1)
|
||||
assert_almost_equal(theta[0], 1.41, 1)
|
||||
|
||||
|
||||
def test_hough_line_peaks_ordered():
|
||||
# Regression test per PR #1421
|
||||
testim = np.zeros((256, 64), dtype=np.bool)
|
||||
|
||||
testim[50:100, 20] = True
|
||||
testim[85:200, 25] = True
|
||||
testim[15:35, 50] = True
|
||||
testim[1:-1, 58] = True
|
||||
|
||||
hough_space, angles, dists = transform.hough_line(testim)
|
||||
|
||||
hspace, _, _ = transform.hough_line_peaks(hough_space, angles, dists)
|
||||
assert hspace[0] > hspace[1]
|
||||
|
||||
|
||||
def test_hough_line_peaks_dist():
|
||||
img = np.zeros((100, 100), dtype=np.bool_)
|
||||
img[:, 30] = True
|
||||
img[:, 40] = True
|
||||
hspace, angles, dists = transform.hough_line(img)
|
||||
assert len(transform.hough_line_peaks(hspace, angles, dists,
|
||||
min_distance=5)[0]) == 2
|
||||
assert len(transform.hough_line_peaks(hspace, angles, dists,
|
||||
min_distance=15)[0]) == 1
|
||||
|
||||
|
||||
def test_hough_line_peaks_angle():
|
||||
check_hough_line_peaks_angle()
|
||||
|
||||
|
||||
def check_hough_line_peaks_angle():
|
||||
img = np.zeros((100, 100), dtype=np.bool_)
|
||||
img[:, 0] = True
|
||||
img[0, :] = True
|
||||
|
||||
hspace, angles, dists = transform.hough_line(img)
|
||||
assert len(transform.hough_line_peaks(hspace, angles, dists,
|
||||
min_angle=45)[0]) == 2
|
||||
assert len(transform.hough_line_peaks(hspace, angles, dists,
|
||||
min_angle=90)[0]) == 1
|
||||
|
||||
theta = np.linspace(0, np.pi, 100)
|
||||
hspace, angles, dists = transform.hough_line(img, theta)
|
||||
assert len(transform.hough_line_peaks(hspace, angles, dists,
|
||||
min_angle=45)[0]) == 2
|
||||
assert len(transform.hough_line_peaks(hspace, angles, dists,
|
||||
min_angle=90)[0]) == 1
|
||||
|
||||
theta = np.linspace(np.pi / 3, 4. / 3 * np.pi, 100)
|
||||
hspace, angles, dists = transform.hough_line(img, theta)
|
||||
assert len(transform.hough_line_peaks(hspace, angles, dists,
|
||||
min_angle=45)[0]) == 2
|
||||
assert len(transform.hough_line_peaks(hspace, angles, dists,
|
||||
min_angle=90)[0]) == 1
|
||||
|
||||
|
||||
def test_hough_line_peaks_num():
|
||||
img = np.zeros((100, 100), dtype=np.bool_)
|
||||
img[:, 30] = True
|
||||
img[:, 40] = True
|
||||
hspace, angles, dists = transform.hough_line(img)
|
||||
assert len(transform.hough_line_peaks(hspace, angles, dists,
|
||||
min_distance=0, min_angle=0,
|
||||
num_peaks=1)[0]) == 1
|
||||
|
||||
|
||||
def test_hough_line_peaks_zero_input():
|
||||
# Test to make sure empty input doesn't cause a failure
|
||||
img = np.zeros((100, 100), dtype='uint8')
|
||||
theta = np.linspace(0, np.pi, 100)
|
||||
hspace, angles, dists = transform.hough_line(img, theta)
|
||||
h, a, d = transform.hough_line_peaks(hspace, angles, dists)
|
||||
assert_equal(a, np.array([]))
|
||||
|
||||
|
||||
@test_parallel()
|
||||
def test_hough_circle():
|
||||
# Prepare picture
|
||||
img = np.zeros((120, 100), dtype=int)
|
||||
radius = 20
|
||||
x_0, y_0 = (99, 50)
|
||||
y, x = circle_perimeter(y_0, x_0, radius)
|
||||
img[x, y] = 1
|
||||
|
||||
out1 = transform.hough_circle(img, radius)
|
||||
out2 = transform.hough_circle(img, [radius])
|
||||
assert_equal(out1, out2)
|
||||
out = transform.hough_circle(img, np.array([radius], dtype=np.intp))
|
||||
assert_equal(out, out1)
|
||||
x, y = np.where(out[0] == out[0].max())
|
||||
assert_equal(x[0], x_0)
|
||||
assert_equal(y[0], y_0)
|
||||
|
||||
|
||||
def test_hough_circle_extended():
|
||||
# Prepare picture
|
||||
# The circle center is outside the image
|
||||
img = np.zeros((100, 100), dtype=int)
|
||||
radius = 20
|
||||
x_0, y_0 = (-5, 50)
|
||||
y, x = circle_perimeter(y_0, x_0, radius)
|
||||
img[x[np.where(x > 0)], y[np.where(x > 0)]] = 1
|
||||
|
||||
out = transform.hough_circle(img, np.array([radius], dtype=np.intp),
|
||||
full_output=True)
|
||||
|
||||
x, y = np.where(out[0] == out[0].max())
|
||||
# Offset for x_0, y_0
|
||||
assert_equal(x[0], x_0 + radius)
|
||||
assert_equal(y[0], y_0 + radius)
|
||||
|
||||
|
||||
def test_hough_circle_peaks():
|
||||
x_0, y_0, rad_0 = (99, 50, 20)
|
||||
img = np.zeros((120, 100), dtype=int)
|
||||
y, x = circle_perimeter(y_0, x_0, rad_0)
|
||||
img[x, y] = 1
|
||||
|
||||
x_1, y_1, rad_1 = (49, 60, 30)
|
||||
y, x = circle_perimeter(y_1, x_1, rad_1)
|
||||
img[x, y] = 1
|
||||
|
||||
radii = [rad_0, rad_1]
|
||||
hspaces = transform.hough_circle(img, radii)
|
||||
out = transform.hough_circle_peaks(hspaces, radii, min_xdistance=1,
|
||||
min_ydistance=1, threshold=None,
|
||||
num_peaks=np.inf,
|
||||
total_num_peaks=np.inf)
|
||||
s = np.argsort(out[3]) # sort by radii
|
||||
assert_equal(out[1][s], np.array([y_0, y_1]))
|
||||
assert_equal(out[2][s], np.array([x_0, x_1]))
|
||||
assert_equal(out[3][s], np.array([rad_0, rad_1]))
|
||||
|
||||
|
||||
def test_hough_circle_peaks_total_peak():
|
||||
img = np.zeros((120, 100), dtype=int)
|
||||
|
||||
x_0, y_0, rad_0 = (99, 50, 20)
|
||||
y, x = circle_perimeter(y_0, x_0, rad_0)
|
||||
img[x, y] = 1
|
||||
|
||||
x_1, y_1, rad_1 = (49, 60, 30)
|
||||
y, x = circle_perimeter(y_1, x_1, rad_1)
|
||||
img[x, y] = 1
|
||||
|
||||
radii = [rad_0, rad_1]
|
||||
hspaces = transform.hough_circle(img, radii)
|
||||
out = transform.hough_circle_peaks(hspaces, radii, min_xdistance=1,
|
||||
min_ydistance=1, threshold=None,
|
||||
num_peaks=np.inf, total_num_peaks=1)
|
||||
assert_equal(out[1][0], np.array([y_1, ]))
|
||||
assert_equal(out[2][0], np.array([x_1, ]))
|
||||
assert_equal(out[3][0], np.array([rad_1, ]))
|
||||
|
||||
|
||||
def test_hough_circle_peaks_min_distance():
|
||||
x_0, y_0, rad_0 = (50, 50, 20)
|
||||
img = np.zeros((120, 100), dtype=int)
|
||||
y, x = circle_perimeter(y_0, x_0, rad_0)
|
||||
img[x, y] = 1
|
||||
|
||||
x_1, y_1, rad_1 = (60, 60, 30)
|
||||
y, x = circle_perimeter(y_1, x_1, rad_1)
|
||||
# Add noise and create an imperfect circle to lower the peak in Hough space
|
||||
y[::2] += 1
|
||||
x[::2] += 1
|
||||
img[x, y] = 1
|
||||
|
||||
x_2, y_2, rad_2 = (70, 70, 20)
|
||||
y, x = circle_perimeter(y_2, x_2, rad_2)
|
||||
# Add noise and create an imperfect circle to lower the peak in Hough space
|
||||
y[::2] += 1
|
||||
x[::2] += 1
|
||||
img[x, y] = 1
|
||||
|
||||
radii = [rad_0, rad_1, rad_2]
|
||||
hspaces = transform.hough_circle(img, radii)
|
||||
out = transform.hough_circle_peaks(hspaces, radii, min_xdistance=15,
|
||||
min_ydistance=15, threshold=None,
|
||||
num_peaks=np.inf,
|
||||
total_num_peaks=np.inf,
|
||||
normalize=True)
|
||||
|
||||
# The second circle is too close to the first one
|
||||
# and has a weaker peak in Hough space due to imperfectness.
|
||||
# Therefore it got removed.
|
||||
assert_equal(out[1], np.array([y_0, y_2]))
|
||||
assert_equal(out[2], np.array([x_0, x_2]))
|
||||
assert_equal(out[3], np.array([rad_0, rad_2]))
|
||||
|
||||
|
||||
def test_hough_circle_peaks_total_peak_and_min_distance():
|
||||
img = np.zeros((120, 120), dtype=int)
|
||||
cx = cy = [40, 50, 60, 70, 80]
|
||||
radii = range(20, 30, 2)
|
||||
for i in range(len(cx)):
|
||||
y, x = circle_perimeter(cy[i], cx[i], radii[i])
|
||||
img[x, y] = 1
|
||||
|
||||
hspaces = transform.hough_circle(img, radii)
|
||||
out = transform.hough_circle_peaks(hspaces, radii, min_xdistance=15,
|
||||
min_ydistance=15, threshold=None,
|
||||
num_peaks=np.inf,
|
||||
total_num_peaks=2,
|
||||
normalize=True)
|
||||
|
||||
# 2nd (4th) circle is removed as it is close to 1st (3rd) oneself.
|
||||
# 5th is removed as total_num_peaks = 2
|
||||
assert_equal(out[1], np.array(cy[:4:2]))
|
||||
assert_equal(out[2], np.array(cx[:4:2]))
|
||||
assert_equal(out[3], np.array(radii[:4:2]))
|
||||
|
||||
|
||||
def test_hough_circle_peaks_normalize():
|
||||
x_0, y_0, rad_0 = (50, 50, 20)
|
||||
img = np.zeros((120, 100), dtype=int)
|
||||
y, x = circle_perimeter(y_0, x_0, rad_0)
|
||||
img[x, y] = 1
|
||||
|
||||
x_1, y_1, rad_1 = (60, 60, 30)
|
||||
y, x = circle_perimeter(y_1, x_1, rad_1)
|
||||
img[x, y] = 1
|
||||
|
||||
radii = [rad_0, rad_1]
|
||||
hspaces = transform.hough_circle(img, radii)
|
||||
out = transform.hough_circle_peaks(hspaces, radii, min_xdistance=15,
|
||||
min_ydistance=15, threshold=None,
|
||||
num_peaks=np.inf,
|
||||
total_num_peaks=np.inf,
|
||||
normalize=False)
|
||||
|
||||
# Two perfect circles are close but the second one is bigger.
|
||||
# Therefore, it is picked due to its high peak.
|
||||
assert_equal(out[1], np.array([y_1]))
|
||||
assert_equal(out[2], np.array([x_1]))
|
||||
assert_equal(out[3], np.array([rad_1]))
|
||||
|
||||
|
||||
def test_hough_ellipse_zero_angle():
|
||||
img = np.zeros((25, 25), dtype=int)
|
||||
rx = 6
|
||||
ry = 8
|
||||
x0 = 12
|
||||
y0 = 15
|
||||
angle = 0
|
||||
rr, cc = ellipse_perimeter(y0, x0, ry, rx)
|
||||
img[rr, cc] = 1
|
||||
result = transform.hough_ellipse(img, threshold=9)
|
||||
best = result[-1]
|
||||
assert_equal(best[1], y0)
|
||||
assert_equal(best[2], x0)
|
||||
assert_almost_equal(best[3], ry, decimal=1)
|
||||
assert_almost_equal(best[4], rx, decimal=1)
|
||||
assert_equal(best[5], angle)
|
||||
# Check if I re-draw the ellipse, points are the same!
|
||||
# ie check API compatibility between hough_ellipse and ellipse_perimeter
|
||||
rr2, cc2 = ellipse_perimeter(y0, x0, int(best[3]), int(best[4]),
|
||||
orientation=best[5])
|
||||
assert_equal(rr, rr2)
|
||||
assert_equal(cc, cc2)
|
||||
|
||||
|
||||
def test_hough_ellipse_non_zero_posangle1():
|
||||
# ry > rx, angle in [0:pi/2]
|
||||
img = np.zeros((30, 24), dtype=int)
|
||||
rx = 6
|
||||
ry = 12
|
||||
x0 = 10
|
||||
y0 = 15
|
||||
angle = np.pi / 1.35
|
||||
rr, cc = ellipse_perimeter(y0, x0, ry, rx, orientation=angle)
|
||||
img[rr, cc] = 1
|
||||
result = transform.hough_ellipse(img, threshold=15, accuracy=3)
|
||||
result.sort(order='accumulator')
|
||||
best = result[-1]
|
||||
assert_almost_equal(best[1] / 100., y0 / 100., decimal=1)
|
||||
assert_almost_equal(best[2] / 100., x0 / 100., decimal=1)
|
||||
assert_almost_equal(best[3] / 10., ry / 10., decimal=1)
|
||||
assert_almost_equal(best[4] / 100., rx / 100., decimal=1)
|
||||
assert_almost_equal(best[5], angle, decimal=1)
|
||||
# Check if I re-draw the ellipse, points are the same!
|
||||
# ie check API compatibility between hough_ellipse and ellipse_perimeter
|
||||
rr2, cc2 = ellipse_perimeter(y0, x0, int(best[3]), int(best[4]),
|
||||
orientation=best[5])
|
||||
assert_equal(rr, rr2)
|
||||
assert_equal(cc, cc2)
|
||||
|
||||
|
||||
def test_hough_ellipse_non_zero_posangle2():
|
||||
# ry < rx, angle in [0:pi/2]
|
||||
img = np.zeros((30, 24), dtype=int)
|
||||
rx = 12
|
||||
ry = 6
|
||||
x0 = 10
|
||||
y0 = 15
|
||||
angle = np.pi / 1.35
|
||||
rr, cc = ellipse_perimeter(y0, x0, ry, rx, orientation=angle)
|
||||
img[rr, cc] = 1
|
||||
result = transform.hough_ellipse(img, threshold=15, accuracy=3)
|
||||
result.sort(order='accumulator')
|
||||
best = result[-1]
|
||||
assert_almost_equal(best[1] / 100., y0 / 100., decimal=1)
|
||||
assert_almost_equal(best[2] / 100., x0 / 100., decimal=1)
|
||||
assert_almost_equal(best[3] / 10., ry / 10., decimal=1)
|
||||
assert_almost_equal(best[4] / 100., rx / 100., decimal=1)
|
||||
assert_almost_equal(best[5], angle, decimal=1)
|
||||
# Check if I re-draw the ellipse, points are the same!
|
||||
# ie check API compatibility between hough_ellipse and ellipse_perimeter
|
||||
rr2, cc2 = ellipse_perimeter(y0, x0, int(best[3]), int(best[4]),
|
||||
orientation=best[5])
|
||||
assert_equal(rr, rr2)
|
||||
assert_equal(cc, cc2)
|
||||
|
||||
|
||||
def test_hough_ellipse_non_zero_posangle3():
|
||||
# ry < rx, angle in [pi/2:pi]
|
||||
img = np.zeros((30, 24), dtype=int)
|
||||
rx = 12
|
||||
ry = 6
|
||||
x0 = 10
|
||||
y0 = 15
|
||||
angle = np.pi / 1.35 + np.pi / 2.
|
||||
rr, cc = ellipse_perimeter(y0, x0, ry, rx, orientation=angle)
|
||||
img[rr, cc] = 1
|
||||
result = transform.hough_ellipse(img, threshold=15, accuracy=3)
|
||||
result.sort(order='accumulator')
|
||||
best = result[-1]
|
||||
# Check if I re-draw the ellipse, points are the same!
|
||||
# ie check API compatibility between hough_ellipse and ellipse_perimeter
|
||||
rr2, cc2 = ellipse_perimeter(y0, x0, int(best[3]), int(best[4]),
|
||||
orientation=best[5])
|
||||
assert_equal(rr, rr2)
|
||||
assert_equal(cc, cc2)
|
||||
|
||||
|
||||
def test_hough_ellipse_non_zero_posangle4():
|
||||
# ry < rx, angle in [pi:3pi/4]
|
||||
img = np.zeros((30, 24), dtype=int)
|
||||
rx = 12
|
||||
ry = 6
|
||||
x0 = 10
|
||||
y0 = 15
|
||||
angle = np.pi / 1.35 + np.pi
|
||||
rr, cc = ellipse_perimeter(y0, x0, ry, rx, orientation=angle)
|
||||
img[rr, cc] = 1
|
||||
result = transform.hough_ellipse(img, threshold=15, accuracy=3)
|
||||
result.sort(order='accumulator')
|
||||
best = result[-1]
|
||||
# Check if I re-draw the ellipse, points are the same!
|
||||
# ie check API compatibility between hough_ellipse and ellipse_perimeter
|
||||
rr2, cc2 = ellipse_perimeter(y0, x0, int(best[3]), int(best[4]),
|
||||
orientation=best[5])
|
||||
assert_equal(rr, rr2)
|
||||
assert_equal(cc, cc2)
|
||||
|
||||
|
||||
def test_hough_ellipse_non_zero_negangle1():
|
||||
# ry > rx, angle in [0:-pi/2]
|
||||
img = np.zeros((30, 24), dtype=int)
|
||||
rx = 6
|
||||
ry = 12
|
||||
x0 = 10
|
||||
y0 = 15
|
||||
angle = - np.pi / 1.35
|
||||
rr, cc = ellipse_perimeter(y0, x0, ry, rx, orientation=angle)
|
||||
img[rr, cc] = 1
|
||||
result = transform.hough_ellipse(img, threshold=15, accuracy=3)
|
||||
result.sort(order='accumulator')
|
||||
best = result[-1]
|
||||
# Check if I re-draw the ellipse, points are the same!
|
||||
# ie check API compatibility between hough_ellipse and ellipse_perimeter
|
||||
rr2, cc2 = ellipse_perimeter(y0, x0, int(best[3]), int(best[4]),
|
||||
orientation=best[5])
|
||||
assert_equal(rr, rr2)
|
||||
assert_equal(cc, cc2)
|
||||
|
||||
|
||||
def test_hough_ellipse_non_zero_negangle2():
|
||||
# ry < rx, angle in [0:-pi/2]
|
||||
img = np.zeros((30, 24), dtype=int)
|
||||
rx = 12
|
||||
ry = 6
|
||||
x0 = 10
|
||||
y0 = 15
|
||||
angle = - np.pi / 1.35
|
||||
rr, cc = ellipse_perimeter(y0, x0, ry, rx, orientation=angle)
|
||||
img[rr, cc] = 1
|
||||
result = transform.hough_ellipse(img, threshold=15, accuracy=3)
|
||||
result.sort(order='accumulator')
|
||||
best = result[-1]
|
||||
# Check if I re-draw the ellipse, points are the same!
|
||||
# ie check API compatibility between hough_ellipse and ellipse_perimeter
|
||||
rr2, cc2 = ellipse_perimeter(y0, x0, int(best[3]), int(best[4]),
|
||||
orientation=best[5])
|
||||
assert_equal(rr, rr2)
|
||||
assert_equal(cc, cc2)
|
||||
|
||||
|
||||
def test_hough_ellipse_non_zero_negangle3():
|
||||
# ry < rx, angle in [-pi/2:-pi]
|
||||
img = np.zeros((30, 24), dtype=int)
|
||||
rx = 12
|
||||
ry = 6
|
||||
x0 = 10
|
||||
y0 = 15
|
||||
angle = - np.pi / 1.35 - np.pi / 2.
|
||||
rr, cc = ellipse_perimeter(y0, x0, ry, rx, orientation=angle)
|
||||
img[rr, cc] = 1
|
||||
result = transform.hough_ellipse(img, threshold=15, accuracy=3)
|
||||
result.sort(order='accumulator')
|
||||
best = result[-1]
|
||||
# Check if I re-draw the ellipse, points are the same!
|
||||
# ie check API compatibility between hough_ellipse and ellipse_perimeter
|
||||
rr2, cc2 = ellipse_perimeter(y0, x0, int(best[3]), int(best[4]),
|
||||
orientation=best[5])
|
||||
assert_equal(rr, rr2)
|
||||
assert_equal(cc, cc2)
|
||||
|
||||
|
||||
def test_hough_ellipse_non_zero_negangle4():
|
||||
# ry < rx, angle in [-pi:-3pi/4]
|
||||
img = np.zeros((30, 24), dtype=int)
|
||||
rx = 12
|
||||
ry = 6
|
||||
x0 = 10
|
||||
y0 = 15
|
||||
angle = - np.pi / 1.35 - np.pi
|
||||
rr, cc = ellipse_perimeter(y0, x0, ry, rx, orientation=angle)
|
||||
img[rr, cc] = 1
|
||||
result = transform.hough_ellipse(img, threshold=15, accuracy=3)
|
||||
result.sort(order='accumulator')
|
||||
best = result[-1]
|
||||
# Check if I re-draw the ellipse, points are the same!
|
||||
# ie check API compatibility between hough_ellipse and ellipse_perimeter
|
||||
rr2, cc2 = ellipse_perimeter(y0, x0, int(best[3]), int(best[4]),
|
||||
orientation=best[5])
|
||||
assert_equal(rr, rr2)
|
||||
assert_equal(cc, cc2)
|
||||
|
||||
|
||||
def test_hough_ellipse_all_black_img():
|
||||
assert(transform.hough_ellipse(np.zeros((100, 100))).shape == (0, 6))
|
|
@ -0,0 +1,47 @@
|
|||
import numpy as np
|
||||
from skimage.transform import integral_image, integrate
|
||||
|
||||
from skimage._shared.testing import assert_equal
|
||||
|
||||
|
||||
np.random.seed(0)
|
||||
x = (np.random.rand(50, 50) * 255).astype(np.uint8)
|
||||
s = integral_image(x)
|
||||
|
||||
|
||||
def test_validity():
|
||||
y = np.arange(12).reshape((4, 3))
|
||||
|
||||
y = (np.random.rand(50, 50) * 255).astype(np.uint8)
|
||||
assert_equal(integral_image(y)[-1, -1],
|
||||
y.sum())
|
||||
|
||||
|
||||
def test_basic():
|
||||
assert_equal(x[12:24, 10:20].sum(), integrate(s, (12, 10), (23, 19)))
|
||||
assert_equal(x[:20, :20].sum(), integrate(s, (0, 0), (19, 19)))
|
||||
assert_equal(x[:20, 10:20].sum(), integrate(s, (0, 10), (19, 19)))
|
||||
assert_equal(x[10:20, :20].sum(), integrate(s, (10, 0), (19, 19)))
|
||||
|
||||
|
||||
def test_single():
|
||||
assert_equal(x[0, 0], integrate(s, (0, 0), (0, 0)))
|
||||
assert_equal(x[10, 10], integrate(s, (10, 10), (10, 10)))
|
||||
|
||||
|
||||
def test_vectorized_integrate():
|
||||
r0 = np.array([12, 0, 0, 10, 0, 10, 30])
|
||||
c0 = np.array([10, 0, 10, 0, 0, 10, 31])
|
||||
r1 = np.array([23, 19, 19, 19, 0, 10, 49])
|
||||
c1 = np.array([19, 19, 19, 19, 0, 10, 49])
|
||||
|
||||
expected = np.array([x[12:24, 10:20].sum(),
|
||||
x[:20, :20].sum(),
|
||||
x[:20, 10:20].sum(),
|
||||
x[10:20, :20].sum(),
|
||||
x[0, 0],
|
||||
x[10, 10],
|
||||
x[30:, 31:].sum()])
|
||||
start_pts = [(r0[i], c0[i]) for i in range(len(r0))]
|
||||
end_pts = [(r1[i], c1[i]) for i in range(len(r0))]
|
||||
assert_equal(expected, integrate(s, start_pts, end_pts))
|
147
venv/Lib/site-packages/skimage/transform/tests/test_pyramids.py
Normal file
147
venv/Lib/site-packages/skimage/transform/tests/test_pyramids.py
Normal file
|
@ -0,0 +1,147 @@
|
|||
import math
|
||||
import pytest
|
||||
import numpy as np
|
||||
from skimage import data
|
||||
from skimage.transform import pyramids
|
||||
|
||||
from skimage._shared import testing
|
||||
from skimage._shared.testing import (assert_array_equal, assert_, assert_equal,
|
||||
assert_almost_equal)
|
||||
|
||||
|
||||
image = data.astronaut()
|
||||
image_gray = image[..., 0]
|
||||
|
||||
|
||||
def test_pyramid_reduce_rgb():
|
||||
rows, cols, dim = image.shape
|
||||
out = pyramids.pyramid_reduce(image, downscale=2, multichannel=True)
|
||||
assert_array_equal(out.shape, (rows / 2, cols / 2, dim))
|
||||
|
||||
|
||||
def test_pyramid_reduce_gray():
|
||||
rows, cols = image_gray.shape
|
||||
out1 = pyramids.pyramid_reduce(image_gray, downscale=2,
|
||||
multichannel=False)
|
||||
assert_array_equal(out1.shape, (rows / 2, cols / 2))
|
||||
assert_almost_equal(out1.ptp(), 1.0, decimal=2)
|
||||
out2 = pyramids.pyramid_reduce(image_gray, downscale=2,
|
||||
multichannel=False, preserve_range=True)
|
||||
assert_almost_equal(out2.ptp() / image_gray.ptp(), 1.0, decimal=2)
|
||||
|
||||
|
||||
def test_pyramid_reduce_nd():
|
||||
for ndim in [1, 2, 3, 4]:
|
||||
img = np.random.randn(*((8, ) * ndim))
|
||||
out = pyramids.pyramid_reduce(img, downscale=2,
|
||||
multichannel=False)
|
||||
expected_shape = np.asarray(img.shape) / 2
|
||||
assert_array_equal(out.shape, expected_shape)
|
||||
|
||||
|
||||
def test_pyramid_expand_rgb():
|
||||
rows, cols, dim = image.shape
|
||||
out = pyramids.pyramid_expand(image, upscale=2,
|
||||
multichannel=True)
|
||||
assert_array_equal(out.shape, (rows * 2, cols * 2, dim))
|
||||
|
||||
|
||||
def test_pyramid_expand_gray():
|
||||
rows, cols = image_gray.shape
|
||||
out = pyramids.pyramid_expand(image_gray, upscale=2,
|
||||
multichannel=False)
|
||||
assert_array_equal(out.shape, (rows * 2, cols * 2))
|
||||
|
||||
|
||||
def test_pyramid_expand_nd():
|
||||
for ndim in [1, 2, 3, 4]:
|
||||
img = np.random.randn(*((4, ) * ndim))
|
||||
out = pyramids.pyramid_expand(img, upscale=2,
|
||||
multichannel=False)
|
||||
expected_shape = np.asarray(img.shape) * 2
|
||||
assert_array_equal(out.shape, expected_shape)
|
||||
|
||||
|
||||
def test_build_gaussian_pyramid_rgb():
|
||||
rows, cols, dim = image.shape
|
||||
pyramid = pyramids.pyramid_gaussian(image, downscale=2,
|
||||
multichannel=True)
|
||||
for layer, out in enumerate(pyramid):
|
||||
layer_shape = (rows / 2 ** layer, cols / 2 ** layer, dim)
|
||||
assert_array_equal(out.shape, layer_shape)
|
||||
|
||||
|
||||
def test_build_gaussian_pyramid_gray():
|
||||
rows, cols = image_gray.shape
|
||||
pyramid = pyramids.pyramid_gaussian(image_gray, downscale=2,
|
||||
multichannel=False)
|
||||
for layer, out in enumerate(pyramid):
|
||||
layer_shape = (rows / 2 ** layer, cols / 2 ** layer)
|
||||
assert_array_equal(out.shape, layer_shape)
|
||||
|
||||
|
||||
def test_build_gaussian_pyramid_nd():
|
||||
for ndim in [1, 2, 3, 4]:
|
||||
img = np.random.randn(*((8, ) * ndim))
|
||||
original_shape = np.asarray(img.shape)
|
||||
pyramid = pyramids.pyramid_gaussian(img, downscale=2,
|
||||
multichannel=False)
|
||||
for layer, out in enumerate(pyramid):
|
||||
layer_shape = original_shape / 2 ** layer
|
||||
assert_array_equal(out.shape, layer_shape)
|
||||
|
||||
|
||||
def test_build_laplacian_pyramid_rgb():
|
||||
rows, cols, dim = image.shape
|
||||
pyramid = pyramids.pyramid_laplacian(image, downscale=2,
|
||||
multichannel=True)
|
||||
for layer, out in enumerate(pyramid):
|
||||
layer_shape = (rows / 2 ** layer, cols / 2 ** layer, dim)
|
||||
assert_array_equal(out.shape, layer_shape)
|
||||
|
||||
|
||||
def test_build_laplacian_pyramid_nd():
|
||||
for ndim in [1, 2, 3, 4]:
|
||||
img = np.random.randn(*(16, )*ndim)
|
||||
original_shape = np.asarray(img.shape)
|
||||
pyramid = pyramids.pyramid_laplacian(img, downscale=2,
|
||||
multichannel=False)
|
||||
for layer, out in enumerate(pyramid):
|
||||
print(out.shape)
|
||||
layer_shape = original_shape / 2 ** layer
|
||||
assert_array_equal(out.shape, layer_shape)
|
||||
|
||||
|
||||
def test_laplacian_pyramid_max_layers():
|
||||
for downscale in [2, 3, 5, 7]:
|
||||
img = np.random.randn(32, 8)
|
||||
pyramid = pyramids.pyramid_laplacian(img, downscale=downscale,
|
||||
multichannel=False)
|
||||
max_layer = int(np.ceil(math.log(np.max(img.shape), downscale)))
|
||||
for layer, out in enumerate(pyramid):
|
||||
if layer < max_layer:
|
||||
# should not reach all axes as size 1 prior to final level
|
||||
assert_(np.max(out.shape) > 1)
|
||||
|
||||
# total number of images is max_layer + 1
|
||||
assert_equal(max_layer, layer)
|
||||
|
||||
# final layer should be size 1 on all axes
|
||||
assert_array_equal((out.shape), (1, 1))
|
||||
|
||||
|
||||
def test_check_factor():
|
||||
with testing.raises(ValueError):
|
||||
pyramids._check_factor(0.99)
|
||||
with testing.raises(ValueError):
|
||||
pyramids._check_factor(- 2)
|
||||
|
||||
|
||||
@pytest.mark.parametrize('dtype, expected',
|
||||
zip(['float32', 'float64', 'uint8', 'int64'],
|
||||
['float32', 'float64', 'float64', 'float64']))
|
||||
def test_pyramid_gaussian_dtype_support(dtype, expected):
|
||||
img = np.random.randn(32, 8).astype(dtype)
|
||||
pyramid = pyramids.pyramid_gaussian(img)
|
||||
|
||||
assert np.all([im.dtype == expected for im in pyramid])
|
|
@ -0,0 +1,473 @@
|
|||
import itertools
|
||||
import pytest
|
||||
|
||||
import numpy as np
|
||||
from skimage.data import shepp_logan_phantom
|
||||
from skimage.transform import radon, iradon, iradon_sart, rescale
|
||||
|
||||
from skimage._shared.utils import convert_to_float
|
||||
from skimage._shared import testing
|
||||
from skimage._shared.testing import test_parallel
|
||||
from skimage._shared._warnings import expected_warnings
|
||||
|
||||
|
||||
PHANTOM = shepp_logan_phantom()[::2, ::2]
|
||||
PHANTOM = rescale(PHANTOM, 0.5, order=1,
|
||||
mode='constant', anti_aliasing=False, multichannel=False)
|
||||
|
||||
|
||||
def _debug_plot(original, result, sinogram=None):
|
||||
from matplotlib import pyplot as plt
|
||||
imkwargs = dict(cmap='gray', interpolation='nearest')
|
||||
if sinogram is None:
|
||||
plt.figure(figsize=(15, 6))
|
||||
sp = 130
|
||||
else:
|
||||
plt.figure(figsize=(11, 11))
|
||||
sp = 221
|
||||
plt.subplot(sp + 0)
|
||||
plt.imshow(sinogram, aspect='auto', **imkwargs)
|
||||
plt.subplot(sp + 1)
|
||||
plt.imshow(original, **imkwargs)
|
||||
plt.subplot(sp + 2)
|
||||
plt.imshow(result, vmin=original.min(), vmax=original.max(), **imkwargs)
|
||||
plt.subplot(sp + 3)
|
||||
plt.imshow(result - original, **imkwargs)
|
||||
plt.colorbar()
|
||||
plt.show()
|
||||
|
||||
|
||||
def _rescale_intensity(x):
|
||||
x = x.astype(float)
|
||||
x -= x.min()
|
||||
x /= x.max()
|
||||
return x
|
||||
|
||||
|
||||
def test_iradon_bias_circular_phantom():
|
||||
"""
|
||||
test that a uniform circular phantom has a small reconstruction bias
|
||||
"""
|
||||
pixels = 128
|
||||
xy = np.arange(-pixels / 2, pixels / 2) + 0.5
|
||||
x, y = np.meshgrid(xy, xy)
|
||||
image = x**2 + y**2 <= (pixels/4)**2
|
||||
|
||||
theta = np.linspace(0., 180., max(image.shape), endpoint=False)
|
||||
sinogram = radon(image, theta=theta)
|
||||
|
||||
reconstruction_fbp = iradon(sinogram, theta=theta)
|
||||
error = reconstruction_fbp - image
|
||||
|
||||
tol = 5e-5
|
||||
roi_err = np.abs(np.mean(error))
|
||||
assert roi_err < tol
|
||||
|
||||
|
||||
def check_radon_center(shape, circle, dtype, preserve_range):
|
||||
# Create a test image with only a single non-zero pixel at the origin
|
||||
image = np.zeros(shape, dtype=dtype)
|
||||
image[(shape[0] // 2, shape[1] // 2)] = 1.
|
||||
# Calculate the sinogram
|
||||
theta = np.linspace(0., 180., max(shape), endpoint=False)
|
||||
sinogram = radon(image, theta=theta, circle=circle,
|
||||
preserve_range=preserve_range)
|
||||
# The sinogram should be a straight, horizontal line
|
||||
sinogram_max = np.argmax(sinogram, axis=0)
|
||||
print(sinogram_max)
|
||||
assert np.std(sinogram_max) < 1e-6
|
||||
|
||||
|
||||
@testing.parametrize("shape", [(16, 16), (17, 17)])
|
||||
@testing.parametrize("circle", [False, True])
|
||||
@testing.parametrize("dtype", [np.float64, np.float32, np.uint8, bool])
|
||||
@testing.parametrize("preserve_range", [False, True])
|
||||
def test_radon_center(shape, circle, dtype, preserve_range):
|
||||
check_radon_center(shape, circle, dtype, preserve_range)
|
||||
|
||||
|
||||
@testing.parametrize("shape", [(32, 16), (33, 17)])
|
||||
@testing.parametrize("circle", [False])
|
||||
@testing.parametrize("dtype", [np.float64, np.float32, np.uint8, bool])
|
||||
@testing.parametrize("preserve_range", [False, True])
|
||||
def test_radon_center_rectangular(shape, circle, dtype, preserve_range):
|
||||
check_radon_center(shape, circle, dtype, preserve_range)
|
||||
|
||||
|
||||
def check_iradon_center(size, theta, circle):
|
||||
debug = False
|
||||
# Create a test sinogram corresponding to a single projection
|
||||
# with a single non-zero pixel at the rotation center
|
||||
if circle:
|
||||
sinogram = np.zeros((size, 1), dtype=np.float)
|
||||
sinogram[size // 2, 0] = 1.
|
||||
else:
|
||||
diagonal = int(np.ceil(np.sqrt(2) * size))
|
||||
sinogram = np.zeros((diagonal, 1), dtype=np.float)
|
||||
sinogram[sinogram.shape[0] // 2, 0] = 1.
|
||||
maxpoint = np.unravel_index(np.argmax(sinogram), sinogram.shape)
|
||||
print('shape of generated sinogram', sinogram.shape)
|
||||
print('maximum in generated sinogram', maxpoint)
|
||||
# Compare reconstructions for theta=angle and theta=angle + 180;
|
||||
# these should be exactly equal
|
||||
reconstruction = iradon(sinogram, theta=[theta], circle=circle)
|
||||
reconstruction_opposite = iradon(sinogram, theta=[theta + 180],
|
||||
circle=circle)
|
||||
print('rms deviance:',
|
||||
np.sqrt(np.mean((reconstruction_opposite - reconstruction)**2)))
|
||||
if debug:
|
||||
import matplotlib.pyplot as plt
|
||||
imkwargs = dict(cmap='gray', interpolation='nearest')
|
||||
plt.figure()
|
||||
plt.subplot(221)
|
||||
plt.imshow(sinogram, **imkwargs)
|
||||
plt.subplot(222)
|
||||
plt.imshow(reconstruction_opposite - reconstruction, **imkwargs)
|
||||
plt.subplot(223)
|
||||
plt.imshow(reconstruction, **imkwargs)
|
||||
plt.subplot(224)
|
||||
plt.imshow(reconstruction_opposite, **imkwargs)
|
||||
plt.show()
|
||||
|
||||
assert np.allclose(reconstruction, reconstruction_opposite)
|
||||
|
||||
|
||||
sizes_for_test_iradon_center = [16, 17]
|
||||
thetas_for_test_iradon_center = [0, 90]
|
||||
circles_for_test_iradon_center = [False, True]
|
||||
|
||||
|
||||
@testing.parametrize("size, theta, circle",
|
||||
itertools.product(sizes_for_test_iradon_center,
|
||||
thetas_for_test_iradon_center,
|
||||
circles_for_test_iradon_center))
|
||||
def test_iradon_center(size, theta, circle):
|
||||
check_iradon_center(size, theta, circle)
|
||||
|
||||
|
||||
def check_radon_iradon(interpolation_type, filter_type):
|
||||
debug = False
|
||||
image = PHANTOM
|
||||
reconstructed = iradon(radon(image, circle=False), filter_name=filter_type,
|
||||
interpolation=interpolation_type, circle=False)
|
||||
delta = np.mean(np.abs(image - reconstructed))
|
||||
print('\n\tmean error:', delta)
|
||||
if debug:
|
||||
_debug_plot(image, reconstructed)
|
||||
if filter_type in ('ramp', 'shepp-logan'):
|
||||
if interpolation_type == 'nearest':
|
||||
allowed_delta = 0.03
|
||||
else:
|
||||
allowed_delta = 0.025
|
||||
else:
|
||||
allowed_delta = 0.05
|
||||
assert delta < allowed_delta
|
||||
|
||||
|
||||
filter_types = ["ramp", "shepp-logan", "cosine", "hamming", "hann"]
|
||||
interpolation_types = ['linear', 'nearest']
|
||||
radon_iradon_inputs = list(itertools.product(interpolation_types,
|
||||
filter_types))
|
||||
# cubic interpolation is slow; only run one test for it
|
||||
radon_iradon_inputs.append(('cubic', 'shepp-logan'))
|
||||
|
||||
|
||||
@testing.parametrize("interpolation_type, filter_type",
|
||||
radon_iradon_inputs)
|
||||
def test_radon_iradon(interpolation_type, filter_type):
|
||||
check_radon_iradon(interpolation_type, filter_type)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("filter_type", filter_types)
|
||||
def test_iradon_new_signature(filter_type):
|
||||
image = PHANTOM
|
||||
sinogram = radon(image, circle=False)
|
||||
with pytest.warns(FutureWarning):
|
||||
assert np.array_equal(iradon(sinogram, filter=filter_type),
|
||||
iradon(sinogram, filter_name=filter_type))
|
||||
|
||||
|
||||
def test_iradon_angles():
|
||||
"""
|
||||
Test with different number of projections
|
||||
"""
|
||||
size = 100
|
||||
# Synthetic data
|
||||
image = np.tri(size) + np.tri(size)[::-1]
|
||||
# Large number of projections: a good quality is expected
|
||||
nb_angles = 200
|
||||
theta = np.linspace(0, 180, nb_angles, endpoint=False)
|
||||
radon_image_200 = radon(image, theta=theta, circle=False)
|
||||
reconstructed = iradon(radon_image_200, circle=False)
|
||||
delta_200 = np.mean(abs(_rescale_intensity(image) -
|
||||
_rescale_intensity(reconstructed)))
|
||||
assert delta_200 < 0.03
|
||||
# Lower number of projections
|
||||
nb_angles = 80
|
||||
radon_image_80 = radon(image, theta=theta, circle=False)
|
||||
# Test whether the sum of all projections is approximately the same
|
||||
s = radon_image_80.sum(axis=0)
|
||||
assert np.allclose(s, s[0], rtol=0.01)
|
||||
reconstructed = iradon(radon_image_80, circle=False)
|
||||
delta_80 = np.mean(abs(image / np.max(image) -
|
||||
reconstructed / np.max(reconstructed)))
|
||||
# Loss of quality when the number of projections is reduced
|
||||
assert delta_80 > delta_200
|
||||
|
||||
|
||||
def check_radon_iradon_minimal(shape, slices):
|
||||
debug = False
|
||||
theta = np.arange(180)
|
||||
image = np.zeros(shape, dtype=np.float)
|
||||
image[slices] = 1.
|
||||
sinogram = radon(image, theta, circle=False)
|
||||
reconstructed = iradon(sinogram, theta, circle=False)
|
||||
print('\n\tMaximum deviation:', np.max(np.abs(image - reconstructed)))
|
||||
if debug:
|
||||
_debug_plot(image, reconstructed, sinogram)
|
||||
if image.sum() == 1:
|
||||
assert (np.unravel_index(np.argmax(reconstructed), image.shape)
|
||||
== np.unravel_index(np.argmax(image), image.shape))
|
||||
|
||||
|
||||
shapes = [(3, 3), (4, 4), (5, 5)]
|
||||
|
||||
|
||||
def generate_test_data_for_radon_iradon_minimal(shapes):
|
||||
def shape2coordinates(shape):
|
||||
c0, c1 = shape[0] // 2, shape[1] // 2
|
||||
coordinates = itertools.product((c0 - 1, c0, c0 + 1),
|
||||
(c1 - 1, c1, c1 + 1))
|
||||
return coordinates
|
||||
|
||||
def shape2shapeandcoordinates(shape):
|
||||
return itertools.product([shape], shape2coordinates(shape))
|
||||
|
||||
return itertools.chain.from_iterable([shape2shapeandcoordinates(shape)
|
||||
for shape in shapes])
|
||||
|
||||
|
||||
@testing.parametrize("shape, coordinate",
|
||||
generate_test_data_for_radon_iradon_minimal(shapes))
|
||||
def test_radon_iradon_minimal(shape, coordinate):
|
||||
check_radon_iradon_minimal(shape, coordinate)
|
||||
|
||||
|
||||
def test_reconstruct_with_wrong_angles():
|
||||
a = np.zeros((3, 3))
|
||||
p = radon(a, theta=[0, 1, 2], circle=False)
|
||||
iradon(p, theta=[0, 1, 2], circle=False)
|
||||
with testing.raises(ValueError):
|
||||
iradon(p, theta=[0, 1, 2, 3])
|
||||
|
||||
|
||||
def _random_circle(shape):
|
||||
# Synthetic random data, zero outside reconstruction circle
|
||||
np.random.seed(98312871)
|
||||
image = np.random.rand(*shape)
|
||||
c0, c1 = np.ogrid[0:shape[0], 0:shape[1]]
|
||||
r = np.sqrt((c0 - shape[0] // 2)**2 + (c1 - shape[1] // 2)**2)
|
||||
radius = min(shape) // 2
|
||||
image[r > radius] = 0.
|
||||
return image
|
||||
|
||||
|
||||
def test_radon_circle():
|
||||
a = np.ones((10, 10))
|
||||
with expected_warnings(['reconstruction circle']):
|
||||
radon(a, circle=True)
|
||||
|
||||
# Synthetic data, circular symmetry
|
||||
shape = (61, 79)
|
||||
c0, c1 = np.ogrid[0:shape[0], 0:shape[1]]
|
||||
r = np.sqrt((c0 - shape[0] // 2)**2 + (c1 - shape[1] // 2)**2)
|
||||
radius = min(shape) // 2
|
||||
image = np.clip(radius - r, 0, np.inf)
|
||||
image = _rescale_intensity(image)
|
||||
angles = np.linspace(0, 180, min(shape), endpoint=False)
|
||||
sinogram = radon(image, theta=angles, circle=True)
|
||||
assert np.all(sinogram.std(axis=1) < 1e-2)
|
||||
|
||||
# Synthetic data, random
|
||||
image = _random_circle(shape)
|
||||
sinogram = radon(image, theta=angles, circle=True)
|
||||
mass = sinogram.sum(axis=0)
|
||||
average_mass = mass.mean()
|
||||
relative_error = np.abs(mass - average_mass) / average_mass
|
||||
print(relative_error.max(), relative_error.mean())
|
||||
assert np.all(relative_error < 3.2e-3)
|
||||
|
||||
|
||||
def check_sinogram_circle_to_square(size):
|
||||
from skimage.transform.radon_transform import _sinogram_circle_to_square
|
||||
image = _random_circle((size, size))
|
||||
theta = np.linspace(0., 180., size, False)
|
||||
sinogram_circle = radon(image, theta, circle=True)
|
||||
|
||||
def argmax_shape(a):
|
||||
return np.unravel_index(np.argmax(a), a.shape)
|
||||
|
||||
print('\n\targmax of circle:', argmax_shape(sinogram_circle))
|
||||
sinogram_square = radon(image, theta, circle=False)
|
||||
print('\targmax of square:', argmax_shape(sinogram_square))
|
||||
sinogram_circle_to_square = _sinogram_circle_to_square(sinogram_circle)
|
||||
print('\targmax of circle to square:',
|
||||
argmax_shape(sinogram_circle_to_square))
|
||||
error = abs(sinogram_square - sinogram_circle_to_square)
|
||||
print(np.mean(error), np.max(error))
|
||||
assert (argmax_shape(sinogram_square) ==
|
||||
argmax_shape(sinogram_circle_to_square))
|
||||
|
||||
|
||||
@testing.parametrize("size", (50, 51))
|
||||
def test_sinogram_circle_to_square(size):
|
||||
check_sinogram_circle_to_square(size)
|
||||
|
||||
|
||||
def check_radon_iradon_circle(interpolation, shape, output_size):
|
||||
# Forward and inverse radon on synthetic data
|
||||
image = _random_circle(shape)
|
||||
radius = min(shape) // 2
|
||||
sinogram_rectangle = radon(image, circle=False)
|
||||
reconstruction_rectangle = iradon(sinogram_rectangle,
|
||||
output_size=output_size,
|
||||
interpolation=interpolation,
|
||||
circle=False)
|
||||
sinogram_circle = radon(image, circle=True)
|
||||
reconstruction_circle = iradon(sinogram_circle,
|
||||
output_size=output_size,
|
||||
interpolation=interpolation,
|
||||
circle=True)
|
||||
# Crop rectangular reconstruction to match circle=True reconstruction
|
||||
width = reconstruction_circle.shape[0]
|
||||
excess = int(np.ceil((reconstruction_rectangle.shape[0] - width) / 2))
|
||||
s = np.s_[excess:width + excess, excess:width + excess]
|
||||
reconstruction_rectangle = reconstruction_rectangle[s]
|
||||
# Find the reconstruction circle, set reconstruction to zero outside
|
||||
c0, c1 = np.ogrid[0:width, 0:width]
|
||||
r = np.sqrt((c0 - width // 2)**2 + (c1 - width // 2)**2)
|
||||
reconstruction_rectangle[r > radius] = 0.
|
||||
print(reconstruction_circle.shape)
|
||||
print(reconstruction_rectangle.shape)
|
||||
np.allclose(reconstruction_rectangle, reconstruction_circle)
|
||||
|
||||
|
||||
# if adding more shapes to test data, you might want to look at commit d0f2bac3f
|
||||
shapes_radon_iradon_circle = ((61, 79), )
|
||||
interpolations = ('nearest', 'linear')
|
||||
output_sizes = (None,
|
||||
min(shapes_radon_iradon_circle[0]),
|
||||
max(shapes_radon_iradon_circle[0]),
|
||||
97)
|
||||
|
||||
|
||||
@testing.parametrize("shape, interpolation, output_size",
|
||||
itertools.product(shapes_radon_iradon_circle,
|
||||
interpolations, output_sizes))
|
||||
def test_radon_iradon_circle(shape, interpolation, output_size):
|
||||
check_radon_iradon_circle(interpolation, shape, output_size)
|
||||
|
||||
|
||||
def test_order_angles_golden_ratio():
|
||||
from skimage.transform.radon_transform import order_angles_golden_ratio
|
||||
np.random.seed(1231)
|
||||
lengths = [1, 4, 10, 180]
|
||||
for l in lengths:
|
||||
theta_ordered = np.linspace(0, 180, l, endpoint=False)
|
||||
theta_random = np.random.uniform(0, 180, l)
|
||||
for theta in (theta_random, theta_ordered):
|
||||
indices = [x for x in order_angles_golden_ratio(theta)]
|
||||
# no duplicate indices allowed
|
||||
assert len(indices) == len(set(indices))
|
||||
|
||||
|
||||
@test_parallel()
|
||||
def test_iradon_sart():
|
||||
debug = False
|
||||
|
||||
image = rescale(PHANTOM, 0.8, mode='reflect',
|
||||
multichannel=False, anti_aliasing=False)
|
||||
theta_ordered = np.linspace(0., 180., image.shape[0], endpoint=False)
|
||||
theta_missing_wedge = np.linspace(0., 150., image.shape[0], endpoint=True)
|
||||
for theta, error_factor in ((theta_ordered, 1.),
|
||||
(theta_missing_wedge, 2.)):
|
||||
sinogram = radon(image, theta, circle=True)
|
||||
reconstructed = iradon_sart(sinogram, theta)
|
||||
|
||||
if debug:
|
||||
from matplotlib import pyplot as plt
|
||||
plt.figure()
|
||||
plt.subplot(221)
|
||||
plt.imshow(image, interpolation='nearest')
|
||||
plt.subplot(222)
|
||||
plt.imshow(sinogram, interpolation='nearest')
|
||||
plt.subplot(223)
|
||||
plt.imshow(reconstructed, interpolation='nearest')
|
||||
plt.subplot(224)
|
||||
plt.imshow(reconstructed - image, interpolation='nearest')
|
||||
plt.show()
|
||||
|
||||
delta = np.mean(np.abs(reconstructed - image))
|
||||
print('delta (1 iteration) =', delta)
|
||||
assert delta < 0.02 * error_factor
|
||||
reconstructed = iradon_sart(sinogram, theta, reconstructed)
|
||||
delta = np.mean(np.abs(reconstructed - image))
|
||||
print('delta (2 iterations) =', delta)
|
||||
assert delta < 0.014 * error_factor
|
||||
reconstructed = iradon_sart(sinogram, theta, clip=(0, 1))
|
||||
delta = np.mean(np.abs(reconstructed - image))
|
||||
print('delta (1 iteration, clip) =', delta)
|
||||
assert delta < 0.018 * error_factor
|
||||
|
||||
np.random.seed(1239867)
|
||||
shifts = np.random.uniform(-3, 3, sinogram.shape[1])
|
||||
x = np.arange(sinogram.shape[0])
|
||||
sinogram_shifted = np.vstack([np.interp(x + shifts[i], x,
|
||||
sinogram[:, i])
|
||||
for i in range(sinogram.shape[1])]).T
|
||||
reconstructed = iradon_sart(sinogram_shifted, theta,
|
||||
projection_shifts=shifts)
|
||||
if debug:
|
||||
from matplotlib import pyplot as plt
|
||||
plt.figure()
|
||||
plt.subplot(221)
|
||||
plt.imshow(image, interpolation='nearest')
|
||||
plt.subplot(222)
|
||||
plt.imshow(sinogram_shifted, interpolation='nearest')
|
||||
plt.subplot(223)
|
||||
plt.imshow(reconstructed, interpolation='nearest')
|
||||
plt.subplot(224)
|
||||
plt.imshow(reconstructed - image, interpolation='nearest')
|
||||
plt.show()
|
||||
|
||||
delta = np.mean(np.abs(reconstructed - image))
|
||||
print('delta (1 iteration, shifted sinogram) =', delta)
|
||||
assert delta < 0.022 * error_factor
|
||||
|
||||
|
||||
def test_radon_dtype():
|
||||
img = convert_to_float(PHANTOM, False)
|
||||
img32 = img.astype(np.float32)
|
||||
|
||||
assert radon(img).dtype == img.dtype
|
||||
assert radon(img32).dtype == img32.dtype
|
||||
|
||||
|
||||
def test_iradon_sart_dtype():
|
||||
sinogram = np.zeros((16, 1), dtype=int)
|
||||
sinogram[8, 0] = 1.
|
||||
sinogram64 = sinogram.astype('float64')
|
||||
sinogram32 = sinogram.astype('float32')
|
||||
|
||||
with expected_warnings(['Input data is cast to float']):
|
||||
assert iradon_sart(sinogram, theta=[0]).dtype == 'float64'
|
||||
|
||||
assert iradon_sart(sinogram64, theta=[0]).dtype == sinogram64.dtype
|
||||
assert iradon_sart(sinogram32, theta=[0]).dtype == sinogram32.dtype
|
||||
|
||||
|
||||
def test_iradon_wrong_dtype():
|
||||
sinogram = np.zeros((16, 1))
|
||||
|
||||
with testing.raises(ValueError):
|
||||
iradon_sart(sinogram, dtype=int)
|
679
venv/Lib/site-packages/skimage/transform/tests/test_warps.py
Normal file
679
venv/Lib/site-packages/skimage/transform/tests/test_warps.py
Normal file
|
@ -0,0 +1,679 @@
|
|||
import numpy as np
|
||||
from scipy.ndimage import map_coordinates
|
||||
|
||||
from skimage.data import checkerboard, astronaut
|
||||
from skimage.util.dtype import img_as_float
|
||||
from skimage.color.colorconv import rgb2gray
|
||||
from skimage.draw.draw import circle_perimeter_aa
|
||||
from skimage.feature.peak import peak_local_max
|
||||
from skimage._shared import testing
|
||||
from skimage._shared.testing import (assert_almost_equal, assert_equal,
|
||||
test_parallel)
|
||||
from skimage._shared._warnings import expected_warnings
|
||||
|
||||
from skimage.transform._warps import (_stackcopy,
|
||||
_linear_polar_mapping,
|
||||
_log_polar_mapping, warp,
|
||||
warp_coords, rotate, resize,
|
||||
rescale, warp_polar, swirl,
|
||||
downscale_local_mean)
|
||||
from skimage.transform._geometric import (AffineTransform,
|
||||
ProjectiveTransform,
|
||||
SimilarityTransform)
|
||||
|
||||
|
||||
np.random.seed(0)
|
||||
|
||||
|
||||
def test_stackcopy():
|
||||
layers = 4
|
||||
x = np.empty((3, 3, layers))
|
||||
y = np.eye(3, 3)
|
||||
_stackcopy(x, y)
|
||||
for i in range(layers):
|
||||
assert_almost_equal(x[..., i], y)
|
||||
|
||||
|
||||
def test_warp_tform():
|
||||
x = np.zeros((5, 5), dtype=np.double)
|
||||
x[2, 2] = 1
|
||||
theta = - np.pi / 2
|
||||
tform = SimilarityTransform(scale=1, rotation=theta, translation=(0, 4))
|
||||
|
||||
x90 = warp(x, tform, order=1)
|
||||
assert_almost_equal(x90, np.rot90(x))
|
||||
|
||||
x90 = warp(x, tform.inverse, order=1)
|
||||
assert_almost_equal(x90, np.rot90(x))
|
||||
|
||||
|
||||
def test_warp_callable():
|
||||
x = np.zeros((5, 5), dtype=np.double)
|
||||
x[2, 2] = 1
|
||||
refx = np.zeros((5, 5), dtype=np.double)
|
||||
refx[1, 1] = 1
|
||||
|
||||
def shift(xy):
|
||||
return xy + 1
|
||||
|
||||
outx = warp(x, shift, order=1)
|
||||
assert_almost_equal(outx, refx)
|
||||
|
||||
|
||||
@test_parallel()
|
||||
def test_warp_matrix():
|
||||
x = np.zeros((5, 5), dtype=np.double)
|
||||
x[2, 2] = 1
|
||||
refx = np.zeros((5, 5), dtype=np.double)
|
||||
refx[1, 1] = 1
|
||||
|
||||
matrix = np.array([[1, 0, 1], [0, 1, 1], [0, 0, 1]])
|
||||
|
||||
# _warp_fast
|
||||
outx = warp(x, matrix, order=1)
|
||||
assert_almost_equal(outx, refx)
|
||||
# check for ndimage.map_coordinates
|
||||
outx = warp(x, matrix, order=5)
|
||||
|
||||
|
||||
def test_warp_nd():
|
||||
for dim in range(2, 8):
|
||||
shape = dim * (5,)
|
||||
|
||||
x = np.zeros(shape, dtype=np.double)
|
||||
x_c = dim * (2,)
|
||||
x[x_c] = 1
|
||||
refx = np.zeros(shape, dtype=np.double)
|
||||
refx_c = dim * (1,)
|
||||
refx[refx_c] = 1
|
||||
|
||||
coord_grid = dim * (slice(0, 5, 1),)
|
||||
coords = np.array(np.mgrid[coord_grid]) + 1
|
||||
|
||||
outx = warp(x, coords, order=0, cval=0)
|
||||
|
||||
assert_almost_equal(outx, refx)
|
||||
|
||||
|
||||
def test_warp_clip():
|
||||
x = np.zeros((5, 5), dtype=np.double)
|
||||
x[2, 2] = 1
|
||||
|
||||
outx = rescale(x, 3, order=3, clip=False,
|
||||
multichannel=False, anti_aliasing=False, mode='constant')
|
||||
assert outx.min() < 0
|
||||
|
||||
outx = rescale(x, 3, order=3, clip=True,
|
||||
multichannel=False, anti_aliasing=False, mode='constant')
|
||||
assert_almost_equal(outx.min(), 0)
|
||||
assert_almost_equal(outx.max(), 1)
|
||||
|
||||
|
||||
def test_homography():
|
||||
x = np.zeros((5, 5), dtype=np.double)
|
||||
x[1, 1] = 1
|
||||
theta = -np.pi / 2
|
||||
M = np.array([[np.cos(theta), - np.sin(theta), 0],
|
||||
[np.sin(theta), np.cos(theta), 4],
|
||||
[0, 0, 1]])
|
||||
|
||||
x90 = warp(x,
|
||||
inverse_map=ProjectiveTransform(M).inverse,
|
||||
order=1)
|
||||
assert_almost_equal(x90, np.rot90(x))
|
||||
|
||||
|
||||
def test_rotate():
|
||||
x = np.zeros((5, 5), dtype=np.double)
|
||||
x[1, 1] = 1
|
||||
x90 = rotate(x, 90)
|
||||
assert_almost_equal(x90, np.rot90(x))
|
||||
|
||||
|
||||
def test_rotate_resize():
|
||||
x = np.zeros((10, 10), dtype=np.double)
|
||||
|
||||
x45 = rotate(x, 45, resize=False)
|
||||
assert x45.shape == (10, 10)
|
||||
|
||||
x45 = rotate(x, 45, resize=True)
|
||||
# new dimension should be d = sqrt(2 * (10/2)^2)
|
||||
assert x45.shape == (14, 14)
|
||||
|
||||
|
||||
def test_rotate_center():
|
||||
x = np.zeros((10, 10), dtype=np.double)
|
||||
x[4, 4] = 1
|
||||
refx = np.zeros((10, 10), dtype=np.double)
|
||||
refx[2, 5] = 1
|
||||
x20 = rotate(x, 20, order=0, center=(0, 0))
|
||||
assert_almost_equal(x20, refx)
|
||||
x0 = rotate(x20, -20, order=0, center=(0, 0))
|
||||
assert_almost_equal(x0, x)
|
||||
|
||||
|
||||
def test_rotate_resize_center():
|
||||
x = np.zeros((10, 10), dtype=np.double)
|
||||
x[0, 0] = 1
|
||||
|
||||
ref_x45 = np.zeros((14, 14), dtype=np.double)
|
||||
ref_x45[6, 0] = 1
|
||||
ref_x45[7, 0] = 1
|
||||
|
||||
x45 = rotate(x, 45, resize=True, center=(3, 3), order=0)
|
||||
# new dimension should be d = sqrt(2 * (10/2)^2)
|
||||
assert x45.shape == (14, 14)
|
||||
assert_equal(x45, ref_x45)
|
||||
|
||||
|
||||
def test_rotate_resize_90():
|
||||
x90 = rotate(np.zeros((470, 230), dtype=np.double), 90, resize=True)
|
||||
assert x90.shape == (230, 470)
|
||||
|
||||
|
||||
def test_rescale():
|
||||
# same scale factor
|
||||
x = np.zeros((5, 5), dtype=np.double)
|
||||
x[1, 1] = 1
|
||||
scaled = rescale(x, 2, order=0,
|
||||
multichannel=False, anti_aliasing=False, mode='constant')
|
||||
ref = np.zeros((10, 10))
|
||||
ref[2:4, 2:4] = 1
|
||||
assert_almost_equal(scaled, ref)
|
||||
|
||||
# different scale factors
|
||||
x = np.zeros((5, 5), dtype=np.double)
|
||||
x[1, 1] = 1
|
||||
|
||||
scaled = rescale(x, (2, 1), order=0,
|
||||
multichannel=False, anti_aliasing=False, mode='constant')
|
||||
ref = np.zeros((10, 5))
|
||||
ref[2:4, 1] = 1
|
||||
assert_almost_equal(scaled, ref)
|
||||
|
||||
|
||||
def test_rescale_invalid_scale():
|
||||
x = np.zeros((10, 10, 3))
|
||||
with testing.raises(ValueError):
|
||||
rescale(x, (2, 2),
|
||||
multichannel=False, anti_aliasing=False, mode='constant')
|
||||
with testing.raises(ValueError):
|
||||
rescale(x, (2, 2, 2),
|
||||
multichannel=True, anti_aliasing=False, mode='constant')
|
||||
|
||||
|
||||
def test_rescale_multichannel():
|
||||
# 1D + channels
|
||||
x = np.zeros((8, 3), dtype=np.double)
|
||||
scaled = rescale(x, 2, order=0, multichannel=True, anti_aliasing=False,
|
||||
mode='constant')
|
||||
assert_equal(scaled.shape, (16, 3))
|
||||
# 2D
|
||||
scaled = rescale(x, 2, order=0, multichannel=False, anti_aliasing=False,
|
||||
mode='constant')
|
||||
assert_equal(scaled.shape, (16, 6))
|
||||
|
||||
# 2D + channels
|
||||
x = np.zeros((8, 8, 3), dtype=np.double)
|
||||
scaled = rescale(x, 2, order=0, multichannel=True, anti_aliasing=False,
|
||||
mode='constant')
|
||||
assert_equal(scaled.shape, (16, 16, 3))
|
||||
# 3D
|
||||
scaled = rescale(x, 2, order=0, multichannel=False, anti_aliasing=False,
|
||||
mode='constant')
|
||||
assert_equal(scaled.shape, (16, 16, 6))
|
||||
|
||||
# 3D + channels
|
||||
x = np.zeros((8, 8, 8, 3), dtype=np.double)
|
||||
scaled = rescale(x, 2, order=0, multichannel=True, anti_aliasing=False,
|
||||
mode='constant')
|
||||
assert_equal(scaled.shape, (16, 16, 16, 3))
|
||||
# 4D
|
||||
scaled = rescale(x, 2, order=0, multichannel=False, anti_aliasing=False,
|
||||
mode='constant')
|
||||
assert_equal(scaled.shape, (16, 16, 16, 6))
|
||||
|
||||
|
||||
def test_rescale_multichannel_multiscale():
|
||||
x = np.zeros((5, 5, 3), dtype=np.double)
|
||||
scaled = rescale(x, (2, 1), order=0, multichannel=True,
|
||||
anti_aliasing=False, mode='constant')
|
||||
assert_equal(scaled.shape, (10, 5, 3))
|
||||
|
||||
|
||||
def test_rescale_multichannel_defaults():
|
||||
x = np.zeros((8, 3), dtype=np.double)
|
||||
scaled = rescale(x, 2, order=0, anti_aliasing=False, mode='constant')
|
||||
assert_equal(scaled.shape, (16, 6))
|
||||
|
||||
x = np.zeros((8, 8, 3), dtype=np.double)
|
||||
scaled = rescale(x, 2, order=0, anti_aliasing=False, mode='constant')
|
||||
assert_equal(scaled.shape, (16, 16, 6))
|
||||
|
||||
|
||||
def test_resize2d():
|
||||
x = np.zeros((5, 5), dtype=np.double)
|
||||
x[1, 1] = 1
|
||||
resized = resize(x, (10, 10), order=0, anti_aliasing=False,
|
||||
mode='constant')
|
||||
ref = np.zeros((10, 10))
|
||||
ref[2:4, 2:4] = 1
|
||||
assert_almost_equal(resized, ref)
|
||||
|
||||
|
||||
def test_resize3d_keep():
|
||||
# keep 3rd dimension
|
||||
x = np.zeros((5, 5, 3), dtype=np.double)
|
||||
x[1, 1, :] = 1
|
||||
resized = resize(x, (10, 10), order=0, anti_aliasing=False,
|
||||
mode='constant')
|
||||
with testing.raises(ValueError):
|
||||
# output_shape too short
|
||||
resize(x, (10, ), order=0, anti_aliasing=False, mode='constant')
|
||||
ref = np.zeros((10, 10, 3))
|
||||
ref[2:4, 2:4, :] = 1
|
||||
assert_almost_equal(resized, ref)
|
||||
resized = resize(x, (10, 10, 3), order=0, anti_aliasing=False,
|
||||
mode='constant')
|
||||
assert_almost_equal(resized, ref)
|
||||
|
||||
|
||||
def test_resize3d_resize():
|
||||
# resize 3rd dimension
|
||||
x = np.zeros((5, 5, 3), dtype=np.double)
|
||||
x[1, 1, :] = 1
|
||||
resized = resize(x, (10, 10, 1), order=0, anti_aliasing=False,
|
||||
mode='constant')
|
||||
ref = np.zeros((10, 10, 1))
|
||||
ref[2:4, 2:4] = 1
|
||||
assert_almost_equal(resized, ref)
|
||||
|
||||
|
||||
def test_resize3d_2din_3dout():
|
||||
# 3D output with 2D input
|
||||
x = np.zeros((5, 5), dtype=np.double)
|
||||
x[1, 1] = 1
|
||||
resized = resize(x, (10, 10, 1), order=0, anti_aliasing=False,
|
||||
mode='constant')
|
||||
ref = np.zeros((10, 10, 1))
|
||||
ref[2:4, 2:4] = 1
|
||||
assert_almost_equal(resized, ref)
|
||||
|
||||
|
||||
def test_resize2d_4d():
|
||||
# resize with extra output dimensions
|
||||
x = np.zeros((5, 5), dtype=np.double)
|
||||
x[1, 1] = 1
|
||||
out_shape = (10, 10, 1, 1)
|
||||
resized = resize(x, out_shape, order=0, anti_aliasing=False,
|
||||
mode='constant')
|
||||
ref = np.zeros(out_shape)
|
||||
ref[2:4, 2:4, ...] = 1
|
||||
assert_almost_equal(resized, ref)
|
||||
|
||||
|
||||
def test_resize_nd():
|
||||
for dim in range(1, 6):
|
||||
shape = 2 + np.arange(dim) * 2
|
||||
x = np.ones(shape)
|
||||
out_shape = np.asarray(shape) * 1.5
|
||||
resized = resize(x, out_shape, order=0, mode='reflect',
|
||||
anti_aliasing=False)
|
||||
expected_shape = 1.5 * shape
|
||||
assert_equal(resized.shape, expected_shape)
|
||||
assert np.all(resized == 1)
|
||||
|
||||
|
||||
def test_resize3d_bilinear():
|
||||
# bilinear 3rd dimension
|
||||
x = np.zeros((5, 5, 2), dtype=np.double)
|
||||
x[1, 1, 0] = 0
|
||||
x[1, 1, 1] = 1
|
||||
resized = resize(x, (10, 10, 1), order=1, mode='constant',
|
||||
anti_aliasing=False)
|
||||
ref = np.zeros((10, 10, 1))
|
||||
ref[1:5, 1:5, :] = 0.03125
|
||||
ref[1:5, 2:4, :] = 0.09375
|
||||
ref[2:4, 1:5, :] = 0.09375
|
||||
ref[2:4, 2:4, :] = 0.28125
|
||||
assert_almost_equal(resized, ref)
|
||||
|
||||
|
||||
def test_resize_dtype():
|
||||
x = np.zeros((5, 5))
|
||||
x_f32 = x.astype(np.float32)
|
||||
x_u8 = x.astype(np.uint8)
|
||||
x_b = x.astype(bool)
|
||||
|
||||
assert resize(x, (10, 10), preserve_range=False).dtype == x.dtype
|
||||
assert resize(x, (10, 10), preserve_range=True).dtype == x.dtype
|
||||
assert resize(x_u8, (10, 10), preserve_range=False).dtype == np.double
|
||||
assert resize(x_u8, (10, 10), preserve_range=True).dtype == np.double
|
||||
assert resize(x_b, (10, 10), preserve_range=False).dtype == np.double
|
||||
assert resize(x_b, (10, 10), preserve_range=True).dtype == np.double
|
||||
assert resize(x_f32, (10, 10), preserve_range=False).dtype == x_f32.dtype
|
||||
assert resize(x_f32, (10, 10), preserve_range=True).dtype == x_f32.dtype
|
||||
|
||||
|
||||
def test_swirl():
|
||||
image = img_as_float(checkerboard())
|
||||
|
||||
swirl_params = {'radius': 80, 'rotation': 0, 'order': 2, 'mode': 'reflect'}
|
||||
|
||||
with expected_warnings(['Bi-quadratic.*bug']):
|
||||
swirled = swirl(image, strength=10, **swirl_params)
|
||||
unswirled = swirl(swirled, strength=-10, **swirl_params)
|
||||
|
||||
assert np.mean(np.abs(image - unswirled)) < 0.01
|
||||
|
||||
swirl_params.pop('mode')
|
||||
|
||||
with expected_warnings(['Bi-quadratic.*bug']):
|
||||
swirled = swirl(image, strength=10, **swirl_params)
|
||||
unswirled = swirl(swirled, strength=-10, **swirl_params)
|
||||
|
||||
assert np.mean(np.abs(image[1:-1, 1:-1] - unswirled[1:-1, 1:-1])) < 0.01
|
||||
|
||||
|
||||
def test_const_cval_out_of_range():
|
||||
img = np.random.randn(100, 100)
|
||||
cval = - 10
|
||||
warped = warp(img, AffineTransform(translation=(10, 10)), cval=cval)
|
||||
assert np.sum(warped == cval) == (2 * 100 * 10 - 10 * 10)
|
||||
|
||||
|
||||
def test_warp_identity():
|
||||
img = img_as_float(rgb2gray(astronaut()))
|
||||
assert len(img.shape) == 2
|
||||
assert np.allclose(img, warp(img, AffineTransform(rotation=0)))
|
||||
assert not np.allclose(img, warp(img, AffineTransform(rotation=0.1)))
|
||||
rgb_img = np.transpose(np.asarray([img, np.zeros_like(img), img]),
|
||||
(1, 2, 0))
|
||||
warped_rgb_img = warp(rgb_img, AffineTransform(rotation=0.1))
|
||||
assert np.allclose(rgb_img, warp(rgb_img, AffineTransform(rotation=0)))
|
||||
assert not np.allclose(rgb_img, warped_rgb_img)
|
||||
# assert no cross-talk between bands
|
||||
assert np.all(0 == warped_rgb_img[:, :, 1])
|
||||
|
||||
|
||||
def test_warp_coords_example():
|
||||
image = astronaut().astype(np.float32)
|
||||
assert 3 == image.shape[2]
|
||||
tform = SimilarityTransform(translation=(0, -10))
|
||||
coords = warp_coords(tform, (30, 30, 3))
|
||||
map_coordinates(image[:, :, 0], coords[:2])
|
||||
|
||||
|
||||
def test_downsize():
|
||||
x = np.zeros((10, 10), dtype=np.double)
|
||||
x[2:4, 2:4] = 1
|
||||
scaled = resize(x, (5, 5), order=0, anti_aliasing=False, mode='constant')
|
||||
assert_equal(scaled.shape, (5, 5))
|
||||
assert_equal(scaled[1, 1], 1)
|
||||
assert_equal(scaled[2:, :].sum(), 0)
|
||||
assert_equal(scaled[:, 2:].sum(), 0)
|
||||
|
||||
|
||||
def test_downsize_anti_aliasing():
|
||||
x = np.zeros((10, 10), dtype=np.double)
|
||||
x[2, 2] = 1
|
||||
scaled = resize(x, (5, 5), order=1, anti_aliasing=True, mode='constant')
|
||||
assert_equal(scaled.shape, (5, 5))
|
||||
assert np.all(scaled[:3, :3] > 0)
|
||||
assert_equal(scaled[3:, :].sum(), 0)
|
||||
assert_equal(scaled[:, 3:].sum(), 0)
|
||||
|
||||
sigma = 0.125
|
||||
out_size = (5, 5)
|
||||
resize(x, out_size, order=1, mode='constant',
|
||||
anti_aliasing=True, anti_aliasing_sigma=sigma)
|
||||
resize(x, out_size, order=1, mode='edge',
|
||||
anti_aliasing=True, anti_aliasing_sigma=sigma)
|
||||
resize(x, out_size, order=1, mode='symmetric',
|
||||
anti_aliasing=True, anti_aliasing_sigma=sigma)
|
||||
resize(x, out_size, order=1, mode='reflect',
|
||||
anti_aliasing=True, anti_aliasing_sigma=sigma)
|
||||
resize(x, out_size, order=1, mode='wrap',
|
||||
anti_aliasing=True, anti_aliasing_sigma=sigma)
|
||||
|
||||
with testing.raises(ValueError): # Unknown mode, or cannot translate mode
|
||||
resize(x, out_size, order=1, mode='non-existent',
|
||||
anti_aliasing=True, anti_aliasing_sigma=sigma)
|
||||
|
||||
|
||||
def test_downsize_anti_aliasing_invalid_stddev():
|
||||
x = np.zeros((10, 10), dtype=np.double)
|
||||
with testing.raises(ValueError):
|
||||
resize(x, (5, 5), order=0, anti_aliasing=True, anti_aliasing_sigma=-1,
|
||||
mode='constant')
|
||||
with expected_warnings(["Anti-aliasing standard deviation greater"]):
|
||||
resize(x, (5, 15), order=0, anti_aliasing=True,
|
||||
anti_aliasing_sigma=(1, 1), mode="reflect")
|
||||
resize(x, (5, 15), order=0, anti_aliasing=True,
|
||||
anti_aliasing_sigma=(0, 1), mode="reflect")
|
||||
|
||||
|
||||
def test_downscale():
|
||||
x = np.zeros((10, 10), dtype=np.double)
|
||||
x[2:4, 2:4] = 1
|
||||
scaled = rescale(x, 0.5, order=0, anti_aliasing=False,
|
||||
multichannel=False, mode='constant')
|
||||
assert_equal(scaled.shape, (5, 5))
|
||||
assert_equal(scaled[1, 1], 1)
|
||||
assert_equal(scaled[2:, :].sum(), 0)
|
||||
assert_equal(scaled[:, 2:].sum(), 0)
|
||||
|
||||
|
||||
def test_downscale_anti_aliasing():
|
||||
x = np.zeros((10, 10), dtype=np.double)
|
||||
x[2, 2] = 1
|
||||
scaled = rescale(x, 0.5, order=1, anti_aliasing=True,
|
||||
multichannel=False, mode='constant')
|
||||
assert_equal(scaled.shape, (5, 5))
|
||||
assert np.all(scaled[:3, :3] > 0)
|
||||
assert_equal(scaled[3:, :].sum(), 0)
|
||||
assert_equal(scaled[:, 3:].sum(), 0)
|
||||
|
||||
|
||||
def test_downscale_local_mean():
|
||||
image1 = np.arange(4 * 6).reshape(4, 6)
|
||||
out1 = downscale_local_mean(image1, (2, 3))
|
||||
expected1 = np.array([[4., 7.],
|
||||
[16., 19.]])
|
||||
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_equal(expected2, out2)
|
||||
|
||||
|
||||
def test_invalid():
|
||||
with testing.raises(ValueError):
|
||||
warp(np.ones((4, 3, 3, 3)),
|
||||
SimilarityTransform())
|
||||
|
||||
|
||||
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_equal(warp(image, inverse_tform), warp(image, tform.inverse))
|
||||
|
||||
|
||||
def test_slow_warp_nonint_oshape():
|
||||
image = np.random.rand(5, 5)
|
||||
|
||||
with testing.raises(ValueError):
|
||||
warp(image, lambda xy: xy,
|
||||
output_shape=(13.1, 19.5))
|
||||
|
||||
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,
|
||||
mode='constant', multichannel=False, anti_aliasing=False)
|
||||
assert out.min() == 0
|
||||
assert out.max() == 2
|
||||
|
||||
out = rescale(image, 2, preserve_range=True, clip=True, order=0,
|
||||
mode='constant', multichannel=False, anti_aliasing=False)
|
||||
assert out.min() == 0
|
||||
assert out.max() == 2
|
||||
|
||||
out = rescale(image.astype(np.uint8), 2, preserve_range=False,
|
||||
mode='constant', multichannel=False, anti_aliasing=False,
|
||||
clip=True, order=0)
|
||||
assert out.min() == 0
|
||||
assert out.max() == 2 / 255.0
|
||||
|
||||
|
||||
def test_zero_image_size():
|
||||
with testing.raises(ValueError):
|
||||
warp(np.zeros(0),
|
||||
SimilarityTransform())
|
||||
with testing.raises(ValueError):
|
||||
warp(np.zeros((0, 10)),
|
||||
SimilarityTransform())
|
||||
with testing.raises(ValueError):
|
||||
warp(np.zeros((10, 0)),
|
||||
SimilarityTransform())
|
||||
with testing.raises(ValueError):
|
||||
warp(np.zeros((10, 10, 0)),
|
||||
SimilarityTransform())
|
||||
|
||||
|
||||
def test_linear_polar_mapping():
|
||||
output_coords = np.array([[0, 0],
|
||||
[0, 90],
|
||||
[0, 180],
|
||||
[0, 270],
|
||||
[99, 0],
|
||||
[99, 180],
|
||||
[99, 270],
|
||||
[99, 45]])
|
||||
ground_truth = np.array([[100, 100],
|
||||
[100, 100],
|
||||
[100, 100],
|
||||
[100, 100],
|
||||
[199, 100],
|
||||
[1, 100],
|
||||
[100, 1],
|
||||
[170.00357134, 170.00357134]])
|
||||
k_angle = 360 / (2 * np.pi)
|
||||
k_radius = 1
|
||||
center = (100, 100)
|
||||
coords = _linear_polar_mapping(output_coords, k_angle, k_radius, center)
|
||||
assert np.allclose(coords, ground_truth)
|
||||
|
||||
|
||||
def test_log_polar_mapping():
|
||||
output_coords = np.array([[0, 0],
|
||||
[0, 90],
|
||||
[0, 180],
|
||||
[0, 270],
|
||||
[99, 0],
|
||||
[99, 180],
|
||||
[99, 270],
|
||||
[99, 45]])
|
||||
ground_truth = np.array([[101, 100],
|
||||
[100, 101],
|
||||
[99, 100],
|
||||
[100, 99],
|
||||
[195.4992586, 100],
|
||||
[4.5007414, 100],
|
||||
[100, 4.5007414],
|
||||
[167.52817336, 167.52817336]])
|
||||
k_angle = 360 / (2 * np.pi)
|
||||
k_radius = 100 / np.log(100)
|
||||
center = (100, 100)
|
||||
coords = _log_polar_mapping(output_coords, k_angle, k_radius, center)
|
||||
assert np.allclose(coords, ground_truth)
|
||||
|
||||
|
||||
def test_linear_warp_polar():
|
||||
radii = [5, 10, 15, 20]
|
||||
image = np.zeros([51, 51])
|
||||
for rad in radii:
|
||||
rr, cc, val = circle_perimeter_aa(25, 25, rad)
|
||||
image[rr, cc] = val
|
||||
warped = warp_polar(image, radius=25)
|
||||
profile = warped.mean(axis=0)
|
||||
peaks = peak_local_max(profile)
|
||||
assert np.alltrue([peak in radii for peak in peaks])
|
||||
|
||||
|
||||
def test_log_warp_polar():
|
||||
radii = [np.exp(2), np.exp(3), np.exp(4), np.exp(5),
|
||||
np.exp(5)-1, np.exp(5)+1]
|
||||
radii = [int(x) for x in radii]
|
||||
image = np.zeros([301, 301])
|
||||
for rad in radii:
|
||||
rr, cc, val = circle_perimeter_aa(150, 150, rad)
|
||||
image[rr, cc] = val
|
||||
warped = warp_polar(image, radius=200, scaling='log')
|
||||
profile = warped.mean(axis=0)
|
||||
peaks_coord = peak_local_max(profile)
|
||||
peaks_coord.sort(axis=0)
|
||||
gaps = peaks_coord[1:] - peaks_coord[:-1]
|
||||
assert np.alltrue([x >= 38 and x <= 40 for x in gaps])
|
||||
|
||||
|
||||
def test_invalid_scaling_polar():
|
||||
with testing.raises(ValueError):
|
||||
warp_polar(np.zeros((10, 10)), (5, 5), scaling='invalid')
|
||||
with testing.raises(ValueError):
|
||||
warp_polar(np.zeros((10, 10)), (5, 5), scaling=None)
|
||||
|
||||
|
||||
def test_invalid_dimensions_polar():
|
||||
with testing.raises(ValueError):
|
||||
warp_polar(np.zeros((10, 10, 3)), (5, 5))
|
||||
with testing.raises(ValueError):
|
||||
warp_polar(np.zeros((10, 10)), (5, 5), multichannel=True)
|
||||
with testing.raises(ValueError):
|
||||
warp_polar(np.zeros((10, 10, 10, 3)), (5, 5), multichannel=True)
|
||||
|
||||
|
||||
def test_bool_img_rescale():
|
||||
img = np.ones((12, 18), dtype=bool)
|
||||
img[2:-2, 4:-4] = False
|
||||
res = rescale(img, 0.5)
|
||||
|
||||
expected = np.ones((6, 9))
|
||||
expected[1:-1, 2:-2] = False
|
||||
|
||||
assert_equal(res, expected)
|
||||
|
||||
|
||||
def test_bool_img_resize():
|
||||
img = np.ones((12, 18), dtype=bool)
|
||||
img[2:-2, 4:-4] = False
|
||||
res = resize(img, (6, 9))
|
||||
|
||||
expected = np.ones((6, 9))
|
||||
expected[1:-1, 2:-2] = False
|
||||
|
||||
assert_equal(res, expected)
|
||||
|
||||
|
||||
def test_boll_array_warnings():
|
||||
img = np.zeros((10, 10), dtype=bool)
|
||||
|
||||
with expected_warnings(['Input image dtype is bool']):
|
||||
rescale(img, 0.5, anti_aliasing=True)
|
||||
|
||||
with expected_warnings(['Input image dtype is bool']):
|
||||
resize(img, (5, 5), anti_aliasing=True)
|
||||
|
||||
with expected_warnings(['Input image dtype is bool']):
|
||||
rescale(img, 0.5, order=1)
|
||||
|
||||
with expected_warnings(['Input image dtype is bool']):
|
||||
resize(img, (5, 5), order=1)
|
||||
|
||||
with expected_warnings(['Input image dtype is bool']):
|
||||
warp(img, np.eye(3), order=1)
|
Loading…
Add table
Add a link
Reference in a new issue