Skip to content

Commit a0575ba

Browse files
authored
Merge pull request #21 from nimh-dsst/eric-testing
BIDS App preparation and serial/parallel behavior
2 parents 9ad7f37 + ccfdb34 commit a0575ba

File tree

10 files changed

+506
-257
lines changed

10 files changed

+506
-257
lines changed

.gitignore

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,9 @@
11
__pycache__/
2-
as_qc_failures.csv
3-
*.swarm
42
*_testing/
53
*_examples/
6-
scripts_outputs/
74
.idea/
5+
.vscode/
86
examples/visualqc_prep/
97
examples/sub-01/
10-
user-testing/*
11-
12-
src/isolate_fsleyes_render_issue.py
8+
lookup.json
9+
rename.py

README.md

Lines changed: 170 additions & 70 deletions
Large diffs are not rendered by default.

images/defacing_pipeline.png

55 KB
Loading

images/dsst_defacing_wf_fig.png

-353 KB
Binary file not shown.

images/generate_mappings.png

-272 KB
Binary file not shown.

images/pipeline_screen_quality.png

-50.5 KB
Binary file not shown.

src/deface.py

Lines changed: 88 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
import gzip
2+
import re
3+
import shutil
4+
import os
15
import subprocess
26
from os import fspath
37
from pathlib import Path
@@ -51,6 +55,79 @@ def get_anat_dir_paths(subj_dir_path):
5155
return anat_dirs
5256

5357

58+
def compress_to_gz(input_file, output_file):
59+
if not output_file.exists():
60+
with open(input_file, 'rb') as f_input:
61+
with gzip.open(output_file, 'wb') as f_output:
62+
f_output.writelines(f_input)
63+
64+
65+
def copy_over_sidecar(scan_filepath, input_anat_dir, output_anat_dir):
66+
prefix = '_'.join([i for i in re.split('_|\.', scan_filepath.name) if i not in ['defaced', 'nii', 'gz']])
67+
filename = prefix + '.json'
68+
json_sidecar = input_anat_dir / filename
69+
shutil.copy2(json_sidecar, output_anat_dir / filename)
70+
71+
72+
def vqcdeface_prep(bids_input_dir, defaced_anat_dir, bids_defaced_outdir):
73+
defacing_qc_dir = bids_defaced_outdir.parent / 'QC_prep' / 'defacing_QC'
74+
interested_files = [f for f in defaced_anat_dir.rglob('*.nii.gz') if
75+
'work_dir' not in str(f).split('/')]
76+
print(interested_files)
77+
for defaced_img in interested_files:
78+
entities = defaced_img.name.split('.')[0].split('_')
79+
vqcd_subj_dir = defacing_qc_dir / f"{'/'.join(entities)}"
80+
vqcd_subj_dir.mkdir(parents=True, exist_ok=True)
81+
82+
defaced_link = vqcd_subj_dir / 'defaced.nii.gz'
83+
if not defaced_link.is_symlink():
84+
defaced_link.symlink_to(defaced_img)
85+
print(list(bids_input_dir.rglob(defaced_img.name)))
86+
img = list(bids_input_dir.rglob(defaced_img.name))[0]
87+
img_link = vqcd_subj_dir / 'orig.nii.gz'
88+
if not img_link.is_symlink(): img_link.symlink_to(img)
89+
90+
91+
def reorganize_into_bids(input_bids_dir, subj_dir, sess_dir, primary_t1, bids_defaced_outdir, no_clean):
92+
subj_id = subj_dir.name
93+
sess_id = sess_dir.name if sess_dir else None
94+
95+
if sess_id:
96+
anat_dirs = list(bids_defaced_outdir.joinpath(subj_id, sess_id).rglob('anat'))
97+
else:
98+
anat_dirs = list(bids_defaced_outdir.joinpath(subj_id).rglob('anat'))
99+
# make workdir for each session within anat dir
100+
for anat_dir in anat_dirs:
101+
# iterate over all nii files within an anat dir to rename all primary and "other" scans
102+
for nii_filepath in anat_dir.rglob('*nii*'):
103+
if nii_filepath.name.startswith('tmp.99.result'):
104+
# convert to nii.gz, rename and copy over to anat dir
105+
gz_file = anat_dir / Path(primary_t1).name
106+
compress_to_gz(nii_filepath, gz_file)
107+
108+
# copy over corresponding json sidecar
109+
copy_over_sidecar(Path(primary_t1), input_bids_dir / anat_dir.relative_to(bids_defaced_outdir),
110+
anat_dir)
111+
112+
elif nii_filepath.name.endswith('_defaced.nii.gz'):
113+
new_filename = '_'.join(nii_filepath.name.split('_')[:-1]) + '.nii.gz'
114+
shutil.copy2(nii_filepath, str(anat_dir / new_filename))
115+
116+
copy_over_sidecar(nii_filepath, input_bids_dir / anat_dir.relative_to(bids_defaced_outdir), anat_dir)
117+
118+
# move QC images and afni intermediate files to a new directory
119+
intermediate_files_dir = anat_dir / 'work_dir'
120+
intermediate_files_dir.mkdir(parents=True, exist_ok=True)
121+
for dirpath in anat_dir.glob('*'):
122+
if dirpath.name.startswith('workdir') or dirpath.name.endswith('QC'):
123+
shutil.move(str(dirpath), str(intermediate_files_dir))
124+
125+
vqcdeface_prep(input_bids_dir, anat_dir, bids_defaced_outdir)
126+
127+
if not no_clean:
128+
shutil.rmtree(intermediate_files_dir)
129+
130+
54131
def run_afni_refacer(primary_t1, others, subj_input_dir, sess_dir, output_dir):
55132
# constructing afni refacer command
56133
if primary_t1:
@@ -77,7 +154,7 @@ def run_afni_refacer(primary_t1, others, subj_input_dir, sess_dir, output_dir):
77154
refacer_cmd = f"@afni_refacer_run -input {primary_t1} -mode_deface -no_clean -prefix {fspath(subj_outdir / prefix)}"
78155

79156
# TODO remove module load afni
80-
full_cmd = f"module load afni ; {refacer_cmd}"
157+
full_cmd = f"module load afni ; export OMP_NUM_THREADS=1 ; {refacer_cmd}"
81158

82159
# TODO make log text less ugly; perhaps in a separate function
83160
log_filename = subj_outdir / 'defacing_pipeline.log'
@@ -102,6 +179,7 @@ def run_afni_refacer(primary_t1, others, subj_input_dir, sess_dir, output_dir):
102179

103180
# register other scans to the primary scan
104181
register.register_to_primary_scan(subj_input_dir, new_afni_workdir, primary_t1, others, log_fileobj)
182+
105183
else:
106184
log_fileobj.write(
107185
f"@afni_refacer_run work directory not found. Most probably because the refacer command failed.")
@@ -110,20 +188,26 @@ def run_afni_refacer(primary_t1, others, subj_input_dir, sess_dir, output_dir):
110188
return missing_refacer_out
111189

112190

113-
def deface_primary_scan(subj_input_dir, sess_dir, mapping_dict, output_dir):
191+
def deface_primary_scan(input_bids_dir, subj_input_dir, sess_dir, mapping_dict, output_dir, no_clean):
114192
missing_refacer_outputs = [] # list to capture missing afni refacer workdirs
115193

116-
subj_id = subj_input_dir.name
117-
sess_id = sess_dir.name if sess_dir else None
194+
subj_id = os.path.basename(subj_input_dir)
195+
sess_id = os.path.basename(sess_dir) if sess_dir else None
118196

119197
if sess_dir:
120198
primary_t1 = mapping_dict[subj_id][sess_id]['primary_t1']
121199
others = [str(s) for s in mapping_dict[subj_id][sess_id]['others'] if s != primary_t1]
122200
missing_refacer_outputs.append(run_afni_refacer(primary_t1, others, subj_input_dir, sess_dir, output_dir))
201+
print(f"Reorganizing {sess_dir} with defaced images into BIDS tree...\n")
202+
123203
else:
124204
primary_t1 = mapping_dict[subj_id]['primary_t1']
125205
others = [str(s) for s in mapping_dict[subj_id]['others'] if s != primary_t1]
126206
missing_refacer_outputs.append(run_afni_refacer(primary_t1, others, subj_input_dir, "", output_dir))
207+
print(f"Reorganizing {subj_input_dir} with defaced images into BIDS tree...\n")
208+
209+
# reorganizing the directory with defaced images into BIDS tree
210+
reorganize_into_bids(input_bids_dir, subj_input_dir, sess_dir, primary_t1, output_dir, no_clean)
127211

128212
return missing_refacer_outputs
129213

0 commit comments

Comments
 (0)