def test_add_cell_info_multiple_frames(self):
        num_frames = 5
        labels = np.zeros((num_frames, 1, 1, 1))
        project = models.Project.create(DummyLoader(labels=labels))
        edit = label.Edit(project)
        cell_ids = edit.labels.cell_ids
        cell_info = edit.labels.cell_info

        cell = 1
        feature = 0

        # Add new label to all frames
        for frame in range(num_frames):
            edit.add_cell_info(cell, frame)
            assert cell in cell_ids[feature]
            assert cell_info[feature][cell] == {
                'label': 1,
                'frames': list(range(frame + 1)),
                'parent': None,
                'frame_div': None,
                'daughters': [],
                'capped': False,
            }
            assert edit.y_changed
            assert edit.labels_changed
    def test_replace_with_parent_with_overlap(self, app):
        """
        Replaces daughter with parent when the daughter exists before the div.
        """
        labels = np.reshape([1, 2, 1, 2], (2, 2, 1, 1))
        cell_info = {
            0: {
                1: {
                    'capped': True,
                    'frame_div': 1,
                    'daughters': [2],
                    'parent': None,
                    'frames': [0, 1],
                },
                2: {
                    'capped': False,
                    'frame_div': None,
                    'daughters': [],
                    'parent': 1,
                    'frames': [0, 1],
                },
            }
        }
        loader = DummyLoader(labels=labels,
                             cell_info=cell_info,
                             path='test.trk')
        project = models.Project.create(loader)
        edit = label.Edit(project)

        daughter = 2
        expected_labels = np.reshape([1, 2, 1, 1], (2, 2, 1, 1))
        expected_cell_info = {
            0: {
                1: {
                    'capped': False,
                    'frame_div': None,
                    'daughters': [],
                    'parent': None,
                    'frames': [0, 1],
                },
                2: {
                    'capped': False,
                    'frame_div': None,
                    'daughters': [],
                    'parent': None,
                    'frames': [0],
                },
            }
        }

        with app.app_context():
            edit.action_replace_with_parent(daughter)
            np.testing.assert_equal(project.label_array, expected_labels)
            assert edit.tracks == expected_cell_info[0]
    def test_action_dilate_other_labels_unchanged(self, app):
        """Tests that other labels not affected by dilating a label."""
        labels = np.reshape([1, 1, 2, 2], (1, 2, 2, 1))
        project = models.Project.create(DummyLoader(labels=labels))
        edit = label.Edit(project)

        cell = 1
        other_cell = 2

        with app.app_context():
            edit.action_dilate(cell)
            np.testing.assert_array_equal(labels[project.frame] == other_cell,
                                          edit.frame == other_cell)
    def test_action_replace_single(self, app):
        # single 2 x 2 frame with two labels: 1s in top row, 2s in bottom
        labels = np.reshape([1, 1, 2, 2], (1, 2, 2, 1))
        project = models.Project.create(DummyLoader(labels=labels))
        edit = label.Edit(project)
        expected_labels = np.array([[[1], [1]], [[1], [1]]])

        cell1 = 1
        cell2 = 2
        with app.app_context():
            edit.action_replace_single(cell1, cell2)
            np.testing.assert_array_equal(edit.frame, expected_labels)
            assert 2 not in edit.tracks
    def test_action_erode_delete_label(self, app):
        """Tests that a label is correctly removed when eroding deletes all of its pixels."""
        labels = np.zeros((1, 3, 3, 1))
        labels[0, 1, 1, 0] = 1
        project = models.Project.create(DummyLoader(labels=labels))
        edit = label.Edit(project)

        cell = 1

        with app.app_context():
            edit.action_erode(cell)
            assert cell not in edit.frame
            assert cell not in project.labels.cell_ids[project.feature]
            assert cell not in project.labels.cell_info[project.feature]
    def test_track_replace_daughter_that_divides_with_parent(self, app):
        labels = np.reshape([1, 2, 3], (3, 1, 1, 1))
        cell_info = {
            0: {
                1: {
                    'capped': True,
                    'frame_div': 1,
                    'daughters': [2],
                    'parent': None,
                    'frames': [0],
                },
                2: {
                    'capped': True,
                    'frame_div': 2,
                    'daughters': [3],
                    'parent': 1,
                    'frames': [1],
                },
                3: {
                    'capped': False,
                    'frame_div': None,
                    'daughters': [],
                    'parent': 2,
                    'frames': [2],
                },
            }
        }
        project = models.Project.create(
            DummyLoader(labels=labels, cell_info=cell_info, path='test.trk'))
        edit = label.Edit(project)
        expected_labels = np.reshape([1, 1, 3], (3, 1, 1, 1))

        parent = 1
        daughter = 2
        granddaughter = 3

        with app.app_context():
            edit.action_replace_with_parent(daughter)
            tracks = edit.tracks
            parent_track = tracks[parent]
            granddaughter_track = tracks[granddaughter]
            assert daughter not in tracks
            assert parent_track['capped']
            assert parent_track['daughters'] == [3]
            assert parent_track['frame_div'] == 2
            assert parent_track['frames'] == [0, 1]
            assert granddaughter_track['parent'] == 1
            np.testing.assert_array_equal(edit.project.label_array,
                                          expected_labels)
    def test_action_active_contour_label_too_large(self, app):
        """Tests that a label that is larger than the raw objects is made
        smaller by active contouring."""
        raw = np.zeros((1, 10, 10, 1))
        labels = np.ones((1, 10, 10, 1))
        raw[0, 3:6, 3:6, 0] = 1
        project = models.Project.create(DummyLoader(raw=raw, labels=labels))
        edit = label.Edit(project)

        cell = 1

        with app.app_context():
            edit.action_active_contour(cell)
            assert int(
                (edit.frame == 1).sum()) < (labels[project.frame] == 1).sum()
    def test_action_handle_draw_add_label(self, app):
        """Adding a label with by drawing it in."""
        labels = np.reshape([0], (1, 1, 1, 1))
        project = models.Project.create(DummyLoader(labels=labels))
        edit = label.Edit(project)

        trace, foreground, background, brush_size = [(0, 0)], 1, 0, 1
        feature = 0
        expected_draw = np.reshape([1], (1, 1))

        with app.app_context():
            edit.action_handle_draw(trace, foreground, background, brush_size)
            np.testing.assert_array_equal(edit.frame[..., feature],
                                          expected_draw)
            assert foreground in edit.labels.cell_info[feature]
            assert foreground in edit.labels.cell_ids[feature]
    def test_action_active_contour_other_labels_unchanged(self, app):
        """
        Tests that other labels not affected by active contouring a label
        """
        labels = np.ones((1, 10, 10, 1))
        labels[:, 5:, :, :] = 2
        project = models.Project.create(DummyLoader(labels=labels))
        edit = label.Edit(project)

        cell = 1
        other_cell = 2

        with app.app_context():
            edit.action_active_contour(cell)
            np.testing.assert_array_equal(labels[project.frame] == other_cell,
                                          edit.frame == other_cell)
    def test_add_daughter_existing_division(self, app):
        # two 2 x 1 frames
        # one label in the first frame and two in the second frame
        labels = np.reshape([0, 1, 2, 3], (2, 2, 1, 1))
        cell_info = {
            0: {
                1: {
                    'capped': True,
                    'frame_div': 1,
                    'daughters': [2],
                    'parent': None
                },
                2: {
                    'capped': False,
                    'frame_div': None,
                    'daughters': [],
                    'parent': 1
                },
                3: {
                    'capped': False,
                    'frame_div': None,
                    'daughters': [],
                    'parent': None,
                },
            }
        }
        project = models.Project.create(
            DummyLoader(labels=labels, cell_info=cell_info, path='test.trk'))
        edit = label.Edit(project)

        parent = 1
        daughter = 2
        other_daughter = 3
        frame_div = 1

        with app.app_context():
            edit.action_add_daughter(parent, other_daughter)
            tracks = edit.tracks
            parent_track = tracks[parent]
            daughter_track = tracks[daughter]
            other_daughter_track = tracks[other_daughter]
            assert parent_track['capped']
            assert daughter in parent_track['daughters']
            assert other_daughter in parent_track['daughters']
            assert parent_track['frame_div'] == frame_div
            assert daughter_track['parent'] == parent
            assert other_daughter_track['parent'] == parent
    def test_action_swap_single_frame_with_label_not_in_frame(self, app):
        """Tests that swapping with a cell not in frame updates the cell info."""
        labels = np.reshape([1], (1, 1, 1, 1))
        project = models.Project.create(DummyLoader(labels=labels))
        edit = label.Edit(project)

        cell1 = 1
        cell2 = 2
        expected_labels = np.reshape([2], (1, 1, 1, 1))

        with app.app_context():
            edit.action_swap_single_frame(cell1, cell2)
            np.testing.assert_array_equal(project.label_array, expected_labels)
            assert edit.y_changed
            assert edit.labels_changed
            assert cell2 in edit.tracks
            assert cell1 not in edit.tracks
    def test_action_flood_background(self, app):
        """Flooding background does NOT spread to diagonal areas."""
        # 3 x 3 frame with label in diamond shape
        labels = np.reshape([0, 1, 0, 1, 0, 1, 0, 1, 0], (1, 3, 3, 1))
        project = models.Project.create(DummyLoader(labels=labels))
        edit = label.Edit(project)

        flood_label, x_loc, y_loc = 2, 1, 1
        feature = 0
        expected_flood = np.reshape([0, 1, 0, 1, 2, 1, 0, 1, 0], (3, 3))

        with app.app_context():
            edit.action_flood(flood_label, x_loc, y_loc)
            np.testing.assert_array_equal(edit.frame[..., feature],
                                          expected_flood)
            assert edit.y_changed
            assert edit.labels_changed
    def test_action_swap_single_frame(self, app):
        # single 2 x 2 frame with one feature; cell 1 in top row, cell 2 in bottom row
        labels = np.reshape([1, 1, 2, 2], (1, 2, 2, 1))
        project = models.Project.create(DummyLoader(labels=labels))
        edit = label.Edit(project)

        cell1 = 1
        cell2 = 2
        feature = 0
        expected_swap = np.array([[2, 2], [1, 1]])

        with app.app_context():
            edit.action_swap_single_frame(cell1, cell2)
            np.testing.assert_array_equal(edit.frame[..., feature],
                                          expected_swap)
            assert edit.y_changed
            assert edit.labels_changed
    def test_def_cell_info_one_frame(self):
        labels = np.ones((2, 1, 1, 1))
        project = models.Project.create(DummyLoader(labels=labels))
        edit = label.Edit(project)
        cell_ids = edit.labels.cell_ids
        cell_info = edit.labels.cell_info

        feature = 0
        cell = 1
        frame = 1

        # Remove from second frame only
        edit.del_cell_info(cell, frame)
        assert cell in cell_ids[feature]
        assert cell in cell_info[feature]
        assert frame not in cell_info[feature][frame]['frames']
        assert edit.y_changed
        assert edit.labels_changed
    def test_action_new_track_first_frame_of_track(self, app):
        """A new track on the first frame a label appears does nothing."""
        # two 1x1 frames with one feature; cell starts on second frame
        labels = np.reshape([0, 1], (2, 1, 1, 1))
        project = models.Project.create(
            DummyLoader(labels=labels, path='test.trk'))
        cell = 1
        frame = 1
        feature = 0
        project.frame = frame
        project.feature = feature
        edit = label.Edit(project)
        tracks = edit.tracks

        prev_track = tracks[cell].copy()
        with app.app_context():
            edit.action_new_track(cell)
            assert cell in edit.frame[..., feature]
            assert prev_track == tracks[cell]
    def test_del_cell_info_last_frame(self):
        labels = np.ones((1, 1, 1, 1))
        project = models.Project.create(DummyLoader(labels=labels))
        edit = label.Edit(project)
        cell_ids = edit.labels.cell_ids
        cell_info = edit.labels.cell_info

        feature = 0
        cell = 1
        frame = 0

        # Remove the label completely (only remaining frame)
        edit.del_cell_info(cell, frame)
        assert cell not in cell_ids[feature]
        assert cell not in cell_info[feature]
        assert edit.y_changed
        assert edit.labels_changed
        np.testing.assert_array_equal(cell_ids[feature], np.array([]))
        assert edit.labels.cell_info[feature] == {}
    def test_remove_daughter_only_daughter(self, app):
        # two 2 x 1 frames
        # one label in the first frame and two in the second frame
        labels = np.reshape([0, 1, 2, 3], (2, 2, 1, 1))
        cell_info = {
            0: {
                1: {
                    'capped': True,
                    'frame_div': 1,
                    'daughters': [2],
                    'parent': None
                },
                2: {
                    'capped': False,
                    'frame_div': None,
                    'daughters': [],
                    'parent': 1
                },
                3: {
                    'capped': False,
                    'frame_div': None,
                    'daughters': [],
                    'parent': None,
                },
            }
        }
        project = models.Project.create(
            DummyLoader(labels=labels, cell_info=cell_info, path='test.trk'))
        edit = label.Edit(project)

        parent = 1
        daughter = 2

        with app.app_context():
            edit.action_remove_daughter(daughter)
            tracks = edit.tracks
            parent_track = tracks[parent]
            daughter_track = tracks[daughter]
            assert not parent_track['capped']
            assert parent_track['daughters'] == []
            assert parent_track['frame_div'] is None
            assert daughter_track['parent'] is None
    def test_add_daughter_new_division(self, app):
        # two 2 x 1 frames
        # one label in the first frame and two in the second frame
        labels = np.reshape([0, 1, 2, 3], (2, 2, 1, 1))
        project = models.Project.create(
            DummyLoader(labels=labels, path='test.trk'))
        project.frame = 1
        edit = label.Edit(project)

        parent = 1
        daughter = 2

        with app.app_context():
            edit.action_add_daughter(parent, daughter)
            tracks = edit.tracks
            parent_track = tracks[parent]
            daughter_track = tracks[daughter]
            assert parent_track['capped']
            assert daughter in parent_track['daughters']
            assert parent_track['frame_div'] == 1
            assert daughter_track['parent'] == parent
    def test_action_new_track(self, app):
        """Create a new track on the second frame of a label."""
        # two 1x1 frames with one feature; cell appears in both frames
        labels = np.reshape([1, 1], (2, 1, 1, 1))
        project = models.Project.create(
            DummyLoader(labels=labels, path='test.trk'))
        cell = 1
        frame = 1
        feature = 0
        expected_new_cell = 2
        project.frame = frame
        edit = label.Edit(project)
        tracks = edit.tracks
        prev_track = tracks[cell].copy()

        with app.app_context():
            edit.action_new_track(cell)
            assert cell not in edit.frame[..., feature]
            assert expected_new_cell in edit.frame[..., feature]
            assert expected_new_cell in edit.labels.cell_ids[feature]
            assert prev_track['frames'] == (
                tracks[cell]['frames'] + tracks[expected_new_cell]['frames'])
    def test_add_self_as_daughter(self, app):
        """
        Add parent as a daughter of itself, creating a new label
        """
        labels = np.reshape([1, 1], (2, 1, 1, 1))
        cell_info = {
            0: {
                1: {
                    'capped': False,
                    'frame_div': None,
                    'daughters': [],
                    'parent': None,
                    'frames': [0, 1],
                }
            }
        }
        expected_labels = np.reshape([1, 2], (2, 1, 1, 1))
        loader = DummyLoader(labels=labels,
                             cell_info=cell_info,
                             path='test.trk')
        project = models.Project.create(loader)
        project.frame = 1
        edit = label.Edit(project)

        parent = 1
        daughter = 1

        with app.app_context():
            edit.action_add_daughter(parent, daughter)
            tracks = edit.tracks
            np.testing.assert_equal(project.label_array, expected_labels)
            assert tracks[1]['capped']
            assert tracks[1]['daughters'] == [2]
            assert tracks[1]['frame_div'] == 1
            assert tracks[1]['parent'] is None
            assert not tracks[2]['capped']
            assert tracks[2]['frame_div'] is None
            assert tracks[2]['daughters'] == []
            assert tracks[2]['parent'] == 1
    def test_zstack_add_cell_info(self):
        labels = np.zeros((1, 1, 1, 1))
        project = models.Project.create(DummyLoader(labels=labels))
        edit = label.Edit(project)
        cell_ids = edit.labels.cell_ids
        cell_info = edit.labels.cell_info

        cell = 1
        frame = 0
        feature = 0

        # Add new label to first frame
        edit.add_cell_info(cell, frame)
        assert cell in cell_ids[feature]
        assert cell_info[feature][cell] == {
            'label': 1,
            'frames': [frame],
            'parent': None,
            'frame_div': None,
            'daughters': [],
            'capped': False,
        }
        assert edit.y_changed
        assert edit.labels_changed
    def test_replace_with_parent_daughter_divides_in_future(self, app):
        """
        Replaces daughter with parent when the daughter divides after the parent.
        """
        labels = np.reshape([1, 0, 0, 2, 3, 0, 4, 5, 3], (3, 3, 1, 1))
        cell_info = {
            0: {
                1: {
                    'capped': True,
                    'frame_div': 1,
                    'daughters': [2, 3],
                    'parent': None,
                    'frames': [0],
                },
                2: {
                    'capped': True,
                    'frame_div': 2,
                    'daughters': [4, 5],
                    'parent': 1,
                    'frames': [1],
                },
                3: {
                    'capped': False,
                    'frame_div': None,
                    'daughters': [],
                    'parent': 1,
                    'frames': [1],
                },
                4: {
                    'capped': False,
                    'frame_div': None,
                    'daughters': [],
                    'parent': 2,
                    'frames': [2],
                },
                5: {
                    'capped': False,
                    'frame_div': None,
                    'daughters': [],
                    'parent': 2,
                    'frames': [2],
                },
            }
        }
        loader = DummyLoader(labels=labels,
                             cell_info=cell_info,
                             path='test.trk')
        project = models.Project.create(loader)
        edit = label.Edit(project)

        daughter = 2
        expected_labels = np.reshape([1, 0, 0, 1, 3, 0, 4, 5, 3], (3, 3, 1, 1))
        expected_cell_info = {
            0: {
                1: {
                    'capped': True,
                    'frame_div': 2,
                    'daughters': [4, 5],
                    'parent': None,
                    'frames': [0, 1],
                },
                3: {
                    'capped': False,
                    'frame_div': None,
                    'daughters': [],
                    'parent': None,
                    'frames': [1],
                },
                4: {
                    'capped': False,
                    'frame_div': None,
                    'daughters': [],
                    'parent': 1,
                    'frames': [2],
                },
                5: {
                    'capped': False,
                    'frame_div': None,
                    'daughters': [],
                    'parent': 1,
                    'frames': [2],
                },
            }
        }

        with app.app_context():
            edit.action_replace_with_parent(daughter)
            np.testing.assert_equal(project.label_array, expected_labels)
            assert edit.tracks == expected_cell_info[0]
    def test_add_self_as_daughter_to_existing_division(self, app):
        """
        Add parent as a daughter of itself when parent has a division in the future.
        """
        labels = np.reshape([0, 1, 2, 1], (2, 2, 1, 1))
        cell_info = {
            0: {
                1: {
                    'label': 1,
                    'capped': True,
                    'frame_div': 1,
                    'daughters': [2],
                    'parent': None,
                    'frames': [0, 1],
                },
                2: {
                    'label': 2,
                    'capped': False,
                    'frame_div': None,
                    'daughters': [],
                    'parent': 1,
                    'frames': [1],
                },
            }
        }
        expected_labels = np.reshape([0, 1, 2, 3], (2, 2, 1, 1))
        expected_lineage = {
            1: {
                'label': 1,
                'capped': True,
                'frame_div': 1,
                'daughters': [2, 3],
                'parent': None,
                'frames': [0],
            },
            2: {
                'label': 2,
                'capped': False,
                'frame_div': None,
                'daughters': [],
                'parent': 1,
                'frames': [1],
            },
            3: {
                'label': 3,
                'capped': False,
                'frame_div': None,
                'daughters': [],
                'parent': 1,
                'frames': [1],
            },
        }
        loader = DummyLoader(labels=labels,
                             cell_info=cell_info,
                             path='test.trk')
        project = models.Project.create(loader)
        project.frame = 1
        edit = label.Edit(project)

        parent = 1
        daughter = 1

        with app.app_context():
            edit.action_add_daughter(parent, daughter)
            tracks = edit.tracks
            np.testing.assert_equal(project.label_array, expected_labels)
            assert tracks == expected_lineage