# Copyright (c) Thomas Else 2023-25.
# License: MIT
import argparse
import glob
import re
from os.path import join
import h5py
import numpy as np
from ..utils import sort_key
[docs]
def init_argparse():
map = {
"TRUE": True,
"FALSE": False,
"T": True,
"F": False,
"YES": True,
"NO": False,
"Y": True,
"N": False,
}
def map_fn(x):
return map[x.upper()]
parser = argparse.ArgumentParser(
description="Copy ROIs between scans made of same thing at same time."
)
parser.add_argument("input", type=str, help="Data Folder")
parser.add_argument(
"-r", "--regex", default=None, type=str, help="Regex for parsing name"
)
parser.add_argument(
"-c",
"--copyclose",
default=False,
type=map_fn,
help="Automatically copy over roi to closest "
"slice if not exact matches (tolerance "
"1mm)",
)
parser.add_argument(
"-d", "--deleteold", default=True, type=map_fn, help="Delete old copies."
)
parser.add_argument(
"-j",
"--justdeletecopies",
default=False,
type=map_fn,
help="Just delete copies of rois.",
)
parser.add_argument(
"-f", "--copyfrom", default=None, help="Copy only from scan type (e.g. OE)."
)
parser.add_argument(
"-t", "--copyto", default=None, help="Copy only to scan type (e.g. DCE)."
)
return parser
[docs]
def main():
p = init_argparse()
args = p.parse_args()
DATA_FOLDER = args.input
regex = args.regex
if args.regex is None:
regex = r"^(?P<mouse>[^_\- ]+)[_\- ](?P<ear>[^_\- ]+)[_\- ]((?P<scan_number>[0-9]+)[_\- ])?(?P<scan_type>.+)$"
elif args.regex == "tom":
regex = r"^(?P<date>[0-9]+)\-(?P<mouse>.+)\-(?P<scan_type>.+)"
elif args.regex == "marilena":
regex = r"VMstudy_MEO#(?P<number>[0-9]+)_((Day(?P<day>[0-9]+))|MSOT)_?(?P<scan_type>GS|SS|MS|GC|again)?"
regex = re.compile(regex)
scan_groups = {}
for file in sorted(
glob.glob(join(DATA_FOLDER, "**", "*.hdf5"), recursive=True), key=sort_key
):
data = h5py.File(file, "r")
name_regex = regex.fullmatch(data["raw_data"].attrs["name"])
print(file, data["raw_data"].attrs["name"], name_regex)
if name_regex is None:
continue
name_regex = name_regex.groupdict()
scan_group = []
for k in name_regex:
if k != "scan_type":
if name_regex[k] is not None:
scan_group.append(name_regex[k])
else:
scan_group.append("1")
scan_group = "-".join(scan_group)
if scan_group not in scan_groups:
scan_groups[scan_group] = [file]
else:
scan_groups[scan_group].append(file)
data.close()
for group in scan_groups:
rois = []
roi_attrs = []
roi_names = []
roi_numbers = []
roi_scan_origin = []
for file in scan_groups[group]:
data = h5py.File(file, "r+")
if args.copyfrom is not None:
if args.copyfrom not in data["raw_data"].attrs["name"]:
continue
if "rois" in data:
for a in data["rois"]:
deletion = False # track whether any rois deleted so we can renumber at the end
for b in data["rois"][a]:
if data["rois"][a][b].attrs.get("copy", default=False):
if args.deleteold or args.justdeletecopies:
del data["rois"][a][b]
deletion = True
continue
rois.append(data["rois"][a][b][:])
roi_attrs.append(dict(data["rois"][a][b].attrs))
roi_names.append(a)
roi_numbers.append(b)
roi_scan_origin.append(data["raw_data"].attrs["name"])
if deletion:
original = sorted(list(data["rois"][a].keys()), key=int)
change = [str(x) for x in range(len(original))]
for old, new in zip(original, change):
data["rois"][a].move(old, new)
data.close()
for file in scan_groups[group]:
data = h5py.File(file, "r+")
if args.copyto is not None:
if args.copyto not in data["raw_data"].attrs["name"]:
continue
for roi, att, nam, num, ori in zip(
rois, roi_attrs, roi_names, roi_numbers, roi_scan_origin
):
if ori != data["raw_data"].attrs["name"]:
zs = np.unique(data["Z-POS"][:])
make_copy = False
if att["z"] in zs:
# print("Able to copy from", ori, data["raw_data"].attrs["name"])
make_copy = True
elif np.any(np.isclose(zs, att["z"], atol=1, rtol=0)):
print(
"Not exact, but close for: ",
ori,
"to",
data["raw_data"].attrs["name"],
)
copy = (
input("Not exact match, copy anyway? Y/[N]")
if not args.copyclose
else True
)
if copy in ["Y", "y", True]:
make_copy = True
old_z = att["z"]
att["z"] = zs[np.argmin(np.abs(zs - att["z"]))]
print(
"Copying, changing z from {:.2f} to {:.2f}".format(
old_z, att["z"]
)
)
else:
# print("No close positions for: ", ori, "to", data["raw_data"].attrs["name"], "so skipping.")
# print(zs[np.argmin(np.abs(zs - att["z"]))], att["z"])
# print(np.isclose(zs, att["z"], atol=0.1, rtol=0))
make_copy = False
if make_copy and not args.justdeletecopies:
# copy the roi to scan
roi_grp = data.require_group("rois")
roi_grp = roi_grp.require_group(nam)
roi_num = str(len(roi_grp.keys()))
roi_data = roi_grp.create_dataset(roi_num, data=roi)
roi_data.attrs["copy"] = True
for a in att:
roi_data.attrs[a] = att[a]
data.close()