Skip to content

Conversation

blowekamp
Copy link
Member

If the spacing between slices is negative, then direction cosine matrix was not correct with reguards to the sign.

PR Checklist

  • No API changes were made (or the changes have been approved)
  • No major design changes were made (or the changes have been approved)
  • Added test (or behavior not changed)
  • Updated API documentation (or API not changed)
  • Added license to new files (if any)
  • Added Python wrapping to new files (if any) as described in ITK Software Guide Section 9.5
  • Added ITK examples for all new major features (if any)

Refer to the ITK Software Guide for
further development details if necessary.

@github-actions github-actions bot added type:Bug Inconsistencies or issues which will cause an incorrect result under some or all circumstances type:Infrastructure Infrastructure/ecosystem related changes, such as CMake or buildbots type:Testing Ensure that the purpose of a class is met/the results on a wide set of test cases are correct area:IO Issues affecting the IO module labels May 20, 2025
@blowekamp
Copy link
Member Author

relates to SimpleITK/SimpleITK#2292

Copy link
Member

@dzenanz dzenanz left a comment

Choose a reason for hiding this comment

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

Looks good after a quick look.

@blowekamp blowekamp force-pushed the dicom_series_reverse branch from 0ba9197 to cb4f83f Compare May 21, 2025 16:12
@blowekamp
Copy link
Member Author

I update the test case to use the sample MRI series in ITK test data, and use the ReverseOrder flag. The test is compare the pixel values at the physical locations match.

@dzenanz
Copy link
Member

dzenanz commented May 21, 2025

Maybe we could have both of those tests?

@blowekamp
Copy link
Member Author

Maybe we could have both of those tests?

The DICOM tags were not correct in the prior example test case where the DICOM files were manually created. And oddly the X,Y spacing was being swapped for some reason likely related to the TAGs. Also this test case for the file has a non identify cosine matrix, which helps verifies the correct column was multiplied and not a row by -1.

@N-Dekker
Copy link
Contributor

Thanks @blowekamp I'm just trying to understand the issue. It reminds me of an ITKElastix issue reported by @thewtex:

Which appeared related to ImageFileReader::GenerateOutputInformation():

// Spacing is expected to be greater than 0
// If negative, flip image direction along this axis.
// and store this information in the metadata
for (unsigned int i = 0; i < TOutputImage::ImageDimension; ++i)
{
if (spacing[i] < 0)
{
spacing[i] = -spacing[i];
for (unsigned int j = 0; j < TOutputImage::ImageDimension; ++j)
{
direction[j][i] = -direction[j][i];
}
}
}

@blowekamp
Copy link
Member Author

The original issue was reported in SimpleITK here:
SimpleITK/SimpleITK#2292

The cause appeared to be that the z-slice direction was negative. This was more easily reproduce by enabling the "ReverseOrder" flag.

It may very well be likely related to this change too:

// Spacing is expected to be greater than 0
// If negative, flip image direction along this axis.
// and store this information in the metadata
for (unsigned int i = 0; i < TOutputImage::ImageDimension; ++i)
{
if (spacing[i] < 0)
{
spacing[i] = -spacing[i];
for (unsigned int j = 0; j < TOutputImage::ImageDimension; ++j)
{
direction[j][i] = -direction[j][i];
}
}
}

It's not clear to me if the original DICOM reported in SimpleITK had negative spacing. The "ReverseOrder" reproduction of the issue, has negative inter-slice distance. There is certainly areas that need further investigation.

The principle used in the test, that for DICOM if the "reverse order" flag is set then the physical location of the voxel should be maintained, should be correct. But DICOM is complicated.

@blowekamp blowekamp force-pushed the dicom_series_reverse branch from cb4f83f to d46d322 Compare May 29, 2025 15:19
@blowekamp
Copy link
Member Author

This patch does not directly deal with the DICOM tags, and specific DICOM information. It only correct a sign change in the DirectionCosine matrix when detected to maintain consistent physical spacing when the order is reversed. The specifics of multi-frame DICOM are not handled here.

If there is a specific DICOM series that should be tested, we can manually do that to ensure consistency.

Otherwise, I think this patch should be good?

@blowekamp blowekamp requested review from thewtex and hjmjohnson June 3, 2025 13:38
@ashwinkumargb
Copy link

@thewtex @hjmjohnson is there any update on this PR? Would be great to get this issue resolved as it's affect pre-processing pipelines!

@blowekamp
Copy link
Member Author

Thanks @blowekamp I'm just trying to understand the issue. It reminds me of an ITKElastix issue reported by @thewtex:

Which appeared related to ImageFileReader::GenerateOutputInformation():

// Spacing is expected to be greater than 0
// If negative, flip image direction along this axis.
// and store this information in the metadata
for (unsigned int i = 0; i < TOutputImage::ImageDimension; ++i)
{
if (spacing[i] < 0)
{
spacing[i] = -spacing[i];
for (unsigned int j = 0; j < TOutputImage::ImageDimension; ++j)
{
direction[j][i] = -direction[j][i];
}
}
}

I just tries this patch with this dataset referenced. I used the file names in the order presented by GDCMSeriesFileNames:

-00-slice009.dcm
-00-slice008.dcm
-00-slice007.dcm
-00-slice006.dcm
-00-slice005.dcm
-00-slice004.dcm
-00-slice003.dcm
-00-slice002.dcm
-00-slice001.dcm
-00-slice000.dcm

When read in this order with the patch the direction(2,2) == -1.0, same as reported in the issue. When ReverseOrder is enable then direction(2,2,) == 1.0 and the physical geometry is the same. For both cases the space is [0.976562, 0.976562, 2].

This behavior is consistent and follow the expectations of the patch.

@blowekamp blowekamp marked this pull request as ready for review September 22, 2025 16:11
@blowekamp blowekamp force-pushed the dicom_series_reverse branch 2 times, most recently from 84e2a15 to eec7c09 Compare September 23, 2025 13:16
If the spacing between slices is negative, then direction cosine
matrix was not correct with reguards to the sign.

This can be easily created with the "ReverseOrder" flag with the
series reader and DICOM data. With the correction, a DICOM series
mantains its correct physical location when this flag is enabled.
Copy link
Member

@thewtex thewtex left a comment

Choose a reason for hiding this comment

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

Please see the inline comments.

Please also add a test where the image is not orthogonal to the z-plane, i.e. the third direction component is neither [0,0,1] or [0,0,-1].

{
for (unsigned int j = 0; j < TOutputImage::ImageDimension; ++j)
{
direction[j][this->m_NumberOfDimensionsInImage] *= -1.0;
Copy link
Member

Choose a reason for hiding this comment

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

This assumes that the image orientation is orthogonal to the z-plane. This is not always the case, e.g. some DICOMs. The solution should look more like:

for (unsigned int j = 0; j < TOutputImage::ImageDimension; ++j)
{
direction[j][this->m_NumberOfDimensionsInImage] = dirN[j] / dirNnorm;
}

Copy link
Member Author

Choose a reason for hiding this comment

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

The "direction" matrix helps transform index-space to xyz space. The third column by construction in GDCM is one of the orthogonal directions to the imaging planes:
https://github.com/InsightSoftwareConsortium/ITK/blob/main/Modules/IO/GDCM/src/itkGDCMImageIO.cxx#L734-L743

This is the case where the wrong direction was computed due to the spacing sign, or the order of the images, and its is being corrected. The non-orthogonal imaging is a separate case.

Copy link
Member

Choose a reason for hiding this comment

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

I understand that the third direction is orthogonal to the first two. That was not my point. And I was not referring to non-orthogonal imaging.

Copy link
Member

Choose a reason for hiding this comment

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

The axis that needs flipping might not be the third, but rather second or first. Is that your point Matt?

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 not necessarily a simple flip of one value.

@blowekamp
Copy link
Member Author

Please also add a test where the image is not orthogonal to the z-plane, i.e. the third direction component is neither [0,0,1] or [0,0,-1].

The "ReadSlicesReverseOrder" has a no-trivial third direction component. The output of the test include the direction matrix without and with the reverse order flag:

baseline direction: 1 0 0
0 0.466651 0.884442
0 -0.884442 0.466651

reversed direction: 1 0 0
0 0.466651 -0.884442
0 -0.884442 -0.466651

@thewtex
Copy link
Member

thewtex commented Sep 23, 2025

The output

When loaded in ITK-SNAP and Slicer, does this look correct? Is it LPS?

@blowekamp
Copy link
Member Author

The output

When loaded in ITK-SNAP and Slicer, does this look correct? Is it LPS?

I am not certain what your request is here.

When ReverseOrder is off ( the default ) this image is the same as before. The test checks that the physical location of the pixels values remains. If there are additions to the gtest requested please be specific. It is not reasonable to compile of ITK-SNAP and/or 3D slicer with this patch. I do understand that effecting the location of DICOM images could have serious impact and this change needs careful review and testing.

@thewtex
Copy link
Member

thewtex commented Sep 26, 2025

To help ensure we are getting correct behavior with DICOM's ,please add a test where the input does not have any 0's and 1's in the direction matrix. And add a check in the test that the resulting direction matrix's third vector equals the cross product of the first two.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area:IO Issues affecting the IO module type:Bug Inconsistencies or issues which will cause an incorrect result under some or all circumstances type:Infrastructure Infrastructure/ecosystem related changes, such as CMake or buildbots type:Testing Ensure that the purpose of a class is met/the results on a wide set of test cases are correct
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants