ENH: Multi frame filter to handle stack with multiple orientations#1485
ENH: Multi frame filter to handle stack with multiple orientations#1485octomike wants to merge 6 commits intonipy:masterfrom
Conversation
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## master #1485 +/- ##
=======================================
Coverage 95.44% 95.44%
=======================================
Files 209 209
Lines 29981 30042 +61
Branches 4483 4493 +10
=======================================
+ Hits 28614 28675 +61
Misses 931 931
Partials 436 436 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
|
It would be best if there was a test that currently failed and then was fixed by this (or another) change. See https://www.codecademy.com/article/tdd-red-green-refactor. We can add a test file to https://github.com/nipy/nibabel/tree/master/nibabel-data (it would go in https://github.com/effigies/nitest-dicom and should be stripped of any PII). @bpinsard Could I ask you to review the code? I have almost no practical experience with DICOMs, so if I'm reviewing it will be based on testing alone. |
|
Can do. I would create a new PR with a test and expect a failure. Then, rebase this PR and change the expectation to success. Does that make sense? |
|
Whatever process works for you. I'm not super worried as long as it's verifiable. |
|
I think the fix makes complete sense. Have you tested downstream software: notably heudiconv and the potential mismatch between the output nifti of dcm2niix (with the |
|
Would be good to mimic the |
@moloney Good idea. I have added this and also removed the warning when a user explicitly requests a frame group. |
I already use this patch to convert a dataset using heudiconv. But to be honest, the impact I test this way is very limited, because we're talking only about a localizer, which is usually discarded anyway. Before, heudiconv failed in Right now, there will always be a mismatch to dcm2niix - because we are only returning a subset of the frames (first group by default). With dcm2niix' development branch I get:
|
There was a problem hiding this comment.
I think I understand this, and it makes reasonable sense to me.
@octomike Can you merge your commit from #1486 into this? I think the shape should be (512, 512, 7), not (512, 512, 3).
It would also be a good use of tests to demonstrate how the feature should actually be used. Here's what I was able to figure out:
@dicom_test
@needs_nibabel_data('dcm_qa_xa60')
def test_data_multi_orient_extract_all(self):
# Test that a file with 3 orientations sharing the same StackID
with ImageOpener(DATA_FILE_XA60_MULTI_ORIENT) as fobj:
dcm_data = pydicom.dcmread(fobj)
for keep_group, nslices in enumerate([7, 3, 3]):
dw = didw.wrapper_from_data(
dcm_data, frame_filters=[didw.FilterMultiOrient(keep_group=keep_group)]
)
assert dw.image_shape == (512, 512, nslices)If other ergonomics are intended, please use those.
|
@effigies done and I included your test - makes total sense to test that feature as well |
Some vendors produce enhanced DICOMs with frame groups of different orientations sharing the same Stack ID. This filter groups frames by orientation and returns only the frames of the first logical stack. Relevant for nipy#1392
Enhanced DICOMs with multiple stacks sharing the same Stack ID should be grouped together, even if the orientations of the stacks don't match and it's unintuitive to do so. Happened first on Siemens XA60 localizers. Relevant spec: https://dicom.nema.org/medical/dicom/2025d/output/chtml/part03/sect_C.7.6.16.2.html`
cc914d0 to
db8e0fc
Compare
|
Rebased on master (and included the typo fix for |
Some vendors produce enhanced DICOMs with (logical) frame groups of different orientations sharing the same Stack ID - this breaks in
image_shape.This filter groups frames by orientation and returns only the frames of the first logical stack.
Relevant for #1392
I basically hit #1392 with some XA70A localizer data from a recent Siemens scanner. Unfortunately filtering in downstream applications (heudiconv) is not possible for us, because we rely on the localizer name to set a session ID (reproin logic).
I'm aware this PR does not solve the issue. Still, it's super helpful for us and maybe to others as well. The code is emitting a warning that frames have been discarded, similar to the two existing filters. Also, I have trouble interpreting the definition of the Stack ID and I'm still not 100% certain this is actually allowed in DICOM or if our data is non-conformant.
Be aware, that the method was drafted by claude/AI with me reviewing and testing it.