From 52974c7c4f71f7469c993c723901ddffb493ed8d Mon Sep 17 00:00:00 2001 From: willgraf <7930703+willgraf@users.noreply.github.com> Date: Fri, 19 Nov 2021 13:08:03 -0800 Subject: [PATCH] Fix bug in `is_valid_lineage`; daughters can be < parent. (#88) * Test parent label > daughter labels. * No need to see if daughter in all_cells. If it is in the lineage (checked for), then it will be removed from all_cells in due time. * Warn for all mistakes and return True if * If not in all cells, don't remove it from the set. * Continue instead of return, keep the short circuit. * Update version to 0.5.4 * Clarify warnings. --- deepcell_tracking/utils.py | 41 +++++++++++++++++++++------------ deepcell_tracking/utils_test.py | 11 +++++++++ setup.py | 2 +- 3 files changed, 38 insertions(+), 16 deletions(-) diff --git a/deepcell_tracking/utils.py b/deepcell_tracking/utils.py index 36127ac..8786163 100644 --- a/deepcell_tracking/utils.py +++ b/deepcell_tracking/utils.py @@ -488,13 +488,16 @@ def is_valid_lineage(y, lineage): all_cells = np.unique(y) all_cells = set([c for c in all_cells if c]) + is_valid = True + # every lineage should have valid fields for cell_label, cell_lineage in lineage.items(): # Get last frame of parent if cell_label not in all_cells: warnings.warn('Cell {} not found in the label image.'.format( cell_label)) - return False + is_valid = False + continue # any cells leftover are missing lineage all_cells.remove(cell_label) @@ -505,28 +508,33 @@ def is_valid_lineage(y, lineage): frames = list(y_index) if frames != cell_lineage['frames']: warnings.warn('Cell {} has invalid frames'.format(cell_label)) - return False + is_valid = False + continue # no need to test further last_parent_frame = cell_lineage['frames'][-1] for daughter in cell_lineage['daughters']: - if daughter not in all_cells or daughter not in lineage: - warnings.warn('lineage {} has invalid daughters: {}'.format( - cell_label, cell_lineage['daughters'])) - return False + if daughter not in lineage: + warnings.warn('Lineage {} has daughter {} not in lineage'.format( + cell_label, daughter)) + is_valid = False + continue # no need to test further # get first frame of daughter try: first_daughter_frame = lineage[daughter]['frames'][0] except IndexError: # frames is empty? warnings.warn('Daughter {} has no frames'.format(daughter)) - return False + is_valid = False + continue # no need to test further # Check that daughter's start frame is one larger than parent end frame if first_daughter_frame - last_parent_frame != 1: - warnings.warn('lineage {} has daughter {} before parent.'.format( - cell_label, daughter)) - return False + warnings.warn('Lineage {} has daughter {} in a ' + 'non-subsequent frame.'.format( + cell_label, daughter)) + is_valid = False + continue # no need to test further # TODO: test parent in lineage parent = cell_lineage.get('parent') @@ -536,13 +544,15 @@ def is_valid_lineage(y, lineage): except KeyError: warnings.warn('Parent {} is not present in the lineage'.format( cell_lineage['parent'])) - return False + is_valid = False + continue # no need to test further try: last_parent_frame = parent_lineage['frames'][-1] first_daughter_frame = cell_lineage['frames'][0] except IndexError: # frames is empty? warnings.warn('Cell {} has no frames'.format(parent)) - return False + is_valid = False + continue # no need to test further # Check that daughter's start frame is one larger than parent end frame if first_daughter_frame - last_parent_frame != 1: warnings.warn( @@ -550,14 +560,15 @@ def is_valid_lineage(y, lineage): 'appears in frame {}.'.format( parent, last_parent_frame, cell_label, first_daughter_frame)) - return False + is_valid = False + continue # no need to test further if all_cells: # all cells with lineages should be removed warnings.warn('Cells missing their lineage: {}'.format( list(all_cells))) - return False + is_valid = False - return True # all cell lineages are valid! + return is_valid # if unchanged, all cell lineages are valid! def get_image_features(X, y, appearance_dim=32): diff --git a/deepcell_tracking/utils_test.py b/deepcell_tracking/utils_test.py index 2cdaefa..c4ccfcd 100644 --- a/deepcell_tracking/utils_test.py +++ b/deepcell_tracking/utils_test.py @@ -401,6 +401,17 @@ def test_is_valid_lineage(self): bad_lineage[daughter_labels[0]]['frames'] = [] assert not utils.is_valid_lineage(movie, bad_lineage) + # parent ID > daughters ID is OK + new_parent = max(np.unique(movie)) + 2 + relabeled_movie = np.where(movie == parent_label, new_parent, movie) + relabeled_lineage = copy.deepcopy(lineage) + relabeled_lineage[new_parent] = lineage[parent_label] + del relabeled_lineage[parent_label] + for daughter in relabeled_lineage[new_parent]['daughters']: + if relabeled_lineage[daughter]['parent'] == parent_label: + relabeled_lineage[daughter]['parent'] = new_parent + assert utils.is_valid_lineage(relabeled_movie, relabeled_lineage) + def test_get_image_features(self): num_labels = 3 y = get_annotated_image(num_labels=num_labels, sequential=True) diff --git a/setup.py b/setup.py index fb7d500..796f914 100644 --- a/setup.py +++ b/setup.py @@ -37,7 +37,7 @@ readme = f.read() -VERSION = '0.5.3' +VERSION = '0.5.4' NAME = 'DeepCell_Tracking' DESCRIPTION = 'Tracking cells and lineage with deep learning.' LICENSE = 'LICENSE'