Skip to content

Commit 6734d48

Browse files
authored
Merge pull request #491 from cbinyu/bvecs_just_for_dwi
BF: Ensure bvec/bval files are only created for dwi output
2 parents b1d80fe + 5099d01 commit 6734d48

File tree

6 files changed

+106
-4
lines changed

6 files changed

+106
-4
lines changed

heudiconv/convert.py

Lines changed: 37 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
)
4242

4343
LOCKFILE = 'heudiconv.lock'
44+
DW_IMAGE_IN_FMAP_FOLDER_WARNING = 'Diffusion-weighted image saved in non dwi folder ({folder})'
4445
lgr = logging.getLogger(__name__)
4546

4647

@@ -674,9 +675,22 @@ def save_converted_files(res, item_dicoms, bids_options, outtype, prefix, outnam
674675
return
675676

676677
if isdefined(res.outputs.bvecs) and isdefined(res.outputs.bvals):
677-
outname_bvecs, outname_bvals = prefix + '.bvec', prefix + '.bval'
678-
safe_movefile(res.outputs.bvecs, outname_bvecs, overwrite)
679-
safe_movefile(res.outputs.bvals, outname_bvals, overwrite)
678+
if prefix_dirname.endswith('dwi'):
679+
outname_bvecs, outname_bvals = prefix + '.bvec', prefix + '.bval'
680+
safe_movefile(res.outputs.bvecs, outname_bvecs, overwrite)
681+
safe_movefile(res.outputs.bvals, outname_bvals, overwrite)
682+
else:
683+
if bvals_are_zero(res.outputs.bvals):
684+
os.remove(res.outputs.bvecs)
685+
os.remove(res.outputs.bvals)
686+
lgr.debug("%s and %s were removed since not dwi", res.outputs.bvecs, res.outputs.bvals)
687+
else:
688+
lgr.warning(DW_IMAGE_IN_FMAP_FOLDER_WARNING.format(folder= prefix_dirname))
689+
lgr.warning(".bvec and .bval files will be generated. This is NOT BIDS compliant")
690+
outname_bvecs, outname_bvals = prefix + '.bvec', prefix + '.bval'
691+
safe_movefile(res.outputs.bvecs, outname_bvecs, overwrite)
692+
safe_movefile(res.outputs.bvals, outname_bvals, overwrite)
693+
680694

681695
if isinstance(res_files, list):
682696
res_files = sorted(res_files)
@@ -806,3 +820,23 @@ def add_taskname_to_infofile(infofiles):
806820

807821
# write to outfile
808822
save_json(infofile, meta_info)
823+
824+
825+
def bvals_are_zero(bval_file):
826+
"""Checks if all entries in a bvals file are zero (or 5, for Siemens files).
827+
Returns True if that is the case, otherwise returns False
828+
829+
Parameters
830+
----------
831+
bval_file : file with the bvals
832+
833+
Returns
834+
-------
835+
True if all are zero; False otherwise.
836+
"""
837+
838+
with open(bval_file) as f:
839+
bvals = f.read().split()
840+
841+
bvals_unique = set(float(b) for b in bvals)
842+
return bvals_unique == {0.} or bvals_unique == {5.}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
"""Heuristic to extract a b-value=0 DWI image (basically, a SE-EPI)
2+
both as a fmap and as dwi
3+
4+
It is used just to test that a 'DIFFUSION' image that the user
5+
chooses to extract as fmap (pepolar case) doesn't produce _bvecs/
6+
_bvals json files, while it does for dwi images
7+
"""
8+
9+
10+
def create_key(template, outtype=('nii.gz',), annotation_classes=None):
11+
if template is None or not template:
12+
raise ValueError('Template must be a valid format string')
13+
return template, outtype, annotation_classes
14+
15+
def infotodict(seqinfo):
16+
"""Heuristic evaluator for determining which runs belong where
17+
18+
allowed template fields - follow python string module:
19+
20+
item: index within category
21+
subject: participant id
22+
seqitem: run number during scanning
23+
subindex: sub index within group
24+
"""
25+
fmap = create_key('sub-{subject}/fmap/sub-{subject}_acq-b0dwi_epi')
26+
dwi = create_key('sub-{subject}/dwi/sub-{subject}_acq-b0dwi_dwi')
27+
28+
info = {fmap: [], dwi: []}
29+
for s in seqinfo:
30+
if 'DIFFUSION' in s.image_type:
31+
info[fmap].append(s.series_id)
32+
info[dwi].append(s.series_id)
33+
return info
Binary file not shown.
Binary file not shown.
Binary file not shown.

heudiconv/tests/test_convert.py

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,18 @@
11
"""Test functions in heudiconv.convert module.
22
"""
3+
import os.path as op
4+
from glob import glob
5+
36
import pytest
7+
from .utils import TESTS_DATA_PATH
48

59
from heudiconv.convert import (update_complex_name,
610
update_multiecho_name,
7-
update_uncombined_name)
11+
update_uncombined_name,
12+
DW_IMAGE_IN_FMAP_FOLDER_WARNING,
13+
)
814
from heudiconv.bids import BIDSError
15+
from heudiconv.cli.run import main as runner
916

1017

1118
def test_update_complex_name():
@@ -76,3 +83,31 @@ def test_update_uncombined_name():
7683
out_fn_true = 'sub-X_ses-Y_task-Z_run-01_ch-04_bold'
7784
out_fn_test = update_uncombined_name(metadata, fn, channel_names)
7885
assert out_fn_test == out_fn_true
86+
87+
88+
def test_b0dwi_for_fmap(tmpdir, capfd):
89+
"""Make sure we raise a warning when .bvec and .bval files
90+
are present but the modality is not dwi.
91+
We check it by extracting a few DICOMs from a series with
92+
bvals: 5 5 1500
93+
"""
94+
tmppath = tmpdir.strpath
95+
subID = 'b0dwiForFmap'
96+
args = (
97+
"-c dcm2niix -o %s -b -f test_b0dwi_for_fmap --files %s -s %s"
98+
% (tmpdir, op.join(TESTS_DATA_PATH, 'b0dwiForFmap'), subID)
99+
).split(' ')
100+
runner(args)
101+
102+
# assert that it raised a warning that the fmap directory will contain
103+
# bvec and bval files.
104+
output = capfd.readouterr().err.split('\n')
105+
expected_msg = DW_IMAGE_IN_FMAP_FOLDER_WARNING.format(folder=op.join(tmppath, 'sub-%s', 'fmap') % subID)
106+
assert [o for o in output if expected_msg in o]
107+
108+
# check that both 'fmap' and 'dwi' directories have been extracted and they contain
109+
# *.bvec and a *.bval files
110+
for mod in ['fmap', 'dwi']:
111+
assert op.isdir(op.join(tmppath, 'sub-%s', mod) % (subID))
112+
for ext in ['bval', 'bvec']:
113+
assert glob(op.join(tmppath, 'sub-%s', mod, 'sub-%s_*.%s') % (subID, subID, ext))

0 commit comments

Comments
 (0)