Browse Source

Added parsing for basic ROI metadata.

zolfa-add_slices_loading
Ruben Verweij 8 years ago
parent
commit
6f800c49f6
4 changed files with 92 additions and 2 deletions
  1. +1
    -0
      nd2reader/main.py
  2. +63
    -0
      nd2reader/model/roi.py
  3. +1
    -0
      nd2reader/parser/base.py
  4. +27
    -2
      nd2reader/parser/v3.py

+ 1
- 0
nd2reader/main.py View File

@ -14,6 +14,7 @@ class Nd2(object):
major_version, minor_version = get_version(self._fh) major_version, minor_version = get_version(self._fh)
self._parser = get_parser(self._fh, major_version, minor_version) self._parser = get_parser(self._fh, major_version, minor_version)
self._metadata = self._parser.metadata self._metadata = self._parser.metadata
self._roi_metadata = self._parser.roi_metadata
def __repr__(self): def __repr__(self):
return "\n".join(["<ND2 %s>" % self._filename, return "\n".join(["<ND2 %s>" % self._filename,


+ 63
- 0
nd2reader/model/roi.py View File

@ -0,0 +1,63 @@
import six
import numpy as np
class Roi(object):
"""
A ND2 ROI representation.
Coordinates are the center coordinates of the ROI in (x, y, z) order.
Sizes are the sizes of the ROI in (x, y, z) order.
Shapes are represented by numbers, defined by constants in this class.
All these properties can be set for multiple timepoints.
"""
SHAPE_RECTANGLE = 3
SHAPE_CIRCLE = 9
def __init__(self, raw_roi_dict):
self.timepoints = []
self.positions = []
self.sizes = []
self.shapes = []
self._extract_vect_anims(raw_roi_dict)
def _extract_vect_anims(self, raw_roi_dict):
"""
Extract the vector animation parameters from the ROI.
This includes the position and size at the given timepoints.
:param raw_roi_dict:
:return:
"""
number_of_timepoints = raw_roi_dict[six.b('m_vectAnimParams_Size')]
for i in range(number_of_timepoints):
shape = raw_roi_dict[six.b('m_sInfo')][six.b('m_uiShapeType')]
self._parse_vect_anim(raw_roi_dict[six.b('m_vectAnimParams_%d') % i], shape)
# convert to NumPy arrays
self.timepoints = np.array(self.timepoints, dtype=np.float)
self.positions = np.array(self.positions, dtype=np.float)
self.sizes = np.array(self.sizes, dtype=np.float)
self.shapes = np.array(self.shapes, dtype=np.uint)
def _parse_vect_anim(self, animation_dict, shape):
"""
Parses a ROI vector animation object and adds it to the global list of timepoints and positions.
:param animation_dict:
:return:
"""
self.timepoints.append(animation_dict[six.b('m_dTimeMs')])
self.positions.append((animation_dict[six.b('m_dCenterX')],
animation_dict[six.b('m_dCenterY')],
animation_dict[six.b('m_dCenterZ')]))
size_dict = animation_dict[six.b('m_sBoxShape')]
self.sizes.append((size_dict[six.b('m_dSizeX')],
size_dict[six.b('m_dSizeY')],
size_dict[six.b('m_dSizeZ')]))
self.shapes.append(shape)
def is_circle(self, timepoint_id=0):
return self.shapes[timepoint_id] == self.SHAPE_CIRCLE
def is_rectangle(self, timepoint_id=0):
return self.shapes[timepoint_id] == self.SHAPE_RECTANGLE

+ 1
- 0
nd2reader/parser/base.py View File

@ -6,6 +6,7 @@ class BaseParser(object):
self._fh = fh self._fh = fh
self.camera_metadata = None self.camera_metadata = None
self.metadata = None self.metadata = None
self.roi_metadata = None
@abstractproperty @abstractproperty
def driver(self): def driver(self):


+ 27
- 2
nd2reader/parser/v3.py View File

@ -3,6 +3,7 @@
from datetime import datetime from datetime import datetime
from nd2reader.model.metadata import Metadata from nd2reader.model.metadata import Metadata
from nd2reader.model.label import LabelMap from nd2reader.model.label import LabelMap
from nd2reader.model.roi import Roi
from nd2reader.parser.base import BaseParser from nd2reader.parser.base import BaseParser
from nd2reader.driver.v3 import V3Driver from nd2reader.driver.v3 import V3Driver
from nd2reader.common.v3 import read_chunk, read_array, read_metadata from nd2reader.common.v3 import read_chunk, read_array, read_metadata
@ -18,6 +19,7 @@ def ignore_missing(func):
return func(*args, **kwargs) return func(*args, **kwargs)
except: except:
return None return None
return wrapper return wrapper
@ -142,6 +144,7 @@ class V3Parser(BaseParser):
self._label_map = self._build_label_map() self._label_map = self._build_label_map()
self.raw_metadata = V3RawMetadata(self._fh, self._label_map) self.raw_metadata = V3RawMetadata(self._fh, self._label_map)
self._parse_metadata() self._parse_metadata()
self._parse_roi_metadata()
@property @property
def driver(self): def driver(self):
@ -165,7 +168,8 @@ class V3Parser(BaseParser):
total_images_per_channel = self._parse_total_images_per_channel(self.raw_metadata) total_images_per_channel = self._parse_total_images_per_channel(self.raw_metadata)
channels = self._parse_channels(self.raw_metadata) channels = self._parse_channels(self.raw_metadata)
pixel_microns = self.raw_metadata.image_calibration.get(six.b('SLxCalibration'), {}).get(six.b('dCalibration')) 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)
self.metadata = Metadata(height, width, channels, date, fields_of_view, frames, z_levels,
total_images_per_channel, pixel_microns)
def _parse_date(self, raw_metadata): def _parse_date(self, raw_metadata):
""" """
@ -205,7 +209,8 @@ class V3Parser(BaseParser):
channels = [] channels = []
metadata = raw_metadata.image_metadata_sequence[six.b('SLxPictureMetadata')][six.b('sPicturePlanes')] metadata = raw_metadata.image_metadata_sequence[six.b('SLxPictureMetadata')][six.b('sPicturePlanes')]
try: try:
validity = raw_metadata.image_metadata[six.b('SLxExperiment')][six.b('ppNextLevelEx')][six.b('')][0][six.b('ppNextLevelEx')][six.b('')][0][six.b('pItemValid')]
validity = raw_metadata.image_metadata[six.b('SLxExperiment')][six.b('ppNextLevelEx')][six.b('')][0][
six.b('ppNextLevelEx')][six.b('')][0][six.b('pItemValid')]
except (KeyError, TypeError): except (KeyError, TypeError):
# If none of the channels have been deleted, there is no validity list, so we just make one # If none of the channels have been deleted, there is no validity list, so we just make one
validity = [True for _ in metadata] validity = [True for _ in metadata]
@ -303,6 +308,26 @@ class V3Parser(BaseParser):
""" """
return raw_metadata.image_attributes[six.b('SLxImageAttributes')][six.b('uiSequenceCount')] return raw_metadata.image_attributes[six.b('SLxImageAttributes')][six.b('uiSequenceCount')]
def _parse_roi_metadata(self):
"""
Parse the raw ROI metadata.
:return:
"""
if not six.b('RoiMetadata_v1') in self.raw_metadata.roi_metadata:
self.roi_metadata = None
return
raw_roi_data = self.raw_metadata.roi_metadata[six.b('RoiMetadata_v1')]
number_of_rois = raw_roi_data[six.b('m_vectGlobal_Size')]
roi_objects = []
for i in range(number_of_rois):
current_roi = raw_roi_data[six.b('m_vectGlobal_%d' % i)]
roi_objects.append(Roi(current_roi))
self.roi_metadata = roi_objects
def _build_label_map(self): def _build_label_map(self):
""" """
Every label ends with an exclamation point, however, we can't directly search for those to find all the labels Every label ends with an exclamation point, however, we can't directly search for those to find all the labels


Loading…
Cancel
Save