2025-12-12 23:52:34 +02:00
|
|
|
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
|
|
|
|
|
"""
|
|
|
|
|
|
2025-12-16 13:24:20 +02:00
|
|
|
def __init__(self, roifile_fn: Path, no_labels: bool):
|
2025-12-12 23:52:34 +02:00
|
|
|
self.roifile_fn = roifile_fn
|
2025-12-16 11:27:38 +02:00
|
|
|
print("is file", self.roifile_fn.is_file())
|
2025-12-16 13:24:20 +02:00
|
|
|
self.rois = None
|
|
|
|
|
if no_labels:
|
|
|
|
|
self.rois = ImagejRoi.fromfile(self.roifile_fn)
|
|
|
|
|
self.stem = self.roifile_fn.stem.split("Roi-")[1]
|
|
|
|
|
else:
|
|
|
|
|
self.roifile_fn = roifile_fn / roifile_fn.parts[-1]
|
|
|
|
|
self.stem = self.roifile_fn.stem
|
|
|
|
|
|
|
|
|
|
print(self.roifile_fn)
|
|
|
|
|
|
|
|
|
|
print(self.stem)
|
2025-12-12 23:52:34 +02:00
|
|
|
self.image, self.image_props = self._load_images()
|
|
|
|
|
|
|
|
|
|
def _load_images(self):
|
|
|
|
|
"""Loading sequence of tif files
|
|
|
|
|
array sequence is CZYX
|
|
|
|
|
"""
|
2025-12-16 13:24:20 +02:00
|
|
|
print("Loading images:", self.roifile_fn.parent, self.stem)
|
2025-12-16 11:27:38 +02:00
|
|
|
fns = list(self.roifile_fn.parent.glob(f"{self.stem.lower()}*.tif*"))
|
2025-12-12 23:52:34 +02:00
|
|
|
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]))
|
|
|
|
|
|
|
|
|
|
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,
|
|
|
|
|
}
|
2025-12-16 11:27:38 +02:00
|
|
|
print("Image props", self.image_props)
|
2025-12-12 23:52:34 +02:00
|
|
|
|
|
|
|
|
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])
|
2025-12-16 11:27:38 +02:00
|
|
|
p = int(stem.split("-")[0].split("p")[1])
|
2025-12-12 23:52:34 +02:00
|
|
|
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:
|
2025-12-16 11:27:38 +02:00
|
|
|
for i, roi in enumerate(self.rois):
|
|
|
|
|
rc = roi.subpixel_coordinates
|
|
|
|
|
if rc is None:
|
|
|
|
|
print(
|
|
|
|
|
f"No coordinates: {self.roifile_fn}, element {i}, out of {len(self.rois)}"
|
|
|
|
|
)
|
|
|
|
|
continue
|
|
|
|
|
xmn, ymn = rc.min(axis=0)
|
|
|
|
|
xmx, ymx = rc.max(axis=0)
|
|
|
|
|
xc = (xmn + xmx) / 2
|
|
|
|
|
yc = (ymn + ymx) / 2
|
|
|
|
|
bw = xmx - xmn
|
|
|
|
|
bh = ymx - ymn
|
|
|
|
|
coords = f"{xc/self.width} {yc/self.height} {bw/self.width} {bh/self.height} "
|
|
|
|
|
for x, y in rc:
|
|
|
|
|
coords += f"{x/self.width} {y/self.height} "
|
2025-12-12 23:52:34 +02:00
|
|
|
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)
|
|
|
|
|
|
2025-12-16 13:24:20 +02:00
|
|
|
print(path / subfolder / f"{self.stem}.tif")
|
2025-12-12 23:52:34 +02:00
|
|
|
with TiffWriter(path / subfolder / f"{self.stem}.tif") as tif:
|
|
|
|
|
tif.write(self.image)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if __name__ == "__main__":
|
|
|
|
|
import argparse
|
|
|
|
|
|
|
|
|
|
parser = argparse.ArgumentParser()
|
2025-12-16 11:27:38 +02:00
|
|
|
parser.add_argument("-i", "--input", nargs="*", type=Path)
|
|
|
|
|
parser.add_argument("-o", "--output", type=Path)
|
2025-12-16 13:24:20 +02:00
|
|
|
parser.add_argument(
|
|
|
|
|
"--no-labels",
|
|
|
|
|
action="store_false",
|
|
|
|
|
help="Source does not have labels, export only images",
|
|
|
|
|
)
|
2025-12-12 23:52:34 +02:00
|
|
|
args = parser.parse_args()
|
|
|
|
|
|
2025-12-16 11:27:38 +02:00
|
|
|
for path in args.input:
|
|
|
|
|
print("Path:", path)
|
2025-12-16 13:24:20 +02:00
|
|
|
if not args.no_labels:
|
|
|
|
|
print("No labels")
|
|
|
|
|
ut = UT(path, args.no_labels)
|
2025-12-16 11:27:38 +02:00
|
|
|
ut.export_image(args.output, plane_mode="max projection", channel=0)
|
|
|
|
|
|
2025-12-16 13:24:20 +02:00
|
|
|
else:
|
|
|
|
|
for rfn in Path(path).glob("*.zip"):
|
|
|
|
|
print("Roi FN:", rfn)
|
|
|
|
|
ut = UT(rfn, args.no_labels)
|
|
|
|
|
ut.export_rois(args.output, class_index=0)
|
|
|
|
|
ut.export_image(args.output, plane_mode="max projection", channel=0)
|
|
|
|
|
|
2025-12-16 11:27:38 +02:00
|
|
|
print()
|