This commit is contained in:
ton
2024-10-07 10:13:40 +07:00
parent aa1631742f
commit 3a7d696db6
9729 changed files with 1832837 additions and 161742 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,186 @@
import numpy as np
from skimage import data, img_as_float
from skimage._shared import testing
from skimage._shared.testing import assert_allclose
from skimage._shared.utils import _supported_float_type
from skimage.color import rgb2gray
from skimage.metrics import mean_squared_error, normalized_root_mse
from skimage.morphology import binary_dilation, disk
from skimage.restoration import inpaint
@testing.parametrize('dtype', [np.float16, np.float32, np.float64])
@testing.parametrize('split_into_regions', [False, True])
def test_inpaint_biharmonic_2d(dtype, split_into_regions):
img = np.tile(np.square(np.linspace(0, 1, 5, dtype=dtype)), (5, 1))
mask = np.zeros_like(img)
mask[2, 2:] = 1
mask[1, 3:] = 1
mask[0, 4:] = 1
img[np.where(mask)] = 0
out = inpaint.inpaint_biharmonic(img, mask, split_into_regions=split_into_regions)
assert out.dtype == _supported_float_type(img.dtype)
ref = np.array(
[
[0.0, 0.0625, 0.25000000, 0.5625000, 0.73925058],
[0.0, 0.0625, 0.25000000, 0.5478048, 0.76557821],
[0.0, 0.0625, 0.25842878, 0.5623079, 0.85927796],
[0.0, 0.0625, 0.25000000, 0.5625000, 1.00000000],
[0.0, 0.0625, 0.25000000, 0.5625000, 1.00000000],
]
)
rtol = 1e-7 if dtype == np.float64 else 1e-6
assert_allclose(ref, out, rtol=rtol)
@testing.parametrize('channel_axis', [0, 1, -1])
def test_inpaint_biharmonic_2d_color(channel_axis):
img = img_as_float(data.astronaut()[:64, :64])
mask = np.zeros(img.shape[:2], dtype=bool)
mask[8:16, :16] = 1
img_defect = img * ~mask[..., np.newaxis]
mse_defect = mean_squared_error(img, img_defect)
img_defect = np.moveaxis(img_defect, -1, channel_axis)
img_restored = inpaint.inpaint_biharmonic(
img_defect, mask, channel_axis=channel_axis
)
img_restored = np.moveaxis(img_restored, channel_axis, -1)
mse_restored = mean_squared_error(img, img_restored)
assert mse_restored < 0.01 * mse_defect
@testing.parametrize('dtype', [np.float32, np.float64])
def test_inpaint_biharmonic_2d_float_dtypes(dtype):
img = np.tile(np.square(np.linspace(0, 1, 5)), (5, 1))
mask = np.zeros_like(img)
mask[2, 2:] = 1
mask[1, 3:] = 1
mask[0, 4:] = 1
img[np.where(mask)] = 0
img = img.astype(dtype, copy=False)
out = inpaint.inpaint_biharmonic(img, mask)
assert out.dtype == img.dtype
ref = np.array(
[
[0.0, 0.0625, 0.25000000, 0.5625000, 0.73925058],
[0.0, 0.0625, 0.25000000, 0.5478048, 0.76557821],
[0.0, 0.0625, 0.25842878, 0.5623079, 0.85927796],
[0.0, 0.0625, 0.25000000, 0.5625000, 1.00000000],
[0.0, 0.0625, 0.25000000, 0.5625000, 1.00000000],
]
)
assert_allclose(ref, out, rtol=1e-5)
@testing.parametrize('split_into_regions', [False, True])
def test_inpaint_biharmonic_3d(split_into_regions):
img = np.tile(np.square(np.linspace(0, 1, 5)), (5, 1))
img = np.dstack((img, img.T))
mask = np.zeros_like(img)
mask[2, 2:, :] = 1
mask[1, 3:, :] = 1
mask[0, 4:, :] = 1
img[np.where(mask)] = 0
out = inpaint.inpaint_biharmonic(img, mask, split_into_regions=split_into_regions)
ref = np.dstack(
(
np.array(
[
[0.0000, 0.0625, 0.25000000, 0.56250000, 0.53752796],
[0.0000, 0.0625, 0.25000000, 0.44443780, 0.53762210],
[0.0000, 0.0625, 0.23693666, 0.46621112, 0.68615592],
[0.0000, 0.0625, 0.25000000, 0.56250000, 1.00000000],
[0.0000, 0.0625, 0.25000000, 0.56250000, 1.00000000],
]
),
np.array(
[
[0.0000, 0.0000, 0.00000000, 0.00000000, 0.19621902],
[0.0625, 0.0625, 0.06250000, 0.17470756, 0.30140091],
[0.2500, 0.2500, 0.27241289, 0.35155440, 0.43068654],
[0.5625, 0.5625, 0.56250000, 0.56250000, 0.56250000],
[1.0000, 1.0000, 1.00000000, 1.00000000, 1.00000000],
]
),
)
)
assert_allclose(ref, out)
def test_invalid_input():
img, mask = np.zeros([]), np.zeros([])
with testing.raises(ValueError):
inpaint.inpaint_biharmonic(img, mask)
img, mask = np.zeros((2, 2)), np.zeros((4, 1))
with testing.raises(ValueError):
inpaint.inpaint_biharmonic(img, mask)
img = np.ma.array(np.zeros((2, 2)), mask=[[0, 0], [0, 0]])
mask = np.zeros((2, 2))
with testing.raises(TypeError):
inpaint.inpaint_biharmonic(img, mask)
@testing.parametrize('dtype', [np.uint8, np.float32, np.float64])
@testing.parametrize('order', ['C', 'F'])
@testing.parametrize('channel_axis', [None, -1])
@testing.parametrize('split_into_regions', [False, True])
def test_inpaint_nrmse(dtype, order, channel_axis, split_into_regions):
image_orig = data.astronaut()[:, :200]
float_dtype = np.float32 if dtype == np.float32 else np.float64
image_orig = image_orig.astype(float_dtype, copy=False)
# Create mask with six block defect regions
mask = np.zeros(image_orig.shape[:-1], dtype=bool)
mask[20:50, 3:20] = 1
mask[165:180, 90:155] = 1
mask[40:60, 170:195] = 1
mask[-60:-40, 170:195] = 1
mask[-180:-165, 90:155] = 1
mask[-50:-20, :20] = 1
# add a few long, narrow defects
mask[200:205, -200:] = 1
mask[150:255, 20:22] = 1
mask[365:368, 60:130] = 1
# add randomly positioned small point-like defects
rstate = np.random.default_rng(0)
for radius in [0, 2, 4]:
# larger defects are less common
thresh = 3.25 + 0.25 * radius # larger defects less common
tmp_mask = rstate.standard_normal(image_orig.shape[:-1]) > thresh
if radius > 0:
tmp_mask = binary_dilation(tmp_mask, disk(radius, dtype=bool))
mask[tmp_mask] = 1
# Defect image over the same region in each color channel
image_defect = image_orig.copy()
for layer in range(image_defect.shape[-1]):
image_defect[np.where(mask)] = 0
if channel_axis is None:
image_orig = rgb2gray(image_orig)
image_defect = rgb2gray(image_defect)
image_orig = image_orig.astype(dtype, copy=False)
image_defect = image_defect.astype(dtype, copy=False)
image_defect = np.asarray(image_defect, order=order)
image_result = inpaint.inpaint_biharmonic(
image_defect,
mask,
channel_axis=channel_axis,
split_into_regions=split_into_regions,
)
assert image_result.dtype == float_dtype
nrmse_defect = normalized_root_mse(image_orig, image_defect)
nrmse_result = normalized_root_mse(img_as_float(image_orig), image_result)
assert nrmse_result < 0.2 * nrmse_defect

View File

@@ -0,0 +1,98 @@
import functools
import numpy as np
import pytest
from skimage._shared.testing import assert_
from skimage._shared.utils import _supported_float_type
from skimage.data import binary_blobs
from skimage.data import camera, chelsea
from skimage.metrics import mean_squared_error as mse
from skimage.restoration import calibrate_denoiser, denoise_wavelet
from skimage.restoration.j_invariant import denoise_invariant
from skimage.util import img_as_float, random_noise
from skimage.restoration.tests.test_denoise import xfail_without_pywt
test_img = img_as_float(camera())
test_img_color = img_as_float(chelsea())
test_img_3d = img_as_float(binary_blobs(64, n_dim=3)) / 2
noisy_img = random_noise(test_img, mode='gaussian', var=0.01)
noisy_img_color = random_noise(test_img_color, mode='gaussian', var=0.01)
noisy_img_3d = random_noise(test_img_3d, mode='gaussian', var=0.1)
_denoise_wavelet = functools.partial(denoise_wavelet, rescale_sigma=True)
@xfail_without_pywt
def test_invariant_denoise():
denoised_img = denoise_invariant(noisy_img, _denoise_wavelet)
denoised_mse = mse(denoised_img, test_img)
original_mse = mse(noisy_img, test_img)
assert_(denoised_mse < original_mse)
@xfail_without_pywt
@pytest.mark.parametrize('dtype', [np.float16, np.float32, np.float64])
def test_invariant_denoise_color(dtype):
denoised_img_color = denoise_invariant(
noisy_img_color.astype(dtype),
_denoise_wavelet,
denoiser_kwargs=dict(channel_axis=-1),
)
denoised_mse = mse(denoised_img_color, test_img_color)
original_mse = mse(noisy_img_color, test_img_color)
assert denoised_mse < original_mse
assert denoised_img_color.dtype == _supported_float_type(dtype)
@xfail_without_pywt
def test_invariant_denoise_3d():
denoised_img_3d = denoise_invariant(noisy_img_3d, _denoise_wavelet)
denoised_mse = mse(denoised_img_3d, test_img_3d)
original_mse = mse(noisy_img_3d, test_img_3d)
assert_(denoised_mse < original_mse)
@xfail_without_pywt
def test_calibrate_denoiser_extra_output():
parameter_ranges = {'sigma': np.linspace(0.1, 1, 5) / 2}
_, (parameters_tested, losses) = calibrate_denoiser(
noisy_img,
_denoise_wavelet,
denoise_parameters=parameter_ranges,
extra_output=True,
)
all_denoised = [
denoise_invariant(noisy_img, _denoise_wavelet, denoiser_kwargs=denoiser_kwargs)
for denoiser_kwargs in parameters_tested
]
ground_truth_losses = [mse(img, test_img) for img in all_denoised]
assert_(np.argmin(losses) == np.argmin(ground_truth_losses))
@xfail_without_pywt
def test_calibrate_denoiser():
parameter_ranges = {'sigma': np.linspace(0.1, 1, 5) / 2}
denoiser = calibrate_denoiser(
noisy_img, _denoise_wavelet, denoise_parameters=parameter_ranges
)
denoised_mse = mse(denoiser(noisy_img), test_img)
original_mse = mse(noisy_img, test_img)
assert_(denoised_mse < original_mse)
@xfail_without_pywt
def test_input_image_not_modified():
input_image = noisy_img.copy()
parameter_ranges = {'sigma': np.random.random(5) / 2}
calibrate_denoiser(
input_image, _denoise_wavelet, denoise_parameters=parameter_ranges
)
assert_(np.all(noisy_img == input_image))

View File

@@ -0,0 +1,182 @@
import numpy as np
import pytest
from scipy import ndimage as ndi
from scipy.signal import convolve2d, convolve
from skimage import restoration, util
from skimage._shared import filters
from skimage._shared.testing import fetch
from skimage._shared.utils import _supported_float_type
from skimage.color import rgb2gray
from skimage.data import astronaut, camera
from skimage.restoration import uft
test_img = util.img_as_float(camera())
def _get_rtol_atol(dtype):
rtol = 1e-3
atol = 0
if dtype == np.float16:
rtol = 1e-2
atol = 1e-3
elif dtype == np.float32:
atol = 1e-5
return rtol, atol
@pytest.mark.parametrize('dtype', [np.float16, np.float32, np.float64])
@pytest.mark.parametrize('ndim', [1, 2, 3])
def test_wiener(dtype, ndim):
"""
currently only performs pixelwise comparison to
precomputed result in 2d case.
"""
rng = np.random.RandomState(0)
psf = np.ones([5] * ndim, dtype=dtype) / 5**ndim
# for ndim == 2 use camera (to compare to presaved result)
if ndim != 2:
test_img = rng.randint(0, 100, [50] * ndim)
else:
test_img = util.img_as_float(camera())
data = convolve(test_img, psf, 'same')
data += 0.1 * data.std() * rng.standard_normal(data.shape)
data = data.astype(dtype, copy=False)
deconvolved = restoration.wiener(data, psf, 0.05)
assert deconvolved.dtype == _supported_float_type(dtype)
if ndim == 2:
rtol, atol = _get_rtol_atol(dtype)
path = fetch('restoration/tests/camera_wiener.npy')
np.testing.assert_allclose(deconvolved, np.load(path), rtol=rtol, atol=atol)
_, laplacian = uft.laplacian(ndim, data.shape)
otf = uft.ir2tf(psf, data.shape, is_real=False)
assert otf.real.dtype == _supported_float_type(dtype)
deconvolved = restoration.wiener(data, otf, 0.05, reg=laplacian, is_real=False)
assert deconvolved.real.dtype == _supported_float_type(dtype)
if ndim == 2:
np.testing.assert_allclose(
np.real(deconvolved), np.load(path), rtol=rtol, atol=atol
)
@pytest.mark.parametrize('dtype', [np.float16, np.float32, np.float64])
def test_unsupervised_wiener(dtype):
psf = np.ones((5, 5), dtype=dtype) / 25
data = convolve2d(test_img, psf, 'same')
seed = 16829302
# keep old-style RandomState here for compatibility with previously stored
# reference data in camera_unsup.npy and camera_unsup2.npy
rng = np.random.RandomState(seed)
data += 0.1 * data.std() * rng.standard_normal(data.shape)
data = data.astype(dtype, copy=False)
deconvolved, _ = restoration.unsupervised_wiener(data, psf, rng=seed)
restoration.unsupervised_wiener(data, psf, rng=seed)
float_type = _supported_float_type(dtype)
assert deconvolved.dtype == float_type
rtol, atol = _get_rtol_atol(dtype)
path = fetch('restoration/tests/camera_unsup.npy')
np.testing.assert_allclose(deconvolved, np.load(path), rtol=rtol, atol=atol)
_, laplacian = uft.laplacian(2, data.shape)
otf = uft.ir2tf(psf, data.shape, is_real=False)
assert otf.real.dtype == _supported_float_type(dtype)
deconvolved2 = restoration.unsupervised_wiener(
data,
otf,
reg=laplacian,
is_real=False,
user_params={
"callback": lambda x: None,
"max_num_iter": 200,
"min_num_iter": 30,
},
rng=seed,
)[0]
assert deconvolved2.real.dtype == float_type
path = fetch('restoration/tests/camera_unsup2.npy')
np.testing.assert_allclose(
np.real(deconvolved2), np.load(path), rtol=rtol, atol=atol
)
def test_unsupervised_wiener_deprecated_user_param():
psf = np.ones((5, 5), dtype=float) / 25
data = convolve2d(test_img, psf, 'same')
otf = uft.ir2tf(psf, data.shape, is_real=False)
_, laplacian = uft.laplacian(2, data.shape)
restoration.unsupervised_wiener(
data,
otf,
reg=laplacian,
is_real=False,
user_params={"max_num_iter": 300, "min_num_iter": 30},
rng=5,
)
def test_image_shape():
"""Test that shape of output image in deconvolution is same as input.
This addresses issue #1172.
"""
point = np.zeros((5, 5), float)
point[2, 2] = 1.0
psf = filters.gaussian(point, sigma=1.0, mode='reflect')
# image shape: (45, 45), as reported in #1172
image = util.img_as_float(camera()[65:165, 215:315]) # just the face
image_conv = ndi.convolve(image, psf)
deconv_sup = restoration.wiener(image_conv, psf, 1)
deconv_un = restoration.unsupervised_wiener(image_conv, psf)[0]
# test the shape
np.testing.assert_equal(image.shape, deconv_sup.shape)
np.testing.assert_equal(image.shape, deconv_un.shape)
# test the reconstruction error
sup_relative_error = np.abs(deconv_sup - image) / image
un_relative_error = np.abs(deconv_un - image) / image
np.testing.assert_array_less(np.median(sup_relative_error), 0.1)
np.testing.assert_array_less(np.median(un_relative_error), 0.1)
@pytest.mark.parametrize('ndim', [1, 2, 3])
def test_richardson_lucy(ndim):
psf = np.ones([5] * ndim, dtype=float) / 5**ndim
if ndim != 2:
test_img = np.random.randint(0, 100, [30] * ndim)
else:
test_img = util.img_as_float(camera())
data = convolve(test_img, psf, 'same')
rng = np.random.RandomState(0)
data += 0.1 * data.std() * rng.standard_normal(data.shape)
deconvolved = restoration.richardson_lucy(data, psf, num_iter=5)
if ndim == 2:
path = fetch('restoration/tests/camera_rl.npy')
np.testing.assert_allclose(deconvolved, np.load(path), rtol=1e-3)
@pytest.mark.parametrize('dtype_image', [np.float16, np.float32, np.float64])
@pytest.mark.parametrize('dtype_psf', [np.float32, np.float64])
def test_richardson_lucy_filtered(dtype_image, dtype_psf):
if dtype_image == np.float64:
atol = 1e-8
else:
atol = 1e-5
test_img_astro = rgb2gray(astronaut())
psf = np.ones((5, 5), dtype=dtype_psf) / 25
data = convolve2d(test_img_astro, psf, 'same')
data = data.astype(dtype_image, copy=False)
deconvolved = restoration.richardson_lucy(data, psf, 5, filter_epsilon=1e-6)
assert deconvolved.dtype == _supported_float_type(data.dtype)
path = fetch('restoration/tests/astronaut_rl.npy')
np.testing.assert_allclose(deconvolved, np.load(path), rtol=1e-3, atol=atol)

View File

@@ -0,0 +1,96 @@
"""
Tests for Rolling Ball Filter
(skimage.restoration.rolling_ball)
"""
import numpy as np
import pytest
from skimage import data
from skimage.restoration._rolling_ball import rolling_ball
from skimage.restoration._rolling_ball import ellipsoid_kernel
@pytest.mark.parametrize(
'dtype', [np.uint8, np.int32, np.float16, np.float32, np.float64]
)
def test_ellipsoid_const(dtype):
img = 155 * np.ones((100, 100), dtype=dtype)
kernel = ellipsoid_kernel((25, 53), 50)
background = rolling_ball(img, kernel=kernel)
assert np.allclose(img - background, np.zeros_like(img))
assert background.dtype == img.dtype
def test_nan_const():
img = 123 * np.ones((100, 100), dtype=float)
img[20, 20] = np.nan
img[50, 53] = np.nan
kernel_shape = (10, 10)
x = np.arange(-kernel_shape[1] // 2, kernel_shape[1] // 2 + 1)[np.newaxis, :]
y = np.arange(-kernel_shape[0] // 2, kernel_shape[0] // 2 + 1)[:, np.newaxis]
expected_img = np.zeros_like(img)
expected_img[y + 20, x + 20] = np.nan
expected_img[y + 50, x + 53] = np.nan
kernel = ellipsoid_kernel(kernel_shape, 100)
background = rolling_ball(img, kernel=kernel, nansafe=True)
assert np.allclose(img - background, expected_img, equal_nan=True)
@pytest.mark.parametrize("radius", [1, 2.5, 10.346, 50])
def test_const_image(radius):
# infinite plane light source at top left corner
img = 23 * np.ones((100, 100), dtype=np.uint8)
background = rolling_ball(img, radius=radius)
assert np.allclose(img - background, np.zeros_like(img))
def test_radial_gradient():
# spot light source at top left corner
spot_radius = 50
x, y = np.meshgrid(range(5), range(5))
img = np.sqrt(np.clip(spot_radius**2 - y**2 - x**2, 0, None))
background = rolling_ball(img, radius=5)
assert np.allclose(img - background, np.zeros_like(img))
def test_linear_gradient():
# linear light source centered at top left corner
x, y = np.meshgrid(range(100), range(100))
img = y * 20 + x * 20
expected_img = 19 * np.ones_like(img)
expected_img[0, 0] = 0
background = rolling_ball(img, radius=1)
assert np.allclose(img - background, expected_img)
@pytest.mark.parametrize("radius", [2, 10, 12.5, 50])
def test_preserve_peaks(radius):
x, y = np.meshgrid(range(100), range(100))
img = 0 * x + 0 * y + 10
img[10, 10] = 20
img[20, 20] = 35
img[45, 26] = 156
expected_img = img - 10
background = rolling_ball(img, radius=radius)
assert np.allclose(img - background, expected_img)
@pytest.mark.parametrize("num_threads", [None, 1, 2])
def test_threads(num_threads):
# not testing if we use multiple threads
# just checking if the API throws an exception
img = 23 * np.ones((100, 100), dtype=np.uint8)
rolling_ball(img, radius=10, num_threads=num_threads)
rolling_ball(img, radius=10, nansafe=True, num_threads=num_threads)
def test_ndim():
image = data.cells3d()[:5, 1, ...]
kernel = ellipsoid_kernel((3, 100, 100), 100)
rolling_ball(image, kernel=kernel)

View File

@@ -0,0 +1,236 @@
import numpy as np
from skimage.restoration import unwrap_phase
import sys
from skimage._shared import testing
from skimage._shared.testing import (
assert_array_almost_equal_nulp,
assert_almost_equal,
assert_array_equal,
assert_,
skipif,
)
from skimage._shared._warnings import expected_warnings
def assert_phase_almost_equal(a, b, *args, **kwargs):
"""An assert_almost_equal insensitive to phase shifts of n*2*pi."""
shift = 2 * np.pi * np.round((b.mean() - a.mean()) / (2 * np.pi))
with expected_warnings(
[r'invalid value encountered|\A\Z', r'divide by zero encountered|\A\Z']
):
print('assert_phase_allclose, abs', np.max(np.abs(a - (b - shift))))
print('assert_phase_allclose, rel', np.max(np.abs((a - (b - shift)) / a)))
if np.ma.isMaskedArray(a):
assert_(np.ma.isMaskedArray(b))
assert_array_equal(a.mask, b.mask)
assert_(a.fill_value == b.fill_value)
au = np.asarray(a)
bu = np.asarray(b)
with expected_warnings(
[r'invalid value encountered|\A\Z', r'divide by zero encountered|\A\Z']
):
print(
'assert_phase_allclose, no mask, abs', np.max(np.abs(au - (bu - shift)))
)
print(
'assert_phase_allclose, no mask, rel',
np.max(np.abs((au - (bu - shift)) / au)),
)
assert_array_almost_equal_nulp(a + shift, b, *args, **kwargs)
def check_unwrap(image, mask=None):
image_wrapped = np.angle(np.exp(1j * image))
if mask is not None:
print('Testing a masked image')
image = np.ma.array(image, mask=mask, fill_value=0.5)
image_wrapped = np.ma.array(image_wrapped, mask=mask, fill_value=0.5)
image_unwrapped = unwrap_phase(image_wrapped, rng=0)
assert_phase_almost_equal(image_unwrapped, image)
def test_unwrap_1d():
image = np.linspace(0, 10 * np.pi, 100)
check_unwrap(image)
# Masked arrays are not allowed in 1D
with testing.raises(ValueError):
check_unwrap(image, True)
# wrap_around is not allowed in 1D
with testing.raises(ValueError):
unwrap_phase(image, True, rng=0)
@testing.parametrize("check_with_mask", (False, True))
def test_unwrap_2d(check_with_mask):
mask = None
x, y = np.ogrid[:8, :16]
image = 2 * np.pi * (x * 0.2 + y * 0.1)
if check_with_mask:
mask = np.zeros(image.shape, dtype=bool)
mask[4:6, 4:8] = True
check_unwrap(image, mask)
@testing.parametrize("check_with_mask", (False, True))
def test_unwrap_3d(check_with_mask):
mask = None
x, y, z = np.ogrid[:8, :12, :16]
image = 2 * np.pi * (x * 0.2 + y * 0.1 + z * 0.05)
if check_with_mask:
mask = np.zeros(image.shape, dtype=bool)
mask[4:6, 4:6, 1:3] = True
check_unwrap(image, mask)
def check_wrap_around(ndim, axis):
# create a ramp, but with the last pixel along axis equalling the first
elements = 100
ramp = np.linspace(0, 12 * np.pi, elements)
ramp[-1] = ramp[0]
image = ramp.reshape(tuple([elements if n == axis else 1 for n in range(ndim)]))
image_wrapped = np.angle(np.exp(1j * image))
index_first = tuple([0] * ndim)
index_last = tuple([-1 if n == axis else 0 for n in range(ndim)])
# unwrap the image without wrap around
# We do not want warnings about length 1 dimensions
with expected_warnings([r'Image has a length 1 dimension|\A\Z']):
image_unwrap_no_wrap_around = unwrap_phase(image_wrapped, rng=0)
print(
'endpoints without wrap_around:',
image_unwrap_no_wrap_around[index_first],
image_unwrap_no_wrap_around[index_last],
)
# without wrap around, the endpoints of the image should differ
assert_(
abs(
image_unwrap_no_wrap_around[index_first]
- image_unwrap_no_wrap_around[index_last]
)
> np.pi
)
# unwrap the image with wrap around
wrap_around = [n == axis for n in range(ndim)]
# We do not want warnings about length 1 dimensions
with expected_warnings([r'Image has a length 1 dimension.|\A\Z']):
image_unwrap_wrap_around = unwrap_phase(image_wrapped, wrap_around, rng=0)
print(
'endpoints with wrap_around:',
image_unwrap_wrap_around[index_first],
image_unwrap_wrap_around[index_last],
)
# with wrap around, the endpoints of the image should be equal
assert_almost_equal(
image_unwrap_wrap_around[index_first], image_unwrap_wrap_around[index_last]
)
dim_axis = [(ndim, axis) for ndim in (2, 3) for axis in range(ndim)]
@skipif(
sys.version_info[:2] == (3, 4),
reason="Doesn't work with python 3.4. See issue #3079",
)
@testing.parametrize("ndim, axis", dim_axis)
def test_wrap_around(ndim, axis):
check_wrap_around(ndim, axis)
def test_mask():
length = 100
ramps = [
np.linspace(0, 4 * np.pi, length),
np.linspace(0, 8 * np.pi, length),
np.linspace(0, 6 * np.pi, length),
]
image = np.vstack(ramps)
mask_1d = np.ones((length,), dtype=bool)
mask_1d[0] = mask_1d[-1] = False
for i in range(len(ramps)):
# mask all ramps but the i'th one
mask = np.zeros(image.shape, dtype=bool)
mask |= mask_1d.reshape(1, -1)
mask[i, :] = False # unmask i'th ramp
image_wrapped = np.ma.array(np.angle(np.exp(1j * image)), mask=mask)
image_unwrapped = unwrap_phase(image_wrapped)
image_unwrapped -= image_unwrapped[0, 0] # remove phase shift
# The end of the unwrapped array should have value equal to the
# endpoint of the unmasked ramp
assert_array_almost_equal_nulp(image_unwrapped[:, -1], image[i, -1])
assert_(np.ma.isMaskedArray(image_unwrapped))
# Same tests, but forcing use of the 3D unwrapper by reshaping
with expected_warnings(['length 1 dimension']):
shape = (1,) + image_wrapped.shape
image_wrapped_3d = image_wrapped.reshape(shape)
image_unwrapped_3d = unwrap_phase(image_wrapped_3d)
# remove phase shift
image_unwrapped_3d -= image_unwrapped_3d[0, 0, 0]
assert_array_almost_equal_nulp(image_unwrapped_3d[:, :, -1], image[i, -1])
def test_invalid_input():
with testing.raises(ValueError):
unwrap_phase(np.zeros([]))
with testing.raises(ValueError):
unwrap_phase(np.zeros((1, 1, 1, 1)))
with testing.raises(ValueError):
unwrap_phase(np.zeros((1, 1)), 3 * [False])
with testing.raises(ValueError):
unwrap_phase(np.zeros((1, 1)), 'False')
def test_unwrap_3d_middle_wrap_around():
# Segmentation fault in 3D unwrap phase with middle dimension connected
# GitHub issue #1171
image = np.zeros((20, 30, 40), dtype=np.float32)
unwrap = unwrap_phase(image, wrap_around=[False, True, False])
assert_(np.all(unwrap == 0))
def test_unwrap_2d_compressed_mask():
# ValueError when image is masked array with a compressed mask (no masked
# elements). GitHub issue #1346
image = np.ma.zeros((10, 10))
unwrap = unwrap_phase(image)
assert_(np.all(unwrap == 0))
def test_unwrap_2d_all_masked():
# Segmentation fault when image is masked array with a all elements masked
# GitHub issue #1347
# all elements masked
image = np.ma.zeros((10, 10))
image[:] = np.ma.masked
unwrap = unwrap_phase(image)
assert_(np.ma.isMaskedArray(unwrap))
assert_(np.all(unwrap.mask))
# 1 unmasked element, still zero edges
image = np.ma.zeros((10, 10))
image[:] = np.ma.masked
image[0, 0] = 0
unwrap = unwrap_phase(image)
assert_(np.ma.isMaskedArray(unwrap))
assert_(np.sum(unwrap.mask) == 99) # all but one masked
assert_(unwrap[0, 0] == 0)
def test_unwrap_3d_all_masked():
# all elements masked
image = np.ma.zeros((10, 10, 10))
image[:] = np.ma.masked
unwrap = unwrap_phase(image)
assert_(np.ma.isMaskedArray(unwrap))
assert_(np.all(unwrap.mask))
# 1 unmasked element, still zero edges
image = np.ma.zeros((10, 10, 10))
image[:] = np.ma.masked
image[0, 0, 0] = 0
unwrap = unwrap_phase(image)
assert_(np.ma.isMaskedArray(unwrap))
assert_(np.sum(unwrap.mask) == 999) # all but one masked
assert_(unwrap[0, 0, 0] == 0)