diff --git a/Modules/DICOMReader/files.cmake b/Modules/DICOMReader/files.cmake index edba81d2f8..a0e0c6b141 100644 --- a/Modules/DICOMReader/files.cmake +++ b/Modules/DICOMReader/files.cmake @@ -1,34 +1,36 @@ set(H_FILES mitkDICOMFileReader.h mitkDICOMImageFrameInfo.h mitkDICOMImageBlockDescriptor.h mitkDICOMGDCMImageFrameInfo.h mitkDICOMITKSeriesGDCMReader.h mitkDICOMDatasetSorter.h mitkDICOMFilenameSorter.h mitkDICOMEnums.h mitkDICOMTagBasedSorter.h mitkDICOMSortCriterion.h mitkDICOMSortByTag.h mitkEquiDistantBlocksSorter.h + mitkSortByImagePositionPatient.h mitkClassicDICOMSeriesReader.h mitkDICOMTag.h ) set(CPP_FILES mitkDICOMFileReader.cpp mitkDICOMImageBlockDescriptor.cpp mitkDICOMITKSeriesGDCMReader.cpp mitkDICOMDatasetSorter.cpp mitkDICOMFilenameSorter.cpp mitkDICOMTagBasedSorter.cpp mitkDICOMGDCMImageFrameInfo.cpp mitkDICOMImageFrameInfo.cpp mitkDICOMSortCriterion.cpp mitkDICOMSortByTag.cpp mitkITKDICOMSeriesReaderHelper.cpp mitkEquiDistantBlocksSorter.cpp + mitkSortByImagePositionPatient.cpp mitkGantryTiltInformation.cpp mitkClassicDICOMSeriesReader.cpp mitkDICOMTag.cpp ) diff --git a/Modules/DICOMReader/mitkClassicDICOMSeriesReader.cpp b/Modules/DICOMReader/mitkClassicDICOMSeriesReader.cpp index 5295a828d5..5e8e7681a5 100644 --- a/Modules/DICOMReader/mitkClassicDICOMSeriesReader.cpp +++ b/Modules/DICOMReader/mitkClassicDICOMSeriesReader.cpp @@ -1,79 +1,80 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkClassicDICOMSeriesReader.h" #include "mitkDICOMTagBasedSorter.h" #include "mitkDICOMSortByTag.h" +#include "mitkSortByImagePositionPatient.h" mitk::ClassicDICOMSeriesReader ::ClassicDICOMSeriesReader() :DICOMITKSeriesGDCMReader() { mitk::DICOMTagBasedSorter::Pointer tagSorter = mitk::DICOMTagBasedSorter::New(); // all the things that split by tag in mitk::DicomSeriesReader tagSorter->AddDistinguishingTag( DICOMTag(0x0028, 0x0010) ); // Number of Rows tagSorter->AddDistinguishingTag( DICOMTag(0x0028, 0x0011) ); // Number of Columns tagSorter->AddDistinguishingTag( DICOMTag(0x0028, 0x0030) ); // Pixel Spacing tagSorter->AddDistinguishingTag( DICOMTag(0x0018, 0x1164) ); // Imager Pixel Spacing tagSorter->AddDistinguishingTag( DICOMTag(0x0020, 0x0037) ); // Image Orientation (Patient) tagSorter->AddDistinguishingTag( DICOMTag(0x0020, 0x000e) ); // Series Instance UID tagSorter->AddDistinguishingTag( DICOMTag(0x0018, 0x0050) ); // Slice Thickness tagSorter->AddDistinguishingTag( DICOMTag(0x0028, 0x0008) ); // Number of Frames tagSorter->AddDistinguishingTag( DICOMTag(0x0020, 0x0052) ); // Frame of Reference UID // a sorter... // TODO ugly syntax, improve.. mitk::DICOMSortCriterion::ConstPointer sorting = - mitk::DICOMSortByTag::New( DICOMTag(0x0020, 0x0013), // instance number + mitk::SortByImagePositionPatient::New( // image position patient and image orientation mitk::DICOMSortByTag::New( DICOMTag(0x0020, 0x0012), // aqcuisition number mitk::DICOMSortByTag::New( DICOMTag(0x0008, 0x0032), // aqcuisition time mitk::DICOMSortByTag::New( DICOMTag(0x0018, 0x1060), // trigger time mitk::DICOMSortByTag::New( DICOMTag(0x0008, 0x0018) // SOP instance UID (last resort, not really meaningful but decides clearly) ).GetPointer() ).GetPointer() ).GetPointer() ).GetPointer() ).GetPointer(); tagSorter->SetSortCriterion( sorting ); // define above sorting for this class this->AddSortingElement( tagSorter ); } mitk::ClassicDICOMSeriesReader ::ClassicDICOMSeriesReader(const ClassicDICOMSeriesReader& other ) :DICOMITKSeriesGDCMReader(other) { } mitk::ClassicDICOMSeriesReader ::~ClassicDICOMSeriesReader() { } mitk::ClassicDICOMSeriesReader& mitk::ClassicDICOMSeriesReader ::operator=(const ClassicDICOMSeriesReader& other) { if (this != &other) { DICOMITKSeriesGDCMReader::operator=(other); } return *this; } diff --git a/Modules/DICOMReader/mitkDICOMSortByTag.cpp b/Modules/DICOMReader/mitkDICOMSortByTag.cpp index 0b2d3b8e24..87f8644811 100644 --- a/Modules/DICOMReader/mitkDICOMSortByTag.cpp +++ b/Modules/DICOMReader/mitkDICOMSortByTag.cpp @@ -1,133 +1,119 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkDICOMSortByTag.h" mitk::DICOMSortByTag ::DICOMSortByTag(const DICOMTag& tag, DICOMSortCriterion::Pointer secondaryCriterion) :DICOMSortCriterion(secondaryCriterion) ,m_Tag(tag) { } mitk::DICOMSortByTag ::~DICOMSortByTag() { } mitk::DICOMSortByTag ::DICOMSortByTag(const DICOMSortByTag& other ) :DICOMSortCriterion(other) ,m_Tag(other.m_Tag) { } mitk::DICOMSortByTag& mitk::DICOMSortByTag ::operator=(const DICOMSortByTag& other) { if (this != &other) { DICOMSortCriterion::operator=(other); m_Tag = other.m_Tag; } return *this; } mitk::DICOMTagList mitk::DICOMSortByTag ::GetTagsOfInterest() const { DICOMTagList list; list.push_back(m_Tag); return list; } bool mitk::DICOMSortByTag ::IsLeftBeforeRight(const mitk::DICOMDatasetAccess* left, const mitk::DICOMDatasetAccess* right) const { return this->NumericCompare(left, right, m_Tag); } bool mitk::DICOMSortByTag ::StringCompare(const mitk::DICOMDatasetAccess* left, const mitk::DICOMDatasetAccess* right, const DICOMTag& tag) const { assert(left); assert(right); std::string leftString = left->GetTagValueAsString(tag); std::string rightString = right->GetTagValueAsString(tag); if (leftString != rightString) { return leftString.compare(rightString) < 0; } else { - if (m_SecondaryCriterion.IsNotNull()) - { - return m_SecondaryCriterion->IsLeftBeforeRight(left, right); - } - else - { - return leftString.compare(rightString) < 0; // TODO last resort needed - } + return this->NextLevelIsLeftBeforeRight(left, right); } } bool mitk::DICOMSortByTag ::NumericCompare(const mitk::DICOMDatasetAccess* left, const mitk::DICOMDatasetAccess* right, const DICOMTag& tag) const { assert(left); assert(right); std::string leftString = left->GetTagValueAsString(tag); std::string rightString = right->GetTagValueAsString(tag); const char* leftInput(leftString.c_str()); const char* rightInput(rightString.c_str()); char* leftEnd(NULL); char* rightEnd(NULL); double leftDouble = strtod(leftInput, &leftEnd); double rightDouble = strtod(rightInput, &rightEnd); if (leftEnd == leftInput || rightEnd == rightInput) // no numerical conversion.. { return this->StringCompare(left,right, tag); // fallback to string compare } else { if (leftDouble != rightDouble) // can we decide? { return leftDouble < rightDouble; } else // ask secondary criterion { - if (m_SecondaryCriterion.IsNotNull()) - { - return m_SecondaryCriterion->IsLeftBeforeRight(left, right); - } - else - { - return leftDouble < rightDouble; // TODO last resort - } + return this->NextLevelIsLeftBeforeRight(left, right); } } } diff --git a/Modules/DICOMReader/mitkDICOMSortCriterion.cpp b/Modules/DICOMReader/mitkDICOMSortCriterion.cpp index b26a5b5f6e..bbeebb07f0 100644 --- a/Modules/DICOMReader/mitkDICOMSortCriterion.cpp +++ b/Modules/DICOMReader/mitkDICOMSortCriterion.cpp @@ -1,65 +1,79 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkDICOMSortCriterion.h" mitk::DICOMSortCriterion ::DICOMSortCriterion(DICOMSortCriterion::Pointer secondaryCriterion) :itk::LightObject() ,m_SecondaryCriterion(secondaryCriterion) { } mitk::DICOMSortCriterion ::~DICOMSortCriterion() { } mitk::DICOMSortCriterion ::DICOMSortCriterion(const DICOMSortCriterion& other ) :itk::LightObject() ,m_SecondaryCriterion(other.m_SecondaryCriterion) { } mitk::DICOMSortCriterion& mitk::DICOMSortCriterion ::operator=(const DICOMSortCriterion& other) { if (this != &other) { m_SecondaryCriterion = other.m_SecondaryCriterion; } return *this; } mitk::DICOMTagList mitk::DICOMSortCriterion ::GetAllTagsOfInterest() const { DICOMTagList allTags; // iterate all secondary criteria, return all tags const DICOMSortCriterion* criterionToCheck = this; while (criterionToCheck) { DICOMTagList newElements = criterionToCheck->GetTagsOfInterest(); allTags.insert( allTags.end(), newElements.begin(), newElements.end() ); // append criterionToCheck = criterionToCheck->m_SecondaryCriterion.GetPointer(); } return allTags; } + +bool +mitk::DICOMSortCriterion +::NextLevelIsLeftBeforeRight(const mitk::DICOMDatasetAccess* left, const mitk::DICOMDatasetAccess* right) const +{ + if (m_SecondaryCriterion.IsNotNull()) + { + return m_SecondaryCriterion->IsLeftBeforeRight(left, right); + } + else + { + return (void*)left < (void*)right; + } +} diff --git a/Modules/DICOMReader/mitkDICOMSortCriterion.h b/Modules/DICOMReader/mitkDICOMSortCriterion.h index 3ba6ae2ce3..061ac3b542 100644 --- a/Modules/DICOMReader/mitkDICOMSortCriterion.h +++ b/Modules/DICOMReader/mitkDICOMSortCriterion.h @@ -1,52 +1,54 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef mitkDICOMSortCriterion_h #define mitkDICOMSortCriterion_h #include "itkObjectFactory.h" #include "mitkCommon.h" #include "mitkDICOMDatasetAccess.h" namespace mitk { class DICOMReader_EXPORT DICOMSortCriterion : public itk::LightObject { public: mitkClassMacro( DICOMSortCriterion, itk::LightObject ); DICOMTagList GetAllTagsOfInterest() const; virtual DICOMTagList GetTagsOfInterest() const = 0; virtual bool IsLeftBeforeRight(const mitk::DICOMDatasetAccess* left, const mitk::DICOMDatasetAccess* right) const = 0; protected: DICOMSortCriterion( DICOMSortCriterion::Pointer secondaryCriterion ); virtual ~DICOMSortCriterion(); + bool NextLevelIsLeftBeforeRight(const mitk::DICOMDatasetAccess* left, const mitk::DICOMDatasetAccess* right) const; + DICOMSortCriterion(const DICOMSortCriterion& other); DICOMSortCriterion& operator=(const DICOMSortCriterion& other); DICOMSortCriterion::Pointer m_SecondaryCriterion; }; } #endif diff --git a/Modules/DICOMReader/mitkSortByImagePositionPatient.cpp b/Modules/DICOMReader/mitkSortByImagePositionPatient.cpp new file mode 100644 index 0000000000..f62d888320 --- /dev/null +++ b/Modules/DICOMReader/mitkSortByImagePositionPatient.cpp @@ -0,0 +1,195 @@ +/*=================================================================== + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center, +Division of Medical and Biological Informatics. +All rights reserved. + +This software is distributed WITHOUT ANY WARRANTY; without +even the implied warranty of MERCHANTABILITY or FITNESS FOR +A PARTICULAR PURPOSE. + +See LICENSE.txt or http://www.mitk.org for details. + +===================================================================*/ + +#include "mitkSortByImagePositionPatient.h" + +mitk::SortByImagePositionPatient +::SortByImagePositionPatient(DICOMSortCriterion::Pointer secondaryCriterion) +:DICOMSortCriterion(secondaryCriterion) +{ +} + +mitk::SortByImagePositionPatient +::~SortByImagePositionPatient() +{ +} + +mitk::SortByImagePositionPatient +::SortByImagePositionPatient(const SortByImagePositionPatient& other ) +:DICOMSortCriterion(other) +{ +} + +mitk::SortByImagePositionPatient& +mitk::SortByImagePositionPatient +::operator=(const SortByImagePositionPatient& other) +{ + if (this != &other) + { + DICOMSortCriterion::operator=(other); + } + return *this; +} + +mitk::DICOMTagList +mitk::SortByImagePositionPatient +::GetTagsOfInterest() const +{ + DICOMTagList tags; + tags.push_back( DICOMTag(0x0020, 0x0032) ); // ImagePositionPatient + tags.push_back( DICOMTag(0x0020, 0x0037) ); // ImageOrientationPatient + + return tags; +} + +mitk::Point3D +mitk::SortByImagePositionPatient +::DICOMStringToPoint3D(const std::string& s, bool& successful) const +{ + Point3D p; + successful = true; + + std::istringstream originReader(s); + std::string coordinate; + unsigned int dim(0); + while( std::getline( originReader, coordinate, '\\' ) && dim < 3) + { + p[dim++]= atof(coordinate.c_str()); + } + + if (dim && dim != 3) + { + successful = false; + MITK_ERROR << "Reader implementation made wrong assumption on tag (0020,0032). Found " << dim << " instead of 3 values."; + } + else if (dim == 0) + { + successful = false; + p.Fill(0.0); // assume default (0,0,0) + } + + return p; +} + + +void +mitk::SortByImagePositionPatient +::DICOMStringToOrientationVectors(const std::string& s, Vector3D& right, Vector3D& up, bool& successful) const +{ + successful = true; + + std::istringstream orientationReader(s); + std::string coordinate; + unsigned int dim(0); + while( std::getline( orientationReader, coordinate, '\\' ) && dim < 6 ) + { + if (dim<3) + { + right[dim++] = atof(coordinate.c_str()); + } + else + { + up[dim++ - 3] = atof(coordinate.c_str()); + } + } + + if (dim && dim != 6) + { + successful = false; + MITK_ERROR << "Reader implementation made wrong assumption on tag (0020,0037). Found " << dim << " instead of 6 values."; + } + else if (dim == 0) + { + // fill with defaults + right.Fill(0.0); + right[0] = 1.0; + + up.Fill(0.0); + up[1] = 1.0; + + successful = false; + } +} + + +bool +mitk::SortByImagePositionPatient +::IsLeftBeforeRight(const mitk::DICOMDatasetAccess* left, const mitk::DICOMDatasetAccess* right) const +{ + // sort by distance to world origin, assuming (almost) equal orientation + static const DICOMTag tagImagePositionPatient = DICOMTag(0x0020,0x0032); // Image Position (Patient) + static const DICOMTag tagImageOrientation = DICOMTag(0x0020, 0x0037); // Image Orientation + + static Vector3D leftRight; leftRight.Fill(0.0); + static Vector3D leftUp; leftUp.Fill(0.0); + static bool leftHasOrientation(false); + DICOMStringToOrientationVectors( left->GetTagValueAsString( tagImageOrientation ), + leftRight, leftUp, leftHasOrientation ); + + static Vector3D rightRight; rightRight.Fill(0.0); + static Vector3D rightUp; rightUp.Fill(0.0); + static bool rightHasOrientation(false); + DICOMStringToOrientationVectors( right->GetTagValueAsString( tagImageOrientation ), + rightRight, rightUp, rightHasOrientation ); + + static Point3D leftOrigin; leftOrigin.Fill(0.0f); + static bool leftHasOrigin(false); + leftOrigin = DICOMStringToPoint3D( left->GetTagValueAsString( tagImagePositionPatient ), leftHasOrigin ); + + static Point3D rightOrigin; rightOrigin.Fill(0.0f); + static bool rightHasOrigin(false); + rightOrigin = DICOMStringToPoint3D( right->GetTagValueAsString( tagImagePositionPatient ), rightHasOrigin ); + + // we tolerate very small differences in image orientation, since we got to know about + // acquisitions where these values change across a single series (7th decimal digit) + // (http://bugs.mitk.org/show_bug.cgi?id=12263) + // still, we want to check if our assumption of 'almost equal' orientations is valid + for (unsigned int dim = 0; dim < 3; ++dim) + { + if ( fabs(leftRight[dim] - rightRight[dim]) > 0.0001 + || fabs(leftUp[dim] - rightUp[dim]) > 0.0001) + { + MITK_ERROR << "Dicom images have different orientations."; + throw std::logic_error("Dicom images have different orientations. Call GetSeries() first to separate images."); + } + } + + static Vector3D normal; + normal[0] = leftRight[1] * leftUp[5] - leftRight[2] * leftUp[4]; + normal[1] = leftRight[2] * leftUp[3] - leftRight[0] * leftUp[5]; + normal[2] = leftRight[0] * leftUp[4] - leftRight[1] * leftUp[3]; + + static double leftDistance = 0.0; + static double rightDistance = 0.0; + + // this computes the distance from world origin (0,0,0) ALONG THE NORMAL of the image planes + for (unsigned int dim = 0; dim < 3; ++dim) + { + leftDistance += normal[dim] * leftOrigin[dim]; + rightDistance += normal[dim] * rightOrigin[dim]; + } + + // if we can sort by just comparing the distance, we do exactly that + if ( fabs(leftDistance - rightDistance) >= mitk::eps) + { + // default: compare position + return leftDistance < rightDistance; + } + else + { + return this->NextLevelIsLeftBeforeRight(left, right); + } +} diff --git a/Modules/DICOMReader/mitkSortByImagePositionPatient.h b/Modules/DICOMReader/mitkSortByImagePositionPatient.h new file mode 100644 index 0000000000..7f0c887c19 --- /dev/null +++ b/Modules/DICOMReader/mitkSortByImagePositionPatient.h @@ -0,0 +1,53 @@ +/*=================================================================== + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center, +Division of Medical and Biological Informatics. +All rights reserved. + +This software is distributed WITHOUT ANY WARRANTY; without +even the implied warranty of MERCHANTABILITY or FITNESS FOR +A PARTICULAR PURPOSE. + +See LICENSE.txt or http://www.mitk.org for details. + +===================================================================*/ + +#ifndef mitkSortByImagePositionPatient_h +#define mitkSortByImagePositionPatient_h + +#include "mitkDICOMSortCriterion.h" + +#include "mitkVector.h" + +namespace mitk +{ + +class DICOMReader_EXPORT SortByImagePositionPatient : public DICOMSortCriterion +{ + public: + + mitkClassMacro( SortByImagePositionPatient, DICOMSortCriterion ); + mitkNewMacro1Param( SortByImagePositionPatient, DICOMSortCriterion::Pointer ); + + virtual DICOMTagList GetTagsOfInterest() const; + virtual bool IsLeftBeforeRight(const mitk::DICOMDatasetAccess* left, const mitk::DICOMDatasetAccess* right) const; + + protected: + + SortByImagePositionPatient( DICOMSortCriterion::Pointer secondaryCriterion = NULL ); + virtual ~SortByImagePositionPatient(); + + SortByImagePositionPatient(const SortByImagePositionPatient& other); + SortByImagePositionPatient& operator=(const SortByImagePositionPatient& other); + + mitk::Point3D DICOMStringToPoint3D(const std::string& s, bool& successful) const; + void DICOMStringToOrientationVectors(const std::string& s, Vector3D& right, Vector3D& up, bool& successful) const; + + private: +}; + +} + +#endif