diff --git a/Modules/DICOMReader/Testing/mitkDICOMITKSeriesGDCMReaderBasicsTest.cpp b/Modules/DICOMReader/Testing/mitkDICOMITKSeriesGDCMReaderBasicsTest.cpp index 69869d5941..b137157b93 100644 --- a/Modules/DICOMReader/Testing/mitkDICOMITKSeriesGDCMReaderBasicsTest.cpp +++ b/Modules/DICOMReader/Testing/mitkDICOMITKSeriesGDCMReaderBasicsTest.cpp @@ -1,86 +1,88 @@ /*=================================================================== 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" +using mitk::DICOMTag; + 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( 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) // TODO add tolerance parameter (l. 1572 of original code) // TODO handle as real vectors! cluster with configurable errors! - 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 - tagSorter->AddDistinguishingTag( std::make_pair(0x0020, 0x0052) ); // Frame of Reference UID + 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( 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 - mitk::DICOMSortByTag::New( std::make_pair(0x0008, 0x0018) // SOP instance UID (last resort, not really meaningful but decides clearly) + mitk::DICOMSortByTag::New( DICOMTag(0x0020, 0x0013), // instance number + 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 ); gdcmReader->AddSortingElement( tagSorter ); mitk::DICOMFileReaderTestHelper::TestOutputsContainInputs( gdcmReader ); gdcmReader->PrintOutputs(std::cout, true); // really load images mitk::DICOMFileReaderTestHelper::TestMitkImagesAreLoaded( gdcmReader ); MITK_TEST_END(); } diff --git a/Modules/DICOMReader/files.cmake b/Modules/DICOMReader/files.cmake index d92bbc61ce..edba81d2f8 100644 --- a/Modules/DICOMReader/files.cmake +++ b/Modules/DICOMReader/files.cmake @@ -1,32 +1,34 @@ 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 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 mitkGantryTiltInformation.cpp mitkClassicDICOMSeriesReader.cpp + mitkDICOMTag.cpp ) diff --git a/Modules/DICOMReader/mitkClassicDICOMSeriesReader.cpp b/Modules/DICOMReader/mitkClassicDICOMSeriesReader.cpp index 11c5d64281..5295a828d5 100644 --- a/Modules/DICOMReader/mitkClassicDICOMSeriesReader.cpp +++ b/Modules/DICOMReader/mitkClassicDICOMSeriesReader.cpp @@ -1,79 +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 "mitkClassicDICOMSeriesReader.h" #include "mitkDICOMTagBasedSorter.h" #include "mitkDICOMSortByTag.h" mitk::ClassicDICOMSeriesReader ::ClassicDICOMSeriesReader() :DICOMITKSeriesGDCMReader() { mitk::DICOMTagBasedSorter::Pointer tagSorter = mitk::DICOMTagBasedSorter::New(); // all the things that split by tag in mitk::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) - 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 - tagSorter->AddDistinguishingTag( std::make_pair(0x0020, 0x0052) ); // Frame of Reference UID + 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( 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 - mitk::DICOMSortByTag::New( std::make_pair(0x0008, 0x0018) // SOP instance UID (last resort, not really meaningful but decides clearly) + mitk::DICOMSortByTag::New( DICOMTag(0x0020, 0x0013), // instance number + 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/mitkDICOMDatasetAccess.h b/Modules/DICOMReader/mitkDICOMDatasetAccess.h index bd79de6f16..e503a4cbf1 100644 --- a/Modules/DICOMReader/mitkDICOMDatasetAccess.h +++ b/Modules/DICOMReader/mitkDICOMDatasetAccess.h @@ -1,40 +1,40 @@ /*=================================================================== 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 mitkDICOMDatasetAccess_h #define mitkDICOMDatasetAccess_h -#include "mitkDICOMEnums.h" +#include "mitkDICOMTag.h" #include "DICOMReaderExports.h" namespace mitk { class DICOMReader_EXPORT DICOMDatasetAccess { public: virtual std::string GetFilenameIfAvailable() const = 0; virtual std::string GetTagValueAsString(const mitk::DICOMTag& tag) const = 0; }; typedef std::vector DICOMDatasetList; } #endif diff --git a/Modules/DICOMReader/mitkDICOMEnums.h b/Modules/DICOMReader/mitkDICOMEnums.h index 394eb66325..ee710e64d7 100644 --- a/Modules/DICOMReader/mitkDICOMEnums.h +++ b/Modules/DICOMReader/mitkDICOMEnums.h @@ -1,39 +1,37 @@ /*=================================================================== 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 mitkDICOMEnums_h #define mitkDICOMEnums_h #include #include #include namespace mitk { - typedef std::pair DICOMTag; typedef std::vector StringList; - typedef std::vector DICOMTagList; typedef std::vector BoolList; typedef enum { SpacingInPatient, /// distances are mm within a patient SpacingAtDetector, /// distances are mm at detector surface SpacingUnknown /// NO spacing information is present, we use (1,1) as default } PixelSpacingInterpretation; } #endif diff --git a/Modules/DICOMReader/mitkDICOMGDCMImageFrameInfo.cpp b/Modules/DICOMReader/mitkDICOMGDCMImageFrameInfo.cpp index d09ccd674e..bfec23e8ee 100644 --- a/Modules/DICOMReader/mitkDICOMGDCMImageFrameInfo.cpp +++ b/Modules/DICOMReader/mitkDICOMGDCMImageFrameInfo.cpp @@ -1,86 +1,86 @@ /*=================================================================== 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 "mitkDICOMGDCMImageFrameInfo.h" mitk::DICOMGDCMImageFrameInfo ::DICOMGDCMImageFrameInfo(const std::string& filename, unsigned int frameNo) :itk::LightObject() ,m_FrameInfo( DICOMImageFrameInfo::New(filename, frameNo) ) ,m_TagForValue(gdcm::Scanner::TagToValue()) // empty { } mitk::DICOMGDCMImageFrameInfo ::DICOMGDCMImageFrameInfo(DICOMImageFrameInfo::Pointer frameinfo) :itk::LightObject() ,m_FrameInfo(frameinfo) ,m_TagForValue(gdcm::Scanner::TagToValue()) // empty { } mitk::DICOMGDCMImageFrameInfo ::DICOMGDCMImageFrameInfo(DICOMImageFrameInfo::Pointer frameinfo, gdcm::Scanner::TagToValue const& tagToValueMapping) :itk::LightObject() ,m_FrameInfo(frameinfo) ,m_TagForValue(tagToValueMapping) { } std::string mitk::DICOMGDCMImageFrameInfo ::GetTagValueAsString(const DICOMTag& tag) const { - gdcm::Scanner::TagToValue::const_iterator mappedValue = m_TagForValue.find( gdcm::Tag(tag.first, tag.second) ); + gdcm::Scanner::TagToValue::const_iterator mappedValue = m_TagForValue.find( gdcm::Tag(tag.GetGroup(), tag.GetElement()) ); if (mappedValue != m_TagForValue.end()) { return mappedValue->second; } else { return std::string(""); } } std::string mitk::DICOMGDCMImageFrameInfo ::GetFilenameIfAvailable() const { if (m_FrameInfo.IsNotNull()) { return m_FrameInfo->Filename; } else { return ""; } } mitk::DICOMImageFrameInfo::Pointer mitk::DICOMGDCMImageFrameInfo ::GetFrameInfo() const { return m_FrameInfo; } void mitk::DICOMGDCMImageFrameInfo ::SetFrameInfo(DICOMImageFrameInfo::Pointer frameinfo) { m_FrameInfo = frameinfo; } diff --git a/Modules/DICOMReader/mitkDICOMITKSeriesGDCMReader.cpp b/Modules/DICOMReader/mitkDICOMITKSeriesGDCMReader.cpp index 73792f05b7..c6cd3a9795 100644 --- a/Modules/DICOMReader/mitkDICOMITKSeriesGDCMReader.cpp +++ b/Modules/DICOMReader/mitkDICOMITKSeriesGDCMReader.cpp @@ -1,283 +1,283 @@ /*=================================================================== 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 "mitkITKDICOMSeriesReaderHelper.h" #include "mitkEquiDistantBlocksSorter.h" #include #include mitk::DICOMITKSeriesGDCMReader ::DICOMITKSeriesGDCMReader() :DICOMFileReader() ,m_FixTiltByShearing(false) ,m_Group3DplusT(false) { } mitk::DICOMITKSeriesGDCMReader ::DICOMITKSeriesGDCMReader(const DICOMITKSeriesGDCMReader& other ) :DICOMFileReader(other) ,m_FixTiltByShearing(false) ,m_Group3DplusT(false) { } mitk::DICOMITKSeriesGDCMReader ::~DICOMITKSeriesGDCMReader() { } mitk::DICOMITKSeriesGDCMReader& mitk::DICOMITKSeriesGDCMReader ::operator=(const DICOMITKSeriesGDCMReader& other) { if (this != &other) { DICOMFileReader::operator=(other); } return *this; } mitk::DICOMGDCMImageFrameList mitk::DICOMITKSeriesGDCMReader ::FromDICOMDatasetList(DICOMDatasetList& input) { DICOMGDCMImageFrameList output; output.reserve(input.size()); for(DICOMDatasetList::iterator inputIter = input.begin(); inputIter != input.end(); ++inputIter) { DICOMGDCMImageFrameInfo* gfi = dynamic_cast(*inputIter); assert(gfi); output.push_back(gfi); } return output; } mitk::DICOMDatasetList mitk::DICOMITKSeriesGDCMReader ::ToDICOMDatasetList(DICOMGDCMImageFrameList& input) { DICOMDatasetList output; output.reserve(input.size()); for(DICOMGDCMImageFrameList::iterator inputIter = input.begin(); inputIter != input.end(); ++inputIter) { DICOMDatasetAccess* da = inputIter->GetPointer(); assert(da); output.push_back(da); } return output; } mitk::DICOMImageFrameList mitk::DICOMITKSeriesGDCMReader ::ToDICOMImageFrameList(DICOMGDCMImageFrameList& input) { DICOMImageFrameList output; output.reserve(input.size()); for(DICOMGDCMImageFrameList::iterator inputIter = input.begin(); inputIter != input.end(); ++inputIter) { DICOMImageFrameInfo::Pointer fi = (*inputIter)->GetFrameInfo(); assert(fi.IsNotNull()); output.push_back(fi); } return output; } void mitk::DICOMITKSeriesGDCMReader ::AnalyzeInputFiles() { // TODO at this point, make sure we have a sorting element at the end that splits geometrically separate blocks this->EnsureMandatorySortersArePresent(); itk::TimeProbesCollectorBase timer; timer.Start("Reset"); this->ClearOutputs(); timer.Stop("Reset"); // prepare initial sorting (== list of input files) StringList inputFilenames = this->GetInputFiles(); // scan files for sorting-relevant tags // TODO provide tagToValueMappings to items in initialFramelist / m_SortingResultInProgress timer.Start("Setup scanning"); gdcm::Scanner gdcmScanner; for(SorterList::iterator sorterIter = m_Sorter.begin(); sorterIter != m_Sorter.end(); ++sorterIter) { assert(sorterIter->IsNotNull()); DICOMTagList tags = (*sorterIter)->GetTagsOfInterest(); for(DICOMTagList::const_iterator tagIter = tags.begin(); tagIter != tags.end(); ++tagIter) { - MITK_DEBUG << "Sorting uses tag " << tagIter->first << "," << tagIter->second; - gdcmScanner.AddTag( gdcm::Tag(tagIter->first, tagIter->second) ); + MITK_DEBUG << "Sorting uses tag " << tagIter->GetGroup() << "," << tagIter->GetElement(); + gdcmScanner.AddTag( gdcm::Tag(tagIter->GetGroup(), tagIter->GetElement()) ); } } timer.Stop("Setup scanning"); timer.Start("Tag scanning"); gdcmScanner.Scan( inputFilenames ); timer.Stop("Tag scanning"); timer.Start("Setup sorting"); DICOMGDCMImageFrameList initialFramelist; for (StringList::const_iterator inputIter = inputFilenames.begin(); inputIter != inputFilenames.end(); ++inputIter) { // TODO check DICOMness and non-multi-framedness initialFramelist.push_back( DICOMGDCMImageFrameInfo::New( DICOMImageFrameInfo::New(*inputIter, 0), gdcmScanner.GetMapping(inputIter->c_str()) ) ); } m_SortingResultInProgress.clear(); m_SortingResultInProgress.push_back( initialFramelist ); timer.Stop("Setup sorting"); // sort and split blocks as configured timer.Start("Sorting frames"); SortingBlockList nextStepSorting; // we should not modify our input list while processing it unsigned int sorterIndex = 0; for(SorterList::iterator sorterIter = m_Sorter.begin(); sorterIter != m_Sorter.end(); ++sorterIndex, ++sorterIter) { std::stringstream ss; ss << "Sorting step " << sorterIndex; timer.Start( ss.str().c_str() ); nextStepSorting.clear(); DICOMDatasetSorter::Pointer& sorter = *sorterIter; for(SortingBlockList::iterator blockIter = m_SortingResultInProgress.begin(); blockIter != m_SortingResultInProgress.end(); ++blockIter) { DICOMGDCMImageFrameList& gdcmInfoFrameList = *blockIter; DICOMDatasetList datasetList = ToDICOMDatasetList( gdcmInfoFrameList ); sorter->SetInput(datasetList); sorter->Sort(); unsigned int numberOfResultingBlocks = sorter->GetNumberOfOutputs(); for (unsigned int b = 0; b < numberOfResultingBlocks; ++b) { DICOMDatasetList blockResult = sorter->GetOutput(b); DICOMGDCMImageFrameList sortedGdcmInfoFrameList = FromDICOMDatasetList(blockResult); nextStepSorting.push_back( sortedGdcmInfoFrameList ); } } m_SortingResultInProgress = nextStepSorting; timer.Stop( ss.str().c_str() ); } timer.Stop("Sorting frames"); // provide final result as output timer.Start("Output"); this->SetNumberOfOutputs( m_SortingResultInProgress.size() ); unsigned int o = 0; for (SortingBlockList::iterator blockIter = m_SortingResultInProgress.begin(); blockIter != m_SortingResultInProgress.end(); ++o, ++blockIter) { DICOMGDCMImageFrameList& gdcmFrameInfoList = *blockIter; DICOMImageFrameList frameList = ToDICOMImageFrameList( gdcmFrameInfoList ); DICOMImageBlockDescriptor block; block.SetImageFrameList( frameList ); this->SetOutput( o, block ); } timer.Stop("Output"); std::cout << "---------------------------------------------------------------" << std::endl; timer.Report( std::cout ); std::cout << "---------------------------------------------------------------" << std::endl; } // void AllocateOutputImages(); bool mitk::DICOMITKSeriesGDCMReader ::LoadImages() { mitk::ITKDICOMSeriesReaderHelper helper; unsigned int numberOfOutputs = this->GetNumberOfOutputs(); for (unsigned int o = 0; o < numberOfOutputs; ++o) { DICOMImageBlockDescriptor& block = this->InternalGetOutput(o); const DICOMImageFrameList& frames = block.GetImageFrameList(); bool correctTilt = false; // TODO bool tiltInfo = false; // TODO ITKDICOMSeriesReaderHelper::StringContainer filenames; for (DICOMImageFrameList::const_iterator frameIter = frames.begin(); frameIter != frames.end(); ++frameIter) { filenames.push_back( (*frameIter)->Filename ); } mitk::Image::Pointer mitkImage = helper.Load( filenames, correctTilt, tiltInfo ); // TODO preloaded images, caching..? block.SetMitkImage( mitkImage ); } return true; } bool mitk::DICOMITKSeriesGDCMReader ::CanHandleFile(const std::string& itkNotUsed(filename)) { return true; // can handle all } void mitk::DICOMITKSeriesGDCMReader ::AddSortingElement(DICOMDatasetSorter* sorter) { assert(sorter); m_Sorter.push_back( sorter ); } void mitk::DICOMITKSeriesGDCMReader ::EnsureMandatorySortersArePresent() { mitk::EquiDistantBlocksSorter::Pointer distanceKeeper = mitk::EquiDistantBlocksSorter::New(); this->AddSortingElement( distanceKeeper ); } diff --git a/Modules/DICOMReader/mitkDICOMTag.cpp b/Modules/DICOMReader/mitkDICOMTag.cpp new file mode 100644 index 0000000000..81904ce32f --- /dev/null +++ b/Modules/DICOMReader/mitkDICOMTag.cpp @@ -0,0 +1,60 @@ +/*=================================================================== + +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 "mitkDICOMTag.h" + +mitk::DICOMTag +::DICOMTag(unsigned int group, unsigned int element) +:m_Group(group) +,m_Element(element) +{ +} + +mitk::DICOMTag +::DICOMTag(const DICOMTag& other) +:m_Group(other.m_Group) +,m_Element(other.m_Element) +{ +} + +mitk::DICOMTag& +mitk::DICOMTag +::operator=(const DICOMTag& other) +{ + if (this != &other) + { + m_Group = other.m_Group; + m_Element = other.m_Element; + } + return *this; +} + +unsigned int +mitk::DICOMTag +::GetGroup() const +{ + return m_Group; +} + +unsigned int +mitk::DICOMTag +::GetElement() const +{ + return m_Element; +} diff --git a/Modules/DICOMReader/mitkDICOMDatasetAccess.h b/Modules/DICOMReader/mitkDICOMTag.h similarity index 58% copy from Modules/DICOMReader/mitkDICOMDatasetAccess.h copy to Modules/DICOMReader/mitkDICOMTag.h index bd79de6f16..2e0152409f 100644 --- a/Modules/DICOMReader/mitkDICOMDatasetAccess.h +++ b/Modules/DICOMReader/mitkDICOMTag.h @@ -1,40 +1,48 @@ /*=================================================================== 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 mitkDICOMDatasetAccess_h -#define mitkDICOMDatasetAccess_h +#ifndef mitkTag_h +#define mitkTag_h -#include "mitkDICOMEnums.h" +#include #include "DICOMReaderExports.h" namespace mitk { -class DICOMReader_EXPORT DICOMDatasetAccess +class DICOMReader_EXPORT DICOMTag { public: - virtual std::string GetFilenameIfAvailable() const = 0; - virtual std::string GetTagValueAsString(const mitk::DICOMTag& tag) const = 0; -}; + DICOMTag(unsigned int group, unsigned int element); + DICOMTag(const DICOMTag& other); + DICOMTag& operator=(const DICOMTag& other); + + unsigned int GetGroup() const; + unsigned int GetElement() const; + protected: + + unsigned int m_Group; + unsigned int m_Element; +}; -typedef std::vector DICOMDatasetList; +typedef std::vector DICOMTagList; } #endif diff --git a/Modules/DICOMReader/mitkDICOMTagBasedSorter.cpp b/Modules/DICOMReader/mitkDICOMTagBasedSorter.cpp index 4ac1afc461..74676eadde 100644 --- a/Modules/DICOMReader/mitkDICOMTagBasedSorter.cpp +++ b/Modules/DICOMReader/mitkDICOMTagBasedSorter.cpp @@ -1,175 +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; 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 ::SetSortCriterion( DICOMSortCriterion::ConstPointer criterion ) { m_SortCriterion = criterion; } void mitk::DICOMTagBasedSorter ::Sort() { // 1. split // 2. sort each group GroupIDToListType groups = this->SplitInputGroups(); GroupIDToListType& sortedGroups = this->SortGroups( groups ); // 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 << tagIter->GetGroup() << tagIter->GetElement(); // make group/element part of the id to cover empty tags groupID << dataset->GetTagValueAsString(*tagIter); } // shorten ID? return groupID.str(); } mitk::DICOMTagBasedSorter::GroupIDToListType mitk::DICOMTagBasedSorter ::SplitInputGroups() { DICOMDatasetList input = GetInput(); // copy 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); } return listForGroupID; } mitk::DICOMTagBasedSorter::GroupIDToListType& mitk::DICOMTagBasedSorter ::SortGroups(GroupIDToListType& groups) { if (m_SortCriterion.IsNotNull()) { // 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; } mitk::DICOMTagBasedSorter::ParameterizedDatasetSort ::ParameterizedDatasetSort(DICOMSortCriterion::ConstPointer criterion) :m_SortCriterion(criterion) { } 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/mitkEquiDistantBlocksSorter.cpp b/Modules/DICOMReader/mitkEquiDistantBlocksSorter.cpp index d6c4fe9422..fa88e1ef55 100644 --- a/Modules/DICOMReader/mitkEquiDistantBlocksSorter.cpp +++ b/Modules/DICOMReader/mitkEquiDistantBlocksSorter.cpp @@ -1,453 +1,453 @@ /*=================================================================== 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 "mitkEquiDistantBlocksSorter.h" #include "mitkGantryTiltInformation.h" mitk::EquiDistantBlocksSorter::SliceGroupingAnalysisResult ::SliceGroupingAnalysisResult() :m_GantryTilt(false) { } mitk::DICOMDatasetList mitk::EquiDistantBlocksSorter::SliceGroupingAnalysisResult ::GetBlockFilenames() { return m_GroupedFiles; } mitk::DICOMDatasetList mitk::EquiDistantBlocksSorter::SliceGroupingAnalysisResult ::GetUnsortedFilenames() { return m_UnsortedFiles; } bool mitk::EquiDistantBlocksSorter::SliceGroupingAnalysisResult ::ContainsGantryTilt() { return m_GantryTilt; } void mitk::EquiDistantBlocksSorter::SliceGroupingAnalysisResult ::AddFileToSortedBlock(DICOMDatasetAccess* dataset) { m_GroupedFiles.push_back( dataset ); } void mitk::EquiDistantBlocksSorter::SliceGroupingAnalysisResult ::AddFileToUnsortedBlock(DICOMDatasetAccess* dataset) { m_UnsortedFiles.push_back( dataset ); } void mitk::EquiDistantBlocksSorter::SliceGroupingAnalysisResult ::AddFilesToUnsortedBlock(const DICOMDatasetList& datasets) { m_UnsortedFiles.insert( m_UnsortedFiles.end(), datasets.begin(), datasets.end() ); } void mitk::EquiDistantBlocksSorter::SliceGroupingAnalysisResult ::FlagGantryTilt() { m_GantryTilt = true; } void mitk::EquiDistantBlocksSorter::SliceGroupingAnalysisResult ::UndoPrematureGrouping() { assert( !m_GroupedFiles.empty() ); m_UnsortedFiles.insert( m_UnsortedFiles.begin(), m_GroupedFiles.back() ); m_GroupedFiles.pop_back(); } // ------------------------ end helper class mitk::EquiDistantBlocksSorter ::EquiDistantBlocksSorter() :DICOMDatasetSorter() { } mitk::EquiDistantBlocksSorter ::~EquiDistantBlocksSorter() { } mitk::EquiDistantBlocksSorter ::EquiDistantBlocksSorter(const EquiDistantBlocksSorter& other ) :DICOMDatasetSorter(other) { } mitk::EquiDistantBlocksSorter& mitk::EquiDistantBlocksSorter ::operator=(const EquiDistantBlocksSorter& other) { if (this != &other) { DICOMDatasetSorter::operator=(other); } return *this; } mitk::DICOMTagList mitk::EquiDistantBlocksSorter ::GetTagsOfInterest() { DICOMTagList tags; - tags.push_back( std::make_pair(0x0020, 0x0032) ); // ImagePositionPatient - tags.push_back( std::make_pair(0x0020, 0x0037) ); // ImageOrientationPatient + tags.push_back( DICOMTag(0x0020, 0x0032) ); // ImagePositionPatient + tags.push_back( DICOMTag(0x0020, 0x0037) ); // ImageOrientationPatient return tags; } void mitk::EquiDistantBlocksSorter ::Sort() { DICOMDatasetList remainingInput = GetInput(); // copy bool acceptGantryTilt = false; // TODO typedef std::list OutputListType; OutputListType outputs; while (!remainingInput.empty()) // repeat until all files are grouped somehow { SliceGroupingAnalysisResult regularBlock = this->AnalyzeFileForITKImageSeriesReaderSpacingAssumption( remainingInput, acceptGantryTilt ); outputs.push_back( regularBlock.GetBlockFilenames() ); remainingInput = regularBlock.GetUnsortedFilenames(); } unsigned int numberOfOutputs = outputs.size(); this->SetNumberOfOutputs(numberOfOutputs); unsigned int outputIndex(0); for (OutputListType::iterator oIter = outputs.begin(); oIter != outputs.end(); ++outputIndex, ++oIter) { this->SetOutput(outputIndex, *oIter); } } std::string mitk::EquiDistantBlocksSorter ::ConstCharStarToString(const char* s) { return s ? std::string(s) : std::string(); } mitk::Point3D mitk::EquiDistantBlocksSorter ::DICOMStringToPoint3D(const std::string& s, bool& successful) { 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::EquiDistantBlocksSorter ::DICOMStringToOrientationVectors(const std::string& s, Vector3D& right, Vector3D& up, bool& successful) { 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; } } mitk::EquiDistantBlocksSorter::SliceGroupingAnalysisResult mitk::EquiDistantBlocksSorter ::AnalyzeFileForITKImageSeriesReaderSpacingAssumption( const DICOMDatasetList& datasets, bool groupImagesWithGantryTilt) { // result.first = files that fit ITK's assumption // result.second = files that do not fit, should be run through AnalyzeFileForITKImageSeriesReaderSpacingAssumption() again SliceGroupingAnalysisResult result; // we const_cast here, because I could not use a map.at(), which would make the code much more readable - const DICOMTag tagImagePositionPatient = std::make_pair(0x0020,0x0032); // Image Position (Patient) - const DICOMTag tagImageOrientation = std::make_pair(0x0020, 0x0037); // Image Orientation - const DICOMTag tagGantryTilt = std::make_pair(0x0018, 0x1120); // gantry tilt + const DICOMTag tagImagePositionPatient = DICOMTag(0x0020,0x0032); // Image Position (Patient) + const DICOMTag tagImageOrientation = DICOMTag(0x0020, 0x0037); // Image Orientation + const DICOMTag tagGantryTilt = DICOMTag(0x0018, 0x1120); // gantry tilt Vector3D fromFirstToSecondOrigin; fromFirstToSecondOrigin.Fill(0.0); bool fromFirstToSecondOriginInitialized(false); Point3D thisOrigin; thisOrigin.Fill(0.0f); Point3D lastOrigin; lastOrigin.Fill(0.0f); Point3D lastDifferentOrigin; lastDifferentOrigin.Fill(0.0f); bool lastOriginInitialized(false); MITK_DEBUG << "--------------------------------------------------------------------------------"; MITK_DEBUG << "Analyzing files for z-spacing assumption of ITK's ImageSeriesReader (group tilted: " << groupImagesWithGantryTilt << ")"; unsigned int fileIndex(0); for (DICOMDatasetList::const_iterator fileIter = datasets.begin(); fileIter != datasets.end(); ++fileIter, ++fileIndex) { bool fileFitsIntoPattern(false); std::string thisOriginString; // Read tag value into point3D. PLEASE replace this by appropriate GDCM code if you figure out how to do that thisOriginString = (*fileIter)->GetTagValueAsString( tagImagePositionPatient ); if (thisOriginString.empty()) { // don't let such files be in a common group. Everything without position information will be loaded as a single slice: // with standard DICOM files this can happen to: CR, DX, SC MITK_DEBUG << " ==> Sort away " << *fileIter << " for later analysis (no position information)"; // we already have one occupying this position if ( result.GetBlockFilenames().empty() ) // nothing WITH position information yet { // ==> this is a group of its own, stop processing, come back later result.AddFileToSortedBlock( *fileIter ); // TODO // TODO change to DICOMDatasetList! DICOMDatasetList remainingFiles; remainingFiles.insert( remainingFiles.end(), fileIter+1, datasets.end() ); result.AddFilesToUnsortedBlock( remainingFiles ); fileFitsIntoPattern = false; break; // no files anymore } else { // ==> this does not match, consider later result.AddFileToUnsortedBlock( *fileIter ); // sort away for further analysis fileFitsIntoPattern = false; continue; // next file } } bool ignoredConversionError(-42); // hard to get here, no graceful way to react thisOrigin = DICOMStringToPoint3D( thisOriginString, ignoredConversionError ); MITK_DEBUG << " " << fileIndex << " " << *fileIter << " at " /* << thisOriginString */ << "(" << thisOrigin[0] << "," << thisOrigin[1] << "," << thisOrigin[2] << ")"; if ( lastOriginInitialized && (thisOrigin == lastOrigin) ) { MITK_DEBUG << " ==> Sort away " << *fileIter << " for separate time step"; // we already have one occupying this position result.AddFileToUnsortedBlock( *fileIter ); // sort away for further analysis fileFitsIntoPattern = false; } else { if (!fromFirstToSecondOriginInitialized && lastOriginInitialized) // calculate vector as soon as possible when we get a new position { fromFirstToSecondOrigin = thisOrigin - lastDifferentOrigin; fromFirstToSecondOriginInitialized = true; // Here we calculate if this slice and the previous one are well aligned, // i.e. we test if the previous origin is on a line through the current // origin, directed into the normal direction of the current slice. // If this is NOT the case, then we have a data set with a TILTED GANTRY geometry, // which cannot be simply loaded into a single mitk::Image at the moment. // For this case, we flag this finding in the result and DicomSeriesReader // can correct for that later. Vector3D right; right.Fill(0.0); Vector3D up; right.Fill(0.0); // might be down as well, but it is just a name at this point std::string orientationValue = (*fileIter)->GetTagValueAsString( tagImageOrientation ); DICOMStringToOrientationVectors( orientationValue, right, up, ignoredConversionError ); GantryTiltInformation tiltInfo( lastDifferentOrigin, thisOrigin, right, up, 1 ); if ( tiltInfo.IsSheared() ) // mitk::eps is too small; 1/1000 of a mm should be enough to detect tilt { /* optimistic approach, accepting gantry tilt: save file for later, check all further files */ // at this point we have TWO slices analyzed! if they are the only two files, we still split, because there is no third to verify our tilting assumption. // later with a third being available, we must check if the initial tilting vector is still valid. if yes, continue. // if NO, we need to split the already sorted part (result.first) and the currently analyzed file (*fileIter) // tell apart gantry tilt from overall skewedness // sort out irregularly sheared slices, that IS NOT tilting if ( groupImagesWithGantryTilt && tiltInfo.IsRegularGantryTilt() ) { bool todoIsDone(false); // TODO add GantryTilt reading // check if this is at least roughly the same angle as recorded in DICOM tags if ( todoIsDone /*&& tagValueMappings[fileIter->c_str()].find(tagGantryTilt) != tagValueMappings[fileIter->c_str()].end() */) { // read value, compare to calculated angle std::string tiltStr = (*fileIter)->GetTagValueAsString( tagGantryTilt ); double angle = atof(tiltStr.c_str()); MITK_DEBUG << "Comparing recorded tilt angle " << angle << " against calculated value " << tiltInfo.GetTiltAngleInDegrees(); // TODO we probably want the signs correct, too (that depends: this is just a rough check, nothing serious) if ( fabs(angle) - tiltInfo.GetTiltAngleInDegrees() > 0.25) { result.AddFileToUnsortedBlock( *fileIter ); // sort away for further analysis fileFitsIntoPattern = false; } else // tilt angle from header is less than 0.25 degrees different from what we calculated, assume this is fine { result.FlagGantryTilt(); result.AddFileToSortedBlock( *fileIter ); // this file is good for current block fileFitsIntoPattern = true; } } else // we cannot check the calculated tilt angle against the one from the dicom header (so we assume we are right) { result.FlagGantryTilt(); result.AddFileToSortedBlock( *fileIter ); // this file is good for current block fileFitsIntoPattern = true; } } else // caller does not want tilt compensation OR shearing is more complicated than tilt { result.AddFileToUnsortedBlock( *fileIter ); // sort away for further analysis fileFitsIntoPattern = false; } } else // not sheared { result.AddFileToSortedBlock( *fileIter ); // this file is good for current block fileFitsIntoPattern = true; } } else if (fromFirstToSecondOriginInitialized) // we already know the offset between slices { Point3D assumedOrigin = lastDifferentOrigin + fromFirstToSecondOrigin; Vector3D originError = assumedOrigin - thisOrigin; double norm = originError.GetNorm(); double toleratedError(0.005); // max. 1/10mm error when measurement crosses 20 slices in z direction if (norm > toleratedError) { MITK_DEBUG << " File does not fit into the inter-slice distance pattern (diff = " << norm << ", allowed " << toleratedError << ")."; MITK_DEBUG << " Expected position (" << assumedOrigin[0] << "," << assumedOrigin[1] << "," << assumedOrigin[2] << "), got position (" << thisOrigin[0] << "," << thisOrigin[1] << "," << thisOrigin[2] << ")"; MITK_DEBUG << " ==> Sort away " << *fileIter << " for later analysis"; // At this point we know we deviated from the expectation of ITK's ImageSeriesReader // We split the input file list at this point, i.e. all files up to this one (excluding it) // are returned as group 1, the remaining files (including the faulty one) are group 2 /* Optimistic approach: check if any of the remaining slices fits in */ result.AddFileToUnsortedBlock( *fileIter ); // sort away for further analysis fileFitsIntoPattern = false; } else { result.AddFileToSortedBlock( *fileIter ); // this file is good for current block fileFitsIntoPattern = true; } } else // this should be the very first slice { result.AddFileToSortedBlock( *fileIter ); // this file is good for current block fileFitsIntoPattern = true; } } // record current origin for reference in later iterations if ( !lastOriginInitialized || ( fileFitsIntoPattern && (thisOrigin != lastOrigin) ) ) { lastDifferentOrigin = thisOrigin; } lastOrigin = thisOrigin; lastOriginInitialized = true; } if ( result.ContainsGantryTilt() ) { // check here how many files were grouped. // IF it was only two files AND we assume tiltedness (e.g. save "distance") // THEN we would want to also split the two previous files (simple) because // we don't have any reason to assume they belong together if ( result.GetBlockFilenames().size() == 2 ) { result.UndoPrematureGrouping(); } } return result; }