- seqread: lazily read delta_t

- calculate timeinterval more efficiently
- remove timeval field
- bugfix in retrieving laser wavelenghts and powers
- add series/pos to summary
- cziread: better assign lightsources to channels
This commit is contained in:
Wim Pomp
2023-07-05 12:34:43 +02:00
parent 8a74698fa5
commit b629f98727
4 changed files with 46 additions and 26 deletions

View File

@@ -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,9 +563,9 @@ 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,)
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)
@@ -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:

View File

@@ -360,10 +360,12 @@ class Reader(Imread, ABC):
light_sources_settings = channel.find("LightSourcesSettings")
# no space in ome for multiple lightsources simultaneously
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,

View File

@@ -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,}$")

View File

@@ -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 <w.pomp@nki.nl>"]
license = "GPLv3"