Browse Source

Merge branch 'master' of github.com:rbnvrw/nd2reader

master
Ruben Verweij 5 years ago
parent
commit
30b2ad9cd4
4 changed files with 33 additions and 18 deletions
  1. +0
    -11
      nd2reader/exceptions.py
  2. +11
    -4
      nd2reader/parser.py
  3. +17
    -3
      nd2reader/raw_metadata.py
  4. +5
    -0
      nd2reader/reader.py

+ 0
- 11
nd2reader/exceptions.py View File

@ -6,17 +6,6 @@ class InvalidVersionError(Exception):
""" """
pass pass
class NoImageError(Exception):
"""No image found.
Some apparent images in ND2s are just completely blank placeholders. These are used when the number of images per
cycle are unequal (e.g. if you take fluorescent images every 2 minutes, and bright field images every minute).
"""
pass
class EmptyFileError(Exception): class EmptyFileError(Exception):
"""This .nd2 file seems to be empty. """This .nd2 file seems to be empty.


+ 11
- 4
nd2reader/parser.py View File

@ -3,11 +3,12 @@ import struct
import array import array
import six import six
import warnings
from pims.base_frames import Frame from pims.base_frames import Frame
import numpy as np import numpy as np
from nd2reader.common import get_version, read_chunk from nd2reader.common import get_version, read_chunk
from nd2reader.exceptions import InvalidVersionError, NoImageError
from nd2reader.exceptions import InvalidVersionError
from nd2reader.label_map import LabelMap from nd2reader.label_map import LabelMap
from nd2reader.raw_metadata import RawMetadata from nd2reader.raw_metadata import RawMetadata
@ -72,7 +73,7 @@ class Parser(object):
try: try:
timestamp, image = self._get_raw_image_data(image_group_number, channel_offset, self.metadata["height"], timestamp, image = self._get_raw_image_data(image_group_number, channel_offset, self.metadata["height"],
self.metadata["width"]) self.metadata["width"])
except (TypeError, NoImageError):
except (TypeError):
return Frame([], frame_no=frame_number, metadata=self._get_frame_metadata()) return Frame([], frame_no=frame_number, metadata=self._get_frame_metadata())
else: else:
return image return image
@ -101,7 +102,7 @@ class Parser(object):
try: try:
timestamp, raw_image_data = self._get_raw_image_data(image_group_number, channel, timestamp, raw_image_data = self._get_raw_image_data(image_group_number, channel,
height, width) height, width)
except (TypeError, NoImageError):
except (TypeError):
return Frame([], frame_no=frame_number, metadata=self._get_frame_metadata()) return Frame([], frame_no=frame_number, metadata=self._get_frame_metadata())
else: else:
return raw_image_data return raw_image_data
@ -140,6 +141,7 @@ class Parser(object):
self._label_map = self._build_label_map() self._label_map = self._build_label_map()
self._raw_metadata = RawMetadata(self._fh, self._label_map) self._raw_metadata = RawMetadata(self._fh, self._label_map)
self.metadata = self._raw_metadata.__dict__ self.metadata = self._raw_metadata.__dict__
self.acquisition_times = self._raw_metadata.acquisition_times
def _build_label_map(self): def _build_label_map(self):
""" """
@ -283,7 +285,12 @@ class Parser(object):
if np.any(image_data): if np.any(image_data):
return timestamp, Frame(image_data, metadata=self._get_frame_metadata()) return timestamp, Frame(image_data, metadata=self._get_frame_metadata())
raise NoImageError
# If a blank "gap" image is encountered, generate an array of corresponding height and width to avoid
# errors with ND2-files with missing frames. Array is filled with nan to reflect that data is missing.
else:
empty_frame = np.full((height, width), np.nan)
warnings.warn('ND2 file contains gap frames which are represented by np.nan-filled arrays; to convert to zeros use e.g. np.nan_to_num(array)')
return timestamp, Frame(empty_frame, metadata=self._get_frame_metadata())
def _get_frame_metadata(self): def _get_frame_metadata(self):
"""Get the metadata for one frame """Get the metadata for one frame


+ 17
- 3
nd2reader/raw_metadata.py View File

@ -43,9 +43,10 @@ class RawMetadata(object):
"fields_of_view": self._parse_fields_of_view(), "fields_of_view": self._parse_fields_of_view(),
"frames": self._parse_frames(), "frames": self._parse_frames(),
"z_levels": self._parse_z_levels(), "z_levels": self._parse_z_levels(),
"z_coordinates": parse_if_not_none(self.z_data, self._parse_z_coordinates),
"total_images_per_channel": frames_per_channel, "total_images_per_channel": frames_per_channel,
"channels": self._parse_channels(), "channels": self._parse_channels(),
"pixel_microns": parse_if_not_none(self.image_calibration, self._parse_calibration),
"pixel_microns": parse_if_not_none(self.image_calibration, self._parse_calibration)
} }
self._set_default_if_not_empty('fields_of_view') self._set_default_if_not_empty('fields_of_view')
@ -161,6 +162,14 @@ class RawMetadata(object):
""" """
return self._parse_dimension(r""".*?Z\((\d+)\).*?""") return self._parse_dimension(r""".*?Z\((\d+)\).*?""")
def _parse_z_coordinates(self):
"""The coordinate in micron for all z planes.
Returns:
list: the z coordinates in micron
"""
return self.z_data.tolist()
def _parse_dimension_text(self): def _parse_dimension_text(self):
"""While there are metadata values that represent a lot of what we want to capture, they seem to be unreliable. """While there are metadata values that represent a lot of what we want to capture, they seem to be unreliable.
Sometimes certain elements don't exist, or change their data type randomly. However, the human-readable text Sometimes certain elements don't exist, or change their data type randomly. However, the human-readable text
@ -193,7 +202,7 @@ class RawMetadata(object):
if not match: if not match:
return [] return []
count = int(match.group(1)) count = int(match.group(1))
return list(range(count))
return range(count)
def _parse_total_images_per_channel(self): def _parse_total_images_per_channel(self):
"""The total number of images per channel. """The total number of images per channel.
@ -492,7 +501,12 @@ class RawMetadata(object):
Returns: Returns:
dict: z_data dict: z_data
""" """
return read_array(self._fh, 'double', self._label_map.z_data)
try:
return read_array(self._fh, 'double', self._label_map.z_data)
except ValueError:
# Depending on the file format/exact settings, this value is
# sometimes saved as float instead of double
return read_array(self._fh, 'float', self._label_map.z_data)
@property @property
def roi_metadata(self): def roi_metadata(self):


+ 5
- 0
nd2reader/reader.py View File

@ -52,6 +52,11 @@ class ND2Reader(FramesSequenceND):
except KeyError: except KeyError:
return 0 return 0
def get_frame_2D(self, c=0, t=0, z=0, x=0, y=0, v=0):
"""Fallback function for backwards compatibility
"""
return get_frame_vczyx(v=v, c=c, t=t, z=z, x=x, y=y)
def get_frame_vczyx(self, v=None, c=None, t=None, z=None, x=None, y=None): def get_frame_vczyx(self, v=None, c=None, t=None, z=None, x=None, y=None):
x = self.metadata["width"] if x <= 0 else x x = self.metadata["width"] if x <= 0 else x
y = self.metadata["height"] if y <= 0 else y y = self.metadata["height"] if y <= 0 else y


Loading…
Cancel
Save