Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

FIX: Conditionally drop derived volumes from DWI sequences #1296

Merged
merged 3 commits into from
Feb 27, 2024

Conversation

effigies
Copy link
Member

@effigies effigies commented Feb 22, 2024

#795 cast a wide net, based on Phillips DICOMs that concatenated derived volumes to diffusion series. This causes problems for Siemens sequences where only derived volumes are contained in an image.

This is a pretty minimal fix that changes the rule to "if raw and derived are present, drop derived, otherwise keep whatever we have".

Fixes #1245.

Copy link

codecov bot commented Feb 22, 2024

Codecov Report

All modified and coverable lines are covered by tests ✅

Project coverage is 92.26%. Comparing base (27c2427) to head (3f81a96).

Additional details and impacted files
@@           Coverage Diff           @@
##           master    #1296   +/-   ##
=======================================
  Coverage   92.25%   92.26%           
=======================================
  Files          99       99           
  Lines       12467    12469    +2     
  Branches     2563     2564    +1     
=======================================
+ Hits        11502    11505    +3     
  Misses        642      642           
+ Partials      323      322    -1     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

@effigies effigies force-pushed the fix/dicom-missing-frame-indices branch from 093b51a to 3f81a96 Compare February 23, 2024 03:55
@effigies
Copy link
Member Author

@yarikoptic @mgxd Would either of you care to have a look?

Copy link
Member

@mgxd mgxd left a comment

Choose a reason for hiding this comment

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

Looks reasonable, though looking back at the docstrings there should be some mention/warning of this conditional removal of derived volumes

@effigies
Copy link
Member Author

That happens a couple lines below.

else:
if n_frames != len(self.frames):
warnings.warn('Derived images found and removed')
n_frames = len(self.frames)
has_derived = True

@yarikoptic
Copy link
Member

yarikoptic commented Feb 27, 2024

this DICOM handling to just figure out the shape of the data is somewhat of a dark magic for me, so can't really assess on validity. But what I can say that

if running with stock nibabel 5.2.0-3 and pydicom installed on debian I get 4 hits on that addressed bug and 1 other
❯ /usr/bin/find dcm_* -type d | grep -v '\.git/' | while read d; do dcm=$(/bin/ls $d/*.dcm 2>/dev/null|  head -n 1 || echo ); [ -z "$dcm" ] || chronic python -W ignore::UserWarning -c 'import sys; f=sys.argv[1]; print(f, end=" "); from nibabel.nicom import dicomwrappers as didw; print(didw.wrapper_from_file(f).image_shape)' $dcm; done;
dcm_qas/dcm_qa_xa30/In/11_DWI_dir80_PA/0001_1.3.12.2.1107.5.2.43.67093.2022071112123417719608653.dcm Traceback (most recent call last):
  File "<string>", line 1, in <module>
  File "/usr/lib/python3/dist-packages/nibabel/onetime.py", line 156, in __get__
    val = self.getter(obj)
          ^^^^^^^^^^^^^^^^
  File "/usr/lib/python3/dist-packages/nibabel/nicom/dicomwrappers.py", line 545, in image_shape
    frame_indices = np.delete(frame_indices, stackid_dim_idx, axis=1)
                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "<__array_function__ internals>", line 200, in delete
  File "/usr/lib/python3/dist-packages/numpy/lib/function_base.py", line 5136, in delete
    axis = normalize_axis_index(axis, ndim)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
numpy.AxisError: axis 1 is out of bounds for array of dimension 1
dcm_qas/dcm_qa_xa30/In/12_DWI_dir80_PA/0001_1.3.12.2.1107.5.2.43.67093.2022071112123419375008659.dcm Traceback (most recent call last):
  File "<string>", line 1, in <module>
  File "/usr/lib/python3/dist-packages/nibabel/onetime.py", line 156, in __get__
    val = self.getter(obj)
          ^^^^^^^^^^^^^^^^
  File "/usr/lib/python3/dist-packages/nibabel/nicom/dicomwrappers.py", line 545, in image_shape
    frame_indices = np.delete(frame_indices, stackid_dim_idx, axis=1)
                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "<__array_function__ internals>", line 200, in delete
  File "/usr/lib/python3/dist-packages/numpy/lib/function_base.py", line 5136, in delete
    axis = normalize_axis_index(axis, ndim)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
numpy.AxisError: axis 1 is out of bounds for array of dimension 1
dcm_qas/dcm_qa_xa30/In/19_DWI_dir80_AP/0001_1.3.12.2.1107.5.2.43.67093.2022071112140609850612301.dcm Traceback (most recent call last):
  File "<string>", line 1, in <module>
  File "/usr/lib/python3/dist-packages/nibabel/onetime.py", line 156, in __get__
    val = self.getter(obj)
          ^^^^^^^^^^^^^^^^
  File "/usr/lib/python3/dist-packages/nibabel/nicom/dicomwrappers.py", line 545, in image_shape
    frame_indices = np.delete(frame_indices, stackid_dim_idx, axis=1)
                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "<__array_function__ internals>", line 200, in delete
  File "/usr/lib/python3/dist-packages/numpy/lib/function_base.py", line 5136, in delete
    axis = normalize_axis_index(axis, ndim)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
numpy.AxisError: axis 1 is out of bounds for array of dimension 1
dcm_qas/dcm_qa_xa30/In/20_DWI_dir80_AP/0001_1.3.12.2.1107.5.2.43.67093.2022071112140611403312307.dcm Traceback (most recent call last):
  File "<string>", line 1, in <module>
  File "/usr/lib/python3/dist-packages/nibabel/onetime.py", line 156, in __get__
    val = self.getter(obj)
          ^^^^^^^^^^^^^^^^
  File "/usr/lib/python3/dist-packages/nibabel/nicom/dicomwrappers.py", line 545, in image_shape
    frame_indices = np.delete(frame_indices, stackid_dim_idx, axis=1)
                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "<__array_function__ internals>", line 200, in delete
  File "/usr/lib/python3/dist-packages/numpy/lib/function_base.py", line 5136, in delete
    axis = normalize_axis_index(axis, ndim)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
numpy.AxisError: axis 1 is out of bounds for array of dimension 1
dcm_qas/dcm_qa_fmap/In/Philips/fmap/IM_0027_fMAP.dcm Traceback (most recent call last):
  File "<string>", line 1, in <module>
  File "/usr/lib/python3/dist-packages/nibabel/onetime.py", line 156, in __get__
    val = self.getter(obj)
          ^^^^^^^^^^^^^^^^
  File "/usr/lib/python3/dist-packages/nibabel/nicom/dicomwrappers.py", line 566, in image_shape
    raise WrapperError('Calculated shape does not match number of frames.')
nibabel.nicom.dicomwrappers.WrapperError: Calculated shape does not match number of frames.

with this patch (with post-installed pydicom) I get only that other `Calculated shape does not match number of frames.

❯ /usr/bin/find dcm_* -type d | grep -v '\.git/' | while read d; do dcm=$(/bin/ls $d/*.dcm 2>/dev/null|  head -n 1 || echo ); [ -z "$dcm" ] || chronic  /home/yoh/proj/nipy/nipy-suite/nibabel/venvs/dev3/bin/python -W ignore::UserWarning -c 'import sys; f=sys.argv[1]; print(f, end=" "); from nibabel.nicom import dicomwrappers as didw; print(didw.wrapper_from_file(f).image_shape)' $dcm; done;
dcm_qas/dcm_qa_fmap/In/Philips/fmap/IM_0027_fMAP.dcm Traceback (most recent call last):
  File "<string>", line 1, in <module>
  File "/home/yoh/proj/nipy/nipy-suite/nibabel/venvs/dev3/lib/python3.11/site-packages/nibabel/onetime.py", line 156, in __get__
    val = self.getter(obj)
          ^^^^^^^^^^^^^^^^
  File "/home/yoh/proj/nipy/nipy-suite/nibabel/venvs/dev3/lib/python3.11/site-packages/nibabel/nicom/dicomwrappers.py", line 569, in image_shape
    raise WrapperError('Calculated shape does not match number of frames.')
nibabel.nicom.dicomwrappers.WrapperError: Calculated shape does not match number of frames.

so, not sure if returned dimensionality is right, but it is definitely not hitting the error any more.

@yarikoptic
Copy link
Member

my question would be - how feasible would be to get the release with this out? ;) or should we test more?

@effigies
Copy link
Member Author

Could put out a 5.2.1 with this and other bug fixes. Is the shape mismatch thing something we can address?

@yarikoptic
Copy link
Member

@effigies effigies merged commit a35e2ac into nipy:master Feb 27, 2024
52 checks passed
@effigies effigies deleted the fix/dicom-missing-frame-indices branch February 27, 2024 03:19
effigies added a commit that referenced this pull request Feb 27, 2024
DATA: Add dcm_qa_xa30 as submodule for test data

TEST: Add test for Siemens TRACE volume

FIX: Conditionally drop isotropic frames
effigies added a commit that referenced this pull request Feb 27, 2024
5.2.1 (Monday 26 February 2024)

Bug-fix release in the 5.2.x series.

Enhancements
------------
* Support "flat" ASCII-encoded GIFTI DataArrays (pr/1298) (PM, reviewed by CM)

Bug fixes
---------
* Tolerate missing ``git`` when reporting version info (pr/1286) (CM, reviewed by
  Yuri Victorovich)
* Handle Siemens XA30 derived DWI DICOMs (pr/1296) (CM, reviewed by YOH and
  Mathias Goncalves)

Maintenance
-----------
* Add tool for generating GitHub-friendly release notes (pr/1284) (CM)
* Accommodate pytest 8 changes (pr/1297) (CM)

* tag '5.2.1':
  REL: 5.2.1
  Backport gh-1296: Conditionally drop derived volumes from DWI sequences
  Backport gh-1298: Support "flat" ASCII-encoded GIFTI DataArrays
  Build(deps): Bump codecov/codecov-action from 3 to 4
  Build(deps): Bump the actions-infrastructure group with 3 updates
  Backport gh-1297: Accommodate pytest 8 changes
  Backport gh-1286: Tolerate missing git
  MNT: Advertise Python 3.12 support
  DOC: Fix intersphinx mapping and reference type
  Backport gh-1284: Add tool for generating GitHub-friendly release notes
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

numpy.AxisError: axis 1 is out of bounds for array of dimension 1
3 participants