Browse Source

cleaned up a bit

master
Jim Rybarski 9 years ago
parent
commit
ac91d388c1
6 changed files with 45 additions and 63 deletions
  1. +3
    -0
      CHANGELOG.md
  2. +2
    -21
      nd2reader/driver/v3.py
  3. +6
    -2
      nd2reader/interface.py
  4. +2
    -2
      nd2reader/parser/parser.py
  5. +27
    -32
      nd2reader/parser/v3.py
  6. +5
    -6
      nd2reader/version.py

+ 3
- 0
CHANGELOG.md View File

@ -1,4 +1,7 @@
## [1.1.2] - 2015-10-09
### ADDED
- `Image` objects now have a `frame_number` attribute.
### CHANGED
- `Image` objects now directly subclass Numpy arrays.
- Refactored code to permit parsing of different versions of ND2s, which will allow us to add support for NIS Elements 3.x.


+ 2
- 21
nd2reader/driver/v3.py View File

@ -5,11 +5,10 @@ import numpy as np
import struct
import six
from nd2reader.model.image import Image
from nd2reader.parser.v3 import read_chunk
class V3Driver(object):
CHUNK_HEADER = 0xabeceda
def __init__(self, metadata, label_map, file_handle):
self._metadata = metadata
self._label_map = label_map
@ -79,7 +78,7 @@ class V3Driver(object):
"""
chunk = self._label_map[six.b("ImageDataSeq|%d!" % image_group_number)]
data = self._read_chunk(chunk)
data = read_chunk(self._file_handle, chunk)
# All images in the same image group share the same timestamp! So if you have complicated image data,
# your timestamps may not be entirely accurate. Practically speaking though, they'll only be off by a few
# seconds unless you're doing something super weird.
@ -97,21 +96,3 @@ class V3Driver(object):
if np.any(image_data):
return timestamp, Image(image_data)
return None
def _read_chunk(self, chunk_location):
"""
Gets the data for a given chunk pointer
:rtype: bytes
"""
self._file_handle.seek(chunk_location)
# The chunk metadata is always 16 bytes long
chunk_metadata = self._file_handle.read(16)
header, relative_offset, data_length = struct.unpack("IIQ", chunk_metadata)
if header != V3Driver.CHUNK_HEADER:
raise ValueError("The ND2 file seems to be corrupted.")
# We start at the location of the chunk metadata, skip over the metadata, and then proceed to the
# start of the actual data field, which is at some arbitrary place after the metadata.
self._file_handle.seek(chunk_location + 16 + relative_offset)
return self._file_handle.read(data_length)

+ 6
- 2
nd2reader/interface.py View File

@ -12,8 +12,9 @@ class Nd2(object):
"""
def __init__(self, filename):
major_version, minor_version = get_version(filename)
parser = get_parser(filename, major_version, minor_version)
self._fh = open(filename, "rb")
major_version, minor_version = get_version(self._fh)
parser = get_parser(self._fh, major_version, minor_version)
self._driver = parser.driver
self._metadata = parser.metadata
self._filename = filename
@ -160,3 +161,6 @@ class Nd2(object):
"""
return self._driver.get_image_by_attributes(frame_number, field_of_view, channel_name, z_level)
def close(self):
self._fh.close()

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

@ -2,9 +2,9 @@ from nd2reader.parser.v3 import V3Parser
from nd2reader.exc import InvalidVersionError
def get_parser(filename, major_version, minor_version):
def get_parser(fh, major_version, minor_version):
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(filename)
return parser(fh)

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

@ -10,15 +10,33 @@ import six
import struct
def read_chunk(fh, chunk_location):
"""
Gets the data for a given chunk pointer
:rtype: bytes
"""
fh.seek(chunk_location)
# The chunk metadata is always 16 bytes long
chunk_metadata = fh.read(16)
header, relative_offset, data_length = struct.unpack("IIQ", chunk_metadata)
if header != 0xabeceda:
raise ValueError("The ND2 file seems to be corrupted.")
# We start at the location of the chunk metadata, skip over the metadata, and then proceed to the
# start of the actual data field, which is at some arbitrary place after the metadata.
fh.seek(chunk_location + 16 + relative_offset)
return fh.read(data_length)
class V3Parser(BaseParser):
""" Parses ND2 files and creates a Metadata and ImageReader object. """
CHUNK_HEADER = 0xabeceda
CHUNK_MAP_START = six.b("ND2 FILEMAP SIGNATURE NAME 0001!")
CHUNK_MAP_END = six.b("ND2 CHUNK MAP SIGNATURE 0000001!")
def __init__(self, filename):
self._filename = filename
self._fh = None
def __init__(self, fh):
self._fh = fh
self._metadata = None
self._label_map = None
@ -30,7 +48,7 @@ class V3Parser(BaseParser):
@property
def driver(self):
return V3Driver(self.metadata, self._label_map, self._get_file_handle())
return V3Driver(self.metadata, self._label_map, self._fh)
def _parse_metadata(self):
"""
@ -41,7 +59,7 @@ class V3Parser(BaseParser):
self._label_map = self._build_label_map()
for label in self._label_map.keys():
if label.endswith(six.b("LV!")) or six.b("LV|") in label:
data = self._read_chunk(self._label_map[label])
data = read_chunk(self._fh, self._label_map[label])
stop = label.index(six.b("LV"))
metadata_dict[label[:stop]] = self._read_metadata(data, 1)
@ -134,11 +152,6 @@ class V3Parser(BaseParser):
"""
return self._parse_dimension(r""".*?Z\((\d+)\).*?""", metadata_dict)
def _get_file_handle(self):
if self._fh is None:
self._fh = open(self._filename, "rb")
return self._fh
def _parse_dimension_text(self, metadata_dict):
"""
While there are metadata values that represent a lot of what we want to capture, they seem to be unreliable.
@ -191,10 +204,10 @@ class V3Parser(BaseParser):
"""
label_map = {}
self._get_file_handle().seek(-8, 2)
chunk_map_start_location = struct.unpack("Q", self._get_file_handle().read(8))[0]
self._get_file_handle().seek(chunk_map_start_location)
raw_text = self._get_file_handle().read(-1)
self._fh.seek(-8, 2)
chunk_map_start_location = struct.unpack("Q", self._fh.read(8))[0]
self._fh.seek(chunk_map_start_location)
raw_text = self._fh.read(-1)
label_start = raw_text.index(V3Parser.CHUNK_MAP_START) + 32
while True:
@ -208,24 +221,6 @@ class V3Parser(BaseParser):
label_start = data_start + 16
return label_map
def _read_chunk(self, chunk_location):
"""
Gets the data for a given chunk pointer
:rtype: bytes
"""
self._get_file_handle().seek(chunk_location)
# The chunk metadata is always 16 bytes long
chunk_metadata = self._get_file_handle().read(16)
header, relative_offset, data_length = struct.unpack("IIQ", chunk_metadata)
if header != V3Parser.CHUNK_HEADER:
raise ValueError("The ND2 file seems to be corrupted.")
# We start at the location of the chunk metadata, skip over the metadata, and then proceed to the
# start of the actual data field, which is at some arbitrary place after the metadata.
self._get_file_handle().seek(chunk_location + 16 + relative_offset)
return self._get_file_handle().read(data_length)
def _parse_unsigned_char(self, data):
return struct.unpack("B", data.read(1))[0]


+ 5
- 6
nd2reader/version.py View File

@ -2,7 +2,7 @@ import re
from nd2reader.exc import InvalidVersionError
def get_version(filename):
def get_version(fh):
"""
Determines what version the ND2 is.
@ -10,11 +10,10 @@ def get_version(filename):
:type filename: str
"""
with open(filename, 'rb') as f:
# the first 16 bytes seem to have no meaning, so we skip them
f.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 = f.read(38).decode("utf8")
# 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)


Loading…
Cancel
Save