Skip to content

Commit 46c0bdb

Browse files
committed
ENH: Allows the user to save the Phoenix Report (Siemens) in the sourcedata
This way the user can go back to the scanner and import the exact same protocol that was run for a given subject, improving reproducibility.
1 parent d8e5c5f commit 46c0bdb

File tree

6 files changed

+84
-3
lines changed

6 files changed

+84
-3
lines changed

heudiconv/dicoms.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -265,8 +265,13 @@ def group_dicoms_into_seqinfos(files, grouping, file_filter=None,
265265
series_id = '-'.join(map(str, series_id))
266266
if mw.image_shape is None:
267267
# this whole thing has no image data (maybe just PSg DICOMs)
268-
# nothing to see here, just move on
269-
continue
268+
# If this is a Siemens PhoenixZipReport or PhysioLog, keep it:
269+
if mw.dcm_data.SeriesDescription == 'PhoenixZIPReport':
270+
# give it a dummy shape, so that we can continue:
271+
mw.image_shape = (0, 0, 0)
272+
else:
273+
# nothing to see here, just move on
274+
continue
270275
seqinfo = create_seqinfo(mw, series_files, series_id)
271276

272277
if per_studyUID:
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
"""Heuristic demonstrating conversion of the PhoenixZIPReport from Siemens.
2+
3+
It only cares about converting a series with have PhoenixZIPReport in their
4+
series_description and outputs **only to sourcedata**.
5+
"""
6+
7+
8+
def create_key(template, outtype=('nii.gz',), annotation_classes=None):
9+
if template is None or not template:
10+
raise ValueError('Template must be a valid format string')
11+
return template, outtype, annotation_classes
12+
13+
14+
def infotodict(seqinfo):
15+
"""Heuristic evaluator for determining which runs belong where
16+
17+
allowed template fields - follow python string module:
18+
19+
item: index within category
20+
subject: participant id
21+
seqitem: run number during scanning
22+
subindex: sub index within group
23+
"""
24+
sbref = create_key('sub-{subject}/func/sub-{subject}_task-QA_sbref', outtype=('nii.gz', 'dicom',))
25+
scout = create_key('sub-{subject}/anat/sub-{subject}_T1w', outtype=('nii.gz', 'dicom',))
26+
phoenix_doc = create_key('sub-{subject}/misc/sub-{subject}_phoenix', outtype=('dicom',))
27+
28+
info = {sbref: [], scout: [], phoenix_doc: []}
29+
for s in seqinfo:
30+
if (
31+
'PhoenixZIPReport' in s.series_description
32+
and s.image_type[3] == 'CSA REPORT'
33+
):
34+
info[phoenix_doc].append({'item': s.series_id})
35+
if 'scout' in s.series_description.lower():
36+
info[scout].append({'item': s.series_id})
37+
38+
return info
Binary file not shown.
Binary file not shown.

heudiconv/tests/test_dicoms.py

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,18 @@
11
import os.path as op
22
import json
3+
from glob import glob
34

45
import pytest
56

67
from heudiconv.external.pydicom import dcm
78
from heudiconv.cli.run import main as runner
89
from heudiconv.convert import nipype_convert
9-
from heudiconv.dicoms import parse_private_csa_header, embed_dicom_and_nifti_metadata
10+
from heudiconv.dicoms import (
11+
parse_private_csa_header,
12+
embed_dicom_and_nifti_metadata,
13+
group_dicoms_into_seqinfos,
14+
OrderedDict,
15+
)
1016
from .utils import (
1117
assert_cwd_unchanged,
1218
TESTS_DATA_PATH,
@@ -64,3 +70,18 @@ def test_embed_dicom_and_nifti_metadata(tmpdir):
6470

6571
assert out3.pop("existing") == "data"
6672
assert out3 == out2
73+
74+
75+
def test_group_dicoms_into_seqinfos(tmpdir):
76+
"""Tests for group_dicoms_into_seqinfos"""
77+
78+
# 1) Check that it works for PhoenixDocuments:
79+
# set up testing files
80+
dcmfolder = op.join(TESTS_DATA_PATH, 'Phoenix')
81+
dcmfiles = glob(op.join(dcmfolder, '*', '*.dcm'))
82+
83+
seqinfo = group_dicoms_into_seqinfos(dcmfiles, 'studyUID', flatten=True)
84+
85+
assert type(seqinfo) is OrderedDict
86+
assert len(seqinfo) == len(dcmfiles)
87+
assert [s.series_description for s in seqinfo] == ['AAHead_Scout_32ch-head-coil', 'PhoenixZIPReport']

heudiconv/tests/test_heuristics.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -176,3 +176,20 @@ def test_notop(tmpdir, bidsoptions):
176176
assert not op.exists(pjoin(tmppath, 'Halchenko/Yarik/950_bids_test4', fname))
177177
else:
178178
assert op.exists(pjoin(tmppath, 'Halchenko/Yarik/950_bids_test4', fname))
179+
180+
181+
def test_phoenix_doc_conversion(tmpdir):
182+
tmppath = tmpdir.strpath
183+
subID = 'Phoenix'
184+
args = (
185+
"-c dcm2niix -o %s -b -f bids_PhoenixReport --files %s -s %s"
186+
% (tmpdir, pjoin(TESTS_DATA_PATH, 'Phoenix'), subID)
187+
).split(' ')
188+
runner(args)
189+
190+
# check that the Phoenix document has been extracted (as gzipped dicom) in
191+
# the sourcedata/misc folder:
192+
assert op.exists(pjoin(tmppath, 'sourcedata', 'sub-%s', 'misc', 'sub-%s_phoenix.dicom.tgz') % (subID, subID))
193+
# check that no "sub-<subID>/misc" folder has been created in the BIDS
194+
# structure:
195+
assert not op.exists(pjoin(tmppath, 'sub-%s', 'misc') % subID)

0 commit comments

Comments
 (0)