314 lines
10 KiB
Python
314 lines
10 KiB
Python
|
import numpy as np
|
||
|
|
||
|
from skimage.measure import label
|
||
|
import skimage.measure._ccomp as ccomp
|
||
|
|
||
|
from skimage._shared import testing
|
||
|
from skimage._shared.testing import assert_array_equal
|
||
|
from skimage._shared._warnings import expected_warnings
|
||
|
|
||
|
|
||
|
BG = 0 # background value
|
||
|
|
||
|
|
||
|
class TestConnectedComponents:
|
||
|
def setup(self):
|
||
|
self.x = np.array([
|
||
|
[0, 0, 3, 2, 1, 9],
|
||
|
[0, 1, 1, 9, 2, 9],
|
||
|
[0, 0, 1, 9, 9, 9],
|
||
|
[3, 1, 1, 5, 3, 0]])
|
||
|
|
||
|
self.labels = np.array([
|
||
|
[0, 0, 1, 2, 3, 4],
|
||
|
[0, 5, 5, 4, 2, 4],
|
||
|
[0, 0, 5, 4, 4, 4],
|
||
|
[6, 5, 5, 7, 8, 0]])
|
||
|
|
||
|
# No background - there is no label 0, instead, labelling starts with 1
|
||
|
# and all labels are incremented by 1.
|
||
|
self.labels_nobg = self.labels + 1
|
||
|
# The 0 at lower right corner is isolated, so it should get a new label
|
||
|
self.labels_nobg[-1, -1] = 10
|
||
|
|
||
|
# We say that background value is 9 (and bg label is 0)
|
||
|
self.labels_bg_9 = self.labels_nobg.copy()
|
||
|
self.labels_bg_9[self.x == 9] = 0
|
||
|
# Then, where there was the label 5, we now expect 4 etc.
|
||
|
# (we assume that the label of value 9 would normally be 5)
|
||
|
self.labels_bg_9[self.labels_bg_9 > 5] -= 1
|
||
|
|
||
|
def test_basic(self):
|
||
|
assert_array_equal(label(self.x), self.labels)
|
||
|
|
||
|
# Make sure data wasn't modified
|
||
|
assert self.x[0, 2] == 3
|
||
|
|
||
|
# Check that everything works if there is no background
|
||
|
assert_array_equal(label(self.x, background=99), self.labels_nobg)
|
||
|
# Check that everything works if background value != 0
|
||
|
assert_array_equal(label(self.x, background=9), self.labels_bg_9)
|
||
|
|
||
|
def test_random(self):
|
||
|
x = (np.random.rand(20, 30) * 5).astype(np.int)
|
||
|
labels = label(x)
|
||
|
|
||
|
n = labels.max()
|
||
|
for i in range(n):
|
||
|
values = x[labels == i]
|
||
|
assert np.all(values == values[0])
|
||
|
|
||
|
def test_diag(self):
|
||
|
x = np.array([[0, 0, 1],
|
||
|
[0, 1, 0],
|
||
|
[1, 0, 0]])
|
||
|
assert_array_equal(label(x), x)
|
||
|
|
||
|
def test_4_vs_8(self):
|
||
|
x = np.array([[0, 1],
|
||
|
[1, 0]], dtype=int)
|
||
|
with expected_warnings(["use 'connectivity'"]):
|
||
|
assert_array_equal(label(x, 4),
|
||
|
[[0, 1],
|
||
|
[2, 0]])
|
||
|
assert_array_equal(label(x, 8),
|
||
|
[[0, 1],
|
||
|
[1, 0]])
|
||
|
|
||
|
assert_array_equal(label(x, connectivity=1),
|
||
|
[[0, 1],
|
||
|
[2, 0]])
|
||
|
assert_array_equal(label(x, connectivity=2),
|
||
|
[[0, 1],
|
||
|
[1, 0]])
|
||
|
|
||
|
def test_background(self):
|
||
|
x = np.array([[1, 0, 0],
|
||
|
[1, 1, 5],
|
||
|
[0, 0, 0]])
|
||
|
|
||
|
assert_array_equal(label(x), [[1, 0, 0],
|
||
|
[1, 1, 2],
|
||
|
[0, 0, 0]])
|
||
|
|
||
|
assert_array_equal(label(x, background=0),
|
||
|
[[1, 0, 0],
|
||
|
[1, 1, 2],
|
||
|
[0, 0, 0]])
|
||
|
|
||
|
def test_background_two_regions(self):
|
||
|
x = np.array([[0, 0, 6],
|
||
|
[0, 0, 6],
|
||
|
[5, 5, 5]])
|
||
|
|
||
|
res = label(x, background=0)
|
||
|
assert_array_equal(res,
|
||
|
[[0, 0, 1],
|
||
|
[0, 0, 1],
|
||
|
[2, 2, 2]])
|
||
|
|
||
|
def test_background_one_region_center(self):
|
||
|
x = np.array([[0, 0, 0],
|
||
|
[0, 1, 0],
|
||
|
[0, 0, 0]])
|
||
|
|
||
|
with expected_warnings(["use 'connectivity'"]):
|
||
|
assert_array_equal(label(x, neighbors=4, background=0),
|
||
|
[[0, 0, 0],
|
||
|
[0, 1, 0],
|
||
|
[0, 0, 0]])
|
||
|
assert_array_equal(label(x, connectivity=1, background=0),
|
||
|
[[0, 0, 0],
|
||
|
[0, 1, 0],
|
||
|
[0, 0, 0]])
|
||
|
|
||
|
|
||
|
def test_return_num(self):
|
||
|
x = np.array([[1, 0, 6],
|
||
|
[0, 0, 6],
|
||
|
[5, 5, 5]])
|
||
|
|
||
|
assert_array_equal(label(x, return_num=True)[1], 3)
|
||
|
|
||
|
assert_array_equal(label(x, background=-1, return_num=True)[1], 4)
|
||
|
|
||
|
|
||
|
class TestConnectedComponents3d:
|
||
|
def setup(self):
|
||
|
self.x = np.zeros((3, 4, 5), int)
|
||
|
self.x[0] = np.array([[0, 3, 2, 1, 9],
|
||
|
[0, 1, 9, 2, 9],
|
||
|
[0, 1, 9, 9, 9],
|
||
|
[3, 1, 5, 3, 0]])
|
||
|
|
||
|
self.x[1] = np.array([[3, 3, 2, 1, 9],
|
||
|
[0, 3, 9, 2, 1],
|
||
|
[0, 3, 3, 1, 1],
|
||
|
[3, 1, 3, 3, 0]])
|
||
|
|
||
|
self.x[2] = np.array([[3, 3, 8, 8, 0],
|
||
|
[2, 3, 9, 8, 8],
|
||
|
[2, 3, 0, 8, 0],
|
||
|
[2, 1, 0, 0, 0]])
|
||
|
|
||
|
self.labels = np.zeros((3, 4, 5), int)
|
||
|
|
||
|
self.labels[0] = np.array([[0, 1, 2, 3, 4],
|
||
|
[0, 5, 4, 2, 4],
|
||
|
[0, 5, 4, 4, 4],
|
||
|
[1, 5, 6, 1, 0]])
|
||
|
|
||
|
self.labels[1] = np.array([[1, 1, 2, 3, 4],
|
||
|
[0, 1, 4, 2, 3],
|
||
|
[0, 1, 1, 3, 3],
|
||
|
[1, 5, 1, 1, 0]])
|
||
|
|
||
|
self.labels[2] = np.array([[1, 1, 7, 7, 0],
|
||
|
[8, 1, 4, 7, 7],
|
||
|
[8, 1, 0, 7, 0],
|
||
|
[8, 5, 0, 0, 0]])
|
||
|
|
||
|
def test_basic(self):
|
||
|
labels = label(self.x)
|
||
|
assert_array_equal(labels, self.labels)
|
||
|
|
||
|
assert self.x[0, 0, 2] == 2, \
|
||
|
"Data was modified!"
|
||
|
|
||
|
def test_random(self):
|
||
|
x = (np.random.rand(20, 30) * 5).astype(np.int)
|
||
|
labels = label(x)
|
||
|
|
||
|
n = labels.max()
|
||
|
for i in range(n):
|
||
|
values = x[labels == i]
|
||
|
assert np.all(values == values[0])
|
||
|
|
||
|
def test_diag(self):
|
||
|
x = np.zeros((3, 3, 3), int)
|
||
|
x[0, 2, 2] = 1
|
||
|
x[1, 1, 1] = 1
|
||
|
x[2, 0, 0] = 1
|
||
|
assert_array_equal(label(x), x)
|
||
|
|
||
|
def test_4_vs_8(self):
|
||
|
x = np.zeros((2, 2, 2), int)
|
||
|
x[0, 1, 1] = 1
|
||
|
x[1, 0, 0] = 1
|
||
|
label4 = x.copy()
|
||
|
label4[1, 0, 0] = 2
|
||
|
with expected_warnings(["use 'connectivity'"]):
|
||
|
assert_array_equal(label(x, 4), label4)
|
||
|
assert_array_equal(label(x, 8), x)
|
||
|
|
||
|
def test_connectivity_1_vs_2(self):
|
||
|
x = np.zeros((2, 2, 2), int)
|
||
|
x[0, 1, 1] = 1
|
||
|
x[1, 0, 0] = 1
|
||
|
label1 = x.copy()
|
||
|
label1[1, 0, 0] = 2
|
||
|
assert_array_equal(label(x, connectivity=1), label1)
|
||
|
assert_array_equal(label(x, connectivity=3), x)
|
||
|
|
||
|
def test_background(self):
|
||
|
x = np.zeros((2, 3, 3), int)
|
||
|
x[0] = np.array([[1, 0, 0],
|
||
|
[1, 0, 0],
|
||
|
[0, 0, 0]])
|
||
|
x[1] = np.array([[0, 0, 0],
|
||
|
[0, 1, 5],
|
||
|
[0, 0, 0]])
|
||
|
|
||
|
lnb = x.copy()
|
||
|
lnb[0] = np.array([[1, 2, 2],
|
||
|
[1, 2, 2],
|
||
|
[2, 2, 2]])
|
||
|
lnb[1] = np.array([[2, 2, 2],
|
||
|
[2, 1, 3],
|
||
|
[2, 2, 2]])
|
||
|
lb = x.copy()
|
||
|
lb[0] = np.array([[1, BG, BG],
|
||
|
[1, BG, BG],
|
||
|
[BG, BG, BG]])
|
||
|
lb[1] = np.array([[BG, BG, BG],
|
||
|
[BG, 1, 2],
|
||
|
[BG, BG, BG]])
|
||
|
|
||
|
assert_array_equal(label(x), lb)
|
||
|
assert_array_equal(label(x, background=-1), lnb)
|
||
|
|
||
|
def test_background_two_regions(self):
|
||
|
x = np.zeros((2, 3, 3), int)
|
||
|
x[0] = np.array([[0, 0, 6],
|
||
|
[0, 0, 6],
|
||
|
[5, 5, 5]])
|
||
|
x[1] = np.array([[6, 6, 0],
|
||
|
[5, 0, 0],
|
||
|
[0, 0, 0]])
|
||
|
lb = x.copy()
|
||
|
lb[0] = np.array([[BG, BG, 1],
|
||
|
[BG, BG, 1],
|
||
|
[2, 2, 2]])
|
||
|
lb[1] = np.array([[1, 1, BG],
|
||
|
[2, BG, BG],
|
||
|
[BG, BG, BG]])
|
||
|
|
||
|
res = label(x, background=0)
|
||
|
assert_array_equal(res, lb)
|
||
|
|
||
|
def test_background_one_region_center(self):
|
||
|
x = np.zeros((3, 3, 3), int)
|
||
|
x[1, 1, 1] = 1
|
||
|
|
||
|
lb = np.ones_like(x) * BG
|
||
|
lb[1, 1, 1] = 1
|
||
|
|
||
|
with expected_warnings(["use 'connectivity'"]):
|
||
|
assert_array_equal(label(x, neighbors=4, background=0), lb)
|
||
|
|
||
|
assert_array_equal(label(x, connectivity=1, background=0), lb)
|
||
|
|
||
|
def test_return_num(self):
|
||
|
x = np.array([[1, 0, 6],
|
||
|
[0, 0, 6],
|
||
|
[5, 5, 5]])
|
||
|
|
||
|
assert_array_equal(label(x, return_num=True)[1], 3)
|
||
|
assert_array_equal(label(x, background=-1, return_num=True)[1], 4)
|
||
|
|
||
|
def test_1D(self):
|
||
|
x = np.array((0, 1, 2, 2, 1, 1, 0, 0))
|
||
|
xlen = len(x)
|
||
|
y = np.array((0, 1, 2, 2, 3, 3, 0, 0))
|
||
|
reshapes = ((xlen,),
|
||
|
(1, xlen), (xlen, 1),
|
||
|
(1, xlen, 1), (xlen, 1, 1), (1, 1, xlen))
|
||
|
for reshape in reshapes:
|
||
|
x2 = x.reshape(reshape)
|
||
|
labelled = label(x2)
|
||
|
assert_array_equal(y, labelled.flatten())
|
||
|
|
||
|
def test_nd(self):
|
||
|
x = np.ones((1, 2, 3, 4))
|
||
|
with testing.raises(NotImplementedError):
|
||
|
label(x)
|
||
|
|
||
|
|
||
|
class TestSupport:
|
||
|
def test_reshape(self):
|
||
|
shapes_in = ((3, 1, 2), (1, 4, 5), (3, 1, 1), (2, 1), (1,))
|
||
|
for shape in shapes_in:
|
||
|
shape = np.array(shape)
|
||
|
numones = sum(shape == 1)
|
||
|
inp = np.random.random(shape)
|
||
|
|
||
|
fixed, swaps = ccomp.reshape_array(inp)
|
||
|
shape2 = fixed.shape
|
||
|
# now check that all ones are at the beginning
|
||
|
for i in range(numones):
|
||
|
assert shape2[i] == 1
|
||
|
|
||
|
back = ccomp.undo_reshape_array(fixed, swaps)
|
||
|
# check that the undo works as expected
|
||
|
assert_array_equal(inp, back)
|