diff --git a/functional_tests/FYLM141111001.py b/functional_tests/FYLM141111001.py index 63e9196..8b78e58 100644 --- a/functional_tests/FYLM141111001.py +++ b/functional_tests/FYLM141111001.py @@ -39,6 +39,9 @@ class FYLM141111Tests(unittest.TestCase): def test_z_levels(self): self.assertTupleEqual(tuple(self.nd2.z_levels), (0, 1, 2)) + def test_pixel_size(self): + self.assertGreater(self.nd2.pixel_microns, 0.0) + def test_image(self): image = self.nd2[14] self.assertEqual(image.field_of_view, 2) diff --git a/functional_tests/monocycle.py b/functional_tests/monocycle.py index 0c1b79d..fbd1904 100644 --- a/functional_tests/monocycle.py +++ b/functional_tests/monocycle.py @@ -17,6 +17,9 @@ class Monocycle1Tests(unittest.TestCase): def test_channels(self): self.assertListEqual(self.nd2.channels, ['Cy3Narrow', 'TxRed-modified', 'FITC', 'DAPI']) + def test_pixel_size(self): + self.assertGreater(self.nd2.pixel_microns, 0.0) + def test_select(self): manual_images = [] for _, image in zip(range(20), self.nd2): @@ -78,6 +81,9 @@ class Monocycle2Tests(unittest.TestCase): def tearDown(self): self.nd2.close() + def test_pixel_size(self): + self.assertGreater(self.nd2.pixel_microns, 0.0) + def test_select(self): manual_images = [] for _, image in zip(range(20), self.nd2): diff --git a/functional_tests/single.py b/functional_tests/single.py index 5d68b57..773080b 100644 --- a/functional_tests/single.py +++ b/functional_tests/single.py @@ -28,6 +28,9 @@ class SingleTests(unittest.TestCase): def test_channels(self): self.assertEqual(self.nd2.channels, ['Quad Band 2']) + def test_pixel_size(self): + self.assertGreater(self.nd2.pixel_microns, 0.0) + def test_actual_length(self): count = 0 for image in self.nd2: diff --git a/nd2reader/main.py b/nd2reader/main.py index 627b020..e50d1f0 100644 --- a/nd2reader/main.py +++ b/nd2reader/main.py @@ -170,6 +170,16 @@ class Nd2(object): """ return self._metadata.date + @property + def pixel_microns(self): + """ + The width of a pixel in microns. Note that the user can override this in NIS Elements so it may not reflect reality. + + :rtype: float + + """ + return self._metadata.pixel_microns + def get_image(self, frame_number, field_of_view, channel_name, z_level): """ Attempts to return the image with the unique combination of given attributes. None will be returned if a match diff --git a/nd2reader/model/metadata.py b/nd2reader/model/metadata.py index 080efad..135d938 100644 --- a/nd2reader/model/metadata.py +++ b/nd2reader/model/metadata.py @@ -3,7 +3,7 @@ import six class Metadata(object): """ A simple container for ND2 metadata. """ - def __init__(self, height, width, channels, date, fields_of_view, frames, z_levels, total_images_per_channel): + def __init__(self, height, width, channels, date, fields_of_view, frames, z_levels, total_images_per_channel, pixel_microns): self._height = height self._width = width self._channels = channels @@ -12,6 +12,7 @@ class Metadata(object): self._frames = frames self._z_levels = z_levels self._total_images_per_channel = total_images_per_channel + self._pixel_microns = pixel_microns @property def height(self): @@ -97,6 +98,16 @@ class Metadata(object): """ return self._total_images_per_channel + @property + def pixel_microns(self): + """ + The width of a pixel in microns. + + :rtype: float + + """ + return self._pixel_microns + class CameraSettings(object): """ Contains some basic information about a physical camera and its settings. """ diff --git a/nd2reader/parser/v3.py b/nd2reader/parser/v3.py index f05a141..19c02b8 100644 --- a/nd2reader/parser/v3.py +++ b/nd2reader/parser/v3.py @@ -174,7 +174,8 @@ class V3Parser(BaseParser): z_levels = self._parse_z_levels(self.raw_metadata) total_images_per_channel = self._parse_total_images_per_channel(self.raw_metadata) channels = self._parse_channels(self.raw_metadata) - self.metadata = Metadata(height, width, channels, date, fields_of_view, frames, z_levels, total_images_per_channel) + pixel_microns = self.raw_metadata.image_calibration.get(six.b('SLxCalibration'), {}).get(six.b('dCalibration')) + self.metadata = Metadata(height, width, channels, date, fields_of_view, frames, z_levels, total_images_per_channel, pixel_microns) def _parse_camera_settings(self): """