This commit is contained in:
ton
2023-10-05 00:01:27 +07:00
parent 1541297f6d
commit 4a987d90c5
12169 changed files with 502 additions and 2656459 deletions

View File

@@ -1,58 +0,0 @@
import numpy as np
import pytest
from skimage.metrics import (adapted_rand_error,
variation_of_information,
contingency_table)
from skimage._shared.testing import (assert_equal,
assert_almost_equal,
assert_array_equal)
def test_contingency_table():
im_true = np.array([1, 2, 3, 4])
im_test = np.array([1, 1, 8, 8])
table1 = np.array([[0., 0., 0., 0., 0., 0., 0., 0., 0.],
[0., 0.25, 0., 0., 0., 0., 0., 0., 0.],
[0., 0.25, 0., 0., 0., 0., 0., 0., 0.],
[0., 0., 0., 0., 0., 0., 0., 0., 0.25],
[0., 0., 0., 0., 0., 0., 0., 0., 0.25]])
sparse_table2 = contingency_table(im_true, im_test, normalize=True)
table2 = sparse_table2.toarray()
assert_array_equal(table1, table2)
def test_vi():
im_true = np.array([1, 2, 3, 4])
im_test = np.array([1, 1, 8, 8])
assert_equal(np.sum(variation_of_information(im_true, im_test)), 1)
def test_vi_ignore_labels():
im1 = np.array([[1, 0],
[2, 3]], dtype='uint8')
im2 = np.array([[1, 1],
[1, 0]], dtype='uint8')
false_splits, false_merges = variation_of_information(im1, im2,
ignore_labels=[0])
assert (false_splits, false_merges) == (0, 2 / 3)
def test_are():
im_true = np.array([[2, 1], [1, 2]])
im_test = np.array([[1, 2], [3, 1]])
assert_almost_equal(adapted_rand_error(im_true, im_test),
(0.3333333, 0.5, 1.0))
assert_almost_equal(adapted_rand_error(im_true, im_test, alpha=0),
(0, 0.5, 1.0))
assert_almost_equal(adapted_rand_error(im_true, im_test, alpha=1),
(0.5, 0.5, 1.0))
with pytest.raises(ValueError):
adapted_rand_error(im_true, im_test, alpha=1.01)
with pytest.raises(ValueError):
adapted_rand_error(im_true, im_test, alpha=-0.01)

View File

@@ -1,179 +0,0 @@
import numpy as np
import pytest
from numpy.testing import assert_almost_equal, assert_array_equal
from scipy.spatial import distance
from skimage._shared._warnings import expected_warnings
from skimage.metrics import hausdorff_distance, hausdorff_pair
def test_hausdorff_empty():
empty = np.zeros((0, 2), dtype=bool)
non_empty = np.zeros((3, 2), dtype=bool)
assert hausdorff_distance(empty, non_empty) == 0.0 # standard Hausdorff
assert (
hausdorff_distance(empty, non_empty, method="modified") == 0.0
) # modified Hausdorff
with expected_warnings(["One or both of the images is empty"]):
assert_array_equal(hausdorff_pair(empty, non_empty), [(), ()])
assert hausdorff_distance(non_empty, empty) == 0.0 # standard Hausdorff
assert (
hausdorff_distance(non_empty, empty, method="modified") == 0.0
) # modified Hausdorff
with expected_warnings(["One or both of the images is empty"]):
assert_array_equal(hausdorff_pair(non_empty, empty), [(), ()])
assert hausdorff_distance(empty, non_empty) == 0.0 # standard Hausdorff
assert (
hausdorff_distance(empty, non_empty, method="modified") == 0.0
) # modified Hausdorff
with expected_warnings(["One or both of the images is empty"]):
assert_array_equal(hausdorff_pair(empty, non_empty), [(), ()])
def test_hausdorff_simple():
points_a = (3, 0)
points_b = (6, 0)
shape = (7, 1)
coords_a = np.zeros(shape, dtype=bool)
coords_b = np.zeros(shape, dtype=bool)
coords_a[points_a] = True
coords_b[points_b] = True
dist = np.sqrt(sum((ca - cb) ** 2 for ca, cb in zip(points_a, points_b)))
d = distance.cdist([points_a], [points_b])
dist_modified = max(np.mean(np.min(d, axis=0)), np.mean(np.min(d, axis=1)))
assert_almost_equal(hausdorff_distance(coords_a, coords_b), dist)
assert_array_equal(hausdorff_pair(coords_a, coords_b), (points_a, points_b))
assert_almost_equal(
hausdorff_distance(
coords_a,
coords_b,
method="modified",
),
dist_modified,
)
@pytest.mark.parametrize("points_a", [(0, 0), (3, 0), (1, 4), (4, 1)])
@pytest.mark.parametrize("points_b", [(0, 0), (3, 0), (1, 4), (4, 1)])
def test_hausdorff_region_single(points_a, points_b):
shape = (5, 5)
coords_a = np.zeros(shape, dtype=bool)
coords_b = np.zeros(shape, dtype=bool)
coords_a[points_a] = True
coords_b[points_b] = True
dist = np.sqrt(sum((ca - cb) ** 2 for ca, cb in zip(points_a, points_b)))
d = distance.cdist([points_a], [points_b])
dist_modified = max(np.mean(np.min(d, axis=0)), np.mean(np.min(d, axis=1)))
assert_almost_equal(hausdorff_distance(coords_a, coords_b), dist)
assert_array_equal(hausdorff_pair(coords_a, coords_b), (points_a, points_b))
assert_almost_equal(
hausdorff_distance(coords_a, coords_b, method="modified"), dist_modified
)
@pytest.mark.parametrize("points_a", [(5, 4), (4, 5), (3, 4), (4, 3)])
@pytest.mark.parametrize("points_b", [(6, 4), (2, 6), (2, 4), (4, 0)])
def test_hausdorff_region_different_points(points_a, points_b):
shape = (7, 7)
coords_a = np.zeros(shape, dtype=bool)
coords_b = np.zeros(shape, dtype=bool)
coords_a[points_a] = True
coords_b[points_b] = True
dist = np.sqrt(sum((ca - cb) ** 2 for ca, cb in zip(points_a, points_b)))
d = distance.cdist([points_a], [points_b])
dist_modified = max(np.mean(np.min(d, axis=0)), np.mean(np.min(d, axis=1)))
assert_almost_equal(hausdorff_distance(coords_a, coords_b), dist)
assert_array_equal(hausdorff_pair(coords_a, coords_b), (points_a, points_b))
assert_almost_equal(
hausdorff_distance(coords_a, coords_b, method="modified"), dist_modified
)
def test_gallery():
shape = (60, 60)
# Create a diamond-like shape where the four corners form the 1st set
# of points
x_diamond = 30
y_diamond = 30
r = 10
plt_x = [0, 1, 0, -1]
plt_y = [1, 0, -1, 0]
set_ax = [(x_diamond + r * x) for x in plt_x]
set_ay = [(y_diamond + r * y) for y in plt_y]
# Create a kite-like shape where the four corners form the 2nd set of
# points
x_kite = 30
y_kite = 30
x_r = 15
y_r = 20
set_bx = [(x_kite + x_r * x) for x in plt_x]
set_by = [(y_kite + y_r * y) for y in plt_y]
# Set up the data to compute the Hausdorff distance
coords_a = np.zeros(shape, dtype=bool)
coords_b = np.zeros(shape, dtype=bool)
for x, y in zip(set_ax, set_ay):
coords_a[(x, y)] = True
for x, y in zip(set_bx, set_by):
coords_b[(x, y)] = True
# Test the Hausdorff function on the coordinates
# Should return 10, the distance between the furthest tip of the kite and
# its closest point on the diamond, which is the furthest someone can make
# you travel to encounter your nearest neighboring point on the other set.
assert_almost_equal(hausdorff_distance(coords_a, coords_b), 10.0)
# There are two pairs of points ((30, 20), (30, 10) or (30, 40), (30, 50)),
# that are Hausdorff distance apart. This tests for either of them.
hd_points = hausdorff_pair(coords_a, coords_b)
assert (
np.equal(hd_points, ((30, 20), (30, 10))).all()
or np.equal(hd_points, ((30, 40), (30, 50))).all()
)
# Test the Modified Hausdorff function on the coordinates
# Should return 7.5.
assert_almost_equal(hausdorff_distance(coords_a, coords_b, method="modified"), 7.5)
@pytest.mark.parametrize("points_a", [(0, 0, 1), (0, 1, 0), (1, 0, 0)])
@pytest.mark.parametrize("points_b", [(0, 0, 2), (0, 2, 0), (2, 0, 0)])
def test_3d_hausdorff_region(points_a, points_b):
shape = (3, 3, 3)
coords_a = np.zeros(shape, dtype=bool)
coords_b = np.zeros(shape, dtype=bool)
coords_a[points_a] = True
coords_b[points_b] = True
dist = np.sqrt(sum((ca - cb) ** 2 for ca, cb in zip(points_a, points_b)))
d = distance.cdist([points_a], [points_b])
dist_modified = max(np.mean(np.min(d, axis=0)), np.mean(np.min(d, axis=1)))
assert_almost_equal(hausdorff_distance(coords_a, coords_b), dist)
assert_array_equal(hausdorff_pair(coords_a, coords_b), (points_a, points_b))
assert_almost_equal(
hausdorff_distance(coords_a, coords_b, method="modified"), dist_modified
)
def test_hausdorff_metrics_match():
# Test that Hausdorff distance is the Euclidean distance between Hausdorff
# pair
points_a = (3, 0)
points_b = (6, 0)
shape = (7, 1)
coords_a = np.zeros(shape, dtype=bool)
coords_b = np.zeros(shape, dtype=bool)
coords_a[points_a] = True
coords_b[points_b] = True
assert_array_equal(hausdorff_pair(coords_a, coords_b), (points_a, points_b))
euclidean_distance = distance.euclidean(points_a, points_b)
assert_almost_equal(euclidean_distance, hausdorff_distance(coords_a, coords_b))

View File

@@ -1,139 +0,0 @@
import numpy as np
import pytest
from numpy.testing import assert_equal, assert_almost_equal
from skimage import data
from skimage._shared._warnings import expected_warnings
from skimage.metrics import (peak_signal_noise_ratio, normalized_root_mse,
mean_squared_error, normalized_mutual_information)
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)
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/.
Notes
-----
To generate p_IPOL, we need a local copy of cam_noisy:
>>> from skimage import io
>>> io.imsave('/tmp/cam_noisy.png', cam_noisy)
Then, we use the following command:
$ ./imdiff -m psnr <path to camera.png>/camera.png /tmp/cam_noisy.png
Values for current data.camera() calculated by Gregory Lee on Sep, 2020.
Available at:
https://github.com/scikit-image/scikit-image/pull/4913#issuecomment-700653165
"""
p_IPOL = 22.409353363576034
p = peak_signal_noise_ratio(cam, cam_noisy)
assert_almost_equal(p, p_IPOL, decimal=4)
@pytest.mark.parametrize('dtype', [np.float16, np.float32, np.float64])
def test_PSNR_float(dtype):
p_uint8 = peak_signal_noise_ratio(cam, cam_noisy)
camf = (cam / 255.).astype(dtype, copy=False)
camf_noisy = (cam_noisy / 255.).astype(dtype, copy=False)
p_float64 = peak_signal_noise_ratio(camf, camf_noisy, data_range=1)
assert p_float64.dtype == np.float64
decimal = 3 if dtype == np.float16 else 5
assert_almost_equal(p_uint8, p_float64, decimal=decimal)
# mixed precision inputs
p_mixed = peak_signal_noise_ratio(cam / 255., np.float32(cam_noisy / 255.),
data_range=1)
assert_almost_equal(p_mixed, p_float64, decimal=decimal)
# mismatched dtype results in a warning if data_range is unspecified
with expected_warnings(['Inputs have mismatched dtype']):
p_mixed = peak_signal_noise_ratio(cam / 255.,
np.float32(cam_noisy / 255.))
assert_almost_equal(p_mixed, p_float64, decimal=decimal)
# mismatched dtype results in a warning if data_range is unspecified
with expected_warnings(['Inputs have mismatched dtype']):
p_mixed = peak_signal_noise_ratio(cam / 255.,
np.float32(cam_noisy / 255.))
assert_almost_equal(p_mixed, p_float64, decimal=decimal)
def test_PSNR_errors():
# shape mismatch
with pytest.raises(ValueError):
peak_signal_noise_ratio(cam, cam[:-1, :])
@pytest.mark.parametrize('dtype', [np.float16, np.float32, np.float64])
def test_NRMSE(dtype):
x = np.ones(4, dtype=dtype)
y = np.asarray([0., 2., 2., 2.], dtype=dtype)
nrmse = normalized_root_mse(y, x, normalization='mean')
assert nrmse.dtype == np.float64
assert_equal(nrmse, 1 / np.mean(y))
assert_equal(normalized_root_mse(y, x, normalization='euclidean'),
1 / np.sqrt(3))
assert_equal(normalized_root_mse(y, x, normalization='min-max'),
1 / (y.max() - y.min()))
# mixed precision inputs are allowed
assert_almost_equal(normalized_root_mse(y, np.float32(x),
normalization='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)
assert_almost_equal(mean_squared_error(cam, cam_noisy),
mean_squared_error(camf, cam_noisyf))
assert_almost_equal(normalized_root_mse(cam, cam_noisy),
normalized_root_mse(camf, cam_noisyf))
def test_NRMSE_errors():
x = np.ones(4)
# shape mismatch
with pytest.raises(ValueError):
normalized_root_mse(x[:-1], x)
# invalid normalization name
with pytest.raises(ValueError):
normalized_root_mse(x, x, normalization='foo')
def test_nmi():
assert_almost_equal(normalized_mutual_information(cam, cam), 2)
assert (normalized_mutual_information(cam, cam_noisy)
< normalized_mutual_information(cam, cam))
def test_nmi_different_sizes():
assert normalized_mutual_information(cam[:, :400], cam[:400, :]) > 1
@pytest.mark.parametrize('dtype', [np.float16, np.float32, np.float64])
def test_nmi_random(dtype):
rng = np.random.default_rng()
random1 = rng.random((100, 100)).astype(dtype)
random2 = rng.random((100, 100)).astype(dtype)
nmi = normalized_mutual_information(random1, random2, bins=10)
assert nmi.dtype == np.float64
assert_almost_equal(nmi, 1, decimal=2)
def test_nmi_random_3d():
random1, random2 = np.random.random((2, 10, 100, 100))
assert_almost_equal(
normalized_mutual_information(random1, random2, bins=10),
1,
decimal=2,
)

View File

@@ -1,270 +0,0 @@
import numpy as np
import pytest
from numpy.testing import assert_equal, assert_almost_equal
from skimage import data
from skimage._shared._warnings import expected_warnings
from skimage._shared.utils import _supported_float_type
from skimage.metrics import structural_similarity
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)
# structural_similarity 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
@pytest.mark.parametrize('seed', [1, 2, 3, 5, 8, 13])
@pytest.mark.parametrize('dtype', [np.float16, np.float32, np.float64])
def test_structural_similarity_grad(seed, dtype):
N = 60
# 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.
rnd = np.random.default_rng(seed)
X = rnd.random((N, N)).astype(dtype, copy=False) * 255
Y = rnd.random((N, N)).astype(dtype, copy=False) * 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 s.dtype == _supported_float_type(dtype)
assert grad.dtype == _supported_float_type(dtype)
assert np.all(grad < 0.05)
@pytest.mark.parametrize(
'dtype', [np.uint8, np.int32, np.float16, np.float32, np.float64]
)
def test_structural_similarity_dtype(dtype):
N = 30
X = np.random.rand(N, N)
Y = np.random.rand(N, N)
if np.dtype(dtype).kind in 'iub':
data_range = 255.0
X = (X * 255).astype(np.uint8)
Y = (X * 255).astype(np.uint8)
else:
data_range = 1.0
X = X.astype(dtype, copy=False)
Y = Y.astype(dtype, copy=False)
S1 = structural_similarity(X, Y, data_range=data_range)
assert S1.dtype == np.float64
assert S1 < 0.1
@pytest.mark.parametrize('channel_axis', [0, 1, 2, -1])
def test_structural_similarity_multichannel(channel_axis):
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))
# move channels from last position to specified channel_axis
Xc, Yc = (np.moveaxis(_arr, -1, channel_axis) for _arr in (Xc, Yc))
S2 = structural_similarity(Xc, Yc, channel_axis=channel_axis, win_size=3)
assert_almost_equal(S1, S2)
# full case should return an image as well
m, S3 = structural_similarity(Xc, Yc, channel_axis=channel_axis, full=True)
assert_equal(S3.shape, Xc.shape)
# gradient case
m, grad = structural_similarity(Xc, Yc, channel_axis=channel_axis,
gradient=True)
assert_equal(grad.shape, Xc.shape)
# full and gradient case
m, grad, S3 = structural_similarity(Xc, Yc,
channel_axis=channel_axis,
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 pytest.raises(ValueError):
structural_similarity(Xc, Yc, win_size=7, channel_axis=None)
@pytest.mark.parametrize('dtype', [np.uint8, np.float32, np.float64])
def test_structural_similarity_nD(dtype):
# 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(dtype)
Y = (np.random.rand(*xsize) * 255).astype(dtype)
mssim = structural_similarity(X, Y, win_size=3, data_range=255.0)
assert mssim.dtype == np.float64
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, channel_axis=-1)
mssim_sep = [structural_similarity(
Yc[..., c], Xc[..., c]) for c in range(Xc.shape[-1])]
assert_almost_equal(mssim, np.mean(mssim_sep))
# structural_similarity of image with itself should be 1.0
assert_equal(structural_similarity(Xc, Xc, channel_axis=-1), 1.0)
def test_gaussian_structural_similarity_vs_IPOL():
""" Tests vs. imdiff result from the following IPOL article and code:
https://www.ipol.im/pub/art/2011/g_lmii/.
Notes
-----
To generate mssim_IPOL, we need a local copy of cam_noisy:
>>> from skimage import io
>>> io.imsave('/tmp/cam_noisy.png', cam_noisy)
Then, we use the following command:
$ ./imdiff -m mssim <path to camera.png>/camera.png /tmp/cam_noisy.png
Values for current data.camera() calculated by Gregory Lee on Sep, 2020.
Available at:
https://github.com/scikit-image/scikit-image/pull/4913#issuecomment-700653165
"""
mssim_IPOL = 0.357959091663361
assert cam.dtype == np.uint8
assert cam_noisy.dtype == np.uint8
mssim = structural_similarity(cam, cam_noisy, gaussian_weights=True,
use_sample_covariance=False)
assert_almost_equal(mssim, mssim_IPOL, decimal=3)
@pytest.mark.parametrize(
'dtype', [np.uint8, np.int32, np.float16, np.float32, np.float64]
)
def test_mssim_vs_legacy(dtype):
# check that ssim with default options matches skimage 0.17 result
mssim_skimage_0pt17 = 0.3674518327910367
assert cam.dtype == np.uint8
assert cam_noisy.dtype == np.uint8
mssim = structural_similarity(cam.astype(dtype),
cam_noisy.astype(dtype), data_range=255)
assert_almost_equal(mssim, mssim_skimage_0pt17)
def test_ssim_warns_about_data_range():
mssim = structural_similarity(cam, cam_noisy)
with expected_warnings(['Setting data_range based on im1.dtype']):
mssim_uint16 = structural_similarity(cam.astype(np.uint16),
cam_noisy.astype(np.uint16))
# The value computed for mssim_uint16 is wrong, because the
# dtype of im1 led to infer an erroneous data_range. The user
# is getting a warning about avoiding mistakes.
assert mssim_uint16 > 0.99
with expected_warnings(['Setting data_range based on im1.dtype',
'Inputs have mismatched dtypes']):
mssim_mixed = structural_similarity(cam, cam_noisy.astype(np.int32))
# 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)
@pytest.mark.parametrize('dtype', [np.float16, np.float32, np.float64])
def test_structural_similarity_small_image(dtype):
X = np.zeros((5, 5), dtype=dtype)
# structural_similarity can be computed for small images if win_size is
# a) odd and b) less than or equal to the images' smaller side
assert_equal(structural_similarity(X, X, win_size=3, data_range=1.0), 1.0)
assert_equal(structural_similarity(X, X, win_size=5, data_range=1.0), 1.0)
# structural_similarity errors for small images if user doesn't specify
# win_size
with pytest.raises(ValueError):
structural_similarity(X, X)
@pytest.mark.parametrize('dtype', [np.float16, np.float32, np.float64])
def test_structural_similarity_errors_on_float_without_data_range(dtype):
X = np.zeros((64, 64), dtype=dtype)
with pytest.raises(ValueError):
structural_similarity(X, X)
def test_invalid_input():
# size mismatch
X = np.zeros((9, 9), dtype=np.float64)
Y = np.zeros((8, 8), dtype=np.float64)
with pytest.raises(ValueError):
structural_similarity(X, Y)
# win_size exceeds image extent
with pytest.raises(ValueError):
structural_similarity(X, X, win_size=X.shape[0] + 1)
# some kwarg inputs must be non-negative
with pytest.raises(ValueError):
structural_similarity(X, X, K1=-0.1)
with pytest.raises(ValueError):
structural_similarity(X, X, K2=-0.1)
with pytest.raises(ValueError):
structural_similarity(X, X, sigma=-1.0)