From 899219a6d483e118a36878bf5159736623bd6a53 Mon Sep 17 00:00:00 2001 From: Bradley Lowekamp Date: Wed, 31 Jul 2024 09:01:19 -0400 Subject: [PATCH 01/10] ENH: Add DICOMOrientation class from remote module The class provides anatomical orientation based enums for describing coordinate systems. In ITK the physical coordinate systems is the same as the DICOM standard. DICOM has the following specification: If Anatomical Orientation Type (0010,2210) is absent or has a value of BIPED, the x-axis is increasing to the left hand side of the patient. The y-axis is increasing to the posterior side of the patient. The z-axis is increasing toward the head of the patient. Which describes the direction of increasing. The above coordidate system is described as "LPS" with this enumeration and corresponds to an ITK image with an identity direction cosine matrix. --- .../Core/Common/include/itkDICOMOrientation.h | 259 +++++++++++++++++ Modules/Core/Common/src/CMakeLists.txt | 4 +- .../Core/Common/src/itkDICOMOrientation.cxx | 273 ++++++++++++++++++ Modules/Core/Common/test/CMakeLists.txt | 4 +- .../Common/test/itkDICOMOrientionGTest.cxx | 206 +++++++++++++ .../Common/wrapping/itkDICOMOrientation.wrap | 1 + 6 files changed, 745 insertions(+), 2 deletions(-) create mode 100644 Modules/Core/Common/include/itkDICOMOrientation.h create mode 100644 Modules/Core/Common/src/itkDICOMOrientation.cxx create mode 100644 Modules/Core/Common/test/itkDICOMOrientionGTest.cxx create mode 100644 Modules/Core/Common/wrapping/itkDICOMOrientation.wrap diff --git a/Modules/Core/Common/include/itkDICOMOrientation.h b/Modules/Core/Common/include/itkDICOMOrientation.h new file mode 100644 index 00000000000..5632b3bd81c --- /dev/null +++ b/Modules/Core/Common/include/itkDICOMOrientation.h @@ -0,0 +1,259 @@ +/*========================================================================= + * + * Copyright NumFOCUS + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0.txt + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + *=========================================================================*/ +#ifndef itkDICOMOrientation_h +#define itkDICOMOrientation_h + +#include "ITKCommonExport.h" +#include "itkImageBase.h" +#include +#include + +namespace itk +{ + +/** \class DICOMOrientation + * \brief A supporting class for DICOMOrientImageFilter. + * + * Defines enums to for patient orientation in a compatible way with DICOM. + * + * Instances hold the patient orientation enum and allow conversion to and from enums, string and direction cosine + * matrices. Conversions from a direction cosine matrix is approximated with the orientation of the closes axes. + * + * \ingroup ITKCommon + */ +class ITKCommon_EXPORT DICOMOrientation +{ +public: + constexpr static unsigned int Dimension = 3; + using DirectionType = typename ImageBase::DirectionType; + constexpr static unsigned int ImageDimension = Dimension; + + + enum class CoordinateEnum : uint8_t + { + UNKNOWN = 0, + Right = 2, // 0b0010 + Left = 3, + Anterior = 4, ///< front - 0b0100 + Posterior = 5, ///< back + Inferior = 8, ///< bottom - 0b1000 + Superior = 9 ///< above + }; + +private: + enum class CoordinateMajornessTermsEnum : uint8_t + { + PrimaryMinor = 0, + SecondaryMinor = 8, + TertiaryMinor = 16 + }; + + constexpr static uint32_t + toOrientation(const CoordinateEnum primary, const CoordinateEnum secondary, const CoordinateEnum tertiary) + { + return (static_cast(primary) << static_cast(CoordinateMajornessTermsEnum::PrimaryMinor)) + + (static_cast(secondary) << static_cast(CoordinateMajornessTermsEnum::SecondaryMinor)) + + (static_cast(tertiary) << static_cast(CoordinateMajornessTermsEnum::TertiaryMinor)); + } + + CoordinateEnum + GetCoordinateTerm(CoordinateMajornessTermsEnum cmt) const; + +public: +#if 0 +// When this is enabled the following error occurs: +//ITK/Modules/Core/Common/include/itkDICOMOrientation.h:93:11: error: enumerator value is not a constant expression +// RIP = ITK_ORIENTATIONENUM(CoordinateEnum::Right, CoordinateEnum::Inferior, CoordinateEnum::Posterior), + +# define ITK_ORIENTATIONENUM DICOMOrientation::toOrientation +#else + +# define ITK_ORIENTATIONENUM(P, S, T) \ + (static_cast(P) << static_cast(CoordinateMajornessTermsEnum::PrimaryMinor)) + \ + (static_cast(S) << static_cast(CoordinateMajornessTermsEnum::SecondaryMinor)) + \ + (static_cast(T) << static_cast(CoordinateMajornessTermsEnum::TertiaryMinor)) + +#endif + + enum class OrientationEnum : uint32_t + + { + INVALID = 0, + RIP = ITK_ORIENTATIONENUM(CoordinateEnum::Right, CoordinateEnum::Inferior, CoordinateEnum::Posterior), + LIP = ITK_ORIENTATIONENUM(CoordinateEnum::Left, CoordinateEnum::Inferior, CoordinateEnum::Posterior), + RSP = ITK_ORIENTATIONENUM(CoordinateEnum::Right, CoordinateEnum::Superior, CoordinateEnum::Posterior), + LSP = ITK_ORIENTATIONENUM(CoordinateEnum::Left, CoordinateEnum::Superior, CoordinateEnum::Posterior), + RIA = ITK_ORIENTATIONENUM(CoordinateEnum::Right, CoordinateEnum::Inferior, CoordinateEnum::Anterior), + LIA = ITK_ORIENTATIONENUM(CoordinateEnum::Left, CoordinateEnum::Inferior, CoordinateEnum::Anterior), + RSA = ITK_ORIENTATIONENUM(CoordinateEnum::Right, CoordinateEnum::Superior, CoordinateEnum::Anterior), + LSA = ITK_ORIENTATIONENUM(CoordinateEnum::Left, CoordinateEnum::Superior, CoordinateEnum::Anterior), + + IRP = ITK_ORIENTATIONENUM(CoordinateEnum::Inferior, CoordinateEnum::Right, CoordinateEnum::Posterior), + ILP = ITK_ORIENTATIONENUM(CoordinateEnum::Inferior, CoordinateEnum::Left, CoordinateEnum::Posterior), + SRP = ITK_ORIENTATIONENUM(CoordinateEnum::Superior, CoordinateEnum::Right, CoordinateEnum::Posterior), + SLP = ITK_ORIENTATIONENUM(CoordinateEnum::Superior, CoordinateEnum::Left, CoordinateEnum::Posterior), + IRA = ITK_ORIENTATIONENUM(CoordinateEnum::Inferior, CoordinateEnum::Right, CoordinateEnum::Anterior), + ILA = ITK_ORIENTATIONENUM(CoordinateEnum::Inferior, CoordinateEnum::Left, CoordinateEnum::Anterior), + SRA = ITK_ORIENTATIONENUM(CoordinateEnum::Superior, CoordinateEnum::Right, CoordinateEnum::Anterior), + SLA = ITK_ORIENTATIONENUM(CoordinateEnum::Superior, CoordinateEnum::Left, CoordinateEnum::Anterior), + + RPI = ITK_ORIENTATIONENUM(CoordinateEnum::Right, CoordinateEnum::Posterior, CoordinateEnum::Inferior), + LPI = ITK_ORIENTATIONENUM(CoordinateEnum::Left, CoordinateEnum::Posterior, CoordinateEnum::Inferior), + RAI = ITK_ORIENTATIONENUM(CoordinateEnum::Right, CoordinateEnum::Anterior, CoordinateEnum::Inferior), + LAI = ITK_ORIENTATIONENUM(CoordinateEnum::Left, CoordinateEnum::Anterior, CoordinateEnum::Inferior), + RPS = ITK_ORIENTATIONENUM(CoordinateEnum::Right, CoordinateEnum::Posterior, CoordinateEnum::Superior), + LPS = ITK_ORIENTATIONENUM(CoordinateEnum::Left, CoordinateEnum::Posterior, CoordinateEnum::Superior), + RAS = ITK_ORIENTATIONENUM(CoordinateEnum::Right, CoordinateEnum::Anterior, CoordinateEnum::Superior), + LAS = ITK_ORIENTATIONENUM(CoordinateEnum::Left, CoordinateEnum::Anterior, CoordinateEnum::Superior), + + PRI = ITK_ORIENTATIONENUM(CoordinateEnum::Posterior, CoordinateEnum::Right, CoordinateEnum::Inferior), + PLI = ITK_ORIENTATIONENUM(CoordinateEnum::Posterior, CoordinateEnum::Left, CoordinateEnum::Inferior), + ARI = ITK_ORIENTATIONENUM(CoordinateEnum::Anterior, CoordinateEnum::Right, CoordinateEnum::Inferior), + ALI = ITK_ORIENTATIONENUM(CoordinateEnum::Anterior, CoordinateEnum::Left, CoordinateEnum::Inferior), + PRS = ITK_ORIENTATIONENUM(CoordinateEnum::Posterior, CoordinateEnum::Right, CoordinateEnum::Superior), + PLS = ITK_ORIENTATIONENUM(CoordinateEnum::Posterior, CoordinateEnum::Left, CoordinateEnum::Superior), + ARS = ITK_ORIENTATIONENUM(CoordinateEnum::Anterior, CoordinateEnum::Right, CoordinateEnum::Superior), + ALS = ITK_ORIENTATIONENUM(CoordinateEnum::Anterior, CoordinateEnum::Left, CoordinateEnum::Superior), + + IPR = ITK_ORIENTATIONENUM(CoordinateEnum::Inferior, CoordinateEnum::Posterior, CoordinateEnum::Right), + SPR = ITK_ORIENTATIONENUM(CoordinateEnum::Superior, CoordinateEnum::Posterior, CoordinateEnum::Right), + IAR = ITK_ORIENTATIONENUM(CoordinateEnum::Inferior, CoordinateEnum::Anterior, CoordinateEnum::Right), + SAR = ITK_ORIENTATIONENUM(CoordinateEnum::Superior, CoordinateEnum::Anterior, CoordinateEnum::Right), + IPL = ITK_ORIENTATIONENUM(CoordinateEnum::Inferior, CoordinateEnum::Posterior, CoordinateEnum::Left), + SPL = ITK_ORIENTATIONENUM(CoordinateEnum::Superior, CoordinateEnum::Posterior, CoordinateEnum::Left), + IAL = ITK_ORIENTATIONENUM(CoordinateEnum::Inferior, CoordinateEnum::Anterior, CoordinateEnum::Left), + SAL = ITK_ORIENTATIONENUM(CoordinateEnum::Superior, CoordinateEnum::Anterior, CoordinateEnum::Left), + + PIR = ITK_ORIENTATIONENUM(CoordinateEnum::Posterior, CoordinateEnum::Inferior, CoordinateEnum::Right), + PSR = ITK_ORIENTATIONENUM(CoordinateEnum::Posterior, CoordinateEnum::Superior, CoordinateEnum::Right), + AIR = ITK_ORIENTATIONENUM(CoordinateEnum::Anterior, CoordinateEnum::Inferior, CoordinateEnum::Right), + ASR = ITK_ORIENTATIONENUM(CoordinateEnum::Anterior, CoordinateEnum::Superior, CoordinateEnum::Right), + PIL = ITK_ORIENTATIONENUM(CoordinateEnum::Posterior, CoordinateEnum::Inferior, CoordinateEnum::Left), + PSL = ITK_ORIENTATIONENUM(CoordinateEnum::Posterior, CoordinateEnum::Superior, CoordinateEnum::Left), + AIL = ITK_ORIENTATIONENUM(CoordinateEnum::Anterior, CoordinateEnum::Inferior, CoordinateEnum::Left), + ASL = ITK_ORIENTATIONENUM(CoordinateEnum::Anterior, CoordinateEnum::Superior, CoordinateEnum::Left) + }; + +#undef ITK_ORIENTATIONENUM + + + /** \brief Initialize with CoordinateEnum's from separate axes. + * + * If multiple CoordinateEnums are from the same axes then the Orientation value is INVALID. + */ + DICOMOrientation(CoordinateEnum primary, CoordinateEnum secondary, CoordinateEnum tertiary); + + DICOMOrientation(OrientationEnum orientation) + : m_Value(orientation) + {} + + explicit DICOMOrientation(const DirectionType & d) + : m_Value(DirectionCosinesToOrientation(d)) + {} + + explicit DICOMOrientation(std::string str); + + operator OrientationEnum() const { return m_Value; } + + const std::string & + GetAsString() const; + + DirectionType + GetAsDirection() const + { + return OrientationToDirectionCosines(m_Value); + } + + OrientationEnum + GetAsOrientation() const + { + return m_Value; + } + + CoordinateEnum + GetPrimaryTerm() const + { + return GetCoordinateTerm(CoordinateMajornessTermsEnum::PrimaryMinor); + } + + CoordinateEnum + GetSecondaryTerm() const + { + return GetCoordinateTerm(CoordinateMajornessTermsEnum::SecondaryMinor); + } + + CoordinateEnum + GetTertiaryTerm() const + { + return GetCoordinateTerm(CoordinateMajornessTermsEnum::TertiaryMinor); + } + + + static bool + SameOrientationAxes(CoordinateEnum a, CoordinateEnum b) + { + const unsigned int AxisField = 0xE; // b1110, mask lowest bit + return (static_cast(a) & AxisField) == (static_cast(b) & AxisField); + } + + /** \brief Return the closest orientation for a direction cosine matrix. */ + static OrientationEnum + DirectionCosinesToOrientation(const DirectionType & dir); + + /** \brief Return the direction cosine matrix for a orientation. */ + static DirectionType OrientationToDirectionCosines(OrientationEnum); + + + friend ITKCommon_EXPORT std::ostream & + operator<<(std::ostream & out, OrientationEnum value); + + +private: + // Private methods to create the maps, these will only be called once. + static std::map + CreateCodeToString(); + static std::map + CreateStringToCode(); + + /** \brief Return the global instance of the map from orientation enum to strings. + * + * The implementation uses a function static local variable so the global is created only if needed, only once. + */ + static const std::map & + GetCodeToString(); + + /** \brief Return the global instance of the map from string to orientation enum. + */ + static const std::map & + GetStringToCode(); + + OrientationEnum m_Value; +}; + + +ITKCommon_EXPORT std::ostream & + operator<<(std::ostream & out, typename DICOMOrientation::OrientationEnum value); + +ITKCommon_EXPORT std::ostream & + operator<<(std::ostream & out, const DICOMOrientation & orientation); + + +} // end namespace itk + + +#endif diff --git a/Modules/Core/Common/src/CMakeLists.txt b/Modules/Core/Common/src/CMakeLists.txt index 74f031b08d1..cbfb85d200e 100644 --- a/Modules/Core/Common/src/CMakeLists.txt +++ b/Modules/Core/Common/src/CMakeLists.txt @@ -145,7 +145,9 @@ set(ITKCommon_SRCS itkFrustumSpatialFunction.cxx itkObjectStore.cxx itkGaussianDerivativeOperator.cxx - itkSpatialOrientation.cxx) + itkSpatialOrientation.cxx + itkDICOMOrientation.cxx +) if(WIN32) list(APPEND ITKCommon_SRCS itkWin32OutputWindow.cxx) diff --git a/Modules/Core/Common/src/itkDICOMOrientation.cxx b/Modules/Core/Common/src/itkDICOMOrientation.cxx new file mode 100644 index 00000000000..152318556b0 --- /dev/null +++ b/Modules/Core/Common/src/itkDICOMOrientation.cxx @@ -0,0 +1,273 @@ +/*========================================================================= + * + * Copyright NumFOCUS + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0.txt + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + *=========================================================================*/ +#include "itkDICOMOrientation.h" + +namespace itk +{ + + +DICOMOrientation::DICOMOrientation(CoordinateEnum primary, CoordinateEnum secondary, CoordinateEnum tertiary) +{ + if (SameOrientationAxes(primary, secondary) || SameOrientationAxes(primary, tertiary) || + SameOrientationAxes(secondary, tertiary)) + { + m_Value = OrientationEnum::INVALID; + } + else + { + m_Value = static_cast(toOrientation(primary, secondary, tertiary)); + } +} + +DICOMOrientation::DICOMOrientation(std::string str) + : m_Value(OrientationEnum::INVALID) +{ + std::transform(str.begin(), str.end(), str.begin(), ::toupper); + const std::map & stringToCode = GetStringToCode(); + auto iter = stringToCode.find(str); + if (iter != stringToCode.end()) + { + m_Value = iter->second; + } +} + + +const std::string & +DICOMOrientation::GetAsString() const +{ + const auto & codeToString = GetCodeToString(); + auto iter = codeToString.find(m_Value); + if (iter != codeToString.end()) + { + return iter->second; + } + assert(codeToString.find(OrientationEnum::INVALID) != codeToString.end()); + return codeToString.find(OrientationEnum::INVALID)->second; +} + + +DICOMOrientation::CoordinateEnum +DICOMOrientation::GetCoordinateTerm(CoordinateMajornessTermsEnum cmt) const +{ + return static_cast(static_cast(m_Value) >> static_cast(cmt) & 0xff); +} + +std::map +DICOMOrientation::CreateStringToCode() +{ + std::map stringToCode; + const std::map & codeToString = GetCodeToString(); + + for (const auto & kv : codeToString) + { + stringToCode[kv.second] = kv.first; + } + return stringToCode; +} + + +const std::map & +DICOMOrientation::GetCodeToString() +{ + static const std::map codeToString = CreateCodeToString(); + return codeToString; +} + +const std::map & +DICOMOrientation::GetStringToCode() +{ + static const std::map stringToCode = CreateStringToCode(); + return stringToCode; +} + + +std::map +DICOMOrientation::CreateCodeToString() +{ + std::map orientToString; + auto helperAddCode = [&orientToString](std::string str, OrientationEnum code) { + orientToString[code] = std::move(str); + }; + + // Map between axis string labels and SpatialOrientation + helperAddCode("RIP", OrientationEnum::RIP); + helperAddCode("LIP", OrientationEnum::LIP); + helperAddCode("RSP", OrientationEnum::RSP); + helperAddCode("LSP", OrientationEnum::LSP); + helperAddCode("RIA", OrientationEnum::RIA); + helperAddCode("LIA", OrientationEnum::LIA); + helperAddCode("RSA", OrientationEnum::RSA); + helperAddCode("LSA", OrientationEnum::LSA); + helperAddCode("IRP", OrientationEnum::IRP); + helperAddCode("ILP", OrientationEnum::ILP); + helperAddCode("SRP", OrientationEnum::SRP); + helperAddCode("SLP", OrientationEnum::SLP); + helperAddCode("IRA", OrientationEnum::IRA); + helperAddCode("ILA", OrientationEnum::ILA); + helperAddCode("SRA", OrientationEnum::SRA); + helperAddCode("SLA", OrientationEnum::SLA); + helperAddCode("RPI", OrientationEnum::RPI); + helperAddCode("LPI", OrientationEnum::LPI); + helperAddCode("RAI", OrientationEnum::RAI); + helperAddCode("LAI", OrientationEnum::LAI); + helperAddCode("RPS", OrientationEnum::RPS); + helperAddCode("LPS", OrientationEnum::LPS); + helperAddCode("RAS", OrientationEnum::RAS); + helperAddCode("LAS", OrientationEnum::LAS); + helperAddCode("PRI", OrientationEnum::PRI); + helperAddCode("PLI", OrientationEnum::PLI); + helperAddCode("ARI", OrientationEnum::ARI); + helperAddCode("ALI", OrientationEnum::ALI); + helperAddCode("PRS", OrientationEnum::PRS); + helperAddCode("PLS", OrientationEnum::PLS); + helperAddCode("ARS", OrientationEnum::ARS); + helperAddCode("ALS", OrientationEnum::ALS); + helperAddCode("IPR", OrientationEnum::IPR); + helperAddCode("SPR", OrientationEnum::SPR); + helperAddCode("IAR", OrientationEnum::IAR); + helperAddCode("SAR", OrientationEnum::SAR); + helperAddCode("IPL", OrientationEnum::IPL); + helperAddCode("SPL", OrientationEnum::SPL); + helperAddCode("IAL", OrientationEnum::IAL); + helperAddCode("SAL", OrientationEnum::SAL); + helperAddCode("PIR", OrientationEnum::PIR); + helperAddCode("PSR", OrientationEnum::PSR); + helperAddCode("AIR", OrientationEnum::AIR); + helperAddCode("ASR", OrientationEnum::ASR); + helperAddCode("PIL", OrientationEnum::PIL); + helperAddCode("PSL", OrientationEnum::PSL); + helperAddCode("AIL", OrientationEnum::AIL); + helperAddCode("ASL", OrientationEnum::ASL); + helperAddCode("INVALID", OrientationEnum::INVALID); + + return orientToString; +} + + +typename DICOMOrientation::OrientationEnum +DICOMOrientation::DirectionCosinesToOrientation(const DirectionType & dir) +{ + // NOTE: This method was based off of itk::SpatialObjectAdaptor::FromDirectionCosines + // but it is DIFFERENT in the meaning of direction in terms of sign-ness. + CoordinateEnum terms[3] = { CoordinateEnum::UNKNOWN, CoordinateEnum::UNKNOWN, CoordinateEnum::UNKNOWN }; + + std::multimap< double, std::pair > value_to_idx; + for (unsigned int c = 0; c < 3; ++c ) + { + for (unsigned int r = 0; r < 3; ++r ) + { + value_to_idx.emplace(std::abs(dir[c][r]), std::make_pair(c,r)); + } + } + + for (unsigned i = 0; i < 3; ++i) + { + + auto max_idx = value_to_idx.rbegin()->second; + const unsigned int max_c = max_idx.first; + const unsigned int max_r = max_idx.second; + + const int max_sgn = Math::sgn(dir[max_c][max_r]); + + for (auto it = value_to_idx.begin(); it != value_to_idx.end();) + { + if (it->second.first == max_c || it->second.second == max_r) + { + value_to_idx.erase(it++); + } + else + { + ++it; + } + } + + switch (max_c) + { + case 0: { + // When the dominant axis sign is positive, assign the coordinate for the direction we are increasing towards. + // ITK is in LPS, so that is the positive direction + terms[max_r] = (max_sgn == 1) ? CoordinateEnum::Left : CoordinateEnum::Right; + break; + } + case 1: { + terms[max_r] = (max_sgn == 1) ? CoordinateEnum::Posterior : CoordinateEnum::Anterior; + break; + } + case 2: { + terms[max_r] = (max_sgn == 1) ? CoordinateEnum::Superior : CoordinateEnum::Inferior; + break; + } + default: + itkGenericExceptionMacro("Unexpected Axis"); + } + } + + return static_cast(toOrientation(terms[0], terms[1], terms[2])); +} + + +typename DICOMOrientation::DirectionType +DICOMOrientation::OrientationToDirectionCosines(OrientationEnum orientationEnum) +{ + const DICOMOrientation o(orientationEnum); + + CoordinateEnum terms[Dimension] = { o.GetPrimaryTerm(), o.GetSecondaryTerm(), o.GetTertiaryTerm() }; + DirectionType direction; + direction.Fill(0.0); + + for (unsigned int i = 0; i < Dimension; ++i) + { + const int sign = (static_cast(terms[i]) & 0x1) ? 1 : -1; + + switch (terms[i]) + { + case CoordinateEnum::Left: + case CoordinateEnum::Right: + direction[0][i] = 1 * sign; + break; + case CoordinateEnum::Anterior: + case CoordinateEnum::Posterior: + direction[1][i] = 1 * sign; + break; + case CoordinateEnum::Inferior: + case CoordinateEnum::Superior: + direction[2][i] = 1 * sign; + break; + case CoordinateEnum::UNKNOWN: + break; + } + } + return direction; +} + +std::ostream & +operator<<(std::ostream & out, typename DICOMOrientation::OrientationEnum value) +{ + auto iter = DICOMOrientation::GetCodeToString().find(value); + assert(iter != DICOMOrientation::GetCodeToString().end()); + return (out << iter->second); +} + + +std::ostream & +operator<<(std::ostream & out, const DICOMOrientation & orientation) +{ + return (out << orientation.GetAsString()); +} + +} // namespace itk diff --git a/Modules/Core/Common/test/CMakeLists.txt b/Modules/Core/Common/test/CMakeLists.txt index 78846a40f49..b974c4eaeab 100644 --- a/Modules/Core/Common/test/CMakeLists.txt +++ b/Modules/Core/Common/test/CMakeLists.txt @@ -1758,7 +1758,9 @@ set(ITKCommonGTests itkWeakPointerGTest.cxx itkCommonTypeTraitsGTest.cxx itkMetaDataDictionaryGTest.cxx - itkSpatialOrientationAdaptorGTest.cxx) + itkSpatialOrientationAdaptorGTest.cxx + itkDICOMOrientionGTest.cxx +) creategoogletestdriver(ITKCommon "${ITKCommon-Test_LIBRARIES}" "${ITKCommonGTests}") # If `-static` was passed to CMAKE_EXE_LINKER_FLAGS, compilation fails. No need to # test this case. diff --git a/Modules/Core/Common/test/itkDICOMOrientionGTest.cxx b/Modules/Core/Common/test/itkDICOMOrientionGTest.cxx new file mode 100644 index 00000000000..ddbd279b488 --- /dev/null +++ b/Modules/Core/Common/test/itkDICOMOrientionGTest.cxx @@ -0,0 +1,206 @@ +/*========================================================================= + * + * Copyright NumFOCUS + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0.txt + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + *=========================================================================*/ + +#include + +#include "itkGTest.h" +#include "itkDICOMOrientation.h" +#include "itkImage.h" +#include + + +TEST(DICOMOrientation, ConstructionAndValues) +{ + using itk::DICOMOrientation; + using OE = DICOMOrientation::OrientationEnum; + using CE = DICOMOrientation::CoordinateEnum; + using ImageType = itk::Image; + + ImageType::DirectionType d; + + DICOMOrientation do1(OE::LPS); + + EXPECT_EQ("LPS", do1.GetAsString()); + EXPECT_EQ(CE::Left, do1.GetPrimaryTerm()); + EXPECT_EQ(CE::Posterior, do1.GetSecondaryTerm()); + EXPECT_EQ(CE::Superior, do1.GetTertiaryTerm()); + EXPECT_EQ(OE::LPS, do1.GetAsOrientation()); + + d.SetIdentity(); + EXPECT_EQ(d, do1.GetAsDirection()); + + + do1 = DICOMOrientation("RAS"); + + EXPECT_EQ("RAS", do1.GetAsString()); + EXPECT_EQ(CE::Right, do1.GetPrimaryTerm()); + EXPECT_EQ(CE::Anterior, do1.GetSecondaryTerm()); + EXPECT_EQ(CE::Superior, do1.GetTertiaryTerm()); + EXPECT_EQ(OE::RAS, do1.GetAsOrientation()); + + d.Fill(0.0); + d(0, 0) = -1.0; + d(1, 1) = -1.0; + d(2, 2) = 1.0; + EXPECT_EQ(d, do1.GetAsDirection()); + + + do1 = DICOMOrientation("rai"); + + EXPECT_EQ("RAI", do1.GetAsString()); + EXPECT_EQ(CE::Right, do1.GetPrimaryTerm()); + EXPECT_EQ(CE::Anterior, do1.GetSecondaryTerm()); + EXPECT_EQ(CE::Inferior, do1.GetTertiaryTerm()); + EXPECT_EQ(OE::RAI, do1.GetAsOrientation()); + + + do1 = DICOMOrientation(OE::PIR); + + EXPECT_EQ("PIR", do1.GetAsString()); + EXPECT_EQ(CE::Posterior, do1.GetPrimaryTerm()); + EXPECT_EQ(CE::Inferior, do1.GetSecondaryTerm()); + EXPECT_EQ(CE::Right, do1.GetTertiaryTerm()); + EXPECT_EQ(OE::PIR, do1.GetAsOrientation()); + + d.Fill(0.0); + d(1, 0) = 1.0; + d(2, 1) = -1.0; + d(0, 2) = -1.0; + EXPECT_EQ(d, do1.GetAsDirection()); + + DICOMOrientation do2(d); + + EXPECT_EQ("PIR", do2.GetAsString()); + EXPECT_EQ(CE::Posterior, do2.GetPrimaryTerm()); + EXPECT_EQ(CE::Inferior, do2.GetSecondaryTerm()); + EXPECT_EQ(CE::Right, do2.GetTertiaryTerm()); + EXPECT_EQ(OE::PIR, do2.GetAsOrientation()); + + EXPECT_EQ(d, do2.GetAsDirection()); + + DICOMOrientation do3("something invalid"); + EXPECT_EQ("INVALID", do3.GetAsString()); + EXPECT_EQ(CE::UNKNOWN, do3.GetPrimaryTerm()); + EXPECT_EQ(CE::UNKNOWN, do3.GetSecondaryTerm()); + EXPECT_EQ(CE::UNKNOWN, do3.GetTertiaryTerm()); + EXPECT_EQ(OE::INVALID, do3.GetAsOrientation()); +} + + +TEST(DICOMOrientation, DirectionCosinesToOrientation) +{ + using itk::DICOMOrientation; + using OE = DICOMOrientation::OrientationEnum; + using ImageType = itk::Image; + ImageType::DirectionType d; + d.SetIdentity(); + + EXPECT_EQ(OE::LPS, DICOMOrientation::DirectionCosinesToOrientation(d)); + + d.Fill(0.0); + d(0, 0) = -1.0; + d(1, 1) = -1.0; + d(2, 2) = -1.0; + EXPECT_EQ(OE::RAI, DICOMOrientation::DirectionCosinesToOrientation(d)); + + d.Fill(0.0); + d(2, 0) = 1; + d(0, 1) = 1; + d(1, 2) = 1; + EXPECT_EQ(OE::SLP, DICOMOrientation::DirectionCosinesToOrientation(d)); + + d.Fill(0.0); + d(1, 0) = 1; + d(2, 1) = 1; + d(0, 2) = 1; + EXPECT_EQ(OE::PSL, DICOMOrientation::DirectionCosinesToOrientation(d)); + + d.Fill(0.0); + d(0, 0) = 1; + d(2, 1) = 1; + d(1, 2) = 1; + EXPECT_EQ(OE::LSP, DICOMOrientation::DirectionCosinesToOrientation(d)); + + d.Fill(0.0); + d(1, 0) = 1; + d(0, 1) = 1; + d(2, 2) = 1; + EXPECT_EQ(OE::PLS, DICOMOrientation::DirectionCosinesToOrientation(d)); + + d.Fill(0.0); + d(2, 0) = 1; + d(1, 1) = 1; + d(0, 2) = 1; + EXPECT_EQ(OE::SPL, DICOMOrientation::DirectionCosinesToOrientation(d)); + + const double data[] = {0.5986634407395047, 0.22716302314740483, -0.768113953548866, + 0.5627936241740271, 0.563067040943212, 0.6051601804419384, + 0.5699696670095713, -0.794576911518317, 0.20924175102261847}; + ImageType::DirectionType::InternalMatrixType m{data}; + d.GetVnlMatrix() = m; + EXPECT_EQ(OE::PIR, DICOMOrientation::DirectionCosinesToOrientation(d)); + +} + +TEST(DICOMOrientation, OrientationToDirectionCosines) +{ + using itk::DICOMOrientation; + using ImageType = itk::Image; + using OE = DICOMOrientation::OrientationEnum; + + ImageType::DirectionType d; + d.SetIdentity(); + + EXPECT_EQ(d, DICOMOrientation::OrientationToDirectionCosines(OE::LPS)); + + d.Fill(0.0); + d(0, 0) = -1.0; + d(1, 1) = -1.0; + d(2, 2) = -1.0; + EXPECT_EQ(d, DICOMOrientation::OrientationToDirectionCosines(OE::RAI)); + + d.Fill(0.0); + d(2, 0) = 1; + d(0, 1) = 1; + d(1, 2) = 1; + EXPECT_EQ(d, DICOMOrientation::OrientationToDirectionCosines(OE::SLP)); + + d.Fill(0.0); + d(1, 0) = 1; + d(2, 1) = 1; + d(0, 2) = 1; + EXPECT_EQ(d, DICOMOrientation::OrientationToDirectionCosines(OE::PSL)); + + d.Fill(0.0); + d(0, 0) = 1; + d(2, 1) = 1; + d(1, 2) = 1; + EXPECT_EQ(d, DICOMOrientation::OrientationToDirectionCosines(OE::LSP)); + + d.Fill(0.0); + d(1, 0) = 1; + d(0, 1) = 1; + d(2, 2) = 1; + EXPECT_EQ(d, DICOMOrientation::OrientationToDirectionCosines(OE::PLS)); + + d.Fill(0.0); + d(2, 0) = 1; + d(1, 1) = 1; + d(0, 2) = 1; + EXPECT_EQ(d, DICOMOrientation::OrientationToDirectionCosines(OE::SPL)); +} diff --git a/Modules/Core/Common/wrapping/itkDICOMOrientation.wrap b/Modules/Core/Common/wrapping/itkDICOMOrientation.wrap new file mode 100644 index 00000000000..88df9faaa6f --- /dev/null +++ b/Modules/Core/Common/wrapping/itkDICOMOrientation.wrap @@ -0,0 +1 @@ +itk_wrap_simple_class("itk::DICOMOrientation") From f65805d48e1079a9e50130e50d644bf02c604f8c Mon Sep 17 00:00:00 2001 From: Bradley Lowekamp Date: Wed, 31 Jul 2024 10:06:44 -0400 Subject: [PATCH 02/10] ENH: Add DICOMOrientImageFilter This is a replacement for the OrientImageFilter, which used the DICOMOrientation enumerations and conventions for describing the patient orientation by the increasing anaomical axis. --- .github/workflows/itk_dict.txt | 2 + .../include/itkDICOMOrientImageFilter.h | 191 ++++++++++ .../include/itkDICOMOrientImageFilter.hxx | 322 +++++++++++++++++ .../Filtering/ImageGrid/test/CMakeLists.txt | 4 +- .../test/itkDICOMOrientImageFilterGTest.cxx | 327 ++++++++++++++++++ .../wrapping/itkDICOMOrientImageFilter.wrap | 6 + 6 files changed, 851 insertions(+), 1 deletion(-) create mode 100644 Modules/Filtering/ImageGrid/include/itkDICOMOrientImageFilter.h create mode 100644 Modules/Filtering/ImageGrid/include/itkDICOMOrientImageFilter.hxx create mode 100644 Modules/Filtering/ImageGrid/test/itkDICOMOrientImageFilterGTest.cxx create mode 100644 Modules/Filtering/ImageGrid/wrapping/itkDICOMOrientImageFilter.wrap diff --git a/.github/workflows/itk_dict.txt b/.github/workflows/itk_dict.txt index 4964a81d868..95cf3ac8257 100644 --- a/.github/workflows/itk_dict.txt +++ b/.github/workflows/itk_dict.txt @@ -715,6 +715,7 @@ orthonormality opencl openjpeg oper +osterior outJacobian's outdir oversegmentation @@ -942,6 +943,7 @@ unsmoothed unvisited upcast updatable +uperior uplist uplists upsampled diff --git a/Modules/Filtering/ImageGrid/include/itkDICOMOrientImageFilter.h b/Modules/Filtering/ImageGrid/include/itkDICOMOrientImageFilter.h new file mode 100644 index 00000000000..beb5f7cbe9e --- /dev/null +++ b/Modules/Filtering/ImageGrid/include/itkDICOMOrientImageFilter.h @@ -0,0 +1,191 @@ +/*========================================================================= + * + * Copyright NumFOCUS + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0.txt + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + *=========================================================================*/ +#ifndef itkDICOMOrientImageFilter_h +#define itkDICOMOrientImageFilter_h + +#include "itkDICOMOrientation.h" +#include "itkPermuteAxesImageFilter.h" +#include "itkFlipImageFilter.h" +#include +#include + +namespace itk +{ + +/** \class DICOMOrientImageFilter + * \brief Permute axes and flip images as needed to obtain an approximation to the desired orientation. + * + * The physical location of all pixels in the image remains the same, but the meta-data and the ordering of the stored + * pixels may change. + * + * + * DICOMOrientImageFilter depends on a set of constants that describe all possible labels. Directions are labeled in + * terms of following pairs: + * - Left and Right (Subject's left and right) + * - Anterior and Posterior (Subject's front and back) + * - Inferior and Superior (Subject's bottom and top, i.e. feet and head) + * + * The initials of these directions are used in a 3 letter code in the enumerated type OrientationEnum. The initials are + * given fastest moving index first, second fastest second, third fastest third, where the label's direction indicates + * increasing values. + * + * An ITK image with an identity direction cosine matrix is in LPS (Left, Posterior, Superior) orientation as defined by + * the DICOM standard. + * + * \f[ + * LPS = \begin{Bmatrix} + * from\ right\ to\ \textbf{L}eft \\ + * from\ anterior\ towards\ \textbf{P}osterior \\ + * from\ inferior\ towards\ \textbf{S}uperior + * \end{Bmatrix} + * \f] + * + * The output orientation is specified with SetDesiredCoordinateOrientation. The input coordinate orientation is + * computed from the input image's direction cosine matrix. + * \ingroup ITKImageGrid + */ +template +class ITK_TEMPLATE_EXPORT DICOMOrientImageFilter : public ImageToImageFilter +{ +public: + ITK_DISALLOW_COPY_AND_MOVE(DICOMOrientImageFilter); + + /** Standard class type aliases. */ + using Self = DICOMOrientImageFilter; + using Superclass = ImageToImageFilter; + using Pointer = SmartPointer; + using ConstPointer = SmartPointer; + + /** Some convenient type alias. */ + using ImageType = TInputImage; + using ImagePointer = typename ImageType::Pointer; + using ImageConstPointer = typename ImageType::ConstPointer; + using ImageRegionType = typename ImageType::RegionType; + using ImagePixelType = typename ImageType::PixelType; + using DirectionType = typename ImageType::DirectionType; + + /** Axes permuter type. */ + using PermuterType = PermuteAxesImageFilter; + using PermuteOrderArrayType = typename PermuterType::PermuteOrderArrayType; + + /** Axes flipper type. */ + using FlipperType = FlipImageFilter; + using FlipAxesArrayType = typename FlipperType::FlipAxesArrayType; + + /** ImageDimension constants */ + static constexpr unsigned int ImageDimension = ImageType::ImageDimension; + + /** Standard New method. */ + itkNewMacro(Self); + + /** Runtime information support. */ + itkTypeMacro(DICOMOrientImageFilter, ImageToImageFilter); + + using OrientationEnum = DICOMOrientation::OrientationEnum; + + /** Get the orientation codes that defines the input coordinate transform. + * + * This value changes during the execution of the Update in the pipeline. */ + itkGetEnumMacro(InputCoordinateOrientation, OrientationEnum); + + /** Set/Get the desired coordinate orientation for the output image */ + itkGetEnumMacro(DesiredCoordinateOrientation, OrientationEnum); + void + SetDesiredCoordinateOrientation(OrientationEnum newCode); + void + SetDesiredCoordinateOrientation(const std::string & desired); + + /** Set Get the desired coordinate orientation from a direction matrix. */ + inline void + SetDesiredCoordinateDirection(const typename ImageType::DirectionType & DesiredDirection) + { + SetDesiredCoordinateOrientation(DICOMOrientation::DirectionCosinesToOrientation(DesiredDirection)); + } + + + /** Get axes permute order. + * + * This value is computed during Update. + * */ + itkGetConstReferenceMacro(PermuteOrder, PermuteOrderArrayType); + + /** Get flip axes. + * + * This value is computed during Update. + * */ + itkGetConstReferenceMacro(FlipAxes, FlipAxesArrayType); + + + /** DICOMOrientImageFilter produces an image which is a different + * meta-data than its input image. + * \sa ProcessObject::GenerateOutputInformation() */ + void + GenerateOutputInformation() override; + + static_assert(ImageDimension == 3, "Only 3 dimensional images are support!"); + +protected: + DICOMOrientImageFilter(); + ~DICOMOrientImageFilter() override = default; + void + PrintSelf(std::ostream & os, Indent indent) const override; + + /** OrientImageFilter will produce the entire output. */ + void + GenerateInputRequestedRegion() override; + + /*** Member functions used by GenerateData: */ + void + DeterminePermutationsAndFlips(DICOMOrientation desired, DICOMOrientation given); + + /** Returns true if a permute is required. Returns false otherwise. */ + bool + NeedToPermute(); + + /** Returns true if flipping is required. Returns false otherwise. */ + bool + NeedToFlip(); + + + void + SetInputCoordinateOrientation(OrientationEnum newCode); + + void + VerifyPreconditions() const override; + + /** Single-threaded version of GenerateData. This filter delegates + * to PermuteAxesImageFilter and FlipImageFilter. */ + void + GenerateData() override; + +private: + DICOMOrientation m_InputCoordinateOrientation{ OrientationEnum::INVALID }; + DICOMOrientation m_DesiredCoordinateOrientation{ OrientationEnum::LPS }; + + PermuteOrderArrayType m_PermuteOrder; + FlipAxesArrayType m_FlipAxes; + +}; // end of class + +} // end namespace itk + +#ifndef ITK_MANUAL_INSTANTIATION +# include "itkDICOMOrientImageFilter.hxx" +#endif + +#endif diff --git a/Modules/Filtering/ImageGrid/include/itkDICOMOrientImageFilter.hxx b/Modules/Filtering/ImageGrid/include/itkDICOMOrientImageFilter.hxx new file mode 100644 index 00000000000..524188ca75a --- /dev/null +++ b/Modules/Filtering/ImageGrid/include/itkDICOMOrientImageFilter.hxx @@ -0,0 +1,322 @@ +/*========================================================================= + * + * Copyright NumFOCUS + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0.txt + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + *=========================================================================*/ +#ifndef itkDICOMOrientImageFilter_hxx +#define itkDICOMOrientImageFilter_hxx + +#include "itkDICOMOrientImageFilter.h" +#include "itkPermuteAxesImageFilter.h" +#include "itkFlipImageFilter.h" +#include "itkCastImageFilter.h" +#include "itkProgressAccumulator.h" + +namespace itk +{ + +template +DICOMOrientImageFilter::DICOMOrientImageFilter() + : m_FlipAxes{ false } +{ + for (unsigned int dimension = 0; dimension < ImageDimension; ++dimension) + { + this->m_PermuteOrder[dimension] = dimension; + } +} + +template +void +DICOMOrientImageFilter::GenerateInputRequestedRegion() +{ + Superclass::GenerateInputRequestedRegion(); + + // Get pointers to the input and output + typename ImageType::Pointer inputPtr = const_cast(this->GetInput()); + if (inputPtr) + { + inputPtr->SetRequestedRegionToLargestPossibleRegion(); + } +} + + +template +void +DICOMOrientImageFilter::DeterminePermutationsAndFlips(DICOMOrientation desired, DICOMOrientation given) +{ + for (unsigned int j = 0; j < ImageDimension; j++) + { + m_PermuteOrder[j] = j; + } + + m_FlipAxes.Fill(false); + + constexpr unsigned int CodeAxisDir = 0x1; + + const DICOMOrientation::CoordinateEnum desired_codes[] = { desired.GetPrimaryTerm(), + desired.GetSecondaryTerm(), + desired.GetTertiaryTerm() }; + const DICOMOrientation::CoordinateEnum given_codes[] = { given.GetPrimaryTerm(), + given.GetSecondaryTerm(), + given.GetTertiaryTerm() }; + + // i, j, k will be the indexes in the Majorness code of the axes to flip; + // they encode the axes as the reader will find them, 0 is the lowest order + // axis of whatever spatial interpretation, and 2 is the highest order axis. + // Perhaps rename them moving_image_reader_axis_i, etc. + + for (unsigned int i = 0; i < ImageDimension - 1; i++) + { + if (!DICOMOrientation::SameOrientationAxes(given_codes[i], desired_codes[i])) + { + for (unsigned int j = 0; j < ImageDimension; j++) + { + if (DICOMOrientation::SameOrientationAxes(given_codes[i], desired_codes[j])) + { + if (i == j) + { + // Axis i is already in place + continue; + } + if (DICOMOrientation::SameOrientationAxes(given_codes[j], desired_codes[i])) + { + // The cyclic permutation (i j) applies. Therefore the remainder + // is (k), i.e., stationary + m_PermuteOrder[i] = j; + m_PermuteOrder[j] = i; + } + else + { + // work out an (i j k) cyclic permutation + for (unsigned int k = 0; k < ImageDimension; k++) + { + + if (DICOMOrientation::SameOrientationAxes(given_codes[j], desired_codes[k])) + { + // At this point, we can pick off (i j k) + m_PermuteOrder[i] = k; + m_PermuteOrder[j] = i; + m_PermuteOrder[k] = j; + break; + } + } + // Effectively, if (k==3) continue + } + break; + } + } + // Effectively, if (j==3) continue + } + } + + for (unsigned int i = 0; i < ImageDimension; i++) + { + const unsigned int j = m_PermuteOrder[i]; + if ((static_cast(given_codes[j]) & CodeAxisDir) != (static_cast(desired_codes[i]) & CodeAxisDir)) + { + m_FlipAxes[i] = true; + } + } +} + +template +void +DICOMOrientImageFilter::SetInputCoordinateOrientation(OrientationEnum newCode) +{ + m_InputCoordinateOrientation = newCode; + + this->DeterminePermutationsAndFlips(m_DesiredCoordinateOrientation, m_InputCoordinateOrientation); +} + +template +void +DICOMOrientImageFilter::SetDesiredCoordinateOrientation(OrientationEnum newCode) +{ + if (m_DesiredCoordinateOrientation != newCode) + { + m_DesiredCoordinateOrientation = newCode; + this->DeterminePermutationsAndFlips(m_DesiredCoordinateOrientation, m_InputCoordinateOrientation); + this->Modified(); + } +} + + +template +void +DICOMOrientImageFilter::SetDesiredCoordinateOrientation(const std::string & desired) +{ + DICOMOrientation o(desired); + + if (OrientationEnum(o) == OrientationEnum::INVALID) + { + itkWarningMacro("Invalid desired coordinate direction string: \"" << desired << "\"!"); + } + this->SetDesiredCoordinateOrientation(o); +} + +template +bool +DICOMOrientImageFilter::NeedToPermute() +{ + for (unsigned int j = 0; j < ImageDimension; j++) + { + if (m_PermuteOrder[j] != j) + { + return true; + } + } + return false; +} + +template +bool +DICOMOrientImageFilter::NeedToFlip() +{ + for (unsigned int j = 0; j < ImageDimension; j++) + { + if (m_FlipAxes[j]) + { + return true; + } + } + return false; +} + +template +void +DICOMOrientImageFilter::GenerateData() +{ + // Create a process accumulator for tracking the progress of this minipipeline + typename ProgressAccumulator::Pointer progress = ProgressAccumulator::New(); + progress->SetMiniPipelineFilter(this); + + // No need to allocate the output since the minipipeline does it + // this->AllocateOutputs(); + + using PermuteFilterType = PermuteAxesImageFilter; + using FlipFilterType = FlipImageFilter; + using CastToOutputFilterType = CastImageFilter; + + typename PermuteFilterType::Pointer permute = PermuteFilterType::New(); + typename FlipFilterType::Pointer flip = FlipFilterType::New(); + typename CastToOutputFilterType::Pointer cast = CastToOutputFilterType::New(); + + progress->RegisterInternalFilter(permute, .45f); + progress->RegisterInternalFilter(flip, .45); + progress->RegisterInternalFilter(cast, .1f); + + typename ImageType::Pointer inputPtr = ImageType::New(); + inputPtr->Graft(const_cast(this->GetInput())); + + typename ImageType::Pointer nextInput = inputPtr; + + // Only run those filters that will do something + if (NeedToPermute()) + { + permute->SetInput(nextInput); + permute->SetOrder(m_PermuteOrder); + permute->ReleaseDataFlagOn(); + nextInput = permute->GetOutput(); + } + else + { + itkDebugMacro("No need to permute"); + } + if (NeedToFlip()) + { + flip->SetInput(nextInput); + flip->SetFlipAxes(m_FlipAxes); + flip->FlipAboutOriginOff(); + nextInput = flip->GetOutput(); + } + else + { + itkDebugMacro(<< "No need to flip"); + } + + // + // Cast might not be necessary, but CastImageFilter is optimized for + // the case where the ImageType == OutputImageType + cast->SetInput(nextInput); + cast->GraftOutput(this->GetOutput()); + cast->Update(); + + this->GraftOutput(cast->GetOutput()); +} + +template +void +DICOMOrientImageFilter::GenerateOutputInformation() +{ + // Call the superclass' implementation of this method + Superclass::GenerateOutputInformation(); + + // Get pointers to the input and output + + typename ImageType::Pointer inputPtr = ImageType::New(); + inputPtr->Graft(const_cast(this->GetInput())); + ImageType * outputPtr = this->GetOutput(); + + + // Compute the GivenOrientation from the image's direction cosines + const DICOMOrientation inputOrientation(inputPtr->GetDirection()); + this->SetInputCoordinateOrientation(inputOrientation.GetAsOrientation()); + + using PermuteFilterType = PermuteAxesImageFilter; + using FlipFilterType = FlipImageFilter; + + typename PermuteFilterType::Pointer permute = PermuteFilterType::New(); + typename FlipFilterType::Pointer flip = FlipFilterType::New(); + permute->SetInput(inputPtr); + permute->SetOrder(m_PermuteOrder); + + flip->SetInput(permute->GetOutput()); + flip->SetFlipAxes(m_FlipAxes); + flip->FlipAboutOriginOff(); + + flip->GraftOutput(this->GetOutput()); + flip->UpdateOutputInformation(); + + outputPtr->CopyInformation(flip->GetOutput()); +} + + +template +void +DICOMOrientImageFilter::VerifyPreconditions() const +{ + Superclass::VerifyPreconditions(); + + if (this->m_DesiredCoordinateOrientation == OrientationEnum::INVALID) + { + itkExceptionMacro(<< "DesiredCoordinateOrientation is 'INVALID'."); + } +} + +template +void +DICOMOrientImageFilter::PrintSelf(std::ostream & os, Indent indent) const +{ + Superclass::PrintSelf(os, indent); + + os << indent << "Desired Coordinate Orientation: " << static_cast(this->GetDesiredCoordinateOrientation()) + << " (" << this->GetDesiredCoordinateOrientation() << ")" << std::endl; + os << indent << "Input Coordinate Orientation: " << static_cast(this->GetInputCoordinateOrientation()) << " (" + << this->GetInputCoordinateOrientation() << ")" << std::endl; + os << indent << "Permute Axes: " << m_PermuteOrder << std::endl; + os << indent << "Flip Axes: " << m_FlipAxes << std::endl; +} +} // end namespace itk +#endif diff --git a/Modules/Filtering/ImageGrid/test/CMakeLists.txt b/Modules/Filtering/ImageGrid/test/CMakeLists.txt index aa14d2efee8..4b31275152d 100644 --- a/Modules/Filtering/ImageGrid/test/CMakeLists.txt +++ b/Modules/Filtering/ImageGrid/test/CMakeLists.txt @@ -749,6 +749,8 @@ set(ITKImageGridGTests itkResampleImageFilterGTest.cxx itkSliceImageFilterTest.cxx itkTileImageFilterGTest.cxx - itkPasteImageFilterGTest.cxx) + itkPasteImageFilterGTest.cxx + itkDICOMOrientImageFilterGTest.cxx +) creategoogletestdriver(ITKImageGrid "${ITKImageGrid-Test_LIBRARIES}" "${ITKImageGridGTests}") diff --git a/Modules/Filtering/ImageGrid/test/itkDICOMOrientImageFilterGTest.cxx b/Modules/Filtering/ImageGrid/test/itkDICOMOrientImageFilterGTest.cxx new file mode 100644 index 00000000000..65e8ef856ae --- /dev/null +++ b/Modules/Filtering/ImageGrid/test/itkDICOMOrientImageFilterGTest.cxx @@ -0,0 +1,327 @@ +/*========================================================================= + * + * Copyright NumFOCUS + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0.txt + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + *=========================================================================*/ + +#include + +#include "itkGTest.h" +#include "itkImageRegionIteratorWithIndex.h" +#include "itkDICOMOrientImageFilter.h" +#include "itkRandomImageSource.h" +#include "itkImage.h" +#include + +namespace +{ + +template +void +CheckImage(const itk::Image * inputImage, const itk::Image * resultsImage) +{ + using ImageType = itk::Image; + + itk::ImageRegionConstIteratorWithIndex iter(resultsImage, resultsImage->GetBufferedRegion()); + + while (!iter.IsAtEnd()) + { + auto resultsIdx = iter.GetIndex(); + auto point = resultsImage->template TransformIndexToPhysicalPoint(resultsIdx); + auto inputIdx = inputImage->TransformPhysicalPointToIndex(point); + + EXPECT_TRUE(inputImage->GetBufferedRegion().IsInside(inputIdx)); + + auto resultValue = iter.Value(); + auto inputValue = inputImage->GetPixel(inputIdx); + + EXPECT_EQ(resultValue, inputValue) << " result idx: " << resultsIdx << " input index: " << inputIdx; + + ++iter; + } +} + +} // namespace + + +TEST(DICOMOrientImageFilter, NoOp) +{ + using ImageType = itk::Image; + auto filter = itk::DICOMOrientImageFilter::New(); +} + + +TEST(DICOMOrientImageFilter, Streaming) +{ + using ImageType = itk::Image; + + auto sourceFilter = itk::RandomImageSource::New(); + sourceFilter->SetMin(-1000); + sourceFilter->SetMax(1000); + sourceFilter->SetSize({ 5, 6, 7 }); + sourceFilter->UpdateLargestPossibleRegion(); + + ImageType::DirectionType direction; + direction.Fill(0.0); + direction(0, 0) = -1.0; + direction(1, 1) = -1.0; + direction(2, 2) = 1.0; + sourceFilter->SetDirection(direction); + + + using OrientFilterType = itk::DICOMOrientImageFilter; + using PermuteArrayType = OrientFilterType::PermuteOrderArrayType; + using FlipArrayType = OrientFilterType::FlipAxesArrayType; + auto filter = OrientFilterType::New(); + filter->SetInput(sourceFilter->GetOutput()); + + filter->GetOutput()->UpdateOutputInformation(); + filter->GetOutput()->SetRequestedRegion(itk::Size<3>({ { 1, 2, 3 } })); + filter->Update(); + + + CheckImage(sourceFilter->GetOutput(), filter->GetOutput()); + EXPECT_EQ(filter->GetOutput()->GetBufferedRegion().GetSize(), itk::Size<3>({ { 1, 2, 3 } })); + EXPECT_EQ(filter->GetInputCoordinateOrientation(), OrientFilterType::OrientationEnum::RAS); + EXPECT_EQ(filter->GetPermuteOrder(), PermuteArrayType({ 0, 1, 2 })); + EXPECT_EQ(filter->GetFlipAxes(), FlipArrayType({ 1, 1, 0 })); + { + ImageType::DirectionType d; + d.SetIdentity(); + EXPECT_EQ(filter->GetOutput()->GetDirection(), d); + } +} + +TEST(DICOMOrientImageFilter, Values) +{ + using ImageType = itk::Image; + + auto sourceFilter = itk::RandomImageSource::New(); + sourceFilter->SetMin(-1000); + sourceFilter->SetMax(1000); + sourceFilter->SetSize({ 5, 6, 7 }); + + + using OrientFilterType = itk::DICOMOrientImageFilter; + using PermuiteArrayType = OrientFilterType::PermuteOrderArrayType; + using FlipArrayType = OrientFilterType::FlipAxesArrayType; + auto filter = OrientFilterType::New(); + filter->SetInput(sourceFilter->GetOutput()); + + filter->UpdateLargestPossibleRegion(); + CheckImage(sourceFilter->GetOutput(), filter->GetOutput()); + + EXPECT_EQ(filter->GetDesiredCoordinateOrientation(), OrientFilterType::OrientationEnum::LPS); + EXPECT_EQ(filter->GetInputCoordinateOrientation(), OrientFilterType::OrientationEnum::LPS); + EXPECT_EQ(filter->GetPermuteOrder(), PermuiteArrayType({ 0, 1, 2 })); + EXPECT_EQ(filter->GetFlipAxes(), FlipArrayType({ 0, 0, 0 })); + { + ImageType::DirectionType d; + d.SetIdentity(); + EXPECT_EQ(filter->GetOutput()->GetDirection(), d); + } + + + filter->SetDesiredCoordinateOrientation(OrientFilterType::OrientationEnum::RAS); + filter->UpdateLargestPossibleRegion(); + CheckImage(sourceFilter->GetOutput(), filter->GetOutput()); + + EXPECT_EQ(filter->GetDesiredCoordinateOrientation(), OrientFilterType::OrientationEnum::RAS); + EXPECT_EQ(filter->GetInputCoordinateOrientation(), OrientFilterType::OrientationEnum::LPS); + EXPECT_EQ(filter->GetPermuteOrder(), PermuiteArrayType({ 0, 1, 2 })); + EXPECT_EQ(filter->GetFlipAxes(), FlipArrayType({ 1, 1, 0 })); + { + ImageType::DirectionType d; + d.Fill(0.0); + d(0, 0) = -1.0; + d(1, 1) = -1.0; + d(2, 2) = 1; + EXPECT_EQ(filter->GetOutput()->GetDirection(), d); + } + + + { + ImageType::DirectionType d; + d.SetIdentity(); + filter->SetDesiredCoordinateDirection(d); + filter->UpdateLargestPossibleRegion(); + EXPECT_EQ(filter->GetInputCoordinateOrientation(), OrientFilterType::OrientationEnum::LPS); + } + + + filter->SetDesiredCoordinateOrientation(OrientFilterType::OrientationEnum::RIP); + filter->UpdateLargestPossibleRegion(); + CheckImage(sourceFilter->GetOutput(), filter->GetOutput()); + EXPECT_EQ(filter->GetDesiredCoordinateOrientation(), OrientFilterType::OrientationEnum::RIP); + EXPECT_EQ(filter->GetInputCoordinateOrientation(), OrientFilterType::OrientationEnum::LPS); + + filter->SetDesiredCoordinateOrientation(OrientFilterType::OrientationEnum::LIA); + filter->UpdateLargestPossibleRegion(); + CheckImage(sourceFilter->GetOutput(), filter->GetOutput()); + EXPECT_EQ(filter->GetDesiredCoordinateOrientation(), OrientFilterType::OrientationEnum::LIA); + EXPECT_EQ(filter->GetInputCoordinateOrientation(), OrientFilterType::OrientationEnum::LPS); + EXPECT_EQ(filter->GetPermuteOrder(), PermuiteArrayType({ 0, 2, 1 })); + EXPECT_EQ(filter->GetFlipAxes(), FlipArrayType({ 0, 1, 1 })); + { + ImageType::DirectionType d; + d.Fill(0.0); + d(0, 0) = 1.0; + d(1, 2) = -1.0; + d(2, 1) = -1.0; + EXPECT_EQ(filter->GetOutput()->GetDirection(), d); + } + + filter->SetDesiredCoordinateOrientation(OrientFilterType::OrientationEnum::IRP); + filter->UpdateLargestPossibleRegion(); + CheckImage(sourceFilter->GetOutput(), filter->GetOutput()); + EXPECT_EQ(filter->GetDesiredCoordinateOrientation(), OrientFilterType::OrientationEnum::IRP); + EXPECT_EQ(filter->GetInputCoordinateOrientation(), OrientFilterType::OrientationEnum::LPS); + EXPECT_EQ(filter->GetPermuteOrder(), PermuiteArrayType({ 2, 0, 1 })); + EXPECT_EQ(filter->GetFlipAxes(), FlipArrayType({ 1, 1, 0 })); + + filter->SetDesiredCoordinateOrientation(OrientFilterType::OrientationEnum::SLA); + filter->UpdateLargestPossibleRegion(); + CheckImage(sourceFilter->GetOutput(), filter->GetOutput()); + EXPECT_EQ(filter->GetDesiredCoordinateOrientation(), OrientFilterType::OrientationEnum::SLA); + EXPECT_EQ(filter->GetInputCoordinateOrientation(), OrientFilterType::OrientationEnum::LPS); + EXPECT_EQ(filter->GetPermuteOrder(), PermuiteArrayType({ 2, 0, 1 })); + EXPECT_EQ(filter->GetFlipAxes(), FlipArrayType({ 0, 0, 1 })); + + filter->SetDesiredCoordinateOrientation(OrientFilterType::OrientationEnum::RPI); + filter->UpdateLargestPossibleRegion(); + CheckImage(sourceFilter->GetOutput(), filter->GetOutput()); + EXPECT_EQ(filter->GetInputCoordinateOrientation(), OrientFilterType::OrientationEnum::LPS); + EXPECT_EQ(filter->GetPermuteOrder(), PermuiteArrayType({ 0, 1, 2 })); + EXPECT_EQ(filter->GetFlipAxes(), FlipArrayType({ 1, 0, 1 })); + + filter->SetDesiredCoordinateOrientation(OrientFilterType::OrientationEnum::LAI); + filter->UpdateLargestPossibleRegion(); + CheckImage(sourceFilter->GetOutput(), filter->GetOutput()); + EXPECT_EQ(filter->GetInputCoordinateOrientation(), OrientFilterType::OrientationEnum::LPS); + EXPECT_EQ(filter->GetPermuteOrder(), PermuiteArrayType({ 0, 1, 2 })); + EXPECT_EQ(filter->GetFlipAxes(), FlipArrayType({ 0, 1, 1 })); + + filter->SetDesiredCoordinateOrientation(OrientFilterType::OrientationEnum::LAS); + filter->UpdateLargestPossibleRegion(); + CheckImage(sourceFilter->GetOutput(), filter->GetOutput()); + EXPECT_EQ(filter->GetInputCoordinateOrientation(), OrientFilterType::OrientationEnum::LPS); + + filter->SetDesiredCoordinateOrientation(OrientFilterType::OrientationEnum::PRI); + filter->UpdateLargestPossibleRegion(); + CheckImage(sourceFilter->GetOutput(), filter->GetOutput()); + EXPECT_EQ(filter->GetInputCoordinateOrientation(), OrientFilterType::OrientationEnum::LPS); + + filter->SetDesiredCoordinateOrientation(OrientFilterType::OrientationEnum::PLS); + filter->UpdateLargestPossibleRegion(); + CheckImage(sourceFilter->GetOutput(), filter->GetOutput()); + EXPECT_EQ(filter->GetInputCoordinateOrientation(), OrientFilterType::OrientationEnum::LPS); + + filter->SetDesiredCoordinateOrientation(OrientFilterType::OrientationEnum::ALS); + filter->UpdateLargestPossibleRegion(); + CheckImage(sourceFilter->GetOutput(), filter->GetOutput()); + EXPECT_EQ(filter->GetInputCoordinateOrientation(), OrientFilterType::OrientationEnum::LPS); + + filter->SetDesiredCoordinateOrientation(OrientFilterType::OrientationEnum::IPR); + filter->UpdateLargestPossibleRegion(); + CheckImage(sourceFilter->GetOutput(), filter->GetOutput()); + EXPECT_EQ(filter->GetInputCoordinateOrientation(), OrientFilterType::OrientationEnum::LPS); + + filter->SetDesiredCoordinateOrientation(OrientFilterType::OrientationEnum::SAL); + filter->UpdateLargestPossibleRegion(); + CheckImage(sourceFilter->GetOutput(), filter->GetOutput()); + EXPECT_EQ(filter->GetInputCoordinateOrientation(), OrientFilterType::OrientationEnum::LPS); + + filter->SetDesiredCoordinateOrientation(OrientFilterType::OrientationEnum::PIR); + filter->UpdateLargestPossibleRegion(); + CheckImage(sourceFilter->GetOutput(), filter->GetOutput()); + EXPECT_EQ(filter->GetInputCoordinateOrientation(), OrientFilterType::OrientationEnum::LPS); + + filter->SetDesiredCoordinateOrientation(OrientFilterType::OrientationEnum::ASR); + filter->UpdateLargestPossibleRegion(); + CheckImage(sourceFilter->GetOutput(), filter->GetOutput()); + EXPECT_EQ(filter->GetInputCoordinateOrientation(), OrientFilterType::OrientationEnum::LPS); + + filter->SetDesiredCoordinateOrientation(OrientFilterType::OrientationEnum::ASL); + filter->UpdateLargestPossibleRegion(); + CheckImage(sourceFilter->GetOutput(), filter->GetOutput()); + EXPECT_EQ(filter->GetInputCoordinateOrientation(), OrientFilterType::OrientationEnum::LPS); +} + + +TEST(DICOMOrientImageFilter, DirectionFromString) +{ + using ImageType = itk::Image; + using OrientFilterType = itk::DICOMOrientImageFilter; + + auto filter = OrientFilterType::New(); + + filter->SetDesiredCoordinateOrientation("LPS"); + EXPECT_EQ(OrientFilterType::OrientationEnum::LPS, filter->GetDesiredCoordinateOrientation()); + + filter->SetDesiredCoordinateOrientation("PsL"); + EXPECT_EQ(OrientFilterType::OrientationEnum::PSL, filter->GetDesiredCoordinateOrientation()); + + filter->SetDesiredCoordinateOrientation("SLP"); + EXPECT_EQ(OrientFilterType::OrientationEnum::SLP, filter->GetDesiredCoordinateOrientation()); + + filter->SetDesiredCoordinateOrientation("rAI"); + EXPECT_EQ(OrientFilterType::OrientationEnum::RAI, filter->GetDesiredCoordinateOrientation()); + + filter->SetDesiredCoordinateOrientation("AIR"); + EXPECT_EQ(OrientFilterType::OrientationEnum::AIR, filter->GetDesiredCoordinateOrientation()); + + filter->SetDesiredCoordinateOrientation("IRA"); + EXPECT_EQ(OrientFilterType::OrientationEnum::IRA, filter->GetDesiredCoordinateOrientation()); +} + + +TEST(DICOMOrientImageFilter, InvalidOrientation) +{ + + using ImageType = itk::Image; + using OrientFilterType = itk::DICOMOrientImageFilter; + + ImageType::DirectionType dRAI; + dRAI.SetIdentity(); + + dRAI.Fill(0.0); + dRAI(0, 0) = -1.0; + dRAI(1, 1) = -1.0; + dRAI(2, 2) = -1.0; + + EXPECT_EQ(OrientFilterType::OrientationEnum::RAI, itk::DICOMOrientation::DirectionCosinesToOrientation(dRAI)); + + auto sourceFilter = itk::RandomImageSource::New(); + sourceFilter->SetMin(-1000); + sourceFilter->SetMax(1000); + sourceFilter->SetSize({ 5, 6, 7 }); + sourceFilter->SetDirection(dRAI); + sourceFilter->UpdateLargestPossibleRegion(); + + auto filter = OrientFilterType::New(); + filter->SetInput(sourceFilter->GetOutput()); + + filter->SetDesiredCoordinateOrientation(OrientFilterType::OrientationEnum::INVALID); + EXPECT_EQ(OrientFilterType::OrientationEnum::INVALID, filter->GetDesiredCoordinateOrientation()); + EXPECT_THROW(filter->Update(), itk::ExceptionObject); + EXPECT_EQ(OrientFilterType::OrientationEnum::INVALID, filter->GetInputCoordinateOrientation()); + + filter = OrientFilterType::New(); + filter->SetInput(sourceFilter->GetOutput()); + filter->SetDesiredCoordinateOrientation(""); + EXPECT_EQ(OrientFilterType::OrientationEnum::INVALID, filter->GetDesiredCoordinateOrientation()); + EXPECT_THROW(filter->Update(), itk::ExceptionObject); + EXPECT_EQ(OrientFilterType::OrientationEnum::INVALID, filter->GetInputCoordinateOrientation()); +} diff --git a/Modules/Filtering/ImageGrid/wrapping/itkDICOMOrientImageFilter.wrap b/Modules/Filtering/ImageGrid/wrapping/itkDICOMOrientImageFilter.wrap new file mode 100644 index 00000000000..415ade9890b --- /dev/null +++ b/Modules/Filtering/ImageGrid/wrapping/itkDICOMOrientImageFilter.wrap @@ -0,0 +1,6 @@ +itk_wrap_filter_dims(has_d_3 3) +if(has_d_3) + itk_wrap_class("itk::DICOMOrientImageFilter" POINTER) + itk_wrap_image_filter("${WRAP_ITK_ALL_TYPES}" 1 3) + itk_end_wrap_class() +endif() From b2a896842c9a5fb98c3bc2b169cde5e49b26b0ea Mon Sep 17 00:00:00 2001 From: Bradley Lowekamp Date: Wed, 31 Jul 2024 10:28:53 -0400 Subject: [PATCH 03/10] ENH: Conversion from SpatialOrientation to DICOMOrientation --- Modules/Core/Common/include/itkDICOMOrientation.h | 15 +++++++++++++++ Modules/Core/Common/include/itkImageBase.hxx | 1 - .../Common/include/itkSpatialOrientationAdapter.h | 4 ++-- Modules/Core/Common/src/itkDICOMOrientation.cxx | 10 ++++++++++ 4 files changed, 27 insertions(+), 3 deletions(-) diff --git a/Modules/Core/Common/include/itkDICOMOrientation.h b/Modules/Core/Common/include/itkDICOMOrientation.h index 5632b3bd81c..5bb81e0c336 100644 --- a/Modules/Core/Common/include/itkDICOMOrientation.h +++ b/Modules/Core/Common/include/itkDICOMOrientation.h @@ -20,6 +20,9 @@ #include "ITKCommonExport.h" #include "itkImageBase.h" +#ifndef ITK_FUTURE_LEGACY_REMOVE +#include "itkSpatialOrientation.h" +#endif #include #include @@ -43,6 +46,9 @@ class ITKCommon_EXPORT DICOMOrientation using DirectionType = typename ImageBase::DirectionType; constexpr static unsigned int ImageDimension = Dimension; +#ifndef ITK_FUTURE_LEGACY_REMOVE + using LegacyOrientationType = SpatialOrientationEnums::ValidCoordinateOrientations; +#endif enum class CoordinateEnum : uint8_t { @@ -162,6 +168,15 @@ class ITKCommon_EXPORT DICOMOrientation : m_Value(orientation) {} + +#ifndef ITK_FUTURE_LEGACY_REMOVE + /** \brief Conversion from Legacy SpatialOrientation + * + * @param legacyOrientation + */ + DICOMOrientation(LegacyOrientationType legacyOrientation); +#endif + explicit DICOMOrientation(const DirectionType & d) : m_Value(DirectionCosinesToOrientation(d)) {} diff --git a/Modules/Core/Common/include/itkImageBase.hxx b/Modules/Core/Common/include/itkImageBase.hxx index 784b4f6e85c..7b04b3a025d 100644 --- a/Modules/Core/Common/include/itkImageBase.hxx +++ b/Modules/Core/Common/include/itkImageBase.hxx @@ -31,7 +31,6 @@ #include #include "itkProcessObject.h" -#include "itkSpatialOrientation.h" #include #include "itkMath.h" diff --git a/Modules/Core/Common/include/itkSpatialOrientationAdapter.h b/Modules/Core/Common/include/itkSpatialOrientationAdapter.h index 2800ed37422..2f3dc57b8f7 100644 --- a/Modules/Core/Common/include/itkSpatialOrientationAdapter.h +++ b/Modules/Core/Common/include/itkSpatialOrientationAdapter.h @@ -94,11 +94,11 @@ class ITKCommon_EXPORT SpatialOrientationAdapter SpatialOrientationAdapter() = default; /** convert from direction cosines. */ - OrientationType + static OrientationType FromDirectionCosines(const DirectionType & Dir); /** convert to direction cosines. */ - DirectionType + static DirectionType ToDirectionCosines(const OrientationType & Or); }; } // namespace itk diff --git a/Modules/Core/Common/src/itkDICOMOrientation.cxx b/Modules/Core/Common/src/itkDICOMOrientation.cxx index 152318556b0..a6c00a30067 100644 --- a/Modules/Core/Common/src/itkDICOMOrientation.cxx +++ b/Modules/Core/Common/src/itkDICOMOrientation.cxx @@ -16,6 +16,9 @@ * *=========================================================================*/ #include "itkDICOMOrientation.h" +#ifndef ITK_FUTURE_LEGACY_REMOVE +#include "itkSpatialOrientationAdapter.h" +#endif namespace itk { @@ -47,6 +50,13 @@ DICOMOrientation::DICOMOrientation(std::string str) } +#ifndef ITK_FUTURE_LEGACY_REMOVE +DICOMOrientation::DICOMOrientation(LegacyOrientationType legacyOrientation) +: DICOMOrientation(SpatialOrientationAdapter::ToDirectionCosines(legacyOrientation)) +{} +#endif + + const std::string & DICOMOrientation::GetAsString() const { From cb079f7d8ae23fedad63cf3c38cd3ace1a46cc18 Mon Sep 17 00:00:00 2001 From: Bradley Lowekamp Date: Wed, 31 Jul 2024 11:53:22 -0400 Subject: [PATCH 04/10] ENH: Migrate MetaIO to DICOMOrientation --- Modules/IO/Meta/src/itkMetaImageIO.cxx | 253 ++----------------------- 1 file changed, 19 insertions(+), 234 deletions(-) diff --git a/Modules/IO/Meta/src/itkMetaImageIO.cxx b/Modules/IO/Meta/src/itkMetaImageIO.cxx index f6c2133cc0d..3506e49fd58 100644 --- a/Modules/IO/Meta/src/itkMetaImageIO.cxx +++ b/Modules/IO/Meta/src/itkMetaImageIO.cxx @@ -17,7 +17,7 @@ *=========================================================================*/ #include "itkMetaImageIO.h" -#include "itkSpatialOrientationAdapter.h" +#include "itkDICOMOrientation.h" #include "itkIOCommon.h" #include "itksys/SystemTools.hxx" #include "itkMath.h" @@ -808,11 +808,8 @@ MetaImageIO::Write(const void * buffer) if (numberOfDimensions == 3) { - using SpatialOrientations = SpatialOrientationEnums::ValidCoordinateOrientations; - SpatialOrientations coordOrient = SpatialOrientations::ITK_COORDINATE_ORIENTATION_INVALID; - std::vector dirx, diry, dirz; - SpatialOrientationAdapter::DirectionType dir; + DICOMOrientation::DirectionType dir; dirx = this->GetDirection(0); diry = this->GetDirection(1); dirz = this->GetDirection(2); @@ -822,238 +819,26 @@ MetaImageIO::Write(const void * buffer) dir[ii][1] = diry[ii]; dir[ii][2] = dirz[ii]; } - coordOrient = SpatialOrientationAdapter().FromDirectionCosines(dir); + DICOMOrientation coordOrient(dir); - switch (coordOrient) - { - default: - case SpatialOrientations::ITK_COORDINATE_ORIENTATION_RPI: - case SpatialOrientations::ITK_COORDINATE_ORIENTATION_RPS: - case SpatialOrientations::ITK_COORDINATE_ORIENTATION_RAI: - case SpatialOrientations::ITK_COORDINATE_ORIENTATION_RAS: - case SpatialOrientations::ITK_COORDINATE_ORIENTATION_RIA: - case SpatialOrientations::ITK_COORDINATE_ORIENTATION_RIP: - case SpatialOrientations::ITK_COORDINATE_ORIENTATION_RSA: - case SpatialOrientations::ITK_COORDINATE_ORIENTATION_RSP: - { - m_MetaImage.AnatomicalOrientation(0, MET_ORIENTATION_RL); - break; - } - case SpatialOrientations::ITK_COORDINATE_ORIENTATION_LPI: - case SpatialOrientations::ITK_COORDINATE_ORIENTATION_LPS: - case SpatialOrientations::ITK_COORDINATE_ORIENTATION_LAI: - case SpatialOrientations::ITK_COORDINATE_ORIENTATION_LAS: - case SpatialOrientations::ITK_COORDINATE_ORIENTATION_LIA: - case SpatialOrientations::ITK_COORDINATE_ORIENTATION_LIP: - case SpatialOrientations::ITK_COORDINATE_ORIENTATION_LSA: - case SpatialOrientations::ITK_COORDINATE_ORIENTATION_LSP: - { - m_MetaImage.AnatomicalOrientation(0, MET_ORIENTATION_LR); - break; - } - case SpatialOrientations::ITK_COORDINATE_ORIENTATION_ALI: - case SpatialOrientations::ITK_COORDINATE_ORIENTATION_ALS: - case SpatialOrientations::ITK_COORDINATE_ORIENTATION_ARI: - case SpatialOrientations::ITK_COORDINATE_ORIENTATION_ARS: - case SpatialOrientations::ITK_COORDINATE_ORIENTATION_AIL: - case SpatialOrientations::ITK_COORDINATE_ORIENTATION_AIR: - case SpatialOrientations::ITK_COORDINATE_ORIENTATION_ASL: - case SpatialOrientations::ITK_COORDINATE_ORIENTATION_ASR: - { - m_MetaImage.AnatomicalOrientation(0, MET_ORIENTATION_AP); - break; - } - case SpatialOrientations::ITK_COORDINATE_ORIENTATION_PLI: - case SpatialOrientations::ITK_COORDINATE_ORIENTATION_PLS: - case SpatialOrientations::ITK_COORDINATE_ORIENTATION_PRI: - case SpatialOrientations::ITK_COORDINATE_ORIENTATION_PRS: - case SpatialOrientations::ITK_COORDINATE_ORIENTATION_PIL: - case SpatialOrientations::ITK_COORDINATE_ORIENTATION_PIR: - case SpatialOrientations::ITK_COORDINATE_ORIENTATION_PSL: - case SpatialOrientations::ITK_COORDINATE_ORIENTATION_PSR: - { - m_MetaImage.AnatomicalOrientation(0, MET_ORIENTATION_PA); - break; - } - case SpatialOrientations::ITK_COORDINATE_ORIENTATION_IPL: - case SpatialOrientations::ITK_COORDINATE_ORIENTATION_IPR: - case SpatialOrientations::ITK_COORDINATE_ORIENTATION_IAL: - case SpatialOrientations::ITK_COORDINATE_ORIENTATION_IAR: - case SpatialOrientations::ITK_COORDINATE_ORIENTATION_ILA: - case SpatialOrientations::ITK_COORDINATE_ORIENTATION_ILP: - case SpatialOrientations::ITK_COORDINATE_ORIENTATION_IRA: - case SpatialOrientations::ITK_COORDINATE_ORIENTATION_IRP: - { - m_MetaImage.AnatomicalOrientation(0, MET_ORIENTATION_IS); - break; - } - case SpatialOrientations::ITK_COORDINATE_ORIENTATION_SPL: - case SpatialOrientations::ITK_COORDINATE_ORIENTATION_SPR: - case SpatialOrientations::ITK_COORDINATE_ORIENTATION_SAL: - case SpatialOrientations::ITK_COORDINATE_ORIENTATION_SAR: - case SpatialOrientations::ITK_COORDINATE_ORIENTATION_SLA: - case SpatialOrientations::ITK_COORDINATE_ORIENTATION_SLP: - case SpatialOrientations::ITK_COORDINATE_ORIENTATION_SRA: - case SpatialOrientations::ITK_COORDINATE_ORIENTATION_SRP: - { - m_MetaImage.AnatomicalOrientation(0, MET_ORIENTATION_SI); - break; - } - } - switch (coordOrient) - { - case SpatialOrientations::ITK_COORDINATE_ORIENTATION_PRI: - case SpatialOrientations::ITK_COORDINATE_ORIENTATION_PRS: - case SpatialOrientations::ITK_COORDINATE_ORIENTATION_ARI: - case SpatialOrientations::ITK_COORDINATE_ORIENTATION_ARS: - case SpatialOrientations::ITK_COORDINATE_ORIENTATION_IRA: - case SpatialOrientations::ITK_COORDINATE_ORIENTATION_IRP: - case SpatialOrientations::ITK_COORDINATE_ORIENTATION_SRA: - case SpatialOrientations::ITK_COORDINATE_ORIENTATION_SRP: - { - m_MetaImage.AnatomicalOrientation(1, MET_ORIENTATION_RL); - break; - } - case SpatialOrientations::ITK_COORDINATE_ORIENTATION_PLI: - case SpatialOrientations::ITK_COORDINATE_ORIENTATION_PLS: - case SpatialOrientations::ITK_COORDINATE_ORIENTATION_ALI: - case SpatialOrientations::ITK_COORDINATE_ORIENTATION_ALS: - case SpatialOrientations::ITK_COORDINATE_ORIENTATION_ILA: - case SpatialOrientations::ITK_COORDINATE_ORIENTATION_ILP: - case SpatialOrientations::ITK_COORDINATE_ORIENTATION_SLA: - case SpatialOrientations::ITK_COORDINATE_ORIENTATION_SLP: - { - m_MetaImage.AnatomicalOrientation(1, MET_ORIENTATION_LR); - break; - } - case SpatialOrientations::ITK_COORDINATE_ORIENTATION_LAI: - case SpatialOrientations::ITK_COORDINATE_ORIENTATION_LAS: - case SpatialOrientations::ITK_COORDINATE_ORIENTATION_RAI: - case SpatialOrientations::ITK_COORDINATE_ORIENTATION_RAS: - case SpatialOrientations::ITK_COORDINATE_ORIENTATION_IAL: - case SpatialOrientations::ITK_COORDINATE_ORIENTATION_IAR: - case SpatialOrientations::ITK_COORDINATE_ORIENTATION_SAL: - case SpatialOrientations::ITK_COORDINATE_ORIENTATION_SAR: - { - m_MetaImage.AnatomicalOrientation(1, MET_ORIENTATION_AP); - break; - } - default: - case SpatialOrientations::ITK_COORDINATE_ORIENTATION_LPI: - case SpatialOrientations::ITK_COORDINATE_ORIENTATION_LPS: - case SpatialOrientations::ITK_COORDINATE_ORIENTATION_RPI: - case SpatialOrientations::ITK_COORDINATE_ORIENTATION_RPS: - case SpatialOrientations::ITK_COORDINATE_ORIENTATION_IPL: - case SpatialOrientations::ITK_COORDINATE_ORIENTATION_IPR: - case SpatialOrientations::ITK_COORDINATE_ORIENTATION_SPL: - case SpatialOrientations::ITK_COORDINATE_ORIENTATION_SPR: + // Mapping from DICOM CoordinateEnum defined as the increasing direction to + // the MetaIO enum which has from/to orientation defined. + const std::map axisToMetOrientation { - m_MetaImage.AnatomicalOrientation(1, MET_ORIENTATION_PA); - break; - } - case SpatialOrientations::ITK_COORDINATE_ORIENTATION_PIL: - case SpatialOrientations::ITK_COORDINATE_ORIENTATION_PIR: - case SpatialOrientations::ITK_COORDINATE_ORIENTATION_AIL: - case SpatialOrientations::ITK_COORDINATE_ORIENTATION_AIR: - case SpatialOrientations::ITK_COORDINATE_ORIENTATION_LIA: - case SpatialOrientations::ITK_COORDINATE_ORIENTATION_LIP: - case SpatialOrientations::ITK_COORDINATE_ORIENTATION_RIA: - case SpatialOrientations::ITK_COORDINATE_ORIENTATION_RIP: - { - m_MetaImage.AnatomicalOrientation(1, MET_ORIENTATION_IS); - break; - } - case SpatialOrientations::ITK_COORDINATE_ORIENTATION_PSL: - case SpatialOrientations::ITK_COORDINATE_ORIENTATION_PSR: - case SpatialOrientations::ITK_COORDINATE_ORIENTATION_ASL: - case SpatialOrientations::ITK_COORDINATE_ORIENTATION_ASR: - case SpatialOrientations::ITK_COORDINATE_ORIENTATION_LSA: - case SpatialOrientations::ITK_COORDINATE_ORIENTATION_LSP: - case SpatialOrientations::ITK_COORDINATE_ORIENTATION_RSA: - case SpatialOrientations::ITK_COORDINATE_ORIENTATION_RSP: - { - m_MetaImage.AnatomicalOrientation(1, MET_ORIENTATION_SI); - break; - } - } - switch (coordOrient) - { - case SpatialOrientations::ITK_COORDINATE_ORIENTATION_PIR: - case SpatialOrientations::ITK_COORDINATE_ORIENTATION_PSR: - case SpatialOrientations::ITK_COORDINATE_ORIENTATION_AIR: - case SpatialOrientations::ITK_COORDINATE_ORIENTATION_ASR: - case SpatialOrientations::ITK_COORDINATE_ORIENTATION_IAR: - case SpatialOrientations::ITK_COORDINATE_ORIENTATION_IPR: - case SpatialOrientations::ITK_COORDINATE_ORIENTATION_SAR: - case SpatialOrientations::ITK_COORDINATE_ORIENTATION_SPR: - { - m_MetaImage.AnatomicalOrientation(2, MET_ORIENTATION_RL); - break; - } - case SpatialOrientations::ITK_COORDINATE_ORIENTATION_PIL: - case SpatialOrientations::ITK_COORDINATE_ORIENTATION_PSL: - case SpatialOrientations::ITK_COORDINATE_ORIENTATION_AIL: - case SpatialOrientations::ITK_COORDINATE_ORIENTATION_ASL: - case SpatialOrientations::ITK_COORDINATE_ORIENTATION_IAL: - case SpatialOrientations::ITK_COORDINATE_ORIENTATION_IPL: - case SpatialOrientations::ITK_COORDINATE_ORIENTATION_SAL: - case SpatialOrientations::ITK_COORDINATE_ORIENTATION_SPL: - { - m_MetaImage.AnatomicalOrientation(2, MET_ORIENTATION_LR); - break; - } - case SpatialOrientations::ITK_COORDINATE_ORIENTATION_LIA: - case SpatialOrientations::ITK_COORDINATE_ORIENTATION_LSA: - case SpatialOrientations::ITK_COORDINATE_ORIENTATION_RIA: - case SpatialOrientations::ITK_COORDINATE_ORIENTATION_RSA: - case SpatialOrientations::ITK_COORDINATE_ORIENTATION_ILA: - case SpatialOrientations::ITK_COORDINATE_ORIENTATION_IRA: - case SpatialOrientations::ITK_COORDINATE_ORIENTATION_SLA: - case SpatialOrientations::ITK_COORDINATE_ORIENTATION_SRA: - { - m_MetaImage.AnatomicalOrientation(2, MET_ORIENTATION_AP); - break; - } - case SpatialOrientations::ITK_COORDINATE_ORIENTATION_LIP: - case SpatialOrientations::ITK_COORDINATE_ORIENTATION_LSP: - case SpatialOrientations::ITK_COORDINATE_ORIENTATION_RIP: - case SpatialOrientations::ITK_COORDINATE_ORIENTATION_RSP: - case SpatialOrientations::ITK_COORDINATE_ORIENTATION_ILP: - case SpatialOrientations::ITK_COORDINATE_ORIENTATION_IRP: - case SpatialOrientations::ITK_COORDINATE_ORIENTATION_SLP: - case SpatialOrientations::ITK_COORDINATE_ORIENTATION_SRP: - { - m_MetaImage.AnatomicalOrientation(2, MET_ORIENTATION_PA); - break; - } - default: - case SpatialOrientations::ITK_COORDINATE_ORIENTATION_PLI: - case SpatialOrientations::ITK_COORDINATE_ORIENTATION_PRI: - case SpatialOrientations::ITK_COORDINATE_ORIENTATION_ALI: - case SpatialOrientations::ITK_COORDINATE_ORIENTATION_ARI: - case SpatialOrientations::ITK_COORDINATE_ORIENTATION_LAI: - case SpatialOrientations::ITK_COORDINATE_ORIENTATION_LPI: - case SpatialOrientations::ITK_COORDINATE_ORIENTATION_RAI: - case SpatialOrientations::ITK_COORDINATE_ORIENTATION_RPI: - { - m_MetaImage.AnatomicalOrientation(2, MET_ORIENTATION_IS); - break; - } - case SpatialOrientations::ITK_COORDINATE_ORIENTATION_PLS: - case SpatialOrientations::ITK_COORDINATE_ORIENTATION_PRS: - case SpatialOrientations::ITK_COORDINATE_ORIENTATION_ALS: - case SpatialOrientations::ITK_COORDINATE_ORIENTATION_ARS: - case SpatialOrientations::ITK_COORDINATE_ORIENTATION_LAS: - case SpatialOrientations::ITK_COORDINATE_ORIENTATION_LPS: - case SpatialOrientations::ITK_COORDINATE_ORIENTATION_RAS: - case SpatialOrientations::ITK_COORDINATE_ORIENTATION_RPS: - { - m_MetaImage.AnatomicalOrientation(2, MET_ORIENTATION_SI); - break; - } - } + { DICOMOrientation::CoordinateEnum::Left, MET_ORIENTATION_RL }, + { DICOMOrientation::CoordinateEnum::Right, MET_ORIENTATION_LR }, + { DICOMOrientation::CoordinateEnum::Posterior, MET_ORIENTATION_AP }, + { DICOMOrientation::CoordinateEnum::Anterior, MET_ORIENTATION_PA }, + { DICOMOrientation::CoordinateEnum::Superior, MET_ORIENTATION_IS }, + { DICOMOrientation::CoordinateEnum::Inferior, MET_ORIENTATION_SI } + }; + + m_MetaImage.AnatomicalOrientation(0, axisToMetOrientation.at(coordOrient.GetPrimaryTerm())); + m_MetaImage.AnatomicalOrientation(1, axisToMetOrientation.at(coordOrient.GetSecondaryTerm())); + m_MetaImage.AnatomicalOrientation(2, axisToMetOrientation.at(coordOrient.GetTertiaryTerm())); + } - // Propagage direction cosine information. + // Propagate direction cosine information. auto * transformMatrix = static_cast(malloc(numberOfDimensions * numberOfDimensions * sizeof(double))); if (transformMatrix) { From 2b83ab541d5214f45622e40416610775a488882a Mon Sep 17 00:00:00 2001 From: Bradley Lowekamp Date: Thu, 1 Aug 2024 11:04:37 -0400 Subject: [PATCH 05/10] ENH: Improve compatibility with legacy coordinate enums Ensure matching byte codes between DICOM and legacy coordinate enums which have the same direction cosine interpretation. Add unambiguous CoordinateEnum duplication of the form LeftToRight. Add method to DICOMOrientationImageFilter which accepts DICOMOrientation object, which can be implicitly converted from legacy coordinate enums. --- .../Core/Common/include/itkDICOMOrientation.h | 14 +++++--- .../Core/Common/src/itkDICOMOrientation.cxx | 22 ++++++------ .../Common/test/itkDICOMOrientionGTest.cxx | 22 ++++++++++++ .../include/itkDICOMOrientImageFilter.h | 6 ++++ .../test/itkDICOMOrientImageFilterGTest.cxx | 36 +++++++++++++++++++ Modules/IO/Meta/src/itkMetaImageIO.cxx | 12 +++---- 6 files changed, 91 insertions(+), 21 deletions(-) diff --git a/Modules/Core/Common/include/itkDICOMOrientation.h b/Modules/Core/Common/include/itkDICOMOrientation.h index 5bb81e0c336..ee586e1d7df 100644 --- a/Modules/Core/Common/include/itkDICOMOrientation.h +++ b/Modules/Core/Common/include/itkDICOMOrientation.h @@ -53,12 +53,18 @@ class ITKCommon_EXPORT DICOMOrientation enum class CoordinateEnum : uint8_t { UNKNOWN = 0, - Right = 2, // 0b0010 - Left = 3, + Left = 2, ///< 0b0010 + RightToLeft = 2, + Right = 3, + LeftToRight = 3, Anterior = 4, ///< front - 0b0100 + PosteriorToAnterior = 4, Posterior = 5, ///< back - Inferior = 8, ///< bottom - 0b1000 - Superior = 9 ///< above + AnteriorToPosterior = 5, + Superior = 8, ///< above - 0b1000 + InferiorToSuperior = 8, + Inferior = 9, ///< bottom + SuperiorToInferior = 9, }; private: diff --git a/Modules/Core/Common/src/itkDICOMOrientation.cxx b/Modules/Core/Common/src/itkDICOMOrientation.cxx index a6c00a30067..62c1decfeb2 100644 --- a/Modules/Core/Common/src/itkDICOMOrientation.cxx +++ b/Modules/Core/Common/src/itkDICOMOrientation.cxx @@ -211,15 +211,15 @@ DICOMOrientation::DirectionCosinesToOrientation(const DirectionType & dir) case 0: { // When the dominant axis sign is positive, assign the coordinate for the direction we are increasing towards. // ITK is in LPS, so that is the positive direction - terms[max_r] = (max_sgn == 1) ? CoordinateEnum::Left : CoordinateEnum::Right; + terms[max_r] = (max_sgn == 1) ? CoordinateEnum::RightToLeft : CoordinateEnum::LeftToRight; break; } case 1: { - terms[max_r] = (max_sgn == 1) ? CoordinateEnum::Posterior : CoordinateEnum::Anterior; + terms[max_r] = (max_sgn == 1) ? CoordinateEnum::AnteriorToPosterior : CoordinateEnum::PosteriorToAnterior; break; } case 2: { - terms[max_r] = (max_sgn == 1) ? CoordinateEnum::Superior : CoordinateEnum::Inferior; + terms[max_r] = (max_sgn == 1) ? CoordinateEnum::InferiorToSuperior : CoordinateEnum::SuperiorToInferior; break; } default: @@ -246,17 +246,17 @@ DICOMOrientation::OrientationToDirectionCosines(OrientationEnum orientationEnum) switch (terms[i]) { - case CoordinateEnum::Left: - case CoordinateEnum::Right: - direction[0][i] = 1 * sign; + case CoordinateEnum::LeftToRight: + case CoordinateEnum::RightToLeft: + direction[0][i] = -1 * sign; break; - case CoordinateEnum::Anterior: - case CoordinateEnum::Posterior: + case CoordinateEnum::AnteriorToPosterior: + case CoordinateEnum::PosteriorToAnterior: direction[1][i] = 1 * sign; break; - case CoordinateEnum::Inferior: - case CoordinateEnum::Superior: - direction[2][i] = 1 * sign; + case CoordinateEnum::InferiorToSuperior: + case CoordinateEnum::SuperiorToInferior: + direction[2][i] = -1 * sign; break; case CoordinateEnum::UNKNOWN: break; diff --git a/Modules/Core/Common/test/itkDICOMOrientionGTest.cxx b/Modules/Core/Common/test/itkDICOMOrientionGTest.cxx index ddbd279b488..9fcfe2f2102 100644 --- a/Modules/Core/Common/test/itkDICOMOrientionGTest.cxx +++ b/Modules/Core/Common/test/itkDICOMOrientionGTest.cxx @@ -204,3 +204,25 @@ TEST(DICOMOrientation, OrientationToDirectionCosines) d(0, 2) = 1; EXPECT_EQ(d, DICOMOrientation::OrientationToDirectionCosines(OE::SPL)); } + +#ifndef ITK_FUTURE_LEGACY_REMOVE +#include "itkSpatialOrientation.h" +TEST(DICOMOrientation, LegacyInteroperability) +{ + + using OE = itk::DICOMOrientation::OrientationEnum; + using SOE = itk::SpatialOrientationEnums::ValidCoordinateOrientations; + + // byte for byte compatibility, may assist with migration of bindings when types are not strictly enforced. + static_assert( int(SOE::ITK_COORDINATE_ORIENTATION_RAI) == int(OE::LPS) ); + static_assert( int(SOE::ITK_COORDINATE_ORIENTATION_LPS) == int(OE::RAI) ); + static_assert( int(SOE::ITK_COORDINATE_ORIENTATION_RSA) == int(OE::LIP) ); + static_assert( int(SOE::ITK_COORDINATE_ORIENTATION_ASL) == int(OE::PIR) ); + + itk::DICOMOrientation itk_rai(SOE::ITK_COORDINATE_ORIENTATION_RAI); + EXPECT_EQ( itk_rai, OE::LPS); + EXPECT_EQ( itk_rai.GetAsOrientation(), OE::LPS); + EXPECT_EQ( itk_rai.GetAsString(), "LPS"); + +} +#endif diff --git a/Modules/Filtering/ImageGrid/include/itkDICOMOrientImageFilter.h b/Modules/Filtering/ImageGrid/include/itkDICOMOrientImageFilter.h index beb5f7cbe9e..7c1f678256a 100644 --- a/Modules/Filtering/ImageGrid/include/itkDICOMOrientImageFilter.h +++ b/Modules/Filtering/ImageGrid/include/itkDICOMOrientImageFilter.h @@ -117,6 +117,12 @@ class ITK_TEMPLATE_EXPORT DICOMOrientImageFilter : public ImageToImageFilterUpdate(), itk::ExceptionObject); EXPECT_EQ(OrientFilterType::OrientationEnum::INVALID, filter->GetInputCoordinateOrientation()); } + + +#ifndef ITK_FUTURE_LEGACY_REMOVE +#include "itkSpatialOrientation.h" + +TEST(DICOMOrientImageFilter, LegacyInteroperability) +{ + using ImageType = itk::Image; + + + auto sourceFilter = itk::RandomImageSource::New(); + sourceFilter->SetMin(-1000); + sourceFilter->SetMax(1000); + sourceFilter->SetSize({ 5, 6, 7 }); + sourceFilter->UpdateLargestPossibleRegion(); + + using OrientFilterType = itk::DICOMOrientImageFilter; + using PermuiteArrayType = OrientFilterType::PermuteOrderArrayType; + using FlipArrayType = OrientFilterType::FlipAxesArrayType; + auto filter = OrientFilterType::New(); + filter->SetInput(sourceFilter->GetOutput()); + + filter->UpdateLargestPossibleRegion(); + CheckImage(sourceFilter->GetOutput(), filter->GetOutput()); + + // Legacy compatibility for setting the desired orientation for SpatialOrientationEnums + // This is done through implicit conversion to the DICOMOrientation class + filter->SetDesiredCoordinateOrientation(itk::SpatialOrientationEnums::ValidCoordinateOrientations::ITK_COORDINATE_ORIENTATION_RAI); + filter->UpdateLargestPossibleRegion(); + CheckImage(sourceFilter->GetOutput(), filter->GetOutput()); + EXPECT_EQ(filter->GetDesiredCoordinateOrientation(), OrientFilterType::OrientationEnum::LPS); + EXPECT_EQ(filter->GetInputCoordinateOrientation(), OrientFilterType::OrientationEnum::LPS); + EXPECT_EQ(filter->GetPermuteOrder(), PermuiteArrayType({ 0, 1, 2 })); + EXPECT_EQ(filter->GetFlipAxes(), FlipArrayType({ 0, 0, 0 })); +} +#endif diff --git a/Modules/IO/Meta/src/itkMetaImageIO.cxx b/Modules/IO/Meta/src/itkMetaImageIO.cxx index 3506e49fd58..51e01bf931b 100644 --- a/Modules/IO/Meta/src/itkMetaImageIO.cxx +++ b/Modules/IO/Meta/src/itkMetaImageIO.cxx @@ -825,12 +825,12 @@ MetaImageIO::Write(const void * buffer) // the MetaIO enum which has from/to orientation defined. const std::map axisToMetOrientation { - { DICOMOrientation::CoordinateEnum::Left, MET_ORIENTATION_RL }, - { DICOMOrientation::CoordinateEnum::Right, MET_ORIENTATION_LR }, - { DICOMOrientation::CoordinateEnum::Posterior, MET_ORIENTATION_AP }, - { DICOMOrientation::CoordinateEnum::Anterior, MET_ORIENTATION_PA }, - { DICOMOrientation::CoordinateEnum::Superior, MET_ORIENTATION_IS }, - { DICOMOrientation::CoordinateEnum::Inferior, MET_ORIENTATION_SI } + { DICOMOrientation::CoordinateEnum::RightToLeft, MET_ORIENTATION_RL }, + { DICOMOrientation::CoordinateEnum::LeftToRight, MET_ORIENTATION_LR }, + { DICOMOrientation::CoordinateEnum::AnteriorToPosterior, MET_ORIENTATION_AP }, + { DICOMOrientation::CoordinateEnum::PosteriorToAnterior, MET_ORIENTATION_PA }, + { DICOMOrientation::CoordinateEnum::InferiorToSuperior, MET_ORIENTATION_IS }, + { DICOMOrientation::CoordinateEnum::SuperiorToInferior, MET_ORIENTATION_SI } }; m_MetaImage.AnatomicalOrientation(0, axisToMetOrientation.at(coordOrient.GetPrimaryTerm())); From f56ccbb5483d50fcba5b6382665ba915d0edf791 Mon Sep 17 00:00:00 2001 From: Bradley Lowekamp Date: Thu, 1 Aug 2024 16:52:02 -0400 Subject: [PATCH 06/10] ENH: Prefer usage of FROMToTO CoordinateEnum --- .../Core/Common/include/itkDICOMOrientation.h | 106 +++++++++--------- 1 file changed, 53 insertions(+), 53 deletions(-) diff --git a/Modules/Core/Common/include/itkDICOMOrientation.h b/Modules/Core/Common/include/itkDICOMOrientation.h index ee586e1d7df..924113babe9 100644 --- a/Modules/Core/Common/include/itkDICOMOrientation.h +++ b/Modules/Core/Common/include/itkDICOMOrientation.h @@ -106,59 +106,59 @@ class ITKCommon_EXPORT DICOMOrientation { INVALID = 0, - RIP = ITK_ORIENTATIONENUM(CoordinateEnum::Right, CoordinateEnum::Inferior, CoordinateEnum::Posterior), - LIP = ITK_ORIENTATIONENUM(CoordinateEnum::Left, CoordinateEnum::Inferior, CoordinateEnum::Posterior), - RSP = ITK_ORIENTATIONENUM(CoordinateEnum::Right, CoordinateEnum::Superior, CoordinateEnum::Posterior), - LSP = ITK_ORIENTATIONENUM(CoordinateEnum::Left, CoordinateEnum::Superior, CoordinateEnum::Posterior), - RIA = ITK_ORIENTATIONENUM(CoordinateEnum::Right, CoordinateEnum::Inferior, CoordinateEnum::Anterior), - LIA = ITK_ORIENTATIONENUM(CoordinateEnum::Left, CoordinateEnum::Inferior, CoordinateEnum::Anterior), - RSA = ITK_ORIENTATIONENUM(CoordinateEnum::Right, CoordinateEnum::Superior, CoordinateEnum::Anterior), - LSA = ITK_ORIENTATIONENUM(CoordinateEnum::Left, CoordinateEnum::Superior, CoordinateEnum::Anterior), - - IRP = ITK_ORIENTATIONENUM(CoordinateEnum::Inferior, CoordinateEnum::Right, CoordinateEnum::Posterior), - ILP = ITK_ORIENTATIONENUM(CoordinateEnum::Inferior, CoordinateEnum::Left, CoordinateEnum::Posterior), - SRP = ITK_ORIENTATIONENUM(CoordinateEnum::Superior, CoordinateEnum::Right, CoordinateEnum::Posterior), - SLP = ITK_ORIENTATIONENUM(CoordinateEnum::Superior, CoordinateEnum::Left, CoordinateEnum::Posterior), - IRA = ITK_ORIENTATIONENUM(CoordinateEnum::Inferior, CoordinateEnum::Right, CoordinateEnum::Anterior), - ILA = ITK_ORIENTATIONENUM(CoordinateEnum::Inferior, CoordinateEnum::Left, CoordinateEnum::Anterior), - SRA = ITK_ORIENTATIONENUM(CoordinateEnum::Superior, CoordinateEnum::Right, CoordinateEnum::Anterior), - SLA = ITK_ORIENTATIONENUM(CoordinateEnum::Superior, CoordinateEnum::Left, CoordinateEnum::Anterior), - - RPI = ITK_ORIENTATIONENUM(CoordinateEnum::Right, CoordinateEnum::Posterior, CoordinateEnum::Inferior), - LPI = ITK_ORIENTATIONENUM(CoordinateEnum::Left, CoordinateEnum::Posterior, CoordinateEnum::Inferior), - RAI = ITK_ORIENTATIONENUM(CoordinateEnum::Right, CoordinateEnum::Anterior, CoordinateEnum::Inferior), - LAI = ITK_ORIENTATIONENUM(CoordinateEnum::Left, CoordinateEnum::Anterior, CoordinateEnum::Inferior), - RPS = ITK_ORIENTATIONENUM(CoordinateEnum::Right, CoordinateEnum::Posterior, CoordinateEnum::Superior), - LPS = ITK_ORIENTATIONENUM(CoordinateEnum::Left, CoordinateEnum::Posterior, CoordinateEnum::Superior), - RAS = ITK_ORIENTATIONENUM(CoordinateEnum::Right, CoordinateEnum::Anterior, CoordinateEnum::Superior), - LAS = ITK_ORIENTATIONENUM(CoordinateEnum::Left, CoordinateEnum::Anterior, CoordinateEnum::Superior), - - PRI = ITK_ORIENTATIONENUM(CoordinateEnum::Posterior, CoordinateEnum::Right, CoordinateEnum::Inferior), - PLI = ITK_ORIENTATIONENUM(CoordinateEnum::Posterior, CoordinateEnum::Left, CoordinateEnum::Inferior), - ARI = ITK_ORIENTATIONENUM(CoordinateEnum::Anterior, CoordinateEnum::Right, CoordinateEnum::Inferior), - ALI = ITK_ORIENTATIONENUM(CoordinateEnum::Anterior, CoordinateEnum::Left, CoordinateEnum::Inferior), - PRS = ITK_ORIENTATIONENUM(CoordinateEnum::Posterior, CoordinateEnum::Right, CoordinateEnum::Superior), - PLS = ITK_ORIENTATIONENUM(CoordinateEnum::Posterior, CoordinateEnum::Left, CoordinateEnum::Superior), - ARS = ITK_ORIENTATIONENUM(CoordinateEnum::Anterior, CoordinateEnum::Right, CoordinateEnum::Superior), - ALS = ITK_ORIENTATIONENUM(CoordinateEnum::Anterior, CoordinateEnum::Left, CoordinateEnum::Superior), - - IPR = ITK_ORIENTATIONENUM(CoordinateEnum::Inferior, CoordinateEnum::Posterior, CoordinateEnum::Right), - SPR = ITK_ORIENTATIONENUM(CoordinateEnum::Superior, CoordinateEnum::Posterior, CoordinateEnum::Right), - IAR = ITK_ORIENTATIONENUM(CoordinateEnum::Inferior, CoordinateEnum::Anterior, CoordinateEnum::Right), - SAR = ITK_ORIENTATIONENUM(CoordinateEnum::Superior, CoordinateEnum::Anterior, CoordinateEnum::Right), - IPL = ITK_ORIENTATIONENUM(CoordinateEnum::Inferior, CoordinateEnum::Posterior, CoordinateEnum::Left), - SPL = ITK_ORIENTATIONENUM(CoordinateEnum::Superior, CoordinateEnum::Posterior, CoordinateEnum::Left), - IAL = ITK_ORIENTATIONENUM(CoordinateEnum::Inferior, CoordinateEnum::Anterior, CoordinateEnum::Left), - SAL = ITK_ORIENTATIONENUM(CoordinateEnum::Superior, CoordinateEnum::Anterior, CoordinateEnum::Left), - - PIR = ITK_ORIENTATIONENUM(CoordinateEnum::Posterior, CoordinateEnum::Inferior, CoordinateEnum::Right), - PSR = ITK_ORIENTATIONENUM(CoordinateEnum::Posterior, CoordinateEnum::Superior, CoordinateEnum::Right), - AIR = ITK_ORIENTATIONENUM(CoordinateEnum::Anterior, CoordinateEnum::Inferior, CoordinateEnum::Right), - ASR = ITK_ORIENTATIONENUM(CoordinateEnum::Anterior, CoordinateEnum::Superior, CoordinateEnum::Right), - PIL = ITK_ORIENTATIONENUM(CoordinateEnum::Posterior, CoordinateEnum::Inferior, CoordinateEnum::Left), - PSL = ITK_ORIENTATIONENUM(CoordinateEnum::Posterior, CoordinateEnum::Superior, CoordinateEnum::Left), - AIL = ITK_ORIENTATIONENUM(CoordinateEnum::Anterior, CoordinateEnum::Inferior, CoordinateEnum::Left), - ASL = ITK_ORIENTATIONENUM(CoordinateEnum::Anterior, CoordinateEnum::Superior, CoordinateEnum::Left) + RIP = ITK_ORIENTATIONENUM(CoordinateEnum::LeftToRight, CoordinateEnum::SuperiorToInferior, CoordinateEnum::AnteriorToPosterior), + LIP = ITK_ORIENTATIONENUM(CoordinateEnum::RightToLeft, CoordinateEnum::SuperiorToInferior, CoordinateEnum::AnteriorToPosterior), + RSP = ITK_ORIENTATIONENUM(CoordinateEnum::LeftToRight, CoordinateEnum::InferiorToSuperior, CoordinateEnum::AnteriorToPosterior), + LSP = ITK_ORIENTATIONENUM(CoordinateEnum::RightToLeft, CoordinateEnum::InferiorToSuperior, CoordinateEnum::AnteriorToPosterior), + RIA = ITK_ORIENTATIONENUM(CoordinateEnum::LeftToRight, CoordinateEnum::SuperiorToInferior, CoordinateEnum::PosteriorToAnterior), + LIA = ITK_ORIENTATIONENUM(CoordinateEnum::RightToLeft, CoordinateEnum::SuperiorToInferior, CoordinateEnum::PosteriorToAnterior), + RSA = ITK_ORIENTATIONENUM(CoordinateEnum::LeftToRight, CoordinateEnum::InferiorToSuperior, CoordinateEnum::PosteriorToAnterior), + LSA = ITK_ORIENTATIONENUM(CoordinateEnum::RightToLeft, CoordinateEnum::InferiorToSuperior, CoordinateEnum::PosteriorToAnterior), + + IRP = ITK_ORIENTATIONENUM(CoordinateEnum::SuperiorToInferior, CoordinateEnum::LeftToRight, CoordinateEnum::AnteriorToPosterior), + ILP = ITK_ORIENTATIONENUM(CoordinateEnum::SuperiorToInferior, CoordinateEnum::RightToLeft, CoordinateEnum::AnteriorToPosterior), + SRP = ITK_ORIENTATIONENUM(CoordinateEnum::InferiorToSuperior, CoordinateEnum::LeftToRight, CoordinateEnum::AnteriorToPosterior), + SLP = ITK_ORIENTATIONENUM(CoordinateEnum::InferiorToSuperior, CoordinateEnum::RightToLeft, CoordinateEnum::AnteriorToPosterior), + IRA = ITK_ORIENTATIONENUM(CoordinateEnum::SuperiorToInferior, CoordinateEnum::LeftToRight, CoordinateEnum::PosteriorToAnterior), + ILA = ITK_ORIENTATIONENUM(CoordinateEnum::SuperiorToInferior, CoordinateEnum::RightToLeft, CoordinateEnum::PosteriorToAnterior), + SRA = ITK_ORIENTATIONENUM(CoordinateEnum::InferiorToSuperior, CoordinateEnum::LeftToRight, CoordinateEnum::PosteriorToAnterior), + SLA = ITK_ORIENTATIONENUM(CoordinateEnum::InferiorToSuperior, CoordinateEnum::RightToLeft, CoordinateEnum::PosteriorToAnterior), + + RPI = ITK_ORIENTATIONENUM(CoordinateEnum::LeftToRight, CoordinateEnum::AnteriorToPosterior, CoordinateEnum::SuperiorToInferior), + LPI = ITK_ORIENTATIONENUM(CoordinateEnum::RightToLeft, CoordinateEnum::AnteriorToPosterior, CoordinateEnum::SuperiorToInferior), + RAI = ITK_ORIENTATIONENUM(CoordinateEnum::LeftToRight, CoordinateEnum::PosteriorToAnterior, CoordinateEnum::SuperiorToInferior), + LAI = ITK_ORIENTATIONENUM(CoordinateEnum::RightToLeft, CoordinateEnum::PosteriorToAnterior, CoordinateEnum::SuperiorToInferior), + RPS = ITK_ORIENTATIONENUM(CoordinateEnum::LeftToRight, CoordinateEnum::AnteriorToPosterior, CoordinateEnum::InferiorToSuperior), + LPS = ITK_ORIENTATIONENUM(CoordinateEnum::RightToLeft, CoordinateEnum::AnteriorToPosterior, CoordinateEnum::InferiorToSuperior), + RAS = ITK_ORIENTATIONENUM(CoordinateEnum::LeftToRight, CoordinateEnum::PosteriorToAnterior, CoordinateEnum::InferiorToSuperior), + LAS = ITK_ORIENTATIONENUM(CoordinateEnum::RightToLeft, CoordinateEnum::PosteriorToAnterior, CoordinateEnum::InferiorToSuperior), + + PRI = ITK_ORIENTATIONENUM(CoordinateEnum::AnteriorToPosterior, CoordinateEnum::LeftToRight, CoordinateEnum::SuperiorToInferior), + PLI = ITK_ORIENTATIONENUM(CoordinateEnum::AnteriorToPosterior, CoordinateEnum::RightToLeft, CoordinateEnum::SuperiorToInferior), + ARI = ITK_ORIENTATIONENUM(CoordinateEnum::PosteriorToAnterior, CoordinateEnum::LeftToRight, CoordinateEnum::SuperiorToInferior), + ALI = ITK_ORIENTATIONENUM(CoordinateEnum::PosteriorToAnterior, CoordinateEnum::RightToLeft, CoordinateEnum::SuperiorToInferior), + PRS = ITK_ORIENTATIONENUM(CoordinateEnum::AnteriorToPosterior, CoordinateEnum::LeftToRight, CoordinateEnum::InferiorToSuperior), + PLS = ITK_ORIENTATIONENUM(CoordinateEnum::AnteriorToPosterior, CoordinateEnum::RightToLeft, CoordinateEnum::InferiorToSuperior), + ARS = ITK_ORIENTATIONENUM(CoordinateEnum::PosteriorToAnterior, CoordinateEnum::LeftToRight, CoordinateEnum::InferiorToSuperior), + ALS = ITK_ORIENTATIONENUM(CoordinateEnum::PosteriorToAnterior, CoordinateEnum::RightToLeft, CoordinateEnum::InferiorToSuperior), + + IPR = ITK_ORIENTATIONENUM(CoordinateEnum::SuperiorToInferior, CoordinateEnum::AnteriorToPosterior, CoordinateEnum::LeftToRight), + SPR = ITK_ORIENTATIONENUM(CoordinateEnum::InferiorToSuperior, CoordinateEnum::AnteriorToPosterior, CoordinateEnum::LeftToRight), + IAR = ITK_ORIENTATIONENUM(CoordinateEnum::SuperiorToInferior, CoordinateEnum::PosteriorToAnterior, CoordinateEnum::LeftToRight), + SAR = ITK_ORIENTATIONENUM(CoordinateEnum::InferiorToSuperior, CoordinateEnum::PosteriorToAnterior, CoordinateEnum::LeftToRight), + IPL = ITK_ORIENTATIONENUM(CoordinateEnum::SuperiorToInferior, CoordinateEnum::AnteriorToPosterior, CoordinateEnum::RightToLeft), + SPL = ITK_ORIENTATIONENUM(CoordinateEnum::InferiorToSuperior, CoordinateEnum::AnteriorToPosterior, CoordinateEnum::RightToLeft), + IAL = ITK_ORIENTATIONENUM(CoordinateEnum::SuperiorToInferior, CoordinateEnum::PosteriorToAnterior, CoordinateEnum::RightToLeft), + SAL = ITK_ORIENTATIONENUM(CoordinateEnum::InferiorToSuperior, CoordinateEnum::PosteriorToAnterior, CoordinateEnum::RightToLeft), + + PIR = ITK_ORIENTATIONENUM(CoordinateEnum::AnteriorToPosterior, CoordinateEnum::SuperiorToInferior, CoordinateEnum::LeftToRight), + PSR = ITK_ORIENTATIONENUM(CoordinateEnum::AnteriorToPosterior, CoordinateEnum::InferiorToSuperior, CoordinateEnum::LeftToRight), + AIR = ITK_ORIENTATIONENUM(CoordinateEnum::PosteriorToAnterior, CoordinateEnum::SuperiorToInferior, CoordinateEnum::LeftToRight), + ASR = ITK_ORIENTATIONENUM(CoordinateEnum::PosteriorToAnterior, CoordinateEnum::InferiorToSuperior, CoordinateEnum::LeftToRight), + PIL = ITK_ORIENTATIONENUM(CoordinateEnum::AnteriorToPosterior, CoordinateEnum::SuperiorToInferior, CoordinateEnum::RightToLeft), + PSL = ITK_ORIENTATIONENUM(CoordinateEnum::AnteriorToPosterior, CoordinateEnum::InferiorToSuperior, CoordinateEnum::RightToLeft), + AIL = ITK_ORIENTATIONENUM(CoordinateEnum::PosteriorToAnterior, CoordinateEnum::SuperiorToInferior, CoordinateEnum::RightToLeft), + ASL = ITK_ORIENTATIONENUM(CoordinateEnum::PosteriorToAnterior, CoordinateEnum::InferiorToSuperior, CoordinateEnum::RightToLeft) }; #undef ITK_ORIENTATIONENUM From a13d28719675676d7bf82f966eb158261bf1251a Mon Sep 17 00:00:00 2001 From: Bradley Lowekamp Date: Thu, 1 Aug 2024 16:53:12 -0400 Subject: [PATCH 07/10] ENH: Use DICOMOrientation with FROMToTO CoordinateEnums Replace usage of SpatialOrientationAdaptor with DICOMOrientation and unambiguous FROMToTO CoordinateEnums in GE related ImageIO classes. --- Modules/IO/GE/src/itkGE4ImageIO.cxx | 34 +++++--------- Modules/IO/GE/src/itkGE5ImageIO.cxx | 20 +++++---- Modules/IO/GE/src/itkGEAdwImageIO.cxx | 31 +++++-------- Modules/IO/IPL/include/itkGEImageHeader.h | 45 ++++++++++--------- Modules/IO/IPL/src/itkIPLCommonImageIO.cxx | 6 +-- .../itkReadWriteImageWithDictionaryTest.cxx | 5 +-- .../PhilipsREC/src/itkPhilipsRECImageIO.cxx | 6 +-- .../Siemens/src/itkSiemensVisionImageIO.cxx | 18 ++++---- 8 files changed, 75 insertions(+), 90 deletions(-) diff --git a/Modules/IO/GE/src/itkGE4ImageIO.cxx b/Modules/IO/GE/src/itkGE4ImageIO.cxx index 9f5211d6bf1..f849930eee8 100644 --- a/Modules/IO/GE/src/itkGE4ImageIO.cxx +++ b/Modules/IO/GE/src/itkGE4ImageIO.cxx @@ -148,37 +148,27 @@ GE4ImageIO::ReadHeader(const char * FileNameToRead) if (strstr(tmpStr, "CORONAL") != nullptr) { - // hdr->imagePlane = - // itk::SpatialOrientationEnums::ValidCoordinateOrientations::ITK_ANALYZE_ORIENTATION_IRP_CORONAL; - // hdr->origin = itk::SpatialOrientationEnums::ValidCoordinateOrientations::ITK_ORIGIN_SRP; - // hdr->origin was SLA in the brains2 filter. - hdr->coordinateOrientation = - itk::SpatialOrientationEnums::ValidCoordinateOrientations::ITK_COORDINATE_ORIENTATION_RSP; + hdr->coordinateOrientation = DICOMOrientation(DICOMOrientation::CoordinateEnum::RightToLeft, + DICOMOrientation::CoordinateEnum::SuperiorToInferior, + DICOMOrientation::CoordinateEnum::PosteriorToAnterior); } else if (strstr(tmpStr, "SAGITTAL") != nullptr) { - // hdr->imagePlane = - // itk::SpatialOrientationEnums::ValidCoordinateOrientations::ITK_ANALYZE_ORIENTATION_IRP_SAGITTAL; - // hdr->origin = itk::SpatialOrientationEnums::ValidCoordinateOrientations::ITK_ORIGIN_SRA; - // hdr->origin was SLP in the brains2 filter. - hdr->coordinateOrientation = - itk::SpatialOrientationEnums::ValidCoordinateOrientations::ITK_COORDINATE_ORIENTATION_AIR; + hdr->coordinateOrientation = DICOMOrientation( DICOMOrientation::CoordinateEnum::AnteriorToPosterior, + DICOMOrientation::CoordinateEnum::InferiorToSuperior, + DICOMOrientation::CoordinateEnum::RightToLeft); } else if (strstr(tmpStr, "AXIAL") != nullptr) { - // hdr->imagePlane = - // itk::SpatialOrientationEnums::ValidCoordinateOrientations::ITK_ANALYZE_ORIENTATION_IRP_TRANSVERSE; - // hdr->origin = itk::SpatialOrientationEnums::ValidCoordinateOrientations::ITK_ORIGIN_SRA; // was SLP - hdr->coordinateOrientation = - itk::SpatialOrientationEnums::ValidCoordinateOrientations::ITK_COORDINATE_ORIENTATION_RAI; + hdr->coordinateOrientation = DICOMOrientation( DICOMOrientation::CoordinateEnum::RightToLeft, + DICOMOrientation::CoordinateEnum::AnteriorToPosterior, + DICOMOrientation::CoordinateEnum::InferiorToSuperior); } else { - // hdr->imagePlane = - // itk::SpatialOrientationEnums::ValidCoordinateOrientations::ITK_ANALYZE_ORIENTATION_IRP_CORONAL; - // hdr->origin = itk::SpatialOrientationEnums::ValidCoordinateOrientations::ITK_ORIGIN_SRP; // was SLA - hdr->coordinateOrientation = - itk::SpatialOrientationEnums::ValidCoordinateOrientations::ITK_COORDINATE_ORIENTATION_RSP; + hdr->coordinateOrientation = DICOMOrientation( DICOMOrientation::CoordinateEnum::RightToLeft, + DICOMOrientation::CoordinateEnum::SuperiorToInferior, + DICOMOrientation::CoordinateEnum::PosteriorToAnterior); } /* Get the Scan Matrix from the IMAGE Header */ diff --git a/Modules/IO/GE/src/itkGE5ImageIO.cxx b/Modules/IO/GE/src/itkGE5ImageIO.cxx index d98bfcf0bbb..c09e3a2b7c4 100644 --- a/Modules/IO/GE/src/itkGE5ImageIO.cxx +++ b/Modules/IO/GE/src/itkGE5ImageIO.cxx @@ -369,20 +369,24 @@ GE5ImageIO::ReadHeader(const char * FileNameToRead) switch (GE_Plane) { case GE_CORONAL: - curImage->coordinateOrientation = - itk::SpatialOrientationEnums::ValidCoordinateOrientations::ITK_COORDINATE_ORIENTATION_RSP; + curImage->coordinateOrientation = DICOMOrientation( DICOMOrientation::CoordinateEnum::RightToLeft, + DICOMOrientation::CoordinateEnum::SuperiorToInferior, + DICOMOrientation::CoordinateEnum::PosteriorToAnterior); break; case GE_SAGITTAL: - curImage->coordinateOrientation = - itk::SpatialOrientationEnums::ValidCoordinateOrientations::ITK_COORDINATE_ORIENTATION_AIR; + curImage->coordinateOrientation = DICOMOrientation( DICOMOrientation::CoordinateEnum::AnteriorToPosterior, + DICOMOrientation::CoordinateEnum::InferiorToSuperior, + DICOMOrientation::CoordinateEnum::RightToLeft); break; case GE_AXIAL: - curImage->coordinateOrientation = - itk::SpatialOrientationEnums::ValidCoordinateOrientations::ITK_COORDINATE_ORIENTATION_RAI; + curImage->coordinateOrientation = DICOMOrientation( DICOMOrientation::CoordinateEnum::RightToLeft, + DICOMOrientation::CoordinateEnum::AnteriorToPosterior, + DICOMOrientation::CoordinateEnum::InferiorToSuperior); break; default: - curImage->coordinateOrientation = - itk::SpatialOrientationEnums::ValidCoordinateOrientations::ITK_COORDINATE_ORIENTATION_RSP; + curImage->coordinateOrientation = DICOMOrientation( DICOMOrientation::CoordinateEnum::RightToLeft, + DICOMOrientation::CoordinateEnum::SuperiorToInferior, + DICOMOrientation::CoordinateEnum::PosteriorToAnterior); break; } diff --git a/Modules/IO/GE/src/itkGEAdwImageIO.cxx b/Modules/IO/GE/src/itkGEAdwImageIO.cxx index 3031cb779bf..37b20606f16 100644 --- a/Modules/IO/GE/src/itkGEAdwImageIO.cxx +++ b/Modules/IO/GE/src/itkGEAdwImageIO.cxx @@ -165,31 +165,24 @@ GEAdwImageIO::ReadHeader(const char * FileNameToRead) switch (tmpShort) { case GE_CORONAL: - // hdr->imagePlane = itk::IOCommon::ITK_ANALYZE_ORIENTATION_IRP_CORONAL; - // hdr->origin = itk::IOCommon::ITK_ORIGIN_SLA; - hdr->coordinateOrientation = - itk::SpatialOrientationEnums::ValidCoordinateOrientations::ITK_COORDINATE_ORIENTATION_RSP; + hdr->coordinateOrientation = DICOMOrientation(DICOMOrientation::CoordinateEnum::RightToLeft, + DICOMOrientation::CoordinateEnum::SuperiorToInferior, + DICOMOrientation::CoordinateEnum::PosteriorToAnterior); break; case GE_SAGITTAL: - // hdr->imagePlane = - // itk::SpatialOrientationEnums::ValidCoordinateOrientations::ITK_ANALYZE_ORIENTATION_IRP_SAGITTAL; - // hdr->origin = itk::SpatialOrientationEnums::ValidCoordinateOrientations::ITK_ORIGIN_SLA; - hdr->coordinateOrientation = - itk::SpatialOrientationEnums::ValidCoordinateOrientations::ITK_COORDINATE_ORIENTATION_AIR; + hdr->coordinateOrientation = DICOMOrientation(DICOMOrientation::CoordinateEnum::AnteriorToPosterior, + DICOMOrientation::CoordinateEnum::InferiorToSuperior, + DICOMOrientation::CoordinateEnum::RightToLeft); break; case GE_AXIAL: - // hdr->imagePlane = - // itk::SpatialOrientationEnums::ValidCoordinateOrientations::ITK_ANALYZE_ORIENTATION_IRP_TRANSVERSE; - // hdr->origin = itk::SpatialOrientationEnums::ValidCoordinateOrientations::ITK_ORIGIN_SLA; - hdr->coordinateOrientation = - itk::SpatialOrientationEnums::ValidCoordinateOrientations::ITK_COORDINATE_ORIENTATION_RAI; + hdr->coordinateOrientation = DICOMOrientation( DICOMOrientation::CoordinateEnum::RightToLeft, + DICOMOrientation::CoordinateEnum::AnteriorToPosterior, + DICOMOrientation::CoordinateEnum::InferiorToSuperior); break; default: - // hdr->imagePlane = - // itk::SpatialOrientationEnums::ValidCoordinateOrientations::ITK_ANALYZE_ORIENTATION_IRP_CORONAL; - // hdr->origin = itk::SpatialOrientationEnums::ValidCoordinateOrientations::ITK_ORIGIN_SLA; - hdr->coordinateOrientation = - itk::SpatialOrientationEnums::ValidCoordinateOrientations::ITK_COORDINATE_ORIENTATION_RSP; + hdr->coordinateOrientation = DICOMOrientation( DICOMOrientation::CoordinateEnum::RightToLeft, + DICOMOrientation::CoordinateEnum::SuperiorToInferior, + DICOMOrientation::CoordinateEnum::PosteriorToAnterior); break; } this->GetFloatAt(f, GE_ADW_IM_LOC, &(hdr->sliceLocation)); diff --git a/Modules/IO/IPL/include/itkGEImageHeader.h b/Modules/IO/IPL/include/itkGEImageHeader.h index 37dd74cec3d..6796bba64a7 100644 --- a/Modules/IO/IPL/include/itkGEImageHeader.h +++ b/Modules/IO/IPL/include/itkGEImageHeader.h @@ -30,6 +30,7 @@ #include "ITKIOIPLExport.h" #include "itkIOCommon.h" +#include "itkDICOMOrientation.h" enum GE_PANE_STRUCT { @@ -72,27 +73,27 @@ struct GEImageHeader float brhcA; float brhcS; - short acqXsize; - short acqYsize; - short frequencyDir; - char scanner[16]; - char pulseSequence[128]; // Needs to be at least 65 for seimens vision - char patientId[32]; - char scanId[32]; - char name[64]; - char date[32]; - short imageXsize; - short imageYsize; - float imageXres; - float imageYres; - itk::SpatialOrientationEnums::ValidCoordinateOrientations coordinateOrientation; - short numberOfSlices; - short offset; - char filename[itk::IOCommon::ITK_MAXPATHLEN + 1]; - char hospital[35]; - char modality[4]; - short imagesPerSlice; - short turboFactor; // This is only relevant for the geADW image format, but - // is put here for convenience + short acqXsize; + short acqYsize; + short frequencyDir; + char scanner[16]; + char pulseSequence[128]; // Needs to be at least 65 for seimens vision + char patientId[32]; + char scanId[32]; + char name[64]; + char date[32]; + short imageXsize; + short imageYsize; + float imageXres; + float imageYres; + itk::DICOMOrientation::OrientationEnum coordinateOrientation{ itk::DICOMOrientation::OrientationEnum::INVALID }; + short numberOfSlices; + short offset; + char filename[itk::IOCommon::ITK_MAXPATHLEN + 1]; + char hospital[35]; + char modality[4]; + short imagesPerSlice; + short turboFactor; // This is only relevant for the geADW image format, but + // is put here for convenience }; #endif diff --git a/Modules/IO/IPL/src/itkIPLCommonImageIO.cxx b/Modules/IO/IPL/src/itkIPLCommonImageIO.cxx index b0698c00e55..7486787acc4 100644 --- a/Modules/IO/IPL/src/itkIPLCommonImageIO.cxx +++ b/Modules/IO/IPL/src/itkIPLCommonImageIO.cxx @@ -19,7 +19,7 @@ #include "itksys/SystemTools.hxx" #include "itkIPLCommonImageIO.h" #include "itkByteSwapper.h" -#include "itkSpatialOrientationAdapter.h" +#include "itkDICOMOrientation.h" #include "itkDirectory.h" #include "itkMetaDataObject.h" #include @@ -256,9 +256,7 @@ IPLCommonImageIO::ReadImageInformation() // // set direction cosines - using OrientAdapterType = SpatialOrientationAdapter; - SpatialOrientationAdapter::DirectionType dir = - OrientAdapterType().ToDirectionCosines(m_ImageHeader->coordinateOrientation); + DICOMOrientation::DirectionType dir = DICOMOrientation(m_ImageHeader->coordinateOrientation).GetAsDirection(); std::vector dirx(3, 0), diry(3, 0), dirz(3, 0); dirx[0] = dir[0][0]; dirx[1] = dir[1][0]; diff --git a/Modules/IO/ImageBase/test/itkReadWriteImageWithDictionaryTest.cxx b/Modules/IO/ImageBase/test/itkReadWriteImageWithDictionaryTest.cxx index 25915796e58..484f3c0afb0 100644 --- a/Modules/IO/ImageBase/test/itkReadWriteImageWithDictionaryTest.cxx +++ b/Modules/IO/ImageBase/test/itkReadWriteImageWithDictionaryTest.cxx @@ -19,7 +19,7 @@ #include "itkImageFileWriter.h" #include "itkIOCommon.h" #include "itkMetaDataObject.h" -#include "itkSpatialOrientationAdapter.h" +#include "itkDICOMOrientation.h" #include "itkTestingMacros.h" int @@ -47,8 +47,7 @@ itkReadWriteImageWithDictionaryTest(int argc, char * argv[]) inputImage->Allocate(); inputImage->FillBuffer(0); - inputImage->SetDirection(itk::SpatialOrientationAdapter().ToDirectionCosines( - itk::SpatialOrientationEnums::ValidCoordinateOrientations::ITK_COORDINATE_ORIENTATION_RIP)); + inputImage->SetDirection(itk::DICOMOrientation("LSA").GetAsDirection()); // Add some metadata in the dictionary itk::MetaDataDictionary & inputDictionary = inputImage->GetMetaDataDictionary(); diff --git a/Modules/IO/PhilipsREC/src/itkPhilipsRECImageIO.cxx b/Modules/IO/PhilipsREC/src/itkPhilipsRECImageIO.cxx index e3d29acb2e3..1b8d4d0119f 100644 --- a/Modules/IO/PhilipsREC/src/itkPhilipsRECImageIO.cxx +++ b/Modules/IO/PhilipsREC/src/itkPhilipsRECImageIO.cxx @@ -29,7 +29,7 @@ #include "itkIOCommon.h" #include "itkByteSwapper.h" #include "itkMetaDataObject.h" -#include "itkSpatialOrientationAdapter.h" +#include "itkDICOMOrientation.h" #include "itksys/SystemTools.hxx" #include "itk_zlib.h" #include @@ -713,7 +713,7 @@ PhilipsRECImageIO::ReadImageInformation() AffineMatrix spacing; spacing.SetIdentity(); - SpatialOrientationEnums::ValidCoordinateOrientations coord_orient; + DICOMOrientation coord_orient; switch (par.sliceorient) { @@ -747,7 +747,7 @@ PhilipsRECImageIO::ReadImageInformation() spacing[2][2] = par.vox[1]; } - SpatialOrientationAdapter::DirectionType dir = SpatialOrientationAdapter().ToDirectionCosines(coord_orient); + DICOMOrientation::DirectionType dir = coord_orient.GetAsDirection(); AffineMatrix direction; direction.SetIdentity(); diff --git a/Modules/IO/Siemens/src/itkSiemensVisionImageIO.cxx b/Modules/IO/Siemens/src/itkSiemensVisionImageIO.cxx index 5ce05187c89..080b4253675 100644 --- a/Modules/IO/Siemens/src/itkSiemensVisionImageIO.cxx +++ b/Modules/IO/Siemens/src/itkSiemensVisionImageIO.cxx @@ -224,7 +224,7 @@ SiemensVisionImageIO::ReadHeader(const char * FileNameToRead) { // hdr->imagePlane = itk::IOCommon::ITK_ANALYZE_ORIENTATION_IRP_CORONAL; hdr->coordinateOrientation = - itk::SpatialOrientationEnums::ValidCoordinateOrientations::ITK_COORDINATE_ORIENTATION_RSP; + DICOMOrientation(itk::SpatialOrientationEnums::ValidCoordinateOrientations::ITK_COORDINATE_ORIENTATION_RSP); } else { @@ -233,14 +233,14 @@ SiemensVisionImageIO::ReadHeader(const char * FileNameToRead) // hdr->imagePlane = // itk::SpatialOrientationEnums::ValidCoordinateOrientations::ITK_ANALYZE_ORIENTATION_IRP_SAGITTAL; hdr->coordinateOrientation = - itk::SpatialOrientationEnums::ValidCoordinateOrientations::ITK_COORDINATE_ORIENTATION_AIR; + DICOMOrientation(itk::SpatialOrientationEnums::ValidCoordinateOrientations::ITK_COORDINATE_ORIENTATION_AIR); } else { // hdr->imagePlane = // itk::SpatialOrientationEnums::ValidCoordinateOrientations::ITK_ANALYZE_ORIENTATION_IRP_TRANSVERSE; hdr->coordinateOrientation = - itk::SpatialOrientationEnums::ValidCoordinateOrientations::ITK_COORDINATE_ORIENTATION_RAI; + DICOMOrientation(itk::SpatialOrientationEnums::ValidCoordinateOrientations::ITK_COORDINATE_ORIENTATION_RAI); } } } @@ -251,7 +251,7 @@ SiemensVisionImageIO::ReadHeader(const char * FileNameToRead) // hdr->imagePlane = // itk::SpatialOrientationEnums::ValidCoordinateOrientations::ITK_ANALYZE_ORIENTATION_IRP_SAGITTAL; hdr->coordinateOrientation = - itk::SpatialOrientationEnums::ValidCoordinateOrientations::ITK_COORDINATE_ORIENTATION_AIR; + DICOMOrientation(itk::SpatialOrientationEnums::ValidCoordinateOrientations::ITK_COORDINATE_ORIENTATION_AIR); } else { @@ -260,14 +260,14 @@ SiemensVisionImageIO::ReadHeader(const char * FileNameToRead) // hdr->imagePlane = // itk::SpatialOrientationEnums::ValidCoordinateOrientations::ITK_ANALYZE_ORIENTATION_IRP_CORONAL; hdr->coordinateOrientation = - itk::SpatialOrientationEnums::ValidCoordinateOrientations::ITK_COORDINATE_ORIENTATION_RSP; + DICOMOrientation(itk::SpatialOrientationEnums::ValidCoordinateOrientations::ITK_COORDINATE_ORIENTATION_RSP); } else { // hdr->imagePlane = // itk::SpatialOrientationEnums::ValidCoordinateOrientations::ITK_ANALYZE_ORIENTATION_IRP_TRANSVERSE; hdr->coordinateOrientation = - itk::SpatialOrientationEnums::ValidCoordinateOrientations::ITK_COORDINATE_ORIENTATION_RAI; + DICOMOrientation(itk::SpatialOrientationEnums::ValidCoordinateOrientations::ITK_COORDINATE_ORIENTATION_RAI); } } } @@ -278,7 +278,7 @@ SiemensVisionImageIO::ReadHeader(const char * FileNameToRead) // hdr->imagePlane = // itk::SpatialOrientationEnums::ValidCoordinateOrientations::ITK_ANALYZE_ORIENTATION_IRP_TRANSVERSE; hdr->coordinateOrientation = - itk::SpatialOrientationEnums::ValidCoordinateOrientations::ITK_COORDINATE_ORIENTATION_RAI; + DICOMOrientation(itk::SpatialOrientationEnums::ValidCoordinateOrientations::ITK_COORDINATE_ORIENTATION_RAI); } else { @@ -287,14 +287,14 @@ SiemensVisionImageIO::ReadHeader(const char * FileNameToRead) // hdr->imagePlane = // itk::SpatialOrientationEnums::ValidCoordinateOrientations::ITK_ANALYZE_ORIENTATION_IRP_CORONAL; hdr->coordinateOrientation = - itk::SpatialOrientationEnums::ValidCoordinateOrientations::ITK_COORDINATE_ORIENTATION_RSP; + DICOMOrientation(itk::SpatialOrientationEnums::ValidCoordinateOrientations::ITK_COORDINATE_ORIENTATION_RSP); } else { // hdr->imagePlane = // itk::SpatialOrientationEnums::ValidCoordinateOrientations::ITK_ANALYZE_ORIENTATION_IRP_SAGITTAL; hdr->coordinateOrientation = - itk::SpatialOrientationEnums::ValidCoordinateOrientations::ITK_COORDINATE_ORIENTATION_AIR; + DICOMOrientation(itk::SpatialOrientationEnums::ValidCoordinateOrientations::ITK_COORDINATE_ORIENTATION_AIR); } } } From 4d8bf5c1ed2201da5695c85bab01863d07b3089b Mon Sep 17 00:00:00 2001 From: Bradley Lowekamp Date: Thu, 1 Aug 2024 17:03:14 -0400 Subject: [PATCH 08/10] ENH: Update NiftiImageIO to use DICOMOrientation --- Modules/IO/NIFTI/src/itkNiftiImageIO.cxx | 35 +++-- Modules/IO/NIFTI/test/itkNiftiImageIOTest.h | 7 +- .../IO/NIFTI/test/itkNiftiImageIOTest4.cxx | 6 +- .../IO/NIFTI/test/itkNiftiReadAnalyzeTest.cxx | 138 ++++++++---------- 4 files changed, 88 insertions(+), 98 deletions(-) diff --git a/Modules/IO/NIFTI/src/itkNiftiImageIO.cxx b/Modules/IO/NIFTI/src/itkNiftiImageIO.cxx index 8ec56d9a117..ac7283a54a5 100644 --- a/Modules/IO/NIFTI/src/itkNiftiImageIO.cxx +++ b/Modules/IO/NIFTI/src/itkNiftiImageIO.cxx @@ -18,7 +18,7 @@ #include "itkNiftiImageIO.h" #include "itkIOCommon.h" #include "itkMetaDataObject.h" -#include "itkSpatialOrientationAdapter.h" +#include "itkDICOMOrientation.h" #include #include "itkNiftiImageIOConfigurePrivate.h" #include "itkMakeUniqueForOverwrite.h" @@ -1867,7 +1867,6 @@ IsAffine(const mat44 & nifti_mat) void NiftiImageIO::SetImageIOOrientationFromNIfTI(unsigned short dims, double spacingscale, double timingscale) { - typedef SpatialOrientationAdapter OrientAdapterType; // in the case of an Analyze75 file, use old analyze orient method. // but this could be a nifti file without qform and sform if (this->m_NiftiImage->qform_code == NIFTI_XFORM_UNKNOWN && this->m_NiftiImage->sform_code == NIFTI_XFORM_UNKNOWN) @@ -1886,32 +1885,46 @@ NiftiImageIO::SetImageIOOrientationFromNIfTI(unsigned short dims, double spacing this->GetLegacyAnalyze75Mode() != NiftiImageIOEnums::Analyze75Flavor::AnalyzeITK4 && this->GetLegacyAnalyze75Mode() != NiftiImageIOEnums::Analyze75Flavor::AnalyzeITK4Warning) { // only do this for Analyze file format - SpatialOrientationAdapter::OrientationType orient; + DICOMOrientation orient(DICOMOrientation::OrientationEnum::INVALID); switch (this->m_NiftiImage->analyze75_orient) { case a75_transverse_unflipped: - orient = SpatialOrientationEnums::ValidCoordinateOrientations::ITK_COORDINATE_ORIENTATION_RPI; + orient = DICOMOrientation(DICOMOrientation::CoordinateEnum::RightToLeft, + DICOMOrientation::CoordinateEnum::PosteriorToAnterior, + DICOMOrientation::CoordinateEnum::InferiorToSuperior); break; case a75_sagittal_unflipped: - orient = SpatialOrientationEnums::ValidCoordinateOrientations::ITK_COORDINATE_ORIENTATION_PIR; + orient = DICOMOrientation( DICOMOrientation::CoordinateEnum::PosteriorToAnterior, + DICOMOrientation::CoordinateEnum::InferiorToSuperior, + DICOMOrientation::CoordinateEnum::RightToLeft); break; case a75_coronal_unflipped: - orient = SpatialOrientationEnums::ValidCoordinateOrientations::ITK_COORDINATE_ORIENTATION_RIP; + orient = DICOMOrientation( DICOMOrientation::CoordinateEnum::RightToLeft, + DICOMOrientation::CoordinateEnum::InferiorToSuperior, + DICOMOrientation::CoordinateEnum::PosteriorToAnterior); break; case a75_transverse_flipped: - orient = SpatialOrientationEnums::ValidCoordinateOrientations::ITK_COORDINATE_ORIENTATION_RAI; + orient = DICOMOrientation( DICOMOrientation::CoordinateEnum::RightToLeft, + DICOMOrientation::CoordinateEnum::AnteriorToPosterior, + DICOMOrientation::CoordinateEnum::InferiorToSuperior); break; case a75_sagittal_flipped: - orient = SpatialOrientationEnums::ValidCoordinateOrientations::ITK_COORDINATE_ORIENTATION_PIL; + orient = DICOMOrientation( DICOMOrientation::CoordinateEnum::PosteriorToAnterior, + DICOMOrientation::CoordinateEnum::InferiorToSuperior, + DICOMOrientation::CoordinateEnum::LeftToRight); break; case a75_coronal_flipped: - orient = SpatialOrientationEnums::ValidCoordinateOrientations::ITK_COORDINATE_ORIENTATION_RSP; + orient = DICOMOrientation( DICOMOrientation::CoordinateEnum::RightToLeft, + DICOMOrientation::CoordinateEnum::SuperiorToInferior, + DICOMOrientation::CoordinateEnum::PosteriorToAnterior); break; case a75_orient_unknown: - orient = SpatialOrientationEnums::ValidCoordinateOrientations::ITK_COORDINATE_ORIENTATION_RIP; + orient = DICOMOrientation( DICOMOrientation::CoordinateEnum::RightToLeft, + DICOMOrientation::CoordinateEnum::InferiorToSuperior, + DICOMOrientation::CoordinateEnum::PosteriorToAnterior); break; } - const SpatialOrientationAdapter::DirectionType dir = OrientAdapterType().ToDirectionCosines(orient); + const auto dir = orient.GetAsDirection(); const int max_defined_orientation_dims = (dims > 3) ? 3 : dims; for (int d = 0; d < max_defined_orientation_dims; ++d) { diff --git a/Modules/IO/NIFTI/test/itkNiftiImageIOTest.h b/Modules/IO/NIFTI/test/itkNiftiImageIOTest.h index 6bf97e9985f..25574ea3706 100644 --- a/Modules/IO/NIFTI/test/itkNiftiImageIOTest.h +++ b/Modules/IO/NIFTI/test/itkNiftiImageIOTest.h @@ -30,7 +30,7 @@ #include "itkNiftiImageIO.h" #include "itkMetaDataObject.h" #include "itkIOCommon.h" -#include "itkSpatialOrientationAdapter.h" +#include "itkDICOMOrientation.h" #include "itkDiffusionTensor3D.h" #include "itkAffineTransform.h" #include "itkVector.h" @@ -228,8 +228,9 @@ template typename ImageType::DirectionType CORDirCosines() { - typename itk::SpatialOrientationAdapter::DirectionType CORdir = itk::SpatialOrientationAdapter().ToDirectionCosines( - itk::SpatialOrientationEnums::ValidCoordinateOrientations::ITK_COORDINATE_ORIENTATION_RIP); + auto CORdir = itk::DICOMOrientation ( itk::DICOMOrientation::CoordinateEnum::RightToLeft, + itk::DICOMOrientation::CoordinateEnum::InferiorToSuperior, + itk::DICOMOrientation::CoordinateEnum::PosteriorToAnterior).GetAsDirection(); typename ImageType::DirectionType dir; for (unsigned int i = 0; i < ImageType::ImageDimension; ++i) { diff --git a/Modules/IO/NIFTI/test/itkNiftiImageIOTest4.cxx b/Modules/IO/NIFTI/test/itkNiftiImageIOTest4.cxx index 64408d4e651..5ac8afc1550 100644 --- a/Modules/IO/NIFTI/test/itkNiftiImageIOTest4.cxx +++ b/Modules/IO/NIFTI/test/itkNiftiImageIOTest4.cxx @@ -71,7 +71,7 @@ itkNiftiImageIOTest4(int argc, char * argv[]) Test4ImageType::DirectionType dir; dir.SetIdentity(); -#if 1 + // arbitrarily rotate the unit vectors to pick random direction // cosines; vnl_random randgen(8775070); @@ -102,10 +102,6 @@ itkNiftiImageIOTest4(int argc, char * argv[]) } } -#else - dir = itk::SpatialOrientationAdapter().ToDirectionCosines( - itk::SpatialOrientationEnums::ValidCoordinateOrientations::ITK_COORDINATE_ORIENTATION_PLI); -#endif test4Image->SetDirection(dir); std::string fname("directionsTest.nii.gz"); try diff --git a/Modules/IO/NIFTI/test/itkNiftiReadAnalyzeTest.cxx b/Modules/IO/NIFTI/test/itkNiftiReadAnalyzeTest.cxx index 5860d24ba6c..63833b281d0 100644 --- a/Modules/IO/NIFTI/test/itkNiftiReadAnalyzeTest.cxx +++ b/Modules/IO/NIFTI/test/itkNiftiReadAnalyzeTest.cxx @@ -22,7 +22,7 @@ #include "itkImageFileReader.h" #include "itkImageRegionConstIterator.h" #include "itkMath.h" -#include "itkSpatialOrientationAdapter.h" +#include "itkDICOMOrientation.h" // debug #include @@ -120,59 +120,6 @@ const unsigned char LittleEndian_img[] = { 0x00, 0x00, 0xe0, 0x42, 0x00, 0x00, 0xe0, 0x42, 0x00, 0x00, 0xe0, 0x42, }; -// Map between axis string labels and spatial orientation -std::map codeToString = { - { itk::SpatialOrientationEnums::ValidCoordinateOrientations::ITK_COORDINATE_ORIENTATION_RIP, "RIP" }, - { itk::SpatialOrientationEnums::ValidCoordinateOrientations::ITK_COORDINATE_ORIENTATION_LIP, "LIP" }, - { itk::SpatialOrientationEnums::ValidCoordinateOrientations::ITK_COORDINATE_ORIENTATION_RSP, "RSP" }, - { itk::SpatialOrientationEnums::ValidCoordinateOrientations::ITK_COORDINATE_ORIENTATION_LSP, "LSP" }, - { itk::SpatialOrientationEnums::ValidCoordinateOrientations::ITK_COORDINATE_ORIENTATION_RIA, "RIA" }, - { itk::SpatialOrientationEnums::ValidCoordinateOrientations::ITK_COORDINATE_ORIENTATION_LIA, "LIA" }, - { itk::SpatialOrientationEnums::ValidCoordinateOrientations::ITK_COORDINATE_ORIENTATION_RSA, "RSA" }, - { itk::SpatialOrientationEnums::ValidCoordinateOrientations::ITK_COORDINATE_ORIENTATION_LSA, "LSA" }, - { itk::SpatialOrientationEnums::ValidCoordinateOrientations::ITK_COORDINATE_ORIENTATION_IRP, "IRP" }, - { itk::SpatialOrientationEnums::ValidCoordinateOrientations::ITK_COORDINATE_ORIENTATION_ILP, "ILP" }, - { itk::SpatialOrientationEnums::ValidCoordinateOrientations::ITK_COORDINATE_ORIENTATION_SRP, "SRP" }, - { itk::SpatialOrientationEnums::ValidCoordinateOrientations::ITK_COORDINATE_ORIENTATION_SLP, "SLP" }, - { itk::SpatialOrientationEnums::ValidCoordinateOrientations::ITK_COORDINATE_ORIENTATION_IRA, "IRA" }, - { itk::SpatialOrientationEnums::ValidCoordinateOrientations::ITK_COORDINATE_ORIENTATION_ILA, "ILA" }, - { itk::SpatialOrientationEnums::ValidCoordinateOrientations::ITK_COORDINATE_ORIENTATION_SRA, "SRA" }, - { itk::SpatialOrientationEnums::ValidCoordinateOrientations::ITK_COORDINATE_ORIENTATION_SLA, "SLA" }, - { itk::SpatialOrientationEnums::ValidCoordinateOrientations::ITK_COORDINATE_ORIENTATION_RPI, "RPI" }, - { itk::SpatialOrientationEnums::ValidCoordinateOrientations::ITK_COORDINATE_ORIENTATION_LPI, "LPI" }, - { itk::SpatialOrientationEnums::ValidCoordinateOrientations::ITK_COORDINATE_ORIENTATION_RAI, "RAI" }, - { itk::SpatialOrientationEnums::ValidCoordinateOrientations::ITK_COORDINATE_ORIENTATION_LAI, "LAI" }, - { itk::SpatialOrientationEnums::ValidCoordinateOrientations::ITK_COORDINATE_ORIENTATION_RPS, "RPS" }, - { itk::SpatialOrientationEnums::ValidCoordinateOrientations::ITK_COORDINATE_ORIENTATION_LPS, "LPS" }, - { itk::SpatialOrientationEnums::ValidCoordinateOrientations::ITK_COORDINATE_ORIENTATION_RAS, "RAS" }, - { itk::SpatialOrientationEnums::ValidCoordinateOrientations::ITK_COORDINATE_ORIENTATION_LAS, "LAS" }, - { itk::SpatialOrientationEnums::ValidCoordinateOrientations::ITK_COORDINATE_ORIENTATION_PRI, "PRI" }, - { itk::SpatialOrientationEnums::ValidCoordinateOrientations::ITK_COORDINATE_ORIENTATION_PLI, "PLI" }, - { itk::SpatialOrientationEnums::ValidCoordinateOrientations::ITK_COORDINATE_ORIENTATION_ARI, "ARI" }, - { itk::SpatialOrientationEnums::ValidCoordinateOrientations::ITK_COORDINATE_ORIENTATION_ALI, "ALI" }, - { itk::SpatialOrientationEnums::ValidCoordinateOrientations::ITK_COORDINATE_ORIENTATION_PRS, "PRS" }, - { itk::SpatialOrientationEnums::ValidCoordinateOrientations::ITK_COORDINATE_ORIENTATION_PLS, "PLS" }, - { itk::SpatialOrientationEnums::ValidCoordinateOrientations::ITK_COORDINATE_ORIENTATION_ARS, "ARS" }, - { itk::SpatialOrientationEnums::ValidCoordinateOrientations::ITK_COORDINATE_ORIENTATION_ALS, "ALS" }, - { itk::SpatialOrientationEnums::ValidCoordinateOrientations::ITK_COORDINATE_ORIENTATION_IPR, "IPR" }, - { itk::SpatialOrientationEnums::ValidCoordinateOrientations::ITK_COORDINATE_ORIENTATION_SPR, "SPR" }, - { itk::SpatialOrientationEnums::ValidCoordinateOrientations::ITK_COORDINATE_ORIENTATION_IAR, "IAR" }, - { itk::SpatialOrientationEnums::ValidCoordinateOrientations::ITK_COORDINATE_ORIENTATION_SAR, "SAR" }, - { itk::SpatialOrientationEnums::ValidCoordinateOrientations::ITK_COORDINATE_ORIENTATION_IPL, "IPL" }, - { itk::SpatialOrientationEnums::ValidCoordinateOrientations::ITK_COORDINATE_ORIENTATION_SPL, "SPL" }, - { itk::SpatialOrientationEnums::ValidCoordinateOrientations::ITK_COORDINATE_ORIENTATION_IAL, "IAL" }, - { itk::SpatialOrientationEnums::ValidCoordinateOrientations::ITK_COORDINATE_ORIENTATION_SAL, "SAL" }, - { itk::SpatialOrientationEnums::ValidCoordinateOrientations::ITK_COORDINATE_ORIENTATION_PIR, "PIR" }, - { itk::SpatialOrientationEnums::ValidCoordinateOrientations::ITK_COORDINATE_ORIENTATION_PSR, "PSR" }, - { itk::SpatialOrientationEnums::ValidCoordinateOrientations::ITK_COORDINATE_ORIENTATION_AIR, "AIR" }, - { itk::SpatialOrientationEnums::ValidCoordinateOrientations::ITK_COORDINATE_ORIENTATION_ASR, "ASR" }, - { itk::SpatialOrientationEnums::ValidCoordinateOrientations::ITK_COORDINATE_ORIENTATION_PIL, "PIL" }, - { itk::SpatialOrientationEnums::ValidCoordinateOrientations::ITK_COORDINATE_ORIENTATION_PSL, "PSL" }, - { itk::SpatialOrientationEnums::ValidCoordinateOrientations::ITK_COORDINATE_ORIENTATION_AIL, "AIL" }, - { itk::SpatialOrientationEnums::ValidCoordinateOrientations::ITK_COORDINATE_ORIENTATION_ASL, "ASL" } -}; - - /** WriteFile * Write out a char array as binary */ @@ -231,17 +178,17 @@ ReadImage(const std::string & fileName, int itkNiftiAnalyzeContentsAndCoordinatesTest(char * argv[], unsigned char hist_orient_code, - itk::SpatialOrientationEnums::ValidCoordinateOrientations expected_code, + itk::DICOMOrientation expected_code, itk::NiftiImageIOEnums::Analyze75Flavor analyze_mode, bool flip_x = false) { std::string hdrName(argv[1]); hdrName += "/littleEndian_"; - hdrName += codeToString[expected_code]; + hdrName += expected_code.GetAsString(); hdrName += ".hdr"; std::string imgName(argv[1]); imgName += "/littleEndian_"; - imgName += codeToString[expected_code]; + imgName += expected_code.GetAsString(); imgName += ".img"; // hack the header to have proper orientation code @@ -301,14 +248,13 @@ itkNiftiAnalyzeContentsAndCoordinatesTest(char * } } - itk::SpatialOrientationEnums::ValidCoordinateOrientations orientation_code = - itk::SpatialOrientationAdapter().FromDirectionCosines(img->GetDirection()); + const itk::DICOMOrientation orientation_code(img->GetDirection()); // verify the correct orientation : if (orientation_code != expected_code) { std::cerr << "Analyze orientation " << static_cast(hist_orient_code) << std::endl; - std::cerr << "expected orientation " << codeToString[expected_code] << " but found " - << codeToString[orientation_code] << std::endl; + std::cerr << "expected orientation " << expected_code.GetAsString() << " but found " + << orientation_code.GetAsString() << std::endl; return EXIT_FAILURE; } @@ -318,7 +264,7 @@ itkNiftiAnalyzeContentsAndCoordinatesTest(char * << "negative x step:" << (flip_x ? "true" : "false") << std::endl << "Origin :" << img->GetOrigin() << std::endl << "Spacing :" << img->GetSpacing() << std::endl - << "Code :" << codeToString[orientation_code] << std::endl + << "Code :" << orientation_code.GetAsString() << std::endl << "Direction:" << img->GetDirection() << std::endl; return EXIT_SUCCESS; @@ -343,95 +289,129 @@ itkNiftiReadAnalyzeTest(int argc, char * argv[]) return itkNiftiAnalyzeContentsAndCoordinatesTest( argv, 0, - itk::SpatialOrientationEnums::ValidCoordinateOrientations::ITK_COORDINATE_ORIENTATION_RPI, + itk::DICOMOrientation(itk::DICOMOrientation::CoordinateEnum::RightToLeft, + itk::DICOMOrientation::CoordinateEnum::PosteriorToAnterior, + itk::DICOMOrientation::CoordinateEnum::InferiorToSuperior), itk::NiftiImageIOEnums::Analyze75Flavor::AnalyzeReject) != EXIT_FAILURE || itkNiftiAnalyzeContentsAndCoordinatesTest( argv, 0, - itk::SpatialOrientationEnums::ValidCoordinateOrientations::ITK_COORDINATE_ORIENTATION_RPI, + itk::DICOMOrientation(itk::DICOMOrientation::CoordinateEnum::RightToLeft, + itk::DICOMOrientation::CoordinateEnum::PosteriorToAnterior, + itk::DICOMOrientation::CoordinateEnum::InferiorToSuperior), itk::NiftiImageIOEnums::Analyze75Flavor::AnalyzeSPM) == EXIT_FAILURE || itkNiftiAnalyzeContentsAndCoordinatesTest( argv, 1, - itk::SpatialOrientationEnums::ValidCoordinateOrientations::ITK_COORDINATE_ORIENTATION_RIP, + itk::DICOMOrientation(itk::DICOMOrientation::CoordinateEnum::RightToLeft, + itk::DICOMOrientation::CoordinateEnum::InferiorToSuperior, + itk::DICOMOrientation::CoordinateEnum::PosteriorToAnterior), itk::NiftiImageIOEnums::Analyze75Flavor::AnalyzeSPM) == EXIT_FAILURE || itkNiftiAnalyzeContentsAndCoordinatesTest( argv, 2, - itk::SpatialOrientationEnums::ValidCoordinateOrientations::ITK_COORDINATE_ORIENTATION_PIR, + itk::DICOMOrientation(itk::DICOMOrientation::CoordinateEnum::PosteriorToAnterior, + itk::DICOMOrientation::CoordinateEnum::InferiorToSuperior, + itk::DICOMOrientation::CoordinateEnum::RightToLeft), itk::NiftiImageIOEnums::Analyze75Flavor::AnalyzeSPM) == EXIT_FAILURE || itkNiftiAnalyzeContentsAndCoordinatesTest( argv, 3, - itk::SpatialOrientationEnums::ValidCoordinateOrientations::ITK_COORDINATE_ORIENTATION_RAI, + itk::DICOMOrientation(itk::DICOMOrientation::CoordinateEnum::RightToLeft, + itk::DICOMOrientation::CoordinateEnum::AnteriorToPosterior, + itk::DICOMOrientation::CoordinateEnum::InferiorToSuperior), itk::NiftiImageIOEnums::Analyze75Flavor::AnalyzeSPM) == EXIT_FAILURE || itkNiftiAnalyzeContentsAndCoordinatesTest( argv, 4, - itk::SpatialOrientationEnums::ValidCoordinateOrientations::ITK_COORDINATE_ORIENTATION_RSP, + itk::DICOMOrientation(itk::DICOMOrientation::CoordinateEnum::RightToLeft, + itk::DICOMOrientation::CoordinateEnum::SuperiorToInferior, + itk::DICOMOrientation::CoordinateEnum::PosteriorToAnterior), itk::NiftiImageIOEnums::Analyze75Flavor::AnalyzeSPM) == EXIT_FAILURE || itkNiftiAnalyzeContentsAndCoordinatesTest( argv, 5, - itk::SpatialOrientationEnums::ValidCoordinateOrientations::ITK_COORDINATE_ORIENTATION_PIL, + itk::DICOMOrientation(itk::DICOMOrientation::CoordinateEnum::PosteriorToAnterior, + itk::DICOMOrientation::CoordinateEnum::InferiorToSuperior, + itk::DICOMOrientation::CoordinateEnum::LeftToRight), itk::NiftiImageIOEnums::Analyze75Flavor::AnalyzeSPM) == EXIT_FAILURE || // ITK4 default behaviour: reader should ignore orientation code and always produce RAI , // there should be a warning on console itkNiftiAnalyzeContentsAndCoordinatesTest( argv, 0, - itk::SpatialOrientationEnums::ValidCoordinateOrientations::ITK_COORDINATE_ORIENTATION_RAI, + itk::DICOMOrientation(itk::DICOMOrientation::CoordinateEnum::RightToLeft, + itk::DICOMOrientation::CoordinateEnum::AnteriorToPosterior, + itk::DICOMOrientation::CoordinateEnum::InferiorToSuperior), itk::NiftiImageIOEnums::Analyze75Flavor::AnalyzeITK4Warning) == EXIT_FAILURE || // ITK4 reader should ignore orientation code and always produce RAI itkNiftiAnalyzeContentsAndCoordinatesTest( argv, 0, - itk::SpatialOrientationEnums::ValidCoordinateOrientations::ITK_COORDINATE_ORIENTATION_RAI, + itk::DICOMOrientation(itk::DICOMOrientation::CoordinateEnum::RightToLeft, + itk::DICOMOrientation::CoordinateEnum::AnteriorToPosterior, + itk::DICOMOrientation::CoordinateEnum::InferiorToSuperior), itk::NiftiImageIOEnums::Analyze75Flavor::AnalyzeITK4) == EXIT_FAILURE || itkNiftiAnalyzeContentsAndCoordinatesTest( argv, 1, - itk::SpatialOrientationEnums::ValidCoordinateOrientations::ITK_COORDINATE_ORIENTATION_RAI, + itk::DICOMOrientation(itk::DICOMOrientation::CoordinateEnum::RightToLeft, + itk::DICOMOrientation::CoordinateEnum::AnteriorToPosterior, + itk::DICOMOrientation::CoordinateEnum::InferiorToSuperior), itk::NiftiImageIOEnums::Analyze75Flavor::AnalyzeITK4) == EXIT_FAILURE || itkNiftiAnalyzeContentsAndCoordinatesTest( argv, 2, - itk::SpatialOrientationEnums::ValidCoordinateOrientations::ITK_COORDINATE_ORIENTATION_RAI, + itk::DICOMOrientation(itk::DICOMOrientation::CoordinateEnum::RightToLeft, + itk::DICOMOrientation::CoordinateEnum::AnteriorToPosterior, + itk::DICOMOrientation::CoordinateEnum::InferiorToSuperior), itk::NiftiImageIOEnums::Analyze75Flavor::AnalyzeITK4) == EXIT_FAILURE || itkNiftiAnalyzeContentsAndCoordinatesTest( argv, 3, - itk::SpatialOrientationEnums::ValidCoordinateOrientations::ITK_COORDINATE_ORIENTATION_RAI, + itk::DICOMOrientation(itk::DICOMOrientation::CoordinateEnum::RightToLeft, + itk::DICOMOrientation::CoordinateEnum::AnteriorToPosterior, + itk::DICOMOrientation::CoordinateEnum::InferiorToSuperior), itk::NiftiImageIOEnums::Analyze75Flavor::AnalyzeITK4) == EXIT_FAILURE || itkNiftiAnalyzeContentsAndCoordinatesTest( argv, 5, - itk::SpatialOrientationEnums::ValidCoordinateOrientations::ITK_COORDINATE_ORIENTATION_RAI, + itk::DICOMOrientation(itk::DICOMOrientation::CoordinateEnum::RightToLeft, + itk::DICOMOrientation::CoordinateEnum::AnteriorToPosterior, + itk::DICOMOrientation::CoordinateEnum::InferiorToSuperior), itk::NiftiImageIOEnums::Analyze75Flavor::AnalyzeITK4) == EXIT_FAILURE || itkNiftiAnalyzeContentsAndCoordinatesTest( argv, 5, - itk::SpatialOrientationEnums::ValidCoordinateOrientations::ITK_COORDINATE_ORIENTATION_RAI, + itk::DICOMOrientation(itk::DICOMOrientation::CoordinateEnum::RightToLeft, + itk::DICOMOrientation::CoordinateEnum::AnteriorToPosterior, + itk::DICOMOrientation::CoordinateEnum::InferiorToSuperior), itk::NiftiImageIOEnums::Analyze75Flavor::AnalyzeITK4) == EXIT_FAILURE || // flip X axis , SPM reader should respect this itkNiftiAnalyzeContentsAndCoordinatesTest( argv, 0, - itk::SpatialOrientationEnums::ValidCoordinateOrientations::ITK_COORDINATE_ORIENTATION_LPI, + itk::DICOMOrientation(itk::DICOMOrientation::CoordinateEnum::LeftToRight, + itk::DICOMOrientation::CoordinateEnum::PosteriorToAnterior, + itk::DICOMOrientation::CoordinateEnum::InferiorToSuperior), itk::NiftiImageIOEnums::Analyze75Flavor::AnalyzeSPM, true) == EXIT_FAILURE || // flip X axis , ITK4 reader should respect this itkNiftiAnalyzeContentsAndCoordinatesTest( argv, 0, - itk::SpatialOrientationEnums::ValidCoordinateOrientations::ITK_COORDINATE_ORIENTATION_LAI, + itk::DICOMOrientation(itk::DICOMOrientation::CoordinateEnum::LeftToRight, + itk::DICOMOrientation::CoordinateEnum::AnteriorToPosterior, + itk::DICOMOrientation::CoordinateEnum::InferiorToSuperior), itk::NiftiImageIOEnums::Analyze75Flavor::AnalyzeITK4, true) == EXIT_FAILURE || // flip X axis , FSL reader should ignore this itkNiftiAnalyzeContentsAndCoordinatesTest( argv, 0, - itk::SpatialOrientationEnums::ValidCoordinateOrientations::ITK_COORDINATE_ORIENTATION_RPI, + itk::DICOMOrientation(itk::DICOMOrientation::CoordinateEnum::RightToLeft, + itk::DICOMOrientation::CoordinateEnum::PosteriorToAnterior, + itk::DICOMOrientation::CoordinateEnum::InferiorToSuperior), itk::NiftiImageIOEnums::Analyze75Flavor::AnalyzeFSL, true) == EXIT_FAILURE From 4ae42c89570cecfea3e5d1f521b1ca4e6748dcb8 Mon Sep 17 00:00:00 2001 From: Bradley Lowekamp Date: Mon, 12 Aug 2024 12:00:40 -0400 Subject: [PATCH 09/10] ENH: Use Variable Templates for coordinate enum values. Co-Author: Niels Dekker --- .../Core/Common/include/itkDICOMOrientation.h | 250 ++++++++++++------ .../Core/Common/src/itkDICOMOrientation.cxx | 44 +-- 2 files changed, 191 insertions(+), 103 deletions(-) diff --git a/Modules/Core/Common/include/itkDICOMOrientation.h b/Modules/Core/Common/include/itkDICOMOrientation.h index 924113babe9..45895d72ba5 100644 --- a/Modules/Core/Common/include/itkDICOMOrientation.h +++ b/Modules/Core/Common/include/itkDICOMOrientation.h @@ -21,7 +21,7 @@ #include "ITKCommonExport.h" #include "itkImageBase.h" #ifndef ITK_FUTURE_LEGACY_REMOVE -#include "itkSpatialOrientation.h" +# include "itkSpatialOrientation.h" #endif #include #include @@ -42,9 +42,9 @@ namespace itk class ITKCommon_EXPORT DICOMOrientation { public: - constexpr static unsigned int Dimension = 3; + static constexpr unsigned int Dimension = 3; using DirectionType = typename ImageBase::DirectionType; - constexpr static unsigned int ImageDimension = Dimension; + static constexpr unsigned int ImageDimension = Dimension; #ifndef ITK_FUTURE_LEGACY_REMOVE using LegacyOrientationType = SpatialOrientationEnums::ValidCoordinateOrientations; @@ -53,17 +53,17 @@ class ITKCommon_EXPORT DICOMOrientation enum class CoordinateEnum : uint8_t { UNKNOWN = 0, - Left = 2, ///< 0b0010 + Left = 2, ///< 0b0010 RightToLeft = 2, Right = 3, LeftToRight = 3, - Anterior = 4, ///< front - 0b0100 + Anterior = 4, ///< front - 0b0100 PosteriorToAnterior = 4, Posterior = 5, ///< back AnteriorToPosterior = 5, - Superior = 8, ///< above - 0b1000 + Superior = 8, ///< above - 0b1000 InferiorToSuperior = 8, - Inferior = 9, ///< bottom + Inferior = 9, ///< bottom SuperiorToInferior = 9, }; @@ -75,93 +75,175 @@ class ITKCommon_EXPORT DICOMOrientation TertiaryMinor = 16 }; - constexpr static uint32_t - toOrientation(const CoordinateEnum primary, const CoordinateEnum secondary, const CoordinateEnum tertiary) - { - return (static_cast(primary) << static_cast(CoordinateMajornessTermsEnum::PrimaryMinor)) + - (static_cast(secondary) << static_cast(CoordinateMajornessTermsEnum::SecondaryMinor)) + - (static_cast(tertiary) << static_cast(CoordinateMajornessTermsEnum::TertiaryMinor)); - } + template + static constexpr uint32_t m_ToOrientation = + (static_cast(VPrimary) << static_cast(CoordinateMajornessTermsEnum::PrimaryMinor)) + + (static_cast(VSecondary) << static_cast(CoordinateMajornessTermsEnum::SecondaryMinor)) + + (static_cast(VTertiary) << static_cast(CoordinateMajornessTermsEnum::TertiaryMinor)); CoordinateEnum GetCoordinateTerm(CoordinateMajornessTermsEnum cmt) const; public: -#if 0 -// When this is enabled the following error occurs: -//ITK/Modules/Core/Common/include/itkDICOMOrientation.h:93:11: error: enumerator value is not a constant expression -// RIP = ITK_ORIENTATIONENUM(CoordinateEnum::Right, CoordinateEnum::Inferior, CoordinateEnum::Posterior), - -# define ITK_ORIENTATIONENUM DICOMOrientation::toOrientation -#else +#define ITK_ORIENTATION_ENUM(P, S, T) m_ToOrientation -# define ITK_ORIENTATIONENUM(P, S, T) \ - (static_cast(P) << static_cast(CoordinateMajornessTermsEnum::PrimaryMinor)) + \ - (static_cast(S) << static_cast(CoordinateMajornessTermsEnum::SecondaryMinor)) + \ - (static_cast(T) << static_cast(CoordinateMajornessTermsEnum::TertiaryMinor)) - -#endif enum class OrientationEnum : uint32_t { INVALID = 0, - RIP = ITK_ORIENTATIONENUM(CoordinateEnum::LeftToRight, CoordinateEnum::SuperiorToInferior, CoordinateEnum::AnteriorToPosterior), - LIP = ITK_ORIENTATIONENUM(CoordinateEnum::RightToLeft, CoordinateEnum::SuperiorToInferior, CoordinateEnum::AnteriorToPosterior), - RSP = ITK_ORIENTATIONENUM(CoordinateEnum::LeftToRight, CoordinateEnum::InferiorToSuperior, CoordinateEnum::AnteriorToPosterior), - LSP = ITK_ORIENTATIONENUM(CoordinateEnum::RightToLeft, CoordinateEnum::InferiorToSuperior, CoordinateEnum::AnteriorToPosterior), - RIA = ITK_ORIENTATIONENUM(CoordinateEnum::LeftToRight, CoordinateEnum::SuperiorToInferior, CoordinateEnum::PosteriorToAnterior), - LIA = ITK_ORIENTATIONENUM(CoordinateEnum::RightToLeft, CoordinateEnum::SuperiorToInferior, CoordinateEnum::PosteriorToAnterior), - RSA = ITK_ORIENTATIONENUM(CoordinateEnum::LeftToRight, CoordinateEnum::InferiorToSuperior, CoordinateEnum::PosteriorToAnterior), - LSA = ITK_ORIENTATIONENUM(CoordinateEnum::RightToLeft, CoordinateEnum::InferiorToSuperior, CoordinateEnum::PosteriorToAnterior), - - IRP = ITK_ORIENTATIONENUM(CoordinateEnum::SuperiorToInferior, CoordinateEnum::LeftToRight, CoordinateEnum::AnteriorToPosterior), - ILP = ITK_ORIENTATIONENUM(CoordinateEnum::SuperiorToInferior, CoordinateEnum::RightToLeft, CoordinateEnum::AnteriorToPosterior), - SRP = ITK_ORIENTATIONENUM(CoordinateEnum::InferiorToSuperior, CoordinateEnum::LeftToRight, CoordinateEnum::AnteriorToPosterior), - SLP = ITK_ORIENTATIONENUM(CoordinateEnum::InferiorToSuperior, CoordinateEnum::RightToLeft, CoordinateEnum::AnteriorToPosterior), - IRA = ITK_ORIENTATIONENUM(CoordinateEnum::SuperiorToInferior, CoordinateEnum::LeftToRight, CoordinateEnum::PosteriorToAnterior), - ILA = ITK_ORIENTATIONENUM(CoordinateEnum::SuperiorToInferior, CoordinateEnum::RightToLeft, CoordinateEnum::PosteriorToAnterior), - SRA = ITK_ORIENTATIONENUM(CoordinateEnum::InferiorToSuperior, CoordinateEnum::LeftToRight, CoordinateEnum::PosteriorToAnterior), - SLA = ITK_ORIENTATIONENUM(CoordinateEnum::InferiorToSuperior, CoordinateEnum::RightToLeft, CoordinateEnum::PosteriorToAnterior), - - RPI = ITK_ORIENTATIONENUM(CoordinateEnum::LeftToRight, CoordinateEnum::AnteriorToPosterior, CoordinateEnum::SuperiorToInferior), - LPI = ITK_ORIENTATIONENUM(CoordinateEnum::RightToLeft, CoordinateEnum::AnteriorToPosterior, CoordinateEnum::SuperiorToInferior), - RAI = ITK_ORIENTATIONENUM(CoordinateEnum::LeftToRight, CoordinateEnum::PosteriorToAnterior, CoordinateEnum::SuperiorToInferior), - LAI = ITK_ORIENTATIONENUM(CoordinateEnum::RightToLeft, CoordinateEnum::PosteriorToAnterior, CoordinateEnum::SuperiorToInferior), - RPS = ITK_ORIENTATIONENUM(CoordinateEnum::LeftToRight, CoordinateEnum::AnteriorToPosterior, CoordinateEnum::InferiorToSuperior), - LPS = ITK_ORIENTATIONENUM(CoordinateEnum::RightToLeft, CoordinateEnum::AnteriorToPosterior, CoordinateEnum::InferiorToSuperior), - RAS = ITK_ORIENTATIONENUM(CoordinateEnum::LeftToRight, CoordinateEnum::PosteriorToAnterior, CoordinateEnum::InferiorToSuperior), - LAS = ITK_ORIENTATIONENUM(CoordinateEnum::RightToLeft, CoordinateEnum::PosteriorToAnterior, CoordinateEnum::InferiorToSuperior), - - PRI = ITK_ORIENTATIONENUM(CoordinateEnum::AnteriorToPosterior, CoordinateEnum::LeftToRight, CoordinateEnum::SuperiorToInferior), - PLI = ITK_ORIENTATIONENUM(CoordinateEnum::AnteriorToPosterior, CoordinateEnum::RightToLeft, CoordinateEnum::SuperiorToInferior), - ARI = ITK_ORIENTATIONENUM(CoordinateEnum::PosteriorToAnterior, CoordinateEnum::LeftToRight, CoordinateEnum::SuperiorToInferior), - ALI = ITK_ORIENTATIONENUM(CoordinateEnum::PosteriorToAnterior, CoordinateEnum::RightToLeft, CoordinateEnum::SuperiorToInferior), - PRS = ITK_ORIENTATIONENUM(CoordinateEnum::AnteriorToPosterior, CoordinateEnum::LeftToRight, CoordinateEnum::InferiorToSuperior), - PLS = ITK_ORIENTATIONENUM(CoordinateEnum::AnteriorToPosterior, CoordinateEnum::RightToLeft, CoordinateEnum::InferiorToSuperior), - ARS = ITK_ORIENTATIONENUM(CoordinateEnum::PosteriorToAnterior, CoordinateEnum::LeftToRight, CoordinateEnum::InferiorToSuperior), - ALS = ITK_ORIENTATIONENUM(CoordinateEnum::PosteriorToAnterior, CoordinateEnum::RightToLeft, CoordinateEnum::InferiorToSuperior), - - IPR = ITK_ORIENTATIONENUM(CoordinateEnum::SuperiorToInferior, CoordinateEnum::AnteriorToPosterior, CoordinateEnum::LeftToRight), - SPR = ITK_ORIENTATIONENUM(CoordinateEnum::InferiorToSuperior, CoordinateEnum::AnteriorToPosterior, CoordinateEnum::LeftToRight), - IAR = ITK_ORIENTATIONENUM(CoordinateEnum::SuperiorToInferior, CoordinateEnum::PosteriorToAnterior, CoordinateEnum::LeftToRight), - SAR = ITK_ORIENTATIONENUM(CoordinateEnum::InferiorToSuperior, CoordinateEnum::PosteriorToAnterior, CoordinateEnum::LeftToRight), - IPL = ITK_ORIENTATIONENUM(CoordinateEnum::SuperiorToInferior, CoordinateEnum::AnteriorToPosterior, CoordinateEnum::RightToLeft), - SPL = ITK_ORIENTATIONENUM(CoordinateEnum::InferiorToSuperior, CoordinateEnum::AnteriorToPosterior, CoordinateEnum::RightToLeft), - IAL = ITK_ORIENTATIONENUM(CoordinateEnum::SuperiorToInferior, CoordinateEnum::PosteriorToAnterior, CoordinateEnum::RightToLeft), - SAL = ITK_ORIENTATIONENUM(CoordinateEnum::InferiorToSuperior, CoordinateEnum::PosteriorToAnterior, CoordinateEnum::RightToLeft), - - PIR = ITK_ORIENTATIONENUM(CoordinateEnum::AnteriorToPosterior, CoordinateEnum::SuperiorToInferior, CoordinateEnum::LeftToRight), - PSR = ITK_ORIENTATIONENUM(CoordinateEnum::AnteriorToPosterior, CoordinateEnum::InferiorToSuperior, CoordinateEnum::LeftToRight), - AIR = ITK_ORIENTATIONENUM(CoordinateEnum::PosteriorToAnterior, CoordinateEnum::SuperiorToInferior, CoordinateEnum::LeftToRight), - ASR = ITK_ORIENTATIONENUM(CoordinateEnum::PosteriorToAnterior, CoordinateEnum::InferiorToSuperior, CoordinateEnum::LeftToRight), - PIL = ITK_ORIENTATIONENUM(CoordinateEnum::AnteriorToPosterior, CoordinateEnum::SuperiorToInferior, CoordinateEnum::RightToLeft), - PSL = ITK_ORIENTATIONENUM(CoordinateEnum::AnteriorToPosterior, CoordinateEnum::InferiorToSuperior, CoordinateEnum::RightToLeft), - AIL = ITK_ORIENTATIONENUM(CoordinateEnum::PosteriorToAnterior, CoordinateEnum::SuperiorToInferior, CoordinateEnum::RightToLeft), - ASL = ITK_ORIENTATIONENUM(CoordinateEnum::PosteriorToAnterior, CoordinateEnum::InferiorToSuperior, CoordinateEnum::RightToLeft) + RIP = ITK_ORIENTATION_ENUM(CoordinateEnum::LeftToRight, + CoordinateEnum::SuperiorToInferior, + CoordinateEnum::AnteriorToPosterior), + LIP = ITK_ORIENTATION_ENUM(CoordinateEnum::RightToLeft, + CoordinateEnum::SuperiorToInferior, + CoordinateEnum::AnteriorToPosterior), + RSP = ITK_ORIENTATION_ENUM(CoordinateEnum::LeftToRight, + CoordinateEnum::InferiorToSuperior, + CoordinateEnum::AnteriorToPosterior), + LSP = ITK_ORIENTATION_ENUM(CoordinateEnum::RightToLeft, + CoordinateEnum::InferiorToSuperior, + CoordinateEnum::AnteriorToPosterior), + RIA = ITK_ORIENTATION_ENUM(CoordinateEnum::LeftToRight, + CoordinateEnum::SuperiorToInferior, + CoordinateEnum::PosteriorToAnterior), + LIA = ITK_ORIENTATION_ENUM(CoordinateEnum::RightToLeft, + CoordinateEnum::SuperiorToInferior, + CoordinateEnum::PosteriorToAnterior), + RSA = ITK_ORIENTATION_ENUM(CoordinateEnum::LeftToRight, + CoordinateEnum::InferiorToSuperior, + CoordinateEnum::PosteriorToAnterior), + LSA = ITK_ORIENTATION_ENUM(CoordinateEnum::RightToLeft, + CoordinateEnum::InferiorToSuperior, + CoordinateEnum::PosteriorToAnterior), + + IRP = ITK_ORIENTATION_ENUM(CoordinateEnum::SuperiorToInferior, + CoordinateEnum::LeftToRight, + CoordinateEnum::AnteriorToPosterior), + ILP = ITK_ORIENTATION_ENUM(CoordinateEnum::SuperiorToInferior, + CoordinateEnum::RightToLeft, + CoordinateEnum::AnteriorToPosterior), + SRP = ITK_ORIENTATION_ENUM(CoordinateEnum::InferiorToSuperior, + CoordinateEnum::LeftToRight, + CoordinateEnum::AnteriorToPosterior), + SLP = ITK_ORIENTATION_ENUM(CoordinateEnum::InferiorToSuperior, + CoordinateEnum::RightToLeft, + CoordinateEnum::AnteriorToPosterior), + IRA = ITK_ORIENTATION_ENUM(CoordinateEnum::SuperiorToInferior, + CoordinateEnum::LeftToRight, + CoordinateEnum::PosteriorToAnterior), + ILA = ITK_ORIENTATION_ENUM(CoordinateEnum::SuperiorToInferior, + CoordinateEnum::RightToLeft, + CoordinateEnum::PosteriorToAnterior), + SRA = ITK_ORIENTATION_ENUM(CoordinateEnum::InferiorToSuperior, + CoordinateEnum::LeftToRight, + CoordinateEnum::PosteriorToAnterior), + SLA = ITK_ORIENTATION_ENUM(CoordinateEnum::InferiorToSuperior, + CoordinateEnum::RightToLeft, + CoordinateEnum::PosteriorToAnterior), + + RPI = ITK_ORIENTATION_ENUM(CoordinateEnum::LeftToRight, + CoordinateEnum::AnteriorToPosterior, + CoordinateEnum::SuperiorToInferior), + LPI = ITK_ORIENTATION_ENUM(CoordinateEnum::RightToLeft, + CoordinateEnum::AnteriorToPosterior, + CoordinateEnum::SuperiorToInferior), + RAI = ITK_ORIENTATION_ENUM(CoordinateEnum::LeftToRight, + CoordinateEnum::PosteriorToAnterior, + CoordinateEnum::SuperiorToInferior), + LAI = ITK_ORIENTATION_ENUM(CoordinateEnum::RightToLeft, + CoordinateEnum::PosteriorToAnterior, + CoordinateEnum::SuperiorToInferior), + RPS = ITK_ORIENTATION_ENUM(CoordinateEnum::LeftToRight, + CoordinateEnum::AnteriorToPosterior, + CoordinateEnum::InferiorToSuperior), + LPS = ITK_ORIENTATION_ENUM(CoordinateEnum::RightToLeft, + CoordinateEnum::AnteriorToPosterior, + CoordinateEnum::InferiorToSuperior), + RAS = ITK_ORIENTATION_ENUM(CoordinateEnum::LeftToRight, + CoordinateEnum::PosteriorToAnterior, + CoordinateEnum::InferiorToSuperior), + LAS = ITK_ORIENTATION_ENUM(CoordinateEnum::RightToLeft, + CoordinateEnum::PosteriorToAnterior, + CoordinateEnum::InferiorToSuperior), + + PRI = ITK_ORIENTATION_ENUM(CoordinateEnum::AnteriorToPosterior, + CoordinateEnum::LeftToRight, + CoordinateEnum::SuperiorToInferior), + PLI = ITK_ORIENTATION_ENUM(CoordinateEnum::AnteriorToPosterior, + CoordinateEnum::RightToLeft, + CoordinateEnum::SuperiorToInferior), + ARI = ITK_ORIENTATION_ENUM(CoordinateEnum::PosteriorToAnterior, + CoordinateEnum::LeftToRight, + CoordinateEnum::SuperiorToInferior), + ALI = ITK_ORIENTATION_ENUM(CoordinateEnum::PosteriorToAnterior, + CoordinateEnum::RightToLeft, + CoordinateEnum::SuperiorToInferior), + PRS = ITK_ORIENTATION_ENUM(CoordinateEnum::AnteriorToPosterior, + CoordinateEnum::LeftToRight, + CoordinateEnum::InferiorToSuperior), + PLS = ITK_ORIENTATION_ENUM(CoordinateEnum::AnteriorToPosterior, + CoordinateEnum::RightToLeft, + CoordinateEnum::InferiorToSuperior), + ARS = ITK_ORIENTATION_ENUM(CoordinateEnum::PosteriorToAnterior, + CoordinateEnum::LeftToRight, + CoordinateEnum::InferiorToSuperior), + ALS = ITK_ORIENTATION_ENUM(CoordinateEnum::PosteriorToAnterior, + CoordinateEnum::RightToLeft, + CoordinateEnum::InferiorToSuperior), + + IPR = ITK_ORIENTATION_ENUM(CoordinateEnum::SuperiorToInferior, + CoordinateEnum::AnteriorToPosterior, + CoordinateEnum::LeftToRight), + SPR = ITK_ORIENTATION_ENUM(CoordinateEnum::InferiorToSuperior, + CoordinateEnum::AnteriorToPosterior, + CoordinateEnum::LeftToRight), + IAR = ITK_ORIENTATION_ENUM(CoordinateEnum::SuperiorToInferior, + CoordinateEnum::PosteriorToAnterior, + CoordinateEnum::LeftToRight), + SAR = ITK_ORIENTATION_ENUM(CoordinateEnum::InferiorToSuperior, + CoordinateEnum::PosteriorToAnterior, + CoordinateEnum::LeftToRight), + IPL = ITK_ORIENTATION_ENUM(CoordinateEnum::SuperiorToInferior, + CoordinateEnum::AnteriorToPosterior, + CoordinateEnum::RightToLeft), + SPL = ITK_ORIENTATION_ENUM(CoordinateEnum::InferiorToSuperior, + CoordinateEnum::AnteriorToPosterior, + CoordinateEnum::RightToLeft), + IAL = ITK_ORIENTATION_ENUM(CoordinateEnum::SuperiorToInferior, + CoordinateEnum::PosteriorToAnterior, + CoordinateEnum::RightToLeft), + SAL = ITK_ORIENTATION_ENUM(CoordinateEnum::InferiorToSuperior, + CoordinateEnum::PosteriorToAnterior, + CoordinateEnum::RightToLeft), + + PIR = ITK_ORIENTATION_ENUM(CoordinateEnum::AnteriorToPosterior, + CoordinateEnum::SuperiorToInferior, + CoordinateEnum::LeftToRight), + PSR = ITK_ORIENTATION_ENUM(CoordinateEnum::AnteriorToPosterior, + CoordinateEnum::InferiorToSuperior, + CoordinateEnum::LeftToRight), + AIR = ITK_ORIENTATION_ENUM(CoordinateEnum::PosteriorToAnterior, + CoordinateEnum::SuperiorToInferior, + CoordinateEnum::LeftToRight), + ASR = ITK_ORIENTATION_ENUM(CoordinateEnum::PosteriorToAnterior, + CoordinateEnum::InferiorToSuperior, + CoordinateEnum::LeftToRight), + PIL = ITK_ORIENTATION_ENUM(CoordinateEnum::AnteriorToPosterior, + CoordinateEnum::SuperiorToInferior, + CoordinateEnum::RightToLeft), + PSL = ITK_ORIENTATION_ENUM(CoordinateEnum::AnteriorToPosterior, + CoordinateEnum::InferiorToSuperior, + CoordinateEnum::RightToLeft), + AIL = ITK_ORIENTATION_ENUM(CoordinateEnum::PosteriorToAnterior, + CoordinateEnum::SuperiorToInferior, + CoordinateEnum::RightToLeft), + ASL = ITK_ORIENTATION_ENUM(CoordinateEnum::PosteriorToAnterior, + CoordinateEnum::InferiorToSuperior, + CoordinateEnum::RightToLeft) }; -#undef ITK_ORIENTATIONENUM +#undef ITK_ORIENTATION_ENUM /** \brief Initialize with CoordinateEnum's from separate axes. @@ -241,7 +323,7 @@ class ITKCommon_EXPORT DICOMOrientation friend ITKCommon_EXPORT std::ostream & - operator<<(std::ostream & out, OrientationEnum value); + operator<<(std::ostream & out, OrientationEnum value); private: @@ -268,10 +350,10 @@ class ITKCommon_EXPORT DICOMOrientation ITKCommon_EXPORT std::ostream & - operator<<(std::ostream & out, typename DICOMOrientation::OrientationEnum value); + operator<<(std::ostream & out, typename DICOMOrientation::OrientationEnum value); ITKCommon_EXPORT std::ostream & - operator<<(std::ostream & out, const DICOMOrientation & orientation); + operator<<(std::ostream & out, const DICOMOrientation & orientation); } // end namespace itk diff --git a/Modules/Core/Common/src/itkDICOMOrientation.cxx b/Modules/Core/Common/src/itkDICOMOrientation.cxx index 62c1decfeb2..5f1cd1f67ee 100644 --- a/Modules/Core/Common/src/itkDICOMOrientation.cxx +++ b/Modules/Core/Common/src/itkDICOMOrientation.cxx @@ -17,7 +17,7 @@ *=========================================================================*/ #include "itkDICOMOrientation.h" #ifndef ITK_FUTURE_LEGACY_REMOVE -#include "itkSpatialOrientationAdapter.h" +# include "itkSpatialOrientationAdapter.h" #endif namespace itk @@ -33,7 +33,10 @@ DICOMOrientation::DICOMOrientation(CoordinateEnum primary, CoordinateEnum second } else { - m_Value = static_cast(toOrientation(primary, secondary, tertiary)); + m_Value = static_cast( + (static_cast(primary) << static_cast(CoordinateMajornessTermsEnum::PrimaryMinor)) + + (static_cast(secondary) << static_cast(CoordinateMajornessTermsEnum::SecondaryMinor)) + + (static_cast(tertiary) << static_cast(CoordinateMajornessTermsEnum::TertiaryMinor))); } } @@ -52,7 +55,7 @@ DICOMOrientation::DICOMOrientation(std::string str) #ifndef ITK_FUTURE_LEGACY_REMOVE DICOMOrientation::DICOMOrientation(LegacyOrientationType legacyOrientation) -: DICOMOrientation(SpatialOrientationAdapter::ToDirectionCosines(legacyOrientation)) + : DICOMOrientation(SpatialOrientationAdapter::ToDirectionCosines(legacyOrientation)) {} #endif @@ -176,19 +179,19 @@ DICOMOrientation::DirectionCosinesToOrientation(const DirectionType & dir) // but it is DIFFERENT in the meaning of direction in terms of sign-ness. CoordinateEnum terms[3] = { CoordinateEnum::UNKNOWN, CoordinateEnum::UNKNOWN, CoordinateEnum::UNKNOWN }; - std::multimap< double, std::pair > value_to_idx; - for (unsigned int c = 0; c < 3; ++c ) + std::multimap> value_to_idx; + for (unsigned int c = 0; c < 3; ++c) + { + for (unsigned int r = 0; r < 3; ++r) { - for (unsigned int r = 0; r < 3; ++r ) - { - value_to_idx.emplace(std::abs(dir[c][r]), std::make_pair(c,r)); - } + value_to_idx.emplace(std::abs(dir[c][r]), std::make_pair(c, r)); } + } for (unsigned i = 0; i < 3; ++i) - { + { - auto max_idx = value_to_idx.rbegin()->second; + auto max_idx = value_to_idx.rbegin()->second; const unsigned int max_c = max_idx.first; const unsigned int max_r = max_idx.second; @@ -196,29 +199,32 @@ DICOMOrientation::DirectionCosinesToOrientation(const DirectionType & dir) for (auto it = value_to_idx.begin(); it != value_to_idx.end();) { - if (it->second.first == max_c || it->second.second == max_r) + if (it->second.first == max_c || it->second.second == max_r) { - value_to_idx.erase(it++); + value_to_idx.erase(it++); } - else + else { - ++it; + ++it; } } switch (max_c) { - case 0: { + case 0: + { // When the dominant axis sign is positive, assign the coordinate for the direction we are increasing towards. // ITK is in LPS, so that is the positive direction terms[max_r] = (max_sgn == 1) ? CoordinateEnum::RightToLeft : CoordinateEnum::LeftToRight; break; } - case 1: { + case 1: + { terms[max_r] = (max_sgn == 1) ? CoordinateEnum::AnteriorToPosterior : CoordinateEnum::PosteriorToAnterior; break; } - case 2: { + case 2: + { terms[max_r] = (max_sgn == 1) ? CoordinateEnum::InferiorToSuperior : CoordinateEnum::SuperiorToInferior; break; } @@ -227,7 +233,7 @@ DICOMOrientation::DirectionCosinesToOrientation(const DirectionType & dir) } } - return static_cast(toOrientation(terms[0], terms[1], terms[2])); + return DICOMOrientation(terms[0], terms[1], terms[2]); } From d7425ef0043e496af70c03b5bd0e9daa6a35bca0 Mon Sep 17 00:00:00 2001 From: Bradley Lowekamp Date: Tue, 13 Aug 2024 09:29:27 -0400 Subject: [PATCH 10/10] BUG: Remove default initialization in struct Addresses: warning: 'void* memset(void*, int, size_t)' clearing an object of non-trivial type 'struct GEImageHeader'; use assignment or value-initialization instead [-Wclass-memaccess] --- Modules/IO/IPL/include/itkGEImageHeader.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Modules/IO/IPL/include/itkGEImageHeader.h b/Modules/IO/IPL/include/itkGEImageHeader.h index 6796bba64a7..d63d8671faa 100644 --- a/Modules/IO/IPL/include/itkGEImageHeader.h +++ b/Modules/IO/IPL/include/itkGEImageHeader.h @@ -86,7 +86,7 @@ struct GEImageHeader short imageYsize; float imageXres; float imageYres; - itk::DICOMOrientation::OrientationEnum coordinateOrientation{ itk::DICOMOrientation::OrientationEnum::INVALID }; + itk::DICOMOrientation::OrientationEnum coordinateOrientation; // uint32_t short numberOfSlices; short offset; char filename[itk::IOCommon::ITK_MAXPATHLEN + 1];