mirror of
https://github.com/wassname/scikit-image.git
synced 2026-07-03 10:53:30 +08:00
Merge pull request #1956 from emmanuelle/label_zero
New handling of background pixels for measure.label
This commit is contained in:
@@ -37,7 +37,3 @@ Version 0.13
|
||||
_shared/interpolation.pyx, transform/_geometric.py, and transform/_warps.py
|
||||
|
||||
|
||||
Version 0.12
|
||||
------------
|
||||
* Change `label` to mark background as 0, not -1, which is consistent with
|
||||
SciPy's labelling.
|
||||
|
||||
@@ -37,8 +37,6 @@ clear_border(cleared)
|
||||
|
||||
# label image regions
|
||||
label_image = label(cleared)
|
||||
borders = np.logical_xor(bw, cleared)
|
||||
label_image[borders] = -1
|
||||
image_label_overlay = label2rgb(label_image, image=image)
|
||||
|
||||
fig, ax = plt.subplots(ncols=1, nrows=1, figsize=(6, 6))
|
||||
|
||||
@@ -29,6 +29,9 @@ include:
|
||||
- Synthetic 2-D and 3-D binary data with rounded blobs (#1485)
|
||||
- Plugin for ``imageio`` library (#1575)
|
||||
- Inpainting algorithm (#1804)
|
||||
- New handling of background pixels for ``measure.label``: 0-valued
|
||||
pixels are considered as background by default, and the label of
|
||||
background pixels is 0.
|
||||
- Partial support of 3-D images for ``skimage.measure.regionprops``
|
||||
(#1505)
|
||||
- Multi-block local binary patterns (MB-LBP) for texture classification (#1536)
|
||||
|
||||
+24
-22
@@ -47,17 +47,14 @@ ctypedef struct bginfo:
|
||||
|
||||
cdef void get_bginfo(background_val, bginfo *ret) except *:
|
||||
if background_val is None:
|
||||
warn(DeprecationWarning(
|
||||
'The default value for `background` will change to 0 in v0.12'
|
||||
))
|
||||
ret.background_val = -1
|
||||
ret.background_val = 0
|
||||
else:
|
||||
ret.background_val = background_val
|
||||
|
||||
# The node -999 doesn't exist, it will get subsituted by a meaningful value
|
||||
# upon the first background pixel occurence
|
||||
ret.background_node = -999
|
||||
ret.background_label = -1
|
||||
ret.background_label = 0
|
||||
|
||||
|
||||
# A pixel has neighbors that have already been scanned.
|
||||
@@ -361,7 +358,7 @@ def undo_reshape_array(arr, swaps):
|
||||
|
||||
|
||||
# Connected components search as described in Fiorio et al.
|
||||
def label(input, neighbors=None, background=None, return_num=False,
|
||||
def label(input, neighbors=None, background=0, return_num=False,
|
||||
connectivity=None):
|
||||
r"""Label connected regions of an integer array.
|
||||
|
||||
@@ -389,14 +386,15 @@ def label(input, neighbors=None, background=None, return_num=False,
|
||||
**Deprecated, use ``connectivity`` instead.**
|
||||
background : int, optional
|
||||
Consider all pixels with this value as background pixels, and label
|
||||
them as -1. (Note: background pixels will be labeled as 0 starting with
|
||||
version 0.12).
|
||||
them as 0. By default, 0-valued pixels are considered as background
|
||||
pixels.
|
||||
return_num : bool, optional
|
||||
Whether to return the number of assigned labels.
|
||||
connectivity : int, optional
|
||||
Maximum number of orthogonal hops to consider a pixel/voxel
|
||||
as a neighbor.
|
||||
Accepted values are ranging from 1 to input.ndim.
|
||||
Accepted values are ranging from 1 to input.ndim. If ``None``, a full
|
||||
connectivity of ``input.ndim`` is used.
|
||||
|
||||
Returns
|
||||
-------
|
||||
@@ -417,24 +415,28 @@ def label(input, neighbors=None, background=None, return_num=False,
|
||||
[0 0 1]]
|
||||
>>> from skimage.measure import label
|
||||
>>> print(label(x, connectivity=1))
|
||||
[[0 1 1]
|
||||
[2 3 1]
|
||||
[2 2 4]]
|
||||
[[1 0 0]
|
||||
[0 2 0]
|
||||
[0 0 3]]
|
||||
|
||||
>>> print(label(x, connectivity=2))
|
||||
[[0 1 1]
|
||||
[1 0 1]
|
||||
[1 1 0]]
|
||||
[[1 0 0]
|
||||
[0 1 0]
|
||||
[0 0 1]]
|
||||
|
||||
>>> print(label(x, background=-1))
|
||||
[[1 2 2]
|
||||
[2 1 2]
|
||||
[2 2 1]]
|
||||
|
||||
>>> x = np.array([[1, 0, 0],
|
||||
... [1, 1, 5],
|
||||
... [0, 0, 0]])
|
||||
|
||||
>>> print(label(x, background=0))
|
||||
[[ 0 -1 -1]
|
||||
[ 0 0 1]
|
||||
[-1 -1 -1]]
|
||||
|
||||
>>> print(label(x))
|
||||
[[1 0 0]
|
||||
[1 1 2]
|
||||
[0 0 0]]
|
||||
"""
|
||||
# We have to ensure that the shape of the input can be handled by the
|
||||
# algorithm the input if it is the case
|
||||
@@ -515,7 +517,7 @@ cdef DTYPE_t resolve_labels(DTYPE_t *data_p, DTYPE_t *forest_p,
|
||||
our knowledge of prov. labels relationship.
|
||||
We also track how many distinct final labels we have.
|
||||
"""
|
||||
cdef DTYPE_t counter = bg.background_label + 1, i
|
||||
cdef DTYPE_t counter = 1, i
|
||||
|
||||
for i in range(shapeinfo.numels):
|
||||
if i == bg.background_node:
|
||||
@@ -527,7 +529,7 @@ cdef DTYPE_t resolve_labels(DTYPE_t *data_p, DTYPE_t *forest_p,
|
||||
counter += 1
|
||||
else:
|
||||
data_p[i] = data_p[forest_p[i]]
|
||||
return counter
|
||||
return counter - 1
|
||||
|
||||
|
||||
cdef void scanBG(DTYPE_t *data_p, DTYPE_t *forest_p, shape_info *shapeinfo,
|
||||
|
||||
@@ -117,7 +117,7 @@ def convex_hull_object(image, neighbors=8):
|
||||
convex_obj = np.zeros(image.shape, dtype=bool)
|
||||
convex_img = np.zeros(image.shape, dtype=bool)
|
||||
|
||||
for i in range(0, labeled_im.max() + 1):
|
||||
for i in range(1, labeled_im.max() + 1):
|
||||
convex_obj = convex_hull_image(labeled_im == i)
|
||||
convex_img = np.logical_or(convex_img, convex_obj)
|
||||
|
||||
|
||||
@@ -5,10 +5,8 @@ from skimage.measure import label
|
||||
import skimage.measure._ccomp as ccomp
|
||||
from skimage._shared._warnings import expected_warnings
|
||||
|
||||
|
||||
# The background label value
|
||||
# is supposed to be changed to 0 soon
|
||||
BG = -1
|
||||
# Background value
|
||||
BG = 0
|
||||
|
||||
|
||||
class TestConnectedComponents:
|
||||
@@ -21,20 +19,17 @@ class TestConnectedComponents:
|
||||
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, 9]])
|
||||
[6, 5, 5, 7, 8, 0]])
|
||||
|
||||
def test_basic(self):
|
||||
with expected_warnings(['`background`']):
|
||||
assert_array_equal(label(self.x), self.labels)
|
||||
assert_array_equal(label(self.x), self.labels)
|
||||
|
||||
# Make sure data wasn't modified
|
||||
assert self.x[0, 2] == 3
|
||||
|
||||
def test_random(self):
|
||||
x = (np.random.rand(20, 30) * 5).astype(np.int)
|
||||
|
||||
with expected_warnings(['`background`']):
|
||||
labels = label(x)
|
||||
labels = label(x)
|
||||
|
||||
n = labels.max()
|
||||
for i in range(n):
|
||||
@@ -45,34 +40,31 @@ class TestConnectedComponents:
|
||||
x = np.array([[0, 0, 1],
|
||||
[0, 1, 0],
|
||||
[1, 0, 0]])
|
||||
with expected_warnings(['`background`']):
|
||||
assert_array_equal(label(x), x)
|
||||
assert_array_equal(label(x), x)
|
||||
|
||||
def test_4_vs_8(self):
|
||||
x = np.array([[0, 1],
|
||||
[1, 0]], dtype=int)
|
||||
with expected_warnings(['`background`']):
|
||||
assert_array_equal(label(x, 4),
|
||||
[[0, 1],
|
||||
[2, 3]])
|
||||
assert_array_equal(label(x, 8),
|
||||
[[0, 1],
|
||||
[1, 0]])
|
||||
assert_array_equal(label(x, 4),
|
||||
[[0, 1],
|
||||
[2, 0]])
|
||||
assert_array_equal(label(x, 8),
|
||||
[[0, 1],
|
||||
[1, 0]])
|
||||
|
||||
def test_background(self):
|
||||
x = np.array([[1, 0, 0],
|
||||
[1, 1, 5],
|
||||
[0, 0, 0]])
|
||||
|
||||
with expected_warnings(['`background`']):
|
||||
assert_array_equal(label(x), [[0, 1, 1],
|
||||
[0, 0, 2],
|
||||
[3, 3, 3]])
|
||||
assert_array_equal(label(x), [[1, 0, 0],
|
||||
[1, 1, 2],
|
||||
[0, 0, 0]])
|
||||
|
||||
assert_array_equal(label(x, background=0),
|
||||
[[0, -1, -1],
|
||||
[0, 0, 1],
|
||||
[-1, -1, -1]])
|
||||
[[1, 0, 0],
|
||||
[1, 1, 2],
|
||||
[0, 0, 0]])
|
||||
|
||||
def test_background_two_regions(self):
|
||||
x = np.array([[0, 0, 6],
|
||||
@@ -81,9 +73,9 @@ class TestConnectedComponents:
|
||||
|
||||
res = label(x, background=0)
|
||||
assert_array_equal(res,
|
||||
[[-1, -1, 0],
|
||||
[-1, -1, 0],
|
||||
[+1, 1, 1]])
|
||||
[[0, 0, 1],
|
||||
[0, 0, 1],
|
||||
[2, 2, 2]])
|
||||
|
||||
def test_background_one_region_center(self):
|
||||
x = np.array([[0, 0, 0],
|
||||
@@ -91,19 +83,18 @@ class TestConnectedComponents:
|
||||
[0, 0, 0]])
|
||||
|
||||
assert_array_equal(label(x, neighbors=4, background=0),
|
||||
[[-1, -1, -1],
|
||||
[-1, 0, -1],
|
||||
[-1, -1, -1]])
|
||||
[[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]])
|
||||
|
||||
with expected_warnings(['`background`']):
|
||||
assert_array_equal(label(x, return_num=True)[1], 4)
|
||||
assert_array_equal(label(x, return_num=True)[1], 3)
|
||||
|
||||
assert_array_equal(label(x, background=0, return_num=True)[1], 3)
|
||||
assert_array_equal(label(x, background=-1, return_num=True)[1], 4)
|
||||
|
||||
|
||||
class TestConnectedComponents3d:
|
||||
@@ -129,21 +120,20 @@ class TestConnectedComponents3d:
|
||||
self.labels[0] = np.array([[0, 1, 2, 3, 4],
|
||||
[0, 5, 4, 2, 4],
|
||||
[0, 5, 4, 4, 4],
|
||||
[1, 5, 6, 1, 7]])
|
||||
[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, 7]])
|
||||
[1, 5, 1, 1, 0]])
|
||||
|
||||
self.labels[2] = np.array([[1, 1, 8, 8, 9],
|
||||
[10, 1, 4, 8, 8],
|
||||
[10, 1, 7, 8, 7],
|
||||
[10, 5, 7, 7, 7]])
|
||||
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):
|
||||
with expected_warnings(['`background`']):
|
||||
labels = label(self.x)
|
||||
labels = label(self.x)
|
||||
assert_array_equal(labels, self.labels)
|
||||
|
||||
assert self.x[0, 0, 2] == 2, \
|
||||
@@ -151,9 +141,7 @@ class TestConnectedComponents3d:
|
||||
|
||||
def test_random(self):
|
||||
x = (np.random.rand(20, 30) * 5).astype(np.int)
|
||||
|
||||
with expected_warnings(['`background`']):
|
||||
labels = label(x)
|
||||
labels = label(x)
|
||||
|
||||
n = labels.max()
|
||||
for i in range(n):
|
||||
@@ -165,8 +153,7 @@ class TestConnectedComponents3d:
|
||||
x[0, 2, 2] = 1
|
||||
x[1, 1, 1] = 1
|
||||
x[2, 0, 0] = 1
|
||||
with expected_warnings(['`background`']):
|
||||
assert_array_equal(label(x), x)
|
||||
assert_array_equal(label(x), x)
|
||||
|
||||
def test_4_vs_8(self):
|
||||
x = np.zeros((2, 2, 2), int)
|
||||
@@ -174,9 +161,8 @@ class TestConnectedComponents3d:
|
||||
x[1, 0, 0] = 1
|
||||
label4 = x.copy()
|
||||
label4[1, 0, 0] = 2
|
||||
with expected_warnings(['`background`']):
|
||||
assert_array_equal(label(x, 4), label4)
|
||||
assert_array_equal(label(x, 8), x)
|
||||
assert_array_equal(label(x, 4), label4)
|
||||
assert_array_equal(label(x, 8), x)
|
||||
|
||||
def test_background(self):
|
||||
x = np.zeros((2, 3, 3), int)
|
||||
@@ -188,24 +174,22 @@ class TestConnectedComponents3d:
|
||||
[0, 0, 0]])
|
||||
|
||||
lnb = x.copy()
|
||||
lnb[0] = np.array([[0, 1, 1],
|
||||
[0, 1, 1],
|
||||
[1, 1, 1]])
|
||||
lnb[1] = np.array([[1, 1, 1],
|
||||
[1, 0, 2],
|
||||
[1, 1, 1]])
|
||||
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([[0, BG, BG],
|
||||
[0, BG, BG],
|
||||
lb[0] = np.array([[1, BG, BG],
|
||||
[1, BG, BG],
|
||||
[BG, BG, BG]])
|
||||
lb[1] = np.array([[BG, BG, BG],
|
||||
[BG, 0, 1],
|
||||
[BG, 1, 2],
|
||||
[BG, BG, BG]])
|
||||
|
||||
with expected_warnings(['`background`']):
|
||||
assert_array_equal(label(x), lnb)
|
||||
|
||||
assert_array_equal(label(x, background=0), lb)
|
||||
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)
|
||||
@@ -216,11 +200,11 @@ class TestConnectedComponents3d:
|
||||
[5, 0, 0],
|
||||
[0, 0, 0]])
|
||||
lb = x.copy()
|
||||
lb[0] = np.array([[BG, BG, 0],
|
||||
[BG, BG, 0],
|
||||
[1, 1, 1]])
|
||||
lb[1] = np.array([[0, 0, BG],
|
||||
[1, BG, BG],
|
||||
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)
|
||||
@@ -231,7 +215,7 @@ class TestConnectedComponents3d:
|
||||
x[1, 1, 1] = 1
|
||||
|
||||
lb = np.ones_like(x) * BG
|
||||
lb[1, 1, 1] = 0
|
||||
lb[1, 1, 1] = 1
|
||||
|
||||
assert_array_equal(label(x, neighbors=4, background=0), lb)
|
||||
|
||||
@@ -240,22 +224,19 @@ class TestConnectedComponents3d:
|
||||
[0, 0, 6],
|
||||
[5, 5, 5]])
|
||||
|
||||
with expected_warnings(['`background`']):
|
||||
assert_array_equal(label(x, return_num=True)[1], 4)
|
||||
|
||||
assert_array_equal(label(x, background=0, return_num=True)[1], 3)
|
||||
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, 4, 4))
|
||||
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)
|
||||
with expected_warnings(['`background`']):
|
||||
labelled = label(x2)
|
||||
labelled = label(x2)
|
||||
assert_array_equal(y, labelled.flatten())
|
||||
|
||||
def test_nd(self):
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import numpy as np
|
||||
#from scipy.ndimage import label
|
||||
from ..measure import label
|
||||
|
||||
|
||||
@@ -60,7 +59,7 @@ def clear_border(labels, buffer_size=0, bgval=0, in_place=False):
|
||||
|
||||
# Re-label, in case we are dealing with a binary image
|
||||
# and to get consistent labeling
|
||||
labels = label(image, background=0) + 1
|
||||
labels = label(image, background=0)
|
||||
number = np.max(labels) + 1
|
||||
|
||||
# determine all objects that are connected to borders
|
||||
|
||||
@@ -82,8 +82,7 @@ def test_hough_line_peaks():
|
||||
|
||||
out, angles, d = tf.hough_line(img)
|
||||
|
||||
with expected_warnings(['`background`']):
|
||||
out, theta, dist = tf.hough_line_peaks(out, angles, d)
|
||||
out, theta, dist = tf.hough_line_peaks(out, angles, d)
|
||||
|
||||
assert_equal(len(dist), 1)
|
||||
assert_almost_equal(dist[0], 80.723, 1)
|
||||
@@ -101,9 +100,8 @@ def test_hough_line_peaks_ordered():
|
||||
|
||||
hough_space, angles, dists = tf.hough_line(testim)
|
||||
|
||||
with expected_warnings(['`background`']):
|
||||
hspace, _, _ = tf.hough_line_peaks(hough_space, angles, dists)
|
||||
assert hspace[0] > hspace[1]
|
||||
hspace, _, _ = tf.hough_line_peaks(hough_space, angles, dists)
|
||||
assert hspace[0] > hspace[1]
|
||||
|
||||
|
||||
def test_hough_line_peaks_dist():
|
||||
@@ -111,16 +109,14 @@ def test_hough_line_peaks_dist():
|
||||
img[:, 30] = True
|
||||
img[:, 40] = True
|
||||
hspace, angles, dists = tf.hough_line(img)
|
||||
with expected_warnings(['`background`']):
|
||||
assert len(tf.hough_line_peaks(hspace, angles, dists,
|
||||
min_distance=5)[0]) == 2
|
||||
assert len(tf.hough_line_peaks(hspace, angles, dists,
|
||||
assert len(tf.hough_line_peaks(hspace, angles, dists,
|
||||
min_distance=5)[0]) == 2
|
||||
assert len(tf.hough_line_peaks(hspace, angles, dists,
|
||||
min_distance=15)[0]) == 1
|
||||
|
||||
|
||||
def test_hough_line_peaks_angle():
|
||||
with expected_warnings(['`background`']):
|
||||
check_hough_line_peaks_angle()
|
||||
check_hough_line_peaks_angle()
|
||||
|
||||
|
||||
def check_hough_line_peaks_angle():
|
||||
@@ -154,9 +150,8 @@ def test_hough_line_peaks_num():
|
||||
img[:, 30] = True
|
||||
img[:, 40] = True
|
||||
hspace, angles, dists = tf.hough_line(img)
|
||||
with expected_warnings(['`background`']):
|
||||
assert len(tf.hough_line_peaks(hspace, angles, dists, min_distance=0,
|
||||
min_angle=0, num_peaks=1)[0]) == 1
|
||||
assert len(tf.hough_line_peaks(hspace, angles, dists, min_distance=0,
|
||||
min_angle=0, num_peaks=1)[0]) == 1
|
||||
|
||||
|
||||
@test_parallel()
|
||||
|
||||
Reference in New Issue
Block a user