- czi: read tirf angle
- tiff: read spacing
This commit is contained in:
@@ -11,3 +11,4 @@
|
|||||||
/poetry.lock
|
/poetry.lock
|
||||||
/dist/
|
/dist/
|
||||||
/uv.lock
|
/uv.lock
|
||||||
|
.agentbridge
|
||||||
@@ -191,7 +191,8 @@ def get_positions(path: str | Path) -> Optional[list[int]]:
|
|||||||
return subclass.get_positions(AbstractReader.split_path_series(path)[0])
|
return subclass.get_positions(AbstractReader.split_path_series(path)[0])
|
||||||
|
|
||||||
|
|
||||||
class Imread(np.lib.mixins.NDArrayOperatorsMixin, ABC):
|
# noinspection PyAbstractClass
|
||||||
|
class Imread(np.lib.mixins.NDArrayOperatorsMixin):
|
||||||
"""class to read image files, while taking good care of important metadata,
|
"""class to read image files, while taking good care of important metadata,
|
||||||
currently optimized for .czi files, but can open anything that bioformats can handle
|
currently optimized for .czi files, but can open anything that bioformats can handle
|
||||||
path: path to the image file
|
path: path to the image file
|
||||||
@@ -1528,6 +1529,12 @@ class AbstractReader(Imread, metaclass=ABCMeta):
|
|||||||
self.immersionN = 1
|
self.immersionN = 1
|
||||||
|
|
||||||
p = re.compile(r"(\d+):(\d+)$")
|
p = re.compile(r"(\d+):(\d+)$")
|
||||||
|
if self.ome.structured_annotations is not None:
|
||||||
|
tirf_angles = {}
|
||||||
|
for annotation in self.ome.structured_annotations:
|
||||||
|
if annotation.description is not None and annotation.description.lower() == "tirfangle":
|
||||||
|
tirf_angles[int(annotation.id.split(":")[1])] = annotation.value
|
||||||
|
self.tirfangle = [angle for track, angle in sorted(tirf_angles.items(), key=lambda x: x[0])]
|
||||||
try:
|
try:
|
||||||
self.track, self.detector = zip(
|
self.track, self.detector = zip(
|
||||||
*[
|
*[
|
||||||
|
|||||||
@@ -248,7 +248,7 @@ class Reader(AbstractReader, ABC):
|
|||||||
return [(i - j, i - j + k) for i, j, k in zip(directory_entry.start, start, directory_entry.shape)]
|
return [(i - j, i - j + k) for i, j, k in zip(directory_entry.start, start, directory_entry.shape)]
|
||||||
|
|
||||||
@cached_property
|
@cached_property
|
||||||
def tiles(self):
|
def tiles(self) -> tuple[int, int]:
|
||||||
columns = 1
|
columns = 1
|
||||||
rows = 1
|
rows = 1
|
||||||
xml = self.reader.metadata()
|
xml = self.reader.metadata()
|
||||||
@@ -335,6 +335,7 @@ class OmeParse:
|
|||||||
self.get_light_sources()
|
self.get_light_sources()
|
||||||
self.get_filters()
|
self.get_filters()
|
||||||
self.get_pixels()
|
self.get_pixels()
|
||||||
|
self.get_tirf_angle()
|
||||||
self.get_channels()
|
self.get_channels()
|
||||||
self.get_planes()
|
self.get_planes()
|
||||||
self.get_annotations()
|
self.get_annotations()
|
||||||
@@ -555,6 +556,18 @@ class OmeParse:
|
|||||||
elif self.size_z > 1 and distance.attrib["Id"] == "Z":
|
elif self.size_z > 1 and distance.attrib["Id"] == "Z":
|
||||||
self.ome.images[0].pixels.physical_size_z = float(self.text(distance.find("Value"))) * 1e6
|
self.ome.images[0].pixels.physical_size_z = float(self.text(distance.find("Value"))) * 1e6
|
||||||
|
|
||||||
|
def get_tirf_angle(self) -> None:
|
||||||
|
if self.version == "1.0":
|
||||||
|
for track_setup in self.multi_track_setup:
|
||||||
|
tirf_angle = track_setup.find("TirfAngle")
|
||||||
|
if tirf_angle is not None:
|
||||||
|
self.ome.structured_annotations.append(
|
||||||
|
model.DoubleAnnotation(
|
||||||
|
description="TirfAngle", id="Annotation:0", value=50 * float(tirf_angle.text)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
self.ome.images[0].annotation_refs.append(model.AnnotationRef(id="Annotation:0"))
|
||||||
|
|
||||||
@cached_property
|
@cached_property
|
||||||
def positions(self) -> tuple[float, float, Optional[float]]:
|
def positions(self) -> tuple[float, float, Optional[float]]:
|
||||||
if self.version == "1.0":
|
if self.version == "1.0":
|
||||||
@@ -677,28 +690,30 @@ class OmeParse:
|
|||||||
else:
|
else:
|
||||||
light_source_settings = None
|
light_source_settings = None
|
||||||
|
|
||||||
self.ome.images[0].pixels.channels.append(
|
channel = dict(
|
||||||
model.Channel(
|
id=f"Channel:{idx}",
|
||||||
id=f"Channel:{idx}",
|
name=channel.attrib["Name"],
|
||||||
name=channel.attrib["Name"],
|
acquisition_mode=self.text(channel.find("AcquisitionMode")).replace( # type: ignore
|
||||||
acquisition_mode=self.text(channel.find("AcquisitionMode")).replace( # type: ignore
|
"SingleMoleculeLocalisation", "SingleMoleculeImaging"
|
||||||
"SingleMoleculeLocalisation", "SingleMoleculeImaging"
|
),
|
||||||
),
|
detector_settings=model.DetectorSettings(
|
||||||
color=color,
|
id=detector.attrib["Id"].replace(" ", ""), binning=binning
|
||||||
detector_settings=model.DetectorSettings(
|
),
|
||||||
id=detector.attrib["Id"].replace(" ", ""), binning=binning
|
emission_wavelength=emission_wavelength,
|
||||||
),
|
excitation_wavelength=self.try_default(
|
||||||
emission_wavelength=emission_wavelength,
|
float, None, self.text(channel.find("ExcitationWavelength"))
|
||||||
excitation_wavelength=self.try_default(
|
),
|
||||||
float, None, self.text(channel.find("ExcitationWavelength"))
|
# filter_set_ref=model.FilterSetRef(id=ome.instruments[0].filter_sets[filterset_idx].id),
|
||||||
),
|
illumination_type=self.text(channel.find("IlluminationType")), # type: ignore
|
||||||
# filter_set_ref=model.FilterSetRef(id=ome.instruments[0].filter_sets[filterset_idx].id),
|
light_source_settings=light_source_settings,
|
||||||
illumination_type=self.text(channel.find("IlluminationType")), # type: ignore
|
samples_per_pixel=samples_per_pixel,
|
||||||
light_source_settings=light_source_settings,
|
|
||||||
samples_per_pixel=samples_per_pixel,
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if color is not None:
|
||||||
|
channel["color"] = color
|
||||||
|
|
||||||
|
self.ome.images[0].pixels.channels.append(model.Channel(**channel))
|
||||||
|
|
||||||
def get_planes(self) -> None:
|
def get_planes(self) -> None:
|
||||||
try:
|
try:
|
||||||
exposure_times = [
|
exposure_times = [
|
||||||
|
|||||||
@@ -142,11 +142,18 @@ class Reader(AbstractReader, ABC):
|
|||||||
)
|
)
|
||||||
for c, z, t in product(range(size_c), range(size_z), range(size_t)):
|
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, delta_t=interval_t * t))
|
ome.images[0].pixels.planes.append(model.Plane(the_c=c, the_z=z, the_t=t, delta_t=interval_t * t))
|
||||||
|
if (spacing := self.metadata.get("spacing")) is not None:
|
||||||
|
ome.images[0].pixels.physical_size_z = spacing
|
||||||
|
if (unit := self.metadata.get("unit")) is not None and unit.lower() != "micron":
|
||||||
|
raise ValueError(f"cannot parse unit {unit}")
|
||||||
|
|
||||||
return ome
|
return ome
|
||||||
|
|
||||||
def open(self):
|
def open(self):
|
||||||
if self.series != 0:
|
if self.series != 0:
|
||||||
raise FileNotFoundError(f"Series {self.series} not found in {self.path}. Tifread only supports one series.")
|
raise FileNotFoundError(
|
||||||
|
f"Series {self.series} not found in {self.path}. Tifread only supports one series."
|
||||||
|
)
|
||||||
self.reader = tifffile.TiffFile(self.path)
|
self.reader = tifffile.TiffFile(self.path)
|
||||||
page = self.reader.pages.first
|
page = self.reader.pages.first
|
||||||
self.p_ndim = page.ndim # noqa
|
self.p_ndim = page.ndim # noqa
|
||||||
|
|||||||
+3
-3
@@ -1,6 +1,6 @@
|
|||||||
[project]
|
[project]
|
||||||
name = "ndbioimage"
|
name = "ndbioimage"
|
||||||
version = "2026.4.0"
|
version = "2026.7.0"
|
||||||
description = "Bio image reading, metadata and some affine registration."
|
description = "Bio image reading, metadata and some affine registration."
|
||||||
authors = [
|
authors = [
|
||||||
{ name = "W. Pomp", email = "w.pomp@nki.nl" }
|
{ name = "W. Pomp", email = "w.pomp@nki.nl" }
|
||||||
@@ -24,8 +24,8 @@ dependencies = [
|
|||||||
"pyyaml",
|
"pyyaml",
|
||||||
"SimpleITK-SimpleElastix; sys_platform != 'darwin'",
|
"SimpleITK-SimpleElastix; sys_platform != 'darwin'",
|
||||||
"scikit-image",
|
"scikit-image",
|
||||||
"tifffile <= 2025.1.10",
|
"tifffile >= 2024.1.30, <= 2025.1.10",
|
||||||
"tiffwrite >= 2024.12.1",
|
"tiffwrite >= 2026.5.0",
|
||||||
"tqdm",
|
"tqdm",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|||||||
Binary file not shown.
Reference in New Issue
Block a user