From 60a3e66eb7efcdf8a2e12008cb745054a82f15ce Mon Sep 17 00:00:00 2001 From: Christopher Rhodes <christopher.rhodes@embl.de> Date: Sun, 22 Oct 2023 09:16:51 +0200 Subject: [PATCH] Expose image pixel scale to Czi accessor --- conf/testing.py | 1 + model_server/accessors.py | 21 +++++++++++++++++++-- tests/test_accessors.py | 6 +++++- 3 files changed, 25 insertions(+), 3 deletions(-) diff --git a/conf/testing.py b/conf/testing.py index f2df5040..3c52b95c 100644 --- a/conf/testing.py +++ b/conf/testing.py @@ -10,6 +10,7 @@ czifile = { 'h': 1274, 'c': 5, 'z': 1, + 'um_per_pixel': 1/3.9881, } filename = 'rgb.png' diff --git a/model_server/accessors.py b/model_server/accessors.py index b6e5da1f..caec8ddf 100644 --- a/model_server/accessors.py +++ b/model_server/accessors.py @@ -37,13 +37,19 @@ class GenericImageDataAccessor(ABC): if self._data.dtype == 'bool': return True elif self._data.dtype == 'uint8': - return np.all(np.unique(self._data) == [0, 255]) + unique = np.unique(self._data) + if unique.shape[0] == 2 and np.all(unique == [0, 255]): + return True return False def get_one_channel_data (self, channel: int): c = int(channel) return InMemoryDataAccessor(self.data[:, :, c:(c+1), :]) + @property + def pixel_scale_in_micrometers(self): + return None + @property def dtype(self): return self.data.dtype @@ -163,12 +169,23 @@ class CziImageFileAccessor(GenericImageFileAccessor): [cf.axes.index(ch) for ch in idx], [0, 1, 2, 3] ) - self._data = self.conform_data(yxcz.reshape(yxcz.shape[0:4])) def __del__(self): self.czifile.close() + @property + def pixel_scale_in_micrometers(self): + meta_sc_str = self.czifile.metadata(raw=False)['ImageDocument']['Metadata']['ImageScaling']['ImagePixelSize'] + meta_mags = self.czifile.metadata(raw=False)['ImageDocument']['Metadata']['ImageScaling']['ScalingComponent'] + sc_xy = list(map( + float, + meta_sc_str.split(',') + )) + assert sc_xy[0] == sc_xy[1] + mags = [float(m['Magnification']) for m in meta_mags] + return sc_xy[0] / np.product(mags) + def write_accessor_data_to_file(fpath: Path, accessor: GenericImageDataAccessor, mkdir=True) -> bool: if mkdir: diff --git a/tests/test_accessors.py b/tests/test_accessors.py index f7a4341d..1a465dfb 100644 --- a/tests/test_accessors.py +++ b/tests/test_accessors.py @@ -110,4 +110,8 @@ class TestCziImageFileAccess(unittest.TestCase): def test_read_zstack_mono_mask(self): acc = generate_file_accessor(monozstackmask['path']) - self.assertTrue(acc.is_mask()) \ No newline at end of file + self.assertTrue(acc.is_mask()) + + def test_read_in_pixel_scale_from_czi(self): + cf = CziImageFileAccessor(czifile['path']) + self.assertAlmostEqual(cf.pixel_scale_in_micrometers, czifile['um_per_pixel'], places=3) \ No newline at end of file -- GitLab