Merge pull request #1956 from emmanuelle/label_zero

New handling of background pixels for measure.label
This commit is contained in:
Juan Nunez-Iglesias
2016-02-23 10:10:56 +11:00
8 changed files with 96 additions and 122 deletions
-4
View File
@@ -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.
-2
View File
@@ -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))
+3
View File
@@ -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
View File
@@ -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,
+1 -1
View File
@@ -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)
+58 -77
View File
@@ -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 -2
View File
@@ -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()