diff --git a/model_server/base/accessors.py b/model_server/base/accessors.py index e4c4a16444ccafcb63172a90cd1bce3169607f8b..3bebea375a4b94a0e91dd2143e872c64d9d92ca6 100644 --- a/model_server/base/accessors.py +++ b/model_server/base/accessors.py @@ -77,14 +77,14 @@ class GenericImageDataAccessor(ABC): return self.data.sum(axis=(0, 1, 2)) @property - def data_xy(self) -> np.ndarray: + def data_yx(self) -> np.ndarray: if not self.chroma == 1 and self.nz == 1: raise InvalidDataShape('Can only return XY array from accessors with a single channel and single z-level') else: return self.data[:, :, 0, 0] @property - def data_xyz(self) -> np.ndarray: + def data_yxz(self) -> np.ndarray: if not self.chroma == 1: raise InvalidDataShape('Can only return XYZ array from accessors with a single channel') else: @@ -428,6 +428,20 @@ class PatchStack(InMemoryDataAccessor): def count(self): return self.shape_dict['P'] + @property + def data_yx(self) -> np.ndarray: + if not self.chroma == 1 and self.nz == 1: + raise InvalidDataShape('Can only return XY array from accessors with a single channel and single z-level') + else: + return self.data[:, :, :, 0, 0] + + @property + def data_yxz(self) -> np.ndarray: + if not self.chroma == 1: + raise InvalidDataShape('Can only return XYZ array from accessors with a single channel') + else: + return self.data[:, :, :, 0, :] + def export_pyxcz(self, fpath: Path): tzcyx = np.moveaxis( self.pyxcz, # yxcz diff --git a/model_server/base/roiset.py b/model_server/base/roiset.py index 186ebb28026f0bc844a563b1727ae1602641fbf7..235327076ca18ed9dcc863374aa835141d3d1e0b 100644 --- a/model_server/base/roiset.py +++ b/model_server/base/roiset.py @@ -75,7 +75,7 @@ def get_label_ids(acc_seg_mask: GenericImageDataAccessor, allow_3d=False, connec """ if allow_3d and connect_3d: nda_la = label( - acc_seg_mask.data_xyz, + acc_seg_mask.data_yxz, connectivity=3, ).astype('uint16') return InMemoryDataAccessor(np.expand_dims(nda_la, 2)) @@ -84,7 +84,7 @@ def get_label_ids(acc_seg_mask: GenericImageDataAccessor, allow_3d=False, connec la_3d = np.zeros((*acc_seg_mask.hw, 1, acc_seg_mask.nz), dtype='uint16') for zi in range(0, acc_seg_mask.nz): la_2d = label( - acc_seg_mask.data_xyz[:, :, zi], + acc_seg_mask.data_yxz[:, :, zi], connectivity=2, ).astype('uint16') la_2d[la_2d > 0] = la_2d[la_2d > 0] + nla @@ -94,7 +94,7 @@ def get_label_ids(acc_seg_mask: GenericImageDataAccessor, allow_3d=False, connec else: return InMemoryDataAccessor( label( - acc_seg_mask.get_mip().data_xy, + acc_seg_mask.get_mip().data_yx, connectivity=1, ).astype('uint16') ) @@ -234,10 +234,10 @@ def make_df_from_object_ids(acc_raw, acc_obj_ids, expand_box_by, deproject_chann ) acc_raw.get_mono(deproject_channel) - zi_map = acc_raw.get_mono(deproject_channel).get_z_argmax().data_xy.astype('uint16') + zi_map = acc_raw.get_mono(deproject_channel).get_z_argmax().data_yx.astype('uint16') assert len(zi_map.shape) == 2 df = pd.DataFrame(regionprops_table( - acc_obj_ids.data_xy, + acc_obj_ids.data_yx, intensity_image=zi_map, properties=('label', 'area', 'intensity_mean', 'bbox') )).rename(columns={'bbox-0': 'y0', 'bbox-1': 'x0', 'bbox-2': 'y1', 'bbox-3': 'x1'}) @@ -245,7 +245,7 @@ def make_df_from_object_ids(acc_raw, acc_obj_ids, expand_box_by, deproject_chann else: # objects' z-coordinates come from arg of max count in object identities map df = pd.DataFrame(regionprops_table( - acc_obj_ids.data_xyz, + acc_obj_ids.data_yxz, properties=('label', 'area', 'bbox') )).rename(columns={ 'bbox-0': 'y0', 'bbox-1': 'x0', 'bbox-2': 'z0', 'bbox-3': 'y1', 'bbox-4': 'x1', 'bbox-5': 'z1' @@ -260,7 +260,7 @@ def make_df_from_object_ids(acc_raw, acc_obj_ids, expand_box_by, deproject_chann def _make_binary_mask(r): acc = InMemoryDataAccessor(acc_obj_ids.data == r.label) - cropped = acc.get_mono(0, mip=True).crop_hw((r.y0, r.x0, (r.y1 - r.y0), (r.x1 - r.x0))).data_xy + cropped = acc.get_mono(0, mip=True).crop_hw((r.y0, r.x0, (r.y1 - r.y0), (r.x1 - r.x0))).data_yx return cropped df['binary_mask'] = df.apply( @@ -743,7 +743,7 @@ class RoiSet(object): if white_channel: assert white_channel < raw.chroma - mono = raw.get_mono(white_channel).data_xyz + mono = raw.get_mono(white_channel).data_yxz stack = np.stack([mono, mono, mono], axis=2) else: stack = np.zeros([*raw.shape[0:2], 3, raw.shape[3]], dtype=raw.dtype) @@ -756,7 +756,7 @@ class RoiSet(object): stack[:, :, ii, :] = safe_add( stack[:, :, ii, :], # either black or grayscale channel rgb_overlay_weights[ii], - raw.get_mono(ci).data_xyz + raw.get_mono(ci).data_yxz ) else: if white_channel is not None: # interpret as just a single channel @@ -771,7 +771,7 @@ class RoiSet(object): annotate_rgb = True break if annotate_rgb: # make RGB patches anyway to include annotation color - mono = raw.get_mono(white_channel).data_xyz + mono = raw.get_mono(white_channel).data_yxz stack = np.stack([mono, mono, mono], axis=2) else: # make monochrome patches stack = raw.get_mono(white_channel).data @@ -1057,7 +1057,7 @@ class RoiSet(object): try: ma_acc = generate_file_accessor(pa_masks / fname) assert ma_acc.chroma == 1 and ma_acc.nz == 1 - mask_data = ma_acc.data_xy / ma_acc.dtype_max + mask_data = ma_acc.data_yx / ma_acc.dtype_max return mask_data except Exception as e: raise DeserializeRoiSetError(e) @@ -1192,8 +1192,8 @@ class IntensityThresholdInstanceMaskSegmentationModel(InstanceMaskSegmentationMo ) -> GenericImageDataAccessor: labels = get_label_ids(mask) df = pd.DataFrame(regionprops_table( - labels.data_xyz, - intensity_image=img.get_mono(self.channel).data_xyz, + labels.data_yxz, + intensity_image=img.get_mono(self.channel).data_yxz, properties=('label', 'area', 'intensity_mean', 'bbox') )) df['intensity_mean'] > self.tr diff --git a/tests/base/test_accessors.py b/tests/base/test_accessors.py index 1df5863c71f1d157d612e74ba83a822632d9f221..cfa929e33b04c59e89f578b5f72b9a6ac9f95a97 100644 --- a/tests/base/test_accessors.py +++ b/tests/base/test_accessors.py @@ -233,7 +233,6 @@ class TestPatchStackAccessor(unittest.TestCase): self.assertEqual(acc.pyxcz.shape, (n, h, w, 1, 1)) return acc - def test_make_patch_stack_from_file(self): h = data['monozstackmask']['h'] w = data['monozstackmask']['w'] @@ -301,6 +300,8 @@ class TestPatchStackAccessor(unittest.TestCase): self.assertEqual(acc.count, n) self.assertEqual(acc.pczyx.shape, (n, nc, nz, h, w)) self.assertEqual(acc.hw, (h, w)) + self.assertEqual(acc.get_mono(channel=0).data_yxz.shape, (n, h, w, nz)) + self.assertEqual(acc.get_mono(channel=0, mip=True).data_yx.shape, (n, h, w)) return acc def test_get_one_channel(self): diff --git a/tests/base/test_roiset.py b/tests/base/test_roiset.py index b8e433e827304421d26ada0b8f9c5cbe1b037c32..2152ade5f108f92bde3015355440cc019282caa0 100644 --- a/tests/base/test_roiset.py +++ b/tests/base/test_roiset.py @@ -643,7 +643,7 @@ class TestRoiSetObjectDetection(unittest.TestCase): from skimage.measure import label, regionprops, regionprops_table mask = self.seg_mask_3d - labels = label(mask.data_xyz, connectivity=3) + labels = label(mask.data_yxz, connectivity=3) table = pd.DataFrame( regionprops_table(labels) ).rename(