rm CondaPkg environment
This commit is contained in:
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1,317 +0,0 @@
|
||||
import numpy as np
|
||||
import pytest
|
||||
from numpy.testing import assert_array_equal, assert_equal
|
||||
from scipy import ndimage as ndi
|
||||
|
||||
from skimage import data, color, morphology
|
||||
from skimage.util import img_as_bool
|
||||
from skimage.morphology import binary, footprints, gray
|
||||
|
||||
|
||||
img = color.rgb2gray(data.astronaut())
|
||||
bw_img = img > 100 / 255.
|
||||
|
||||
|
||||
def test_non_square_image():
|
||||
footprint = morphology.square(3)
|
||||
binary_res = binary.binary_erosion(bw_img[:100, :200], footprint)
|
||||
gray_res = img_as_bool(gray.erosion(bw_img[:100, :200], footprint))
|
||||
assert_array_equal(binary_res, gray_res)
|
||||
|
||||
|
||||
def test_binary_erosion():
|
||||
footprint = morphology.square(3)
|
||||
binary_res = binary.binary_erosion(bw_img, footprint)
|
||||
gray_res = img_as_bool(gray.erosion(bw_img, footprint))
|
||||
assert_array_equal(binary_res, gray_res)
|
||||
|
||||
|
||||
def test_binary_dilation():
|
||||
footprint = morphology.square(3)
|
||||
binary_res = binary.binary_dilation(bw_img, footprint)
|
||||
gray_res = img_as_bool(gray.dilation(bw_img, footprint))
|
||||
assert_array_equal(binary_res, gray_res)
|
||||
|
||||
|
||||
def test_binary_closing():
|
||||
footprint = morphology.square(3)
|
||||
binary_res = binary.binary_closing(bw_img, footprint)
|
||||
gray_res = img_as_bool(gray.closing(bw_img, footprint))
|
||||
assert_array_equal(binary_res, gray_res)
|
||||
|
||||
|
||||
def test_binary_opening():
|
||||
footprint = morphology.square(3)
|
||||
binary_res = binary.binary_opening(bw_img, footprint)
|
||||
gray_res = img_as_bool(gray.opening(bw_img, footprint))
|
||||
assert_array_equal(binary_res, gray_res)
|
||||
|
||||
|
||||
def _get_decomp_test_data(function, ndim=2):
|
||||
if function == 'binary_erosion':
|
||||
img = np.ones((17, ) * ndim, dtype=np.uint8)
|
||||
img[8, 8] = 0
|
||||
elif function == 'binary_dilation':
|
||||
img = np.zeros((17, ) * ndim, dtype=np.uint8)
|
||||
img[8, 8] = 1
|
||||
else:
|
||||
img = data.binary_blobs(32, n_dim=ndim, seed=1)
|
||||
return img
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"function",
|
||||
["binary_erosion", "binary_dilation", "binary_closing", "binary_opening"],
|
||||
)
|
||||
@pytest.mark.parametrize("size", (3, 4, 11))
|
||||
@pytest.mark.parametrize("decomposition", ['separable', 'sequence'])
|
||||
def test_square_decomposition(function, size, decomposition):
|
||||
"""Validate footprint decomposition for various shapes.
|
||||
|
||||
comparison is made to the case without decomposition.
|
||||
"""
|
||||
footprint_ndarray = footprints.square(size, decomposition=None)
|
||||
footprint = footprints.square(size, decomposition=decomposition)
|
||||
img = _get_decomp_test_data(function)
|
||||
func = getattr(binary, function)
|
||||
expected = func(img, footprint=footprint_ndarray)
|
||||
out = func(img, footprint=footprint)
|
||||
assert_array_equal(expected, out)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"function",
|
||||
["binary_erosion", "binary_dilation", "binary_closing", "binary_opening"],
|
||||
)
|
||||
@pytest.mark.parametrize("nrows", (3, 4, 11))
|
||||
@pytest.mark.parametrize("ncols", (3, 4, 11))
|
||||
@pytest.mark.parametrize("decomposition", ['separable', 'sequence'])
|
||||
def test_rectangle_decomposition(function, nrows, ncols, decomposition):
|
||||
"""Validate footprint decomposition for various shapes.
|
||||
|
||||
comparison is made to the case without decomposition.
|
||||
"""
|
||||
footprint_ndarray = footprints.rectangle(nrows, ncols, decomposition=None)
|
||||
footprint = footprints.rectangle(nrows, ncols, decomposition=decomposition)
|
||||
img = _get_decomp_test_data(function)
|
||||
func = getattr(binary, function)
|
||||
expected = func(img, footprint=footprint_ndarray)
|
||||
out = func(img, footprint=footprint)
|
||||
assert_array_equal(expected, out)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"function",
|
||||
["binary_erosion", "binary_dilation", "binary_closing", "binary_opening"],
|
||||
)
|
||||
@pytest.mark.parametrize("m", (0, 1, 2, 3, 4, 5))
|
||||
@pytest.mark.parametrize("n", (0, 1, 2, 3, 4, 5))
|
||||
@pytest.mark.parametrize("decomposition", ['sequence'])
|
||||
def test_octagon_decomposition(function, m, n, decomposition):
|
||||
"""Validate footprint decomposition for various shapes.
|
||||
|
||||
comparison is made to the case without decomposition.
|
||||
"""
|
||||
if m == 0 and n == 0:
|
||||
with pytest.raises(ValueError):
|
||||
footprints.octagon(m, n, decomposition=decomposition)
|
||||
else:
|
||||
footprint_ndarray = footprints.octagon(m, n, decomposition=None)
|
||||
footprint = footprints.octagon(m, n, decomposition=decomposition)
|
||||
img = _get_decomp_test_data(function)
|
||||
func = getattr(binary, function)
|
||||
expected = func(img, footprint=footprint_ndarray)
|
||||
out = func(img, footprint=footprint)
|
||||
assert_array_equal(expected, out)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"function",
|
||||
["binary_erosion", "binary_dilation", "binary_closing", "binary_opening"],
|
||||
)
|
||||
@pytest.mark.parametrize("radius", (1, 2, 5))
|
||||
@pytest.mark.parametrize("decomposition", ['sequence'])
|
||||
def test_diamond_decomposition(function, radius, decomposition):
|
||||
"""Validate footprint decomposition for various shapes.
|
||||
|
||||
comparison is made to the case without decomposition.
|
||||
"""
|
||||
footprint_ndarray = footprints.diamond(radius, decomposition=None)
|
||||
footprint = footprints.diamond(radius, decomposition=decomposition)
|
||||
img = _get_decomp_test_data(function)
|
||||
func = getattr(binary, function)
|
||||
expected = func(img, footprint=footprint_ndarray)
|
||||
out = func(img, footprint=footprint)
|
||||
assert_array_equal(expected, out)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"function",
|
||||
["binary_erosion", "binary_dilation", "binary_closing", "binary_opening"],
|
||||
)
|
||||
@pytest.mark.parametrize("size", (3, 4, 5))
|
||||
@pytest.mark.parametrize("decomposition", ['separable', 'sequence'])
|
||||
def test_cube_decomposition(function, size, decomposition):
|
||||
"""Validate footprint decomposition for various shapes.
|
||||
|
||||
comparison is made to the case without decomposition.
|
||||
"""
|
||||
footprint_ndarray = footprints.cube(size, decomposition=None)
|
||||
footprint = footprints.cube(size, decomposition=decomposition)
|
||||
img = _get_decomp_test_data(function, ndim=3)
|
||||
func = getattr(binary, function)
|
||||
expected = func(img, footprint=footprint_ndarray)
|
||||
out = func(img, footprint=footprint)
|
||||
assert_array_equal(expected, out)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"function",
|
||||
["binary_erosion", "binary_dilation", "binary_closing", "binary_opening"],
|
||||
)
|
||||
@pytest.mark.parametrize("radius", (1, 2, 3))
|
||||
@pytest.mark.parametrize("decomposition", ['sequence'])
|
||||
def test_octahedron_decomposition(function, radius, decomposition):
|
||||
"""Validate footprint decomposition for various shapes.
|
||||
|
||||
comparison is made to the case without decomposition.
|
||||
"""
|
||||
footprint_ndarray = footprints.octahedron(radius, decomposition=None)
|
||||
footprint = footprints.octahedron(radius, decomposition=decomposition)
|
||||
img = _get_decomp_test_data(function, ndim=3)
|
||||
func = getattr(binary, function)
|
||||
expected = func(img, footprint=footprint_ndarray)
|
||||
out = func(img, footprint=footprint)
|
||||
assert_array_equal(expected, out)
|
||||
|
||||
|
||||
def test_footprint_overflow():
|
||||
footprint = np.ones((17, 17), dtype=np.uint8)
|
||||
img = np.zeros((20, 20), dtype=bool)
|
||||
img[2:19, 2:19] = True
|
||||
binary_res = binary.binary_erosion(img, footprint)
|
||||
gray_res = img_as_bool(gray.erosion(img, footprint))
|
||||
assert_array_equal(binary_res, gray_res)
|
||||
|
||||
|
||||
def test_out_argument():
|
||||
for func in (binary.binary_erosion, binary.binary_dilation):
|
||||
footprint = np.ones((3, 3), dtype=np.uint8)
|
||||
img = np.ones((10, 10))
|
||||
out = np.zeros_like(img)
|
||||
out_saved = out.copy()
|
||||
func(img, footprint, out=out)
|
||||
assert np.any(out != out_saved)
|
||||
assert_array_equal(out, func(img, footprint))
|
||||
|
||||
|
||||
binary_functions = [binary.binary_erosion, binary.binary_dilation,
|
||||
binary.binary_opening, binary.binary_closing]
|
||||
|
||||
|
||||
@pytest.mark.parametrize("function", binary_functions)
|
||||
def test_default_footprint(function):
|
||||
footprint = morphology.diamond(radius=1)
|
||||
image = np.array([[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
||||
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
||||
[0, 0, 1, 1, 1, 1, 1, 1, 0, 0],
|
||||
[0, 0, 1, 1, 1, 1, 1, 1, 0, 0],
|
||||
[0, 0, 1, 1, 1, 1, 1, 1, 0, 0],
|
||||
[0, 0, 1, 1, 1, 0, 0, 1, 0, 0],
|
||||
[0, 0, 1, 1, 1, 0, 0, 1, 0, 0],
|
||||
[0, 0, 1, 1, 1, 0, 0, 1, 0, 0],
|
||||
[0, 0, 1, 1, 1, 1, 1, 1, 0, 0],
|
||||
[0, 0, 1, 1, 1, 1, 1, 1, 0, 0],
|
||||
[0, 0, 1, 1, 1, 1, 1, 1, 0, 0],
|
||||
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
||||
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0]], np.uint8)
|
||||
im_expected = function(image, footprint)
|
||||
im_test = function(image)
|
||||
assert_array_equal(im_expected, im_test)
|
||||
|
||||
|
||||
def test_3d_fallback_default_footprint():
|
||||
# 3x3x3 cube inside a 7x7x7 image:
|
||||
image = np.zeros((7, 7, 7), bool)
|
||||
image[2:-2, 2:-2, 2:-2] = 1
|
||||
|
||||
opened = binary.binary_opening(image)
|
||||
|
||||
# expect a "hyper-cross" centered in the 5x5x5:
|
||||
image_expected = np.zeros((7, 7, 7), dtype=bool)
|
||||
image_expected[2:5, 2:5, 2:5] = ndi.generate_binary_structure(3, 1)
|
||||
assert_array_equal(opened, image_expected)
|
||||
|
||||
|
||||
binary_3d_fallback_functions = [binary.binary_opening, binary.binary_closing]
|
||||
|
||||
|
||||
@pytest.mark.parametrize("function", binary_3d_fallback_functions)
|
||||
def test_3d_fallback_cube_footprint(function):
|
||||
# 3x3x3 cube inside a 7x7x7 image:
|
||||
image = np.zeros((7, 7, 7), bool)
|
||||
image[2:-2, 2:-2, 2:-2] = 1
|
||||
|
||||
cube = np.ones((3, 3, 3), dtype=np.uint8)
|
||||
|
||||
new_image = function(image, cube)
|
||||
assert_array_equal(new_image, image)
|
||||
|
||||
|
||||
def test_2d_ndimage_equivalence():
|
||||
image = np.zeros((9, 9), np.uint16)
|
||||
image[2:-2, 2:-2] = 2**14
|
||||
image[3:-3, 3:-3] = 2**15
|
||||
image[4, 4] = 2**16-1
|
||||
|
||||
bin_opened = binary.binary_opening(image)
|
||||
bin_closed = binary.binary_closing(image)
|
||||
|
||||
footprint = ndi.generate_binary_structure(2, 1)
|
||||
ndimage_opened = ndi.binary_opening(image, structure=footprint)
|
||||
ndimage_closed = ndi.binary_closing(image, structure=footprint)
|
||||
|
||||
assert_array_equal(bin_opened, ndimage_opened)
|
||||
assert_array_equal(bin_closed, ndimage_closed)
|
||||
|
||||
|
||||
def test_binary_output_2d():
|
||||
image = np.zeros((9, 9), np.uint16)
|
||||
image[2:-2, 2:-2] = 2**14
|
||||
image[3:-3, 3:-3] = 2**15
|
||||
image[4, 4] = 2**16-1
|
||||
|
||||
bin_opened = binary.binary_opening(image)
|
||||
bin_closed = binary.binary_closing(image)
|
||||
|
||||
int_opened = np.empty_like(image, dtype=np.uint8)
|
||||
int_closed = np.empty_like(image, dtype=np.uint8)
|
||||
binary.binary_opening(image, out=int_opened)
|
||||
binary.binary_closing(image, out=int_closed)
|
||||
|
||||
assert_equal(bin_opened.dtype, bool)
|
||||
assert_equal(bin_closed.dtype, bool)
|
||||
|
||||
assert_equal(int_opened.dtype, np.uint8)
|
||||
assert_equal(int_closed.dtype, np.uint8)
|
||||
|
||||
|
||||
def test_binary_output_3d():
|
||||
image = np.zeros((9, 9, 9), np.uint16)
|
||||
image[2:-2, 2:-2, 2:-2] = 2**14
|
||||
image[3:-3, 3:-3, 3:-3] = 2**15
|
||||
image[4, 4, 4] = 2**16-1
|
||||
|
||||
bin_opened = binary.binary_opening(image)
|
||||
bin_closed = binary.binary_closing(image)
|
||||
|
||||
int_opened = np.empty_like(image, dtype=np.uint8)
|
||||
int_closed = np.empty_like(image, dtype=np.uint8)
|
||||
binary.binary_opening(image, out=int_opened)
|
||||
binary.binary_closing(image, out=int_closed)
|
||||
|
||||
assert_equal(bin_opened.dtype, bool)
|
||||
assert_equal(bin_closed.dtype, bool)
|
||||
|
||||
assert_equal(int_opened.dtype, np.uint8)
|
||||
assert_equal(int_closed.dtype, np.uint8)
|
||||
@@ -1,197 +0,0 @@
|
||||
import numpy as np
|
||||
from skimage.morphology import convex_hull_image, convex_hull_object
|
||||
from skimage.morphology._convex_hull import possible_hull
|
||||
|
||||
from skimage._shared import testing
|
||||
from skimage._shared.testing import assert_array_equal
|
||||
from skimage._shared._warnings import expected_warnings
|
||||
|
||||
|
||||
def test_basic():
|
||||
image = np.array(
|
||||
[[0, 0, 0, 0, 0, 0, 0, 0, 0],
|
||||
[0, 0, 0, 0, 1, 0, 0, 0, 0],
|
||||
[0, 0, 0, 1, 0, 1, 0, 0, 0],
|
||||
[0, 0, 1, 0, 0, 0, 1, 0, 0],
|
||||
[0, 1, 0, 0, 0, 0, 0, 1, 0],
|
||||
[0, 0, 0, 0, 0, 0, 0, 0, 0]], dtype=bool)
|
||||
|
||||
expected = np.array(
|
||||
[[0, 0, 0, 0, 0, 0, 0, 0, 0],
|
||||
[0, 0, 0, 0, 1, 0, 0, 0, 0],
|
||||
[0, 0, 0, 1, 1, 1, 0, 0, 0],
|
||||
[0, 0, 1, 1, 1, 1, 1, 0, 0],
|
||||
[0, 1, 1, 1, 1, 1, 1, 1, 0],
|
||||
[0, 0, 0, 0, 0, 0, 0, 0, 0]], dtype=bool)
|
||||
|
||||
assert_array_equal(convex_hull_image(image), expected)
|
||||
|
||||
|
||||
def test_empty_image():
|
||||
image = np.zeros((6, 6), dtype=bool)
|
||||
with expected_warnings(['entirely zero']):
|
||||
assert_array_equal(convex_hull_image(image), image)
|
||||
|
||||
|
||||
def test_qhull_offset_example():
|
||||
nonzeros = (([1367, 1368, 1368, 1368, 1369, 1369, 1369, 1369, 1369, 1370,
|
||||
1370, 1370, 1370, 1370, 1370, 1370, 1371, 1371, 1371, 1371,
|
||||
1371, 1371, 1371, 1371, 1371, 1372, 1372, 1372, 1372, 1372,
|
||||
1372, 1372, 1372, 1372, 1373, 1373, 1373, 1373, 1373, 1373,
|
||||
1373, 1373, 1373, 1374, 1374, 1374, 1374, 1374, 1374, 1374,
|
||||
1375, 1375, 1375, 1375, 1375, 1376, 1376, 1376, 1377, 1372]),
|
||||
([151, 150, 151, 152, 149, 150, 151, 152, 153, 148, 149, 150,
|
||||
151, 152, 153, 154, 147, 148, 149, 150, 151, 152, 153, 154,
|
||||
155, 146, 147, 148, 149, 150, 151, 152, 153, 154, 146, 147,
|
||||
148, 149, 150, 151, 152, 153, 154, 147, 148, 149, 150, 151,
|
||||
152, 153, 148, 149, 150, 151, 152, 149, 150, 151, 150, 155]))
|
||||
image = np.zeros((1392, 1040), dtype=bool)
|
||||
image[nonzeros] = True
|
||||
expected = image.copy()
|
||||
assert_array_equal(convex_hull_image(image), expected)
|
||||
|
||||
|
||||
def test_pathological_qhull_example():
|
||||
image = np.array(
|
||||
[[0, 0, 0, 0, 1, 0, 0],
|
||||
[0, 0, 1, 1, 1, 1, 1],
|
||||
[1, 1, 1, 0, 0, 0, 0]], dtype=bool)
|
||||
expected = np.array(
|
||||
[[0, 0, 0, 1, 1, 1, 0],
|
||||
[0, 1, 1, 1, 1, 1, 1],
|
||||
[1, 1, 1, 1, 1, 0, 0]], dtype=bool)
|
||||
assert_array_equal(convex_hull_image(image), expected)
|
||||
|
||||
|
||||
def test_pathological_qhull_labels():
|
||||
image = np.array([[0, 0, 0, 0, 1, 0, 0],
|
||||
[0, 0, 1, 1, 1, 1, 1],
|
||||
[1, 1, 1, 0, 0, 0, 0]], dtype=bool)
|
||||
|
||||
expected = np.array([[0, 0, 0, 0, 1, 0, 0],
|
||||
[0, 0, 1, 1, 1, 1, 1],
|
||||
[1, 1, 1, 1, 0, 0, 0]], dtype=bool)
|
||||
|
||||
actual = convex_hull_image(image, include_borders=False)
|
||||
assert_array_equal(actual, expected)
|
||||
|
||||
|
||||
def test_possible_hull():
|
||||
image = np.array(
|
||||
[[0, 0, 0, 0, 0, 0, 0, 0, 0],
|
||||
[0, 0, 0, 0, 1, 0, 0, 0, 0],
|
||||
[0, 0, 0, 1, 0, 1, 0, 0, 0],
|
||||
[0, 0, 1, 1, 1, 1, 1, 0, 0],
|
||||
[0, 1, 1, 1, 1, 1, 1, 1, 0],
|
||||
[0, 0, 0, 0, 0, 0, 0, 0, 0]], dtype=np.uint8)
|
||||
|
||||
expected = np.array([[1, 4],
|
||||
[2, 3],
|
||||
[3, 2],
|
||||
[4, 1],
|
||||
[4, 1],
|
||||
[3, 2],
|
||||
[2, 3],
|
||||
[1, 4],
|
||||
[2, 5],
|
||||
[3, 6],
|
||||
[4, 7],
|
||||
[2, 5],
|
||||
[3, 6],
|
||||
[4, 7],
|
||||
[4, 2],
|
||||
[4, 3],
|
||||
[4, 4],
|
||||
[4, 5],
|
||||
[4, 6]])
|
||||
|
||||
ph = possible_hull(image)
|
||||
assert_array_equal(ph, expected)
|
||||
|
||||
|
||||
def test_object():
|
||||
image = np.array(
|
||||
[[0, 0, 0, 0, 0, 0, 0, 0, 0],
|
||||
[1, 0, 0, 0, 0, 0, 0, 0, 0],
|
||||
[1, 0, 0, 0, 0, 0, 0, 0, 0],
|
||||
[1, 0, 0, 0, 0, 0, 0, 0, 0],
|
||||
[1, 1, 1, 1, 0, 0, 1, 0, 1],
|
||||
[1, 0, 0, 0, 0, 0, 0, 1, 0],
|
||||
[1, 0, 0, 0, 0, 0, 1, 0, 1],
|
||||
[1, 0, 0, 0, 0, 0, 0, 0, 0],
|
||||
[0, 0, 0, 0, 0, 0, 0, 0, 0]], dtype=bool)
|
||||
|
||||
expected_conn_1 = np.array(
|
||||
[[0, 0, 0, 0, 0, 0, 0, 0, 0],
|
||||
[1, 0, 0, 0, 0, 0, 0, 0, 0],
|
||||
[1, 1, 0, 0, 0, 0, 0, 0, 0],
|
||||
[1, 1, 1, 0, 0, 0, 0, 0, 0],
|
||||
[1, 1, 1, 1, 0, 0, 1, 0, 1],
|
||||
[1, 1, 1, 0, 0, 0, 0, 1, 0],
|
||||
[1, 1, 0, 0, 0, 0, 1, 0, 1],
|
||||
[1, 0, 0, 0, 0, 0, 0, 0, 0],
|
||||
[0, 0, 0, 0, 0, 0, 0, 0, 0]], dtype=bool)
|
||||
|
||||
assert_array_equal(convex_hull_object(image, connectivity=1),
|
||||
expected_conn_1)
|
||||
|
||||
expected_conn_2 = np.array(
|
||||
[[0, 0, 0, 0, 0, 0, 0, 0, 0],
|
||||
[1, 0, 0, 0, 0, 0, 0, 0, 0],
|
||||
[1, 1, 0, 0, 0, 0, 0, 0, 0],
|
||||
[1, 1, 1, 0, 0, 0, 0, 0, 0],
|
||||
[1, 1, 1, 1, 0, 0, 1, 1, 1],
|
||||
[1, 1, 1, 0, 0, 0, 1, 1, 1],
|
||||
[1, 1, 0, 0, 0, 0, 1, 1, 1],
|
||||
[1, 0, 0, 0, 0, 0, 0, 0, 0],
|
||||
[0, 0, 0, 0, 0, 0, 0, 0, 0]], dtype=bool)
|
||||
|
||||
assert_array_equal(convex_hull_object(image, connectivity=2),
|
||||
expected_conn_2)
|
||||
|
||||
with testing.raises(ValueError):
|
||||
convex_hull_object(image, connectivity=3)
|
||||
|
||||
out = convex_hull_object(image, connectivity=1)
|
||||
assert_array_equal(out, expected_conn_1)
|
||||
|
||||
|
||||
def test_non_c_contiguous():
|
||||
# 2D Fortran-contiguous
|
||||
image = np.ones((2, 2), order='F', dtype=bool)
|
||||
assert_array_equal(convex_hull_image(image), image)
|
||||
# 3D Fortran-contiguous
|
||||
image = np.ones((2, 2, 2), order='F', dtype=bool)
|
||||
assert_array_equal(convex_hull_image(image), image)
|
||||
# 3D non-contiguous
|
||||
image = np.transpose(np.ones((2, 2, 2), dtype=bool), [0, 2, 1])
|
||||
assert_array_equal(convex_hull_image(image), image)
|
||||
|
||||
|
||||
@testing.fixture
|
||||
def images2d3d():
|
||||
from ...measure.tests.test_regionprops import SAMPLE as image
|
||||
image3d = np.stack((image, image, image))
|
||||
return image, image3d
|
||||
|
||||
|
||||
def test_consistent_2d_3d_hulls(images2d3d):
|
||||
image, image3d = images2d3d
|
||||
chimage = convex_hull_image(image)
|
||||
chimage[8, 0] = True # correct for single point exactly on hull edge
|
||||
chimage3d = convex_hull_image(image3d)
|
||||
assert_array_equal(chimage3d[1], chimage)
|
||||
|
||||
|
||||
def test_few_points():
|
||||
image = np.array(
|
||||
[[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, 1, 0, 0, 0, 0, 0, 0, 0],
|
||||
[0, 0, 0, 0, 0, 0, 0, 0, 0]], dtype=np.uint8)
|
||||
image3d = np.stack([image, image, image])
|
||||
with testing.assert_warns(UserWarning):
|
||||
chimage3d = convex_hull_image(image3d)
|
||||
assert_array_equal(chimage3d, np.zeros(image3d.shape, dtype=bool))
|
||||
@@ -1,637 +0,0 @@
|
||||
import math
|
||||
import unittest
|
||||
|
||||
import numpy as np
|
||||
from numpy.testing import assert_equal
|
||||
from pytest import raises, warns
|
||||
|
||||
from skimage._shared.testing import expected_warnings
|
||||
from skimage.morphology import extrema
|
||||
|
||||
|
||||
eps = 1e-12
|
||||
|
||||
|
||||
def diff(a, b):
|
||||
a = np.asarray(a, dtype=np.float64)
|
||||
b = np.asarray(b, dtype=np.float64)
|
||||
t = ((a - b) ** 2).sum()
|
||||
return math.sqrt(t)
|
||||
|
||||
|
||||
class TestExtrema():
|
||||
|
||||
def test_saturated_arithmetic(self):
|
||||
"""Adding/subtracting a constant and clipping"""
|
||||
# Test for unsigned integer
|
||||
data = np.array([[250, 251, 5, 5],
|
||||
[100, 200, 253, 252],
|
||||
[4, 10, 1, 3]],
|
||||
dtype=np.uint8)
|
||||
# adding the constant
|
||||
img_constant_added = extrema._add_constant_clip(data, 4)
|
||||
expected = np.array([[254, 255, 9, 9],
|
||||
[104, 204, 255, 255],
|
||||
[8, 14, 5, 7]],
|
||||
dtype=np.uint8)
|
||||
error = diff(img_constant_added, expected)
|
||||
assert error < eps
|
||||
img_constant_subtracted = extrema._subtract_constant_clip(data, 4)
|
||||
expected = np.array([[246, 247, 1, 1],
|
||||
[96, 196, 249, 248],
|
||||
[0, 6, 0, 0]],
|
||||
dtype=np.uint8)
|
||||
error = diff(img_constant_subtracted, expected)
|
||||
assert error < eps
|
||||
|
||||
# Test for signed integer
|
||||
data = np.array([[32767, 32766],
|
||||
[-32768, -32767]],
|
||||
dtype=np.int16)
|
||||
img_constant_added = extrema._add_constant_clip(data, 1)
|
||||
expected = np.array([[32767, 32767],
|
||||
[-32767, -32766]],
|
||||
dtype=np.int16)
|
||||
error = diff(img_constant_added, expected)
|
||||
assert error < eps
|
||||
img_constant_subtracted = extrema._subtract_constant_clip(data, 1)
|
||||
expected = np.array([[32766, 32765],
|
||||
[-32768, -32768]],
|
||||
dtype=np.int16)
|
||||
error = diff(img_constant_subtracted, expected)
|
||||
assert error < eps
|
||||
|
||||
def test_h_maxima(self):
|
||||
"""h-maxima for various data types"""
|
||||
|
||||
data = np.array([[10, 11, 13, 14, 14, 15, 14, 14, 13, 11],
|
||||
[11, 13, 15, 16, 16, 16, 16, 16, 15, 13],
|
||||
[13, 15, 40, 40, 18, 18, 18, 60, 60, 15],
|
||||
[14, 16, 40, 40, 19, 19, 19, 60, 60, 16],
|
||||
[14, 16, 18, 19, 19, 19, 19, 19, 18, 16],
|
||||
[15, 16, 18, 19, 19, 20, 19, 19, 18, 16],
|
||||
[14, 16, 18, 19, 19, 19, 19, 19, 18, 16],
|
||||
[14, 16, 80, 80, 19, 19, 19, 100, 100, 16],
|
||||
[13, 15, 80, 80, 18, 18, 18, 100, 100, 15],
|
||||
[11, 13, 15, 16, 16, 16, 16, 16, 15, 13]],
|
||||
dtype=np.uint8)
|
||||
|
||||
expected_result = np.array([[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
||||
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
||||
[0, 0, 0, 0, 0, 0, 0, 1, 1, 0],
|
||||
[0, 0, 0, 0, 0, 0, 0, 1, 1, 0],
|
||||
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
||||
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
||||
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
||||
[0, 0, 1, 1, 0, 0, 0, 1, 1, 0],
|
||||
[0, 0, 1, 1, 0, 0, 0, 1, 1, 0],
|
||||
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0]],
|
||||
dtype=np.uint8)
|
||||
for dtype in [np.uint8, np.uint64, np.int8, np.int64]:
|
||||
data = data.astype(dtype)
|
||||
out = extrema.h_maxima(data, 40)
|
||||
|
||||
error = diff(expected_result, out)
|
||||
assert error < eps
|
||||
|
||||
def test_h_minima(self):
|
||||
"""h-minima for various data types"""
|
||||
|
||||
data = np.array([[10, 11, 13, 14, 14, 15, 14, 14, 13, 11],
|
||||
[11, 13, 15, 16, 16, 16, 16, 16, 15, 13],
|
||||
[13, 15, 40, 40, 18, 18, 18, 60, 60, 15],
|
||||
[14, 16, 40, 40, 19, 19, 19, 60, 60, 16],
|
||||
[14, 16, 18, 19, 19, 19, 19, 19, 18, 16],
|
||||
[15, 16, 18, 19, 19, 20, 19, 19, 18, 16],
|
||||
[14, 16, 18, 19, 19, 19, 19, 19, 18, 16],
|
||||
[14, 16, 80, 80, 19, 19, 19, 100, 100, 16],
|
||||
[13, 15, 80, 80, 18, 18, 18, 100, 100, 15],
|
||||
[11, 13, 15, 16, 16, 16, 16, 16, 15, 13]],
|
||||
dtype=np.uint8)
|
||||
data = 100 - data
|
||||
expected_result = np.array([[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
||||
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
||||
[0, 0, 0, 0, 0, 0, 0, 1, 1, 0],
|
||||
[0, 0, 0, 0, 0, 0, 0, 1, 1, 0],
|
||||
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
||||
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
||||
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
||||
[0, 0, 1, 1, 0, 0, 0, 1, 1, 0],
|
||||
[0, 0, 1, 1, 0, 0, 0, 1, 1, 0],
|
||||
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0]],
|
||||
dtype=np.uint8)
|
||||
for dtype in [np.uint8, np.uint64, np.int8, np.int64]:
|
||||
data = data.astype(dtype)
|
||||
out = extrema.h_minima(data, 40)
|
||||
|
||||
error = diff(expected_result, out)
|
||||
assert error < eps
|
||||
assert out.dtype == expected_result.dtype
|
||||
|
||||
def test_extrema_float(self):
|
||||
"""specific tests for float type"""
|
||||
data = np.array([[0.10, 0.11, 0.13, 0.14, 0.14, 0.15, 0.14,
|
||||
0.14, 0.13, 0.11],
|
||||
[0.11, 0.13, 0.15, 0.16, 0.16, 0.16, 0.16,
|
||||
0.16, 0.15, 0.13],
|
||||
[0.13, 0.15, 0.40, 0.40, 0.18, 0.18, 0.18,
|
||||
0.60, 0.60, 0.15],
|
||||
[0.14, 0.16, 0.40, 0.40, 0.19, 0.19, 0.19,
|
||||
0.60, 0.60, 0.16],
|
||||
[0.14, 0.16, 0.18, 0.19, 0.19, 0.19, 0.19,
|
||||
0.19, 0.18, 0.16],
|
||||
[0.15, 0.182, 0.18, 0.19, 0.204, 0.20, 0.19,
|
||||
0.19, 0.18, 0.16],
|
||||
[0.14, 0.16, 0.18, 0.19, 0.19, 0.19, 0.19,
|
||||
0.19, 0.18, 0.16],
|
||||
[0.14, 0.16, 0.80, 0.80, 0.19, 0.19, 0.19,
|
||||
1.0, 1.0, 0.16],
|
||||
[0.13, 0.15, 0.80, 0.80, 0.18, 0.18, 0.18,
|
||||
1.0, 1.0, 0.15],
|
||||
[0.11, 0.13, 0.15, 0.16, 0.16, 0.16, 0.16,
|
||||
0.16, 0.15, 0.13]],
|
||||
dtype=np.float32)
|
||||
inverted_data = 1.0 - data
|
||||
|
||||
out = extrema.h_maxima(data, 0.003)
|
||||
expected_result = np.array([[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
||||
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
||||
[0, 0, 1, 1, 0, 0, 0, 1, 1, 0],
|
||||
[0, 0, 1, 1, 0, 0, 0, 1, 1, 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, 1, 1, 0, 0, 0, 1, 1, 0],
|
||||
[0, 0, 1, 1, 0, 0, 0, 1, 1, 0],
|
||||
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0]],
|
||||
dtype=np.uint8)
|
||||
|
||||
error = diff(expected_result, out)
|
||||
assert error < eps
|
||||
|
||||
out = extrema.h_minima(inverted_data, 0.003)
|
||||
error = diff(expected_result, out)
|
||||
assert error < eps
|
||||
|
||||
def test_h_maxima_float_image(self):
|
||||
"""specific tests for h-maxima float image type"""
|
||||
w = 10
|
||||
x, y = np.mgrid[0:w, 0:w]
|
||||
data = 20 - 0.2 * ((x - w / 2) ** 2 + (y - w / 2) ** 2)
|
||||
data[2:4, 2:4] = 40
|
||||
data[2:4, 7:9] = 60
|
||||
data[7:9, 2:4] = 80
|
||||
data[7:9, 7:9] = 100
|
||||
data = data.astype(np.float32)
|
||||
|
||||
expected_result = np.zeros_like(data)
|
||||
expected_result[(data > 19.9)] = 1.0
|
||||
|
||||
for h in [1.0e-12, 1.0e-6, 1.0e-3, 1.0e-2, 1.0e-1, 0.1]:
|
||||
out = extrema.h_maxima(data, h)
|
||||
error = diff(expected_result, out)
|
||||
assert error < eps
|
||||
|
||||
def test_h_maxima_float_h(self):
|
||||
"""specific tests for h-maxima float h parameter"""
|
||||
data = np.array([[0, 0, 0, 0, 0],
|
||||
[0, 3, 3, 3, 0],
|
||||
[0, 3, 4, 3, 0],
|
||||
[0, 3, 3, 3, 0],
|
||||
[0, 0, 0, 0, 0]], dtype=np.uint8)
|
||||
|
||||
h_vals = np.linspace(1.0, 2.0, 100)
|
||||
failures = 0
|
||||
|
||||
for h in h_vals:
|
||||
if h % 1 != 0:
|
||||
msgs = ['possible precision loss converting image']
|
||||
else:
|
||||
msgs = []
|
||||
|
||||
with expected_warnings(msgs):
|
||||
maxima = extrema.h_maxima(data, h)
|
||||
|
||||
if (maxima[2, 2] == 0):
|
||||
failures += 1
|
||||
|
||||
assert (failures == 0)
|
||||
|
||||
def test_h_maxima_large_h(self):
|
||||
"""test that h-maxima works correctly for large h"""
|
||||
data = np.array([[10, 10, 10, 10, 10],
|
||||
[10, 13, 13, 13, 10],
|
||||
[10, 13, 14, 13, 10],
|
||||
[10, 13, 13, 13, 10],
|
||||
[10, 10, 10, 10, 10]], dtype=np.uint8)
|
||||
|
||||
maxima = extrema.h_maxima(data, 5)
|
||||
assert (np.sum(maxima) == 0)
|
||||
|
||||
data = np.array([[10, 10, 10, 10, 10],
|
||||
[10, 13, 13, 13, 10],
|
||||
[10, 13, 14, 13, 10],
|
||||
[10, 13, 13, 13, 10],
|
||||
[10, 10, 10, 10, 10]], dtype=np.float32)
|
||||
|
||||
maxima = extrema.h_maxima(data, 5.0)
|
||||
assert (np.sum(maxima) == 0)
|
||||
|
||||
def test_h_minima_float_image(self):
|
||||
"""specific tests for h-minima float image type"""
|
||||
w = 10
|
||||
x, y = np.mgrid[0:w, 0:w]
|
||||
data = 180 + 0.2 * ((x - w / 2) ** 2 + (y - w / 2) ** 2)
|
||||
data[2:4, 2:4] = 160
|
||||
data[2:4, 7:9] = 140
|
||||
data[7:9, 2:4] = 120
|
||||
data[7:9, 7:9] = 100
|
||||
data = data.astype(np.float32)
|
||||
|
||||
expected_result = np.zeros_like(data)
|
||||
expected_result[(data < 180.1)] = 1.0
|
||||
|
||||
for h in [1.0e-12, 1.0e-6, 1.0e-3, 1.0e-2, 1.0e-1, 0.1]:
|
||||
out = extrema.h_minima(data, h)
|
||||
error = diff(expected_result, out)
|
||||
assert error < eps
|
||||
|
||||
def test_h_minima_float_h(self):
|
||||
"""specific tests for h-minima float h parameter"""
|
||||
data = np.array([[4, 4, 4, 4, 4],
|
||||
[4, 1, 1, 1, 4],
|
||||
[4, 1, 0, 1, 4],
|
||||
[4, 1, 1, 1, 4],
|
||||
[4, 4, 4, 4, 4]], dtype=np.uint8)
|
||||
|
||||
h_vals = np.linspace(1.0, 2.0, 100)
|
||||
failures = 0
|
||||
for h in h_vals:
|
||||
if h % 1 != 0:
|
||||
msgs = ['possible precision loss converting image']
|
||||
else:
|
||||
msgs = []
|
||||
|
||||
with expected_warnings(msgs):
|
||||
minima = extrema.h_minima(data, h)
|
||||
|
||||
if (minima[2, 2] == 0):
|
||||
failures += 1
|
||||
|
||||
assert (failures == 0)
|
||||
|
||||
def test_h_minima_large_h(self):
|
||||
"""test that h-minima works correctly for large h"""
|
||||
data = np.array([[14, 14, 14, 14, 14],
|
||||
[14, 11, 11, 11, 14],
|
||||
[14, 11, 10, 11, 14],
|
||||
[14, 11, 11, 11, 14],
|
||||
[14, 14, 14, 14, 14]], dtype=np.uint8)
|
||||
|
||||
maxima = extrema.h_minima(data, 5)
|
||||
assert (np.sum(maxima) == 0)
|
||||
|
||||
data = np.array([[14, 14, 14, 14, 14],
|
||||
[14, 11, 11, 11, 14],
|
||||
[14, 11, 10, 11, 14],
|
||||
[14, 11, 11, 11, 14],
|
||||
[14, 14, 14, 14, 14]], dtype=np.float32)
|
||||
|
||||
maxima = extrema.h_minima(data, 5.0)
|
||||
assert (np.sum(maxima) == 0)
|
||||
|
||||
|
||||
class TestLocalMaxima(unittest.TestCase):
|
||||
"""Some tests for local_minima are included as well."""
|
||||
|
||||
supported_dtypes = [
|
||||
np.uint8, np.uint16, np.uint32, np.uint64,
|
||||
np.int8, np.int16, np.int32, np.int64,
|
||||
np.float32, np.float64
|
||||
]
|
||||
image = np.array(
|
||||
[[1, 1, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0],
|
||||
[1, 1, 0, 1, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0],
|
||||
[0, 0, 0, 2, 0, 0, 3, 3, 0, 0, 4, 0, 2, 0, 0],
|
||||
[0, 1, 0, 0, 0, 0, 0, 0, 4, 4, 0, 3, 0, 0, 0],
|
||||
[0, 2, 0, 1, 0, 2, 1, 0, 0, 0, 0, 3, 0, 0, 0],
|
||||
[0, 0, 2, 0, 2, 0, 0, 0, 2, 1, 0, 0, 0, 0, 0]],
|
||||
dtype=np.uint8
|
||||
)
|
||||
# Connectivity 2, maxima can touch border, returned with default values
|
||||
expected_default = np.array(
|
||||
[[1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
||||
[1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
||||
[0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0],
|
||||
[0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0],
|
||||
[0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
||||
[0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0]],
|
||||
dtype=bool
|
||||
)
|
||||
# Connectivity 1 (cross), maxima can touch border
|
||||
expected_cross = np.array(
|
||||
[[1, 1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0],
|
||||
[1, 1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0],
|
||||
[0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 0, 0],
|
||||
[0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0],
|
||||
[0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0],
|
||||
[0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0]],
|
||||
dtype=bool
|
||||
)
|
||||
|
||||
def test_empty(self):
|
||||
"""Test result with empty image."""
|
||||
result = extrema.local_maxima(np.array([[]]), indices=False)
|
||||
assert result.size == 0
|
||||
assert result.dtype == bool
|
||||
assert result.shape == (1, 0)
|
||||
|
||||
result = extrema.local_maxima(np.array([]), indices=True)
|
||||
assert isinstance(result, tuple)
|
||||
assert len(result) == 1
|
||||
assert result[0].size == 0
|
||||
assert result[0].dtype == np.intp
|
||||
|
||||
result = extrema.local_maxima(np.array([[]]), indices=True)
|
||||
assert isinstance(result, tuple)
|
||||
assert len(result) == 2
|
||||
assert result[0].size == 0
|
||||
assert result[0].dtype == np.intp
|
||||
assert result[1].size == 0
|
||||
assert result[1].dtype == np.intp
|
||||
|
||||
def test_dtypes(self):
|
||||
"""Test results with default configuration for all supported dtypes."""
|
||||
for dtype in self.supported_dtypes:
|
||||
result = extrema.local_maxima(self.image.astype(dtype))
|
||||
assert result.dtype == bool
|
||||
assert_equal(result, self.expected_default)
|
||||
|
||||
def test_dtypes_old(self):
|
||||
"""
|
||||
Test results with default configuration and data copied from old unit
|
||||
tests for all supported dtypes.
|
||||
"""
|
||||
data = np.array(
|
||||
[[10, 11, 13, 14, 14, 15, 14, 14, 13, 11],
|
||||
[11, 13, 15, 16, 16, 16, 16, 16, 15, 13],
|
||||
[13, 15, 40, 40, 18, 18, 18, 60, 60, 15],
|
||||
[14, 16, 40, 40, 19, 19, 19, 60, 60, 16],
|
||||
[14, 16, 18, 19, 19, 19, 19, 19, 18, 16],
|
||||
[15, 16, 18, 19, 19, 20, 19, 19, 18, 16],
|
||||
[14, 16, 18, 19, 19, 19, 19, 19, 18, 16],
|
||||
[14, 16, 80, 80, 19, 19, 19, 100, 100, 16],
|
||||
[13, 15, 80, 80, 18, 18, 18, 100, 100, 15],
|
||||
[11, 13, 15, 16, 16, 16, 16, 16, 15, 13]],
|
||||
dtype=np.uint8
|
||||
)
|
||||
expected = np.array(
|
||||
[[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
||||
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
||||
[0, 0, 1, 1, 0, 0, 0, 1, 1, 0],
|
||||
[0, 0, 1, 1, 0, 0, 0, 1, 1, 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, 1, 1, 0, 0, 0, 1, 1, 0],
|
||||
[0, 0, 1, 1, 0, 0, 0, 1, 1, 0],
|
||||
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0]],
|
||||
dtype=bool
|
||||
)
|
||||
for dtype in self.supported_dtypes:
|
||||
image = data.astype(dtype)
|
||||
result = extrema.local_maxima(image)
|
||||
assert result.dtype == bool
|
||||
assert_equal(result, expected)
|
||||
|
||||
def test_connectivity(self):
|
||||
"""Test results if footprint is a scalar."""
|
||||
# Connectivity 1: generates cross shaped footprint
|
||||
result_conn1 = extrema.local_maxima(self.image, connectivity=1)
|
||||
assert result_conn1.dtype == bool
|
||||
assert_equal(result_conn1, self.expected_cross)
|
||||
|
||||
# Connectivity 2: generates square shaped footprint
|
||||
result_conn2 = extrema.local_maxima(self.image, connectivity=2)
|
||||
assert result_conn2.dtype == bool
|
||||
assert_equal(result_conn2, self.expected_default)
|
||||
|
||||
# Connectivity 3: generates square shaped footprint
|
||||
result_conn3 = extrema.local_maxima(self.image, connectivity=3)
|
||||
assert result_conn3.dtype == bool
|
||||
assert_equal(result_conn3, self.expected_default)
|
||||
|
||||
def test_footprint(self):
|
||||
"""Test results if footprint is given."""
|
||||
footprint_cross = np.array(
|
||||
[[0, 1, 0], [1, 1, 1], [0, 1, 0]], dtype=bool)
|
||||
result_footprint_cross = extrema.local_maxima(
|
||||
self.image, footprint=footprint_cross)
|
||||
assert result_footprint_cross.dtype == bool
|
||||
assert_equal(result_footprint_cross, self.expected_cross)
|
||||
|
||||
for footprint in [
|
||||
((True,) * 3,) * 3,
|
||||
np.ones((3, 3), dtype=np.float64),
|
||||
np.ones((3, 3), dtype=np.uint8),
|
||||
np.ones((3, 3), dtype=bool),
|
||||
]:
|
||||
# Test different dtypes for footprint which expects a boolean array
|
||||
# but will accept and convert other types if possible
|
||||
result_footprint_square = extrema.local_maxima(
|
||||
self.image, footprint=footprint
|
||||
)
|
||||
assert result_footprint_square.dtype == bool
|
||||
assert_equal(result_footprint_square, self.expected_default)
|
||||
|
||||
footprint_x = np.array([[1, 0, 1], [0, 1, 0], [1, 0, 1]], dtype=bool)
|
||||
expected_footprint_x = np.array(
|
||||
[[1, 1, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0],
|
||||
[1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
||||
[0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0],
|
||||
[0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0],
|
||||
[0, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0],
|
||||
[0, 0, 1, 0, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0]],
|
||||
dtype=bool
|
||||
)
|
||||
result_footprint_x = extrema.local_maxima(self.image,
|
||||
footprint=footprint_x)
|
||||
assert result_footprint_x.dtype == bool
|
||||
assert_equal(result_footprint_x, expected_footprint_x)
|
||||
|
||||
def test_indices(self):
|
||||
"""Test output if indices of peaks are desired."""
|
||||
# Connectivity 1
|
||||
expected_conn1 = np.nonzero(self.expected_cross)
|
||||
result_conn1 = extrema.local_maxima(self.image, connectivity=1,
|
||||
indices=True)
|
||||
assert_equal(result_conn1, expected_conn1)
|
||||
|
||||
# Connectivity 2
|
||||
expected_conn2 = np.nonzero(self.expected_default)
|
||||
result_conn2 = extrema.local_maxima(self.image, connectivity=2,
|
||||
indices=True)
|
||||
assert_equal(result_conn2, expected_conn2)
|
||||
|
||||
def test_allow_borders(self):
|
||||
"""Test maxima detection at the image border."""
|
||||
# Use connectivity 1 to allow many maxima, only filtering at border is
|
||||
# of interest
|
||||
result_with_boder = extrema.local_maxima(
|
||||
self.image, connectivity=1, allow_borders=True)
|
||||
assert result_with_boder.dtype == bool
|
||||
assert_equal(result_with_boder, self.expected_cross)
|
||||
|
||||
expected_without_border = np.array(
|
||||
[[0, 0, 0, 0, 0, 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, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 0, 0],
|
||||
[0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0],
|
||||
[0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0],
|
||||
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]],
|
||||
dtype=bool
|
||||
)
|
||||
result_without_border = extrema.local_maxima(
|
||||
self.image, connectivity=1, allow_borders=False)
|
||||
assert result_with_boder.dtype == bool
|
||||
assert_equal(result_without_border, expected_without_border)
|
||||
|
||||
def test_nd(self):
|
||||
"""Test one- and three-dimensional case."""
|
||||
# One-dimension
|
||||
x_1d = np.array([1, 1, 0, 1, 2, 3, 0, 2, 1, 2, 0])
|
||||
expected_1d = np.array([1, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0],
|
||||
dtype=bool)
|
||||
result_1d = extrema.local_maxima(x_1d)
|
||||
assert result_1d.dtype == bool
|
||||
assert_equal(result_1d, expected_1d)
|
||||
|
||||
# 3-dimensions (adapted from old unit test)
|
||||
x_3d = np.zeros((8, 8, 8), dtype=np.uint8)
|
||||
expected_3d = np.zeros((8, 8, 8), dtype=bool)
|
||||
# first maximum: only one pixel
|
||||
x_3d[1, 1:3, 1:3] = 100
|
||||
x_3d[2, 2, 2] = 200
|
||||
x_3d[3, 1:3, 1:3] = 100
|
||||
expected_3d[2, 2, 2] = 1
|
||||
# second maximum: three pixels in z-direction
|
||||
x_3d[5:8, 1, 1] = 200
|
||||
expected_3d[5:8, 1, 1] = 1
|
||||
# third: two maxima in 0 and 3.
|
||||
x_3d[0, 5:8, 5:8] = 200
|
||||
x_3d[1, 6, 6] = 100
|
||||
x_3d[2, 5:7, 5:7] = 200
|
||||
x_3d[0:3, 5:8, 5:8] += 50
|
||||
expected_3d[0, 5:8, 5:8] = 1
|
||||
expected_3d[2, 5:7, 5:7] = 1
|
||||
# four : one maximum in the corner of the square
|
||||
x_3d[6:8, 6:8, 6:8] = 200
|
||||
x_3d[7, 7, 7] = 255
|
||||
expected_3d[7, 7, 7] = 1
|
||||
result_3d = extrema.local_maxima(x_3d)
|
||||
assert result_3d.dtype == bool
|
||||
assert_equal(result_3d, expected_3d)
|
||||
|
||||
def test_constant(self):
|
||||
"""Test behaviour for 'flat' images."""
|
||||
const_image = np.full((7, 6), 42, dtype=np.uint8)
|
||||
expected = np.zeros((7, 6), dtype=np.uint8)
|
||||
for dtype in self.supported_dtypes:
|
||||
const_image = const_image.astype(dtype)
|
||||
# test for local maxima
|
||||
result = extrema.local_maxima(const_image)
|
||||
assert result.dtype == bool
|
||||
assert_equal(result, expected)
|
||||
# test for local minima
|
||||
result = extrema.local_minima(const_image)
|
||||
assert result.dtype == bool
|
||||
assert_equal(result, expected)
|
||||
|
||||
def test_extrema_float(self):
|
||||
"""Specific tests for float type."""
|
||||
# Copied from old unit test for local_maxma
|
||||
image = np.array(
|
||||
[[0.10, 0.11, 0.13, 0.14, 0.14, 0.15, 0.14, 0.14, 0.13, 0.11],
|
||||
[0.11, 0.13, 0.15, 0.16, 0.16, 0.16, 0.16, 0.16, 0.15, 0.13],
|
||||
[0.13, 0.15, 0.40, 0.40, 0.18, 0.18, 0.18, 0.60, 0.60, 0.15],
|
||||
[0.14, 0.16, 0.40, 0.40, 0.19, 0.19, 0.19, 0.60, 0.60, 0.16],
|
||||
[0.14, 0.16, 0.18, 0.19, 0.19, 0.19, 0.19, 0.19, 0.18, 0.16],
|
||||
[0.15, 0.182, 0.18, 0.19, 0.204, 0.20, 0.19, 0.19, 0.18, 0.16],
|
||||
[0.14, 0.16, 0.18, 0.19, 0.19, 0.19, 0.19, 0.19, 0.18, 0.16],
|
||||
[0.14, 0.16, 0.80, 0.80, 0.19, 0.19, 0.19, 1.0, 1.0, 0.16],
|
||||
[0.13, 0.15, 0.80, 0.80, 0.18, 0.18, 0.18, 1.0, 1.0, 0.15],
|
||||
[0.11, 0.13, 0.15, 0.16, 0.16, 0.16, 0.16, 0.16, 0.15, 0.13]],
|
||||
dtype=np.float32
|
||||
)
|
||||
inverted_image = 1.0 - image
|
||||
expected_result = np.array(
|
||||
[[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
||||
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
||||
[0, 0, 1, 1, 0, 0, 0, 1, 1, 0],
|
||||
[0, 0, 1, 1, 0, 0, 0, 1, 1, 0],
|
||||
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
||||
[0, 1, 0, 0, 1, 0, 0, 0, 0, 0],
|
||||
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
||||
[0, 0, 1, 1, 0, 0, 0, 1, 1, 0],
|
||||
[0, 0, 1, 1, 0, 0, 0, 1, 1, 0],
|
||||
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0]],
|
||||
dtype=bool
|
||||
)
|
||||
|
||||
# Test for local maxima with automatic step calculation
|
||||
result = extrema.local_maxima(image)
|
||||
assert result.dtype == bool
|
||||
assert_equal(result, expected_result)
|
||||
|
||||
# Test for local minima with automatic step calculation
|
||||
result = extrema.local_minima(inverted_image)
|
||||
assert result.dtype == bool
|
||||
assert_equal(result, expected_result)
|
||||
|
||||
def test_exceptions(self):
|
||||
"""Test if input validation triggers correct exceptions."""
|
||||
# Mismatching number of dimensions
|
||||
with raises(ValueError, match="number of dimensions"):
|
||||
extrema.local_maxima(
|
||||
self.image, footprint=np.ones((3, 3, 3), dtype=bool))
|
||||
with raises(ValueError, match="number of dimensions"):
|
||||
extrema.local_maxima(
|
||||
self.image, footprint=np.ones((3,), dtype=bool))
|
||||
|
||||
# All dimensions in footprint must be of size 3
|
||||
with raises(ValueError, match="dimension size"):
|
||||
extrema.local_maxima(
|
||||
self.image, footprint=np.ones((2, 3), dtype=bool))
|
||||
with raises(ValueError, match="dimension size"):
|
||||
extrema.local_maxima(
|
||||
self.image, footprint=np.ones((5, 5), dtype=bool))
|
||||
|
||||
with raises(TypeError, match="float16 which is not supported"):
|
||||
extrema.local_maxima(np.empty(1, dtype=np.float16))
|
||||
|
||||
def test_small_array(self):
|
||||
"""Test output for arrays with dimension smaller 3.
|
||||
|
||||
If any dimension of an array is smaller than 3 and `allow_borders` is
|
||||
false a footprint, which has at least 3 elements in each
|
||||
dimension, can't be applied. This is an implementation detail so
|
||||
`local_maxima` should still return valid output (see gh-3261).
|
||||
|
||||
If `allow_borders` is true the array is padded internally and there is
|
||||
no problem.
|
||||
"""
|
||||
warning_msg = "maxima can't exist .* any dimension smaller 3 .*"
|
||||
x = np.array([0, 1])
|
||||
extrema.local_maxima(x, allow_borders=True) # no warning
|
||||
with warns(UserWarning, match=warning_msg):
|
||||
result = extrema.local_maxima(x, allow_borders=False)
|
||||
assert_equal(result, [0, 0])
|
||||
assert result.dtype == bool
|
||||
|
||||
x = np.array([[1, 2], [2, 2]])
|
||||
extrema.local_maxima(x, allow_borders=True, indices=True) # no warning
|
||||
with warns(UserWarning, match=warning_msg):
|
||||
result = extrema.local_maxima(x, allow_borders=False, indices=True)
|
||||
assert_equal(result, np.zeros((2, 0), dtype=np.intp))
|
||||
assert result[0].dtype == np.intp
|
||||
assert result[1].dtype == np.intp
|
||||
@@ -1,286 +0,0 @@
|
||||
import numpy as np
|
||||
import pytest
|
||||
|
||||
from skimage.morphology import flood, flood_fill
|
||||
|
||||
eps = 1e-12
|
||||
|
||||
|
||||
def test_empty_input():
|
||||
# Test shortcut
|
||||
output = flood_fill(np.empty(0), (), 2)
|
||||
assert output.size == 0
|
||||
|
||||
# Boolean output type
|
||||
assert flood(np.empty(0), ()).dtype == bool
|
||||
|
||||
# Maintain shape, even with zero size present
|
||||
assert flood(np.empty((20, 0, 4)), ()).shape == (20, 0, 4)
|
||||
|
||||
|
||||
def test_float16():
|
||||
image = np.array([9., 0.1, 42], dtype=np.float16)
|
||||
with pytest.raises(TypeError, match="dtype of `image` is float16"):
|
||||
flood_fill(image, 0, 1)
|
||||
|
||||
|
||||
def test_overrange_tolerance_int():
|
||||
image = np.arange(256, dtype=np.uint8).reshape((8, 8, 4))
|
||||
expected = np.zeros_like(image)
|
||||
|
||||
output = flood_fill(image, (7, 7, 3), 0, tolerance=379)
|
||||
|
||||
np.testing.assert_equal(output, expected)
|
||||
|
||||
|
||||
def test_overrange_tolerance_float():
|
||||
max_value = np.finfo(np.float32).max
|
||||
|
||||
image = np.random.uniform(size=(64, 64), low=-1., high=1.).astype(
|
||||
np.float32)
|
||||
image *= max_value
|
||||
|
||||
expected = np.ones_like(image)
|
||||
output = flood_fill(image, (0, 1), 1., tolerance=max_value * 10)
|
||||
|
||||
np.testing.assert_equal(output, expected)
|
||||
|
||||
|
||||
def test_inplace_int():
|
||||
image = np.array([[0, 0, 0, 0, 0, 0, 0],
|
||||
[0, 1, 1, 0, 2, 2, 0],
|
||||
[0, 1, 1, 0, 2, 2, 0],
|
||||
[1, 0, 0, 0, 0, 0, 3],
|
||||
[0, 1, 1, 1, 3, 3, 4]])
|
||||
|
||||
flood_fill(image, (0, 0), 5, in_place=True)
|
||||
|
||||
expected = np.array([[5, 5, 5, 5, 5, 5, 5],
|
||||
[5, 1, 1, 5, 2, 2, 5],
|
||||
[5, 1, 1, 5, 2, 2, 5],
|
||||
[1, 5, 5, 5, 5, 5, 3],
|
||||
[5, 1, 1, 1, 3, 3, 4]])
|
||||
|
||||
np.testing.assert_array_equal(image, expected)
|
||||
|
||||
|
||||
def test_inplace_float():
|
||||
image = np.array([[0, 0, 0, 0, 0, 0, 0],
|
||||
[0, 1, 1, 0, 2, 2, 0],
|
||||
[0, 1, 1, 0, 2, 2, 0],
|
||||
[1, 0, 0, 0, 0, 0, 3],
|
||||
[0, 1, 1, 1, 3, 3, 4]], dtype=np.float32)
|
||||
|
||||
flood_fill(image, (0, 0), 5, in_place=True)
|
||||
|
||||
expected = np.array([[5., 5., 5., 5., 5., 5., 5.],
|
||||
[5., 1., 1., 5., 2., 2., 5.],
|
||||
[5., 1., 1., 5., 2., 2., 5.],
|
||||
[1., 5., 5., 5., 5., 5., 3.],
|
||||
[5., 1., 1., 1., 3., 3., 4.]], dtype=np.float32)
|
||||
|
||||
np.testing.assert_allclose(image, expected)
|
||||
|
||||
|
||||
def test_inplace_noncontiguous():
|
||||
image = np.array([[0, 0, 0, 0, 0, 0, 0],
|
||||
[0, 1, 1, 0, 2, 2, 0],
|
||||
[0, 1, 1, 0, 2, 2, 0],
|
||||
[1, 0, 0, 0, 0, 0, 3],
|
||||
[0, 1, 1, 1, 3, 3, 4]])
|
||||
|
||||
# Transpose is noncontiguous
|
||||
image2 = image[::2, ::2]
|
||||
|
||||
flood_fill(image2, (0, 0), 5, in_place=True)
|
||||
|
||||
# The inplace modified result
|
||||
expected2 = np.array([[5, 5, 5, 5],
|
||||
[5, 1, 2, 5],
|
||||
[5, 1, 3, 4]])
|
||||
|
||||
np.testing.assert_allclose(image2, expected2)
|
||||
|
||||
# Projected back through the view, `image` also modified
|
||||
expected = np.array([[5, 0, 5, 0, 5, 0, 5],
|
||||
[0, 1, 1, 0, 2, 2, 0],
|
||||
[5, 1, 1, 0, 2, 2, 5],
|
||||
[1, 0, 0, 0, 0, 0, 3],
|
||||
[5, 1, 1, 1, 3, 3, 4]])
|
||||
|
||||
np.testing.assert_allclose(image, expected)
|
||||
|
||||
|
||||
def test_1d():
|
||||
image = np.arange(11)
|
||||
expected = np.array([0, 1, -20, -20, -20, -20, -20, -20, -20, 9, 10])
|
||||
|
||||
output = flood_fill(image, 5, -20, tolerance=3)
|
||||
output2 = flood_fill(image, (5,), -20, tolerance=3)
|
||||
|
||||
np.testing.assert_equal(output, expected)
|
||||
np.testing.assert_equal(output, output2)
|
||||
|
||||
|
||||
def test_wraparound():
|
||||
# If the borders (or neighbors) aren't correctly accounted for, this fails,
|
||||
# because the algorithm uses an ravelled array.
|
||||
test = np.zeros((5, 7), dtype=np.float64)
|
||||
test[:, 3] = 100
|
||||
|
||||
expected = np.array([[-1., -1., -1., 100., 0., 0., 0.],
|
||||
[-1., -1., -1., 100., 0., 0., 0.],
|
||||
[-1., -1., -1., 100., 0., 0., 0.],
|
||||
[-1., -1., -1., 100., 0., 0., 0.],
|
||||
[-1., -1., -1., 100., 0., 0., 0.]])
|
||||
|
||||
np.testing.assert_equal(flood_fill(test, (0, 0), -1), expected)
|
||||
|
||||
|
||||
def test_neighbors():
|
||||
# This test will only pass if the neighbors are exactly correct
|
||||
test = np.zeros((5, 7), dtype=np.float64)
|
||||
test[:, 3] = 100
|
||||
|
||||
expected = np.array([[0, 0, 0, 255, 0, 0, 0],
|
||||
[0, 0, 0, 255, 0, 0, 0],
|
||||
[0, 0, 0, 255, 0, 0, 0],
|
||||
[0, 0, 0, 255, 0, 0, 0],
|
||||
[0, 0, 0, 255, 0, 0, 0]])
|
||||
output = flood_fill(test, (0, 3), 255)
|
||||
|
||||
np.testing.assert_equal(output, expected)
|
||||
|
||||
test[2] = 100
|
||||
expected[2] = 255
|
||||
|
||||
output2 = flood_fill(test, (2, 3), 255)
|
||||
|
||||
np.testing.assert_equal(output2, expected)
|
||||
|
||||
|
||||
def test_footprint():
|
||||
# Basic tests for nonstandard footprints
|
||||
footprint = np.array([[0, 1, 1],
|
||||
[0, 1, 1],
|
||||
[0, 0, 0]]) # Cannot grow left or down
|
||||
|
||||
output = flood_fill(np.zeros((5, 6), dtype=np.uint8), (3, 1), 255,
|
||||
footprint=footprint)
|
||||
|
||||
expected = np.array([[0, 255, 255, 255, 255, 255],
|
||||
[0, 255, 255, 255, 255, 255],
|
||||
[0, 255, 255, 255, 255, 255],
|
||||
[0, 255, 255, 255, 255, 255],
|
||||
[0, 0, 0, 0, 0, 0]], dtype=np.uint8)
|
||||
|
||||
np.testing.assert_equal(output, expected)
|
||||
|
||||
footprint = np.array([[0, 0, 0],
|
||||
[1, 1, 0],
|
||||
[1, 1, 0]]) # Cannot grow right or up
|
||||
|
||||
output = flood_fill(np.zeros((5, 6), dtype=np.uint8), (1, 4), 255,
|
||||
footprint=footprint)
|
||||
|
||||
expected = np.array([[ 0, 0, 0, 0, 0, 0],
|
||||
[255, 255, 255, 255, 255, 0],
|
||||
[255, 255, 255, 255, 255, 0],
|
||||
[255, 255, 255, 255, 255, 0],
|
||||
[255, 255, 255, 255, 255, 0]], dtype=np.uint8)
|
||||
|
||||
np.testing.assert_equal(output, expected)
|
||||
|
||||
|
||||
def test_basic_nd():
|
||||
for dimension in (3, 4, 5):
|
||||
shape = (5,) * dimension
|
||||
hypercube = np.zeros(shape)
|
||||
slice_mid = tuple(slice(1, -1, None) for dim in range(dimension))
|
||||
hypercube[slice_mid] = 1 # sum is 3**dimension
|
||||
filled = flood_fill(hypercube, (2,) * dimension, 2)
|
||||
|
||||
# Test that the middle sum is correct
|
||||
assert filled.sum() == 3**dimension * 2
|
||||
|
||||
# Test that the entire array is as expected
|
||||
np.testing.assert_equal(
|
||||
filled, np.pad(np.ones((3,) * dimension) * 2, 1, 'constant'))
|
||||
|
||||
|
||||
@pytest.mark.parametrize("tolerance", [None, 0])
|
||||
def test_f_order(tolerance):
|
||||
image = np.array([
|
||||
[0, 0, 0, 0],
|
||||
[1, 0, 0, 0],
|
||||
[0, 1, 0, 0],
|
||||
], order="F")
|
||||
expected = np.array([
|
||||
[0, 0, 0, 0],
|
||||
[1, 0, 0, 0],
|
||||
[0, 1, 0, 0],
|
||||
], dtype=bool)
|
||||
|
||||
mask = flood(image, seed_point=(1, 0), tolerance=tolerance)
|
||||
np.testing.assert_array_equal(expected, mask)
|
||||
|
||||
mask = flood(image, seed_point=(2, 1), tolerance=tolerance)
|
||||
np.testing.assert_array_equal(expected, mask)
|
||||
|
||||
|
||||
def test_negative_indexing_seed_point():
|
||||
image = np.array([[0, 0, 0, 0, 0, 0, 0],
|
||||
[0, 1, 1, 0, 2, 2, 0],
|
||||
[0, 1, 1, 0, 2, 2, 0],
|
||||
[1, 0, 0, 0, 0, 0, 3],
|
||||
[0, 1, 1, 1, 3, 3, 4]], dtype=np.float32)
|
||||
|
||||
expected = np.array([[5., 5., 5., 5., 5., 5., 5.],
|
||||
[5., 1., 1., 5., 2., 2., 5.],
|
||||
[5., 1., 1., 5., 2., 2., 5.],
|
||||
[1., 5., 5., 5., 5., 5., 3.],
|
||||
[5., 1., 1., 1., 3., 3., 4.]], dtype=np.float32)
|
||||
|
||||
image = flood_fill(image, (0, -1), 5)
|
||||
|
||||
np.testing.assert_allclose(image, expected)
|
||||
|
||||
|
||||
def test_non_adjacent_footprint():
|
||||
# Basic tests for non-adjacent footprints
|
||||
footprint = np.array([[1, 0, 0, 0, 1],
|
||||
[0, 0, 0, 0, 0],
|
||||
[0, 0, 1, 0, 0],
|
||||
[0, 0, 0, 0, 0],
|
||||
[1, 0, 0, 0, 1]])
|
||||
|
||||
output = flood_fill(np.zeros((5, 6), dtype=np.uint8), (2, 3), 255,
|
||||
footprint=footprint)
|
||||
|
||||
expected = np.array([[0, 255, 0, 0, 0, 255],
|
||||
[0, 0, 0, 0, 0, 0],
|
||||
[0, 0, 0, 255, 0, 0],
|
||||
[0, 0, 0, 0, 0, 0],
|
||||
[0, 255, 0, 0, 0, 255]], dtype=np.uint8)
|
||||
|
||||
np.testing.assert_equal(output, expected)
|
||||
|
||||
footprint = np.array([[1, 1, 1, 1, 1],
|
||||
[1, 1, 1, 1, 1],
|
||||
[1, 1, 1, 1, 1],
|
||||
[1, 1, 1, 1, 1],
|
||||
[1, 1, 1, 1, 1]])
|
||||
|
||||
image = np.zeros((5, 10), dtype=np.uint8)
|
||||
image[:, (3, 7, 8)] = 100
|
||||
|
||||
output = flood_fill(image, (0, 0), 255, footprint=footprint)
|
||||
|
||||
expected = np.array([[255, 255, 255, 100, 255, 255, 255, 100, 100, 0],
|
||||
[255, 255, 255, 100, 255, 255, 255, 100, 100, 0],
|
||||
[255, 255, 255, 100, 255, 255, 255, 100, 100, 0],
|
||||
[255, 255, 255, 100, 255, 255, 255, 100, 100, 0],
|
||||
[255, 255, 255, 100, 255, 255, 255, 100, 100, 0]],
|
||||
dtype=np.uint8)
|
||||
|
||||
np.testing.assert_equal(output, expected)
|
||||
@@ -1,238 +0,0 @@
|
||||
"""
|
||||
Tests for Morphological footprints
|
||||
(skimage.morphology.footprint)
|
||||
|
||||
Author: Damian Eads
|
||||
"""
|
||||
import numpy as np
|
||||
import pytest
|
||||
from numpy.testing import assert_equal
|
||||
|
||||
from skimage._shared.testing import fetch
|
||||
from skimage.morphology import footprints
|
||||
|
||||
|
||||
class TestFootprints:
|
||||
|
||||
def test_square_footprint(self):
|
||||
"""Test square footprints"""
|
||||
for k in range(0, 5):
|
||||
actual_mask = footprints.square(k)
|
||||
expected_mask = np.ones((k, k), dtype='uint8')
|
||||
assert_equal(expected_mask, actual_mask)
|
||||
|
||||
def test_rectangle_footprint(self):
|
||||
"""Test rectangle footprints"""
|
||||
for i in range(0, 5):
|
||||
for j in range(0, 5):
|
||||
actual_mask = footprints.rectangle(i, j)
|
||||
expected_mask = np.ones((i, j), dtype='uint8')
|
||||
assert_equal(expected_mask, actual_mask)
|
||||
|
||||
def test_cube_footprint(self):
|
||||
"""Test cube footprints"""
|
||||
for k in range(0, 5):
|
||||
actual_mask = footprints.cube(k)
|
||||
expected_mask = np.ones((k, k, k), dtype='uint8')
|
||||
assert_equal(expected_mask, actual_mask)
|
||||
|
||||
def strel_worker(self, fn, func):
|
||||
matlab_masks = np.load(fetch(fn))
|
||||
k = 0
|
||||
for arrname in sorted(matlab_masks):
|
||||
expected_mask = matlab_masks[arrname]
|
||||
actual_mask = func(k)
|
||||
if expected_mask.shape == (1,):
|
||||
expected_mask = expected_mask[:, np.newaxis]
|
||||
assert_equal(expected_mask, actual_mask)
|
||||
k = k + 1
|
||||
|
||||
def strel_worker_3d(self, fn, func):
|
||||
matlab_masks = np.load(fetch(fn))
|
||||
k = 0
|
||||
for arrname in sorted(matlab_masks):
|
||||
expected_mask = matlab_masks[arrname]
|
||||
actual_mask = func(k)
|
||||
if expected_mask.shape == (1,):
|
||||
expected_mask = expected_mask[:, np.newaxis]
|
||||
# Test center slice for each dimension. This gives a good
|
||||
# indication of validity without the need for a 3D reference
|
||||
# mask.
|
||||
c = int(expected_mask.shape[0]/2)
|
||||
assert_equal(expected_mask, actual_mask[c, :, :])
|
||||
assert_equal(expected_mask, actual_mask[:, c, :])
|
||||
assert_equal(expected_mask, actual_mask[:, :, c])
|
||||
k = k + 1
|
||||
|
||||
def test_footprint_disk(self):
|
||||
"""Test disk footprints"""
|
||||
self.strel_worker("data/disk-matlab-output.npz", footprints.disk)
|
||||
|
||||
def test_footprint_diamond(self):
|
||||
"""Test diamond footprints"""
|
||||
self.strel_worker("data/diamond-matlab-output.npz", footprints.diamond)
|
||||
|
||||
def test_footprint_ball(self):
|
||||
"""Test ball footprints"""
|
||||
self.strel_worker_3d("data/disk-matlab-output.npz", footprints.ball)
|
||||
|
||||
def test_footprint_octahedron(self):
|
||||
"""Test octahedron footprints"""
|
||||
self.strel_worker_3d("data/diamond-matlab-output.npz",
|
||||
footprints.octahedron)
|
||||
|
||||
def test_footprint_octagon(self):
|
||||
"""Test octagon footprints"""
|
||||
expected_mask1 = np.array([[0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0],
|
||||
[0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0],
|
||||
[0, 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, 1, 1, 1, 1],
|
||||
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
|
||||
[1, 1, 1, 1, 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, 0],
|
||||
[0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0],
|
||||
[0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0]],
|
||||
dtype=np.uint8)
|
||||
actual_mask1 = footprints.octagon(5, 3)
|
||||
expected_mask2 = np.array([[0, 1, 0],
|
||||
[1, 1, 1],
|
||||
[0, 1, 0]], dtype=np.uint8)
|
||||
actual_mask2 = footprints.octagon(1, 1)
|
||||
assert_equal(expected_mask1, actual_mask1)
|
||||
assert_equal(expected_mask2, actual_mask2)
|
||||
|
||||
def test_footprint_ellipse(self):
|
||||
"""Test ellipse footprints"""
|
||||
expected_mask1 = np.array([[0, 0, 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, 1, 1, 1, 1, 1, 1],
|
||||
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
|
||||
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
|
||||
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
|
||||
[0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0]],
|
||||
dtype=np.uint8)
|
||||
actual_mask1 = footprints.ellipse(5, 3)
|
||||
expected_mask2 = np.array([[1, 1, 1],
|
||||
[1, 1, 1],
|
||||
[1, 1, 1]], dtype=np.uint8)
|
||||
actual_mask2 = footprints.ellipse(1, 1)
|
||||
assert_equal(expected_mask1, actual_mask1)
|
||||
assert_equal(expected_mask2, actual_mask2)
|
||||
assert_equal(expected_mask1, footprints.ellipse(3, 5).T)
|
||||
assert_equal(expected_mask2, footprints.ellipse(1, 1).T)
|
||||
|
||||
def test_footprint_star(self):
|
||||
"""Test star footprints"""
|
||||
expected_mask1 = np.array([[0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0],
|
||||
[0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0],
|
||||
[0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0],
|
||||
[0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0],
|
||||
[0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0],
|
||||
[0, 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],
|
||||
[0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0],
|
||||
[0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0],
|
||||
[0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0],
|
||||
[0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0],
|
||||
[0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0],
|
||||
[0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0]],
|
||||
dtype=np.uint8)
|
||||
actual_mask1 = footprints.star(4)
|
||||
expected_mask2 = np.array([[1, 1, 1],
|
||||
[1, 1, 1],
|
||||
[1, 1, 1]], dtype=np.uint8)
|
||||
actual_mask2 = footprints.star(1)
|
||||
assert_equal(expected_mask1, actual_mask1)
|
||||
assert_equal(expected_mask2, actual_mask2)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
'function, args, supports_sequence_decomposition',
|
||||
[
|
||||
(footprints.disk, (3,), True),
|
||||
(footprints.ball, (3,), True),
|
||||
(footprints.square, (3,), True),
|
||||
(footprints.cube, (3,), True),
|
||||
(footprints.diamond, (3,), True),
|
||||
(footprints.octahedron, (3,), True),
|
||||
(footprints.rectangle, (3, 4), True),
|
||||
(footprints.ellipse, (3, 4), False),
|
||||
(footprints.octagon, (3, 4), True),
|
||||
(footprints.star, (3,), False),
|
||||
]
|
||||
)
|
||||
@pytest.mark.parametrize("dtype", [np.uint8, np.float64])
|
||||
def test_footprint_dtype(function, args, supports_sequence_decomposition,
|
||||
dtype):
|
||||
# make sure footprint dtype matches what was requested
|
||||
footprint = function(*args, dtype=dtype)
|
||||
assert footprint.dtype == dtype
|
||||
|
||||
if supports_sequence_decomposition:
|
||||
sequence = function(*args, dtype=dtype, decomposition='sequence')
|
||||
assert all([fp_tuple[0].dtype == dtype for fp_tuple in sequence])
|
||||
|
||||
|
||||
@pytest.mark.parametrize("function", ["disk", "ball"])
|
||||
@pytest.mark.parametrize("radius", [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 20, 50, 75,
|
||||
100])
|
||||
def test_nsphere_series_approximation(function, radius):
|
||||
fp_func = getattr(footprints, function)
|
||||
expected = fp_func(radius, strict_radius=False, decomposition=None)
|
||||
footprint_sequence = fp_func(radius, strict_radius=False,
|
||||
decomposition="sequence")
|
||||
approximate = footprints.footprint_from_sequence(footprint_sequence)
|
||||
assert approximate.shape == expected.shape
|
||||
|
||||
# verify that maximum error does not exceed some fraction of the size
|
||||
error = np.sum(np.abs(expected.astype(int) - approximate.astype(int)))
|
||||
if radius == 1:
|
||||
assert error == 0
|
||||
else:
|
||||
max_error = 0.1 if function == "disk" else 0.15
|
||||
assert error / expected.size <= max_error
|
||||
|
||||
|
||||
@pytest.mark.parametrize("radius", [1, 2, 3, 4, 5, 10, 20, 50, 75])
|
||||
@pytest.mark.parametrize("strict_radius", [False, True])
|
||||
def test_disk_crosses_approximation(radius, strict_radius):
|
||||
fp_func = footprints.disk
|
||||
expected = fp_func(radius, strict_radius=strict_radius, decomposition=None)
|
||||
footprint_sequence = fp_func(radius, strict_radius=strict_radius,
|
||||
decomposition="crosses")
|
||||
approximate = footprints.footprint_from_sequence(footprint_sequence)
|
||||
assert approximate.shape == expected.shape
|
||||
|
||||
# verify that maximum error does not exceed some fraction of the size
|
||||
error = np.sum(np.abs(expected.astype(int) - approximate.astype(int)))
|
||||
max_error = 0.05
|
||||
assert error / expected.size <= max_error
|
||||
|
||||
|
||||
@pytest.mark.parametrize("width", [3, 8, 20, 50])
|
||||
@pytest.mark.parametrize("height", [3, 8, 20, 50])
|
||||
def test_ellipse_crosses_approximation(width, height):
|
||||
fp_func = footprints.ellipse
|
||||
expected = fp_func(width, height, decomposition=None)
|
||||
footprint_sequence = fp_func(width, height, decomposition="crosses")
|
||||
approximate = footprints.footprint_from_sequence(footprint_sequence)
|
||||
assert approximate.shape == expected.shape
|
||||
|
||||
# verify that maximum error does not exceed some fraction of the size
|
||||
error = np.sum(np.abs(expected.astype(int) - approximate.astype(int)))
|
||||
max_error = 0.05
|
||||
assert error / expected.size <= max_error
|
||||
|
||||
|
||||
def test_disk_series_approximation_unavailable():
|
||||
# ValueError if radius is too large (only precomputed up to radius=250)
|
||||
with pytest.raises(ValueError):
|
||||
footprints.disk(radius=10000, decomposition="sequence")
|
||||
|
||||
|
||||
def test_ball_series_approximation_unavailable():
|
||||
# ValueError if radius is too large (only precomputed up to radius=100)
|
||||
with pytest.raises(ValueError):
|
||||
footprints.ball(radius=10000, decomposition="sequence")
|
||||
@@ -1,410 +0,0 @@
|
||||
import numpy as np
|
||||
import pytest
|
||||
from scipy import ndimage as ndi
|
||||
from numpy.testing import assert_allclose, assert_array_equal, assert_equal
|
||||
|
||||
from skimage import color, data, transform
|
||||
from skimage._shared._warnings import expected_warnings
|
||||
from skimage._shared.testing import fetch
|
||||
from skimage.morphology import gray, footprints
|
||||
from skimage.util import img_as_uint, img_as_ubyte
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def cam_image():
|
||||
from skimage import data
|
||||
return np.ascontiguousarray(data.camera()[64:112, 64:96])
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def cell3d_image():
|
||||
from skimage import data
|
||||
return np.ascontiguousarray(data.cells3d()[30:48, 0, 20:36, 20:32])
|
||||
|
||||
|
||||
class TestMorphology():
|
||||
|
||||
# These expected outputs were generated with skimage v0.12.1
|
||||
# using:
|
||||
#
|
||||
# from skimage.morphology.tests.test_gray import TestMorphology
|
||||
# import numpy as np
|
||||
# output = TestMorphology()._build_expected_output()
|
||||
# np.savez_compressed('gray_morph_output.npz', **output)
|
||||
|
||||
def _build_expected_output(self):
|
||||
funcs = (gray.erosion, gray.dilation, gray.opening, gray.closing,
|
||||
gray.white_tophat, gray.black_tophat)
|
||||
footprints_2D = (footprints.square, footprints.diamond,
|
||||
footprints.disk, footprints.star)
|
||||
|
||||
image = img_as_ubyte(transform.downscale_local_mean(
|
||||
color.rgb2gray(data.coffee()), (20, 20)))
|
||||
|
||||
output = {}
|
||||
for n in range(1, 4):
|
||||
for strel in footprints_2D:
|
||||
for func in funcs:
|
||||
key = f'{strel.__name__}_{n}_{func.__name__}'
|
||||
output[key] = func(image, strel(n))
|
||||
|
||||
return output
|
||||
|
||||
def test_gray_morphology(self):
|
||||
expected = dict(np.load(fetch('data/gray_morph_output.npz')))
|
||||
calculated = self._build_expected_output()
|
||||
assert_equal(expected, calculated)
|
||||
|
||||
|
||||
class TestEccentricStructuringElements():
|
||||
def setup_class(self):
|
||||
self.black_pixel = 255 * np.ones((4, 4), dtype=np.uint8)
|
||||
self.black_pixel[1, 1] = 0
|
||||
self.white_pixel = 255 - self.black_pixel
|
||||
self.footprints = [footprints.square(2), footprints.rectangle(2, 2),
|
||||
footprints.rectangle(2, 1),
|
||||
footprints.rectangle(1, 2)]
|
||||
|
||||
def test_dilate_erode_symmetry(self):
|
||||
for s in self.footprints:
|
||||
c = gray.erosion(self.black_pixel, s)
|
||||
d = gray.dilation(self.white_pixel, s)
|
||||
assert np.all(c == (255 - d))
|
||||
|
||||
def test_open_black_pixel(self):
|
||||
for s in self.footprints:
|
||||
gray_open = gray.opening(self.black_pixel, s)
|
||||
assert np.all(gray_open == self.black_pixel)
|
||||
|
||||
def test_close_white_pixel(self):
|
||||
for s in self.footprints:
|
||||
gray_close = gray.closing(self.white_pixel, s)
|
||||
assert np.all(gray_close == self.white_pixel)
|
||||
|
||||
def test_open_white_pixel(self):
|
||||
for s in self.footprints:
|
||||
assert np.all(gray.opening(self.white_pixel, s) == 0)
|
||||
|
||||
def test_close_black_pixel(self):
|
||||
for s in self.footprints:
|
||||
assert np.all(gray.closing(self.black_pixel, s) == 255)
|
||||
|
||||
def test_white_tophat_white_pixel(self):
|
||||
for s in self.footprints:
|
||||
tophat = gray.white_tophat(self.white_pixel, s)
|
||||
assert np.all(tophat == self.white_pixel)
|
||||
|
||||
def test_black_tophat_black_pixel(self):
|
||||
for s in self.footprints:
|
||||
tophat = gray.black_tophat(self.black_pixel, s)
|
||||
assert np.all(tophat == (255 - self.black_pixel))
|
||||
|
||||
def test_white_tophat_black_pixel(self):
|
||||
for s in self.footprints:
|
||||
tophat = gray.white_tophat(self.black_pixel, s)
|
||||
assert np.all(tophat == 0)
|
||||
|
||||
def test_black_tophat_white_pixel(self):
|
||||
for s in self.footprints:
|
||||
tophat = gray.black_tophat(self.white_pixel, s)
|
||||
assert np.all(tophat == 0)
|
||||
|
||||
|
||||
gray_functions = [gray.erosion, gray.dilation,
|
||||
gray.opening, gray.closing,
|
||||
gray.white_tophat, gray.black_tophat]
|
||||
|
||||
|
||||
@pytest.mark.parametrize("function", gray_functions)
|
||||
def test_default_footprint(function):
|
||||
strel = footprints.diamond(radius=1)
|
||||
image = np.array([[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
||||
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
||||
[0, 0, 1, 1, 1, 1, 1, 1, 0, 0],
|
||||
[0, 0, 1, 1, 1, 1, 1, 1, 0, 0],
|
||||
[0, 0, 1, 1, 1, 1, 1, 1, 0, 0],
|
||||
[0, 0, 1, 1, 1, 0, 0, 1, 0, 0],
|
||||
[0, 0, 1, 1, 1, 0, 0, 1, 0, 0],
|
||||
[0, 0, 1, 1, 1, 0, 0, 1, 0, 0],
|
||||
[0, 0, 1, 1, 1, 1, 1, 1, 0, 0],
|
||||
[0, 0, 1, 1, 1, 1, 1, 1, 0, 0],
|
||||
[0, 0, 1, 1, 1, 1, 1, 1, 0, 0],
|
||||
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
||||
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0]], np.uint8)
|
||||
im_expected = function(image, strel)
|
||||
im_test = function(image)
|
||||
assert_array_equal(im_expected, im_test)
|
||||
|
||||
|
||||
def test_3d_fallback_default_footprint():
|
||||
# 3x3x3 cube inside a 7x7x7 image:
|
||||
image = np.zeros((7, 7, 7), bool)
|
||||
image[2:-2, 2:-2, 2:-2] = 1
|
||||
|
||||
opened = gray.opening(image)
|
||||
|
||||
# expect a "hyper-cross" centered in the 5x5x5:
|
||||
image_expected = np.zeros((7, 7, 7), dtype=bool)
|
||||
image_expected[2:5, 2:5, 2:5] = ndi.generate_binary_structure(3, 1)
|
||||
assert_array_equal(opened, image_expected)
|
||||
|
||||
|
||||
gray_3d_fallback_functions = [gray.closing, gray.opening]
|
||||
|
||||
|
||||
@pytest.mark.parametrize("function", gray_3d_fallback_functions)
|
||||
def test_3d_fallback_cube_footprint(function):
|
||||
# 3x3x3 cube inside a 7x7x7 image:
|
||||
image = np.zeros((7, 7, 7), bool)
|
||||
image[2:-2, 2:-2, 2:-2] = 1
|
||||
|
||||
cube = np.ones((3, 3, 3), dtype=np.uint8)
|
||||
|
||||
new_image = function(image, cube)
|
||||
assert_array_equal(new_image, image)
|
||||
|
||||
|
||||
def test_3d_fallback_white_tophat():
|
||||
image = np.zeros((7, 7, 7), dtype=bool)
|
||||
image[2, 2:4, 2:4] = 1
|
||||
image[3, 2:5, 2:5] = 1
|
||||
image[4, 3:5, 3:5] = 1
|
||||
|
||||
with expected_warnings([r'operator.*deprecated|\A\Z']):
|
||||
new_image = gray.white_tophat(image)
|
||||
footprint = ndi.generate_binary_structure(3, 1)
|
||||
with expected_warnings([r'operator.*deprecated|\A\Z']):
|
||||
image_expected = ndi.white_tophat(
|
||||
image.view(dtype=np.uint8), footprint=footprint)
|
||||
assert_array_equal(new_image, image_expected)
|
||||
|
||||
|
||||
def test_3d_fallback_black_tophat():
|
||||
image = np.ones((7, 7, 7), dtype=bool)
|
||||
image[2, 2:4, 2:4] = 0
|
||||
image[3, 2:5, 2:5] = 0
|
||||
image[4, 3:5, 3:5] = 0
|
||||
|
||||
with expected_warnings([r'operator.*deprecated|\A\Z']):
|
||||
new_image = gray.black_tophat(image)
|
||||
footprint = ndi.generate_binary_structure(3, 1)
|
||||
with expected_warnings([r'operator.*deprecated|\A\Z']):
|
||||
image_expected = ndi.black_tophat(
|
||||
image.view(dtype=np.uint8), footprint=footprint)
|
||||
assert_array_equal(new_image, image_expected)
|
||||
|
||||
|
||||
def test_2d_ndimage_equivalence():
|
||||
image = np.zeros((9, 9), np.uint8)
|
||||
image[2:-2, 2:-2] = 128
|
||||
image[3:-3, 3:-3] = 196
|
||||
image[4, 4] = 255
|
||||
|
||||
opened = gray.opening(image)
|
||||
closed = gray.closing(image)
|
||||
|
||||
footprint = ndi.generate_binary_structure(2, 1)
|
||||
ndimage_opened = ndi.grey_opening(image, footprint=footprint)
|
||||
ndimage_closed = ndi.grey_closing(image, footprint=footprint)
|
||||
|
||||
assert_array_equal(opened, ndimage_opened)
|
||||
assert_array_equal(closed, ndimage_closed)
|
||||
|
||||
|
||||
# float test images
|
||||
im = np.array([[ 0.55, 0.72, 0.6 , 0.54, 0.42],
|
||||
[ 0.65, 0.44, 0.89, 0.96, 0.38],
|
||||
[ 0.79, 0.53, 0.57, 0.93, 0.07],
|
||||
[ 0.09, 0.02, 0.83, 0.78, 0.87],
|
||||
[ 0.98, 0.8 , 0.46, 0.78, 0.12]])
|
||||
|
||||
eroded = np.array([[ 0.55, 0.44, 0.54, 0.42, 0.38],
|
||||
[ 0.44, 0.44, 0.44, 0.38, 0.07],
|
||||
[ 0.09, 0.02, 0.53, 0.07, 0.07],
|
||||
[ 0.02, 0.02, 0.02, 0.78, 0.07],
|
||||
[ 0.09, 0.02, 0.46, 0.12, 0.12]])
|
||||
|
||||
dilated = np.array([[ 0.72, 0.72, 0.89, 0.96, 0.54],
|
||||
[ 0.79, 0.89, 0.96, 0.96, 0.96],
|
||||
[ 0.79, 0.79, 0.93, 0.96, 0.93],
|
||||
[ 0.98, 0.83, 0.83, 0.93, 0.87],
|
||||
[ 0.98, 0.98, 0.83, 0.78, 0.87]])
|
||||
|
||||
opened = np.array([[ 0.55, 0.55, 0.54, 0.54, 0.42],
|
||||
[ 0.55, 0.44, 0.54, 0.44, 0.38],
|
||||
[ 0.44, 0.53, 0.53, 0.78, 0.07],
|
||||
[ 0.09, 0.02, 0.78, 0.78, 0.78],
|
||||
[ 0.09, 0.46, 0.46, 0.78, 0.12]])
|
||||
|
||||
closed = np.array([[ 0.72, 0.72, 0.72, 0.54, 0.54],
|
||||
[ 0.72, 0.72, 0.89, 0.96, 0.54],
|
||||
[ 0.79, 0.79, 0.79, 0.93, 0.87],
|
||||
[ 0.79, 0.79, 0.83, 0.78, 0.87],
|
||||
[ 0.98, 0.83, 0.78, 0.78, 0.78]])
|
||||
|
||||
|
||||
def test_float():
|
||||
assert_allclose(gray.erosion(im), eroded)
|
||||
assert_allclose(gray.dilation(im), dilated)
|
||||
assert_allclose(gray.opening(im), opened)
|
||||
assert_allclose(gray.closing(im), closed)
|
||||
|
||||
|
||||
def test_uint16():
|
||||
im16, eroded16, dilated16, opened16, closed16 = (
|
||||
map(img_as_uint, [im, eroded, dilated, opened, closed]))
|
||||
assert_allclose(gray.erosion(im16), eroded16)
|
||||
assert_allclose(gray.dilation(im16), dilated16)
|
||||
assert_allclose(gray.opening(im16), opened16)
|
||||
assert_allclose(gray.closing(im16), closed16)
|
||||
|
||||
|
||||
def test_discontiguous_out_array():
|
||||
image = np.array([[5, 6, 2],
|
||||
[7, 2, 2],
|
||||
[3, 5, 1]], np.uint8)
|
||||
out_array_big = np.zeros((5, 5), np.uint8)
|
||||
out_array = out_array_big[::2, ::2]
|
||||
expected_dilation = np.array([[7, 0, 6, 0, 6],
|
||||
[0, 0, 0, 0, 0],
|
||||
[7, 0, 7, 0, 2],
|
||||
[0, 0, 0, 0, 0],
|
||||
[7, 0, 5, 0, 5]], np.uint8)
|
||||
expected_erosion = np.array([[5, 0, 2, 0, 2],
|
||||
[0, 0, 0, 0, 0],
|
||||
[2, 0, 2, 0, 1],
|
||||
[0, 0, 0, 0, 0],
|
||||
[3, 0, 1, 0, 1]], np.uint8)
|
||||
gray.dilation(image, out=out_array)
|
||||
assert_array_equal(out_array_big, expected_dilation)
|
||||
gray.erosion(image, out=out_array)
|
||||
assert_array_equal(out_array_big, expected_erosion)
|
||||
|
||||
|
||||
def test_1d_erosion():
|
||||
image = np.array([1, 2, 3, 2, 1])
|
||||
expected = np.array([1, 1, 2, 1, 1])
|
||||
eroded = gray.erosion(image)
|
||||
assert_array_equal(eroded, expected)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"function", ["erosion", "dilation", "closing", "opening", "white_tophat",
|
||||
"black_tophat"],
|
||||
)
|
||||
@pytest.mark.parametrize("size", (7,))
|
||||
@pytest.mark.parametrize("decomposition", ['separable', 'sequence'])
|
||||
def test_square_decomposition(cam_image, function, size, decomposition):
|
||||
"""Validate footprint decomposition for various shapes.
|
||||
|
||||
comparison is made to the case without decomposition.
|
||||
"""
|
||||
footprint_ndarray = footprints.square(size, decomposition=None)
|
||||
footprint = footprints.square(size, decomposition=decomposition)
|
||||
func = getattr(gray, function)
|
||||
expected = func(cam_image, footprint=footprint_ndarray)
|
||||
out = func(cam_image, footprint=footprint)
|
||||
assert_array_equal(expected, out)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"function", ["erosion", "dilation", "closing", "opening", "white_tophat",
|
||||
"black_tophat"],
|
||||
)
|
||||
@pytest.mark.parametrize("nrows", (3, 11))
|
||||
@pytest.mark.parametrize("ncols", (3, 11))
|
||||
@pytest.mark.parametrize("decomposition", ['separable', 'sequence'])
|
||||
def test_rectangle_decomposition(cam_image, function, nrows, ncols,
|
||||
decomposition):
|
||||
"""Validate footprint decomposition for various shapes.
|
||||
|
||||
comparison is made to the case without decomposition.
|
||||
"""
|
||||
footprint_ndarray = footprints.rectangle(nrows, ncols, decomposition=None)
|
||||
footprint = footprints.rectangle(nrows, ncols, decomposition=decomposition)
|
||||
func = getattr(gray, function)
|
||||
expected = func(cam_image, footprint=footprint_ndarray)
|
||||
out = func(cam_image, footprint=footprint)
|
||||
assert_array_equal(expected, out)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"function", ["erosion", "dilation", "closing", "opening", "white_tophat",
|
||||
"black_tophat"],
|
||||
)
|
||||
@pytest.mark.parametrize("radius", (2, 3))
|
||||
@pytest.mark.parametrize("decomposition", ['separable', 'sequence'])
|
||||
def test_diamond_decomposition(cam_image, function, radius, decomposition):
|
||||
"""Validate footprint decomposition for various shapes.
|
||||
|
||||
comparison is made to the case without decomposition.
|
||||
"""
|
||||
footprint_ndarray = footprints.square(radius, decomposition=None)
|
||||
footprint = footprints.square(radius, decomposition=decomposition)
|
||||
func = getattr(gray, function)
|
||||
expected = func(cam_image, footprint=footprint_ndarray)
|
||||
out = func(cam_image, footprint=footprint)
|
||||
assert_array_equal(expected, out)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"function", ["erosion", "dilation", "closing", "opening", "white_tophat",
|
||||
"black_tophat"],
|
||||
)
|
||||
@pytest.mark.parametrize("m", (0, 1, 3, 5))
|
||||
@pytest.mark.parametrize("n", (0, 1, 2, 3))
|
||||
@pytest.mark.parametrize("decomposition", ['sequence'])
|
||||
def test_octagon_decomposition(cam_image, function, m, n, decomposition):
|
||||
"""Validate footprint decomposition for various shapes.
|
||||
|
||||
comparison is made to the case without decomposition.
|
||||
"""
|
||||
if m == 0 and n == 0:
|
||||
with pytest.raises(ValueError):
|
||||
footprints.octagon(m, n, decomposition=decomposition)
|
||||
else:
|
||||
footprint_ndarray = footprints.octagon(m, n, decomposition=None)
|
||||
footprint = footprints.octagon(m, n, decomposition=decomposition)
|
||||
func = getattr(gray, function)
|
||||
expected = func(cam_image, footprint=footprint_ndarray)
|
||||
out = func(cam_image, footprint=footprint)
|
||||
assert_array_equal(expected, out)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"function", ["erosion", "dilation", "closing", "opening", "white_tophat",
|
||||
"black_tophat"],
|
||||
)
|
||||
@pytest.mark.parametrize("size", (5,))
|
||||
@pytest.mark.parametrize("decomposition", ['separable', 'sequence'])
|
||||
def test_cube_decomposition(cell3d_image, function, size, decomposition):
|
||||
"""Validate footprint decomposition for various shapes.
|
||||
|
||||
comparison is made to the case without decomposition.
|
||||
"""
|
||||
footprint_ndarray = footprints.cube(size, decomposition=None)
|
||||
footprint = footprints.cube(size, decomposition=decomposition)
|
||||
func = getattr(gray, function)
|
||||
expected = func(cell3d_image, footprint=footprint_ndarray)
|
||||
out = func(cell3d_image, footprint=footprint)
|
||||
assert_array_equal(expected, out)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"function", ["erosion", "dilation", "closing", "opening", "white_tophat",
|
||||
"black_tophat"],
|
||||
)
|
||||
@pytest.mark.parametrize("radius", (3,))
|
||||
@pytest.mark.parametrize("decomposition", ['sequence'])
|
||||
def test_octahedron_decomposition(cell3d_image, function, radius,
|
||||
decomposition):
|
||||
"""Validate footprint decomposition for various shapes.
|
||||
|
||||
comparison is made to the case without decomposition.
|
||||
"""
|
||||
footprint_ndarray = footprints.octahedron(radius, decomposition=None)
|
||||
footprint = footprints.octahedron(radius, decomposition=decomposition)
|
||||
func = getattr(gray, function)
|
||||
expected = func(cell3d_image, footprint=footprint_ndarray)
|
||||
out = func(cell3d_image, footprint=footprint)
|
||||
assert_array_equal(expected, out)
|
||||
@@ -1,82 +0,0 @@
|
||||
import numpy as np
|
||||
from numpy.testing import assert_array_equal
|
||||
|
||||
from skimage import color, data, morphology
|
||||
from skimage.morphology import binary, isotropic
|
||||
from skimage.util import img_as_bool
|
||||
|
||||
img = color.rgb2gray(data.astronaut())
|
||||
bw_img = img > 100 / 255.
|
||||
|
||||
|
||||
def test_non_square_image():
|
||||
isotropic_res = isotropic.isotropic_erosion(bw_img[:100, :200], 3)
|
||||
binary_res = img_as_bool(binary.binary_erosion(
|
||||
bw_img[:100, :200], morphology.disk(3)))
|
||||
assert_array_equal(isotropic_res, binary_res)
|
||||
|
||||
|
||||
def test_isotropic_erosion():
|
||||
isotropic_res = isotropic.isotropic_erosion(bw_img, 3)
|
||||
binary_res = img_as_bool(binary.binary_erosion(bw_img, morphology.disk(3)))
|
||||
assert_array_equal(isotropic_res, binary_res)
|
||||
|
||||
|
||||
def _disk_with_spacing(radius, dtype=np.uint8, *, strict_radius=True, spacing=None):
|
||||
# Identical to morphology.disk, but with a spacing parameter and without decomposition.
|
||||
# This is different from morphology.ellipse which produces a slightly different footprint.
|
||||
L = np.arange(-radius, radius + 1)
|
||||
X, Y = np.meshgrid(L, L)
|
||||
|
||||
if spacing is not None:
|
||||
X *= spacing[1]
|
||||
Y *= spacing[0]
|
||||
|
||||
if not strict_radius:
|
||||
radius += 0.5
|
||||
return np.array((X ** 2 + Y ** 2) <= radius ** 2, dtype=dtype)
|
||||
|
||||
|
||||
def test_isotropic_erosion_spacing():
|
||||
isotropic_res = isotropic.isotropic_dilation(bw_img, 6, spacing=(1,2))
|
||||
binary_res = img_as_bool(binary.binary_dilation(bw_img, _disk_with_spacing(6, spacing=(1,2))))
|
||||
assert_array_equal(isotropic_res, binary_res)
|
||||
|
||||
|
||||
def test_isotropic_dilation():
|
||||
isotropic_res = isotropic.isotropic_dilation(bw_img, 3)
|
||||
binary_res = img_as_bool(
|
||||
binary.binary_dilation(
|
||||
bw_img, morphology.disk(3)))
|
||||
assert_array_equal(isotropic_res, binary_res)
|
||||
|
||||
|
||||
def test_isotropic_closing():
|
||||
isotropic_res = isotropic.isotropic_closing(bw_img, 3)
|
||||
binary_res = img_as_bool(binary.binary_closing(bw_img, morphology.disk(3)))
|
||||
assert_array_equal(isotropic_res, binary_res)
|
||||
|
||||
|
||||
def test_isotropic_opening():
|
||||
isotropic_res = isotropic.isotropic_opening(bw_img, 3)
|
||||
binary_res = img_as_bool(binary.binary_opening(bw_img, morphology.disk(3)))
|
||||
assert_array_equal(isotropic_res, binary_res)
|
||||
|
||||
|
||||
def test_footprint_overflow():
|
||||
img = np.zeros((20, 20), dtype=bool)
|
||||
img[2:19, 2:19] = True
|
||||
isotropic_res = isotropic.isotropic_erosion(img, 9)
|
||||
binary_res = img_as_bool(binary.binary_erosion(img, morphology.disk(9)))
|
||||
assert_array_equal(isotropic_res, binary_res)
|
||||
|
||||
|
||||
def test_out_argument():
|
||||
for func in (isotropic.isotropic_erosion, isotropic.isotropic_dilation):
|
||||
radius = 3
|
||||
img = np.ones((10, 10))
|
||||
out = np.zeros_like(img)
|
||||
out_saved = out.copy()
|
||||
func(img, radius, out=out)
|
||||
assert np.any(out != out_saved)
|
||||
assert_array_equal(out, func(img, radius))
|
||||
@@ -1,453 +0,0 @@
|
||||
import numpy as np
|
||||
from skimage.morphology import max_tree, area_closing, area_opening
|
||||
from skimage.morphology import max_tree_local_maxima, diameter_opening
|
||||
from skimage.morphology import diameter_closing
|
||||
from skimage.util import invert
|
||||
|
||||
from skimage._shared.testing import assert_array_equal, TestCase
|
||||
|
||||
eps = 1e-12
|
||||
|
||||
|
||||
def _full_type_test(img, param, expected, func, param_scale=False,
|
||||
**keywords):
|
||||
|
||||
# images as they are
|
||||
out = func(img, param, **keywords)
|
||||
assert_array_equal(out, expected)
|
||||
|
||||
# unsigned int
|
||||
for dt in [np.uint32, np.uint64]:
|
||||
img_cast = img.astype(dt)
|
||||
out = func(img_cast, param, **keywords)
|
||||
exp_cast = expected.astype(dt)
|
||||
assert_array_equal(out, exp_cast)
|
||||
|
||||
# float
|
||||
data_float = img.astype(np.float64)
|
||||
data_float = data_float / 255.0
|
||||
expected_float = expected.astype(np.float64)
|
||||
expected_float = expected_float / 255.0
|
||||
if param_scale:
|
||||
param_cast = param / 255.0
|
||||
else:
|
||||
param_cast = param
|
||||
for dt in [np.float32, np.float64]:
|
||||
data_cast = data_float.astype(dt)
|
||||
out = func(data_cast, param_cast, **keywords)
|
||||
exp_cast = expected_float.astype(dt)
|
||||
error_img = 255.0 * exp_cast - 255.0 * out
|
||||
error = (error_img >= 1.0).sum()
|
||||
assert error < eps
|
||||
|
||||
# signed images
|
||||
img_signed = img.astype(np.int16)
|
||||
img_signed = img_signed - 128
|
||||
exp_signed = expected.astype(np.int16)
|
||||
exp_signed = exp_signed - 128
|
||||
for dt in [np.int8, np.int16, np.int32, np.int64]:
|
||||
img_s = img_signed.astype(dt)
|
||||
out = func(img_s, param, **keywords)
|
||||
exp_s = exp_signed.astype(dt)
|
||||
assert_array_equal(out, exp_s)
|
||||
|
||||
|
||||
class TestMaxtree(TestCase):
|
||||
|
||||
def test_max_tree(self):
|
||||
"Test for max tree"
|
||||
img_type = np.uint8
|
||||
img = np.array([[10, 8, 8, 9],
|
||||
[7, 7, 9, 9],
|
||||
[8, 7, 10, 10],
|
||||
[9, 9, 10, 10]], dtype=img_type)
|
||||
|
||||
P_exp = np.array([[1, 4, 1, 1],
|
||||
[4, 4, 3, 3],
|
||||
[1, 4, 3, 10],
|
||||
[3, 3, 10, 10]], dtype=np.int64)
|
||||
|
||||
S_exp = np.array([4, 5, 9, 1, 2, 8, 3, 6, 7,
|
||||
12, 13, 0, 10, 11, 14, 15],
|
||||
dtype=np.int64)
|
||||
|
||||
for img_type in [np.uint8, np.uint16, np.uint32, np.uint64]:
|
||||
img = img.astype(img_type)
|
||||
P, S = max_tree(img, connectivity=2)
|
||||
assert_array_equal(P, P_exp)
|
||||
assert_array_equal(S, S_exp)
|
||||
|
||||
for img_type in [np.int8, np.int16, np.int32, np.int64]:
|
||||
img = img.astype(img_type)
|
||||
img_shifted = img - 9
|
||||
P, S = max_tree(img_shifted, connectivity=2)
|
||||
assert_array_equal(P, P_exp)
|
||||
assert_array_equal(S, S_exp)
|
||||
|
||||
img_float = img.astype(float)
|
||||
img_float = (img_float - 8) / 2.0
|
||||
for img_type in [np.float32, np.float64]:
|
||||
img_float = img_float.astype(img_type)
|
||||
P, S = max_tree(img_float, connectivity=2)
|
||||
assert_array_equal(P, P_exp)
|
||||
assert_array_equal(S, S_exp)
|
||||
|
||||
return
|
||||
|
||||
def test_area_closing(self):
|
||||
"Test for Area Closing (2 thresholds, all types)"
|
||||
|
||||
# original image
|
||||
img = np.array(
|
||||
[[240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240],
|
||||
[240, 200, 200, 240, 200, 240, 200, 200, 240, 240, 200, 240],
|
||||
[240, 200, 40, 240, 240, 240, 240, 240, 240, 240, 40, 240],
|
||||
[240, 240, 240, 240, 100, 240, 100, 100, 240, 240, 200, 240],
|
||||
[240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240],
|
||||
[200, 200, 200, 200, 200, 200, 200, 240, 200, 200, 255, 255],
|
||||
[200, 255, 200, 200, 200, 255, 200, 240, 255, 255, 255, 40],
|
||||
[200, 200, 200, 100, 200, 200, 200, 240, 255, 255, 255, 255],
|
||||
[200, 200, 200, 100, 200, 200, 200, 240, 200, 200, 255, 255],
|
||||
[200, 200, 200, 200, 200, 40, 200, 240, 240, 100, 255, 255],
|
||||
[200, 40, 255, 255, 255, 40, 200, 255, 200, 200, 255, 255],
|
||||
[200, 200, 200, 200, 200, 200, 200, 255, 255, 255, 255, 255]],
|
||||
dtype=np.uint8)
|
||||
|
||||
# expected area closing with area 2
|
||||
expected_2 = np.array(
|
||||
[[240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240],
|
||||
[240, 200, 200, 240, 240, 240, 200, 200, 240, 240, 200, 240],
|
||||
[240, 200, 200, 240, 240, 240, 240, 240, 240, 240, 200, 240],
|
||||
[240, 240, 240, 240, 240, 240, 100, 100, 240, 240, 200, 240],
|
||||
[240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240],
|
||||
[200, 200, 200, 200, 200, 200, 200, 240, 200, 200, 255, 255],
|
||||
[200, 255, 200, 200, 200, 255, 200, 240, 255, 255, 255, 255],
|
||||
[200, 200, 200, 100, 200, 200, 200, 240, 255, 255, 255, 255],
|
||||
[200, 200, 200, 100, 200, 200, 200, 240, 200, 200, 255, 255],
|
||||
[200, 200, 200, 200, 200, 40, 200, 240, 240, 200, 255, 255],
|
||||
[200, 200, 255, 255, 255, 40, 200, 255, 200, 200, 255, 255],
|
||||
[200, 200, 200, 200, 200, 200, 200, 255, 255, 255, 255, 255]],
|
||||
dtype=np.uint8)
|
||||
|
||||
# expected diameter closing with diameter 4
|
||||
expected_4 = np.array(
|
||||
[[240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240],
|
||||
[240, 200, 200, 240, 240, 240, 240, 240, 240, 240, 240, 240],
|
||||
[240, 200, 200, 240, 240, 240, 240, 240, 240, 240, 240, 240],
|
||||
[240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240],
|
||||
[240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240],
|
||||
[200, 200, 200, 200, 200, 200, 200, 240, 240, 240, 255, 255],
|
||||
[200, 255, 200, 200, 200, 255, 200, 240, 255, 255, 255, 255],
|
||||
[200, 200, 200, 200, 200, 200, 200, 240, 255, 255, 255, 255],
|
||||
[200, 200, 200, 200, 200, 200, 200, 240, 200, 200, 255, 255],
|
||||
[200, 200, 200, 200, 200, 200, 200, 240, 240, 200, 255, 255],
|
||||
[200, 200, 255, 255, 255, 200, 200, 255, 200, 200, 255, 255],
|
||||
[200, 200, 200, 200, 200, 200, 200, 255, 255, 255, 255, 255]],
|
||||
dtype=np.uint8)
|
||||
|
||||
# _full_type_test makes a test with many image types.
|
||||
_full_type_test(img, 2, expected_2, area_closing, connectivity=2)
|
||||
_full_type_test(img, 4, expected_4, area_closing, connectivity=2)
|
||||
|
||||
P, S = max_tree(invert(img), connectivity=2)
|
||||
_full_type_test(img, 4, expected_4, area_closing,
|
||||
parent=P, tree_traverser=S)
|
||||
|
||||
def test_area_opening(self):
|
||||
"Test for Area Opening (2 thresholds, all types)"
|
||||
|
||||
# original image
|
||||
img = np.array([[15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15],
|
||||
[15, 55, 55, 15, 55, 15, 55, 55, 15, 15, 55, 15],
|
||||
[15, 55, 215, 15, 15, 15, 15, 15, 15, 15, 215, 15],
|
||||
[15, 15, 15, 15, 155, 15, 155, 155, 15, 15, 55, 15],
|
||||
[15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15],
|
||||
[55, 55, 55, 55, 55, 55, 55, 15, 55, 55, 0, 0],
|
||||
[55, 0, 55, 55, 55, 0, 55, 15, 0, 0, 0, 215],
|
||||
[55, 55, 55, 155, 55, 55, 55, 15, 0, 0, 0, 0],
|
||||
[55, 55, 55, 155, 55, 55, 55, 15, 55, 55, 0, 0],
|
||||
[55, 55, 55, 55, 55, 215, 55, 15, 15, 155, 0, 0],
|
||||
[55, 215, 0, 0, 0, 215, 55, 0, 55, 55, 0, 0],
|
||||
[55, 55, 55, 55, 55, 55, 55, 0, 0, 0, 0, 0]],
|
||||
dtype=np.uint8)
|
||||
|
||||
# expected area closing with area 2
|
||||
expected_2 = np.array([[15, 15, 15, 15, 15, 15, 15, 15, 15,
|
||||
15, 15, 15],
|
||||
[15, 55, 55, 15, 15, 15, 55, 55, 15,
|
||||
15, 55, 15],
|
||||
[15, 55, 55, 15, 15, 15, 15, 15, 15,
|
||||
15, 55, 15],
|
||||
[15, 15, 15, 15, 15, 15, 155, 155, 15,
|
||||
15, 55, 15],
|
||||
[15, 15, 15, 15, 15, 15, 15, 15, 15,
|
||||
15, 15, 15],
|
||||
[55, 55, 55, 55, 55, 55, 55, 15, 55,
|
||||
55, 0, 0],
|
||||
[55, 0, 55, 55, 55, 0, 55, 15, 0,
|
||||
0, 0, 0],
|
||||
[55, 55, 55, 155, 55, 55, 55, 15, 0,
|
||||
0, 0, 0],
|
||||
[55, 55, 55, 155, 55, 55, 55, 15, 55,
|
||||
55, 0, 0],
|
||||
[55, 55, 55, 55, 55, 215, 55, 15, 15,
|
||||
55, 0, 0],
|
||||
[55, 55, 0, 0, 0, 215, 55, 0, 55,
|
||||
55, 0, 0],
|
||||
[55, 55, 55, 55, 55, 55, 55, 0, 0,
|
||||
0, 0, 0]],
|
||||
dtype=np.uint8)
|
||||
|
||||
# expected diameter closing with diameter 4
|
||||
expected_4 = np.array([[15, 15, 15, 15, 15, 15, 15, 15, 15,
|
||||
15, 15, 15],
|
||||
[15, 55, 55, 15, 15, 15, 15, 15, 15,
|
||||
15, 15, 15],
|
||||
[15, 55, 55, 15, 15, 15, 15, 15, 15,
|
||||
15, 15, 15],
|
||||
[15, 15, 15, 15, 15, 15, 15, 15, 15,
|
||||
15, 15, 15],
|
||||
[15, 15, 15, 15, 15, 15, 15, 15, 15,
|
||||
15, 15, 15],
|
||||
[55, 55, 55, 55, 55, 55, 55, 15, 15,
|
||||
15, 0, 0],
|
||||
[55, 0, 55, 55, 55, 0, 55, 15, 0,
|
||||
0, 0, 0],
|
||||
[55, 55, 55, 55, 55, 55, 55, 15, 0,
|
||||
0, 0, 0],
|
||||
[55, 55, 55, 55, 55, 55, 55, 15, 55,
|
||||
55, 0, 0],
|
||||
[55, 55, 55, 55, 55, 55, 55, 15, 15,
|
||||
55, 0, 0],
|
||||
[55, 55, 0, 0, 0, 55, 55, 0, 55,
|
||||
55, 0, 0],
|
||||
[55, 55, 55, 55, 55, 55, 55, 0, 0,
|
||||
0, 0, 0]],
|
||||
dtype=np.uint8)
|
||||
|
||||
# _full_type_test makes a test with many image types.
|
||||
_full_type_test(img, 2, expected_2, area_opening, connectivity=2)
|
||||
_full_type_test(img, 4, expected_4, area_opening, connectivity=2)
|
||||
|
||||
P, S = max_tree(img, connectivity=2)
|
||||
_full_type_test(img, 4, expected_4, area_opening,
|
||||
parent=P, tree_traverser=S)
|
||||
|
||||
def test_diameter_closing(self):
|
||||
"Test for Diameter Opening (2 thresholds, all types)"
|
||||
img = np.array([[97, 95, 93, 92, 91, 90, 90, 90, 91, 92, 93, 95],
|
||||
[95, 93, 91, 89, 88, 88, 88, 88, 88, 89, 91, 93],
|
||||
[93, 63, 63, 63, 63, 86, 86, 86, 87, 43, 43, 91],
|
||||
[92, 89, 88, 86, 85, 85, 84, 85, 85, 43, 43, 89],
|
||||
[91, 88, 87, 85, 84, 84, 83, 84, 84, 85, 87, 88],
|
||||
[90, 88, 86, 85, 84, 83, 83, 83, 84, 85, 86, 88],
|
||||
[90, 88, 86, 84, 83, 83, 82, 83, 83, 84, 86, 88],
|
||||
[90, 88, 86, 85, 84, 83, 83, 83, 84, 85, 86, 88],
|
||||
[91, 88, 87, 85, 84, 84, 83, 84, 84, 85, 87, 88],
|
||||
[92, 89, 23, 23, 85, 85, 84, 85, 85, 3, 3, 89],
|
||||
[93, 91, 23, 23, 87, 86, 86, 86, 87, 88, 3, 91],
|
||||
[95, 93, 91, 89, 88, 88, 88, 88, 88, 89, 91, 93]],
|
||||
dtype=np.uint8)
|
||||
|
||||
ex2 = np.array([[97, 95, 93, 92, 91, 90, 90, 90, 91, 92, 93, 95],
|
||||
[95, 93, 91, 89, 88, 88, 88, 88, 88, 89, 91, 93],
|
||||
[93, 63, 63, 63, 63, 86, 86, 86, 87, 43, 43, 91],
|
||||
[92, 89, 88, 86, 85, 85, 84, 85, 85, 43, 43, 89],
|
||||
[91, 88, 87, 85, 84, 84, 83, 84, 84, 85, 87, 88],
|
||||
[90, 88, 86, 85, 84, 83, 83, 83, 84, 85, 86, 88],
|
||||
[90, 88, 86, 84, 83, 83, 83, 83, 83, 84, 86, 88],
|
||||
[90, 88, 86, 85, 84, 83, 83, 83, 84, 85, 86, 88],
|
||||
[91, 88, 87, 85, 84, 84, 83, 84, 84, 85, 87, 88],
|
||||
[92, 89, 23, 23, 85, 85, 84, 85, 85, 3, 3, 89],
|
||||
[93, 91, 23, 23, 87, 86, 86, 86, 87, 88, 3, 91],
|
||||
[95, 93, 91, 89, 88, 88, 88, 88, 88, 89, 91, 93]],
|
||||
dtype=np.uint8)
|
||||
|
||||
ex4 = np.array([[97, 95, 93, 92, 91, 90, 90, 90, 91, 92, 93, 95],
|
||||
[95, 93, 91, 89, 88, 88, 88, 88, 88, 89, 91, 93],
|
||||
[93, 63, 63, 63, 63, 86, 86, 86, 87, 84, 84, 91],
|
||||
[92, 89, 88, 86, 85, 85, 84, 85, 85, 84, 84, 89],
|
||||
[91, 88, 87, 85, 84, 84, 83, 84, 84, 85, 87, 88],
|
||||
[90, 88, 86, 85, 84, 83, 83, 83, 84, 85, 86, 88],
|
||||
[90, 88, 86, 84, 83, 83, 83, 83, 83, 84, 86, 88],
|
||||
[90, 88, 86, 85, 84, 83, 83, 83, 84, 85, 86, 88],
|
||||
[91, 88, 87, 85, 84, 84, 83, 84, 84, 85, 87, 88],
|
||||
[92, 89, 84, 84, 85, 85, 84, 85, 85, 84, 84, 89],
|
||||
[93, 91, 84, 84, 87, 86, 86, 86, 87, 88, 84, 91],
|
||||
[95, 93, 91, 89, 88, 88, 88, 88, 88, 89, 91, 93]],
|
||||
dtype=np.uint8)
|
||||
|
||||
# _full_type_test makes a test with many image types.
|
||||
_full_type_test(img, 2, ex2, diameter_closing, connectivity=2)
|
||||
_full_type_test(img, 4, ex4, diameter_closing, connectivity=2)
|
||||
|
||||
P, S = max_tree(invert(img), connectivity=2)
|
||||
_full_type_test(img, 4, ex4, diameter_opening,
|
||||
parent=P, tree_traverser=S)
|
||||
|
||||
def test_diameter_opening(self):
|
||||
"Test for Diameter Opening (2 thresholds, all types)"
|
||||
img = np.array([[5, 7, 9, 11, 12, 12, 12, 12, 12, 11, 9, 7],
|
||||
[7, 10, 11, 13, 14, 14, 15, 14, 14, 13, 11, 10],
|
||||
[9, 40, 40, 40, 40, 16, 16, 16, 16, 60, 60, 11],
|
||||
[11, 13, 15, 16, 17, 18, 18, 18, 17, 60, 60, 13],
|
||||
[12, 14, 16, 17, 18, 19, 19, 19, 18, 17, 16, 14],
|
||||
[12, 14, 16, 18, 19, 19, 19, 19, 19, 18, 16, 14],
|
||||
[12, 15, 16, 18, 19, 19, 20, 19, 19, 18, 16, 15],
|
||||
[12, 14, 16, 18, 19, 19, 19, 19, 19, 18, 16, 14],
|
||||
[12, 14, 16, 17, 18, 19, 19, 19, 18, 17, 16, 14],
|
||||
[11, 13, 80, 80, 17, 18, 18, 18, 17, 100, 100, 13],
|
||||
[9, 11, 80, 80, 16, 16, 16, 16, 16, 15, 100, 11],
|
||||
[7, 10, 11, 13, 14, 14, 15, 14, 14, 13, 11, 10]])
|
||||
|
||||
ex2 = np.array([[5, 7, 9, 11, 12, 12, 12, 12, 12, 11, 9, 7],
|
||||
[7, 10, 11, 13, 14, 14, 15, 14, 14, 13, 11, 10],
|
||||
[9, 40, 40, 40, 40, 16, 16, 16, 16, 60, 60, 11],
|
||||
[11, 13, 15, 16, 17, 18, 18, 18, 17, 60, 60, 13],
|
||||
[12, 14, 16, 17, 18, 19, 19, 19, 18, 17, 16, 14],
|
||||
[12, 14, 16, 18, 19, 19, 19, 19, 19, 18, 16, 14],
|
||||
[12, 15, 16, 18, 19, 19, 19, 19, 19, 18, 16, 15],
|
||||
[12, 14, 16, 18, 19, 19, 19, 19, 19, 18, 16, 14],
|
||||
[12, 14, 16, 17, 18, 19, 19, 19, 18, 17, 16, 14],
|
||||
[11, 13, 80, 80, 17, 18, 18, 18, 17, 100, 100, 13],
|
||||
[9, 11, 80, 80, 16, 16, 16, 16, 16, 15, 100, 11],
|
||||
[7, 10, 11, 13, 14, 14, 15, 14, 14, 13, 11, 10]])
|
||||
|
||||
ex4 = np.array([[5, 7, 9, 11, 12, 12, 12, 12, 12, 11, 9, 7],
|
||||
[7, 10, 11, 13, 14, 14, 15, 14, 14, 13, 11, 10],
|
||||
[9, 40, 40, 40, 40, 16, 16, 16, 16, 18, 18, 11],
|
||||
[11, 13, 15, 16, 17, 18, 18, 18, 17, 18, 18, 13],
|
||||
[12, 14, 16, 17, 18, 19, 19, 19, 18, 17, 16, 14],
|
||||
[12, 14, 16, 18, 19, 19, 19, 19, 19, 18, 16, 14],
|
||||
[12, 15, 16, 18, 19, 19, 19, 19, 19, 18, 16, 15],
|
||||
[12, 14, 16, 18, 19, 19, 19, 19, 19, 18, 16, 14],
|
||||
[12, 14, 16, 17, 18, 19, 19, 19, 18, 17, 16, 14],
|
||||
[11, 13, 18, 18, 17, 18, 18, 18, 17, 18, 18, 13],
|
||||
[9, 11, 18, 18, 16, 16, 16, 16, 16, 15, 18, 11],
|
||||
[7, 10, 11, 13, 14, 14, 15, 14, 14, 13, 11, 10]])
|
||||
|
||||
# _full_type_test makes a test with many image types.
|
||||
_full_type_test(img, 2, ex2, diameter_opening, connectivity=2)
|
||||
_full_type_test(img, 4, ex4, diameter_opening, connectivity=2)
|
||||
|
||||
P, S = max_tree(img, connectivity=2)
|
||||
_full_type_test(img, 4, ex4, diameter_opening,
|
||||
parent=P, tree_traverser=S)
|
||||
|
||||
def test_local_maxima(self):
|
||||
"local maxima for various data types"
|
||||
data = np.array([[10, 11, 13, 14, 14, 15, 14, 14, 13, 11],
|
||||
[11, 13, 15, 16, 16, 16, 16, 16, 15, 13],
|
||||
[13, 15, 40, 40, 18, 18, 18, 60, 60, 15],
|
||||
[14, 16, 40, 40, 19, 19, 19, 60, 60, 16],
|
||||
[14, 16, 18, 19, 19, 19, 19, 19, 18, 16],
|
||||
[15, 16, 18, 19, 19, 20, 19, 19, 18, 16],
|
||||
[14, 16, 18, 19, 19, 19, 19, 19, 18, 16],
|
||||
[14, 16, 80, 80, 19, 19, 19, 100, 100, 16],
|
||||
[13, 15, 80, 80, 18, 18, 18, 100, 100, 15],
|
||||
[11, 13, 15, 16, 16, 16, 16, 16, 15, 13]],
|
||||
dtype=np.uint8)
|
||||
expected_result = np.array([[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
||||
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
||||
[0, 0, 1, 1, 0, 0, 0, 1, 1, 0],
|
||||
[0, 0, 1, 1, 0, 0, 0, 1, 1, 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, 1, 1, 0, 0, 0, 1, 1, 0],
|
||||
[0, 0, 1, 1, 0, 0, 0, 1, 1, 0],
|
||||
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0]],
|
||||
dtype=np.uint64)
|
||||
for dtype in [np.uint8, np.uint64, np.int8, np.int64]:
|
||||
|
||||
test_data = data.astype(dtype)
|
||||
out = max_tree_local_maxima(test_data, connectivity=1)
|
||||
out_bin = out > 0
|
||||
assert_array_equal(expected_result, out_bin)
|
||||
assert out.dtype == expected_result.dtype
|
||||
assert np.max(out) == 5
|
||||
|
||||
P, S = max_tree(test_data)
|
||||
out = max_tree_local_maxima(test_data,
|
||||
parent=P,
|
||||
tree_traverser=S)
|
||||
|
||||
assert_array_equal(expected_result, out_bin)
|
||||
|
||||
assert out.dtype == expected_result.dtype
|
||||
assert np.max(out) == 5
|
||||
|
||||
def test_extrema_float(self):
|
||||
"specific tests for float type"
|
||||
data = np.array([[0.10, 0.11, 0.13, 0.14, 0.14, 0.15, 0.14,
|
||||
0.14, 0.13, 0.11],
|
||||
[0.11, 0.13, 0.15, 0.16, 0.16, 0.16, 0.16,
|
||||
0.16, 0.15, 0.13],
|
||||
[0.13, 0.15, 0.40, 0.40, 0.18, 0.18, 0.18,
|
||||
0.60, 0.60, 0.15],
|
||||
[0.14, 0.16, 0.40, 0.40, 0.19, 0.19, 0.19,
|
||||
0.60, 0.60, 0.16],
|
||||
[0.14, 0.16, 0.18, 0.19, 0.19, 0.19, 0.19,
|
||||
0.19, 0.18, 0.16],
|
||||
[0.15, 0.182, 0.18, 0.19, 0.204, 0.20, 0.19,
|
||||
0.19, 0.18, 0.16],
|
||||
[0.14, 0.16, 0.18, 0.19, 0.19, 0.19, 0.19,
|
||||
0.19, 0.18, 0.16],
|
||||
[0.14, 0.16, 0.80, 0.80, 0.19, 0.19, 0.19,
|
||||
4.0, 1.0, 0.16],
|
||||
[0.13, 0.15, 0.80, 0.80, 0.18, 0.18, 0.18,
|
||||
1.0, 1.0, 0.15],
|
||||
[0.11, 0.13, 0.15, 0.16, 0.16, 0.16, 0.16,
|
||||
0.16, 0.15, 0.13]],
|
||||
dtype=np.float32)
|
||||
|
||||
expected_result = np.array([[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
||||
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
||||
[0, 0, 1, 1, 0, 0, 0, 1, 1, 0],
|
||||
[0, 0, 1, 1, 0, 0, 0, 1, 1, 0],
|
||||
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
||||
[0, 1, 0, 0, 1, 0, 0, 0, 0, 0],
|
||||
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
||||
[0, 0, 1, 1, 0, 0, 0, 1, 0, 0],
|
||||
[0, 0, 1, 1, 0, 0, 0, 0, 0, 0],
|
||||
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0]],
|
||||
dtype=np.uint8)
|
||||
|
||||
# test for local maxima
|
||||
out = max_tree_local_maxima(data, connectivity=1)
|
||||
out_bin = out > 0
|
||||
assert_array_equal(expected_result, out_bin)
|
||||
assert np.max(out) == 6
|
||||
|
||||
def test_3d(self):
|
||||
"""tests the detection of maxima in 3D."""
|
||||
img = np.zeros((8, 8, 8), dtype=np.uint8)
|
||||
local_maxima = np.zeros((8, 8, 8), dtype=np.uint64)
|
||||
|
||||
# first maximum: only one pixel
|
||||
img[1, 1:3, 1:3] = 100
|
||||
img[2, 2, 2] = 200
|
||||
img[3, 1:3, 1:3] = 100
|
||||
local_maxima[2, 2, 2] = 1
|
||||
|
||||
# second maximum: three pixels in z-direction
|
||||
img[5:8, 1, 1] = 200
|
||||
local_maxima[5:8, 1, 1] = 1
|
||||
|
||||
# third: two maxima in 0 and 3.
|
||||
img[0, 5:8, 5:8] = 200
|
||||
img[1, 6, 6] = 100
|
||||
img[2, 5:7, 5:7] = 200
|
||||
img[0:3, 5:8, 5:8] += 50
|
||||
local_maxima[0, 5:8, 5:8] = 1
|
||||
local_maxima[2, 5:7, 5:7] = 1
|
||||
|
||||
# four : one maximum in the corner of the square
|
||||
img[6:8, 6:8, 6:8] = 200
|
||||
img[7, 7, 7] = 255
|
||||
local_maxima[7, 7, 7] = 1
|
||||
|
||||
out = max_tree_local_maxima(img)
|
||||
out_bin = out > 0
|
||||
assert_array_equal(local_maxima, out_bin)
|
||||
assert np.max(out) == 5
|
||||
@@ -1,225 +0,0 @@
|
||||
import numpy as np
|
||||
import pytest
|
||||
from skimage.morphology import remove_small_objects, remove_small_holes
|
||||
|
||||
from skimage._shared import testing
|
||||
from skimage._shared.testing import assert_array_equal, assert_equal
|
||||
from skimage._shared._warnings import expected_warnings
|
||||
|
||||
|
||||
test_image = np.array([[0, 0, 0, 1, 0],
|
||||
[1, 1, 1, 0, 0],
|
||||
[1, 1, 1, 0, 1]], bool)
|
||||
|
||||
|
||||
def test_one_connectivity():
|
||||
expected = np.array([[0, 0, 0, 0, 0],
|
||||
[1, 1, 1, 0, 0],
|
||||
[1, 1, 1, 0, 0]], bool)
|
||||
observed = remove_small_objects(test_image, min_size=6)
|
||||
assert_array_equal(observed, expected)
|
||||
|
||||
|
||||
def test_two_connectivity():
|
||||
expected = np.array([[0, 0, 0, 1, 0],
|
||||
[1, 1, 1, 0, 0],
|
||||
[1, 1, 1, 0, 0]], bool)
|
||||
observed = remove_small_objects(test_image, min_size=7, connectivity=2)
|
||||
assert_array_equal(observed, expected)
|
||||
|
||||
|
||||
def test_in_place():
|
||||
image = test_image.copy()
|
||||
observed = remove_small_objects(image, min_size=6, out=image)
|
||||
assert_equal(observed is image, True,
|
||||
"remove_small_objects in_place argument failed.")
|
||||
|
||||
|
||||
@pytest.mark.parametrize("in_dtype", [bool, int, np.int32])
|
||||
@pytest.mark.parametrize("out_dtype", [bool, int, np.int32])
|
||||
def test_out(in_dtype, out_dtype):
|
||||
image = test_image.astype(in_dtype, copy=True)
|
||||
expected_out = np.empty_like(test_image, dtype=out_dtype)
|
||||
|
||||
if out_dtype != bool:
|
||||
# object with only 1 label will warn on non-bool output dtype
|
||||
exp_warn = ["Only one label was provided"]
|
||||
else:
|
||||
exp_warn = []
|
||||
|
||||
with expected_warnings(exp_warn):
|
||||
out = remove_small_objects(image, min_size=6, out=expected_out)
|
||||
|
||||
assert out is expected_out
|
||||
|
||||
|
||||
def test_labeled_image():
|
||||
labeled_image = np.array([[2, 2, 2, 0, 1],
|
||||
[2, 2, 2, 0, 1],
|
||||
[2, 0, 0, 0, 0],
|
||||
[0, 0, 3, 3, 3]], dtype=int)
|
||||
expected = np.array([[2, 2, 2, 0, 0],
|
||||
[2, 2, 2, 0, 0],
|
||||
[2, 0, 0, 0, 0],
|
||||
[0, 0, 3, 3, 3]], dtype=int)
|
||||
observed = remove_small_objects(labeled_image, min_size=3)
|
||||
assert_array_equal(observed, expected)
|
||||
|
||||
|
||||
def test_uint_image():
|
||||
labeled_image = np.array([[2, 2, 2, 0, 1],
|
||||
[2, 2, 2, 0, 1],
|
||||
[2, 0, 0, 0, 0],
|
||||
[0, 0, 3, 3, 3]], dtype=np.uint8)
|
||||
expected = np.array([[2, 2, 2, 0, 0],
|
||||
[2, 2, 2, 0, 0],
|
||||
[2, 0, 0, 0, 0],
|
||||
[0, 0, 3, 3, 3]], dtype=np.uint8)
|
||||
observed = remove_small_objects(labeled_image, min_size=3)
|
||||
assert_array_equal(observed, expected)
|
||||
|
||||
|
||||
def test_single_label_warning():
|
||||
image = np.array([[0, 0, 0, 1, 0],
|
||||
[1, 1, 1, 0, 0],
|
||||
[1, 1, 1, 0, 0]], int)
|
||||
with expected_warnings(['use a boolean array?']):
|
||||
remove_small_objects(image, min_size=6)
|
||||
|
||||
|
||||
def test_float_input():
|
||||
float_test = np.random.rand(5, 5)
|
||||
with testing.raises(TypeError):
|
||||
remove_small_objects(float_test)
|
||||
|
||||
|
||||
def test_negative_input():
|
||||
negative_int = np.random.randint(-4, -1, size=(5, 5))
|
||||
with testing.raises(ValueError):
|
||||
remove_small_objects(negative_int)
|
||||
|
||||
|
||||
test_holes_image = np.array([[0, 0, 0, 0, 0, 0, 1, 0, 0, 0],
|
||||
[0, 1, 1, 1, 1, 1, 0, 0, 0, 0],
|
||||
[0, 1, 0, 0, 1, 1, 0, 0, 0, 0],
|
||||
[0, 1, 1, 1, 0, 1, 0, 0, 0, 0],
|
||||
[0, 1, 1, 1, 1, 1, 0, 0, 0, 0],
|
||||
[0, 0, 0, 0, 0, 0, 0, 1, 1, 1],
|
||||
[0, 0, 0, 0, 0, 0, 0, 1, 0, 1],
|
||||
[0, 0, 0, 0, 0, 0, 0, 1, 1, 1]], bool)
|
||||
|
||||
|
||||
def test_one_connectivity_holes():
|
||||
expected = np.array([[0, 0, 0, 0, 0, 0, 1, 0, 0, 0],
|
||||
[0, 1, 1, 1, 1, 1, 0, 0, 0, 0],
|
||||
[0, 1, 1, 1, 1, 1, 0, 0, 0, 0],
|
||||
[0, 1, 1, 1, 1, 1, 0, 0, 0, 0],
|
||||
[0, 1, 1, 1, 1, 1, 0, 0, 0, 0],
|
||||
[0, 0, 0, 0, 0, 0, 0, 1, 1, 1],
|
||||
[0, 0, 0, 0, 0, 0, 0, 1, 1, 1],
|
||||
[0, 0, 0, 0, 0, 0, 0, 1, 1, 1]], bool)
|
||||
observed = remove_small_holes(test_holes_image, area_threshold=3)
|
||||
assert_array_equal(observed, expected)
|
||||
|
||||
|
||||
def test_two_connectivity_holes():
|
||||
expected = np.array([[0, 0, 0, 0, 0, 0, 1, 0, 0, 0],
|
||||
[0, 1, 1, 1, 1, 1, 0, 0, 0, 0],
|
||||
[0, 1, 0, 0, 1, 1, 0, 0, 0, 0],
|
||||
[0, 1, 1, 1, 0, 1, 0, 0, 0, 0],
|
||||
[0, 1, 1, 1, 1, 1, 0, 0, 0, 0],
|
||||
[0, 0, 0, 0, 0, 0, 0, 1, 1, 1],
|
||||
[0, 0, 0, 0, 0, 0, 0, 1, 1, 1],
|
||||
[0, 0, 0, 0, 0, 0, 0, 1, 1, 1]], bool)
|
||||
observed = remove_small_holes(test_holes_image, area_threshold=3,
|
||||
connectivity=2)
|
||||
assert_array_equal(observed, expected)
|
||||
|
||||
|
||||
def test_in_place_holes():
|
||||
image = test_holes_image.copy()
|
||||
observed = remove_small_holes(image, area_threshold=3, out=image)
|
||||
assert_equal(observed is image, True,
|
||||
"remove_small_holes in_place argument failed.")
|
||||
|
||||
|
||||
def test_out_remove_small_holes():
|
||||
image = test_holes_image.copy()
|
||||
expected_out = np.empty_like(image)
|
||||
out = remove_small_holes(image, area_threshold=3, out=expected_out)
|
||||
|
||||
assert out is expected_out
|
||||
|
||||
|
||||
def test_non_bool_out():
|
||||
image = test_holes_image.copy()
|
||||
expected_out = np.empty_like(image, dtype=int)
|
||||
with testing.raises(TypeError):
|
||||
remove_small_holes(image, area_threshold=3, out=expected_out)
|
||||
|
||||
|
||||
def test_labeled_image_holes():
|
||||
labeled_holes_image = np.array([[0, 0, 0, 0, 0, 0, 1, 0, 0, 0],
|
||||
[0, 1, 1, 1, 1, 1, 0, 0, 0, 0],
|
||||
[0, 1, 0, 0, 1, 1, 0, 0, 0, 0],
|
||||
[0, 1, 1, 1, 0, 1, 0, 0, 0, 0],
|
||||
[0, 1, 1, 1, 1, 1, 0, 0, 0, 0],
|
||||
[0, 0, 0, 0, 0, 0, 0, 2, 2, 2],
|
||||
[0, 0, 0, 0, 0, 0, 0, 2, 0, 2],
|
||||
[0, 0, 0, 0, 0, 0, 0, 2, 2, 2]],
|
||||
dtype=int)
|
||||
expected = np.array([[0, 0, 0, 0, 0, 0, 1, 0, 0, 0],
|
||||
[0, 1, 1, 1, 1, 1, 0, 0, 0, 0],
|
||||
[0, 1, 1, 1, 1, 1, 0, 0, 0, 0],
|
||||
[0, 1, 1, 1, 1, 1, 0, 0, 0, 0],
|
||||
[0, 1, 1, 1, 1, 1, 0, 0, 0, 0],
|
||||
[0, 0, 0, 0, 0, 0, 0, 1, 1, 1],
|
||||
[0, 0, 0, 0, 0, 0, 0, 1, 1, 1],
|
||||
[0, 0, 0, 0, 0, 0, 0, 1, 1, 1]], dtype=bool)
|
||||
with expected_warnings(['returned as a boolean array']):
|
||||
observed = remove_small_holes(labeled_holes_image, area_threshold=3)
|
||||
assert_array_equal(observed, expected)
|
||||
|
||||
|
||||
def test_uint_image_holes():
|
||||
labeled_holes_image = np.array([[0, 0, 0, 0, 0, 0, 1, 0, 0, 0],
|
||||
[0, 1, 1, 1, 1, 1, 0, 0, 0, 0],
|
||||
[0, 1, 0, 0, 1, 1, 0, 0, 0, 0],
|
||||
[0, 1, 1, 1, 0, 1, 0, 0, 0, 0],
|
||||
[0, 1, 1, 1, 1, 1, 0, 0, 0, 0],
|
||||
[0, 0, 0, 0, 0, 0, 0, 2, 2, 2],
|
||||
[0, 0, 0, 0, 0, 0, 0, 2, 0, 2],
|
||||
[0, 0, 0, 0, 0, 0, 0, 2, 2, 2]],
|
||||
dtype=np.uint8)
|
||||
expected = np.array([[0, 0, 0, 0, 0, 0, 1, 0, 0, 0],
|
||||
[0, 1, 1, 1, 1, 1, 0, 0, 0, 0],
|
||||
[0, 1, 1, 1, 1, 1, 0, 0, 0, 0],
|
||||
[0, 1, 1, 1, 1, 1, 0, 0, 0, 0],
|
||||
[0, 1, 1, 1, 1, 1, 0, 0, 0, 0],
|
||||
[0, 0, 0, 0, 0, 0, 0, 1, 1, 1],
|
||||
[0, 0, 0, 0, 0, 0, 0, 1, 1, 1],
|
||||
[0, 0, 0, 0, 0, 0, 0, 1, 1, 1]], dtype=bool)
|
||||
with expected_warnings(['returned as a boolean array']):
|
||||
observed = remove_small_holes(labeled_holes_image, area_threshold=3)
|
||||
assert_array_equal(observed, expected)
|
||||
|
||||
|
||||
def test_label_warning_holes():
|
||||
labeled_holes_image = np.array([[0, 0, 0, 0, 0, 0, 1, 0, 0, 0],
|
||||
[0, 1, 1, 1, 1, 1, 0, 0, 0, 0],
|
||||
[0, 1, 0, 0, 1, 1, 0, 0, 0, 0],
|
||||
[0, 1, 1, 1, 0, 1, 0, 0, 0, 0],
|
||||
[0, 1, 1, 1, 1, 1, 0, 0, 0, 0],
|
||||
[0, 0, 0, 0, 0, 0, 0, 2, 2, 2],
|
||||
[0, 0, 0, 0, 0, 0, 0, 2, 0, 2],
|
||||
[0, 0, 0, 0, 0, 0, 0, 2, 2, 2]],
|
||||
dtype=int)
|
||||
with expected_warnings(['use a boolean array?']):
|
||||
remove_small_holes(labeled_holes_image, area_threshold=3)
|
||||
remove_small_holes(labeled_holes_image.astype(bool), area_threshold=3)
|
||||
|
||||
|
||||
def test_float_input_holes():
|
||||
float_test = np.random.rand(5, 5)
|
||||
with testing.raises(TypeError):
|
||||
remove_small_holes(float_test)
|
||||
@@ -1,152 +0,0 @@
|
||||
import math
|
||||
import numpy as np
|
||||
import pytest
|
||||
from numpy.testing import assert_array_almost_equal
|
||||
|
||||
from skimage._shared.utils import _supported_float_type
|
||||
from skimage.morphology.grayreconstruct import reconstruction
|
||||
|
||||
|
||||
def test_zeros():
|
||||
"""Test reconstruction with image and mask of zeros"""
|
||||
assert_array_almost_equal(
|
||||
reconstruction(np.zeros((5, 7)), np.zeros((5, 7))), 0)
|
||||
|
||||
|
||||
def test_image_equals_mask():
|
||||
"""Test reconstruction where the image and mask are the same"""
|
||||
assert_array_almost_equal(
|
||||
reconstruction(np.ones((7, 5)), np.ones((7, 5))), 1)
|
||||
|
||||
|
||||
def test_image_less_than_mask():
|
||||
"""Test reconstruction where the image is uniform and less than mask"""
|
||||
image = np.ones((5, 5))
|
||||
mask = np.ones((5, 5)) * 2
|
||||
assert_array_almost_equal(reconstruction(image, mask), 1)
|
||||
|
||||
|
||||
def test_one_image_peak():
|
||||
"""Test reconstruction with one peak pixel"""
|
||||
image = np.ones((5, 5))
|
||||
image[2, 2] = 2
|
||||
mask = np.ones((5, 5)) * 3
|
||||
assert_array_almost_equal(reconstruction(image, mask), 2)
|
||||
|
||||
|
||||
# minsize chosen to test sizes covering use of 8, 16 and 32-bit integers
|
||||
# internally
|
||||
@pytest.mark.parametrize('minsize', [None, 200, 20000, 40000, 80000])
|
||||
@pytest.mark.parametrize('dtype', [np.uint8, np.float32])
|
||||
def test_two_image_peaks(minsize, dtype):
|
||||
"""Test reconstruction with two peak pixels isolated by the mask"""
|
||||
image = np.array([[1, 1, 1, 1, 1, 1, 1, 1, 1],
|
||||
[1, 2, 1, 1, 1, 1, 1, 1, 1],
|
||||
[1, 1, 1, 1, 1, 1, 1, 1, 1],
|
||||
[1, 1, 1, 1, 1, 1, 1, 1, 1],
|
||||
[1, 1, 1, 1, 1, 1, 3, 1, 1],
|
||||
[1, 1, 1, 1, 1, 1, 1, 1, 1]], dtype=dtype)
|
||||
|
||||
mask = np.array([[4, 4, 4, 1, 1, 1, 1, 1, 1],
|
||||
[4, 4, 4, 1, 1, 1, 1, 1, 1],
|
||||
[4, 4, 4, 1, 1, 1, 1, 1, 1],
|
||||
[1, 1, 1, 1, 1, 4, 4, 4, 1],
|
||||
[1, 1, 1, 1, 1, 4, 4, 4, 1],
|
||||
[1, 1, 1, 1, 1, 4, 4, 4, 1]], dtype=dtype)
|
||||
|
||||
expected = np.array([[2, 2, 2, 1, 1, 1, 1, 1, 1],
|
||||
[2, 2, 2, 1, 1, 1, 1, 1, 1],
|
||||
[2, 2, 2, 1, 1, 1, 1, 1, 1],
|
||||
[1, 1, 1, 1, 1, 3, 3, 3, 1],
|
||||
[1, 1, 1, 1, 1, 3, 3, 3, 1],
|
||||
[1, 1, 1, 1, 1, 3, 3, 3, 1]], dtype=dtype)
|
||||
if minsize is not None:
|
||||
# increase data size by tiling (done to test various int types)
|
||||
nrow = math.ceil(math.sqrt(minsize / image.size))
|
||||
ncol = math.ceil(minsize / (image.size * nrow))
|
||||
image = np.tile(image, (nrow, ncol))
|
||||
mask = np.tile(mask, (nrow, ncol))
|
||||
expected = np.tile(expected, (nrow, ncol))
|
||||
out = reconstruction(image, mask)
|
||||
assert out.dtype == _supported_float_type(mask.dtype)
|
||||
assert_array_almost_equal(out, expected)
|
||||
|
||||
|
||||
def test_zero_image_one_mask():
|
||||
"""Test reconstruction with an image of all zeros and a mask that's not"""
|
||||
result = reconstruction(np.zeros((10, 10)), np.ones((10, 10)))
|
||||
assert_array_almost_equal(result, 0)
|
||||
|
||||
|
||||
@pytest.mark.parametrize('dtype', [np.int8, np.uint8, np.int16, np.uint16,
|
||||
np.int32, np.uint32, np.int64, np.uint64,
|
||||
np.float16, np.float32, np.float64])
|
||||
def test_fill_hole(dtype):
|
||||
"""Test reconstruction by erosion, which should fill holes in mask."""
|
||||
seed = np.array([0, 8, 8, 8, 8, 8, 8, 8, 8, 0], dtype=dtype)
|
||||
mask = np.array([0, 3, 6, 2, 1, 1, 1, 4, 2, 0], dtype=dtype)
|
||||
result = reconstruction(seed, mask, method='erosion')
|
||||
assert result.dtype == _supported_float_type(mask.dtype)
|
||||
expected = np.array([0, 3, 6, 4, 4, 4, 4, 4, 2, 0], dtype=dtype)
|
||||
assert_array_almost_equal(result, expected)
|
||||
|
||||
|
||||
def test_invalid_seed():
|
||||
seed = np.ones((5, 5))
|
||||
mask = np.ones((5, 5))
|
||||
with pytest.raises(ValueError):
|
||||
reconstruction(seed * 2, mask,
|
||||
method='dilation')
|
||||
with pytest.raises(ValueError):
|
||||
reconstruction(seed * 0.5, mask,
|
||||
method='erosion')
|
||||
|
||||
|
||||
def test_invalid_footprint():
|
||||
seed = np.ones((5, 5))
|
||||
mask = np.ones((5, 5))
|
||||
with pytest.raises(ValueError):
|
||||
reconstruction(seed, mask,
|
||||
footprint=np.ones((4, 4)))
|
||||
with pytest.raises(ValueError):
|
||||
reconstruction(seed, mask,
|
||||
footprint=np.ones((3, 4)))
|
||||
reconstruction(seed, mask, footprint=np.ones((3, 3)))
|
||||
|
||||
|
||||
def test_invalid_method():
|
||||
seed = np.array([0, 8, 8, 8, 8, 8, 8, 8, 8, 0])
|
||||
mask = np.array([0, 3, 6, 2, 1, 1, 1, 4, 2, 0])
|
||||
with pytest.raises(ValueError):
|
||||
reconstruction(seed, mask, method='foo')
|
||||
|
||||
|
||||
def test_invalid_offset_not_none():
|
||||
"""Test reconstruction with invalid not None offset parameter"""
|
||||
image = np.array([[1, 1, 1, 1, 1, 1, 1, 1],
|
||||
[1, 2, 1, 1, 1, 1, 1, 1],
|
||||
[1, 1, 1, 1, 1, 1, 1, 1],
|
||||
[1, 1, 1, 1, 1, 1, 1, 1],
|
||||
[1, 1, 1, 1, 1, 1, 3, 1],
|
||||
[1, 1, 1, 1, 1, 1, 1, 1]])
|
||||
|
||||
mask = np.array([[4, 4, 4, 1, 1, 1, 1, 1],
|
||||
[4, 4, 4, 1, 1, 1, 1, 1],
|
||||
[4, 4, 4, 1, 1, 1, 1, 1],
|
||||
[1, 1, 1, 1, 1, 4, 4, 4],
|
||||
[1, 1, 1, 1, 1, 4, 4, 4],
|
||||
[1, 1, 1, 1, 1, 4, 4, 4]])
|
||||
with pytest.raises(ValueError):
|
||||
reconstruction(image, mask, method='dilation',
|
||||
footprint=np.ones((3, 3)), offset=np.array([3, 0]))
|
||||
|
||||
|
||||
def test_offset_not_none():
|
||||
"""Test reconstruction with valid offset parameter"""
|
||||
seed = np.array([0, 3, 6, 2, 1, 1, 1, 4, 2, 0])
|
||||
mask = np.array([0, 8, 6, 8, 8, 8, 8, 4, 4, 0])
|
||||
expected = np.array([0, 3, 6, 6, 6, 6, 6, 4, 4, 0])
|
||||
|
||||
assert_array_almost_equal(
|
||||
reconstruction(seed, mask, method='dilation',
|
||||
footprint=np.ones(3), offset=np.array([0])), expected)
|
||||
@@ -1,229 +0,0 @@
|
||||
import numpy as np
|
||||
import pytest
|
||||
from numpy.testing import assert_array_equal
|
||||
from scipy.ndimage import correlate
|
||||
|
||||
from skimage import draw
|
||||
from skimage._shared.testing import fetch
|
||||
from skimage.io import imread
|
||||
from skimage.morphology import medial_axis, skeletonize, thin
|
||||
from skimage.morphology._skeletonize import (_generate_thin_luts,
|
||||
G123_LUT, G123P_LUT)
|
||||
|
||||
|
||||
class TestSkeletonize():
|
||||
def test_skeletonize_no_foreground(self):
|
||||
im = np.zeros((5, 5))
|
||||
result = skeletonize(im)
|
||||
assert_array_equal(result, np.zeros((5, 5)))
|
||||
|
||||
def test_skeletonize_wrong_dim1(self):
|
||||
im = np.zeros(5)
|
||||
with pytest.raises(ValueError):
|
||||
skeletonize(im)
|
||||
|
||||
def test_skeletonize_wrong_dim2(self):
|
||||
im = np.zeros((5, 5, 5))
|
||||
with pytest.raises(ValueError):
|
||||
skeletonize(im, method='zhang')
|
||||
|
||||
def test_skeletonize_all_foreground(self):
|
||||
im = np.ones((3, 4))
|
||||
skeletonize(im)
|
||||
|
||||
def test_skeletonize_single_point(self):
|
||||
im = np.zeros((5, 5), np.uint8)
|
||||
im[3, 3] = 1
|
||||
result = skeletonize(im)
|
||||
assert_array_equal(result, im)
|
||||
|
||||
def test_skeletonize_already_thinned(self):
|
||||
im = np.zeros((5, 5), np.uint8)
|
||||
im[3, 1:-1] = 1
|
||||
im[2, -1] = 1
|
||||
im[4, 0] = 1
|
||||
result = skeletonize(im)
|
||||
assert_array_equal(result, im)
|
||||
|
||||
def test_skeletonize_output(self):
|
||||
im = imread(fetch("data/bw_text.png"), as_gray=True)
|
||||
|
||||
# make black the foreground
|
||||
im = (im == 0)
|
||||
result = skeletonize(im)
|
||||
|
||||
expected = np.load(fetch("data/bw_text_skeleton.npy"))
|
||||
assert_array_equal(result, expected)
|
||||
|
||||
def test_skeletonize_num_neighbors(self):
|
||||
# an empty image
|
||||
image = np.zeros((300, 300))
|
||||
|
||||
# foreground object 1
|
||||
image[10:-10, 10:100] = 1
|
||||
image[-100:-10, 10:-10] = 1
|
||||
image[10:-10, -100:-10] = 1
|
||||
|
||||
# foreground object 2
|
||||
rs, cs = draw.line(250, 150, 10, 280)
|
||||
for i in range(10):
|
||||
image[rs + i, cs] = 1
|
||||
rs, cs = draw.line(10, 150, 250, 280)
|
||||
for i in range(20):
|
||||
image[rs + i, cs] = 1
|
||||
|
||||
# foreground object 3
|
||||
ir, ic = np.indices(image.shape)
|
||||
circle1 = (ic - 135)**2 + (ir - 150)**2 < 30**2
|
||||
circle2 = (ic - 135)**2 + (ir - 150)**2 < 20**2
|
||||
image[circle1] = 1
|
||||
image[circle2] = 0
|
||||
result = skeletonize(image)
|
||||
|
||||
# there should never be a 2x2 block of foreground pixels in a skeleton
|
||||
mask = np.array([[1, 1],
|
||||
[1, 1]], np.uint8)
|
||||
blocks = correlate(result, mask, mode='constant')
|
||||
assert not np.any(blocks == 4)
|
||||
|
||||
def test_lut_fix(self):
|
||||
im = np.zeros((6, 6), np.uint8)
|
||||
im[1, 2] = 1
|
||||
im[2, 2] = 1
|
||||
im[2, 3] = 1
|
||||
im[3, 3] = 1
|
||||
im[3, 4] = 1
|
||||
im[4, 4] = 1
|
||||
im[4, 5] = 1
|
||||
result = skeletonize(im)
|
||||
expected = np.array([[0, 0, 0, 0, 0, 0],
|
||||
[0, 0, 1, 0, 0, 0],
|
||||
[0, 0, 0, 1, 0, 0],
|
||||
[0, 0, 0, 0, 1, 0],
|
||||
[0, 0, 0, 0, 0, 1],
|
||||
[0, 0, 0, 0, 0, 0]], dtype=np.uint8)
|
||||
assert np.all(result == expected)
|
||||
|
||||
|
||||
class TestThin():
|
||||
@property
|
||||
def input_image(self):
|
||||
"""image to test thinning with"""
|
||||
ii = np.array([[0, 0, 0, 0, 0, 0, 0],
|
||||
[0, 1, 1, 1, 1, 1, 0],
|
||||
[0, 1, 0, 1, 1, 1, 0],
|
||||
[0, 1, 1, 1, 1, 1, 0],
|
||||
[0, 1, 1, 1, 1, 1, 0],
|
||||
[0, 1, 1, 1, 1, 1, 0],
|
||||
[0, 0, 0, 0, 0, 0, 0]], dtype=np.uint8)
|
||||
return ii
|
||||
|
||||
def test_zeros(self):
|
||||
assert np.all(thin(np.zeros((10, 10))) == False)
|
||||
|
||||
def test_iter_1(self):
|
||||
result = thin(self.input_image, 1).astype(np.uint8)
|
||||
expected = np.array([[0, 0, 0, 0, 0, 0, 0],
|
||||
[0, 0, 1, 0, 0, 0, 0],
|
||||
[0, 1, 0, 1, 1, 0, 0],
|
||||
[0, 0, 1, 1, 1, 0, 0],
|
||||
[0, 0, 1, 1, 1, 0, 0],
|
||||
[0, 0, 0, 0, 0, 0, 0],
|
||||
[0, 0, 0, 0, 0, 0, 0]], dtype=np.uint8)
|
||||
assert_array_equal(result, expected)
|
||||
|
||||
def test_noiter(self):
|
||||
result = thin(self.input_image).astype(np.uint8)
|
||||
expected = np.array([[0, 0, 0, 0, 0, 0, 0],
|
||||
[0, 0, 1, 0, 0, 0, 0],
|
||||
[0, 1, 0, 1, 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, 0, 0, 0, 0]], dtype=np.uint8)
|
||||
assert_array_equal(result, expected)
|
||||
|
||||
def test_baddim(self):
|
||||
for ii in [np.zeros(3), np.zeros((3, 3, 3))]:
|
||||
with pytest.raises(ValueError):
|
||||
thin(ii)
|
||||
|
||||
def test_lut_generation(self):
|
||||
g123, g123p = _generate_thin_luts()
|
||||
|
||||
assert_array_equal(g123, G123_LUT)
|
||||
assert_array_equal(g123p, G123P_LUT)
|
||||
|
||||
|
||||
class TestMedialAxis():
|
||||
def test_00_00_zeros(self):
|
||||
'''Test skeletonize on an array of all zeros'''
|
||||
result = medial_axis(np.zeros((10, 10), bool))
|
||||
assert np.all(result == False)
|
||||
|
||||
def test_00_01_zeros_masked(self):
|
||||
'''Test skeletonize on an array that is completely masked'''
|
||||
result = medial_axis(np.zeros((10, 10), bool),
|
||||
np.zeros((10, 10), bool))
|
||||
assert np.all(result == False)
|
||||
|
||||
def test_vertical_line(self):
|
||||
'''Test a thick vertical line, issue #3861'''
|
||||
img = np.zeros((9, 9))
|
||||
img[:, 2] = 1
|
||||
img[:, 3] = 1
|
||||
img[:, 4] = 1
|
||||
|
||||
expected = np.full(img.shape, False)
|
||||
expected[:, 3] = True
|
||||
|
||||
result = medial_axis(img)
|
||||
assert_array_equal(result, expected)
|
||||
|
||||
def test_01_01_rectangle(self):
|
||||
'''Test skeletonize on a rectangle'''
|
||||
image = np.zeros((9, 15), bool)
|
||||
image[1:-1, 1:-1] = True
|
||||
#
|
||||
# The result should be four diagonals from the
|
||||
# corners, meeting in a horizontal line
|
||||
#
|
||||
expected = np.array([[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, 1, 0],
|
||||
[0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0],
|
||||
[0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0],
|
||||
[0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0],
|
||||
[0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0],
|
||||
[0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0],
|
||||
[0, 1, 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]],
|
||||
dtype=bool)
|
||||
result = medial_axis(image)
|
||||
assert np.all(result == expected)
|
||||
result, distance = medial_axis(image, return_distance=True)
|
||||
assert distance.max() == 4
|
||||
|
||||
def test_01_02_hole(self):
|
||||
'''Test skeletonize on a rectangle with a hole in the middle'''
|
||||
image = np.zeros((9, 15), bool)
|
||||
image[1:-1, 1:-1] = True
|
||||
image[4, 4:-4] = False
|
||||
expected = np.array([[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, 1, 0],
|
||||
[0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0],
|
||||
[0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0],
|
||||
[0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0],
|
||||
[0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0],
|
||||
[0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0],
|
||||
[0, 1, 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]],
|
||||
dtype=bool)
|
||||
result = medial_axis(image)
|
||||
assert np.all(result == expected)
|
||||
|
||||
def test_narrow_image(self):
|
||||
"""Test skeletonize on a 1-pixel thin strip"""
|
||||
image = np.zeros((1, 5), bool)
|
||||
image[:, 1:-1] = True
|
||||
result = medial_axis(image)
|
||||
assert np.all(result == image)
|
||||
@@ -1,185 +0,0 @@
|
||||
import numpy as np
|
||||
import scipy.ndimage as ndi
|
||||
|
||||
from skimage import io, draw
|
||||
from skimage.data import binary_blobs
|
||||
from skimage.util import img_as_ubyte
|
||||
from skimage.morphology import skeletonize, skeletonize_3d
|
||||
|
||||
from skimage._shared import testing
|
||||
from skimage._shared.testing import assert_equal, assert_, parametrize, fetch
|
||||
|
||||
# basic behavior tests (mostly copied over from 2D skeletonize)
|
||||
|
||||
def test_skeletonize_wrong_dim():
|
||||
im = np.zeros(5, dtype=np.uint8)
|
||||
with testing.raises(ValueError):
|
||||
skeletonize(im, method='lee')
|
||||
|
||||
im = np.zeros((5, 5, 5, 5), dtype=np.uint8)
|
||||
with testing.raises(ValueError):
|
||||
skeletonize(im, method='lee')
|
||||
|
||||
|
||||
def test_skeletonize_1D_old_api():
|
||||
# a corner case of an image of a shape(1, N)
|
||||
im = np.ones((5, 1), dtype=np.uint8)
|
||||
res = skeletonize_3d(im)
|
||||
assert_equal(res, im)
|
||||
|
||||
|
||||
def test_skeletonize_1D():
|
||||
# a corner case of an image of a shape(1, N)
|
||||
im = np.ones((5, 1), dtype=np.uint8)
|
||||
res = skeletonize(im, method='lee')
|
||||
assert_equal(res, im)
|
||||
|
||||
|
||||
def test_skeletonize_no_foreground():
|
||||
im = np.zeros((5, 5), dtype=np.uint8)
|
||||
result = skeletonize(im, method='lee')
|
||||
assert_equal(result, im)
|
||||
|
||||
|
||||
def test_skeletonize_all_foreground():
|
||||
im = np.ones((3, 4), dtype=np.uint8)
|
||||
assert_equal(skeletonize(im, method='lee'),
|
||||
np.array([[0, 0, 0, 0],
|
||||
[1, 1, 1, 1],
|
||||
[0, 0, 0, 0]], dtype=np.uint8))
|
||||
|
||||
|
||||
def test_skeletonize_single_point():
|
||||
im = np.zeros((5, 5), dtype=np.uint8)
|
||||
im[3, 3] = 1
|
||||
result = skeletonize(im, method='lee')
|
||||
assert_equal(result, im)
|
||||
|
||||
|
||||
def test_skeletonize_already_thinned():
|
||||
im = np.zeros((5, 5), dtype=np.uint8)
|
||||
im[3, 1:-1] = 1
|
||||
im[2, -1] = 1
|
||||
im[4, 0] = 1
|
||||
result = skeletonize(im, method='lee')
|
||||
assert_equal(result, im)
|
||||
|
||||
|
||||
def test_dtype_conv():
|
||||
# check that the operation does the right thing with floats etc
|
||||
# also check non-contiguous input
|
||||
img = np.random.random((16, 16))[::2, ::2]
|
||||
img[img < 0.5] = 0
|
||||
|
||||
orig = img.copy()
|
||||
res = skeletonize(img, method='lee')
|
||||
img_max = img_as_ubyte(img).max()
|
||||
|
||||
assert_equal(res.dtype, np.uint8)
|
||||
assert_equal(img, orig) # operation does not clobber the original
|
||||
assert_equal(res.max(), img_max) # the intensity range is preserved
|
||||
|
||||
|
||||
@parametrize("img", [
|
||||
np.ones((8, 8), dtype=float), np.ones((4, 8, 8), dtype=float)
|
||||
])
|
||||
def test_input_with_warning(img):
|
||||
# check that the input is not clobbered
|
||||
# for 2D and 3D images of varying dtypes
|
||||
check_input(img)
|
||||
|
||||
|
||||
@parametrize("img", [
|
||||
np.ones((8, 8), dtype=np.uint8), np.ones((4, 8, 8), dtype=np.uint8),
|
||||
np.ones((8, 8), dtype=bool), np.ones((4, 8, 8), dtype=bool)
|
||||
])
|
||||
def test_input_without_warning(img):
|
||||
# check that the input is not clobbered
|
||||
# for 2D and 3D images of varying dtypes
|
||||
check_input(img)
|
||||
|
||||
|
||||
def check_input(img):
|
||||
orig = img.copy()
|
||||
skeletonize(img, method='lee')
|
||||
assert_equal(img, orig)
|
||||
|
||||
|
||||
def test_skeletonize_num_neighbors():
|
||||
# an empty image
|
||||
image = np.zeros((300, 300))
|
||||
|
||||
# foreground object 1
|
||||
image[10:-10, 10:100] = 1
|
||||
image[-100:-10, 10:-10] = 1
|
||||
image[10:-10, -100:-10] = 1
|
||||
|
||||
# foreground object 2
|
||||
rs, cs = draw.line(250, 150, 10, 280)
|
||||
for i in range(10):
|
||||
image[rs + i, cs] = 1
|
||||
rs, cs = draw.line(10, 150, 250, 280)
|
||||
for i in range(20):
|
||||
image[rs + i, cs] = 1
|
||||
|
||||
# foreground object 3
|
||||
ir, ic = np.indices(image.shape)
|
||||
circle1 = (ic - 135)**2 + (ir - 150)**2 < 30**2
|
||||
circle2 = (ic - 135)**2 + (ir - 150)**2 < 20**2
|
||||
image[circle1] = 1
|
||||
image[circle2] = 0
|
||||
result = skeletonize(image, method='lee')
|
||||
|
||||
# there should never be a 2x2 block of foreground pixels in a skeleton
|
||||
mask = np.array([[1, 1],
|
||||
[1, 1]], np.uint8)
|
||||
blocks = ndi.correlate(result, mask, mode='constant')
|
||||
assert_(not np.any(blocks == 4))
|
||||
|
||||
|
||||
def test_two_hole_image():
|
||||
# test a simple 2D image against FIJI
|
||||
img_o = np.array([[0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0],
|
||||
[0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0],
|
||||
[0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0],
|
||||
[0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0],
|
||||
[0, 1, 1, 1, 1, 0, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0],
|
||||
[0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0],
|
||||
[0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0],
|
||||
[0, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0],
|
||||
[0, 0, 0, 1, 1, 0, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0],
|
||||
[0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0],
|
||||
[0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 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, 0, 0, 0],
|
||||
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]],
|
||||
dtype=np.uint8)
|
||||
img_f = np.array([[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
||||
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
||||
[0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0],
|
||||
[0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0],
|
||||
[0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0],
|
||||
[0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0],
|
||||
[0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0],
|
||||
[0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0],
|
||||
[0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0],
|
||||
[0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0],
|
||||
[0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0],
|
||||
[0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 1, 0, 0, 0, 0],
|
||||
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
||||
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]],
|
||||
dtype=np.uint8)
|
||||
res = skeletonize(img_o, method='lee')
|
||||
assert_equal(res, img_f)
|
||||
|
||||
|
||||
def test_3d_vs_fiji():
|
||||
# generate an image with blobs and compate its skeleton to
|
||||
# the skeleton generated by FIJI (Plugins>Skeleton->Skeletonize)
|
||||
img = binary_blobs(32, 0.05, n_dim=3, seed=1234)
|
||||
img = img[:-2, ...]
|
||||
img = img.astype(np.uint8)*255
|
||||
|
||||
img_s = skeletonize(img)
|
||||
img_f = io.imread(fetch("data/_blobs_3d_fiji_skeleton.tif"))
|
||||
assert_equal(img_s, img_f)
|
||||
@@ -1,126 +0,0 @@
|
||||
"""Tests for `_util`."""
|
||||
|
||||
|
||||
import numpy as np
|
||||
import pytest
|
||||
from numpy.testing import assert_array_equal
|
||||
|
||||
from skimage.morphology import _util
|
||||
|
||||
|
||||
@pytest.mark.parametrize("image_shape", [
|
||||
(111,), (33, 44), (22, 55, 11), (6, 5, 4, 3)
|
||||
])
|
||||
@pytest.mark.parametrize("order", ["C", "F"])
|
||||
def test_offsets_to_raveled_neighbors_highest_connectivity(image_shape, order):
|
||||
"""
|
||||
Check scenarios where footprint is always of the highest connectivity
|
||||
and all dimensions are > 2.
|
||||
"""
|
||||
footprint = np.ones((3,) * len(image_shape), dtype=bool)
|
||||
center = (1,) * len(image_shape)
|
||||
offsets = _util._offsets_to_raveled_neighbors(
|
||||
image_shape, footprint, center, order
|
||||
)
|
||||
|
||||
# Assert only neighbors are present, center was removed
|
||||
assert len(offsets) == footprint.sum() - 1
|
||||
assert 0 not in offsets
|
||||
# Assert uniqueness
|
||||
assert len(set(offsets)) == offsets.size
|
||||
# offsets form pairs of with same value but different signs
|
||||
# if footprint is symmetric around center
|
||||
assert all(-x in offsets for x in offsets)
|
||||
|
||||
# Construct image whose values are the Manhattan distance to its center
|
||||
image_center = tuple(s // 2 for s in image_shape)
|
||||
coords = [
|
||||
np.abs(np.arange(s, dtype=np.intp) - c)
|
||||
for s, c in zip(image_shape, image_center)
|
||||
]
|
||||
grid = np.meshgrid(*coords, indexing="ij")
|
||||
image = np.sum(grid, axis=0)
|
||||
|
||||
image_raveled = image.ravel(order)
|
||||
image_center_raveled = np.ravel_multi_index(
|
||||
image_center, image_shape, order=order
|
||||
)
|
||||
|
||||
# Sample raveled image around its center
|
||||
samples = []
|
||||
for offset in offsets:
|
||||
index = image_center_raveled + offset
|
||||
samples.append(image_raveled[index])
|
||||
|
||||
# Assert that center with value 0 wasn't selected
|
||||
assert np.min(samples) == 1
|
||||
# Assert that only neighbors where selected
|
||||
# (highest value == connectivity)
|
||||
assert np.max(samples) == len(image_shape)
|
||||
# Assert that nearest neighbors are selected first
|
||||
assert list(sorted(samples)) == samples
|
||||
|
||||
|
||||
@pytest.mark.parametrize("image_shape", [
|
||||
(2,), (2, 2), (2, 1, 2), (2, 2, 1, 2), (0, 2, 1, 2)
|
||||
])
|
||||
@pytest.mark.parametrize("order", ["C", "F"])
|
||||
def test_offsets_to_raveled_neighbors_footprint_smaller_image(image_shape,
|
||||
order):
|
||||
"""
|
||||
Test if a dimension indicated by `image_shape` is smaller than in
|
||||
`footprint`.
|
||||
"""
|
||||
footprint = np.ones((3,) * len(image_shape), dtype=bool)
|
||||
center = (1,) * len(image_shape)
|
||||
offsets = _util._offsets_to_raveled_neighbors(
|
||||
image_shape, footprint, center, order
|
||||
)
|
||||
|
||||
# Assert only neighbors are present, center and duplicates (possible
|
||||
# for this scenario) where removed
|
||||
assert len(offsets) <= footprint.sum() - 1
|
||||
assert 0 not in offsets
|
||||
# Assert uniqueness
|
||||
assert len(set(offsets)) == offsets.size
|
||||
# offsets form pairs of with same value but different signs
|
||||
# if footprint is symmetric around center
|
||||
assert all(-x in offsets for x in offsets)
|
||||
|
||||
|
||||
def test_offsets_to_raveled_neighbors_explicit_0():
|
||||
"""Check reviewed example."""
|
||||
image_shape = (100, 200, 3)
|
||||
footprint = np.ones((3, 3, 3), dtype=bool)
|
||||
center = (1, 1, 1)
|
||||
offsets = _util._offsets_to_raveled_neighbors(
|
||||
image_shape, footprint, center
|
||||
)
|
||||
|
||||
desired = np.array([
|
||||
3, -600, 1, -1, 600, -3, 4, 2, 603, -2, -4,
|
||||
-597, 601, -599, -601, -603, 599, 597, 602, -604, 596, -596,
|
||||
-598, -602, 598, 604
|
||||
])
|
||||
assert_array_equal(offsets, desired)
|
||||
|
||||
|
||||
def test_offsets_to_raveled_neighbors_explicit_1():
|
||||
"""Check reviewed example where footprint is larger in last dimension."""
|
||||
image_shape = (10, 9, 8, 3)
|
||||
footprint = np.ones((3, 3, 3, 4), dtype=bool)
|
||||
center = (1, 1, 1, 1)
|
||||
offsets = _util._offsets_to_raveled_neighbors(
|
||||
image_shape, footprint, center
|
||||
)
|
||||
|
||||
desired = np.array([
|
||||
216, 24, -24, 3, -216, 1, -1, -3, 215, -27, -25, -23, -21, -2,
|
||||
-192, 192, 2, 4, 21, 23, 25, 27, -4, 217, 213, -219, 219, -217,
|
||||
-213, -215, 240, -240, 193, 239, -237, 241, -239, 218, -220, 22,
|
||||
-241, 243, 189, 26, -243, 191, 20, -218, 195, -193, 220, -191,
|
||||
-212, -189, 214, 28, -195, -214, -28, 212, -22, 237, -20, -26, 236,
|
||||
196, 190, 242, 238, 194, 188, -244, -188, -196, -194, -190, -238,
|
||||
-236, 244, -242, 5, 221, -211, -19, 29, -235, -187, 197, 245
|
||||
])
|
||||
assert_array_equal(offsets, desired)
|
||||
Reference in New Issue
Block a user