Browse Source

Refactor, add metadata to Frame objects, support PIMS open function

master
Ruben Verweij 7 years ago
parent
commit
d19a9c305b
6 changed files with 78 additions and 47 deletions
  1. +1
    -2
      nd2reader/legacy.py
  2. +39
    -10
      nd2reader/nd2reader.py
  3. +1
    -1
      nd2reader/parser/__init__.py
  4. +36
    -1
      nd2reader/parser/parser.py
  5. +0
    -32
      nd2reader/version.py
  6. +1
    -1
      tests/driver/version.py

+ 1
- 2
nd2reader/legacy.py View File

@ -1,7 +1,6 @@
# -*- coding: utf-8 -*-
from nd2reader.parser import get_parser
from nd2reader.version import get_version
from nd2reader.parser import get_parser, get_version
import six


+ 39
- 10
nd2reader/nd2reader.py View File

@ -3,7 +3,6 @@ import numpy as np
from nd2reader.exc import NoImageError
from nd2reader.parser import get_parser
from nd2reader.version import get_version
import six
@ -17,12 +16,28 @@ class ND2Reader(FramesSequenceND):
# first use the parser to parse the file
self._fh = open(filename, "rb")
major_version, minor_version = get_version(self._fh)
self._parser = get_parser(self._fh, major_version, minor_version)
self._parser = get_parser(self._fh)
self._metadata = self._parser.metadata
self._roi_metadata = self._parser.roi_metadata
# Set data type
self._dtype = self._get_dtype_from_metadata()
# Setup the axes
self._init_axis('x', self._metadata.width)
self._init_axis('y', self._metadata.height)
self._init_axis('c', len(self._metadata.channels))
self._init_axis('t', len(self._metadata.frames))
self._init_axis('z', len(self._metadata.z_levels))
# provide the default
self.iter_axes = 't'
def _get_dtype_from_metadata(self):
"""
Determine the data type from the metadata.
:return:
"""
bit_depth = self._parser.raw_metadata.image_attributes[six.b('SLxImageAttributes')][six.b('uiBpcInMemory')]
if bit_depth <= 16:
self._dtype = np.float16
@ -31,12 +46,11 @@ class ND2Reader(FramesSequenceND):
else:
self._dtype = np.float64
# Setup the axes
self._init_axis('x', self._metadata.width)
self._init_axis('y', self._metadata.height)
self._init_axis('c', len(self._metadata.channels))
self._init_axis('t', len(self._metadata.frames))
self._init_axis('z', len(self._metadata.z_levels))
return self._dtype
@classmethod
def class_exts(cls):
return {'nd2'} | super(ND2Reader, cls).class_exts()
def close(self):
if self._fh is not None:
@ -57,7 +71,22 @@ class ND2Reader(FramesSequenceND):
except (TypeError, NoImageError):
return Frame([])
else:
return Frame(image, frame_no=image.frame_number)
return Frame(image, frame_no=image.frame_number, metadata=self._get_frame_metadata())
def _get_frame_metadata(self):
"""
Get the metadata for one frame
:return:
"""
frame_metadata = {
"height": self._metadata.height,
"width": self._metadata.width,
"date": self._metadata.date,
"pixel_microns": self._metadata.pixel_microns,
"rois": self._roi_metadata
}
return frame_metadata
@property
def pixel_type(self):


+ 1
- 1
nd2reader/parser/__init__.py View File

@ -1 +1 @@
from . parser import get_parser
from . parser import get_parser, get_version, parse_version

+ 36
- 1
nd2reader/parser/parser.py View File

@ -1,8 +1,9 @@
from nd2reader.parser.v3 import V3Parser
import re
from nd2reader.exc import InvalidVersionError
def get_parser(fh, major_version, minor_version):
def get_parser(fh):
"""
Picks the appropriate parser based on the ND2 version.
@ -13,8 +14,42 @@ def get_parser(fh, major_version, minor_version):
:rtype: a parser object
"""
major_version, minor_version = get_version(fh)
parsers = {(3, None): V3Parser}
parser = parsers.get((major_version, minor_version)) or parsers.get((major_version, None))
if not parser:
raise InvalidVersionError("No parser is available for that version.")
return parser(fh)
def get_version(fh):
"""
Determines what version the ND2 is.
:param fh: an open file handle to the ND2
:type fh: file
"""
# the first 16 bytes seem to have no meaning, so we skip them
fh.seek(16)
# the next 38 bytes contain the string that we want to parse. Unlike most of the ND2, this is in UTF-8
data = fh.read(38).decode("utf8")
return parse_version(data)
def parse_version(data):
"""
Parses a string with the version data in it.
:param data: the 19th through 54th byte of the ND2, representing the version
:type data: unicode
"""
match = re.search(r"""^ND2 FILE SIGNATURE CHUNK NAME01!Ver(?P<major>\d)\.(?P<minor>\d)$""", data)
if match:
# We haven't seen a lot of ND2s but the ones we have seen conform to this
return int(match.group('major')), int(match.group('minor'))
raise InvalidVersionError("The version of the ND2 you specified is not supported.")

+ 0
- 32
nd2reader/version.py View File

@ -1,32 +0,0 @@
import re
from nd2reader.exc import InvalidVersionError
def get_version(fh):
"""
Determines what version the ND2 is.
:param fh: an open file handle to the ND2
:type fh: file
"""
# the first 16 bytes seem to have no meaning, so we skip them
fh.seek(16)
# the next 38 bytes contain the string that we want to parse. Unlike most of the ND2, this is in UTF-8
data = fh.read(38).decode("utf8")
return parse_version(data)
def parse_version(data):
"""
Parses a string with the version data in it.
:param data: the 19th through 54th byte of the ND2, representing the version
:type data: unicode
"""
match = re.search(r"""^ND2 FILE SIGNATURE CHUNK NAME01!Ver(?P<major>\d)\.(?P<minor>\d)$""", data)
if match:
# We haven't seen a lot of ND2s but the ones we have seen conform to this
return int(match.group('major')), int(match.group('minor'))
raise InvalidVersionError("The version of the ND2 you specified is not supported.")

+ 1
- 1
tests/driver/version.py View File

@ -1,5 +1,5 @@
import unittest
from nd2reader.version import parse_version
from nd2reader.parser import parse_version
class VersionTests(unittest.TestCase):


Loading…
Cancel
Save