Fixed database typo and removed unnecessary class identifier.

This commit is contained in:
Batuhan Berk Başoğlu 2020-10-14 10:10:37 -04:00
parent 00ad49a143
commit 45fb349a7d
5098 changed files with 952558 additions and 85 deletions

View file

@ -0,0 +1,9 @@
from ..._shared.testing import setup_test, teardown_test
def setup():
setup_test()
def teardown():
teardown_test()

View file

@ -0,0 +1,117 @@
import numpy as np
from skimage.measure import block_reduce
from skimage._shared import testing
from skimage._shared.testing import assert_equal
def test_block_reduce_sum():
image1 = np.arange(4 * 6).reshape(4, 6)
out1 = block_reduce(image1, (2, 3))
expected1 = np.array([[ 24, 42],
[ 96, 114]])
assert_equal(expected1, out1)
image2 = np.arange(5 * 8).reshape(5, 8)
out2 = block_reduce(image2, (3, 3))
expected2 = np.array([[ 81, 108, 87],
[174, 192, 138]])
assert_equal(expected2, out2)
def test_block_reduce_mean():
image1 = np.arange(4 * 6).reshape(4, 6)
out1 = block_reduce(image1, (2, 3), func=np.mean)
expected1 = np.array([[ 4., 7.],
[ 16., 19.]])
assert_equal(expected1, out1)
image2 = np.arange(5 * 8).reshape(5, 8)
out2 = block_reduce(image2, (4, 5), func=np.mean)
expected2 = np.array([[14. , 10.8],
[ 8.5, 5.7]])
assert_equal(expected2, out2)
def test_block_reduce_median():
image1 = np.arange(4 * 6).reshape(4, 6)
out1 = block_reduce(image1, (2, 3), func=np.median)
expected1 = np.array([[ 4., 7.],
[ 16., 19.]])
assert_equal(expected1, out1)
image2 = np.arange(5 * 8).reshape(5, 8)
out2 = block_reduce(image2, (4, 5), func=np.median)
expected2 = np.array([[ 14., 6.5],
[ 0., 0. ]])
assert_equal(expected2, out2)
image3 = np.array([[1, 5, 5, 5], [5, 5, 5, 1000]])
out3 = block_reduce(image3, (2, 4), func=np.median)
assert_equal(5, out3)
def test_block_reduce_min():
image1 = np.arange(4 * 6).reshape(4, 6)
out1 = block_reduce(image1, (2, 3), func=np.min)
expected1 = np.array([[ 0, 3],
[12, 15]])
assert_equal(expected1, out1)
image2 = np.arange(5 * 8).reshape(5, 8)
out2 = block_reduce(image2, (4, 5), func=np.min)
expected2 = np.array([[0, 0],
[0, 0]])
assert_equal(expected2, out2)
def test_block_reduce_max():
image1 = np.arange(4 * 6).reshape(4, 6)
out1 = block_reduce(image1, (2, 3), func=np.max)
expected1 = np.array([[ 8, 11],
[20, 23]])
assert_equal(expected1, out1)
image2 = np.arange(5 * 8).reshape(5, 8)
out2 = block_reduce(image2, (4, 5), func=np.max)
expected2 = np.array([[28, 31],
[36, 39]])
assert_equal(expected2, out2)
def test_invalid_block_size():
image = np.arange(4 * 6).reshape(4, 6)
with testing.raises(ValueError):
block_reduce(image, [1, 2, 3])
with testing.raises(ValueError):
block_reduce(image, [1, 0.5])
def test_func_kwargs_same_dtype():
image = np.array([[97, 123, 173, 227],
[217, 241, 221, 214],
[211, 11, 170, 53],
[214, 205, 101, 57]], dtype=np.uint8)
out = block_reduce(image, (2, 2), func=np.mean,
func_kwargs={'dtype': np.uint8})
expected = np.array([[41, 16], [32, 31]], dtype=np.uint8)
assert_equal(out, expected)
assert out.dtype == expected.dtype
def test_func_kwargs_different_dtype():
image = np.array([[0.45745366, 0.67479345, 0.20949775, 0.3147348],
[0.7209286, 0.88915504, 0.66153409, 0.07919526],
[0.04640037, 0.54008495, 0.34664343, 0.56152301],
[0.58085003, 0.80144708, 0.87844473, 0.29811511]],
dtype=np.float64)
out = block_reduce(image, (2, 2), func=np.mean,
func_kwargs={'dtype': np.float16})
expected = np.array([[0.6855, 0.3164], [0.4922, 0.521]], dtype=np.float16)
assert_equal(out, expected)
assert out.dtype == expected.dtype

View file

@ -0,0 +1,16 @@
import numpy as np
from skimage.measure import shannon_entropy
from skimage._shared.testing import assert_almost_equal
def test_shannon_ones():
img = np.ones((10, 10))
res = shannon_entropy(img, base=np.e)
assert_almost_equal(res, 0.0)
def test_shannon_all_unique():
img = np.arange(64)
res = shannon_entropy(img, base=2)
assert_almost_equal(res, np.log(64) / np.log(2))

View file

@ -0,0 +1,132 @@
import numpy as np
from skimage.measure import find_contours
from skimage._shared import testing
from skimage._shared.testing import assert_array_equal
from pytest import raises
a = np.ones((8, 8), dtype=np.float32)
a[1:-1, 1] = 0
a[1, 1:-1] = 0
x, y = np.mgrid[-1:1:5j, -1:1:5j]
r = np.sqrt(x**2 + y**2)
def test_binary():
ref = [[6. , 1.5],
[5. , 1.5],
[4. , 1.5],
[3. , 1.5],
[2. , 1.5],
[1.5, 2. ],
[1.5, 3. ],
[1.5, 4. ],
[1.5, 5. ],
[1.5, 6. ],
[1. , 6.5],
[0.5, 6. ],
[0.5, 5. ],
[0.5, 4. ],
[0.5, 3. ],
[0.5, 2. ],
[0.5, 1. ],
[1. , 0.5],
[2. , 0.5],
[3. , 0.5],
[4. , 0.5],
[5. , 0.5],
[6. , 0.5],
[6.5, 1. ],
[6. , 1.5]]
contours = find_contours(a, 0.5, positive_orientation='high')
assert len(contours) == 1
assert_array_equal(contours[0][::-1], ref)
# target contour for mask tests
mask_contour = [
[6. , 0.5],
[5. , 0.5],
[4. , 0.5],
[3. , 0.5],
[2. , 0.5],
[1. , 0.5],
[0.5, 1. ],
[0.5, 2. ],
[0.5, 3. ],
[0.5, 4. ],
[0.5, 5. ],
[0.5, 6. ],
[1. , 6.5],
[1.5, 6. ],
[1.5, 5. ],
[1.5, 4. ],
[1.5, 3. ],
[1.5, 2. ],
[2. , 1.5],
[3. , 1.5],
[4. , 1.5],
[5. , 1.5],
[6. , 1.5],
]
mask = np.ones((8, 8), dtype=bool)
# Some missing data that should result in a hole in the contour:
mask[7, 0:3] = False
def test_nodata():
# Test missing data via NaNs in input array
b = np.copy(a)
b[~mask] = np.nan
contours = find_contours(b, 0.5, positive_orientation='high')
assert len(contours) == 1
assert_array_equal(contours[0], mask_contour)
def test_mask():
# Test missing data via explicit masking
contours = find_contours(a, 0.5, positive_orientation='high', mask=mask)
assert len(contours) == 1
assert_array_equal(contours[0], mask_contour)
def test_mask_shape():
bad_mask = np.ones((8, 7), dtype=bool)
with raises(ValueError, match='shape'):
find_contours(a, 0, mask=bad_mask)
def test_mask_dtype():
bad_mask = np.ones((8,8), dtype=np.uint8)
with raises(TypeError, match='binary'):
find_contours(a, 0, mask=bad_mask)
def test_float():
contours = find_contours(r, 0.5)
assert len(contours) == 1
assert_array_equal(contours[0],
[[ 2., 3.],
[ 1., 2.],
[ 2., 1.],
[ 3., 2.],
[ 2., 3.]])
def test_memory_order():
contours = find_contours(np.ascontiguousarray(r), 0.5)
assert len(contours) == 1
contours = find_contours(np.asfortranarray(r), 0.5)
assert len(contours) == 1
def test_invalid_input():
with testing.raises(ValueError):
find_contours(r, 0.5, 'foo', 'bar')
with testing.raises(ValueError):
find_contours(r[..., None], 0.5)

View file

@ -0,0 +1,388 @@
import numpy as np
from skimage.measure import LineModelND, CircleModel, EllipseModel, ransac
from skimage.transform import AffineTransform
from skimage.measure.fit import _dynamic_max_trials
from skimage._shared import testing
from skimage._shared.testing import (assert_equal, assert_almost_equal,
assert_array_less, xfail, arch32)
def test_line_model_invalid_input():
with testing.raises(ValueError):
LineModelND().estimate(np.empty((1, 3)))
def test_line_model_predict():
model = LineModelND()
model.params = ((0, 0), (1, 1))
x = np.arange(-10, 10)
y = model.predict_y(x)
assert_almost_equal(x, model.predict_x(y))
def test_line_model_nd_invalid_input():
with testing.raises(ValueError):
LineModelND().predict_x(np.zeros(1))
with testing.raises(ValueError):
LineModelND().predict_y(np.zeros(1))
with testing.raises(ValueError):
LineModelND().predict_x(np.zeros(1), np.zeros(1))
with testing.raises(ValueError):
LineModelND().predict_y(np.zeros(1))
with testing.raises(ValueError):
LineModelND().predict_y(np.zeros(1), np.zeros(1))
with testing.raises(ValueError):
LineModelND().estimate(np.empty((1, 3)))
with testing.raises(ValueError):
LineModelND().residuals(np.empty((1, 3)))
data = np.empty((1, 2))
with testing.raises(ValueError):
LineModelND().estimate(data)
def test_line_model_nd_predict():
model = LineModelND()
model.params = (np.array([0, 0]), np.array([0.2, 0.8]))
x = np.arange(-10, 10)
y = model.predict_y(x)
assert_almost_equal(x, model.predict_x(y))
def test_line_model_nd_estimate():
# generate original data without noise
model0 = LineModelND()
model0.params = (np.array([0, 0, 0], dtype='float'),
np.array([1, 1, 1], dtype='float')/np.sqrt(3))
# we scale the unit vector with a factor 10 when generating points on the
# line in order to compensate for the scale of the random noise
data0 = (model0.params[0] +
10 * np.arange(-100, 100)[..., np.newaxis] * model0.params[1])
# add gaussian noise to data
random_state = np.random.RandomState(1234)
data = data0 + random_state.normal(size=data0.shape)
# estimate parameters of noisy data
model_est = LineModelND()
model_est.estimate(data)
# assert_almost_equal(model_est.residuals(data0), np.zeros(len(data)), 1)
# test whether estimated parameters are correct
# we use the following geometric property: two aligned vectors have
# a cross-product equal to zero
# test if direction vectors are aligned
assert_almost_equal(np.linalg.norm(np.cross(model0.params[1],
model_est.params[1])), 0, 1)
# test if origins are aligned with the direction
a = model_est.params[0] - model0.params[0]
if np.linalg.norm(a) > 0:
a /= np.linalg.norm(a)
assert_almost_equal(np.linalg.norm(np.cross(model0.params[1], a)), 0, 1)
def test_line_model_nd_residuals():
model = LineModelND()
model.params = (np.array([0, 0, 0]), np.array([0, 0, 1]))
assert_equal(abs(model.residuals(np.array([[0, 0, 0]]))), 0)
assert_equal(abs(model.residuals(np.array([[0, 0, 1]]))), 0)
assert_equal(abs(model.residuals(np.array([[10, 0, 0]]))), 10)
# test params argument in model.rediduals
data = np.array([[10, 0, 0]])
params = (np.array([0, 0, 0]), np.array([2, 0, 0]))
assert_equal(abs(model.residuals(data, params=params)), 30)
def test_line_modelND_under_determined():
data = np.empty((1, 3))
with testing.raises(ValueError):
LineModelND().estimate(data)
def test_circle_model_invalid_input():
with testing.raises(ValueError):
CircleModel().estimate(np.empty((5, 3)))
def test_circle_model_predict():
model = CircleModel()
r = 5
model.params = (0, 0, r)
t = np.arange(0, 2 * np.pi, np.pi / 2)
xy = np.array(((5, 0), (0, 5), (-5, 0), (0, -5)))
assert_almost_equal(xy, model.predict_xy(t))
def test_circle_model_estimate():
# generate original data without noise
model0 = CircleModel()
model0.params = (10, 12, 3)
t = np.linspace(0, 2 * np.pi, 1000)
data0 = model0.predict_xy(t)
# add gaussian noise to data
random_state = np.random.RandomState(1234)
data = data0 + random_state.normal(size=data0.shape)
# estimate parameters of noisy data
model_est = CircleModel()
model_est.estimate(data)
# test whether estimated parameters almost equal original parameters
assert_almost_equal(model0.params, model_est.params, 0)
def test_circle_model_residuals():
model = CircleModel()
model.params = (0, 0, 5)
assert_almost_equal(abs(model.residuals(np.array([[5, 0]]))), 0)
assert_almost_equal(abs(model.residuals(np.array([[6, 6]]))),
np.sqrt(2 * 6**2) - 5)
assert_almost_equal(abs(model.residuals(np.array([[10, 0]]))), 5)
def test_ellipse_model_invalid_input():
with testing.raises(ValueError):
EllipseModel().estimate(np.empty((5, 3)))
def test_ellipse_model_predict():
model = EllipseModel()
model.params = (0, 0, 5, 10, 0)
t = np.arange(0, 2 * np.pi, np.pi / 2)
xy = np.array(((5, 0), (0, 10), (-5, 0), (0, -10)))
assert_almost_equal(xy, model.predict_xy(t))
def test_ellipse_model_estimate():
for angle in range(0, 180, 15):
rad = np.deg2rad(angle)
# generate original data without noise
model0 = EllipseModel()
model0.params = (10, 20, 15, 25, rad)
t = np.linspace(0, 2 * np.pi, 100)
data0 = model0.predict_xy(t)
# add gaussian noise to data
random_state = np.random.RandomState(1234)
data = data0 + random_state.normal(size=data0.shape)
# estimate parameters of noisy data
model_est = EllipseModel()
model_est.estimate(data)
# test whether estimated parameters almost equal original parameters
assert_almost_equal(model0.params[:2], model_est.params[:2], 0)
res = model_est.residuals(data0)
assert_array_less(res, np.ones(res.shape))
def test_ellipse_model_estimate_from_data():
data = np.array([
[264, 854], [265, 875], [268, 863], [270, 857], [275, 905], [285, 915],
[305, 925], [324, 934], [335, 764], [336, 915], [345, 925], [345, 945],
[354, 933], [355, 745], [364, 936], [365, 754], [375, 745], [375, 735],
[385, 736], [395, 735], [394, 935], [405, 727], [415, 736], [415, 727],
[425, 727], [426, 929], [435, 735], [444, 933], [445, 735], [455, 724],
[465, 934], [465, 735], [475, 908], [475, 726], [485, 753], [485, 728],
[492, 762], [495, 745], [491, 910], [493, 909], [499, 904], [505, 905],
[504, 747], [515, 743], [516, 752], [524, 855], [525, 844], [525, 885],
[533, 845], [533, 873], [535, 883], [545, 874], [543, 864], [553, 865],
[553, 845], [554, 825], [554, 835], [563, 845], [565, 826], [563, 855],
[563, 795], [565, 735], [573, 778], [572, 815], [574, 804], [575, 665],
[575, 685], [574, 705], [574, 745], [575, 875], [572, 732], [582, 795],
[579, 709], [583, 805], [583, 854], [586, 755], [584, 824], [585, 655],
[581, 718], [586, 844], [585, 915], [587, 905], [594, 824], [593, 855],
[590, 891], [594, 776], [596, 767], [593, 763], [603, 785], [604, 775],
[603, 885], [605, 753], [605, 655], [606, 935], [603, 761], [613, 802],
[613, 945], [613, 965], [615, 693], [617, 665], [623, 962], [624, 972],
[625, 995], [633, 673], [633, 965], [633, 683], [633, 692], [633, 954],
[634, 1016], [635, 664], [641, 804], [637, 999], [641, 956], [643, 946],
[643, 926], [644, 975], [643, 655], [646, 705], [651, 664], [651, 984],
[647, 665], [651, 715], [651, 725], [651, 734], [647, 809], [651, 825],
[651, 873], [647, 900], [652, 917], [651, 944], [652, 742], [648, 811],
[651, 994], [652, 783], [650, 911], [654, 879]])
# estimate parameters of real data
model = EllipseModel()
model.estimate(data)
# test whether estimated parameters are smaller then 1000, so means stable
assert_array_less(np.abs(model.params[:4]), np.array([2e3] * 4))
@xfail(condition=arch32,
reason=('Known test failure on 32-bit platforms. See links for '
'details: '
'https://github.com/scikit-image/scikit-image/issues/3091 '
'https://github.com/scikit-image/scikit-image/issues/2670'))
def test_ellipse_model_estimate_failers():
# estimate parameters of real data
model = EllipseModel()
assert not model.estimate(np.ones((5, 2)))
assert not model.estimate(np.array([[50, 80], [51, 81], [52, 80]]))
def test_ellipse_model_residuals():
model = EllipseModel()
# vertical line through origin
model.params = (0, 0, 10, 5, 0)
assert_almost_equal(abs(model.residuals(np.array([[10, 0]]))), 0)
assert_almost_equal(abs(model.residuals(np.array([[0, 5]]))), 0)
assert_almost_equal(abs(model.residuals(np.array([[0, 10]]))), 5)
def test_ransac_shape():
# generate original data without noise
model0 = CircleModel()
model0.params = (10, 12, 3)
t = np.linspace(0, 2 * np.pi, 1000)
data0 = model0.predict_xy(t)
# add some faulty data
outliers = (10, 30, 200)
data0[outliers[0], :] = (1000, 1000)
data0[outliers[1], :] = (-50, 50)
data0[outliers[2], :] = (-100, -10)
# estimate parameters of corrupted data
model_est, inliers = ransac(data0, CircleModel, 3, 5, random_state=1)
# test whether estimated parameters equal original parameters
assert_almost_equal(model0.params, model_est.params)
for outlier in outliers:
assert outlier not in inliers
def test_ransac_geometric():
random_state = np.random.RandomState(1)
# generate original data without noise
src = 100 * random_state.random_sample((50, 2))
model0 = AffineTransform(scale=(0.5, 0.3), rotation=1,
translation=(10, 20))
dst = model0(src)
# add some faulty data
outliers = (0, 5, 20)
dst[outliers[0]] = (10000, 10000)
dst[outliers[1]] = (-100, 100)
dst[outliers[2]] = (50, 50)
# estimate parameters of corrupted data
model_est, inliers = ransac((src, dst), AffineTransform, 2, 20,
random_state=random_state)
# test whether estimated parameters equal original parameters
assert_almost_equal(model0.params, model_est.params)
assert np.all(np.nonzero(inliers == False)[0] == outliers)
def test_ransac_is_data_valid():
def is_data_valid(data):
return data.shape[0] > 2
model, inliers = ransac(np.empty((10, 2)), LineModelND, 2, np.inf,
is_data_valid=is_data_valid, random_state=1)
assert_equal(model, None)
assert_equal(inliers, None)
def test_ransac_is_model_valid():
def is_model_valid(model, data):
return False
model, inliers = ransac(np.empty((10, 2)), LineModelND, 2, np.inf,
is_model_valid=is_model_valid, random_state=1)
assert_equal(model, None)
assert_equal(inliers, None)
def test_ransac_dynamic_max_trials():
# Numbers hand-calculated and confirmed on page 119 (Table 4.3) in
# Hartley, R.~I. and Zisserman, A., 2004,
# Multiple View Geometry in Computer Vision, Second Edition,
# Cambridge University Press, ISBN: 0521540518
# e = 0%, min_samples = X
assert_equal(_dynamic_max_trials(100, 100, 2, 0.99), 1)
# e = 5%, min_samples = 2
assert_equal(_dynamic_max_trials(95, 100, 2, 0.99), 2)
# e = 10%, min_samples = 2
assert_equal(_dynamic_max_trials(90, 100, 2, 0.99), 3)
# e = 30%, min_samples = 2
assert_equal(_dynamic_max_trials(70, 100, 2, 0.99), 7)
# e = 50%, min_samples = 2
assert_equal(_dynamic_max_trials(50, 100, 2, 0.99), 17)
# e = 5%, min_samples = 8
assert_equal(_dynamic_max_trials(95, 100, 8, 0.99), 5)
# e = 10%, min_samples = 8
assert_equal(_dynamic_max_trials(90, 100, 8, 0.99), 9)
# e = 30%, min_samples = 8
assert_equal(_dynamic_max_trials(70, 100, 8, 0.99), 78)
# e = 50%, min_samples = 8
assert_equal(_dynamic_max_trials(50, 100, 8, 0.99), 1177)
# e = 0%, min_samples = 5
assert_equal(_dynamic_max_trials(1, 100, 5, 0), 0)
assert_equal(_dynamic_max_trials(1, 100, 5, 1), np.inf)
def test_ransac_invalid_input():
# `residual_threshold` must be greater than zero
with testing.raises(ValueError):
ransac(np.zeros((10, 2)), None, min_samples=2,
residual_threshold=-0.5)
# "`max_trials` must be greater than zero"
with testing.raises(ValueError):
ransac(np.zeros((10, 2)), None, min_samples=2,
residual_threshold=0, max_trials=-1)
# `stop_probability` must be in range (0, 1)
with testing.raises(ValueError):
ransac(np.zeros((10, 2)), None, min_samples=2,
residual_threshold=0, stop_probability=-1)
# `stop_probability` must be in range (0, 1)
with testing.raises(ValueError):
ransac(np.zeros((10, 2)), None, min_samples=2,
residual_threshold=0, stop_probability=1.01)
# `min_samples` as ratio must be in range (0, nb)
with testing.raises(ValueError):
ransac(np.zeros((10, 2)), None, min_samples=0,
residual_threshold=0)
# `min_samples` as ratio must be in range (0, nb)
with testing.raises(ValueError):
ransac(np.zeros((10, 2)), None, min_samples=10,
residual_threshold=0)
# `min_samples` must be greater than zero
with testing.raises(ValueError):
ransac(np.zeros((10, 2)), None, min_samples=-1,
residual_threshold=0)
def test_ransac_sample_duplicates():
class DummyModel(object):
"""Dummy model to check for duplicates."""
def estimate(self, data):
# Assert that all data points are unique.
assert_equal(np.unique(data).size, data.size)
return True
def residuals(self, data):
return np.ones(len(data), dtype=np.double)
# Create dataset with four unique points. Force 10 iterations
# and check that there are no duplicated data points.
data = np.arange(4)
ransac(data, DummyModel, min_samples=3, residual_threshold=0.0,
max_trials=10)

View file

@ -0,0 +1,180 @@
import numpy as np
from skimage.draw import ellipsoid, ellipsoid_stats
from skimage.measure import marching_cubes, mesh_surface_area
from skimage._shared import testing
from skimage._shared.testing import assert_array_equal
import pytest
def test_marching_cubes_isotropic():
ellipsoid_isotropic = ellipsoid(6, 10, 16, levelset=True)
_, surf = ellipsoid_stats(6, 10, 16)
# Classic
verts, faces = marching_cubes(ellipsoid_isotropic, 0., method='_lorensen')
surf_calc = mesh_surface_area(verts, faces)
# Test within 1% tolerance for isotropic. Will always underestimate.
assert surf > surf_calc and surf_calc > surf * 0.99
# Lewiner
verts, faces = marching_cubes(ellipsoid_isotropic, 0.)[:2]
surf_calc = mesh_surface_area(verts, faces)
# Test within 1% tolerance for isotropic. Will always underestimate.
assert surf > surf_calc and surf_calc > surf * 0.99
def test_marching_cubes_anisotropic():
# test spacing as numpy array (and not just tuple)
spacing = np.array([1., 10 / 6., 16 / 6.])
ellipsoid_anisotropic = ellipsoid(6, 10, 16, spacing=spacing,
levelset=True)
_, surf = ellipsoid_stats(6, 10, 16)
# Classic
verts, faces = marching_cubes(ellipsoid_anisotropic, 0.,
spacing=spacing, method='_lorensen')
surf_calc = mesh_surface_area(verts, faces)
# Test within 1.5% tolerance for anisotropic. Will always underestimate.
assert surf > surf_calc and surf_calc > surf * 0.985
# Lewiner
verts, faces = marching_cubes(ellipsoid_anisotropic, 0.,
spacing=spacing)[:2]
surf_calc = mesh_surface_area(verts, faces)
# Test within 1.5% tolerance for anisotropic. Will always underestimate.
assert surf > surf_calc and surf_calc > surf * 0.985
# Test marching cube with mask
with pytest.raises(ValueError):
verts, faces = marching_cubes(
ellipsoid_anisotropic, 0., spacing=spacing,
mask=np.array([]))[:2]
# Test spacing together with allow_degenerate=False
marching_cubes(ellipsoid_anisotropic, 0, spacing=spacing,
allow_degenerate=False)
def test_invalid_input():
# Classic
with testing.raises(ValueError):
marching_cubes(np.zeros((2, 2, 1)), 0, method='_lorensen')
with testing.raises(ValueError):
marching_cubes(np.zeros((2, 2, 1)), 1, method='_lorensen')
with testing.raises(ValueError):
marching_cubes(np.ones((3, 3, 3)), 1, spacing=(1, 2), method='_lorensen')
with testing.raises(ValueError):
marching_cubes(np.zeros((20, 20)), 0, method='_lorensen')
# Lewiner
with testing.raises(ValueError):
marching_cubes(np.zeros((2, 2, 1)), 0)
with testing.raises(ValueError):
marching_cubes(np.zeros((2, 2, 1)), 1)
with testing.raises(ValueError):
marching_cubes(np.ones((3, 3, 3)), 1, spacing=(1, 2))
with testing.raises(ValueError):
marching_cubes(np.zeros((20, 20)), 0)
def test_both_algs_same_result_ellipse():
# Performing this test on data that does not have ambiguities
sphere_small = ellipsoid(1, 1, 1, levelset=True)
vertices1, faces1 = marching_cubes(sphere_small, 0, method='_lorensen')[:2]
vertices2, faces2 = marching_cubes(sphere_small, 0,
allow_degenerate=False)[:2]
vertices3, faces3 = marching_cubes(sphere_small, 0,
allow_degenerate=False,
method='lorensen')[:2]
# Order is different, best we can do is test equal shape and same
# vertices present
assert _same_mesh(vertices1, faces1, vertices2, faces2)
assert _same_mesh(vertices1, faces1, vertices3, faces3)
def _same_mesh(vertices1, faces1, vertices2, faces2, tol=1e-10):
""" Compare two meshes, using a certain tolerance and invariant to
the order of the faces.
"""
# Unwind vertices
triangles1 = vertices1[np.array(faces1)]
triangles2 = vertices2[np.array(faces2)]
# Sort vertices within each triangle
triang1 = [np.concatenate(sorted(t, key=lambda x:tuple(x)))
for t in triangles1]
triang2 = [np.concatenate(sorted(t, key=lambda x:tuple(x)))
for t in triangles2]
# Sort the resulting 9-element "tuples"
triang1 = np.array(sorted([tuple(x) for x in triang1]))
triang2 = np.array(sorted([tuple(x) for x in triang2]))
return (triang1.shape == triang2.shape and
np.allclose(triang1, triang2, 0, tol))
def test_both_algs_same_result_donut():
# Performing this test on data that does not have ambiguities
n = 48
a, b = 2.5/n, -1.25
vol = np.empty((n, n, n), 'float32')
for iz in range(vol.shape[0]):
for iy in range(vol.shape[1]):
for ix in range(vol.shape[2]):
# Double-torii formula by Thomas Lewiner
z, y, x = float(iz)*a+b, float(iy)*a+b, float(ix)*a+b
vol[iz,iy,ix] = ( (
(8*x)**2 + (8*y-2)**2 + (8*z)**2 + 16 - 1.85*1.85 ) * ( (8*x)**2 +
(8*y-2)**2 + (8*z)**2 + 16 - 1.85*1.85 ) - 64 * ( (8*x)**2 + (8*y-2)**2 )
) * ( ( (8*x)**2 + ((8*y-2)+4)*((8*y-2)+4) + (8*z)**2 + 16 - 1.85*1.85 )
* ( (8*x)**2 + ((8*y-2)+4)*((8*y-2)+4) + (8*z)**2 + 16 - 1.85*1.85 ) -
64 * ( ((8*y-2)+4)*((8*y-2)+4) + (8*z)**2
) ) + 1025
vertices1, faces1 = marching_cubes(vol, 0, method='_lorensen')[:2]
vertices2, faces2 = marching_cubes(vol, 0)[:2]
vertices3, faces3 = marching_cubes(vol, 0, method='lorensen')[:2]
# Old and new alg are different
assert not _same_mesh(vertices1, faces1, vertices2, faces2)
# New classic and new Lewiner are different
assert not _same_mesh(vertices2, faces2, vertices3, faces3)
# Would have been nice if old and new classic would have been the same
# assert _same_mesh(vertices1, faces1, vertices3, faces3, 5)
def test_masked_marching_cubes():
ellipsoid_scalar = ellipsoid(6, 10, 16, levelset=True)
mask = np.ones_like(ellipsoid_scalar, dtype=bool)
mask[:10, :, :] = False
mask[:, :, 20:] = False
ver, faces, _, _ = marching_cubes(ellipsoid_scalar, 0, mask=mask)
area = mesh_surface_area(ver, faces)
np.testing.assert_allclose(area, 299.56878662109375, rtol=.01)
def test_masked_marching_cubes_empty():
ellipsoid_scalar = ellipsoid(6, 10, 16, levelset=True)
mask = np.array([])
with pytest.raises(ValueError):
_ = marching_cubes(ellipsoid_scalar, 0, mask=mask)
def test_masked_marching_cubes_old_lewiner():
ellipsoid_scalar = ellipsoid(6, 10, 16, levelset=True)
mask = np.array([])
with pytest.raises(NotImplementedError):
_ = marching_cubes(ellipsoid_scalar, 0, mask=mask, method='_lorensen')
def test_masked_marching_cubes_all_true():
ellipsoid_scalar = ellipsoid(6, 10, 16, levelset=True)
mask = np.ones_like(ellipsoid_scalar, dtype=bool)
ver_m, faces_m, _, _ = marching_cubes(ellipsoid_scalar, 0, mask=mask)
ver, faces, _, _ = marching_cubes(ellipsoid_scalar, 0, mask=mask)
np.testing.assert_allclose(ver_m, ver, rtol=.00001)
np.testing.assert_allclose(faces_m, faces, rtol=.00001)

View file

@ -0,0 +1,187 @@
import numpy as np
from scipy import ndimage as ndi
from skimage import draw
from skimage.measure import (moments, moments_central, moments_coords,
moments_coords_central, moments_normalized,
moments_hu, centroid, inertia_tensor,
inertia_tensor_eigvals)
from skimage._shared import testing
from skimage._shared.testing import (assert_equal, assert_almost_equal,
assert_allclose)
from skimage._shared._warnings import expected_warnings
def test_moments():
image = np.zeros((20, 20), dtype=np.double)
image[14, 14] = 1
image[15, 15] = 1
image[14, 15] = 0.5
image[15, 14] = 0.5
m = moments(image)
assert_equal(m[0, 0], 3)
assert_almost_equal(m[1, 0] / m[0, 0], 14.5)
assert_almost_equal(m[0, 1] / m[0, 0], 14.5)
def test_moments_central():
image = np.zeros((20, 20), dtype=np.double)
image[14, 14] = 1
image[15, 15] = 1
image[14, 15] = 0.5
image[15, 14] = 0.5
mu = moments_central(image, (14.5, 14.5))
# check for proper centroid computation
mu_calc_centroid = moments_central(image)
assert_equal(mu, mu_calc_centroid)
# shift image by dx=2, dy=2
image2 = np.zeros((20, 20), dtype=np.double)
image2[16, 16] = 1
image2[17, 17] = 1
image2[16, 17] = 0.5
image2[17, 16] = 0.5
mu2 = moments_central(image2, (14.5 + 2, 14.5 + 2))
# central moments must be translation invariant
assert_equal(mu, mu2)
def test_moments_coords():
image = np.zeros((20, 20), dtype=np.double)
image[13:17, 13:17] = 1
mu_image = moments(image)
coords = np.array([[r, c] for r in range(13, 17)
for c in range(13, 17)], dtype=np.double)
mu_coords = moments_coords(coords)
assert_almost_equal(mu_coords, mu_image)
def test_moments_central_coords():
image = np.zeros((20, 20), dtype=np.double)
image[13:17, 13:17] = 1
mu_image = moments_central(image, (14.5, 14.5))
coords = np.array([[r, c] for r in range(13, 17)
for c in range(13, 17)], dtype=np.double)
mu_coords = moments_coords_central(coords, (14.5, 14.5))
assert_almost_equal(mu_coords, mu_image)
# ensure that center is being calculated normally
mu_coords_calc_centroid = moments_coords_central(coords)
assert_almost_equal(mu_coords_calc_centroid, mu_coords)
# shift image by dx=3 dy=3
image = np.zeros((20, 20), dtype=np.double)
image[16:20, 16:20] = 1
mu_image = moments_central(image, (14.5, 14.5))
coords = np.array([[r, c] for r in range(16, 20)
for c in range(16, 20)], dtype=np.double)
mu_coords = moments_coords_central(coords, (14.5, 14.5))
assert_almost_equal(mu_coords, mu_image)
def test_moments_normalized():
image = np.zeros((20, 20), dtype=np.double)
image[13:17, 13:17] = 1
mu = moments_central(image, (14.5, 14.5))
nu = moments_normalized(mu)
# shift image by dx=-3, dy=-3 and scale by 0.5
image2 = np.zeros((20, 20), dtype=np.double)
image2[11:13, 11:13] = 1
mu2 = moments_central(image2, (11.5, 11.5))
nu2 = moments_normalized(mu2)
# central moments must be translation and scale invariant
assert_almost_equal(nu, nu2, decimal=1)
def test_moments_normalized_3d():
image = draw.ellipsoid(1, 1, 10)
mu_image = moments_central(image)
nu = moments_normalized(mu_image)
assert nu[0, 0, 2] > nu[0, 2, 0]
assert_almost_equal(nu[0, 2, 0], nu[2, 0, 0])
coords = np.where(image)
mu_coords = moments_coords_central(coords)
assert_almost_equal(mu_coords, mu_image)
def test_moments_normalized_invalid():
with testing.raises(ValueError):
moments_normalized(np.zeros((3, 3)), 3)
with testing.raises(ValueError):
moments_normalized(np.zeros((3, 3)), 4)
def test_moments_hu():
image = np.zeros((20, 20), dtype=np.double)
image[13:15, 13:17] = 1
mu = moments_central(image, (13.5, 14.5))
nu = moments_normalized(mu)
hu = moments_hu(nu)
# shift image by dx=2, dy=3, scale by 0.5 and rotate by 90deg
image2 = np.zeros((20, 20), dtype=np.double)
image2[11, 11:13] = 1
image2 = image2.T
mu2 = moments_central(image2, (11.5, 11))
nu2 = moments_normalized(mu2)
hu2 = moments_hu(nu2)
# central moments must be translation and scale invariant
assert_almost_equal(hu, hu2, decimal=1)
def test_centroid():
image = np.zeros((20, 20), dtype=np.double)
image[14, 14:16] = 1
image[15, 14:16] = 1/3
image_centroid = centroid(image)
assert_allclose(image_centroid, (14.25, 14.5))
def test_inertia_tensor_2d():
image = np.zeros((40, 40))
image[15:25, 5:35] = 1 # big horizontal rectangle (aligned with axis 1)
T = inertia_tensor(image)
assert T[0, 0] > T[1, 1]
np.testing.assert_allclose(T[0, 1], 0)
v0, v1 = inertia_tensor_eigvals(image, T=T)
np.testing.assert_allclose(np.sqrt(v0/v1), 3, rtol=0.01, atol=0.05)
def test_inertia_tensor_3d():
image = draw.ellipsoid(10, 5, 3)
T0 = inertia_tensor(image)
eig0, V0 = np.linalg.eig(T0)
# principal axis of ellipse = eigenvector of smallest eigenvalue
v0 = V0[:, np.argmin(eig0)]
assert np.allclose(v0, [1, 0, 0]) or np.allclose(-v0, [1, 0, 0])
imrot = ndi.rotate(image.astype(float), 30, axes=(0, 1), order=1)
Tr = inertia_tensor(imrot)
eigr, Vr = np.linalg.eig(Tr)
vr = Vr[:, np.argmin(eigr)]
# Check that axis has rotated by expected amount
pi, cos, sin = np.pi, np.cos, np.sin
R = np.array([[ cos(pi/6), -sin(pi/6), 0],
[ sin(pi/6), cos(pi/6), 0],
[ 0, 0, 1]])
expected_vr = R @ v0
assert (np.allclose(vr, expected_vr, atol=1e-3, rtol=0.01) or
np.allclose(-vr, expected_vr, atol=1e-3, rtol=0.01))
def test_inertia_tensor_eigvals():
# Floating point precision problems could make a positive
# semidefinite matrix have an eigenvalue that is very slightly
# negative. Check that we have caught and fixed this problem.
image = np.array([[1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]])
# mu = np.array([[3, 0, 98], [0, 14, 0], [2, 0, 98]])
eigvals = inertia_tensor_eigvals(image=image)
assert (min(eigvals) >= 0)

View file

@ -0,0 +1,35 @@
import numpy as np
from skimage.measure import points_in_poly, grid_points_in_poly
from skimage._shared.testing import assert_array_equal
class TestNpnpoly():
def test_square(self):
v = np.array([[0, 0],
[0, 1],
[1, 1],
[1, 0]])
assert(points_in_poly([[0.5, 0.5]], v)[0])
assert(not points_in_poly([[-0.1, 0.1]], v)[0])
def test_triangle(self):
v = np.array([[0, 0],
[1, 0],
[0.5, 0.75]])
assert(points_in_poly([[0.5, 0.7]], v)[0])
assert(not points_in_poly([[0.5, 0.76]], v)[0])
assert(not points_in_poly([[0.7, 0.5]], v)[0])
def test_type(self):
assert(points_in_poly([[0, 0]], [[0, 0]]).dtype == np.bool)
def test_grid_points_in_poly():
v = np.array([[0, 0],
[5, 0],
[5, 5]])
expected = np.tril(np.ones((5, 5), dtype=bool))
assert_array_equal(grid_points_in_poly((5, 5), v), expected)

View file

@ -0,0 +1,64 @@
import numpy as np
from skimage.measure import approximate_polygon, subdivide_polygon
from skimage.measure._polygon import _SUBDIVISION_MASKS
from skimage._shared import testing
from skimage._shared.testing import assert_array_equal, assert_equal
square = np.array([
[0, 0], [0, 1], [0, 2], [0, 3],
[1, 3], [2, 3], [3, 3],
[3, 2], [3, 1], [3, 0],
[2, 0], [1, 0], [0, 0]
])
def test_approximate_polygon():
out = approximate_polygon(square, 0.1)
assert_array_equal(out, square[(0, 3, 6, 9, 12), :])
out = approximate_polygon(square, 2.2)
assert_array_equal(out, square[(0, 6, 12), :])
out = approximate_polygon(square[(0, 1, 3, 4, 5, 6, 7, 9, 11, 12), :], 0.1)
assert_array_equal(out, square[(0, 3, 6, 9, 12), :])
out = approximate_polygon(square, -1)
assert_array_equal(out, square)
out = approximate_polygon(square, 0)
assert_array_equal(out, square)
def test_subdivide_polygon():
new_square1 = square
new_square2 = square[:-1]
new_square3 = square[:-1]
# test iterative subdvision
for _ in range(10):
square1, square2, square3 = new_square1, new_square2, new_square3
# test different B-Spline degrees
for degree in range(1, 7):
mask_len = len(_SUBDIVISION_MASKS[degree][0])
# test circular
new_square1 = subdivide_polygon(square1, degree)
assert_array_equal(new_square1[-1], new_square1[0])
assert_equal(new_square1.shape[0],
2 * square1.shape[0] - 1)
# test non-circular
new_square2 = subdivide_polygon(square2, degree)
assert_equal(new_square2.shape[0],
2 * (square2.shape[0] - mask_len + 1))
# test non-circular, preserve_ends
new_square3 = subdivide_polygon(square3, degree, True)
assert_equal(new_square3[0], square3[0])
assert_equal(new_square3[-1], square3[-1])
assert_equal(new_square3.shape[0],
2 * (square3.shape[0] - mask_len + 2))
# not supported B-Spline degree
with testing.raises(ValueError):
subdivide_polygon(square, 0)
with testing.raises(ValueError):
subdivide_polygon(square, 8)

View file

@ -0,0 +1,214 @@
import numpy as np
from ..._shared.testing import assert_equal, assert_almost_equal
from ..._shared._warnings import expected_warnings
from ..profile import profile_line
image = np.arange(100).reshape((10, 10)).astype(np.float)
def test_horizontal_rightward():
prof = profile_line(image, (0, 2), (0, 8), order=0, mode='constant')
expected_prof = np.arange(2, 9)
assert_equal(prof, expected_prof)
def test_horizontal_leftward():
prof = profile_line(image, (0, 8), (0, 2), order=0, mode='constant')
expected_prof = np.arange(8, 1, -1)
assert_equal(prof, expected_prof)
def test_vertical_downward():
prof = profile_line(image, (2, 5), (8, 5), order=0, mode='constant')
expected_prof = np.arange(25, 95, 10)
assert_equal(prof, expected_prof)
def test_vertical_upward():
prof = profile_line(image, (8, 5), (2, 5), order=0, mode='constant')
expected_prof = np.arange(85, 15, -10)
assert_equal(prof, expected_prof)
def test_45deg_right_downward():
prof = profile_line(image, (2, 2), (8, 8), order=0, mode='constant')
expected_prof = np.array([22, 33, 33, 44, 55, 55, 66, 77, 77, 88])
# repeats are due to aliasing using nearest neighbor interpolation.
# to see this, imagine a diagonal line with markers every unit of
# length traversing a checkerboard pattern of squares also of unit
# length. Because the line is diagonal, sometimes more than one
# marker will fall on the same checkerboard box.
assert_almost_equal(prof, expected_prof)
def test_45deg_right_downward_interpolated():
prof = profile_line(image, (2, 2), (8, 8), order=1, mode='constant')
expected_prof = np.linspace(22, 88, 10)
assert_almost_equal(prof, expected_prof)
def test_45deg_right_upward():
prof = profile_line(image, (8, 2), (2, 8), order=1, mode='constant')
expected_prof = np.arange(82, 27, -6)
assert_almost_equal(prof, expected_prof)
def test_45deg_left_upward():
prof = profile_line(image, (8, 8), (2, 2), order=1, mode='constant')
expected_prof = np.arange(88, 21, -22. / 3)
assert_almost_equal(prof, expected_prof)
def test_45deg_left_downward():
prof = profile_line(image, (2, 8), (8, 2), order=1, mode='constant')
expected_prof = np.arange(28, 83, 6)
assert_almost_equal(prof, expected_prof)
def test_pythagorean_triangle_right_downward():
prof = profile_line(image, (1, 1), (7, 9), order=0, mode='constant')
expected_prof = np.array([11, 22, 23, 33, 34, 45, 56, 57, 67, 68, 79])
assert_equal(prof, expected_prof)
def test_pythagorean_triangle_right_downward_interpolated():
prof = profile_line(image, (1, 1), (7, 9), order=1, mode='constant')
expected_prof = np.linspace(11, 79, 11)
assert_almost_equal(prof, expected_prof)
pyth_image = np.zeros((6, 7), np.float)
line = ((1, 2, 2, 3, 3, 4), (1, 2, 3, 3, 4, 5))
below = ((2, 2, 3, 4, 4, 5), (0, 1, 2, 3, 4, 4))
above = ((0, 1, 1, 2, 3, 3), (2, 2, 3, 4, 5, 6))
pyth_image[line] = 1.8
pyth_image[below] = 0.6
pyth_image[above] = 0.6
def test_pythagorean_triangle_right_downward_linewidth():
prof = profile_line(pyth_image, (1, 1), (4, 5), linewidth=3, order=0,
mode='constant')
expected_prof = np.ones(6)
assert_almost_equal(prof, expected_prof)
def test_pythagorean_triangle_right_upward_linewidth():
prof = profile_line(pyth_image[::-1, :], (4, 1), (1, 5),
linewidth=3, order=0, mode='constant')
expected_prof = np.ones(6)
assert_almost_equal(prof, expected_prof)
def test_pythagorean_triangle_transpose_left_down_linewidth():
prof = profile_line(pyth_image.T[:, ::-1], (1, 4), (5, 1),
linewidth=3, order=0, mode='constant')
expected_prof = np.ones(6)
assert_almost_equal(prof, expected_prof)
def test_reduce_func_mean():
prof = profile_line(pyth_image, (0, 1), (3, 1), linewidth=3, order=0,
reduce_func=np.mean, mode='reflect')
expected_prof = pyth_image[:4, :3].mean(1)
assert_almost_equal(prof, expected_prof)
def test_reduce_func_max():
prof = profile_line(pyth_image, (0, 1), (3, 1), linewidth=3, order=0,
reduce_func=np.max, mode='reflect')
expected_prof = pyth_image[:4, :3].max(1)
assert_almost_equal(prof, expected_prof)
def test_reduce_func_sum():
prof = profile_line(pyth_image, (0, 1), (3, 1), linewidth=3, order=0,
reduce_func=np.sum, mode='reflect')
expected_prof = pyth_image[:4, :3].sum(1)
assert_almost_equal(prof, expected_prof)
def test_reduce_func_mean_linewidth_1():
prof = profile_line(pyth_image, (0, 1), (3, 1), linewidth=1, order=0,
reduce_func=np.mean, mode='constant')
expected_prof = pyth_image[:4, 1]
assert_almost_equal(prof, expected_prof)
def test_reduce_func_None_linewidth_1():
prof = profile_line(pyth_image, (1, 2), (4, 2), linewidth=1,
order=0, reduce_func=None, mode='constant')
expected_prof = pyth_image[1:5, 2, np.newaxis]
assert_almost_equal(prof, expected_prof)
def test_reduce_func_None_linewidth_3():
prof = profile_line(pyth_image, (1, 2), (4, 2), linewidth=3,
order=0, reduce_func=None, mode='constant')
expected_prof = pyth_image[1:5, 1:4]
assert_almost_equal(prof, expected_prof)
def test_reduce_func_lambda_linewidth_3():
def reduce_func(x):
return x + x ** 2
prof = profile_line(pyth_image, (1, 2), (4, 2), linewidth=3, order=0,
reduce_func=reduce_func, mode='constant')
expected_prof = np.apply_along_axis(reduce_func,
arr=pyth_image[1:5, 1:4], axis=1)
assert_almost_equal(prof, expected_prof)
def test_reduce_func_sqrt_linewidth_3():
def reduce_func(x):
return x ** 0.5
prof = profile_line(pyth_image, (1, 2), (4, 2), linewidth=3,
order=0, reduce_func=reduce_func,
mode='constant')
expected_prof = np.apply_along_axis(reduce_func,
arr=pyth_image[1:5, 1:4], axis=1)
assert_almost_equal(prof, expected_prof)
def test_reduce_func_sumofsqrt_linewidth_3():
def reduce_func(x):
return np.sum(x ** 0.5)
prof = profile_line(pyth_image, (1, 2), (4, 2), linewidth=3, order=0,
reduce_func=reduce_func, mode='constant')
expected_prof = np.apply_along_axis(reduce_func,
arr=pyth_image[1:5, 1:4], axis=1)
assert_almost_equal(prof, expected_prof)
def test_oob_coodinates():
offset = 2
idx = pyth_image.shape[0] + offset
prof = profile_line(pyth_image, (-offset, 2), (idx, 2), linewidth=1,
order=0, reduce_func=None, mode='constant')
expected_prof = np.vstack([np.zeros((offset, 1)),
pyth_image[:, 2, np.newaxis],
np.zeros((offset + 1, 1))])
assert_almost_equal(prof, expected_prof)
def test_bool_array_input():
shape = (200, 200)
center_x, center_y = (140, 150)
radius = 20
x, y = np.meshgrid(range(shape[1]), range(shape[0]))
mask = (y - center_y) ** 2 + (x - center_x) ** 2 < radius ** 2
src = (center_y, center_x)
phi = 4 * np.pi / 9.
dy = 31 * np.cos(phi)
dx = 31 * np.sin(phi)
dst = (center_y + dy, center_x + dx)
profile_u8 = profile_line(mask.astype(np.uint8), src, dst)
assert all(profile_u8[:radius] == 1)
profile_b = profile_line(mask, src, dst)
assert all(profile_b[:radius] == 1)
assert all(profile_b == profile_u8)

View file

@ -0,0 +1,575 @@
import math
import numpy as np
from numpy import array
from skimage._shared._warnings import expected_warnings
from skimage.measure._regionprops import (regionprops, PROPS, perimeter,
_parse_docs, _props_to_dict,
regionprops_table, OBJECT_COLUMNS,
COL_DTYPES)
from skimage._shared import testing
from skimage._shared.testing import (assert_array_equal, assert_almost_equal,
assert_array_almost_equal, assert_equal)
SAMPLE = np.array(
[[0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0],
[0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0],
[1, 0, 1, 0, 0, 1, 1, 0, 1, 1, 0, 0, 1, 1, 1, 1, 1, 0],
[0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1],
[0, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1]]
)
INTENSITY_SAMPLE = SAMPLE.copy()
INTENSITY_SAMPLE[1, 9:11] = 2
SAMPLE_3D = np.zeros((6, 6, 6), dtype=np.uint8)
SAMPLE_3D[1:3, 1:3, 1:3] = 1
SAMPLE_3D[3, 2, 2] = 1
INTENSITY_SAMPLE_3D = SAMPLE_3D.copy()
def test_all_props():
region = regionprops(SAMPLE, INTENSITY_SAMPLE)[0]
for prop in PROPS:
try:
assert_almost_equal(region[prop], getattr(region, PROPS[prop]))
except TypeError: # the `slice` property causes this
pass
def test_all_props_3d():
region = regionprops(SAMPLE_3D, INTENSITY_SAMPLE_3D)[0]
for prop in PROPS:
try:
assert_almost_equal(region[prop], getattr(region, PROPS[prop]))
except (NotImplementedError, TypeError):
pass
def test_dtype():
regionprops(np.zeros((10, 10), dtype=np.int))
regionprops(np.zeros((10, 10), dtype=np.uint))
with testing.raises(TypeError):
regionprops(np.zeros((10, 10), dtype=np.float))
with testing.raises(TypeError):
regionprops(np.zeros((10, 10), dtype=np.double))
with testing.raises(TypeError):
regionprops(np.zeros((10, 10), dtype=np.bool))
def test_ndim():
regionprops(np.zeros((10, 10), dtype=np.int))
regionprops(np.zeros((10, 10, 1), dtype=np.int))
regionprops(np.zeros((10, 10, 10), dtype=np.int))
regionprops(np.zeros((1, 1), dtype=np.int))
regionprops(np.zeros((1, 1, 1), dtype=np.int))
with testing.raises(TypeError):
regionprops(np.zeros((10, 10, 10, 2), dtype=np.int))
def test_area():
area = regionprops(SAMPLE)[0].area
assert area == np.sum(SAMPLE)
area = regionprops(SAMPLE_3D)[0].area
assert area == np.sum(SAMPLE_3D)
def test_bbox():
bbox = regionprops(SAMPLE)[0].bbox
assert_array_almost_equal(bbox, (0, 0, SAMPLE.shape[0], SAMPLE.shape[1]))
SAMPLE_mod = SAMPLE.copy()
SAMPLE_mod[:, -1] = 0
bbox = regionprops(SAMPLE_mod)[0].bbox
assert_array_almost_equal(bbox, (0, 0, SAMPLE.shape[0], SAMPLE.shape[1]-1))
bbox = regionprops(SAMPLE_3D)[0].bbox
assert_array_almost_equal(bbox, (1, 1, 1, 4, 3, 3))
def test_bbox_area():
padded = np.pad(SAMPLE, 5, mode='constant')
bbox_area = regionprops(padded)[0].bbox_area
assert_array_almost_equal(bbox_area, SAMPLE.size)
def test_moments_central():
mu = regionprops(SAMPLE)[0].moments_central
# determined with OpenCV
assert_almost_equal(mu[2, 0], 436.00000000000045)
# different from OpenCV results, bug in OpenCV
assert_almost_equal(mu[3, 0], -737.333333333333)
assert_almost_equal(mu[1, 1], -87.33333333333303)
assert_almost_equal(mu[2, 1], -127.5555555555593)
assert_almost_equal(mu[0, 2], 1259.7777777777774)
assert_almost_equal(mu[1, 2], 2000.296296296291)
assert_almost_equal(mu[0, 3], -760.0246913580195)
def test_centroid():
centroid = regionprops(SAMPLE)[0].centroid
# determined with MATLAB
assert_array_almost_equal(centroid, (5.66666666666666, 9.444444444444444))
def test_centroid_3d():
centroid = regionprops(SAMPLE_3D)[0].centroid
# determined by mean along axis 1 of SAMPLE_3D.nonzero()
assert_array_almost_equal(centroid, (1.66666667, 1.55555556, 1.55555556))
def test_convex_area():
area = regionprops(SAMPLE)[0].convex_area
# determined with MATLAB
assert area == 124
def test_convex_image():
img = regionprops(SAMPLE)[0].convex_image
# determined with MATLAB
ref = np.array(
[[0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0],
[0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0],
[0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0],
[0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0],
[0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0],
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
[0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
[0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]]
)
assert_array_equal(img, ref)
def test_coordinates():
sample = np.zeros((10, 10), dtype=np.int8)
coords = np.array([[3, 2], [3, 3], [3, 4]])
sample[coords[:, 0], coords[:, 1]] = 1
prop_coords = regionprops(sample)[0].coords
assert_array_equal(prop_coords, coords)
sample = np.zeros((6, 6, 6), dtype=np.int8)
coords = np.array([[1, 1, 1], [1, 2, 1], [1, 3, 1]])
sample[coords[:, 0], coords[:, 1], coords[:, 2]] = 1
prop_coords = regionprops(sample)[0].coords
assert_array_equal(prop_coords, coords)
def test_slice():
padded = np.pad(SAMPLE, ((2, 4), (5, 2)), mode='constant')
nrow, ncol = SAMPLE.shape
result = regionprops(padded)[0].slice
expected = (slice(2, 2+nrow), slice(5, 5+ncol))
assert_equal(result, expected)
def test_eccentricity():
eps = regionprops(SAMPLE)[0].eccentricity
assert_almost_equal(eps, 0.814629313427)
img = np.zeros((5, 5), dtype=np.int)
img[2, 2] = 1
eps = regionprops(img)[0].eccentricity
assert_almost_equal(eps, 0)
def test_equiv_diameter():
diameter = regionprops(SAMPLE)[0].equivalent_diameter
# determined with MATLAB
assert_almost_equal(diameter, 9.57461472963)
def test_euler_number():
en = regionprops(SAMPLE)[0].euler_number
assert en == 1
SAMPLE_mod = SAMPLE.copy()
SAMPLE_mod[7, -3] = 0
en = regionprops(SAMPLE_mod)[0].euler_number
assert en == 0
def test_extent():
extent = regionprops(SAMPLE)[0].extent
assert_almost_equal(extent, 0.4)
def test_moments_hu():
hu = regionprops(SAMPLE)[0].moments_hu
ref = np.array([
3.27117627e-01,
2.63869194e-02,
2.35390060e-02,
1.23151193e-03,
1.38882330e-06,
-2.72586158e-05,
-6.48350653e-06
])
# bug in OpenCV caused in Central Moments calculation?
assert_array_almost_equal(hu, ref)
def test_image():
img = regionprops(SAMPLE)[0].image
assert_array_equal(img, SAMPLE)
img = regionprops(SAMPLE_3D)[0].image
assert_array_equal(img, SAMPLE_3D[1:4, 1:3, 1:3])
def test_label():
label = regionprops(SAMPLE)[0].label
assert_array_equal(label, 1)
label = regionprops(SAMPLE_3D)[0].label
assert_array_equal(label, 1)
def test_filled_area():
area = regionprops(SAMPLE)[0].filled_area
assert area == np.sum(SAMPLE)
SAMPLE_mod = SAMPLE.copy()
SAMPLE_mod[7, -3] = 0
area = regionprops(SAMPLE_mod)[0].filled_area
assert area == np.sum(SAMPLE)
def test_filled_image():
img = regionprops(SAMPLE)[0].filled_image
assert_array_equal(img, SAMPLE)
def test_major_axis_length():
length = regionprops(SAMPLE)[0].major_axis_length
# MATLAB has different interpretation of ellipse than found in literature,
# here implemented as found in literature
assert_almost_equal(length, 16.7924234999)
def test_max_intensity():
intensity = regionprops(SAMPLE, intensity_image=INTENSITY_SAMPLE
)[0].max_intensity
assert_almost_equal(intensity, 2)
def test_mean_intensity():
intensity = regionprops(SAMPLE, intensity_image=INTENSITY_SAMPLE
)[0].mean_intensity
assert_almost_equal(intensity, 1.02777777777777)
def test_min_intensity():
intensity = regionprops(SAMPLE, intensity_image=INTENSITY_SAMPLE
)[0].min_intensity
assert_almost_equal(intensity, 1)
def test_minor_axis_length():
length = regionprops(SAMPLE)[0].minor_axis_length
# MATLAB has different interpretation of ellipse than found in literature,
# here implemented as found in literature
assert_almost_equal(length, 9.739302807263)
def test_moments():
m = regionprops(SAMPLE)[0].moments
# determined with OpenCV
assert_almost_equal(m[0, 0], 72.0)
assert_almost_equal(m[0, 1], 680.0)
assert_almost_equal(m[0, 2], 7682.0)
assert_almost_equal(m[0, 3], 95588.0)
assert_almost_equal(m[1, 0], 408.0)
assert_almost_equal(m[1, 1], 3766.0)
assert_almost_equal(m[1, 2], 43882.0)
assert_almost_equal(m[2, 0], 2748.0)
assert_almost_equal(m[2, 1], 24836.0)
assert_almost_equal(m[3, 0], 19776.0)
def test_moments_normalized():
nu = regionprops(SAMPLE)[0].moments_normalized
# determined with OpenCV
assert_almost_equal(nu[0, 2], 0.24301268861454037)
assert_almost_equal(nu[0, 3], -0.017278118992041805)
assert_almost_equal(nu[1, 1], -0.016846707818929982)
assert_almost_equal(nu[1, 2], 0.045473992910668816)
assert_almost_equal(nu[2, 0], 0.08410493827160502)
assert_almost_equal(nu[2, 1], -0.002899800614433943)
def test_orientation():
orient = regionprops(SAMPLE)[0].orientation
# determined with MATLAB
assert_almost_equal(orient, -1.4663278802756865)
# test diagonal regions
diag = np.eye(10, dtype=int)
orient_diag = regionprops(diag)[0].orientation
assert_almost_equal(orient_diag, -math.pi / 4)
orient_diag = regionprops(np.flipud(diag))[0].orientation
assert_almost_equal(orient_diag, math.pi / 4)
orient_diag = regionprops(np.fliplr(diag))[0].orientation
assert_almost_equal(orient_diag, math.pi / 4)
orient_diag = regionprops(np.fliplr(np.flipud(diag)))[0].orientation
assert_almost_equal(orient_diag, -math.pi / 4)
def test_perimeter():
per = regionprops(SAMPLE)[0].perimeter
assert_almost_equal(per, 55.2487373415)
per = perimeter(SAMPLE.astype('double'), neighbourhood=8)
assert_almost_equal(per, 46.8284271247)
def test_solidity():
solidity = regionprops(SAMPLE)[0].solidity
# determined with MATLAB
assert_almost_equal(solidity, 0.580645161290323)
def test_weighted_moments_central():
wmu = regionprops(SAMPLE, intensity_image=INTENSITY_SAMPLE
)[0].weighted_moments_central
ref = np.array(
[[7.4000000000e+01, 3.7303493627e-14, 1.2602837838e+03,
-7.6561796932e+02],
[-2.1316282073e-13, -8.7837837838e+01, 2.1571526662e+03,
-4.2385971907e+03],
[4.7837837838e+02, -1.4801314828e+02, 6.6989799420e+03,
-9.9501164076e+03],
[-7.5943608473e+02, -1.2714707125e+03, 1.5304076361e+04,
-3.3156729271e+04]])
np.set_printoptions(precision=10)
assert_array_almost_equal(wmu, ref)
def test_weighted_centroid():
centroid = regionprops(SAMPLE, intensity_image=INTENSITY_SAMPLE
)[0].weighted_centroid
assert_array_almost_equal(centroid, (5.540540540540, 9.445945945945))
def test_weighted_moments_hu():
whu = regionprops(SAMPLE, intensity_image=INTENSITY_SAMPLE
)[0].weighted_moments_hu
ref = np.array([
3.1750587329e-01,
2.1417517159e-02,
2.3609322038e-02,
1.2565683360e-03,
8.3014209421e-07,
-3.5073773473e-05,
-6.7936409056e-06
])
assert_array_almost_equal(whu, ref)
def test_weighted_moments():
wm = regionprops(SAMPLE, intensity_image=INTENSITY_SAMPLE
)[0].weighted_moments
ref = np.array(
[[7.4000000e+01, 6.9900000e+02, 7.8630000e+03, 9.7317000e+04],
[4.1000000e+02, 3.7850000e+03, 4.4063000e+04, 5.7256700e+05],
[2.7500000e+03, 2.4855000e+04, 2.9347700e+05, 3.9007170e+06],
[1.9778000e+04, 1.7500100e+05, 2.0810510e+06, 2.8078871e+07]]
)
assert_array_almost_equal(wm, ref)
def test_weighted_moments_normalized():
wnu = regionprops(SAMPLE, intensity_image=INTENSITY_SAMPLE
)[0].weighted_moments_normalized
ref = np.array(
[[ np.nan, np.nan, 0.2301467830, -0.0162529732],
[ np.nan, -0.0160405109, 0.0457932622, -0.0104598869],
[ 0.0873590903, -0.0031421072, 0.0165315478, -0.0028544152],
[-0.0161217406, -0.0031376984, 0.0043903193, -0.0011057191]]
)
assert_array_almost_equal(wnu, ref)
def test_label_sequence():
a = np.empty((2, 2), dtype=np.int)
a[:, :] = 2
ps = regionprops(a)
assert len(ps) == 1
assert ps[0].label == 2
def test_pure_background():
a = np.zeros((2, 2), dtype=np.int)
ps = regionprops(a)
assert len(ps) == 0
def test_invalid():
ps = regionprops(SAMPLE)
def get_intensity_image():
ps[0].intensity_image
with testing.raises(AttributeError):
get_intensity_image()
def test_invalid_size():
wrong_intensity_sample = np.array([[1], [1]])
with testing.raises(ValueError):
regionprops(SAMPLE, wrong_intensity_sample)
def test_equals():
arr = np.zeros((100, 100), dtype=np.int)
arr[0:25, 0:25] = 1
arr[50:99, 50:99] = 2
regions = regionprops(arr)
r1 = regions[0]
regions = regionprops(arr)
r2 = regions[0]
r3 = regions[1]
assert_equal(r1 == r2, True, "Same regionprops are not equal")
assert_equal(r1 != r3, True, "Different regionprops are equal")
def test_iterate_all_props():
region = regionprops(SAMPLE)[0]
p0 = {p: region[p] for p in region}
region = regionprops(SAMPLE, intensity_image=INTENSITY_SAMPLE)[0]
p1 = {p: region[p] for p in region}
assert len(p0) < len(p1)
def test_cache():
SAMPLE_mod = SAMPLE.copy()
region = regionprops(SAMPLE_mod)[0]
f0 = region.filled_image
region._label_image[:10] = 1
f1 = region.filled_image
# Changed underlying image, but cache keeps result the same
assert_array_equal(f0, f1)
# Now invalidate cache
region._cache_active = False
f1 = region.filled_image
assert np.any(f0 != f1)
def test_docstrings_and_props():
def foo():
"""foo"""
has_docstrings = bool(foo.__doc__)
region = regionprops(SAMPLE)[0]
docs = _parse_docs()
props = [m for m in dir(region) if not m.startswith('_')]
nr_docs_parsed = len(docs)
nr_props = len(props)
if has_docstrings:
assert_equal(nr_docs_parsed, nr_props)
ds = docs['weighted_moments_normalized']
assert 'iteration' not in ds
assert len(ds.split('\n')) > 3
else:
assert_equal(nr_docs_parsed, 0)
def test_props_to_dict():
regions = regionprops(SAMPLE)
out = _props_to_dict(regions)
assert out == {'label': array([1]),
'bbox-0': array([0]), 'bbox-1': array([0]),
'bbox-2': array([10]), 'bbox-3': array([18])}
regions = regionprops(SAMPLE)
out = _props_to_dict(regions, properties=('label', 'area', 'bbox'),
separator='+')
assert out == {'label': array([1]), 'area': array([72]),
'bbox+0': array([0]), 'bbox+1': array([0]),
'bbox+2': array([10]), 'bbox+3': array([18])}
def test_regionprops_table():
out = regionprops_table(SAMPLE)
assert out == {'label': array([1]),
'bbox-0': array([0]), 'bbox-1': array([0]),
'bbox-2': array([10]), 'bbox-3': array([18])}
out = regionprops_table(SAMPLE, properties=('label', 'area', 'bbox'),
separator='+')
assert out == {'label': array([1]), 'area': array([72]),
'bbox+0': array([0]), 'bbox+1': array([0]),
'bbox+2': array([10]), 'bbox+3': array([18])}
out = regionprops_table(np.zeros((2, 2), dtype=int),
properties=('label', 'area', 'bbox'),
separator='+')
assert len(out) == 6
assert len(out['label']) == 0
assert len(out['area']) == 0
assert len(out['bbox+0']) == 0
assert len(out['bbox+1']) == 0
assert len(out['bbox+2']) == 0
assert len(out['bbox+3']) == 0
def test_props_dict_complete():
region = regionprops(SAMPLE)[0]
properties = [s for s in dir(region) if not s.startswith('_')]
assert set(properties) == set(PROPS.values())
def test_column_dtypes_complete():
assert set(COL_DTYPES.keys()).union(OBJECT_COLUMNS) == set(PROPS.values())
def test_column_dtypes_correct():
msg = 'mismatch with expected type,'
region = regionprops(SAMPLE, intensity_image=INTENSITY_SAMPLE)[0]
for col in COL_DTYPES:
r = region[col]
if col in OBJECT_COLUMNS:
assert COL_DTYPES[col] == object
continue
t = type(np.ravel(r)[0])
if np.issubdtype(t, np.floating):
assert COL_DTYPES[col] == float, (
f'{col} dtype {t} {msg} {COL_DTYPES[col]}'
)
elif np.issubdtype(t, np.integer):
assert COL_DTYPES[col] == int, (
f'{col} dtype {t} {msg} {COL_DTYPES[col]}'
)
else:
assert False, (
f'{col} dtype {t} {msg} {COL_DTYPES[col]}'
)
def test_deprecated_coords_argument():
with expected_warnings(['coordinates keyword argument']):
region = regionprops(SAMPLE, coordinates='rc')
with testing.raises(ValueError):
region = regionprops(SAMPLE, coordinates='xy')

View file

@ -0,0 +1,84 @@
import numpy as np
import skimage.data
from skimage.measure import compare_nrmse, compare_psnr, compare_mse
from skimage._shared import testing
from skimage._shared.testing import assert_equal, assert_almost_equal
from skimage._shared._warnings import expected_warnings
np.random.seed(5)
cam = skimage.data.camera()
sigma = 20.0
cam_noisy = np.clip(cam + sigma * np.random.randn(*cam.shape), 0, 255)
cam_noisy = cam_noisy.astype(cam.dtype)
def test_PSNR_vs_IPOL():
# Tests vs. imdiff result from the following IPOL article and code:
# https://www.ipol.im/pub/art/2011/g_lmii/
p_IPOL = 22.4497
with expected_warnings(['DEPRECATED']):
p = compare_psnr(cam, cam_noisy)
assert_almost_equal(p, p_IPOL, decimal=4)
def test_PSNR_float():
with expected_warnings(['DEPRECATED']):
p_uint8 = compare_psnr(cam, cam_noisy)
p_float64 = compare_psnr(cam / 255., cam_noisy / 255.,
data_range=1)
assert_almost_equal(p_uint8, p_float64, decimal=5)
# mixed precision inputs
with expected_warnings(['DEPRECATED']):
p_mixed = compare_psnr(cam / 255., np.float32(cam_noisy / 255.),
data_range=1)
assert_almost_equal(p_mixed, p_float64, decimal=5)
# mismatched dtype results in a warning if data_range is unspecified
with expected_warnings(['Inputs have mismatched dtype', 'DEPRECATED']):
p_mixed = compare_psnr(cam / 255., np.float32(cam_noisy / 255.))
assert_almost_equal(p_mixed, p_float64, decimal=5)
def test_PSNR_errors():
with expected_warnings(['DEPRECATED']):
# shape mismatch
with testing.raises(ValueError):
compare_psnr(cam, cam[:-1, :])
def test_NRMSE():
x = np.ones(4)
y = np.asarray([0., 2., 2., 2.])
with expected_warnings(['DEPRECATED']):
assert_equal(compare_nrmse(y, x, 'mean'), 1 / np.mean(y))
assert_equal(compare_nrmse(y, x, 'Euclidean'), 1 / np.sqrt(3))
assert_equal(compare_nrmse(y, x, 'min-max'), 1 / (y.max() - y.min()))
# mixed precision inputs are allowed
assert_almost_equal(compare_nrmse(y, np.float32(x), 'min-max'),
1 / (y.max() - y.min()))
def test_NRMSE_no_int_overflow():
camf = cam.astype(np.float32)
cam_noisyf = cam_noisy.astype(np.float32)
with expected_warnings(['DEPRECATED']):
assert_almost_equal(compare_mse(cam, cam_noisy),
compare_mse(camf, cam_noisyf))
assert_almost_equal(compare_nrmse(cam, cam_noisy),
compare_nrmse(camf, cam_noisyf))
def test_NRMSE_errors():
x = np.ones(4)
with expected_warnings(['DEPRECATED']):
# shape mismatch
with testing.raises(ValueError):
compare_nrmse(x[:-1], x)
# invalid normalization name
with testing.raises(ValueError):
compare_nrmse(x, x, norm_type='foo')

View file

@ -0,0 +1,240 @@
import os
import numpy as np
from skimage import data, data_dir
from skimage.metrics import structural_similarity
from skimage._shared import testing
from skimage._shared._warnings import expected_warnings
from skimage._shared.testing import (assert_equal, assert_almost_equal,
assert_array_almost_equal, fetch)
np.random.seed(5)
cam = data.camera()
sigma = 20.0
cam_noisy = np.clip(cam + sigma * np.random.randn(*cam.shape), 0, 255)
cam_noisy = cam_noisy.astype(cam.dtype)
np.random.seed(1234)
def test_structural_similarity_patch_range():
N = 51
X = (np.random.rand(N, N) * 255).astype(np.uint8)
Y = (np.random.rand(N, N) * 255).astype(np.uint8)
assert(structural_similarity(X, Y, win_size=N) < 0.1)
assert_equal(structural_similarity(X, X, win_size=N), 1)
def test_structural_similarity_image():
N = 100
X = (np.random.rand(N, N) * 255).astype(np.uint8)
Y = (np.random.rand(N, N) * 255).astype(np.uint8)
S0 = structural_similarity(X, X, win_size=3)
assert_equal(S0, 1)
S1 = structural_similarity(X, Y, win_size=3)
assert(S1 < 0.3)
S2 = structural_similarity(X, Y, win_size=11, gaussian_weights=True)
assert(S2 < 0.3)
mssim0, S3 = structural_similarity(X, Y, full=True)
assert_equal(S3.shape, X.shape)
mssim = structural_similarity(X, Y)
assert_equal(mssim0, mssim)
# ssim of image with itself should be 1.0
assert_equal(structural_similarity(X, X), 1.0)
# Because we are forcing a random seed state, it is probably good to test
# against a few seeds in case on seed gives a particularly bad example
@testing.parametrize('seed', [1, 2, 3, 5, 8, 13])
def test_structural_similarity_grad(seed):
N = 30
# NOTE: This test is known to randomly fail on some systems (Mac OS X 10.6)
# And when testing tests in parallel. Therefore, we choose a few
# seeds that are known to work.
# The likely cause of this failure is that we are setting a hard
# threshold on the value of the gradient. Often the computed gradient
# is only slightly larger than what was measured.
# X = np.random.rand(N, N) * 255
# Y = np.random.rand(N, N) * 255
rnd = np.random.RandomState(seed)
X = rnd.rand(N, N) * 255
Y = rnd.rand(N, N) * 255
f = structural_similarity(X, Y, data_range=255)
g = structural_similarity(X, Y, data_range=255, gradient=True)
assert f < 0.05
assert g[0] < 0.05
assert np.all(g[1] < 0.05)
mssim, grad, s = structural_similarity(X, Y, data_range=255,
gradient=True, full=True)
assert np.all(grad < 0.05)
def test_structural_similarity_dtype():
N = 30
X = np.random.rand(N, N)
Y = np.random.rand(N, N)
S1 = structural_similarity(X, Y)
X = (X * 255).astype(np.uint8)
Y = (X * 255).astype(np.uint8)
S2 = structural_similarity(X, Y)
assert S1 < 0.1
assert S2 < 0.1
def test_structural_similarity_multichannel():
N = 100
X = (np.random.rand(N, N) * 255).astype(np.uint8)
Y = (np.random.rand(N, N) * 255).astype(np.uint8)
S1 = structural_similarity(X, Y, win_size=3)
# replicate across three channels. should get identical value
Xc = np.tile(X[..., np.newaxis], (1, 1, 3))
Yc = np.tile(Y[..., np.newaxis], (1, 1, 3))
S2 = structural_similarity(Xc, Yc, multichannel=True, win_size=3)
assert_almost_equal(S1, S2)
# full case should return an image as well
m, S3 = structural_similarity(Xc, Yc, multichannel=True, full=True)
assert_equal(S3.shape, Xc.shape)
# gradient case
m, grad = structural_similarity(Xc, Yc, multichannel=True, gradient=True)
assert_equal(grad.shape, Xc.shape)
# full and gradient case
m, grad, S3 = structural_similarity(Xc, Yc,
multichannel=True,
full=True,
gradient=True)
assert_equal(grad.shape, Xc.shape)
assert_equal(S3.shape, Xc.shape)
# fail if win_size exceeds any non-channel dimension
with testing.raises(ValueError):
structural_similarity(Xc, Yc, win_size=7, multichannel=False)
def test_structural_similarity_nD():
# test 1D through 4D on small random arrays
N = 10
for ndim in range(1, 5):
xsize = [N, ] * 5
X = (np.random.rand(*xsize) * 255).astype(np.uint8)
Y = (np.random.rand(*xsize) * 255).astype(np.uint8)
mssim = structural_similarity(X, Y, win_size=3)
assert mssim < 0.05
def test_structural_similarity_multichannel_chelsea():
# color image example
Xc = data.chelsea()
sigma = 15.0
Yc = np.clip(Xc + sigma * np.random.randn(*Xc.shape), 0, 255)
Yc = Yc.astype(Xc.dtype)
# multichannel result should be mean of the individual channel results
mssim = structural_similarity(Xc, Yc, multichannel=True)
mssim_sep = [structural_similarity(Yc[..., c], Xc[..., c])
for c in range(Xc.shape[-1])]
assert_almost_equal(mssim, np.mean(mssim_sep))
# ssim of image with itself should be 1.0
assert_equal(structural_similarity(Xc, Xc, multichannel=True), 1.0)
def test_gaussian_mssim_vs_IPOL():
# Tests vs. imdiff result from the following IPOL article and code:
# https://www.ipol.im/pub/art/2011/g_lmii/
mssim_IPOL = 0.327309966087341
mssim = structural_similarity(cam, cam_noisy, gaussian_weights=True,
use_sample_covariance=False)
assert_almost_equal(mssim, mssim_IPOL, decimal=3)
def test_gaussian_mssim_vs_author_ref():
"""
test vs. result from original author's Matlab implementation available at
https://ece.uwaterloo.ca/~z70wang/research/ssim/
Matlab test code:
img1 = imread('camera.png')
img2 = imread('camera_noisy.png')
mssim = ssim_index(img1, img2)
"""
mssim_matlab = 0.327314295673357
mssim = structural_similarity(cam, cam_noisy, gaussian_weights=True,
use_sample_covariance=False)
assert_almost_equal(mssim, mssim_matlab, decimal=10)
def test_gaussian_mssim_and_gradient_vs_Matlab():
# comparison to Matlab implementation of N. Avanaki:
# https://ece.uwaterloo.ca/~nnikvand/Coderep/SHINE%20TOOLBOX/SHINEtoolbox/
# Note: final line of ssim_sens.m was modified to discard image borders
ref = np.load(fetch('data/mssim_matlab_output.npz'))
grad_matlab = ref['grad_matlab']
mssim_matlab = float(ref['mssim_matlab'])
mssim, grad = structural_similarity(cam, cam_noisy, gaussian_weights=True,
gradient=True,
use_sample_covariance=False)
assert_almost_equal(mssim, mssim_matlab, decimal=3)
# check almost equal aside from object borders
assert_array_almost_equal(grad_matlab[5:-5], grad[5:-5])
def test_mssim_vs_legacy():
# check that ssim with default options matches skimage 0.11 result
mssim_skimage_0pt11 = 0.34192589699605191
mssim = structural_similarity(cam, cam_noisy)
assert_almost_equal(mssim, mssim_skimage_0pt11)
def test_mssim_mixed_dtype():
mssim = structural_similarity(cam, cam_noisy)
with expected_warnings(['Inputs have mismatched dtype']):
mssim_mixed = structural_similarity(cam, cam_noisy.astype(np.float32))
assert_almost_equal(mssim, mssim_mixed)
# no warning when user supplies data_range
mssim_mixed = structural_similarity(cam, cam_noisy.astype(np.float32),
data_range=255)
assert_almost_equal(mssim, mssim_mixed)
def test_invalid_input():
# size mismatch
X = np.zeros((9, 9), dtype=np.double)
Y = np.zeros((8, 8), dtype=np.double)
with testing.raises(ValueError):
structural_similarity(X, Y)
# win_size exceeds image extent
with testing.raises(ValueError):
structural_similarity(X, X, win_size=X.shape[0] + 1)
# some kwarg inputs must be non-negative
with testing.raises(ValueError):
structural_similarity(X, X, K1=-0.1)
with testing.raises(ValueError):
structural_similarity(X, X, K2=-0.1)
with testing.raises(ValueError):
structural_similarity(X, X, sigma=-1.0)