diff --git a/model_server/base/accessors.py b/model_server/base/accessors.py index b0df05873d982be7b9578bebdab47ad5dd13b1c8..8cba54bcb199ece41610aa5dd138df5eda93ec27 100644 --- a/model_server/base/accessors.py +++ b/model_server/base/accessors.py @@ -292,6 +292,22 @@ class PatchStack(InMemoryDataAccessor): def count(self): return self.shape_dict['P'] + def export_pyxcz(self, fpath: Path): + tzcyx = np.moveaxis( + self.pyxcz, # yxcz + [0, 4, 3, 1, 2], + [0, 1, 2, 3, 4] + ) + + if self.is_mask(): + if self.dtype == 'bool': + data = (tzcyx * 255).astype('uint8') + else: + data = tzcyx.astype('uint8') + tifffile.imwrite(fpath, data, imagej=True) + else: + tifffile.imwrite(fpath, tzcyx, imagej=True) + @property def shape_dict(self): return dict(zip(('P', 'Y', 'X', 'C', 'Z'), self.data.shape)) @@ -321,16 +337,32 @@ class PatchStack(InMemoryDataAccessor): return dict(zip(('P', 'Y', 'X', 'C', 'Z'), self.data.shape)) -def make_patch_stack_from_file(fpath): # interpret z-dimension as patch position +def make_patch_stack_from_file(fpath): # interpret t-dimension as patch position if not Path(fpath).exists(): raise FileNotFoundError(f'Could not find {fpath}') - pyxc = np.moveaxis( - generate_file_accessor(fpath).data, # yxcz - [0, 1, 2, 3], - [1, 2, 3, 0] + try: + tf = tifffile.TiffFile(fpath) + except Exception: + raise FileAccessorError(f'Unable to access data in {fpath}') + + if len(tf.series) != 1: + raise DataShapeError(f'Expect only one series in {fpath}') + + se = tf.series[0] + + axs = [a for a in se.axes if a in [*'TZCYX']] + sd = dict(zip(axs, se.shape)) + for a in [*'TZC']: + if a not in axs: + sd[a] = 1 + tzcyx = se.asarray().reshape([sd[k] for k in [*'TZCYX']]) + + pyxcz = np.moveaxis( + tzcyx, + [0, 3, 4, 2, 1], + [0, 1, 2, 3, 4], ) - pyxcz = np.expand_dims(pyxc, axis=3) return PatchStack(pyxcz) diff --git a/tests/test_accessors.py b/tests/test_accessors.py index fc94c6fb7b5c3fa42e841cb4650ac94c83238d0e..d2ca777c988c0101d8f7f4efb09611f4fe7a71f7 100644 --- a/tests/test_accessors.py +++ b/tests/test_accessors.py @@ -198,4 +198,11 @@ 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)) - return acc \ No newline at end of file + return acc + + def test_export_pczyx_patch_hyperstack(self): + acc = self.test_pczyx() + fp = output_path / 'patch_hyperstack.tif' + acc.export_pyxcz(fp) + acc2 = make_patch_stack_from_file(fp) + self.assertEqual(acc.shape, acc2.shape) \ No newline at end of file