diff --git a/ndbioimage/__init__.py b/ndbioimage/__init__.py index 0c342fd..1573843 100755 --- a/ndbioimage/__init__.py +++ b/ndbioimage/__init__.py @@ -17,12 +17,12 @@ from parfor import parfor from tiffwrite import IJTiffFile from numbers import Number from argparse import ArgumentParser -from ndbioimage.transforms import Transform, Transforms -from ndbioimage.jvm import JVM from typing import List from pathlib import Path from importlib.metadata import version from traceback import print_exc +from ndbioimage.transforms import Transform, Transforms +from ndbioimage.jvm import JVM try: @@ -535,8 +535,9 @@ class Imread(np.lib.mixins.NDArrayOperatorsMixin, metaclass=ABCMeta): self.objective = find(instrument.objectives, id=self.ome.images[0].objective_settings.id) else: self.objective = None - self.timeval = [find(image.pixels.planes, the_c=0, the_t=t, the_z=0).delta_t for t in range(self.shape['t'])] - self.timeinterval = np.diff(self.timeval).mean() if len(self.timeval) > 1 else 0 + t0 = find(image.pixels.planes, the_c=0, the_t=0, the_z=0).delta_t + t1 = find(image.pixels.planes, the_c=0, the_t=self.shape['t'] - 1, the_z=0).delta_t + self.timeinterval = (t1 - t0) / (self.shape['t'] - 1) if self.shape['t'] > 1 else None try: self.binning = [int(i) for i in image.pixels.channels[0].detector_settings.binning.value.split('x')] self.pxsize *= self.binning[0] @@ -562,10 +563,10 @@ class Imread(np.lib.mixins.NDArrayOperatorsMixin, metaclass=ABCMeta): for channel in image.pixels.channels if channel.detector_settings and find(instrument.detectors, id=channel.detector_settings.id).amplification_gain] - self.laserwavelengths = [(channel.emission_wavelength_quantity.to(self.ureg.nm).m,) - for channel in pixels.channels if channel.emission_wavelength_quantity] - self.laserpowers = try_default(lambda channel: [(1 - channel.light_source_settings.attenuation,) - for channel in pixels.channels], []) + self.laserwavelengths = [(channel.excitation_wavelength_quantity.to(self.ureg.nm).m,) + for channel in pixels.channels if channel.excitation_wavelength_quantity] + self.laserpowers = try_default(lambda: [(1 - channel.light_source_settings.attenuation,) + for channel in pixels.channels], []) self.filter = try_default(lambda: [find(instrument.filter_sets, id=channel.filter_set_ref.id).model for channel in image.pixels.channels], None) self.pxsize_um = None if self.pxsize is None else self.pxsize.to(self.ureg.um).m @@ -697,6 +698,7 @@ class Imread(np.lib.mixins.NDArrayOperatorsMixin, metaclass=ABCMeta): def summary(self): """ gives a helpful summary of the recorded experiment """ s = [f"path/filename: {self.path}", + f"series/pos: {self.series}", f"reader: {self.__class__.__module__.split('.')[-1]}", f"shape ({self.axes}):".ljust(15) + f"{' x '.join(str(i) for i in self.shape)}"] if self.pxsize_um: diff --git a/ndbioimage/readers/cziread.py b/ndbioimage/readers/cziread.py index fe1c074..bee1bd6 100644 --- a/ndbioimage/readers/cziread.py +++ b/ndbioimage/readers/cziread.py @@ -360,10 +360,12 @@ class Reader(Imread, ABC): light_sources_settings = channel.find("LightSourcesSettings") # no space in ome for multiple lightsources simultaneously - light_source_settings = light_sources_settings[0] + if len(light_sources_settings) > idx: + light_source_settings = light_sources_settings[idx] + else: + light_source_settings = light_sources_settings[0] light_source_settings = model.LightSourceSettings( - id="_".join([light_source_settings.find("LightSource").attrib["Id"] - for light_source_settings in light_sources_settings]), + id=light_source_settings.find("LightSource").attrib["Id"], attenuation=float(text(light_source_settings.find("Attenuation"))), wavelength=float(text(light_source_settings.find("Wavelength"))), wavelength_unit=nm) @@ -376,7 +378,7 @@ class Reader(Imread, ABC): color=model.simple_types.Color(text(channels_ds[channel.attrib["Id"]].find("Color"))), detector_settings=model.DetectorSettings(id=detector.attrib["Id"], binning=binning), emission_wavelength=text(channel.find("EmissionWavelength")), - excitation_wavelength=text(channel.find("ExcitationWavelength")), + excitation_wavelength=light_source_settings.wavelength, filter_set_ref=model.FilterSetRef(id=ome.instruments[0].filter_sets[filterset_idx].id), illumination_type=text(channel.find("IlluminationType")), light_source_settings=light_source_settings, diff --git a/ndbioimage/readers/seqread.py b/ndbioimage/readers/seqread.py index e021005..50f64e7 100644 --- a/ndbioimage/readers/seqread.py +++ b/ndbioimage/readers/seqread.py @@ -5,10 +5,35 @@ from ndbioimage import Imread from pathlib import Path from functools import cached_property from ome_types import model +from ome_types._base_type import quantity_property from itertools import product from datetime import datetime from abc import ABC -from parfor import pmap + + +def lazy_property(function, field, *arg_fields): + def lazy(self): + if self.__dict__.get(field) is None: + self.__dict__[field] = function(*[getattr(self, arg_field) for arg_field in arg_fields]) + self.__fields_set__.add(field) + return self.__dict__[field] + return property(lazy) + + +class Plane(model.Plane): + """ Lazily retrieve delta_t from metadata """ + def __init__(self, t0, file, **kwargs): + super().__init__(**kwargs) + setattr(self.__class__, 'delta_t', lazy_property(self.get_delta_t, 'delta_t', 't0', 'file')) + setattr(self.__class__, 'delta_t_quantity', quantity_property('delta_t')) + self.__dict__['t0'] = t0 + self.__dict__['file'] = file + + @staticmethod + def get_delta_t(t0, file): + with tifffile.TiffFile(file) as tif: + info = yaml.safe_load(tif.pages[0].tags[50839].value['Info']) + return float((datetime.strptime(info["Time"], "%Y-%m-%d %H:%M:%S %z") - t0).seconds) class Reader(Imread, ABC): @@ -65,19 +90,10 @@ class Reader(Imread, ABC): physical_size_z=metadata["Info"]["Summary"]["z-step_um"]), objective_settings=model.ObjectiveSettings(id="Objective:0"))) - def timeval_fun(i): - with tifffile.TiffFile(self.filedict[i]) as tif: - info = yaml.safe_load(tif.pages[0].tags[50839].value['Info']) - return (datetime.strptime(info["Time"], "%Y-%m-%d %H:%M:%S %z") - t0).seconds - - length = size_c * size_z * size_t - timeval = pmap(timeval_fun, product(range(size_c), range(size_z), range(size_t)), length=length, - serial=length <= 24, desc='Reading metadata') - - for (c, z, t), time in zip(product(range(size_c), range(size_z), range(size_t)), timeval): + for c, z, t in product(range(size_c), range(size_z), range(size_t)): ome.images[0].pixels.planes.append( - model.Plane( - the_c=c, the_z=z, the_t=t, exposure_time=metadata["Info"]["Exposure-ms"] / 1000, delta_t=time)) + Plane(t0, self.filedict[c, z, t], + the_c=c, the_z=z, the_t=t, exposure_time=metadata["Info"]["Exposure-ms"] / 1000)) # compare channel names from metadata with filenames pattern_c = re.compile(r"img_\d{3,}_(.*)_\d{3,}$") diff --git a/pyproject.toml b/pyproject.toml index 138a9a7..e03da36 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "ndbioimage" -version = "2023.7.0" +version = "2023.7.1" description = "Bio image reading, metadata and some affine registration." authors = ["W. Pomp "] license = "GPLv3"