From 9c4c39fb39a651a5ab32eeec5fc9b143c1200386 Mon Sep 17 00:00:00 2001 From: Martin Laasmaa Date: Fri, 12 Dec 2025 23:52:34 +0200 Subject: [PATCH] Adding image converter --- src/utils/image_converters.py | 122 ++++++++++++++++++++++++++++++++++ 1 file changed, 122 insertions(+) create mode 100644 src/utils/image_converters.py diff --git a/src/utils/image_converters.py b/src/utils/image_converters.py new file mode 100644 index 0000000..17b52eb --- /dev/null +++ b/src/utils/image_converters.py @@ -0,0 +1,122 @@ +import numpy as np + +from roifile import ImagejRoi +from tifffile import TiffFile, TiffWriter +from pathlib import Path + + +class UT: + """ + Docstring for UT + + Operetta files along with rois drawn in ImageJ + """ + + def __init__(self, roifile_fn: Path): + self.roifile_fn = roifile_fn + self.rois = ImagejRoi.fromfile(self.roifile_fn) + self.stem = self.roifile_fn.stem.strip("-RoiSet") + self.image, self.image_props = self._load_images() + + def _load_images(self): + """Loading sequence of tif files + array sequence is CZYX + """ + print(self.roifile_fn.parent, self.stem) + fns = list(self.roifile_fn.parent.glob(f"{self.stem}*.tif*")) + stems = [fn.stem.split(self.stem)[-1] for fn in fns] + n_ch = len(set([stem.split("-ch")[-1].split("t")[0] for stem in stems])) + n_p = len(set([stem.split("-")[0] for stem in stems])) + n_t = len(set([stem.split("t")[1] for stem in stems])) + print(n_ch, n_p, n_t) + + with TiffFile(fns[0]) as tif: + img = tif.asarray() + w, h = img.shape + dtype = img.dtype + self.image_props = { + "channels": n_ch, + "planes": n_p, + "tiles": n_t, + "width": w, + "height": h, + "dtype": dtype, + } + + image_stack = np.zeros((n_ch, n_p, w, h), dtype=dtype) + for fn in fns: + with TiffFile(fn) as tif: + img = tif.asarray() + stem = fn.stem.split(self.stem)[-1] + ch = int(stem.split("-ch")[-1].split("t")[0]) + p = int(stem.split("-")[0].lstrip("p")) + t = int(stem.split("t")[1]) + print(fn.stem, "ch", ch, "p", p, "t", t) + image_stack[ch - 1, p - 1] = img + + print(image_stack.shape) + + return image_stack, self.image_props + + @property + def width(self): + return self.image_props["width"] + + @property + def height(self): + return self.image_props["height"] + + @property + def nchannels(self): + return self.image_props["channels"] + + @property + def nplanes(self): + return self.image_props["planes"] + + def export_rois( + self, + path: Path, + subfolder: str = "labels", + class_index: int = 0, + ): + """Export rois to a file""" + with open(path / subfolder / f"{self.stem}.txt", "w") as f: + for roi in self.rois: + # TODO add image coordinates normalization + coords = "" + for x, y in roi.subpixel_coordinates: + coords += f"{x/self.width} {y/self.height}" + f.write(f"{class_index} {coords}\n") + + return + + def export_image( + self, + path: Path, + subfolder: str = "images", + plane_mode: str = "max projection", + channel: int = 0, + ): + """Export image to a file""" + + if plane_mode == "max projection": + self.image = np.max(self.image[channel], axis=0) + print(self.image.shape) + + with TiffWriter(path / subfolder / f"{self.stem}.tif") as tif: + tif.write(self.image) + + +if __name__ == "__main__": + import argparse + + parser = argparse.ArgumentParser() + parser.add_argument("input", type=Path) + parser.add_argument("output", type=Path) + args = parser.parse_args() + + for rfn in args.input.glob("*.zip"): + ut = UT(rfn) + ut.export_rois(args.output, class_index=0) + ut.export_image(args.output, plane_mode="max projection", channel=0)