From 8733b366d8ec482d8d5a52aae4193b0036abd81f Mon Sep 17 00:00:00 2001 From: Jim Rybarski Date: Sat, 30 May 2015 22:35:09 +0000 Subject: [PATCH] #39 --- nd2reader/__init__.py | 10 ++--- nd2reader/parser.py | 95 +++++++++++++++++++++---------------------- 2 files changed, 52 insertions(+), 53 deletions(-) diff --git a/nd2reader/__init__.py b/nd2reader/__init__.py index 75c0741..efad3a9 100644 --- a/nd2reader/__init__.py +++ b/nd2reader/__init__.py @@ -73,13 +73,13 @@ class Nd2(Nd2Parser): return image def _calculate_field_of_view(self, frame_number): - return (frame_number - (frame_number % (self._channel_count + self._z_level_count))) % self._field_of_view_count + return (frame_number - (frame_number % (len(self.channels) + len(self.z_levels))) % len(self.fields_of_view) def _calculate_channel(self, frame_number): - pass + return self._channels[frame_number % self._channel_count] def _calculate_z_level(self, frame_number): - pass + return self._z_levels[] @property def image_sets(self): @@ -119,9 +119,9 @@ class Nd2(Nd2Parser): :rtype: nd2reader.model.Image() or None """ - image_set_number = self._calculate_image_group_number(time_index, field_of_view, z_level) + image_group_number = self._calculate_image_group_number(time_index, field_of_view, z_level) try: - timestamp, raw_image_data = self._get_raw_image_data(image_set_number, self._channel_offset[channel_name]) + timestamp, raw_image_data = self._get_raw_image_data(image_group_number, self._channel_offset[channel_name]) image = Image(timestamp, raw_image_data, field_of_view, channel_name, z_level, self.height, self.width) except TypeError: return None diff --git a/nd2reader/parser.py b/nd2reader/parser.py index 58914a3..2758879 100644 --- a/nd2reader/parser.py +++ b/nd2reader/parser.py @@ -21,13 +21,18 @@ class Nd2Parser(object): def __init__(self, filename): self._filename = filename self._fh = None + self._channels = None + self._channel_count = None self._chunk_map_start_location = None self._cursor_position = 0 self._dimension_text = None + self._fields_of_view = None self._label_map = {} self.metadata = {} self._read_map() + self._time_indexes = None self._parse_metadata() + self._z_levels = None @property def _file_handle(self): @@ -93,7 +98,7 @@ class Nd2Parser(object): return self._dimension_text @property - def _channels(self): + def channels(self): """ These are labels created by the NIS Elements user. Typically they may a short description of the filter cube used (e.g. "bright field", "GFP", etc.) @@ -101,19 +106,21 @@ class Nd2Parser(object): :rtype: str """ - metadata = self.metadata['ImageMetadataSeq']['SLxPictureMetadata']['sPicturePlanes'] - try: - validity = self.metadata['ImageMetadata']['SLxExperiment']['ppNextLevelEx'][''][0]['ppNextLevelEx'][''][0]['pItemValid'] - except KeyError: - # If none of the channels have been deleted, there is no validity list, so we just make one - validity = [True for _ in metadata] - # Channel information is contained in dictionaries with the keys a0, a1...an where the number - # indicates the order in which the channel is stored. So by sorting the dicts alphabetically - # we get the correct order. - for (label, chan), valid in zip(sorted(metadata['sPlaneNew'].items()), validity): - if not valid: - continue - yield chan['sDescription'] + if not self._channels: + metadata = self.metadata['ImageMetadataSeq']['SLxPictureMetadata']['sPicturePlanes'] + try: + validity = self.metadata['ImageMetadata']['SLxExperiment']['ppNextLevelEx'][''][0]['ppNextLevelEx'][''][0]['pItemValid'] + except KeyError: + # If none of the channels have been deleted, there is no validity list, so we just make one + validity = [True for _ in metadata] + # Channel information is contained in dictionaries with the keys a0, a1...an where the number + # indicates the order in which the channel is stored. So by sorting the dicts alphabetically + # we get the correct order. + for (label, chan), valid in zip(sorted(metadata['sPlaneNew'].items()), validity): + if not valid: + continue + self._channels.append(chan['sDescription']) + return self._channels def _calculate_image_group_number(self, time_index, fov, z_level): """ @@ -126,7 +133,7 @@ class Nd2Parser(object): :rtype: int """ - return time_index * self._field_of_view_count * self._z_level_count + (fov * self._z_level_count + z_level) + return time_index * len(self.fields_of_view) * len(self.z_levels) + (fov * len(self.z_levels) + z_level) @property def _channel_offset(self): @@ -167,24 +174,28 @@ class Nd2Parser(object): return absolute_start_12 if absolute_start_12 else absolute_start_24 raise ValueError("This ND2 has no recorded start time. This is probably a bug.") + def _parse_dimension_text(self, pattern): + try: + count = int(re.match(pattern, self._dimensions).group(1)) + except AttributeError: + return [0] + else: + return range(count) + @property - def _channel_count(self): + def channel_count(self): """ The number of different channels used, including bright field. :rtype: int """ - pattern = r""".*?λ\((\d+)\).*?""" - try: - count = int(re.match(pattern, self._dimensions).group(1)) - except AttributeError: - return 1 - else: - return count + if self._channel_count is None: + self._channel_count = self._parse_dimension_text(r""".*?λ\((\d+)\).*?""") + return self._channel_count @property - def _field_of_view_count(self): + def fields_of_view(self): """ The metadata contains information about fields of view, but it contains it even if some fields of view were cropped. We can't find anything that states which fields of view are actually @@ -194,45 +205,33 @@ class Nd2Parser(object): :rtype: int """ - pattern = r""".*?XY\((\d+)\).*?""" - try: - count = int(re.match(pattern, self._dimensions).group(1)) - except AttributeError: - return 1 - else: - return count + if self._fields_of_view is None: + self._fields_of_view = self._parse_dimension_text(r""".*?XY\((\d+)\).*?""") + return self._fields_of_view @property - def _time_index_count(self): + def time_indexes(self): """ The number of cycles. :rtype: int """ - pattern = r""".*?T'\((\d+)\).*?""" - try: - count = int(re.match(pattern, self._dimensions).group(1)) - except AttributeError: - return 1 - else: - return count + if self._time_indexes is None: + self._time_indexes = self._parse_dimension_text(r""".*?T'\((\d+)\).*?""") + return self._time_indexes @property - def _z_level_count(self): + def z_levels(self): """ - The number of different levels in the Z-plane. + The different levels in the Z-plane. Just a sequence from 0 to n. :rtype: int """ - pattern = r""".*?Z\((\d+)\).*?""" - try: - count = int(re.match(pattern, self._dimensions).group(1)) - except AttributeError: - return 1 - else: - return count + if self._z_levels is None: + self._z_levels = self._parse_dimension_text(r""".*?Z\((\d+)\).*?""") + return self._z_levels @property def _image_count(self):