diff --git a/nd2reader/model/label.py b/nd2reader/model/label.py new file mode 100644 index 0000000..adbac16 --- /dev/null +++ b/nd2reader/model/label.py @@ -0,0 +1,121 @@ +import six +import struct +from collections import namedtuple +import re + + +data_location = namedtuple("DataLocation", ["location", "length"]) + + +class LabelMap(object): + """ + """ + def __init__(self, raw_binary_data): + self._data = raw_binary_data + + def _get_location(self, label): + try: + label_location = self._data.index(label) + len(label) + return self._parse_data_location(label_location) + except ValueError: + return None + + def _parse_data_location(self, label_location): + location, length = struct.unpack("QQ", self._data[label_location: label_location + 16]) + return data_location(location=location, length=length) + + @property + def image_text_info(self): + return self._get_location(six.b("ImageTextInfoLV!")) + + @property + def image_metadata_sequence(self): + # there is always only one of these, even though it has a pipe followed by a zero, which is how they do indexes + return self._get_location(six.b("ImageMetadataSeqLV|0!")) + + @property + def image_data(self): + image_data = {} + regex = re.compile(six.b("""ImageDataSeq\|(\d+)!""")) + for match in regex.finditer(self._data): + if match: + print(match.start(), match.end()) + location = self._parse_data_location(match.end()) + image_data[int(match.group(1))] = location + return image_data + + @property + def image_calibration(self): + return self._get_location(six.b("ImageCalibrationLV|0!")) + + @property + def image_attributes(self): + return self._get_location(six.b("ImageAttributesLV!")) + + @property + def x_data(self): + return self._get_location(six.b("CustomData|X!")) + + @property + def y_data(self): + return self._get_location(six.b("CustomData|Y!")) + + @property + def z_data(self): + return self._get_location(six.b("CustomData|Z!")) + + @property + def roi_metadata(self): + return self._get_location(six.b("CustomData|RoiMetadata_v1!")) + + @property + def pfs_status(self): + return self._get_location(six.b("CustomData|PFS_STATUS!")) + + @property + def pfs_offset(self): + return self._get_location(six.b("CustomData|PFS_OFFSET!")) + + @property + def guid(self): + return self._get_location(six.b("CustomData|GUIDStore!")) + + @property + def description(self): + return self._get_location(six.b("CustomData|CustomDescriptionV1_0!")) + + @property + def camera_exposure_time(self): + return self._get_location(six.b("CustomData|Camera_ExposureTime1!")) + + @property + def camera_temp(self): + return self._get_location(six.b("CustomData|CameraTemp1!")) + + @property + def acquisition_times(self): + return self._get_location(six.b("CustomData|AcqTimesCache!")) + + @property + def acquisition_times_2(self): + return self._get_location(six.b("CustomData|AcqTimes2Cache!")) + + @property + def acquisition_frames(self): + return self._get_location(six.b("CustomData|AcqFramesCache!")) + + @property + def lut_data(self): + return self._get_location(six.b("CustomDataVar|LUTDataV1_0!")) + + @property + def grabber_settings(self): + return self._get_location(six.b("CustomDataVar|GrabberCameraSettingsV1_0!")) + + @property + def custom_data(self): + return self._get_location(six.b("CustomDataVar|CustomDataV2_0!")) + + @property + def app_info(self): + return self._get_location(six.b("CustomDataVar|AppInfo_V1_0!")) diff --git a/nd2reader/parser/v3.py b/nd2reader/parser/v3.py index 147902a..e831f62 100644 --- a/nd2reader/parser/v3.py +++ b/nd2reader/parser/v3.py @@ -3,6 +3,7 @@ import array from datetime import datetime from nd2reader.model.metadata import Metadata +from nd2reader.model.label import LabelMap from nd2reader.parser.base import BaseParser from nd2reader.driver.v3 import V3Driver from nd2reader.common.v3 import read_chunk @@ -40,11 +41,7 @@ class V3Parser(BaseParser): def driver(self): return V3Driver(self.metadata, self._label_map, self._fh) - def _parse_metadata(self): - """ - Reads all metadata and instantiates the Metadata object. - - """ + def _build_metadata_dict(self): metadata_dict = {} self._label_map = self._build_label_map() for label in self._label_map.keys(): @@ -52,7 +49,14 @@ class V3Parser(BaseParser): data = read_chunk(self._fh, self._label_map[label]) stop = label.index(six.b("LV")) metadata_dict[label[:stop]] = self._read_metadata(data, 1) + return metadata_dict + def _parse_metadata(self): + """ + Reads all metadata and instantiates the Metadata object. + + """ + metadata_dict = self._build_metadata_dict() height = metadata_dict[six.b('ImageAttributes')][six.b('SLxImageAttributes')][six.b('uiHeight')] width = metadata_dict[six.b('ImageAttributes')][six.b('SLxImageAttributes')][six.b('uiWidth')] channels = self._parse_channels(metadata_dict) @@ -208,23 +212,24 @@ class V3Parser(BaseParser): :rtype: dict """ - label_map = {} + # label_map = {} 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: - data_start = raw_text.index(six.b("!"), label_start) + 1 - key = raw_text[label_start: data_start] - location, length = struct.unpack("QQ", raw_text[data_start: data_start + 16]) - if key == V3Parser.CHUNK_MAP_END: - # We've reached the end of the chunk map - break - label_map[key] = location - label_start = data_start + 16 - return label_map + # label_start = raw_text.index(V3Parser.CHUNK_MAP_START) + 32 + return LabelMap(raw_text) + + # while True: + # data_start = raw_text.index(six.b("!"), label_start) + 1 + # key = raw_text[label_start: data_start] + # location, length = struct.unpack("QQ", raw_text[data_start: data_start + 16]) + # if key == V3Parser.CHUNK_MAP_END: + # # We've reached the end of the chunk map + # break + # label_map[key] = location + # label_start = data_start + 16 + # return label_map def _parse_unsigned_char(self, data): return struct.unpack("B", data.read(1))[0]