diff --git a/.gitignore b/.gitignore index 51cbe85..6130346 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ +run.py +.gitignore # Byte-compiled / optimized / DLL files __pycache__/ *.py[cod] diff --git a/nd2reader/__init__.py b/nd2reader/__init__.py index 2df8a28..742aa95 100644 --- a/nd2reader/__init__.py +++ b/nd2reader/__init__.py @@ -1,8 +1,6 @@ import array import numpy as np -import re import struct -from pprint import pprint class Nd2Reader(object): @@ -14,7 +12,7 @@ class Nd2Reader(object): self._filename = filename self._file_handler = None self._chunk_map_start_location = None - self._map_keys = {} + self._label_map = {} self._read_map() @property @@ -30,7 +28,7 @@ class Nd2Reader(object): grab the subsequent data (always 16 bytes long), advance to the next label and repeat. """ - raw_text = self.raw_chunk_map_text + raw_text = self._get_raw_chunk_map_text() label_start = self._find_first_label_offset(raw_text) while True: data_start = self._get_data_start(label_start, raw_text) @@ -38,7 +36,7 @@ class Nd2Reader(object): if label == "ND2 CHUNK MAP SIGNATURE 0000001!": # We've reached the end of the chunk map break - self._map_keys[label] = value + self._label_map[label] = value label_start = data_start + 16 @staticmethod @@ -84,8 +82,46 @@ class Nd2Reader(object): self._chunk_map_start_location = struct.unpack("Q", self.fh.read(8))[0] return self._chunk_map_start_location - @property - def raw_chunk_map_text(self): + def _read_chunk(self, chunk_location): + """ + Gets the data for a given chunk pointer + + """ + self.fh.seek(chunk_location) + chunk_data = self._read_chunk_metadata() + header, relative_offset, data_length = self._parse_chunk_metadata(chunk_data) + return self._read_chunk_data(chunk_location, relative_offset, data_length) + + def _read_chunk_metadata(self): + """ + Gets the chunks metadata, which is always 16 bytes + + """ + return self.fh.read(16) + + def _read_chunk_data(self, chunk_location, relative_offset, data_length): + """ + Reads the actual data for a given chunk + + """ + # 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.fh.seek(chunk_location + 16 + relative_offset) + return self.fh.read(data_length) + + @staticmethod + def _parse_chunk_metadata(chunk_data): + """ + Finds out everything about a given chunk. Every chunk begins with the same value, so if that's ever + different we can assume the file has suffered some kind of damage. + + """ + header, relative_offset, data_length = struct.unpack("IIQ", chunk_data) + if header != 0xabeceda: + raise ValueError("The ND2 file seems to be corrupted.") + return header, relative_offset, data_length + + def _get_raw_chunk_map_text(self): """ Reads the entire chunk map and returns it as a string.