diff --git a/nibabel/nicom/dicomwrappers.py b/nibabel/nicom/dicomwrappers.py index fc3fbe4905..c95815c9cc 100755 --- a/nibabel/nicom/dicomwrappers.py +++ b/nibabel/nicom/dicomwrappers.py @@ -14,6 +14,7 @@ from __future__ import division import operator +import warnings import numpy as np @@ -21,7 +22,7 @@ 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): @@ -502,8 +503,32 @@ 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' + ) + except IndexError: + # Sequence tag is found but missing items! + raise WrapperError("Diffusion file missing information") + except AttributeError: + # DiffusionDirectionality tag is not required + pass + else: + if n_frames != len(self.frames): + warnings.warn("Derived images found and removed") + n_frames = len(self.frames) + has_derived = True + assert len(self.frames) == n_frames frame_indices = np.array( [frame.FrameContentSequence[0].DimensionIndexValues @@ -522,6 +547,15 @@ 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) + dim_seq.pop(stackid_dim_idx) + if has_derived: + # derived volume is included + derived_tag = tag_for_keyword("DiffusionBValue") + if derived_tag not in dim_seq: + raise WrapperError("Missing information, cannot remove indices " + "with confidence.") + derived_dim_idx = dim_seq.index(derived_tag) + frame_indices = np.delete(frame_indices, derived_dim_idx, axis=1) # account for the 2 additional dimensions (row and column) not included # in the indices n_dim = frame_indices.shape[1] + 2 diff --git a/nibabel/nicom/tests/data/4d_multiframe_with_derived.dcm b/nibabel/nicom/tests/data/4d_multiframe_with_derived.dcm new file mode 100644 index 0000000000..26051e6d3a Binary files /dev/null and b/nibabel/nicom/tests/data/4d_multiframe_with_derived.dcm differ diff --git a/nibabel/nicom/tests/test_dicomwrappers.py b/nibabel/nicom/tests/test_dicomwrappers.py index 7e611c569d..b4907c22c1 100755 --- a/nibabel/nicom/tests/test_dicomwrappers.py +++ b/nibabel/nicom/tests/test_dicomwrappers.py @@ -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 @@ -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 diff --git a/nibabel/pydicom_compat.py b/nibabel/pydicom_compat.py index 7a8658cf47..beb787f315 100644 --- a/nibabel/pydicom_compat.py +++ b/nibabel/pydicom_compat.py @@ -21,12 +21,10 @@ 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 @@ -34,9 +32,13 @@ 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: