diff --git a/Modules/DICOMReader/Testing/mitkDICOMITKSeriesGDCMReaderBasicsTest.cpp b/Modules/DICOMReader/Testing/mitkDICOMITKSeriesGDCMReaderBasicsTest.cpp index 965046ca72..4644513886 100644 --- a/Modules/DICOMReader/Testing/mitkDICOMITKSeriesGDCMReaderBasicsTest.cpp +++ b/Modules/DICOMReader/Testing/mitkDICOMITKSeriesGDCMReaderBasicsTest.cpp @@ -1,63 +1,78 @@ /*=================================================================== 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 "mitkDICOMITKSeriesGDCMReader.h" #include "mitkDICOMFileReaderTestHelper.h" #include "mitkDICOMFilenameSorter.h" #include "mitkDICOMTagBasedSorter.h" +#include "mitkDICOMSortByTag.h" #include "mitkTestingMacros.h" int mitkDICOMITKSeriesGDCMReaderBasicsTest(int argc, char* argv[]) { MITK_TEST_BEGIN("mitkDICOMITKSeriesGDCMReaderBasicsTest"); mitk::DICOMITKSeriesGDCMReader::Pointer gdcmReader = mitk::DICOMITKSeriesGDCMReader::New(); MITK_TEST_CONDITION_REQUIRED(gdcmReader.IsNotNull(), "DICOMITKSeriesGDCMReader can be instantiated."); mitk::DICOMFileReaderTestHelper::SetTestInputFilenames( argc,argv ); // check the Set/GetInput function mitk::DICOMFileReaderTestHelper::TestInputFilenames( gdcmReader ); // check that output is a good reproduction of input (no duplicates, no new elements) mitk::DICOMFileReaderTestHelper::TestOutputsContainInputs( gdcmReader ); // repeat test with filename based sorter in-between mitk::DICOMFilenameSorter::Pointer filenameSorter = mitk::DICOMFilenameSorter::New(); gdcmReader->AddSortingElement( filenameSorter ); mitk::DICOMFileReaderTestHelper::TestOutputsContainInputs( gdcmReader ); // repeat test with some more realistic sorting gdcmReader = mitk::DICOMITKSeriesGDCMReader::New(); // this also tests destruction mitk::DICOMTagBasedSorter::Pointer tagSorter = mitk::DICOMTagBasedSorter::New(); + // all the things that split by tag in DicomSeriesReader tagSorter->AddDistinguishingTag( std::make_pair(0x0028, 0x0010) ); // Number of Rows tagSorter->AddDistinguishingTag( std::make_pair(0x0028, 0x0011) ); // Number of Columns tagSorter->AddDistinguishingTag( std::make_pair(0x0028, 0x0030) ); // Pixel Spacing tagSorter->AddDistinguishingTag( std::make_pair(0x0018, 0x1164) ); // Imager Pixel Spacing tagSorter->AddDistinguishingTag( std::make_pair(0x0020, 0x0037) ); // Image Orientation (Patient) // TODO add tolerance parameter (l. 1572 of original code) tagSorter->AddDistinguishingTag( std::make_pair(0x0020, 0x000e) ); // Series Instance UID tagSorter->AddDistinguishingTag( std::make_pair(0x0018, 0x0050) ); // Slice Thickness tagSorter->AddDistinguishingTag( std::make_pair(0x0028, 0x0008) ); // Number of Frames + + // a sorter... + // TODO ugly syntax, improve.. + mitk::DICOMSortCriterion::Pointer sorting = + mitk::DICOMSortByTag::New( std::make_pair(0x0020, 0x0013), // instance number + mitk::DICOMSortByTag::New( std::make_pair(0x0020, 0x0012), // aqcuisition number + mitk::DICOMSortByTag::New( std::make_pair(0x0008, 0x0032), // aqcuisition time + mitk::DICOMSortByTag::New( std::make_pair(0x0018, 0x1060) // trigger time + ).GetPointer() + ).GetPointer() + ).GetPointer() + ).GetPointer(); + gdcmReader->AddSortingElement( tagSorter ); mitk::DICOMFileReaderTestHelper::TestOutputsContainInputs( gdcmReader ); - gdcmReader->PrintOutputs(std::cout); + gdcmReader->PrintOutputs(std::cout, true); MITK_TEST_END(); } diff --git a/Modules/DICOMReader/files.cmake b/Modules/DICOMReader/files.cmake index 0a784316cc..7e77d13a28 100644 --- a/Modules/DICOMReader/files.cmake +++ b/Modules/DICOMReader/files.cmake @@ -1,22 +1,26 @@ 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 ) set(CPP_FILES mitkDICOMFileReader.cpp mitkDICOMImageBlockDescriptor.cpp mitkDICOMITKSeriesGDCMReader.cpp mitkDICOMDatasetSorter.cpp mitkDICOMFilenameSorter.cpp mitkDICOMTagBasedSorter.cpp mitkDICOMGDCMImageFrameInfo.cpp mitkDICOMImageFrameInfo.cpp + mitkDICOMSortCriterion.cpp + mitkDICOMSortByTag.cpp ) diff --git a/Modules/DICOMReader/mitkDICOMSortByTag.cpp b/Modules/DICOMReader/mitkDICOMSortByTag.cpp new file mode 100644 index 0000000000..6bb880ac24 --- /dev/null +++ b/Modules/DICOMReader/mitkDICOMSortByTag.cpp @@ -0,0 +1,133 @@ +/*=================================================================== + +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 + } + } +} + +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) + { + return this->StringCompare(left,right, tag); + } + else + { + if (leftDouble != rightDouble) + { + return leftDouble < rightDouble; + } + else + { + if (m_SecondaryCriterion.IsNotNull()) + { + return m_SecondaryCriterion->IsLeftBeforeRight(left, right); + } + else + { + return leftDouble < rightDouble; // TODO last resort + } + } + } +} diff --git a/Modules/DICOMReader/mitkDICOMSortByTag.h b/Modules/DICOMReader/mitkDICOMSortByTag.h new file mode 100644 index 0000000000..8ca6c48596 --- /dev/null +++ b/Modules/DICOMReader/mitkDICOMSortByTag.h @@ -0,0 +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 mitkDICOMSortByTag_h +#define mitkDICOMSortByTag_h + +#include "mitkDICOMSortCriterion.h" + +namespace mitk +{ + +class DICOMReader_EXPORT DICOMSortByTag : public DICOMSortCriterion +{ + public: + + mitkClassMacro( DICOMSortByTag, DICOMSortCriterion ); + mitkNewMacro1Param( DICOMSortByTag, const DICOMTag& ); + mitkNewMacro2Param( DICOMSortByTag, const DICOMTag&, DICOMSortCriterion::Pointer ); + + virtual DICOMTagList GetTagsOfInterest() const; + virtual bool IsLeftBeforeRight(const mitk::DICOMDatasetAccess* left, const mitk::DICOMDatasetAccess* right) const; + + protected: + + DICOMSortByTag( const DICOMTag& tag, DICOMSortCriterion::Pointer secondaryCriterion = NULL ); + virtual ~DICOMSortByTag(); + + DICOMSortByTag(const DICOMSortByTag& other); + DICOMSortByTag& operator=(const DICOMSortByTag& other); + + bool StringCompare(const mitk::DICOMDatasetAccess* left, const mitk::DICOMDatasetAccess* right, const DICOMTag& tag) const; + bool NumericCompare(const mitk::DICOMDatasetAccess* left, const mitk::DICOMDatasetAccess* right, const DICOMTag& tag) const; + + private: + + DICOMTag m_Tag; +}; + +} + +#endif diff --git a/Modules/DICOMReader/mitkDICOMSortCriterion.cpp b/Modules/DICOMReader/mitkDICOMSortCriterion.cpp new file mode 100644 index 0000000000..b26a5b5f6e --- /dev/null +++ b/Modules/DICOMReader/mitkDICOMSortCriterion.cpp @@ -0,0 +1,65 @@ +/*=================================================================== + +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; +} diff --git a/Modules/DICOMReader/mitkDICOMSortCriterion.h b/Modules/DICOMReader/mitkDICOMSortCriterion.h new file mode 100644 index 0000000000..3ba6ae2ce3 --- /dev/null +++ b/Modules/DICOMReader/mitkDICOMSortCriterion.h @@ -0,0 +1,52 @@ +/*=================================================================== + +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(); + + DICOMSortCriterion(const DICOMSortCriterion& other); + DICOMSortCriterion& operator=(const DICOMSortCriterion& other); + + DICOMSortCriterion::Pointer m_SecondaryCriterion; +}; + +} + +#endif diff --git a/Modules/DICOMReader/mitkDICOMTagBasedSorter.cpp b/Modules/DICOMReader/mitkDICOMTagBasedSorter.cpp index 6e5dac78cd..4ac1afc461 100644 --- a/Modules/DICOMReader/mitkDICOMTagBasedSorter.cpp +++ b/Modules/DICOMReader/mitkDICOMTagBasedSorter.cpp @@ -1,147 +1,175 @@ /*=================================================================== 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 "mitkDICOMTagBasedSorter.h" #include mitk::DICOMTagBasedSorter ::DICOMTagBasedSorter() :DICOMDatasetSorter() { } mitk::DICOMTagBasedSorter ::~DICOMTagBasedSorter() { } mitk::DICOMTagBasedSorter ::DICOMTagBasedSorter(const DICOMTagBasedSorter& other ) :DICOMDatasetSorter(other) { } mitk::DICOMTagBasedSorter& mitk::DICOMTagBasedSorter ::operator=(const DICOMTagBasedSorter& other) { if (this != &other) { DICOMDatasetSorter::operator=(other); } return *this; } mitk::DICOMTagList mitk::DICOMTagBasedSorter ::GetTagsOfInterest() { DICOMTagList allTags = m_DistinguishingTags; - allTags.insert( allTags.end(), m_SortCriteria.begin(), m_SortCriteria.end() ); // append - // TODO sort, unique + DICOMTagList sortingRelevantTags = m_SortCriterion->GetAllTagsOfInterest(); + allTags.insert( allTags.end(), sortingRelevantTags.begin(), sortingRelevantTags.end() ); // append return allTags; } void mitk::DICOMTagBasedSorter ::AddDistinguishingTag( const DICOMTag& tag ) { m_DistinguishingTags.push_back(tag); } void mitk::DICOMTagBasedSorter -::AddSortCriterion( const DICOMTag& tag ) +::SetSortCriterion( DICOMSortCriterion::ConstPointer criterion ) { - m_SortCriteria.push_back(tag); + m_SortCriterion = criterion; } void mitk::DICOMTagBasedSorter ::Sort() { // 1. split - this->SplitGroups(); - // 2. sort each group - this->SortGroups(); + GroupIDToListType groups = this->SplitInputGroups(); + GroupIDToListType& sortedGroups = this->SortGroups( groups ); - //std::sort( output.begin(), output.end(), FilenameSort() ); + // 3. define output + this->SetNumberOfOutputs(sortedGroups.size()); + unsigned int outputIndex(0); + for (GroupIDToListType::iterator groupIter = sortedGroups.begin(); + groupIter != sortedGroups.end(); + ++outputIndex, ++groupIter) + { + this->SetOutput(outputIndex, groupIter->second); + } } std::string mitk::DICOMTagBasedSorter ::BuildGroupID( DICOMDatasetAccess* dataset ) { // just concatenate all tag values assert(dataset); std::stringstream groupID; groupID << "g"; for (DICOMTagList::iterator tagIter = m_DistinguishingTags.begin(); tagIter != m_DistinguishingTags.end(); ++tagIter) { groupID << tagIter->first << tagIter->second; // make group/element part of the id to cover empty tags groupID << dataset->GetTagValueAsString(*tagIter); } // shorten ID? return groupID.str(); } -void +mitk::DICOMTagBasedSorter::GroupIDToListType mitk::DICOMTagBasedSorter -::SplitGroups() +::SplitInputGroups() { DICOMDatasetList input = GetInput(); // copy - typedef std::map GroupIDToListType; GroupIDToListType listForGroupID; for (DICOMDatasetList::iterator dsIter = input.begin(); dsIter != input.end(); ++dsIter) { DICOMDatasetAccess* dataset = *dsIter; assert(dataset); std::string groupID = this->BuildGroupID( dataset ); MITK_DEBUG << "Group ID for for " << dataset->GetFilenameIfAvailable() << ": " << groupID; listForGroupID[groupID].push_back(dataset); } - this->SetNumberOfOutputs(listForGroupID.size()); - unsigned int outputIndex(0); - for (GroupIDToListType::iterator groupIter = listForGroupID.begin(); - groupIter != listForGroupID.end(); - ++outputIndex, ++groupIter) + return listForGroupID; +} + +mitk::DICOMTagBasedSorter::GroupIDToListType& +mitk::DICOMTagBasedSorter +::SortGroups(GroupIDToListType& groups) +{ + if (m_SortCriterion.IsNotNull()) { - this->SetOutput(outputIndex, groupIter->second); + // for each output + // sort by all configured tags, use secondary tags when equal or empty + // make configurable: + // - sorting order (ascending, descending) + // - sort numerically + // - ... ? + for (GroupIDToListType::iterator gIter = groups.begin(); + gIter != groups.end(); + ++gIter) + { + DICOMDatasetList& dsList = gIter->second; + std::sort( dsList.begin(), dsList.end(), ParameterizedDatasetSort( m_SortCriterion ) ); + } } + + return groups; } -void -mitk::DICOMTagBasedSorter -::SortGroups() +mitk::DICOMTagBasedSorter::ParameterizedDatasetSort +::ParameterizedDatasetSort(DICOMSortCriterion::ConstPointer criterion) +:m_SortCriterion(criterion) { - // for each output - // sort by all configured tags, use secondary tags when equal or empty - // make configurable: - // - sorting order (ascending, descending) - // - sort numerically - // - ... ? +} + +bool +mitk::DICOMTagBasedSorter::ParameterizedDatasetSort +::operator() (const mitk::DICOMDatasetAccess* left, const mitk::DICOMDatasetAccess* right) +{ + assert(left); + assert(right); + assert(m_SortCriterion.IsNotNull()); + + return m_SortCriterion->IsLeftBeforeRight(left, right); } diff --git a/Modules/DICOMReader/mitkDICOMTagBasedSorter.h b/Modules/DICOMReader/mitkDICOMTagBasedSorter.h index 4d047c2d55..1d348f9fdd 100644 --- a/Modules/DICOMReader/mitkDICOMTagBasedSorter.h +++ b/Modules/DICOMReader/mitkDICOMTagBasedSorter.h @@ -1,61 +1,73 @@ /*=================================================================== 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 mitkDICOMTagBasedSorter_h #define mitkDICOMTagBasedSorter_h #include "mitkDICOMDatasetSorter.h" +#include "mitkDICOMSortCriterion.h" namespace mitk { /** \brief sort files based on filename (last resort). */ class DICOMReader_EXPORT DICOMTagBasedSorter : public DICOMDatasetSorter { public: mitkClassMacro( DICOMTagBasedSorter, DICOMDatasetSorter ) itkNewMacro( DICOMTagBasedSorter ) void AddDistinguishingTag( const DICOMTag& ); - void AddSortCriterion( const DICOMTag& ); + void SetSortCriterion( DICOMSortCriterion::ConstPointer criterion ); virtual DICOMTagList GetTagsOfInterest(); virtual void Sort(); protected: + struct ParameterizedDatasetSort + { + ParameterizedDatasetSort(DICOMSortCriterion::ConstPointer); + bool operator() (const mitk::DICOMDatasetAccess* left, const mitk::DICOMDatasetAccess* right); + bool StringCompare(const mitk::DICOMDatasetAccess* left, const mitk::DICOMDatasetAccess* right); + bool NumericCompare(const mitk::DICOMDatasetAccess* left, const mitk::DICOMDatasetAccess* right); + DICOMSortCriterion::ConstPointer m_SortCriterion; + }; + + DICOMTagBasedSorter(); virtual ~DICOMTagBasedSorter(); DICOMTagBasedSorter(const DICOMTagBasedSorter& other); DICOMTagBasedSorter& operator=(const DICOMTagBasedSorter& other); std::string BuildGroupID( DICOMDatasetAccess* dataset ); - void SplitGroups(); - void SortGroups(); + typedef std::map GroupIDToListType; + GroupIDToListType SplitInputGroups(); + GroupIDToListType& SortGroups(GroupIDToListType& groups); DICOMTagList m_DistinguishingTags; - DICOMTagList m_SortCriteria; + DICOMSortCriterion::ConstPointer m_SortCriterion; }; } #endif