Source code for snake.toolkit.cli.reconstruction

"""CLI for SNAKE."""

import gc
import json
import logging
import os
from pathlib import Path

import numpy as np
from omegaconf import DictConfig, OmegaConf

from snake.mrd_utils import (
    CartesianFrameDataLoader,
    MRDLoader,
    NonCartesianFrameDataLoader,
    parse_sim_conf,
    read_mrd_header,
)
from snake.toolkit.analysis.stats import contrast_zscore, get_scores
from snake.toolkit.cli.config import cleanup_cuda, conf_validator, make_hydra_cli

log = logging.getLogger(__name__)


[docs] def reconstruction(cfg: DictConfig) -> None: """Reconstruction function.""" # Disable HDF5 file locking os.environ["HDF5_USE_FILE_LOCKING"] = "FALSE" cfg = conf_validator(cfg) log.info("Starting Reconstruction") hdr = read_mrd_header(cfg.filename) *version, engine = hdr.acquisitionSystemInformation.systemModel.split("-") log.info(f"Data from {version}, using engine {engine}") # Extract sim_confg sim_conf = parse_sim_conf(hdr) if sim_conf != cfg.sim_conf: log.warning( "Loaded simulation configuration is different from the one in the config." " using the one from CLI." ) log.warning("Loaded: %s", sim_conf) log.warning("Config: %s", cfg.sim_conf) sim_conf = cfg.sim_conf DataLoader: type[MRDLoader] if engine in ["EPI", "EVI"]: DataLoader = CartesianFrameDataLoader elif engine == "NUFFT": DataLoader = NonCartesianFrameDataLoader # Reconstructor.setup(sim_conf) # initialize operators # array = Reconstructor.reconstruct(dataloader, sim_conf) with DataLoader(cfg.filename) as data_loader: for name, rec in cfg.reconstructors.items(): rec_str = str(rec) # FIXME Also use parameters of reconstructors data_rec_file = Path(f"data_rec_{rec_str}.npy") log.info(f"Using {name} reconstructor") rec.setup(sim_conf) rec_data = rec.reconstruct(data_loader, sim_conf) log.info(f"Reconstruction done with {name}") # Save the reconstruction np.save(data_rec_file, rec_data) log.info(f"Saved to {data_rec_file.resolve()}") phantom = data_loader.get_phantom() roi_mask = phantom.masks[phantom.labels == cfg.stats.roi_tissue_name] dyn_datas = data_loader.get_all_dynamic() waveform_name = f"activation-{cfg.stats.event_name}" good_d = None for d in dyn_datas: if d.name == waveform_name: good_d = d if good_d is None: raise ValueError("No dynamic data found matching waveform name") bold_signal = good_d.data[0] bold_sample_time = np.arange(len(bold_signal)) * sim_conf.seq.TR / 1000 del phantom del dyn_datas gc.collect() results = [] for _name, rec in cfg.reconstructors.items(): rec_str = str(rec) # FIXME Also use parameters of reconstructors data_rec_file = Path(f"data_rec_{rec_str}.npy").resolve() data_zscore_file = Path(f"data_zscore_{rec_str}.npy").resolve() rec_data = np.load(data_rec_file) TR_vol = sim_conf.max_sim_time / len(rec_data) z_score = contrast_zscore( rec_data, TR_vol, bold_signal, bold_sample_time, cfg.stats.event_name ) stats_results = get_scores(z_score, roi_mask, cfg.stats.roi_threshold) np.save(data_zscore_file, z_score) # Reload the config from hydra and add it to the result file # This way OmegaConf does all the serialization for us. conf_dict = OmegaConf.load(Path.cwd() / ".hydra" / "config.yaml") conf_dict = OmegaConf.to_container(conf_dict) results.append( conf_dict | { "results": stats_results, "data_zscore": data_zscore_file, "data_rec": data_rec_file, "data_raw": cfg.filename.resolve(), } ) with open("results.json", "w") as f: json.dump(results, f, default=lambda o: str(o)) cleanup_cuda()
reconstruction_cli = make_hydra_cli(reconstruction) if __name__ == "__main__": reconstruction_cli()