CofeehousePy/deps/scikit-image/skimage/segmentation/tests/test_expand_labels.py

186 lines
6.3 KiB
Python

from scipy import ndimage as ndi
from skimage import data
import numpy as np
from skimage import measure
from skimage.segmentation._expand_labels import expand_labels
from skimage._shared import testing
from skimage._shared.testing import assert_array_equal
SAMPLE1D = np.array([0, 0, 4, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0])
SAMPLE1D_EXPANDED_3 = np.array([4, 4, 4, 4, 4, 4, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0])
# Some pixels are important edge cases with undefined behaviour:
# these are the pixels that are at the same distance from
# multiple labels. Ideally the label would be chosen at random
# to avoid bias, but as we are relying on the index map returned
# by the scipy.ndimage distance transform, what actually happens
# is determined by the upstream implementation of the distance
# tansform, thus we don't give any guarantees for the edge case pixels.
#
# Regardless, it seems prudent to have a test including an edge case
# so we can detect whether future upstream changes in scipy.ndimage
# modify the behaviour.
EDGECASE1D = np.array([0, 0, 4, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0])
EDGECASE1D_EXPANDED_3 = np.array([4, 4, 4, 4, 4, 4, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0])
SAMPLE2D = 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, 1, 1, 0, 0, 0, 0, 0, 0, 0],
[0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0],
[0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 1, 0, 0, 0, 0, 0, 0, 2, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 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]]
)
SAMPLE2D_EXPANDED_3 = np.array(
[[1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0],
[1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0],
[1, 1, 1, 1, 1, 1, 1, 0, 0, 2, 0],
[1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2],
[1, 1, 1, 1, 1, 1, 0, 2, 2, 2, 2],
[1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2],
[1, 1, 1, 1, 1, 0, 2, 2, 2, 2, 2],
[1, 1, 1, 1, 1, 0, 0, 2, 2, 2, 2],
[0, 0, 1, 0, 0, 0, 0, 2, 2, 2, 2],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0]]
)
# non-integer expansion
SAMPLE2D_EXPANDED_1_5 = np.array(
[[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0],
[1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0],
[1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0],
[1, 1, 1, 1, 1, 0, 0, 0, 2, 2, 2],
[1, 1, 1, 1, 0, 0, 0, 0, 2, 2, 2],
[0, 1, 1, 1, 0, 0, 0, 0, 2, 2, 2],
[0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]])
EDGECASE2D = np.array(
[[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 2, 2, 0, 0, 0, 0],
[0, 0, 1, 1, 0, 2, 2, 0, 0, 0, 0],
[0, 1, 1, 1, 0, 2, 0, 0, 0, 0, 0],
[0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0]]
)
EDGECASE2D_EXPANDED_4 = np.array(
[[1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 0],
[1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2],
[1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2],
[1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 0],
[1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 0]])
SAMPLE3D = np.array(
[[[0, 0, 0, 0],
[0, 3, 0, 0],
[0, 0, 0, 0],
[0, 0, 0, 0]],
[[0, 0, 0, 0],
[0, 3, 3, 0],
[0, 0, 0, 0],
[0, 0, 0, 0]],
[[0, 0, 0, 0],
[0, 3, 0, 0],
[0, 0, 0, 0],
[0, 0, 5, 0]],
[[0, 0, 0, 0],
[0, 0, 0, 0],
[0, 0, 0, 0],
[0, 0, 5, 0]]])
SAMPLE3D_EXPANDED_2 =np.array(
[[[3, 3, 3, 3],
[3, 3, 3, 3],
[3, 3, 3, 3],
[0, 3, 5, 0]],
[[3, 3, 3, 3],
[3, 3, 3, 3],
[3, 3, 3, 3],
[0, 5, 5, 5]],
[[3, 3, 3, 3],
[3, 3, 3, 3],
[3, 3, 5, 5],
[5, 5, 5, 5]],
[[3, 3, 3, 0],
[3, 3, 3, 0],
[3, 3, 5, 5],
[5, 5, 5, 5]]])
SAMPLE_EDGECASE_BEHAVIOUR = np.array([[0, 1, 0, 0], [2, 0, 0, 0], [0, 3, 0, 0]])
@testing.parametrize(
"input_array, expected_output, expand_distance",
[
(SAMPLE1D, SAMPLE1D_EXPANDED_3, 3),
(SAMPLE2D, SAMPLE2D_EXPANDED_3, 3),
(SAMPLE2D, SAMPLE2D_EXPANDED_1_5, 1.5),
(EDGECASE1D, EDGECASE1D_EXPANDED_3, 3),
(EDGECASE2D, EDGECASE2D_EXPANDED_4, 4),
(SAMPLE3D, SAMPLE3D_EXPANDED_2, 2)
]
)
def test_expand_labels(input_array, expected_output, expand_distance):
expanded = expand_labels(input_array, expand_distance)
assert_array_equal(expanded, expected_output)
@testing.parametrize('ndim', [2, 3])
@testing.parametrize('distance', range(6))
def test_binary_blobs(ndim, distance):
"""Check some invariants with label expansion.
- New labels array should exactly contain the original labels array.
- Distance to old labels array within new labels should never exceed input
distance.
- Distance beyond the expanded labels should always exceed the input
distance.
"""
img = data.binary_blobs(length=64, blob_size_fraction=0.05, n_dim=ndim)
labels = measure.label(img)
expanded = expand_labels(labels, distance=distance)
original_mask = labels != 0
assert_array_equal(labels[original_mask], expanded[original_mask])
expanded_only_mask = (expanded - labels).astype(bool)
distance_map = ndi.distance_transform_edt(~original_mask)
expanded_distances = distance_map[expanded_only_mask]
if expanded_distances.size > 0:
assert np.all(expanded_distances <= distance)
beyond_expanded_distances = distance_map[~expanded.astype(bool)]
if beyond_expanded_distances.size > 0:
assert np.all(beyond_expanded_distances > distance)
def test_edge_case_behaviour():
""" Check edge case behavior to detect upstream changes
For edge cases where a pixel has the same distance to several regions,
lexicographical order seems to determine which region gets to expand
into this pixel given the current upstream behaviour in
scipy.ndimage.distance_map_edt.
As a result, we expect different results when transposing the array.
If this test fails, something has changed upstream.
"""
expanded = expand_labels(SAMPLE_EDGECASE_BEHAVIOUR, 1)
expanded_transpose = expand_labels(SAMPLE_EDGECASE_BEHAVIOUR.T, 1)
assert not np.all(expanded == expanded_transpose.T)