From 0a1988bc69b397bab3c206aeb3b40275e825f81a Mon Sep 17 00:00:00 2001 From: Lorenzo Zolfanelli Date: Fri, 22 Jan 2021 00:37:26 +0100 Subject: [PATCH] first messy roi loading modifications --- nd2reader/parser.py | 75 +++++++++++++++++++++++++++++++++++++++++++++ nd2reader/reader.py | 13 ++++++++ 2 files changed, 88 insertions(+) diff --git a/nd2reader/parser.py b/nd2reader/parser.py index 8534e79..49514c4 100644 --- a/nd2reader/parser.py +++ b/nd2reader/parser.py @@ -6,6 +6,7 @@ import six import warnings from pims.base_frames import Frame import numpy as np +from tqdm import tqdm from nd2reader.common import get_version, read_chunk from nd2reader.label_map import LabelMap @@ -78,6 +79,21 @@ class Parser(object): else: return Frame(image, frame_no=frame_number, metadata=self._get_frame_metadata()) + def get_roi_by_attributes(self, frame_number, field_of_view, channel, z_level, height, width, xywh): + frame_number = 0 if frame_number is None else frame_number + field_of_view = 0 if field_of_view is None else field_of_view + channel = 0 if channel is None else channel + z_level = 0 if z_level is None else z_level + + image_group_number = self._calculate_image_group_number(frame_number, field_of_view, z_level) + try: + timestamp, raw_image_data = self._get_raw_image_data_xywh( + image_group_number, channel, height, width, xywh) + except (TypeError): + return Frame([], frame_no=frame_number, metadata=self._get_frame_metadata()) + else: + return Frame(raw_image_data, frame_no=frame_number, metadata=self._get_frame_metadata()) + def get_image_by_attributes(self, frame_number, field_of_view, channel, z_level, height, width): """Gets an image based on its attributes alone @@ -246,6 +262,65 @@ class Parser(object): """ return {channel: n for n, channel in enumerate(self.metadata["channels"])} + def _get_raw_image_data_xywh(self, image_group_number, channel, height, width, xywh): + + size_c = len(self.metadata["channels"]) + + x0, y0, w, h = xywh + chunk_location = self._label_map.get_image_data_location(image_group_number) + fh = self._fh + + if chunk_location is None or fh is None: + return None + 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) + + # Read timestamp + timestamp = struct.unpack("d", fh.read(8))[0] + + # Stitched Images: evaluate number of bytes to strip + n_unwanted_bytes = (data_length-8) % (height * width) + assert 0 == n_unwanted_bytes % height + rowskip = n_unwanted_bytes // height + + # Read ROI: row-by-row + image_start_pos = chunk_location + 16 + relative_offset + 8 + + line_bytemask = np.zeros(size_c, dtype=np.bool) + line_bytemask[channel] = True + line_bytemask = np.tile(line_bytemask.repeat(2),w) + + def get_line(y): + fh.seek(image_start_pos + size_c*2*((width)*y+x0) + y*rowskip) + return np.frombuffer(fh.read(size_c*2*w), np.byte)[line_bytemask] + + data = [get_line(y) for y in tqdm(range(y0, y0+h))] + data = bytes().join(data) + + image_group_data = array.array("H", data) + true_channels_no = int(len(image_group_data) / (h * w)) + + image_data = np.reshape(image_group_data, (h, w, true_channels_no)) + + missing_channels = ~np.any(image_data, axis=(0, 1)) + image_data[..., missing_channels] = np.full( + (h, w, missing_channels.sum()), np.nan) + + if np.any(missing_channels): + warnings.warn( + "ND2 file contains gap frames which are represented by " + + "np.nan-filled arrays; to convert to zeros use e.g. " + + "np.nan_to_num(array)") + return timestamp, image_data[...,0] + + def _get_raw_image_data(self, image_group_number, channel_offset, height, width): """Reads the raw bytes and the timestamp of an image. diff --git a/nd2reader/reader.py b/nd2reader/reader.py index 4e14f42..06fbba1 100644 --- a/nd2reader/reader.py +++ b/nd2reader/reader.py @@ -69,6 +69,19 @@ class ND2Reader(FramesSequenceND): except KeyError: return 0 + def get_roi(self, roi, c=0, t=0, z=0, x=0, y=0, v=0): + height = self.metadata['height'] + width = self.metadata['width'] + ylim = roi[0].indices(height) + xlim = roi[1].indices(width) + + y = ylim[0] + x = xlim[0] + w = xlim[1]-xlim[0] + h = ylim[1]-ylim[0] + + return self._parser.get_roi_by_attributes(t, v, c, z, height, width, (x, y, w, h)) + def get_frame_2D(self, c=0, t=0, z=0, x=0, y=0, v=0): """Gets a given frame using the parser Args: