Source code for brighteyes_ism.dataio.mcs

from fileinput import filename

import h5py
import re
import warnings

[docs] class metadata: """ Object containing the metadata read from a mcs data file. Attributes ---------- version : str Version of the MCS software that generated the h5 file. comment : str User comment rangex : float Field of view along the x-axis (um). rangey : float Field of view along the y-axis (um). rangez : float Field of view along the z-axis (um). nbin : int Number of time bins per pixel. nbin : int Number of time bins per pixel. dt : float Duration of each time bin (us) nx : int Number of pixel along the x-axis. ny : int Number of pixel along the y-axis. nz : int Number of pixel along the z-axis. nrep : int Number of repetitions. calib_x : float calibration value of the x pixel size (um/V). calib_y : float calibration value of the y pixel size (um/V). calib_z : float calibration value of the y pixel size (um/V). bitfile : str Bitfile name or path stored in the acquisition metadata. dfd_freq : int | None DFD repetition frequency parsed from the bitfile name, in MHz when available. dfd_nbins : int | None DFD histogram bin count parsed from the bitfile name, when available. dfd_activate : bool Whether DFD mode was active according to the FPGA configuration. Methods ------- pxdwelltime : float Returns the pixel dwell time. frametime : float Returns the duration of each fram (xy) acquisition. dx : float Returns the x pixel size (um). dy : float Returns the y pixel size (um). dz : float Returns the z pixel size (um). pxsizes : list Returns the list of pixel sizes in the z, y, x dimensions (um). nmicroim : int <returns total number of microimages read during the measurement. ndatapoints : int Returns the total number of words transferred from low level to high level (2 words per microimage). duration : float Returns thetotal measurement duration (s). Print(): Prints all the metadata on screen (name and value). """ # __slots__ = ['version', 'comment', 'rangex', 'rangey', 'rangez', 'nbin', 'dt', 'nx', 'ny', 'nz', 'nrep', 'calib_x', 'calib_y', 'calib_z'] def __init__(self, f): # MCS version self.version = f.attrs['data_format_version'] try: self.comment = f.attrs['comment'] except: self.comment = '' # range in um self.rangex = f['configurationGUI'].attrs['range_x'] self.rangey = f['configurationGUI'].attrs['range_y'] self.rangez = f['configurationGUI'].attrs['range_z'] # number of time bins per pixel self.nbin = f['configurationGUI'].attrs['timebin_per_pixel'] # time resolution in us self.dt = f['configurationGUI'].attrs['time_resolution'] # number of pixels in x, y, z direction self.nx = f['configurationGUI'].attrs['nx'] self.ny = f['configurationGUI'].attrs['ny'] self.nz = f['configurationGUI'].attrs['nframe'] # number of repetitions self.nrep = f['configurationGUI'].attrs['nrep'] # calibration values self.calib_x = f['configurationGUI'].attrs['calib_x'] self.calib_y = f['configurationGUI'].attrs['calib_y'] self.calib_z = f['configurationGUI'].attrs['calib_z'] try: self.bitfile = f["configurationGUI"].attrs["bitFile"] except Exception: self.bitfile = "" self._dfd_metadata_loaded = False self._dfd_freq = None self._dfd_nbins = None try: self.dfd_activate = f["configurationFPGA"].attrs["DFD_Activate"] except Exception: self.dfd_activate = False @property def pxdwelltime(self): # pixel dwell time in us return self.dt * self.nbin @property def frametime(self): # frame time in s return self.pxdwelltime * self.nx * self.ny / 1e6 @property def framerate(self): # frame rate in Hz return 1 / self.frametime @property def dx(self): # pixel size in x direction return self.rangex / self.nx @property def dy(self): # pixel size in y direction return self.rangey / self.ny @property def dz(self): # pixel size in z direction return self.rangez / self.nz @property def pxszizes(self): # List of pixel sizes in z, y, x directions return [self.dz, self.dy, self.dx] @property def nmicroim(self): # total number of microimages read during the measurement return self.nx * self.ny * self.nz * self.nrep * self.nbin @property def ndatapoints(self): # total number of words transferred from low level to high level # 2 words per microimage return 2 * self.nmicroim @property def duration(self): # total measurement duration in s return self.nmicroim * self.dt * 1e-6 def _load_dfd_metadata_from_bitfile_name(self): if self._dfd_metadata_loaded: return self._dfd_metadata_loaded = True self._dfd_freq, self._dfd_nbins = self.parse_dfd_metadata_from_bitfile_name(self.bitfile) @property def dfd_freq(self): self._load_dfd_metadata_from_bitfile_name() return self._dfd_freq @property def dfd_nbins(self): self._load_dfd_metadata_from_bitfile_name() return self._dfd_nbins
[docs] @staticmethod def parse_dfd_metadata_from_bitfile_name(bitfile="", default_cycle_mhz=40): """ Infer DFD metadata from a bitfile name token like ``40M91``. Accept any ``xxxxxMyyyyyyy`` token found in the basename, provided: - 3 < xxxxx < 100 - 3 < yyyyyyy < 1000 """ filename = str(bitfile).replace("\\", "/").split("/")[-1] match = re.search(r"(?P<cycle>\d+)M(?P<bins>\d+)", filename, re.IGNORECASE) if not match: warnings.warn( ( "\n" "================ WARNING ==============\n" "brighteyes_ism.dataio.mcs.load() failed to extract DFD metadata " f"from the bitfile name ({filename!r}).\n\n" "Falling back to defaults:\n" f" - Laser cycle frequency: {default_cycle_mhz} MHz\n" " - DFD bin count: NOT set\n\n" "If your data was acquired in DFD mode, THESE DEFAULTS ARE VERY " "LIKELY WRONG and will corrupt your analysis.\n\n" "You must explicitly set the correct DFD parameters in your " "analysis code.\n" "===========================================" ), stacklevel=2, ) return default_cycle_mhz, None parsed_cycle_mhz = int(match.group("cycle")) parsed_bins = int(match.group("bins")) if not (3 < parsed_cycle_mhz < 100 and 3 < parsed_bins < 1000): warnings.warn( ( "\n" "================ WARNING ==============\n" "brighteyes_ism.dataio.mcs.load() failed to extract DFD metadata " f"from the bitfile name ({filename!r}).\n\n" "Falling back to defaults:\n" f" - Laser cycle frequency: {default_cycle_mhz} MHz\n" " - DFD bin count: NOT set\n\n" "If your data was acquired in DFD mode, THESE DEFAULTS ARE VERY " "LIKELY WRONG and will corrupt your analysis.\n\n" "You must explicitly set the correct DFD parameters in your " "analysis code.\n" "===========================================" ), stacklevel=2, ) return default_cycle_mhz, None return parsed_cycle_mhz, parsed_bins
[docs] def Print(self): dic = self.__dict__ names = list(dic) values = list(dic.values()) for n, name in enumerate(names): print(name, end='') print(' ' * int(14 - len(name)), end='') print(str(values[n]))
[docs] def metadata_load(fname: str): ''' It loads the metadata of a mcs h5 file. Parameters ---------- fname : str h5 file path. Returns ------- meta : metadata object MCS metadata of the dataset. ''' with h5py.File(fname, "r") as f: meta = metadata(f) return meta
[docs] def metadata_print(fname: str): ''' It prints the metadata of a mcs h5 file. Parameters ---------- fname : str h5 file path. Returns ------- None. ''' with h5py.File(fname, "r") as f: meta = metadata(f) meta.Print()
[docs] def load(fname: str, key: str = 'data', data_format: str = 'numpy'): ''' Return numpy array with image data from MCS .h5 file Parameters ---------- fname : str h5 file path. key : str, optional Key name of the stored data. The default is 'data'. data_format : str, optional Return the data either as numpy array ('numpy') or as h5 file ('h5'). The default is 'numpy'. Returns ------- data: ndarray ISM dataset. meta : metadata object MCS metadata of the dataset. ''' with h5py.File(fname, "r") as f: data = f[key] meta = metadata(f) if data_format == 'numpy': return data[:], meta elif data_format == 'h5': return data, meta