### Install nd2reader with pip Source: https://open-science-tools.github.io/nd2reader/_sources/tutorial.rst.txt Install the nd2reader package from PyPi. Dependencies numpy, pims, six, and xmltodict are installed automatically if using setup.py. Python 2.7 and >=3.4 are supported. ```bash pip install nd2reader ``` -------------------------------- ### Install nd2reader via conda Source: https://open-science-tools.github.io/nd2reader/_sources/tutorial.rst.txt Install nd2reader from the conda-forge channel after it has been enabled. ```bash conda install nd2reader ``` -------------------------------- ### Print ND2 metadata dictionary Source: https://open-science-tools.github.io/nd2reader/_sources/tutorial.rst.txt Open an ND2 file and print the full metadata dictionary. The output shows all metadata properties for the example file. ```python from nd2reader import ND2Reader with ND2Reader('my_directory/example.nd2') as images: # width and height of the image print(images.metadata) ``` ```python {'channels': ['BF100xoil-1x-R', 'BF+RITC'], 'date': datetime.datetime(2017, 10, 30, 14, 35, 18), 'experiment': {'description': 'ND Acquisition', 'loops': [{'duration': 0, 'sampling_interval': 0.0, 'start': 0, 'stimulation': False}]}, 'fields_of_view': [0, 1], 'frames': [0], 'height': 1895, 'num_frames': 1, 'pixel_microns': 0.09214285714285715, 'total_images_per_channel': 6, 'width': 2368, 'z_levels': [0, 1, 2]} ``` -------------------------------- ### Get SLx picture metadata Source: https://open-science-tools.github.io/nd2reader/_modules/nd2reader/artificial.html Returns a dictionary of SLx picture metadata. Used for defining picture plane information in ND2 files. ```python @staticmethod def _get_slx_picture_metadata(): return {'sPicturePlanes': { 'sPlaneNew': { # channels are numbered a0, a1, ..., aN 'a0': { 'sDescription': 'TRITC' } } } } ``` -------------------------------- ### ND2Reader.get_timesteps() Source: https://open-science-tools.github.io/nd2reader/nd2reader.html Get the timesteps of the experiment. ```APIDOC ## get_timesteps() ### Description Get the timesteps of the experiment. ### Method instance method ### Returns - **np.ndarray** - An array of times in milliseconds. ``` -------------------------------- ### ArtificialND2 class and label map construction Source: https://open-science-tools.github.io/nd2reader/_modules/nd2reader/artificial.html Core class for creating artificial ND2 files. The constructor opens a binary file, writes the version header, label map, and label map marker, and supports skipping blocks via `skip_blocks`. The `create_label_map_bytes` method is incomplete in the source; it starts building the label map but the rest is not shown. ```python [docs]class ArtificialND2(object): """Artificial ND2 class (for testing purposes) """ header = 0xabeceda relative_offset = 0 data_types = {'unsigned_char': 1, 'unsigned_int': 2, 'unsigned_int_2': 3, 'unsigned_long': 5, 'double': 6, 'string': 8, 'char_array': 9, 'metadata_item': 11, } def __init__(self, file, version=(3, 0), skip_blocks=None): self.version = version self.raw_text, self.locations, self.data = b'', None, None check_or_make_dir(path.dirname(file)) self._fh = open(file, 'w+b', 0) self.write_file(skip_blocks) def __enter__(self): return self def __exit__(self, exc_type, exc_value, traceback): self.close() @property def file_handle(self): """The file handle to the binary file Returns: file: the file handle """ return self._fh [docs] def close(self): """Correctly close the file handle """ if self._fh is not None: self._fh.close() [docs] def write_file(self, skip_blocks=None): if skip_blocks is None: skip_blocks = [] if 'version' not in skip_blocks: # write version header at start of the file self.write_version() if 'label_map' not in skip_blocks: # write label map + data in the center self.locations, self.data = self.write_label_map() if 'label_map_marker' not in skip_blocks: # write start position of label map at the end of the file self.write_label_map_info() # write all to file self._fh.write(self.raw_text) [docs] def write_version(self): """Write file header """ # write 16 empty bytes self.raw_text += bytearray(16) # write version info self.raw_text += self._get_version_string() [docs] def write_label_map_info(self): """Write the location of the start of the label map at the end of the file """ location = self._get_version_byte_length() self.raw_text += struct.pack("Q", location) def _get_version_string(self): return six.b('ND2 FILE SIGNATURE CHUNK NAME01!Ver%s.%s' % self.version) def _get_version_byte_length(self): return 16 + len(self._get_version_string()) [docs] def write_label_map(self): raw_text, locations, data = self.create_label_map_bytes() self.raw_text += raw_text return locations, data [docs] def create_label_map_bytes(self): """Construct a binary label map Returns: tuple: (binary data, dictionary data) """ raw_text = six.b('') labels = global_labels file_labels = global_file_labels file_data, file_data_dict = self._get_file_data(labels) locations = {} # generate random positions and lengths version_length = self._get_version_byte_length() # calculate data length label_length = np.sum([len(six.b(l)) + 16 for l in file_labels]) ``` -------------------------------- ### Get SLx image attributes Source: https://open-science-tools.github.io/nd2reader/_modules/nd2reader/artificial.html Returns a dictionary of SLx image attributes. Used for defining image properties in ND2 files. ```python @staticmethod def _get_slx_img_attrib(): return {'uiWidth': 128, 'uiWidthBytes': 256, 'uiHeight': 128, 'uiComp': 1, 'uiBpcInMemory': 16, 'uiBpcSignificant': 12, 'uiSequenceCount': 70, 'uiTileWidth': 128, 'uiTileHeight': 128, 'eCompression': 2, 'dCompressionParam': -1.0, 'ePixelType': 1, 'uiVirtualComponents': 1 } ``` -------------------------------- ### Get timesteps from metadata Source: https://open-science-tools.github.io/nd2reader/_modules/nd2reader/reader.html Returns an array of timesteps in milliseconds, caching the result. Converts raw acquisition times from seconds to milliseconds using numpy. ```python def get_timesteps(self): """Get the timesteps of the experiment Returns: np.ndarray: an array of times in milliseconds. """ if self._timesteps is not None and len(self._timesteps) > 0: return self._timesteps self._timesteps = ( np.array(list(self._parser._raw_metadata.acquisition_times), dtype=np.float) * 1000.0 ) return self._timesteps ``` -------------------------------- ### Check or create directory Source: https://open-science-tools.github.io/nd2reader/_modules/nd2reader/common.html Creates a directory if it does not already exist. Uses os.path.exists and os.makedirs. ```python def check_or_make_dir(directory): """ Check if a directory exists, if not, create it Args: directory: the path to the directory """ if not os.path.exists(directory): os.makedirs(directory) ``` -------------------------------- ### Initialize axes from metadata Source: https://open-science-tools.github.io/nd2reader/_modules/nd2reader/reader.html Initializes x, y, c, t, z, and v axes based on metadata properties, with defaults and minimum sizes. Raises EmptyFileError if no axes are found. ```python self._init_axis_if_exists("x", self._get_metadata_property("width", default=0)) self._init_axis_if_exists("y", self._get_metadata_property("height", default=0)) self._init_axis_if_exists( "c", len(self._get_metadata_property("channels", default=[])), min_size=2 ) self._init_axis_if_exists( "t", len(self._get_metadata_property("frames", default=[])) ) self._init_axis_if_exists( "z", len(self._get_metadata_property("z_levels", default=[])), min_size=2 ) self._init_axis_if_exists( "v", len(self._get_metadata_property("fields_of_view", default=[])), min_size=2, ) if len(self.sizes) == 0: raise EmptyFileError("No axes were found for this .nd2 file.") # provide the default self.iter_axes = self._guess_default_iter_axis() self._register_get_frame(self.get_frame_2D, "yx") ``` -------------------------------- ### _set_default_if_not_empty method Source: https://open-science-tools.github.io/nd2reader/_modules/nd2reader/raw_metadata.html Sets a default value of [0] for the given metadata entry if it is empty and the file is not empty (total_images_per_channel > 0). ```python def _set_default_if_not_empty(self, entry): total_images = self._metadata_parsed['total_images_per_channel'] \ if self._metadata_parsed['total_images_per_channel'] is not None else 0 if len(self._metadata_parsed[entry]) == 0 and total_images > 0: # if the file is not empty, we always have one of this entry self._metadata_parsed[entry] = [0] ``` -------------------------------- ### Add conda-forge channel Source: https://open-science-tools.github.io/nd2reader/_sources/tutorial.rst.txt Add the conda-forge channel to your conda configuration to enable installation of nd2reader from that channel. ```bash conda config --add channels conda-forge ``` -------------------------------- ### RawMetadata class definition and constructor Source: https://open-science-tools.github.io/nd2reader/_modules/nd2reader/raw_metadata.html Defines the RawMetadata class with an initializer that stores the file handle and label map, and a property that returns the parsed metadata dictionary. ```python class RawMetadata(object): """RawMetadata class parses and stores the raw metadata that is read from the binary file in dict format. """ def __init__(self, fh, label_map): self._fh = fh self._label_map = label_map self._metadata_parsed = None @property def __dict__(self): """Returns the parsed metadata in dictionary form. Returns: dict: the parsed metadata """ return self.get_parsed_metadata() ``` -------------------------------- ### Get file data Source: https://open-science-tools.github.io/nd2reader/_modules/nd2reader/artificial.html Returns a list of file data structures for ND2 files. Used for constructing the file data section. ```python def _get_file_data(self, labels): file_data = [ {'SLxImageAttributes': self._get_slx_img_attrib()}, # ImageAttributesLV!", 7, # ImageTextInfoLV!", 7, # ImageMetadataLV!", {'SLxPictureMetadata': self._get_slx_picture_metadata()}, # ImageMetadataSeqLV|0!", 7, # ImageCalibrationLV|0!", 7, # CustomData|X!", 7, # CustomData|Y!", 7, # CustomData|Z!", 7, # CustomData|RoiMetadata_v1!", 7, # CustomData|PFS_STATUS!", 7, # CustomData|PFS_OFFSET!", 7, # CustomData|GUIDStore!", 7, # CustomData|CustomDescriptionV1_0!", 7, # CustomData|Camera_ExposureTime1!", 7, # CustomData|CameraTemp1!", [0], # CustomData|AcqTimesCache!", [0], # CustomData|AcqTimes2Cache!", [0], # CustomData|AcqFramesCache!", 7, # CustomDataVar|LUTDataV1_0!", 7, # CustomDataVar|GrabberCameraSettingsV1_0!", 7, # CustomDataVar|CustomDataV2_0!", 7, # CustomDataVar|AppInfo_V1_0!", 7, # ImageDataSeq|0!" ] ``` -------------------------------- ### _parse_date method Source: https://open-science-tools.github.io/nd2reader/_modules/nd2reader/raw_metadata.html Parses the acquisition date from the image text info, returning None if the key is missing. ```python def _parse_date(self): try: return parse_date(self.image_text_info[six.b('SLxImageTextInfo')]) except KeyError: return None ``` -------------------------------- ### ND2Reader.class_exts() Source: https://open-science-tools.github.io/nd2reader/nd2reader.html Class method that lets PIMS open function use this reader for opening .nd2 files. ```APIDOC ## classmethod class_exts() ### Description Lets PIMS open function use this reader for opening .nd2 files. ### Method classmethod ### Returns None ``` -------------------------------- ### Pack data with metadata Source: https://open-science-tools.github.io/nd2reader/_modules/nd2reader/artificial.html Packs data with metadata by prepending header, relative offset, and length. Used for creating ND2 file data blocks. ```python def _pack_data_with_metadata(self, data): packed_data = self._pack_raw_data_with_metadata(data) raw_data = struct.pack("IIQ", self.header, self.relative_offset, len(packed_data)) raw_data += packed_data return raw_data ``` -------------------------------- ### Get data type identifier Source: https://open-science-tools.github.io/nd2reader/_modules/nd2reader/artificial.html Returns the data type identifier for a given data value. Used for determining how to pack data in ND2 files. ```python def _get_data_type(self, data): if isinstance(data, dict): return self.data_types['metadata_item'] elif isinstance(data, int): return self.data_types['unsigned_int'] elif isinstance(data, str): return self.data_types['string'] else: return self.data_types['double'] ``` -------------------------------- ### _parse_width method Source: https://open-science-tools.github.io/nd2reader/_modules/nd2reader/raw_metadata.html Parses the image width using the 'uiWidth' key. ```python def _parse_width(self): return self._parse_width_or_height('uiWidth') ``` -------------------------------- ### Get dictionary value if exists Source: https://open-science-tools.github.io/nd2reader/_modules/nd2reader/common.html Safely retrieves a value from a dictionary, optionally converting the key to binary first. Returns None if the key is absent. ```python def get_from_dict_if_exists(key, dictionary, convert_key_to_binary=True): """ Get the entry from the dictionary if it exists Args: key: key to lookup dictionary: dictionary to look in convert_key_to_binary: convert the key from string to binary if true Returns: the value of dictionary[key] or None """ if convert_key_to_binary: key = six.b(key) if key not in dictionary: return None return dictionary[key] ``` -------------------------------- ### Get ND2 value by data type Source: https://open-science-tools.github.io/nd2reader/_modules/nd2reader/common.html Dispatches to the appropriate parser based on the ND2 data type code. Returns None on KeyError or struct.error. ```python def _get_value(data, data_type, cursor_position): """ND2s use various codes to indicate different data types, which we translate here. Args: data: the binary data data_type: the data type (unsigned char = 1, unsigned int = 2 or 3, unsigned long = 5, double = 6, string = 8, char array = 9, metadata item = 11) cursor_position: the cursor position in the binary nd2 file Returns: mixed: the parsed value """ parser = {1: _parse_unsigned_char, 2: _parse_unsigned_int, 3: _parse_unsigned_int, 5: _parse_unsigned_long, 6: _parse_double, 8: _parse_string, 9: _parse_char_array, 11: _parse_metadata_item} try: value = parser[data_type](data) if data_type < 11 else parser[data_type](data, cursor_position) except (KeyError, struct.error): value = None return value ``` -------------------------------- ### Pack raw data with metadata Source: https://open-science-tools.github.io/nd2reader/_modules/nd2reader/artificial.html Packs raw data with metadata by writing label map and data. Used internally for constructing ND2 file content. ```python # write label map cur_pos = version_length + label_length for label, file_label, data in zip(labels, file_labels, file_data): raw_text += six.b(file_label) data_length = len(data) raw_text += struct.pack('QQ', cur_pos, data_length) locations[label] = (cur_pos, data_length) cur_pos += data_length # write data raw_text += six.b('').join(file_data) return raw_text, locations, file_data_dict ``` -------------------------------- ### ND2Reader.get_frame_2D(c=0, t=0, z=0, x=0, y=0, v=0) Source: https://open-science-tools.github.io/nd2reader/nd2reader.html Gets a given frame using the parser. Parameters include x-index, y-index, color channel, frame number, z stack number, and field of view index. ```APIDOC ## get_frame_2D(c=0, t=0, z=0, x=0, y=0, v=0) ### Description Gets a given frame using the parser. ### Method instance method ### Parameters - **c** (int) - Optional - The color channel number. - **t** (int) - Optional - The frame number. - **z** (int) - Optional - The z stack number. - **x** (int) - Optional - The x-index (pims expects this). - **y** (int) - Optional - The y-index (pims expects this). - **v** (int) - Optional - The field of view index. ### Returns - **pims.Frame** - The requested frame. ``` -------------------------------- ### Parse ROI metadata with _parse_roi and _parse_vect_anim Source: https://open-science-tools.github.io/nd2reader/_modules/nd2reader/raw_metadata.html These methods parse ROI metadata from ND2 files. _parse_roi initializes the ROI dictionary and iterates over timepoints, calling _parse_vect_anim for each. _parse_vect_anim appends timepoints, positions, and sizes, converting positions and sizes to physical units using pixel_microns and image dimensions. ```python def _parse_roi(self, raw_roi_dict): """Extract the vector animation parameters from the ROI. This includes the position and size at the given timepoints. Args: raw_roi_dict: dictionary of raw roi metadata Returns: dict: the parsed ROI metadata """ number_of_timepoints = raw_roi_dict[six.b('m_vectAnimParams_Size')] roi_dict = { "timepoints": [], "positions": [], "sizes": [], "shape": parse_roi_shape(raw_roi_dict[six.b('m_sInfo')][six.b('m_uiShapeType')]), "type": parse_roi_type(raw_roi_dict[six.b('m_sInfo')][six.b('m_uiInterpType')]) } for i in range(number_of_timepoints): roi_dict = self._parse_vect_anim(roi_dict, raw_roi_dict[six.b('m_vectAnimParams_%d' % i)]) # convert to NumPy arrays roi_dict["timepoints"] = np.array(roi_dict["timepoints"], dtype=np.float) roi_dict["positions"] = np.array(roi_dict["positions"], dtype=np.float) roi_dict["sizes"] = np.array(roi_dict["sizes"], dtype=np.float) return roi_dict ``` ```python def _parse_vect_anim(self, roi_dict, animation_dict): """ Parses a ROI vector animation object and adds it to the global list of timepoints and positions. Args: roi_dict: the raw roi dictionary animation_dict: the raw animation dictionary Returns: dict: the parsed metadata """ roi_dict["timepoints"].append(animation_dict[six.b('m_dTimeMs')]) image_width = self._metadata_parsed["width"] * self._metadata_parsed["pixel_microns"] image_height = self._metadata_parsed["height"] * self._metadata_parsed["pixel_microns"] # positions are taken from the center of the image as a fraction of the half width/height of the image position = np.array((0.5 * image_width * (1 + animation_dict[six.b('m_dCenterX')]), 0.5 * image_height * (1 + animation_dict[six.b('m_dCenterY')]), animation_dict[six.b('m_dCenterZ')])) roi_dict["positions"].append(position) size_dict = animation_dict[six.b('m_sBoxShape')] # sizes are fractions of the half width/height of the image roi_dict["sizes"].append((size_dict[six.b('m_dSizeX')] * 0.25 * image_width, size_dict[six.b('m_dSizeY')] * 0.25 * image_height, size_dict[six.b('m_dSizeZ')])) return roi_dict ``` -------------------------------- ### Parse experiment metadata with _parse_experiment_metadata and _parse_loop_data Source: https://open-science-tools.github.io/nd2reader/_modules/nd2reader/raw_metadata.html These methods parse experiment metadata from ND2 files. _parse_experiment_metadata initializes the experiment dictionary and extracts description and loop data if present. _parse_loop_data iterates over loops, computing duration, sampling interval, and stimulation flag, and accumulates time offsets. ```python def _parse_experiment_metadata(self): """Parse the metadata of the ND experiment """ self._metadata_parsed['experiment'] = { 'description': 'unknown', 'loops': [] } if self.image_metadata is None or six.b('SLxExperiment') not in self.image_metadata: return raw_data = self.image_metadata[six.b('SLxExperiment')] if six.b('wsApplicationDesc') in raw_data: self._metadata_parsed['experiment']['description'] = raw_data[six.b('wsApplicationDesc')].decode('utf8') if six.b('uLoopPars') in raw_data: self._metadata_parsed['experiment']['loops'] = self._parse_loop_data(raw_data[six.b('uLoopPars')]) ``` ```python def _parse_loop_data(self, loop_data): """Parse the experimental loop data Args: loop_data: dictionary of experiment loops Returns: list: list of the parsed loops """ loops = get_loops_from_data(loop_data) # take into account the absolute time in ms time_offset = 0 parsed_loops = [] for loop in loops: # duration of this loop duration = get_from_dict_if_exists('dDuration', loop) or 0 interval = determine_sampling_interval(duration, loop) # if duration is not saved, infer it duration = self.get_duration_from_interval_and_loops(duration, interval, loop) # uiLoopType == 6 is a stimulation loop is_stimulation = get_from_dict_if_exists('uiLoopType', loop) == 6 parsed_loop = { 'start': time_offset, 'duration': duration, 'stimulation': is_stimulation, 'sampling_interval': interval } parsed_loops.append(parsed_loop) # increase the time offset time_offset += duration return parsed_loops ``` -------------------------------- ### Initialize axis if size meets minimum Source: https://open-science-tools.github.io/nd2reader/_modules/nd2reader/reader.html Helper method that initializes an axis only if its size is at least the given minimum size. ```python def _init_axis_if_exists(self, axis, size, min_size=1): if size >= min_size: self._init_axis(axis, size) ``` -------------------------------- ### Pack raw data with metadata (recursive) Source: https://open-science-tools.github.io/nd2reader/_modules/nd2reader/artificial.html Recursively packs data with metadata, handling dict, int, float, and str types. Used for building nested ND2 data structures. ```python def _pack_raw_data_with_metadata(self, data): raw_data = b'' if isinstance(data, dict): raw_data = self._pack_dict_with_metadata(data) elif isinstance(data, int): raw_data = struct.pack('I', data) elif isinstance(data, float): raw_data = struct.pack('d', data) elif isinstance(data, str): raw_data = self._str_to_padded_bytes(data) return raw_data ``` -------------------------------- ### _parse_channels method Source: https://open-science-tools.github.io/nd2reader/_modules/nd2reader/raw_metadata.html Parses the channel labels from the image metadata sequence. Returns an empty list if the metadata is missing or the required keys are absent. ```python def _parse_channels(self): """These are labels created by the NIS Elements user. Typically they may a short description of the filter cube used (e.g. 'bright field', 'GFP', etc.) Returns: list: the color channels """ if self.image_metadata_sequence is None: return [] try: metadata = self.image_metadata_sequence[six.b('SLxPictureMetadata')][six.b('sPicturePlanes')] except KeyError: return [] channels = self._process_channels_metadata(metadata) return channels ``` -------------------------------- ### Pack dictionary with metadata Source: https://open-science-tools.github.io/nd2reader/_modules/nd2reader/artificial.html Packs a dictionary with metadata, including key names, data types, and nested data. Used for building complex ND2 metadata structures. ```python def _pack_dict_with_metadata(self, data): raw_data = b'' for data_key in data.keys(): # names have always one character extra and are padded in zero bytes??? b_data_key = self._str_to_padded_bytes(data_key) # header consists of data type and length of key name, it is represented by 2 unsigned chars raw_data += struct.pack('BB', self._get_data_type(data[data_key]), len(data_key) + 1) raw_data += b_data_key sub_data = self._pack_raw_data_with_metadata(data[data_key]) if isinstance(data[data_key], dict): # Pack: the number of keys and the length of raw data until now, sub data # and the 12 bytes that we add now raw_data += struct.pack("