Skip to content

Commit df7a80d

Browse files
authored
Merge pull request #795 from effigies/mgxd-fix/trace
FIX: Correctly handle Philips DICOMs w/ derived volume
2 parents 6d4660c + 265511e commit df7a80d

File tree

5 files changed

+55
-4
lines changed

5 files changed

+55
-4
lines changed

.gitmodules

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,3 +16,6 @@
1616
[submodule "nibabel-data/nitest-cifti2"]
1717
path = nibabel-data/nitest-cifti2
1818
url = https://github.com/demianw/nibabel-nitest-cifti2.git
19+
[submodule "nibabel-data/nitest-dicom"]
20+
path = nibabel-data/nitest-dicom
21+
url = https://github.com/effigies/nitest-dicom

nibabel-data/nitest-dicom

Submodule nitest-dicom added at ff6844f

nibabel/nicom/dicomwrappers.py

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,15 @@
1414
from __future__ import division
1515

1616
import operator
17+
import warnings
1718

1819
import numpy as np
1920

2021
from . import csareader as csar
2122
from .dwiparams import B2q, nearest_pos_semi_def, q2bg
2223
from ..openers import ImageOpener
2324
from ..onetime import setattr_on_read as one_time
24-
from ..pydicom_compat import tag_for_keyword
25+
from ..pydicom_compat import tag_for_keyword, Sequence
2526

2627

2728
class WrapperError(Exception):
@@ -502,8 +503,32 @@ def image_shape(self):
502503
rows, cols = self.get('Rows'), self.get('Columns')
503504
if None in (rows, cols):
504505
raise WrapperError("Rows and/or Columns are empty.")
506+
505507
# Check number of frames
508+
first_frame = self.frames[0]
506509
n_frames = self.get('NumberOfFrames')
510+
# some Philips may have derived images appended
511+
has_derived = False
512+
if hasattr(first_frame, 'get') and first_frame.get([0x18, 0x9117]):
513+
# DWI image may include derived isotropic, ADC or trace volume
514+
try:
515+
self.frames = Sequence(
516+
frame for frame in self.frames if
517+
frame.MRDiffusionSequence[0].DiffusionDirectionality
518+
!= 'ISOTROPIC'
519+
)
520+
except IndexError:
521+
# Sequence tag is found but missing items!
522+
raise WrapperError("Diffusion file missing information")
523+
except AttributeError:
524+
# DiffusionDirectionality tag is not required
525+
pass
526+
else:
527+
if n_frames != len(self.frames):
528+
warnings.warn("Derived images found and removed")
529+
n_frames = len(self.frames)
530+
has_derived = True
531+
507532
assert len(self.frames) == n_frames
508533
frame_indices = np.array(
509534
[frame.FrameContentSequence[0].DimensionIndexValues
@@ -522,6 +547,15 @@ def image_shape(self):
522547
if stackid_tag in dim_seq:
523548
stackid_dim_idx = dim_seq.index(stackid_tag)
524549
frame_indices = np.delete(frame_indices, stackid_dim_idx, axis=1)
550+
dim_seq.pop(stackid_dim_idx)
551+
if has_derived:
552+
# derived volume is included
553+
derived_tag = tag_for_keyword("DiffusionBValue")
554+
if derived_tag not in dim_seq:
555+
raise WrapperError("Missing information, cannot remove indices "
556+
"with confidence.")
557+
derived_dim_idx = dim_seq.index(derived_tag)
558+
frame_indices = np.delete(frame_indices, derived_dim_idx, axis=1)
525559
# account for the 2 additional dimensions (row and column) not included
526560
# in the indices
527561
n_dim = frame_indices.shape[1] + 2

nibabel/nicom/tests/test_dicomwrappers.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
assert_not_equal, assert_raises)
2222

2323
from numpy.testing import assert_array_equal, assert_array_almost_equal
24+
from ...tests.nibabel_data import get_nibabel_data, needs_nibabel_data
2425

2526
IO_DATA_PATH = pjoin(dirname(__file__), 'data')
2627
DATA_FILE = pjoin(IO_DATA_PATH, 'siemens_dwi_1000.dcm.gz')
@@ -36,6 +37,8 @@
3637
DATA_FILE_DEC_RSCL = pjoin(IO_DATA_PATH, 'decimal_rescale.dcm')
3738
DATA_FILE_4D = pjoin(IO_DATA_PATH, '4d_multiframe_test.dcm')
3839
DATA_FILE_EMPTY_ST = pjoin(IO_DATA_PATH, 'slicethickness_empty_string.dcm')
40+
DATA_FILE_4D_DERIVED = pjoin(get_nibabel_data(), 'nitest-dicom',
41+
'4d_multiframe_with_derived.dcm')
3942

4043
# This affine from our converted image was shown to match our image spatially
4144
# with an image from SPM DICOM conversion. We checked the matching with SPM
@@ -622,6 +625,14 @@ def test_slicethickness_fallback(self):
622625
dw = didw.wrapper_from_file(DATA_FILE_EMPTY_ST)
623626
assert_equal(dw.voxel_sizes[2], 1.0)
624627

628+
@dicom_test
629+
@needs_nibabel_data('nitest-dicom')
630+
def test_data_derived_shape(self):
631+
# Test 4D diffusion data with an additional trace volume included
632+
# Excludes the trace volume and generates the correct shape
633+
dw = didw.wrapper_from_file(DATA_FILE_4D_DERIVED)
634+
assert_equal(dw.image_shape, (96, 96, 60, 33))
635+
625636
@dicom_test
626637
def test_data_fake(self):
627638
# Test algorithm for get_data

nibabel/pydicom_compat.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,22 +21,24 @@
2121
import numpy as np
2222

2323
have_dicom = True
24-
pydicom = read_file = tag_for_keyword = None
24+
pydicom = read_file = tag_for_keyword = Sequence = None
2525

2626
try:
2727
import dicom as pydicom
28-
# Values not imported by default
29-
import dicom.values
3028
except ImportError:
3129
try:
3230
import pydicom
3331
except ImportError:
3432
have_dicom = False
3533
else: # pydicom module available
3634
from pydicom.dicomio import read_file
35+
from pydicom.sequence import Sequence
3736
# Values not imported by default
3837
import pydicom.values
3938
else: # dicom module available
39+
# Values not imported by default
40+
import dicom.values
41+
from dicom.sequence import Sequence
4042
read_file = pydicom.read_file
4143

4244
if have_dicom:

0 commit comments

Comments
 (0)