From 44150989eeaafc56d457cfb9eb7238176645603f Mon Sep 17 00:00:00 2001 From: Stefan van der Walt Date: Sat, 23 Jan 2016 16:02:28 -0800 Subject: [PATCH 1/2] WIP --- skimage/measure/_ccomp.pyx | 20 ++++----- skimage/morphology/tests/test_ccomp.py | 59 +++++++++----------------- 2 files changed, 29 insertions(+), 50 deletions(-) diff --git a/skimage/measure/_ccomp.pyx b/skimage/measure/_ccomp.pyx index e7088d24..8aac2390 100644 --- a/skimage/measure/_ccomp.pyx +++ b/skimage/measure/_ccomp.pyx @@ -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. @@ -389,8 +386,7 @@ 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. return_num : bool, optional Whether to return the number of assigned labels. connectivity : int, optional @@ -430,10 +426,10 @@ def label(input, neighbors=None, background=None, return_num=False, ... [1, 1, 5], ... [0, 0, 0]]) - >>> print(label(x, background=0)) - [[ 0 -1 -1] - [ 0 0 1] - [-1 -1 -1]] + >>> print(label(x, background=1)) + [[ 0 1 1] + [ 0 0 2] + [ 3 3 3]] """ # We have to ensure that the shape of the input can be handled by the @@ -515,7 +511,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 = 0, i for i in range(shapeinfo.numels): if i == bg.background_node: diff --git a/skimage/morphology/tests/test_ccomp.py b/skimage/morphology/tests/test_ccomp.py index 901a18b9..ffbfa76a 100644 --- a/skimage/morphology/tests/test_ccomp.py +++ b/skimage/morphology/tests/test_ccomp.py @@ -24,17 +24,14 @@ class TestConnectedComponents: [6, 5, 5, 7, 8, 9]]) 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,29 +42,26 @@ 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, 3]]) + 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), [[0, 1, 1], + [0, 0, 2], + [3, 3, 3]]) assert_array_equal(label(x, background=0), [[0, -1, -1], @@ -100,8 +94,7 @@ class TestConnectedComponents: [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], 4) assert_array_equal(label(x, background=0, return_num=True)[1], 3) @@ -142,8 +135,7 @@ class TestConnectedComponents3d: [10, 5, 7, 7, 7]]) 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 +143,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 +155,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 +163,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) @@ -202,9 +190,7 @@ class TestConnectedComponents3d: [BG, 0, 1], [BG, BG, BG]]) - with expected_warnings(['`background`']): - assert_array_equal(label(x), lnb) - + assert_array_equal(label(x), lnb) assert_array_equal(label(x, background=0), lb) def test_background_two_regions(self): @@ -240,9 +226,7 @@ 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, return_num=True)[1], 4) assert_array_equal(label(x, background=0, return_num=True)[1], 3) def test_1D(self): @@ -254,8 +238,7 @@ class TestConnectedComponents3d: (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): From a5a771a8e48976eba2d77a9a4f5a388a051ed420 Mon Sep 17 00:00:00 2001 From: emmanuelle Date: Sun, 21 Feb 2016 15:56:35 +0100 Subject: [PATCH 2/2] Modified label function so that background pixels are labeled with 0, and background=0 by default. Modified label function so that background pixels are labeled with 0, and background=0 by default. All tests of _ccomp.pyx pass Modified a couple of files to be consistent with the new behavior of measure.label Modified doctring of label to pass doctest Modified TODO.txt as well as release notes to mention the new behavior of label. Typo in docstring Typo in docstring Changed default value of kw argument background in measure.label Removed unnecessary and outdated comment --- TODO.txt | 4 - doc/examples/segmentation/plot_label.py | 2 - doc/release/release_0.12.txt | 3 + skimage/measure/_ccomp.pyx | 38 ++++---- skimage/morphology/convex_hull.py | 2 +- skimage/morphology/tests/test_ccomp.py | 90 +++++++++---------- skimage/segmentation/_clear_border.py | 3 +- .../transform/tests/test_hough_transform.py | 23 ++--- 8 files changed, 80 insertions(+), 85 deletions(-) diff --git a/TODO.txt b/TODO.txt index d1ceea93..a6dc649e 100644 --- a/TODO.txt +++ b/TODO.txt @@ -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. diff --git a/doc/examples/segmentation/plot_label.py b/doc/examples/segmentation/plot_label.py index f80271ce..310c70a1 100644 --- a/doc/examples/segmentation/plot_label.py +++ b/doc/examples/segmentation/plot_label.py @@ -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)) diff --git a/doc/release/release_0.12.txt b/doc/release/release_0.12.txt index 84a8dfae..f1f042ef 100644 --- a/doc/release/release_0.12.txt +++ b/doc/release/release_0.12.txt @@ -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) diff --git a/skimage/measure/_ccomp.pyx b/skimage/measure/_ccomp.pyx index 8aac2390..77d237eb 100644 --- a/skimage/measure/_ccomp.pyx +++ b/skimage/measure/_ccomp.pyx @@ -358,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. @@ -386,13 +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 0. + 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 ------- @@ -413,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=1)) - [[ 0 1 1] - [ 0 0 2] - [ 3 3 3]] - + >>> 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 @@ -511,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 = 0, i + cdef DTYPE_t counter = 1, i for i in range(shapeinfo.numels): if i == bg.background_node: @@ -523,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, diff --git a/skimage/morphology/convex_hull.py b/skimage/morphology/convex_hull.py index 31b92a47..bb372476 100644 --- a/skimage/morphology/convex_hull.py +++ b/skimage/morphology/convex_hull.py @@ -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) diff --git a/skimage/morphology/tests/test_ccomp.py b/skimage/morphology/tests/test_ccomp.py index ffbfa76a..a5b1baeb 100644 --- a/skimage/morphology/tests/test_ccomp.py +++ b/skimage/morphology/tests/test_ccomp.py @@ -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,7 +19,7 @@ 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): assert_array_equal(label(self.x), self.labels) @@ -49,7 +47,7 @@ class TestConnectedComponents: [1, 0]], dtype=int) assert_array_equal(label(x, 4), [[0, 1], - [2, 3]]) + [2, 0]]) assert_array_equal(label(x, 8), [[0, 1], [1, 0]]) @@ -59,14 +57,14 @@ class TestConnectedComponents: [1, 1, 5], [0, 0, 0]]) - 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], @@ -75,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], @@ -85,18 +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]]) - 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: @@ -122,17 +120,17 @@ 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): labels = label(self.x) @@ -176,22 +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]]) - 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) @@ -202,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) @@ -217,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) @@ -226,13 +224,13 @@ class TestConnectedComponents3d: [0, 0, 6], [5, 5, 5]]) - 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)) diff --git a/skimage/segmentation/_clear_border.py b/skimage/segmentation/_clear_border.py index bc972754..a44f0785 100644 --- a/skimage/segmentation/_clear_border.py +++ b/skimage/segmentation/_clear_border.py @@ -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 diff --git a/skimage/transform/tests/test_hough_transform.py b/skimage/transform/tests/test_hough_transform.py index 0babc769..2f7a6b96 100644 --- a/skimage/transform/tests/test_hough_transform.py +++ b/skimage/transform/tests/test_hough_transform.py @@ -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()