- 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 tiffwrite import IJTiffFile
from numbers import Number from numbers import Number
from argparse import ArgumentParser from argparse import ArgumentParser
from ndbioimage.transforms import Transform, Transforms
from ndbioimage.jvm import JVM
from typing import List from typing import List
from pathlib import Path from pathlib import Path
from importlib.metadata import version from importlib.metadata import version
from traceback import print_exc from traceback import print_exc
from ndbioimage.transforms import Transform, Transforms
from ndbioimage.jvm import JVM
try: 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) self.objective = find(instrument.objectives, id=self.ome.images[0].objective_settings.id)
else: else:
self.objective = None 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'])] t0 = find(image.pixels.planes, the_c=0, the_t=0, the_z=0).delta_t
self.timeinterval = np.diff(self.timeval).mean() if len(self.timeval) > 1 else 0 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: try:
self.binning = [int(i) for i in image.pixels.channels[0].detector_settings.binning.value.split('x')] self.binning = [int(i) for i in image.pixels.channels[0].detector_settings.binning.value.split('x')]
self.pxsize *= self.binning[0] self.pxsize *= self.binning[0]
@@ -562,9 +563,9 @@ class Imread(np.lib.mixins.NDArrayOperatorsMixin, metaclass=ABCMeta):
for channel in image.pixels.channels for channel in image.pixels.channels
if channel.detector_settings if channel.detector_settings
and find(instrument.detectors, id=channel.detector_settings.id).amplification_gain] and find(instrument.detectors, id=channel.detector_settings.id).amplification_gain]
self.laserwavelengths = [(channel.emission_wavelength_quantity.to(self.ureg.nm).m,) self.laserwavelengths = [(channel.excitation_wavelength_quantity.to(self.ureg.nm).m,)
for channel in pixels.channels if channel.emission_wavelength_quantity] for channel in pixels.channels if channel.excitation_wavelength_quantity]
self.laserpowers = try_default(lambda channel: [(1 - channel.light_source_settings.attenuation,) self.laserpowers = try_default(lambda: [(1 - channel.light_source_settings.attenuation,)
for channel in pixels.channels], []) for channel in pixels.channels], [])
self.filter = try_default(lambda: [find(instrument.filter_sets, id=channel.filter_set_ref.id).model self.filter = try_default(lambda: [find(instrument.filter_sets, id=channel.filter_set_ref.id).model
for channel in image.pixels.channels], None) for channel in image.pixels.channels], None)
@@ -697,6 +698,7 @@ class Imread(np.lib.mixins.NDArrayOperatorsMixin, metaclass=ABCMeta):
def summary(self): def summary(self):
""" gives a helpful summary of the recorded experiment """ """ gives a helpful summary of the recorded experiment """
s = [f"path/filename: {self.path}", s = [f"path/filename: {self.path}",
f"series/pos: {self.series}",
f"reader: {self.__class__.__module__.split('.')[-1]}", f"reader: {self.__class__.__module__.split('.')[-1]}",
f"shape ({self.axes}):".ljust(15) + f"{' x '.join(str(i) for i in self.shape)}"] f"shape ({self.axes}):".ljust(15) + f"{' x '.join(str(i) for i in self.shape)}"]
if self.pxsize_um: if self.pxsize_um:

View File

@@ -360,10 +360,12 @@ class Reader(Imread, ABC):
light_sources_settings = channel.find("LightSourcesSettings") light_sources_settings = channel.find("LightSourcesSettings")
# no space in ome for multiple lightsources simultaneously # 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 = light_sources_settings[0]
light_source_settings = model.LightSourceSettings( light_source_settings = model.LightSourceSettings(
id="_".join([light_source_settings.find("LightSource").attrib["Id"] id=light_source_settings.find("LightSource").attrib["Id"],
for light_source_settings in light_sources_settings]),
attenuation=float(text(light_source_settings.find("Attenuation"))), attenuation=float(text(light_source_settings.find("Attenuation"))),
wavelength=float(text(light_source_settings.find("Wavelength"))), wavelength=float(text(light_source_settings.find("Wavelength"))),
wavelength_unit=nm) wavelength_unit=nm)
@@ -376,7 +378,7 @@ class Reader(Imread, ABC):
color=model.simple_types.Color(text(channels_ds[channel.attrib["Id"]].find("Color"))), color=model.simple_types.Color(text(channels_ds[channel.attrib["Id"]].find("Color"))),
detector_settings=model.DetectorSettings(id=detector.attrib["Id"], binning=binning), detector_settings=model.DetectorSettings(id=detector.attrib["Id"], binning=binning),
emission_wavelength=text(channel.find("EmissionWavelength")), 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), filter_set_ref=model.FilterSetRef(id=ome.instruments[0].filter_sets[filterset_idx].id),
illumination_type=text(channel.find("IlluminationType")), illumination_type=text(channel.find("IlluminationType")),
light_source_settings=light_source_settings, light_source_settings=light_source_settings,

View File

@@ -5,10 +5,35 @@ from ndbioimage import Imread
from pathlib import Path from pathlib import Path
from functools import cached_property from functools import cached_property
from ome_types import model from ome_types import model
from ome_types._base_type import quantity_property
from itertools import product from itertools import product
from datetime import datetime from datetime import datetime
from abc import ABC 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): class Reader(Imread, ABC):
@@ -65,19 +90,10 @@ class Reader(Imread, ABC):
physical_size_z=metadata["Info"]["Summary"]["z-step_um"]), physical_size_z=metadata["Info"]["Summary"]["z-step_um"]),
objective_settings=model.ObjectiveSettings(id="Objective:0"))) objective_settings=model.ObjectiveSettings(id="Objective:0")))
def timeval_fun(i): for c, z, t in product(range(size_c), range(size_z), range(size_t)):
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):
ome.images[0].pixels.planes.append( ome.images[0].pixels.planes.append(
model.Plane( Plane(t0, self.filedict[c, z, t],
the_c=c, the_z=z, the_t=t, exposure_time=metadata["Info"]["Exposure-ms"] / 1000, delta_t=time)) the_c=c, the_z=z, the_t=t, exposure_time=metadata["Info"]["Exposure-ms"] / 1000))
# compare channel names from metadata with filenames # compare channel names from metadata with filenames
pattern_c = re.compile(r"img_\d{3,}_(.*)_\d{3,}$") pattern_c = re.compile(r"img_\d{3,}_(.*)_\d{3,}$")

View File

@@ -1,6 +1,6 @@
[tool.poetry] [tool.poetry]
name = "ndbioimage" name = "ndbioimage"
version = "2023.7.0" version = "2023.7.1"
description = "Bio image reading, metadata and some affine registration." description = "Bio image reading, metadata and some affine registration."
authors = ["W. Pomp <w.pomp@nki.nl>"] authors = ["W. Pomp <w.pomp@nki.nl>"]
license = "GPLv3" license = "GPLv3"