pesky-fieldmaps/shim_delta.py
Michał Szczepanik e744431151 Add scripts for investigating field maps
The focus is on visual grepping, ie. printing a few lines for each
subject to get an overview of the situation. We require JSON sidecars to
be present (to have been gotten).
2026-04-29 15:58:46 +02:00

77 lines
2.9 KiB
Python

# /// script
# dependencies = ["numpy"]
# ///
#
# This script prints whether shims are identical within a subset of
# acquisitions, and how far off they are from the first. See arguments
# (or --help) and code comments for details.
import argparse
import json
from pathlib import Path
import numpy as np
def load_shim(p: str | Path):
p = Path(p) if isinstance(p, str) else p
with p.open() as fp:
d = json.load(fp)
return np.array(d["ShimSetting"])
parser = argparse.ArgumentParser(formatter_class=argparse.ArgumentDefaultsHelpFormatter)
parser.add_argument("ds_path", type=Path, help="Path to BIDS dataset")
parser.add_argument("--func", action="store_true", help="Include func in comparison")
parser.add_argument("--dwi", action="store_true", help="Include dwi in comparison")
parser.add_argument("--session", default="001", help="Session label")
parser.add_argument("--subject", help="Focus on this subject (label) and print more")
args = parser.parse_args()
max_rel_deltas = []
for subject_path in sorted(list(args.ds_path.glob("sub-*"))):
if args.subject is not None and subject_path.name.split("-")[-1] != args.subject:
continue
# load shims
fmap_sidecars = list((subject_path / f"ses-{args.session}" / "fmap").glob("*.json"))
func_sidecars = list((subject_path / f"ses-{args.session}" / "func").glob("*.json"))
dwi_sidecars = list((subject_path / f"ses-{args.session}" / "dwi").glob("*.json"))
if not all(p.is_file() for p in fmap_sidecars + func_sidecars + dwi_sidecars):
print(subject_path.name, "----")
continue
if len(fmap_sidecars) == 0:
print(subject_path.name, "----")
continue
fmap_shims = np.array([load_shim(p) for p in sorted(fmap_sidecars)]) # file x shim
func_shims = np.array([load_shim(p) for p in sorted(func_sidecars)])
dwi_shims = np.array([load_shim(p) for p in sorted(dwi_sidecars)])
# restrict to chosen modalities
to_stack = [fmap_shims]
if args.func:
to_stack.append(func_shims)
if args.dwi:
to_stack.append(dwi_shims)
shims = np.vstack(to_stack)
all_equal = np.all(shims == shims[0])
rel_delta = np.abs((shims - shims[0]) / shims[0])
# rel_vec_dist = np.linalg.norm(shims - shims[0], axis=1) / np.linalg.norm(shims[0])
# if showing single subject, print all shims, and biggest relative difference per dimension
if args.subject is not None:
print(shims)
print(np.round(np.max(rel_delta, axis=0), 3))
exit()
# if showing all subjects, print whether all shims are equal, followed by biggest relative difference if non-zero
if all_equal:
print(subject_path.name, all_equal)
max_rel_deltas.append(0)
else:
max_rel_deltas.append(np.max(rel_delta))
print(subject_path.name, all_equal, np.round(np.max(rel_delta), 3))
print("Median of max relative deltas", np.round(np.median(np.array(max_rel_deltas)), 3))