diff --git a/neurom/check/morphtree.py b/neurom/check/morphtree.py index 0be18c03..293293e1 100644 --- a/neurom/check/morphtree.py +++ b/neurom/check/morphtree.py @@ -84,7 +84,7 @@ def is_flat(neurite, tol, method='tolerance'): return any(ext < float(tol)) -def is_back_tracking(neurite): +def back_tracking_segments(neurite): """Check if a neurite process backtracks to a previous node. Back-tracking takes place @@ -95,10 +95,11 @@ def is_back_tracking(neurite): neurite(Neurite): neurite to operate on Returns: - True Under the following scenaria: - 1. A segment endpoint falls back and overlaps with a previous segment's point - 2. The geometry of a segment overlaps with a previous one in the section + A generator of tuples containing the section ID and the two segment indices in this section + for which a back tracking is detected (so the first point of these segments can be + retrieved with ``morph.section(section_id).points[segment_id]``. """ + # pylint: disable=too-many-locals def pair(segs): """Pairs the input list into triplets.""" return zip(segs, segs[1:]) @@ -179,9 +180,27 @@ def is_inside_cylinder(seg1, seg2): for i, seg1 in enumerate(segment_pairs[1:]): # check if the end point of the segment lies within the previous # ones in the current section - for seg2 in segment_pairs[0: i + 1]: + for j, seg2 in enumerate(segment_pairs[0: i + 1]): if is_inside_cylinder(seg1, seg2): - return True + yield (sec.id, i, j) + + +def is_back_tracking(neurite): + """Check if a neurite process backtracks to a previous node. + + See back_tracking_segments() for more details. + + Args: + neurite(Neurite): neurite to operate on + + Returns: + True Under the following scenaria: + 1. A segment endpoint falls back and overlaps with a previous segment's point + 2. The geometry of a segment overlaps with a previous one in the section + """ + for _i in back_tracking_segments(neurite): + # If one segment is found then the neurite is back tracking + return True return False diff --git a/tests/check/test_morphtree.py b/tests/check/test_morphtree.py index 7137f49b..a851987a 100644 --- a/tests/check/test_morphtree.py +++ b/tests/check/test_morphtree.py @@ -128,6 +128,30 @@ def test_is_flat(): assert not mt.is_flat(m.neurites[0], 0.1, method='ratio') +def test_back_tracking_segments(): + # case 1: a back-track falls directly on a previous node + t1 = _generate_back_track_tree(1, (0.0, 0.0, 0.0)) + assert list(mt.back_tracking_segments(t1.neurites[0])) == [(2, 1, 0), (2, 1, 1)] + + # case 2: a zigzag is close to another segment + t2 = _generate_back_track_tree(1, (0.1, -0.1, 0.02)) + assert list(mt.back_tracking_segments(t2.neurites[0])) == [(2, 1, 0), (2, 1, 1)] + + # case 3: a zigzag is close to another segment 2 + t3 = _generate_back_track_tree(1, (-0.2, 0.04, 0.144)) + assert list(mt.back_tracking_segments(t3.neurites[0])) == [(2, 1, 0)] + + # case 4: a zigzag far from civilization + t4 = _generate_back_track_tree(1, (10.0, -10.0, 10.0)) + assert list(mt.back_tracking_segments(t4.neurites[0])) == [] + + # case 5: a zigzag on another section + # currently zigzag is defined on the same section + # thus this test should not be true + t5 = _generate_back_track_tree(0, (-0.2, 0.04, 0.144)) + assert list(mt.back_tracking_segments(t5.neurites[0])) == [] + + def test_is_back_tracking(): # case 1: a back-track falls directly on a previous node t = _generate_back_track_tree(1, (0., 0., 0.))