Skip to content

Commit cb4f83f

Browse files
committed
BUG: Fix case where DICOM series order has negative spacing
If the spacing between slices is negative, then direction cosine matrix was not correct with reguards to the sign.
1 parent c69a657 commit cb4f83f

File tree

3 files changed

+130
-1
lines changed

3 files changed

+130
-1
lines changed

Modules/IO/GDCM/test/CMakeLists.txt

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -439,3 +439,13 @@ addcompliancetest(raw-YBR_FULL_422)
439439
addcompliancetest(RLE-RGB)
440440
addcompliancetest(HTJ2K-YBR_ICT Lily_full.mha)
441441
addcompliancetest(HTJ2K-YBR_RCT Lily_full.mha)
442+
443+
444+
set(ITKGDCMImageIOGTests itkGDCMImageIOGTest.cxx
445+
)
446+
creategoogletestdriver(ITKGDCMImageIO "${ITKIOGDCM-Test_LIBRARIES}" "${ITKGDCMImageIOGTests}")
447+
448+
target_compile_definitions(ITKGDCMImageIOGTestDriver PRIVATE "-DITK_TEST_OUTPUT_DIR=${ITK_TEST_OUTPUT_DIR}")
449+
450+
ExternalData_expand_arguments(ITKData _DICOM_SERIES_INPUT "DATA{${ITK_DATA_ROOT}/Input/DicomSeries/,REGEX:Image[0-9]+.dcm}" )
451+
target_compile_definitions(ITKGDCMImageIOGTestDriver PRIVATE "-DDICOM_SERIES_INPUT=${_DICOM_SERIES_INPUT}")
Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
/*=========================================================================
2+
*
3+
* Copyright NumFOCUS
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* https://www.apache.org/licenses/LICENSE-2.0.txt
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*
17+
*=========================================================================*/
18+
19+
#include "itkImage.h"
20+
#include "itkImageFileWriter.h"
21+
#include "itkGDCMImageIO.h"
22+
#include "itkGDCMSeriesFileNames.h"
23+
#include "itkMetaDataObject.h"
24+
#include "itkGTest.h"
25+
#include "itksys/SystemTools.hxx"
26+
#include "itkImageSeriesReader.h"
27+
#include <string>
28+
#include <vector>
29+
30+
31+
#define _STRING(s) #s
32+
#define TOSTRING(s) std::string(_STRING(s))
33+
34+
namespace
35+
{
36+
37+
struct ITKGDCMImageIO : public ::testing::Test
38+
{
39+
void
40+
SetUp() override
41+
{
42+
itksys::SystemTools::MakeDirectory(m_TempDir);
43+
}
44+
45+
void
46+
TearDown() override
47+
{
48+
itksys::SystemTools::RemoveADirectory(m_TempDir);
49+
}
50+
51+
const std::string m_TempDir{ TOSTRING(ITK_TEST_OUTPUT_DIR) + "/ITKGDCMImageIO" };
52+
const std::string m_DicomSeriesInput{ TOSTRING(DICOM_SERIES_INPUT) };
53+
};
54+
55+
} // namespace
56+
57+
TEST_F(ITKGDCMImageIO, ReadSlicesNegativeSpacing)
58+
{
59+
using PixelType = uint16_t;
60+
constexpr unsigned int Dimension = 3;
61+
using ImageType = itk::Image<PixelType, Dimension>;
62+
63+
using NamesGeneratorType = itk::GDCMSeriesFileNames;
64+
auto namesGenerator = NamesGeneratorType::New();
65+
namesGenerator->SetDirectory(m_DicomSeriesInput);
66+
namesGenerator->SetUseSeriesDetails(true);
67+
std::vector<std::string> fileNames = namesGenerator->GetInputFileNames();
68+
69+
using SeriesReaderType = itk::ImageSeriesReader<ImageType>;
70+
auto seriesReader = SeriesReaderType::New();
71+
seriesReader->SetFileNames(fileNames);
72+
auto gdcmIO = itk::GDCMImageIO::New();
73+
seriesReader->SetImageIO(gdcmIO);
74+
ASSERT_NO_THROW(seriesReader->UpdateLargestPossibleRegion());
75+
76+
ImageType::Pointer outputImage = seriesReader->GetOutput();
77+
outputImage->DisconnectPipeline();
78+
79+
seriesReader->ReverseOrderOn();
80+
ASSERT_NO_THROW(seriesReader->UpdateLargestPossibleRegion());
81+
ImageType::Pointer reversedOutputImage = seriesReader->GetOutput();
82+
reversedOutputImage->DisconnectPipeline();
83+
84+
EXPECT_EQ(outputImage->GetLargestPossibleRegion().GetSize(),
85+
reversedOutputImage->GetLargestPossibleRegion().GetSize());
86+
ITK_EXPECT_VECTOR_NEAR(outputImage->GetSpacing(), reversedOutputImage->GetSpacing(), 1e-6);
87+
EXPECT_NE(outputImage->GetOrigin(), reversedOutputImage->GetOrigin());
88+
89+
// calculate the index at the middle of the image
90+
ImageType::IndexType middleIndex;
91+
for (unsigned int d = 0; d < Dimension; ++d)
92+
{
93+
middleIndex[d] =
94+
outputImage->GetLargestPossibleRegion().GetIndex()[d] + outputImage->GetLargestPossibleRegion().GetSize()[d] / 2;
95+
}
96+
97+
const std::vector<ImageType::IndexType> testIndices = {
98+
{ { 0, 0, 0 } }, { { 1, 1, 1 } }, { { 2, 2, 2 } }, middleIndex
99+
};
100+
101+
// test that the reversed image has the same pixel values at the same physical location
102+
for (const auto & idx : testIndices)
103+
{
104+
ImageType::PointType point;
105+
outputImage->TransformIndexToPhysicalPoint(idx, point);
106+
auto reverseIdx = reversedOutputImage->TransformPhysicalPointToIndex(point);
107+
108+
std::cout << "Testing idx: " << idx << " reverseIdx: " << reverseIdx << std::endl;
109+
ASSERT_TRUE(reversedOutputImage->GetLargestPossibleRegion().IsInside(reverseIdx));
110+
EXPECT_EQ(outputImage->GetPixel(idx), reversedOutputImage->GetPixel(reverseIdx));
111+
}
112+
}

Modules/IO/ImageBase/include/itkImageSeriesReader.hxx

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -184,7 +184,14 @@ ImageSeriesReader<TOutputImage>::GenerateOutputInformation()
184184
{
185185
spacing[this->m_NumberOfDimensionsInImage] = dirNnorm / (numberOfFiles - 1);
186186
this->m_SpacingDefined = true;
187-
if (!m_ForceOrthogonalDirection)
187+
if (m_ForceOrthogonalDirection)
188+
{
189+
for (unsigned int j = 0; j < TOutputImage::ImageDimension; ++j)
190+
{
191+
direction[j][this->m_NumberOfDimensionsInImage] *= std::signbit(dirN[j]) ? -1.0 : 1.0;
192+
}
193+
}
194+
else
188195
{
189196
for (unsigned int j = 0; j < TOutputImage::ImageDimension; ++j)
190197
{

0 commit comments

Comments
 (0)