From 672ec3d36072dc57dacef709c9f52934e108a876 Mon Sep 17 00:00:00 2001 From: Christopher Rhodes <christopher.rhodes@embl.de> Date: Thu, 5 Oct 2023 10:34:14 +0200 Subject: [PATCH] Keep all objects in dataframe and apply filter only inside zmask object loop --- .../chaeo/examples/batch_run_patches.py | 10 +++++++--- extensions/chaeo/tests/test_zstack.py | 6 +++++- extensions/chaeo/workflows.py | 3 ++- extensions/chaeo/zmask.py | 20 ++++++++++++++----- 4 files changed, 29 insertions(+), 10 deletions(-) diff --git a/extensions/chaeo/examples/batch_run_patches.py b/extensions/chaeo/examples/batch_run_patches.py index 291b5c52..09d2ee8b 100644 --- a/extensions/chaeo/examples/batch_run_patches.py +++ b/extensions/chaeo/examples/batch_run_patches.py @@ -4,6 +4,7 @@ import pandas as pd from extensions.chaeo.workflows import export_patches_from_multichannel_zstack from extensions.ilastik.models import IlastikPixelClassifierModel +from model_server.accessors import InMemoryDataAccessor, write_accessor_data_to_file if __name__ == '__main__': where_czi = Path( @@ -43,6 +44,9 @@ if __name__ == '__main__': pd.DataFrame(result['timer_results'], index=[0]).to_csv(where_output / 'timer_results.csv', **csv_args) csv_args = {'mode': 'a', 'header': False} # append to CSV from here on - - - + # export intermediate data if flagged + for k in result['interm'].keys(): + write_accessor_data_to_file( + where_output / k / (ff.stem + '.tif'), + InMemoryDataAccessor(result['interm'][k]) + ) diff --git a/extensions/chaeo/tests/test_zstack.py b/extensions/chaeo/tests/test_zstack.py index 3ce95969..f2394889 100644 --- a/extensions/chaeo/tests/test_zstack.py +++ b/extensions/chaeo/tests/test_zstack.py @@ -27,7 +27,7 @@ class TestZStackDerivedDataProducts(unittest.TestCase): # write_accessor_data_to_file(output_path / 'obmap.tif', self.obmap) def test_zmask_makes_correct_boxes(self, mask_type='boxes', **kwargs): - zmask, meta, df = build_zmask_from_object_mask( + zmask, meta, df, interm = build_zmask_from_object_mask( self.obmap.get_one_channel_data(0), self.stack.get_one_channel_data(0), mask_type=mask_type, @@ -51,6 +51,10 @@ class TestZStackDerivedDataProducts(unittest.TestCase): ar = meta[1]['info'].area self.assertGreaterEqual(sh[0] * sh[1], ar) + # assert dimensionality of intermediate data products + self.assertEqual(interm['label_map'].shape, zmask.shape[0:2]) + self.assertEqual(interm['argmax'].shape, zmask.shape[0:2]) + return zmask, meta def test_zmask_makes_correct_contours(self): diff --git a/extensions/chaeo/workflows.py b/extensions/chaeo/workflows.py index 997826ee..3afc7d26 100644 --- a/extensions/chaeo/workflows.py +++ b/extensions/chaeo/workflows.py @@ -49,7 +49,7 @@ def export_patches_from_multichannel_zstack( ti.click('threshold_pixel_mask') # make zmask - zmask, zmask_meta, df = build_zmask_from_object_mask( + zmask, zmask_meta, df, interm = build_zmask_from_object_mask( obmask.get_one_channel_data(pixel_class), stack.get_one_channel_data(zmask_channel), mask_type=mask_type, @@ -89,4 +89,5 @@ def export_patches_from_multichannel_zstack( 'success': True, 'timer_results': ti.events, 'dataframe': df, + 'interm': interm, } \ No newline at end of file diff --git a/extensions/chaeo/zmask.py b/extensions/chaeo/zmask.py index 8304b836..1996a641 100644 --- a/extensions/chaeo/zmask.py +++ b/extensions/chaeo/zmask.py @@ -30,6 +30,9 @@ def build_zmask_from_object_mask( contour: object's contour returned by skimage.measure.find_contours mask: mask of object in relative frame of (optionally) expanded bounding box pd.DataFrame: objects, including bounding, box information after filtering + Dict of intermediate image products: + label_map: np.ndarray (h x w) where each unique object has an integer label + argmax: np.ndarray (h x w x 1 x 1) z-index of highest intensity in zstack """ # validate inputs @@ -42,7 +45,7 @@ def build_zmask_from_object_mask( assert zstack.hw == obmask.hw # assign object labels and build object query - lamap = label(obmask.data[:, :, 0, 0]) + lamap = label(obmask.data[:, :, 0, 0]).astype('uint16') query_str = 'label > 0' # always true if filters is not None: for k in filters.keys(): @@ -52,7 +55,7 @@ def build_zmask_from_object_mask( query_str = query_str + f' & {k} > {vmin} & {k} < {vmax}' # build dataframe of objects, assign z index to each object - argmax = zstack.data.argmax(axis=3, keepdims=True)[:, :, 0, 0] + argmax = zstack.data.argmax(axis=3, keepdims=True)[:, :, 0, 0].astype('uint16') df = ( pd.DataFrame( regionprops_table( @@ -61,7 +64,6 @@ def build_zmask_from_object_mask( properties=('label', 'area', 'intensity_mean', 'solidity', 'bbox') ) ) - .query(query_str) .rename( columns={ 'bbox-0': 'y0', @@ -72,6 +74,8 @@ def build_zmask_from_object_mask( ) ) df['zi'] = df['intensity_mean'].round().astype('int') + df['keeper'] = False + df.loc[df.query(query_str).index, 'keeper'] = True # make an object map where label is replaced by focus position in stack and background is -1 lut = np.zeros(lamap.max() + 1) - 1 @@ -82,7 +86,7 @@ def build_zmask_from_object_mask( h, w, c, nz = zstack.shape meta = [] - for ob in df.itertuples(name='LabeledObject'): + for ob in df[df['keeper']].itertuples(name='LabeledObject'): y0 = max(ob.y0 - ebxy, 0) y1 = min(ob.y1 + ebxy, h - 1) x0 = max(ob.x0 - ebxy, 0) @@ -133,4 +137,10 @@ def build_zmask_from_object_mask( sl = bb['slice'] zi_st[sl] = 1 - return zi_st, meta, df \ No newline at end of file + # return intermediate image arrays + interm = { + 'label_map': lamap, + 'argmax': argmax, + } + + return zi_st, meta, df, interm \ No newline at end of file -- GitLab