Skip to content
Merged
29 changes: 28 additions & 1 deletion nibabel/nicom/dicomwrappers.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,15 @@
from __future__ import division

import operator
import warnings

import numpy as np

from . import csareader as csar
from .dwiparams import B2q, nearest_pos_semi_def, q2bg
from ..openers import ImageOpener
from ..onetime import setattr_on_read as one_time
from ..pydicom_compat import tag_for_keyword
from ..pydicom_compat import tag_for_keyword, Sequence


class WrapperError(Exception):
Expand Down Expand Up @@ -502,8 +503,31 @@ def image_shape(self):
rows, cols = self.get('Rows'), self.get('Columns')
if None in (rows, cols):
raise WrapperError("Rows and/or Columns are empty.")

# Check number of frames
first_frame = self.frames[0]
n_frames = self.get('NumberOfFrames')
# some Philips may have derived images appended
has_derived = False
if hasattr(first_frame, 'get') and first_frame.get([0x18, 0x9117]):
# DWI image may include derived isotropic, ADC or trace volume
try:
self.frames = Sequence(
frame for frame in self.frames if
frame.MRDiffusionSequence[0].DiffusionDirectionality
!= 'ISOTROPIC'
)
if n_frames != len(self.frames):
warnings.warn("Derived images found and removed")
n_frames = len(self.frames)
has_derived = True
except IndexError:
# Sequence tag is found but missing items!
raise WrapperError("Diffusion file missing information")
except AttributeError:
# DiffusionDirectionality tag is not required
pass

assert len(self.frames) == n_frames
frame_indices = np.array(
[frame.FrameContentSequence[0].DimensionIndexValues
Expand All @@ -522,6 +546,9 @@ def image_shape(self):
if stackid_tag in dim_seq:
stackid_dim_idx = dim_seq.index(stackid_tag)
frame_indices = np.delete(frame_indices, stackid_dim_idx, axis=1)
if has_derived:
# derived volume is included
frame_indices = np.delete(frame_indices, 1, axis=1)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is always the second entry?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Based off my limited testing yes - however we should definitely keep an eye on this

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we use either a derived index or make an assertion that will fail loudly when the assumption is violated?

# account for the 2 additional dimensions (row and column) not included
# in the indices
n_dim = frame_indices.shape[1] + 2
Expand Down
Binary file not shown.
8 changes: 8 additions & 0 deletions nibabel/nicom/tests/test_dicomwrappers.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
DATA_FILE_SLC_NORM = pjoin(IO_DATA_PATH, 'csa_slice_norm.dcm')
DATA_FILE_DEC_RSCL = pjoin(IO_DATA_PATH, 'decimal_rescale.dcm')
DATA_FILE_4D = pjoin(IO_DATA_PATH, '4d_multiframe_test.dcm')
DATA_FILE_4D_DERIVED = pjoin(IO_DATA_PATH, '4d_multiframe_with_derived.dcm')

# This affine from our converted image was shown to match our image spatially
# with an image from SPM DICOM conversion. We checked the matching with SPM
Expand Down Expand Up @@ -616,6 +617,13 @@ def test_data_real(self):
assert_equal(sha1(dat_str).hexdigest(),
'149323269b0af92baa7508e19ca315240f77fa8c')

@dicom_test
def test_data_derived_shape(self):
# Test 4D diffusion data with an additional trace volume included
# Excludes the trace volume and generates the correct shape
dw = didw.wrapper_from_file(DATA_FILE_4D_DERIVED)
assert_equal(dw.image_shape, (96, 96, 60, 33))

@dicom_test
def test_data_fake(self):
# Test algorithm for get_data
Expand Down
8 changes: 5 additions & 3 deletions nibabel/pydicom_compat.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,22 +21,24 @@
import numpy as np

have_dicom = True
pydicom = read_file = tag_for_keyword = None
pydicom = read_file = tag_for_keyword = Sequence = None

try:
import dicom as pydicom
# Values not imported by default
import dicom.values
except ImportError:
try:
import pydicom
except ImportError:
have_dicom = False
else: # pydicom module available
from pydicom.dicomio import read_file
from pydicom.sequence import Sequence
# Values not imported by default
import pydicom.values
else: # dicom module available
# Values not imported by default
import dicom.values
from dicom.sequence import Sequence
read_file = pydicom.read_file

if have_dicom:
Expand Down