def test_get_centered_cutout(): """Test getting a cutout of arbitrary size centered on the roi bounding box center. """ rng = np.random.default_rng(1234) image = rng.integers(100, size=(512, 512), dtype='int16') width = 10 height = 10 x0 = 100 y0 = 200 mask = np.zeros((height, width), dtype=bool) mask[2, 4] = True mask[3, 6] = True roi = OphysROI(roi_id=1, x0=x0, y0=y0, width=width, height=height, valid_roi=True, mask_matrix=mask) cutout_size = 128 cutout = roi.get_centered_cutout(image, cutout_size, cutout_size) np.testing.assert_array_equal( cutout, image[roi.bounding_box_center_y - cutout_size // 2:roi.bounding_box_center_y + cutout_size // 2, roi.bounding_box_center_x - cutout_size // 2:roi.bounding_box_center_x + cutout_size // 2])
def ophys_roi_list_fixture(): """ List of OphysROI """ output = [] # construct an image with a bunch of connected # ROIs full_field = np.zeros((64, 64), dtype=int) full_field[32:49, 32:40] = 1 full_field[20:34, 20:35] = 2 full_field[34:38, 20:35] = 3 full_field[32:37, 40:45] = 4 full_field[32:37, 45:50] = 5 full_field[10:17, 39:50] = 6 full_field[10:17, 37:39] = 7 full_field[5:10, 37:39] = 8 full_field[34:46, 34:37] = 9 for roi_id in range(1, 10, 1): valid = np.argwhere(full_field == roi_id) min_row = valid[:, 0].min() min_col = valid[:, 1].min() max_row = valid[:, 0].max() max_col = valid[:, 1].max() height = max_row-min_row+1 width = max_col-min_col+1 mask = np.zeros((height, width), dtype=bool) mask[valid[:, 0]-min_row, valid[:, 1]-min_col] = True roi = OphysROI(x0=int(min_col), width=int(width), y0=int(min_row), height=int(height), mask_matrix=mask, roi_id=roi_id, valid_roi=True) output.append(roi) return output
def test_roi_instantiation(ophys_plane_data_fixture): schema_dict = ophys_plane_data_fixture ct = 0 for meta_pair in schema_dict['coupled_planes']: pair = meta_pair['planes'] for plane in pair: for roi_args in plane['rois']: _ = OphysROI.from_schema_dict(roi_args) ct += 1 assert ct == 8
def test_get_roi_pixels(): """ Test method that maps a list of ROIs to a dict of pixels """ roi_list = [] expected_pixel_set = {} roi = OphysROI(x0=4, y0=17, width=5, height=3, mask_matrix=[[True, False, False, True, True], [False, False, False, True, False], [True, True, False, False, False]], roi_id=0, valid_roi=True) roi_list.append(roi) expected_pixel_set[0] = set([(4, 17), (7, 17), (8, 17), (7, 18), (4, 19), (5, 19)]) roi = OphysROI(x0=9, y0=7, width=2, height=5, mask_matrix=[[False, False], [True, False], [False, True], [False, False], [True, True]], roi_id=1, valid_roi=True) roi_list.append(roi) expected_pixel_set[1] = set([(9, 8), (10, 9), (9, 11), (10, 11)]) result = get_roi_pixels(roi_list) assert len(result) == 2 assert 0 in result assert 1 in result assert result[0] == expected_pixel_set[0] assert result[1] == expected_pixel_set[1]
def from_schema_dict(cls, schema_dict): """ Create an OphysPlane from a dict taken from the module's argschema Parameters ---------- schema_dict -- a dict codifying the plane, as read from argschema, i.e. { # start of ophys_experiment "ophys_experiment_id": ,# an int "motion_corrected_stack": , # path to h5 movie file "motion_border": { # border widths "x0": , # a float "x1": , # a float "y0": , # a float "y1": , # a float }, "rois": [ # list of dicts definining the ROIs for this experiment { # start of individual ROI "id": , # an int "x": , # an int "y": , # an int "width": , # an int "height": , # an int "valid_roi": , # boolean "mask_matrix": [[]] # 2-D array of booleans }, # end of individual ROI, { "id": , "x": , "y": , "width": , "height": , "valid_roi": , "mask_matrix": [[]] }, ... ] } """ roi_list = [] for roi in schema_dict['rois']: roi_list.append(OphysROI.from_schema_dict(roi)) max_path = None if 'maximum_projection_image_file' in schema_dict: max_path = schema_dict['maximum_projection_image_file'] return cls(experiment_id=schema_dict['ophys_experiment_id'], movie_path=schema_dict['motion_corrected_stack'], motion_border=schema_dict['motion_border'], roi_list=roi_list, max_projection_path=max_path)
def test_get_bound_box_cutout(): """Test retrieval of a cutout of the roi bounding box. """ rng = np.random.default_rng(1234) image = rng.integers(100, size=(512, 512), dtype='int16') width = 7 height = 5 x0 = 100 y0 = 200 mask = np.zeros((height, width), dtype=bool) mask[2, 4] = True mask[3, 6] = True roi = OphysROI(roi_id=1, x0=x0, y0=y0, width=width, height=height, valid_roi=True, mask_matrix=mask) cutout = roi.get_bounding_box_cutout(image) np.testing.assert_array_equal(cutout, image[y0:y0 + height, x0:x0 + width])
def test_roi_global_pixel_set(): width = 7 height = 5 mask = np.zeros((height, width), dtype=bool) mask[2, 4] = True mask[3, 6] = True roi = OphysROI(roi_id=1, x0=100, y0=200, width=width, height=height, valid_roi=True, mask_matrix=mask) assert roi.global_pixel_set == set([(202, 104), (203, 106)])
def example_ophys_roi_list_fixture(): rng = np.random.default_rng(6412439) roi_list = [] for ii in range(30): x0 = rng.integers(0, 25) y0 = rng.integers(0, 25) height = rng.integers(3, 7) width = rng.integers(3, 7) mask = rng.integers(0, 2, (height, width)).astype(bool) roi = OphysROI(x0=int(x0), y0=int(y0), height=int(height), width=int(width), mask_matrix=mask, roi_id=ii, valid_roi=True) roi_list.append(roi) return roi_list
def extract_roi_to_ophys_roi(roi: ExtractROI) -> OphysROI: """ Convert an ExtractROI to an equivalent OphysROI Parameters ---------- ExtractROI Returns ------- OphysROI """ new_roi = OphysROI(x0=int(roi['x']), y0=int(roi['y']), width=int(roi['width']), height=int(roi['height']), mask_matrix=roi['mask'], roi_id=int(roi['id']), valid_roi=roi['valid']) return new_roi
def test_add_roi_contour_to_img(alpha): """Test that add_roi_contour_to_img creates an image with the expected contours of the expected colors""" img = 100 * np.ones((64, 64, 3), dtype=int) height = 7 width = 12 mask = np.zeros((height, width), dtype=bool) mask[1, 5:7] = True mask[2, 4:8] = True mask[3, 3:9] = True mask[4, 2:10] = True mask[5, 3:9] = True bdry_pixels = set([(1, 5), (1, 6), (2, 4), (2, 7), (3, 3), (3, 8), (4, 2), (4, 9), (5, 3), (5, 4), (5, 5), (5, 6), (5, 7), (5, 8)]) roi = OphysROI(x0=20, width=width, y0=15, height=height, valid_roi=True, roi_id=0, mask_matrix=mask) color = (22, 33, 44) img = add_roi_contour_to_img(img, roi, color, alpha) for row in range(height): for col in range(width): for ic in range(3): if (row, col) not in bdry_pixels: assert img[15 + row, 20 + col, ic] == 100 else: expected = np.round(alpha * color[ic] + (1.0 - alpha) * 100).astype(int) assert img[15 + row, 20 + col, ic] == expected
def add_roi_contour_to_video(sub_video: np.ndarray, origin: Tuple[int, int], roi: ExtractROI, roi_color: Tuple[int, int, int]) -> np.ndarray: """ Add the contour of an ROI to a video Parameters ---------- sub_video: np.ndarray The video as an RGB movie. Shape is (n_t, nrows, ncols, 3) origin: Tuple[int, int] global (rowmin, colmin) of the video in sub_video roi: ExtractROI The parameters of this ROI are in global coordinates, which is why we need origin as an argument roi_color: Tuple[int, int, int] RGB color of the ROI contour Returns ------- sub_video: np.ndarray sub_video with the ROI contour Note: ----- While it does return a np.ndarray, this method will change sub_video in-place """ n_video_rows = sub_video.shape[1] n_video_cols = sub_video.shape[2] # construct an ROI object to get the contour mask # for us ophys_roi = OphysROI(roi_id=-1, x0=roi['x'], y0=roi['y'], width=roi['width'], height=roi['height'], valid_roi=False, mask_matrix=roi['mask']) contour_mask = ophys_roi.contour_mask for irow in range(contour_mask.shape[0]): row = irow+ophys_roi.y0-origin[0] if row < 0 or row >= n_video_rows: continue for icol in range(contour_mask.shape[1]): if not contour_mask[irow, icol]: continue col = icol+ophys_roi.x0-origin[1] if col < 0 or col >= n_video_cols: continue for i_color in range(3): sub_video[:, row, col, i_color] = roi_color[i_color] return sub_video
def test_roi_abut(): height = 6 width = 7 mask = np.zeros((height, width), dtype=bool) mask[1:5, 1:6] = True # overlapping roi0 = OphysROI(x0=22, y0=44, height=height, width=width, mask_matrix=mask, roi_id=0, valid_roi=True) roi1 = OphysROI(x0=23, y0=46, height=height, width=width, mask_matrix=mask, roi_id=1, valid_roi=True) assert rois_utils.do_rois_abut(roi0, roi1, pixel_distance=1.0) # just touching roi1 = OphysROI(x0=26, y0=48, height=height, width=width, mask_matrix=mask, roi_id=1, valid_roi=True) assert rois_utils.do_rois_abut(roi0, roi1, pixel_distance=1.0) roi1 = OphysROI(x0=27, y0=48, height=height, width=width, mask_matrix=mask, roi_id=1, valid_roi=True) assert not rois_utils.do_rois_abut(roi0, roi1, pixel_distance=1.0) # they are, however, just diagonally 1 pixel away # from each other assert rois_utils.do_rois_abut(roi0, roi1, pixel_distance=np.sqrt(2)) # gap of one pixel assert rois_utils.do_rois_abut(roi0, roi1, pixel_distance=2) roi1 = OphysROI(x0=28, y0=48, height=height, width=width, mask_matrix=mask, roi_id=1, valid_roi=True) assert not rois_utils.do_rois_abut(roi0, roi1, pixel_distance=2)
def test_find_overlapping_roi_pairs(): roi_list_0 = [] roi_list_1 = [] roi = OphysROI(x0=4, y0=5, width=4, height=3, mask_matrix=[[True, False, False, True], [False, True, True, False], [False, True, False, False]], roi_id=0, valid_roi=True) roi_list_0.append(roi) # photo-negative of roi_0 roi = OphysROI(x0=4, y0=5, width=4, height=3, mask_matrix=[[False, True, True, False], [True, False, False, True], [True, False, True, True]], roi_id=1, valid_roi=True) roi_list_1.append(roi) # intersects one point of roi_0 roi = OphysROI(x0=6, y0=6, width=2, height=3, mask_matrix=[[True, False], [True, True], [True, True]], roi_id=2, valid_roi=True) roi_list_1.append(roi) # no intersection with roi_0 roi = OphysROI(x0=6, y0=6, width=2, height=3, mask_matrix=[[False, False], [True, True], [True, True]], roi_id=3, valid_roi=True) roi_list_1.append(roi) # one corner overlaps with roi_2 and roi_3 roi = OphysROI(x0=7, y0=8, width=3, height=4, mask_matrix=[[True, True, False], [True, True, True], [True, True, True], [True, True, True]], roi_id=4, valid_roi=True) roi_list_0.append(roi) # no overlaps roi = OphysROI(x0=7, y0=8, width=3, height=4, mask_matrix=[[False, False, False], [True, True, True], [True, True, True], [True, True, True]], roi_id=5, valid_roi=True) roi_list_0.append(roi) overlap_list = find_overlapping_roi_pairs(roi_list_0, roi_list_1) assert len(overlap_list) == 3 assert (0, 2, 1 / 5, 1 / 5) in overlap_list assert (4, 3, 1 / 11, 1 / 4) in overlap_list assert (4, 2, 1 / 11, 1 / 5) in overlap_list
def test_get_img_thumbnails(x0, y0, x1, y1): """ Test that, when fed two ROIs that are very distant and do not overlap, get_img_thumbnails returns bounds (xmin, xmax), (ymin, ymax) bounds that cover both ROIs """ this_dir = pathlib.Path(__file__).parent.resolve() data_dir = this_dir / 'data/qc_plotting' roi0 = OphysROI(x0=x0, y0=y0, width=3, height=4, mask_matrix=[[False, False, False], [True, True, True], [True, True, True], [True, True, True]], roi_id=5, valid_roi=True) roi1 = OphysROI(x0=x1, y0=y1, width=3, height=4, mask_matrix=[[False, False, False], [True, True, True], [True, True, True], [True, True, True]], roi_id=5, valid_roi=True) img_fname = '1071738402_suite2p_maximum_projection.png' raw_img = PIL.Image.open(data_dir / img_fname, mode='r') n_rows = raw_img.size[0] n_cols = raw_img.size[1] max_img = np.array(raw_img).reshape(n_rows, n_cols) (img0, img1, (xmin, xmax), (ymin, ymax)) = get_img_thumbnails(roi0, roi1, max_img, max_img) mask = roi0.mask_matrix for ix in range(mask.shape[1]): xx = ix + roi0.x0 for iy in range(mask.shape[0]): yy = iy + roi0.y0 if mask[iy, ix]: assert yy >= ymin assert yy < ymax assert xx >= xmin assert xx < xmax mask = roi1.mask_matrix for ix in range(mask.shape[1]): xx = ix + roi1.x0 for iy in range(mask.shape[0]): yy = iy + roi1.y0 if mask[iy, ix]: assert yy >= ymin assert yy < ymax assert xx >= xmin assert xx < xmax assert xmin >= 0 assert ymin >= 0 assert xmax <= 512 assert ymax <= 512