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_redo_frame_not_changed_in_next_action(): """ Tests redoing when a frame may not be stored in the next action. """ # Create project with two frames project = models.Project.create(DummyLoader(raw=np.zeros((2, 1, 1, 1)))) # Mock action on both frame for frame in project.label_frames: frame.frame[:] = 1 project.create_memento('both_frames') project.update() # Mock action on first frame project.label_frames[0].frame[:] = 2 project.create_memento('first_frame_only') project.update() # Undo both action project.undo() project.undo() # Redo first action project.redo() assert 0 not in project.label_array assert 2 not in project.label_array assert 1 in project.label_frames[0].frame assert 1 in project.label_frames[1].frame
def test_raw_frame_init(): """Test constructing the raw frames for a project.""" project = models.Project.create(DummyLoader()) raw_frames = project.raw_frames for frame in raw_frames: assert len(frame.frame.shape) == 3 # Height, width, channels assert frame.frame_id is not None
def test_export_npz(self, app, db_session): with app.app_context(): db_session.autoflush = False project = models.Project.create(DummyLoader()) exporter = exporters.Exporter(project, 'npz') file_ = exporter.export() assert isinstance(file_, io.BytesIO)
def test_label_frame_init(): """Test constructing the label frames for a project.""" project = models.Project.create(DummyLoader()) label_frames = project.label_frames for frame in label_frames: assert frame.frame.ndim == 3 # Height, width, features assert frame.frame_id is not None
def test_get_max_label_two_frames(): labels = np.array([[[[1]]], [[[2]]]]) project = models.Project.create(DummyLoader(labels=labels)) feature = 0 project.feature = feature project.update() max_label = project.get_max_label(feature) assert max_label == 2
def test_get_max_label_all_ones(): labels = np.ones((1, 1, 1, 1)) project = models.Project.create(DummyLoader(labels=labels)) feature = 0 project.feature = feature project.update() max_label = project.get_max_label(feature) assert max_label == 1
def test_export(self, mocker, app, db_session): with app.app_context(): mocked = mocker.patch('boto3.s3.inject.upload_fileobj') db_session.autoflush = False project = models.Project.create(DummyLoader()) exporter = exporters.S3Exporter(project, 'npz') exporter.export('test') mocked.assert_called()
def test_labels_init(): """Test constructing the Labels row for a Project.""" project = models.Project.create(DummyLoader()) labels = project.labels assert len(labels.cell_ids) == project.num_features assert len(labels.cell_info) == project.num_features for feature in range(project.num_features): assert len(labels.cell_ids[feature]) == len(labels.cell_info[feature])
def test_redo_no_next_action(): """Test redoing at the end of the action history.""" project = models.Project.create(DummyLoader()) action = project.action num_actions = project.num_actions project.redo() assert project.action is action assert project.num_actions == num_actions
def test_undo_no_previous_action(): """Test undoing at the start of the action history.""" project = models.Project.create(DummyLoader()) action = project.action num_actions = project.num_actions project.undo() assert project.action is action assert project.num_actions == num_actions
def test_get_project(): """ Test getting a project from the Projects table. creates a project, then gets it again. """ project = models.Project.create(DummyLoader()) valid_id = project.token found_project = models.Project.get(valid_id) assert found_project == project
def test_frames_init(): """Test that raw, and label frames within a project are all compatible.""" project = models.Project.create(DummyLoader()) raw_frames = project.raw_frames label_frames = project.label_frames assert len(raw_frames) == len(label_frames) for raw_frame, label_frame in zip(raw_frames, label_frames): assert raw_frame.frame.shape[:-1] == label_frame.frame.shape[:-1] assert raw_frame.frame_id == label_frame.frame_id assert raw_frame.project_id == label_frame.project_id
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_undo(client): # Project not found response = client.post('/api/undo/0') assert response.status_code == 404 # Create a project project = models.Project.create(DummyLoader()) # Undo with no action to undo silently does nothing response = client.post('/api/undo/{}'.format(project.token)) assert response.status_code == 200
def test_redo(client): # Project not found response = client.post('/api/redo/0') assert response.status_code == 404 # Create a project project = models.Project.create(DummyLoader()) # Redo with no action to redo silently does nothing response = client.post(f'/api/redo/{project.token}') assert response.status_code == 200
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_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_project_init(): """ Test constructor for Project table. """ project = models.Project(DummyLoader()) # Check columns filled in by constructor assert project.path is not None assert project.source is not None assert project.num_frames is not None assert project.height is not None assert project.width is not None assert project.num_channels is not None assert project.num_features is not None
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_finish_project(): """ Test finishing a project. Checks that the project's relationship are also finished. """ # create project project = models.Project.create(DummyLoader()) project.finish() assert project.finished is not None assert project.labels.cell_ids is None assert project.labels.cell_info is None for raw, label in zip(project.raw_frames, project.label_frames): assert raw.frame is None assert label.frame is None
def test_get_labeled_array(): """ Test outlined label arrays to send to the front-end. """ project = models.Project.create(DummyLoader()) frame = 0 feature = 0 expected_frame = project.label_frames[frame].frame[..., feature] label_arr = project.get_labeled_array(frame, feature) assert label_arr.shape == (project.height, project.width) np.testing.assert_array_equal(label_arr[label_arr >= 0], expected_frame[label_arr >= 0]) np.testing.assert_array_equal(label_arr[label_arr < 0], -expected_frame[label_arr < 0])
def test_add_channel(client): project = models.Project.create(DummyLoader(raw=np.zeros((1, 1, 1, 1)))) npz = io.BytesIO() np.savez(npz, new_channel=np.ones((1, 1, 1, 1))) npz.seek(0) data = {'file': (npz, 'test.npz')} response = client.post(f'/api/raw/{project.token}', data=data, content_type='multipart/form-data') assert response.status_code == 200 assert response.json.get('numChannels') == 2 assert project.raw_array.shape == (1, 1, 1, 2) assert 0 in project.raw_array assert 1 in project.raw_array
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_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_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