From 6bface70c5ebe39ed8d30a483c06c49a2ebc150b Mon Sep 17 00:00:00 2001 From: David Manthey Date: Fri, 6 Dec 2024 16:12:59 -0500 Subject: [PATCH] Harden the trace boundary code (#1150) A one pixel object could get stuck in an infinite loop in the moore boundary call. --- .../label/_trace_object_boundaries_cython.pyx | 21 ++++++++++--------- tests/test_segmentation_label.py | 12 ++++++++++- 2 files changed, 22 insertions(+), 11 deletions(-) diff --git a/histomicstk/segmentation/label/_trace_object_boundaries_cython.pyx b/histomicstk/segmentation/label/_trace_object_boundaries_cython.pyx index 236a599fe..a71db5a72 100644 --- a/histomicstk/segmentation/label/_trace_object_boundaries_cython.pyx +++ b/histomicstk/segmentation/label/_trace_object_boundaries_cython.pyx @@ -26,8 +26,8 @@ def _trace_object_boundaries_cython(long[:, :] im_label not None, long connectiv # find starting x and y points if not defined if (x_start == -1) & (y_start == -1): - for i in range(nrows): - for j in range(ncols): + for i in range(1, nrows - 1): + for j in range(1, ncols - 1): if (im_label[i, j] > 0) & (flag == 0): # check if the number of points is one @@ -41,16 +41,17 @@ def _trace_object_boundaries_cython(long[:, :] im_label not None, long connectiv bx = [] by = [] + if x_start >= 0 and y_start >= 0: - if connectivity == 4: - bx, by = _isbf( - im_label, im_label_90, im_label_180, im_label_270, - x_start, y_start, max_length); + if connectivity == 4: + bx, by = _isbf( + im_label, im_label_90, im_label_180, im_label_270, + x_start, y_start, max_length); - else: - bx, by = _moore( - im_label, im_label_90, im_label_180, im_label_270, - x_start, y_start, max_length); + else: + bx, by = _moore( + im_label, im_label_90, im_label_180, im_label_270, + x_start, y_start, max_length); return np.asarray(bx), np.asarray(by) diff --git a/tests/test_segmentation_label.py b/tests/test_segmentation_label.py index 560d39c33..4803541c0 100644 --- a/tests/test_segmentation_label.py +++ b/tests/test_segmentation_label.py @@ -22,7 +22,7 @@ def test_trace_boundary(self): [0, 0, 0, 0, 0, 0, 0, 1, 1, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]], dtype=bool) - # refenece neighbors for isbf + # reference neighbors for isbf rx_isbf = [1, 1, 2, 2, 3, 4, 5, 6, 7, 8, 8, 9, 9, 8, 7, 7, 7, 7, 6, 6, 5, 5, 5, 4, 4, 3, 3, 3, 3, 2, 1] ry_isbf = [7, 8, 8, 7, 6, 6, 6, 6, 6, 7, 8, 8, 7, 7, 6, 5, 4, @@ -46,6 +46,16 @@ def test_trace_boundary(self): np.testing.assert_allclose(rx_moore, x_moore[0]) np.testing.assert_allclose(ry_moore, y_moore[0]) + def test_trace_boundary_bad(self): + # Make sure the algorithms terminate work in a degenerate case + m_neighbor = np.array([[0, 0, 0, 0, 0], + [0, 1, 0, 1, 0], + [0, 0, 0, 0, 0]], dtype=bool) + x, y = trace_object_boundaries(m_neighbor, simplify_colinear_spurs=False) + assert not len(x) + x, y = trace_object_boundaries(m_neighbor, 8, simplify_colinear_spurs=False) + assert not len(x) + class TestDeleteBorderLabel: