Skip to content

Commit 394a848

Browse files
committed
parallelizing reorganization of bids dir
1 parent 9ad7f37 commit 394a848

File tree

2 files changed

+65
-64
lines changed

2 files changed

+65
-64
lines changed

src/deface.py

Lines changed: 61 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
import gzip
2+
import re
3+
import shutil
14
import subprocess
25
from os import fspath
36
from pathlib import Path
@@ -51,6 +54,57 @@ def get_anat_dir_paths(subj_dir_path):
5154
return anat_dirs
5255

5356

57+
def compress_to_gz(input_file, output_file):
58+
if not output_file.exists():
59+
with open(input_file, 'rb') as f_input:
60+
with gzip.open(output_file, 'wb') as f_output:
61+
f_output.writelines(f_input)
62+
63+
64+
def copy_over_sidecar(scan_filepath, input_anat_dir, output_anat_dir):
65+
prefix = '_'.join([i for i in re.split('_|\.', scan_filepath.name) if i not in ['defaced', 'nii', 'gz']])
66+
filename = prefix + '.json'
67+
json_sidecar = input_anat_dir / filename
68+
shutil.copy2(json_sidecar, output_anat_dir / filename)
69+
70+
71+
def reorganize_into_bids(input_bids_dir, subj_dir, sess_dir, primary_t1, defacing_outdir, no_clean):
72+
subj_id = subj_dir.name
73+
sess_id = sess_dir.name if sess_dir else None
74+
75+
if sess_id:
76+
anat_dirs = list(defacing_outdir.joinpath(subj_id, sess_id).rglob('anat'))
77+
else:
78+
anat_dirs = list(defacing_outdir.joinpath(subj_id).rglob('anat'))
79+
# make workdir for each session within anat dir
80+
for anat_dir in anat_dirs:
81+
# iterate over all nii files within an anat dir to rename all primary and "other" scans
82+
for nii_filepath in anat_dir.rglob('*nii*'):
83+
if nii_filepath.name.startswith('tmp.99.result'):
84+
# convert to nii.gz, rename and copy over to anat dir
85+
gz_file = anat_dir / Path(primary_t1).name
86+
compress_to_gz(nii_filepath, gz_file)
87+
88+
# copy over corresponding json sidecar
89+
copy_over_sidecar(Path(primary_t1), input_bids_dir / anat_dir.relative_to(defacing_outdir), anat_dir)
90+
91+
elif nii_filepath.name.endswith('_defaced.nii.gz'):
92+
new_filename = '_'.join(nii_filepath.name.split('_')[:-1]) + '.nii.gz'
93+
shutil.copy2(nii_filepath, str(anat_dir / new_filename))
94+
95+
copy_over_sidecar(nii_filepath, input_bids_dir / anat_dir.relative_to(defacing_outdir), anat_dir)
96+
97+
# move QC images and afni intermediate files to a new directory
98+
intermediate_files_dir = anat_dir / 'workdir'
99+
intermediate_files_dir.mkdir(parents=True, exist_ok=True)
100+
for dirpath in anat_dir.glob('*'):
101+
if dirpath.name.startswith('workdir') or dirpath.name.endswith('QC'):
102+
shutil.move(dirpath, intermediate_files_dir)
103+
104+
# if not no_clean:
105+
# shutil.rmtree(intermediate_files_dir)
106+
107+
54108
def run_afni_refacer(primary_t1, others, subj_input_dir, sess_dir, output_dir):
55109
# constructing afni refacer command
56110
if primary_t1:
@@ -102,6 +156,7 @@ def run_afni_refacer(primary_t1, others, subj_input_dir, sess_dir, output_dir):
102156

103157
# register other scans to the primary scan
104158
register.register_to_primary_scan(subj_input_dir, new_afni_workdir, primary_t1, others, log_fileobj)
159+
105160
else:
106161
log_fileobj.write(
107162
f"@afni_refacer_run work directory not found. Most probably because the refacer command failed.")
@@ -110,7 +165,7 @@ def run_afni_refacer(primary_t1, others, subj_input_dir, sess_dir, output_dir):
110165
return missing_refacer_out
111166

112167

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

116171
subj_id = subj_input_dir.name
@@ -120,11 +175,16 @@ def deface_primary_scan(subj_input_dir, sess_dir, mapping_dict, output_dir):
120175
primary_t1 = mapping_dict[subj_id][sess_id]['primary_t1']
121176
others = [str(s) for s in mapping_dict[subj_id][sess_id]['others'] if s != primary_t1]
122177
missing_refacer_outputs.append(run_afni_refacer(primary_t1, others, subj_input_dir, sess_dir, output_dir))
178+
123179
else:
124180
primary_t1 = mapping_dict[subj_id]['primary_t1']
125181
others = [str(s) for s in mapping_dict[subj_id]['others'] if s != primary_t1]
126182
missing_refacer_outputs.append(run_afni_refacer(primary_t1, others, subj_input_dir, "", output_dir))
127183

184+
# reorganizing the directory with defaced images into BIDS tree
185+
print(f"Reorganizing {sess_dir} with defaced images into BIDS tree...\n")
186+
reorganize_into_bids(input_bids_dir, subj_input_dir, sess_dir, primary_t1, output_dir, no_clean)
187+
128188
return missing_refacer_outputs
129189

130190

src/dsst_defacing_wf.py

Lines changed: 4 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
import argparse
2-
import gzip
32
import json
43
import re
5-
import shutil
64
import subprocess
75
from pathlib import Path
86

@@ -58,61 +56,6 @@ def get_sess_dirs(subj_dir_path, mapping_dict):
5856
return sess_dirs
5957

6058

61-
def compress_to_gz(input_file, output_file):
62-
if not output_file.exists():
63-
with open(input_file, 'rb') as f_input:
64-
with gzip.open(output_file, 'wb') as f_output:
65-
f_output.writelines(f_input)
66-
67-
68-
def copy_over_sidecar(scan_filepath, input_anat_dir, output_anat_dir):
69-
prefix = '_'.join([i for i in re.split('_|\.', scan_filepath.name) if i not in ['defaced', 'nii', 'gz']])
70-
filename = prefix + '.json'
71-
json_sidecar = input_anat_dir / filename
72-
shutil.copy2(json_sidecar, output_anat_dir / filename)
73-
74-
75-
def reorganize_into_bids(input_dir, defacing_outputs, mapping_dict, no_clean):
76-
# make workdir for each session within anat dir
77-
for anat_dir in defacing_outputs.rglob('anat'):
78-
sess_id = None
79-
# extract subject/session IDs
80-
subj_id = [i for i in anat_dir.parts if i.startswith('sub-')][0]
81-
82-
# one anat dir associated with one sess dir, if at all
83-
for s in anat_dir.parts:
84-
if s.startswith('ses-'):
85-
sess_id = s
86-
87-
primary_t1 = mapping_dict[subj_id][sess_id]['primary_t1'] if sess_id else mapping_dict[subj_id]['primary_t1']
88-
89-
# iterate over all nii files within an anat dir to rename all primary and "other" scans
90-
for nii_filepath in anat_dir.rglob('*nii*'):
91-
if nii_filepath.name.startswith('tmp.99.result'):
92-
# convert to nii.gz, rename and copy over to anat dir
93-
gz_file = anat_dir / Path(primary_t1).name
94-
compress_to_gz(nii_filepath, gz_file)
95-
96-
# copy over corresponding json sidecar
97-
copy_over_sidecar(Path(primary_t1), input_dir / anat_dir.relative_to(defacing_outputs), anat_dir)
98-
99-
elif nii_filepath.name.endswith('_defaced.nii.gz'):
100-
new_filename = '_'.join(nii_filepath.name.split('_')[:-1]) + '.nii.gz'
101-
shutil.copy2(nii_filepath, str(anat_dir / new_filename))
102-
103-
copy_over_sidecar(nii_filepath, input_dir / anat_dir.relative_to(defacing_outputs), anat_dir)
104-
105-
# move QC images and afni intermediate files to a new directory
106-
intermediate_files_dir = anat_dir / 'workdir'
107-
intermediate_files_dir.mkdir(parents=True, exist_ok=True)
108-
for dirpath in anat_dir.glob('*'):
109-
if dirpath.name.startswith('workdir') or dirpath.name.endswith('QC'):
110-
shutil.move(dirpath, intermediate_files_dir)
111-
112-
# if not no_clean:
113-
# shutil.rmtree(intermediate_files_dir)
114-
115-
11659
def generate_3d_renders(defaced_img, render_outdir):
11760
rotations = [(45, 5, 10), (-45, 5, 10)]
11861
for idx, rot in enumerate(rotations):
@@ -121,6 +64,7 @@ def generate_3d_renders(defaced_img, render_outdir):
12164
fsleyes_render_cmd = f"fsleyes render --scene 3d -rot {yaw} {pitch} {roll} --outfile {outfile} {defaced_img} -dr 20 250 -in spline -bf 0.3 -r 100 -ns 500"
12265
print(fsleyes_render_cmd)
12366
run_command(fsleyes_render_cmd)
67+
print(f"Has the render been created? {outfile.exists()}")
12468

12569

12670
def create_defacing_id_list(qc_dir):
@@ -141,7 +85,7 @@ def vqcdeface_prep(input_dir, defacing_output_dir):
14185
defaced_link = vqcd_subj_dir / 'defaced.nii.gz'
14286
if not defaced_link.exists():
14387
defaced_link.symlink_to(defaced_img)
144-
generate_3d_renders(defaced_img, vqcd_subj_dir)
88+
generate_3d_renders(defaced_img, vqcd_subj_dir)
14589

14690
img = list(input_dir.rglob(defaced_img.name))[0]
14791
img_link = vqcd_subj_dir / 'orig.nii.gz'
@@ -182,7 +126,8 @@ def main():
182126

183127
# calling deface.py script
184128
for subj_sess in subj_sess_list:
185-
missing_refacer_out = deface.deface_primary_scan(subj_sess[0], subj_sess[1], mapping_dict, defacing_outputs)
129+
missing_refacer_out = deface.deface_primary_scan(input_dir, subj_sess[0], subj_sess[1], mapping_dict,
130+
defacing_outputs, no_clean)
186131
if missing_refacer_out is not None:
187132
afni_refacer_failures.extend(missing_refacer_out)
188133

@@ -192,10 +137,6 @@ def main():
192137
# unload fsl module and use fsleyes installed on conda env
193138
run_command(f"export TMP_DISPLAY=`echo $DISPLAY`; unset DISPLAY; module unload fsl")
194139

195-
# reorganizing the directory with defaced images into BIDS tree
196-
print(f"Reorganizing the directory with defaced images into BIDS tree...\n")
197-
reorganize_into_bids(input_dir, defacing_outputs, mapping_dict, no_clean)
198-
199140
# prep for visual inspection using visualqc deface
200141
print(f"Preparing for QC by visual inspection...\n")
201142

0 commit comments

Comments
 (0)