- main_channel and default_transform arguments for Imread.with_transform

This commit is contained in:
w.pomp
2026-01-26 17:03:27 +01:00
parent 066a39719a
commit 8ff52f5af5
3 changed files with 28 additions and 14 deletions

View File

@@ -1253,6 +1253,8 @@ class Imread(np.lib.mixins.NDArrayOperatorsMixin, ABC):
drift: bool = False, drift: bool = False,
file: Path | str = None, file: Path | str = None,
bead_files: Sequence[Path | str] = (), bead_files: Sequence[Path | str] = (),
main_channel: int = None,
default_transform: Sequence[float] = None,
) -> View: ) -> View:
"""returns a view where channels and/or frames are registered with an affine transformation """returns a view where channels and/or frames are registered with an affine transformation
channels: True/False register channels using bead_files channels: True/False register channels using bead_files
@@ -1260,6 +1262,11 @@ class Imread(np.lib.mixins.NDArrayOperatorsMixin, ABC):
file: load registration from file with name file, default: transform.yml in self.path.parent file: load registration from file with name file, default: transform.yml in self.path.parent
bead_files: files used to register channels, default: files in self.path.parent, bead_files: files used to register channels, default: files in self.path.parent,
with names starting with 'beads' with names starting with 'beads'
main channel: which channel to transform with default_transform
default_transform: parameters of the default transform: [a, b, c, d, e, f] where the affine transform matrix is
| a b e |
| c d f |
| 0 0 1 |
""" """
view = self.view() view = self.view()
if file is None: if file is None:
@@ -1278,7 +1285,7 @@ class Imread(np.lib.mixins.NDArrayOperatorsMixin, ABC):
try: try:
view.transform = Transforms.from_file(file, T=drift) view.transform = Transforms.from_file(file, T=drift)
except Exception: # noqa except Exception: # noqa
view.transform = Transforms().with_beads(view.cyllens, bead_files) view.transform = Transforms().with_beads(view.cyllens, bead_files, main_channel, default_transform)
if drift: if drift:
view.transform = view.transform.with_drift(view) view.transform = view.transform.with_drift(view)
view.transform.save(file.with_suffix(".yml")) view.transform.save(file.with_suffix(".yml"))

View File

@@ -147,9 +147,11 @@ class Transforms(dict):
else: else:
raise TypeError("Not a pandas DataFrame or Series.") raise TypeError("Not a pandas DataFrame or Series.")
def with_beads(self, cyllens, bead_files): def with_beads(self, cyllens, bead_files, main_channel=None, default_transform=None):
assert len(bead_files) > 0, "At least one file is needed to calculate the registration." assert len(bead_files) > 0, "At least one file is needed to calculate the registration."
transforms = [self.calculate_channel_transforms(file, cyllens) for file in bead_files] transforms = [
self.calculate_channel_transforms(file, cyllens, main_channel, default_transform) for file in bead_files
]
for key in {key for transform in transforms for key in transform.keys()}: for key in {key for transform in transforms for key in transform.keys()}:
new_transforms = [transform[key] for transform in transforms if key in transform] new_transforms = [transform[key] for transform in transforms if key in transform]
if len(new_transforms) == 1: if len(new_transforms) == 1:
@@ -191,7 +193,7 @@ class Transforms(dict):
return checked_files return checked_files
@staticmethod @staticmethod
def calculate_channel_transforms(bead_file, cyllens): def calculate_channel_transforms(bead_file, cyllens, main_channel=None, default_transform=None):
"""When no channel is not transformed by a cylindrical lens, assume that the image is scaled by a factor 1.162 """When no channel is not transformed by a cylindrical lens, assume that the image is scaled by a factor 1.162
in the horizontal direction""" in the horizontal direction"""
from . import Imread from . import Imread
@@ -204,25 +206,30 @@ class Transforms(dict):
untransformed = [c for c in range(im.shape["c"]) if cyllens[im.detector[c]].lower() == "none"] untransformed = [c for c in range(im.shape["c"]) if cyllens[im.detector[c]].lower() == "none"]
good_and_untrans = sorted(set(goodch) & set(untransformed)) good_and_untrans = sorted(set(goodch) & set(untransformed))
if main_channel is None:
if good_and_untrans: if good_and_untrans:
masterch = good_and_untrans[0] main_channel = good_and_untrans[0]
else: else:
masterch = goodch[0] main_channel = goodch[0]
transform = Transform() transform = Transform()
if not good_and_untrans: if not good_and_untrans:
matrix = transform.matrix matrix = transform.matrix
if default_transform is None:
matrix[0, 0] = 0.86 matrix[0, 0] = 0.86
else:
for i, t in zip(([0, 0], [0, 1], [1, 0], [1, 1], [0, 2], [1, 2]), default_transform):
matrix[i] = t
transform.matrix = matrix transform.matrix = matrix
transforms = Transforms() transforms = Transforms()
for c in tqdm(goodch, desc="Calculating channel transforms"): # noqa for c in tqdm(goodch, desc="Calculating channel transforms"): # noqa
if c == masterch: if c == main_channel:
transforms[im.channel_names[c]] = transform transforms[im.channel_names[c]] = transform
else: else:
transforms[im.channel_names[c]] = Transform.register(max_ims[masterch], max_ims[c]) * transform transforms[im.channel_names[c]] = Transform.register(max_ims[main_channel], max_ims[c]) * transform
return transforms return transforms
@staticmethod @staticmethod
def save_channel_transform_tiff(bead_files, tiffile): def save_channel_transform_tiff(bead_files, tiffile, default_transform=None):
from . import Imread from . import Imread
n_channels = 0 n_channels = 0
@@ -232,7 +239,7 @@ class Transforms(dict):
with IJTiffFile(tiffile) as tif: with IJTiffFile(tiffile) as tif:
for t, file in enumerate(bead_files): for t, file in enumerate(bead_files):
with Imread(file) as im: with Imread(file) as im:
with Imread(file).with_transform() as jm: with Imread(file).with_transform(default_transform=default_transform) as jm:
for c in range(im.shape["c"]): for c in range(im.shape["c"]):
tif.save(np.hstack((im(c=c, t=0).max("z"), jm(c=c, t=0).max("z"))), c, 0, t) tif.save(np.hstack((im(c=c, t=0).max("z"), jm(c=c, t=0).max("z"))), c, 0, t)

View File

@@ -1,6 +1,6 @@
[project] [project]
name = "ndbioimage" name = "ndbioimage"
version = "2026.1.0" version = "2026.1.1"
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" }