Package that provides tools for brain MRI Deep Learning pre-processing.
Source code for brainprep.workflow.prequal
# -*- coding: utf-8 -*-
##########################################################################
# NSAp - Copyright (C) CEA, 2021 - 2023
# Distributed under the terms of the CeCILL-B license, as published by
# the CEA-CNRS-INRIA. Refer to the LICENSE file or to
# http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.html
# for details.
##########################################################################
"""
Interface for prequal.
"""
# Imports
import os
import glob
import shutil
import tempfile
import subprocess
import pandas as pd
from brainprep.color_utils import (
print_result, print_subtitle, print_title, print_command)
from brainprep.plotting import plot_hists
from brainprep.utils import listify
[docs]def brainprep_prequal(dwi, bvec, bval, pe, readout_time, output_dir, t1=None):
""" Define the dMRI pre-processing workflow.
Parameters
----------
dwi: str
path to the diffusion weighted image.
bvec:
path to the bvec file.
bval:
path to the bval file.
pe: str
the de phase encoding direction (i, i-, j, j-, k, k-).
readout_time: str
readout time of the dwi image.
output_dir: str
path to the output directory.
t1: str, default None
path to the t1 image in case of synb0 use.
Notes
-----
In order to use the synb0 feature you must bind your freesurfer license as
such: -B /path/to/freesurfer/license.txt:/APPS/freesurfer/license.txt
"""
print_title("INPUTS")
dwi = listify(dwi)
bval = listify(bval)
bvec = listify(bvec)
pe = listify(pe)
readout_time = list(readout_time)
print("diffusion image(s) : ", dwi, type(dwi))
if t1 is not None:
print("T1w image : ", t1, type(t1))
print("bvec file(s) : ", bvec, type(bvec))
print("bval file(s) :", bval, type(bval))
print("phase encoding direction : ", pe, type(pe))
print("readout time : ", readout_time, type(readout_time))
print("output directory : ", output_dir, type(output_dir))
print_title("check input for topup or synb0")
if (isinstance(dwi, list) and isinstance(bvec, list)
and isinstance(bval, list) and isinstance(pe, list)
and isinstance(readout_time, list) and len(dwi) == 2):
topup = True
print_result("Using topup")
else:
topup = False
print("Using synb0")
print_title("PreQual dtiQA pipeline")
pe_sign = []
pe_axe = None
for pe_ind in pe:
if pe_axe is None:
pe_axe = pe_ind[0]
else:
assert pe_axe == pe_ind[0], "Incoherant PE."
assert pe_axe in ["i", "j", "k"], "Invalid PE."
if pe_ind in ["i", "j", "k"]:
pe_sign.append("+")
elif pe_ind in ["i-", "j-", "k-"]:
pe_sign.append("-")
else:
raise Exception("Valid input for PE are (i, i-, j, j-, k, k-).")
print_subtitle("Making dtiQA_config.csv")
if topup:
dtiQA_config = [[os.path.basename(dwi[0]).split(".")[0],
pe_sign[0],
readout_time[0]],
["rpe",
pe_sign[1],
readout_time[1]]]
else:
dtiQA_config = [os.path.basename(dwi[0]).split(".")[0],
pe_sign[0],
readout_time[0]]
df_dtiQA_config = pd.DataFrame(dtiQA_config)
print_result("dtiQA_config file content :\n")
print_result(dtiQA_config)
print_subtitle("Copy before launch")
with tempfile.TemporaryDirectory() as tmpdir:
if not topup:
df_dtiQA_config = df_dtiQA_config.T
df_dtiQA_config.to_csv(os.path.join(tmpdir, "dtiQA_config.csv"),
sep=",", header=False, index=False)
shutil.copy(dwi[0], tmpdir)
shutil.copy(bvec[0], tmpdir)
shutil.copy(bval[0], tmpdir)
if topup:
shutil.copy(dwi[1], os.path.join(tmpdir, "rpe.nii.gz"))
shutil.copy(bvec[1], os.path.join(tmpdir, "rpe.bvec"))
shutil.copy(bval[1], os.path.join(tmpdir, "rpe.bval"))
else:
assert t1 is not None, "Need T1 image for SynB0."
shutil.copy(t1, os.path.join(tmpdir, "t1.nii.gz"))
print_subtitle("Launch prequal...")
cmd = ["xvfb-run", "-a", "--server-num=1",
"--server-args='-screen 0 1600x1280x24 -ac'",
"bash", "/CODE/run_dtiQA.sh", tmpdir, output_dir, pe_axe]
print_command(" ".join(cmd))
with subprocess.Popen(cmd,
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT) as process:
for line in process.stdout:
print(line.decode("utf8"))
[docs]def brainprep_prequal_qc(data_regex, outdir, sub_idx=-4, thr_low=0.3,
thr_up=0.75):
""" Define the dMRI pre-processing quality control workflow.
Parameters
----------
datadir: str
regex to the dmriprep 'stats.csv' files.
outdir: str
path to the destination folder.
sub_idx: int, default -4
the position of the subject identifier in the input path.
thr_low: float, default 0.3
lower treshold for outlier selection.
thr_up: float, default 0.75
upper treshold for outlier selection.
"""
import seaborn as sns
import matplotlib.pyplot as plt
print_title("Loading PreQual dtiQA stats files...")
stat_files = glob.glob(data_regex)
stats = []
for path in stat_files:
df = pd.read_csv(path, index_col=0, header=None).T
sid = path.split(os.sep)[sub_idx]
ses = path.split(os.sep)[sub_idx+1]
df["ses"] = ses
df["participant_id"] = sid
stats.append(df)
df = pd.concat(stats)
df.to_csv(os.path.join(outdir, "transformation.tsv"), sep="\t",
index=False)
print_result(os.path.join(outdir, "transformation.tsv"))
print_title("Computing box plot by category...")
for score_name in ("fa", "md", "rd", "ad"):
_cols = [name for name in df.columns
if name.endswith(f"_{score_name}")]
_df = pd.melt(df, id_vars=["participant_id"], value_vars=_cols,
var_name="metric", value_name="value")
_df["label"] = _df.metric.values
sns.set(style="whitegrid")
ax = sns.boxplot(x="label", y="value", data=_df)
plt.setp(ax.get_xticklabels(), rotation=90)
ax.tick_params(labelsize=3.5)
plt.tight_layout()
plt.savefig(os.path.join(outdir, f"{score_name}.png"), dpi=400)
_cols = ["eddy_avg_rotations_x", "eddy_avg_rotations_y",
"eddy_avg_rotations_z", "eddy_avg_translations_x",
"eddy_avg_translations_y", "eddy_avg_translations_z",
"eddy_avg_abs_displacement", "eddy_avg_rel_displacement"]
_df = pd.melt(df, id_vars=["participant_id"], value_vars=_cols,
var_name="metric", value_name="value")
sns.set(style="whitegrid")
ax = sns.boxplot(x="metric", y="value", data=_df)
plt.setp(ax.get_xticklabels(), rotation=90)
ax.tick_params(labelsize=3.5)
plt.tight_layout()
plt.savefig(os.path.join(outdir, "transformation.png"), dpi=400)
print_title("Outlier hist")
choosen_bundle = ["Genu_of_corpus_callosum_med_fa",
"Body_of_corpus_callosum_med_fa",
"Splenium_of_corpus_callosum_med_fa",
"Corticospinal_tract_L_med_fa",
"Corticospinal_tract_R_med_fa"]
choosen_bundle_pid = choosen_bundle.copy()
choosen_bundle_pid.append("participant_id")
choosen_bundle_pid.append("ses")
for fiber_bundle in choosen_bundle:
data = {"fa": {"data": df[fiber_bundle].values,
"bar_low": thr_low,
"bar_up": thr_up}}
snap = plot_hists(data, outdir, fiber_bundle)
print_result(snap)
result_df = df.loc[:, choosen_bundle_pid]
result_df["qc"] = 1
for faisceaux in choosen_bundle:
result_df.loc[result_df[faisceaux] <= thr_low, "qc"] = 0
result_df.loc[result_df[faisceaux] >= thr_up, "qc"] = 0
result_df.sort_values(by=["qc"], inplace=True)
result_df = result_df.reindex(columns=["participant_id", "ses"] +
choosen_bundle +
["qc"])
outdir_csv = os.path.join(outdir, "qc.tsv")
result_df.to_csv(outdir_csv, index=False, sep="\t")
print_result(outdir_csv)
Follow us