diff --git a/Core/Code/Testing/DICOMTesting/VerifyDICOMMitkImageDump.cpp b/Core/Code/Testing/DICOMTesting/VerifyDICOMMitkImageDump.cpp index 86da2b9f4a..6119eab26e 100644 --- a/Core/Code/Testing/DICOMTesting/VerifyDICOMMitkImageDump.cpp +++ b/Core/Code/Testing/DICOMTesting/VerifyDICOMMitkImageDump.cpp @@ -1,92 +1,99 @@ #include "mitkTestDICOMLoading.h" std::vector LoadDumps(const std::string& fileName) { std::vector separatedDumps; std::ifstream fileStream( fileName.c_str() ); std::string buffer; std::string line; while(fileStream){ std::getline(fileStream, line); if (line.find("-- Image ") == 0) { // separator: starts a new image block if ( !buffer.empty() ) { // unless this is the first block separatedDumps.push_back(buffer); buffer.clear(); } } else { buffer += line + "\n"; } } fileStream.close(); // eat last image dump if ( !buffer.empty() ) { separatedDumps.push_back(buffer); buffer.clear(); } return separatedDumps; } int main(int argc, char** argv) { /** Loads a list of DICOM images, compares generated mitk::Images against stored references. first argument: file with reference dumps following arguments: file names to load */ if (argc < 2) { MITK_ERROR << "Usage:"; MITK_ERROR << " " << argv[0] << " reference.dump file1 [file2 .. fileN]"; MITK_ERROR << " "; MITK_ERROR << " Loads all DICOM images in file1 to fileN as MITK images "; MITK_ERROR << " and compares loaded images against stored expectations (dumps)."; MITK_ERROR << " See also DumpDICOMMitkImage (generates dumps)"; return EXIT_FAILURE; } mitk::TestDICOMLoading loader; mitk::TestDICOMLoading::StringContainer files; // TODO load reference dumps // load from argv[1] // separate at "-- Image n" // store in an array of dumps std::vector expectedDumps = LoadDumps(argv[1]); for (int arg = 2; arg < argc; ++arg) files.push_back( argv[arg] ); mitk::TestDICOMLoading::ImageList images = loader.LoadFiles(files); unsigned int imageCounter(0); for ( mitk::TestDICOMLoading::ImageList::const_iterator imageIter = images.begin(); imageIter != images.end(); ++imageIter, ++imageCounter ) { std::string imageDump = loader.DumpImageInformation( *imageIter ); + if (imageCounter >= expectedDumps.size()) + { + MITK_ERROR << "Loader produces more images than expected. Aborting after image " << (imageCounter-1); + MITK_INFO << "Image " << imageCounter << " loaded as:\n" << imageDump; + return EXIT_FAILURE; + } + bool loadedAsExpected = loader.CompareImageInformationDumps( expectedDumps[imageCounter], imageDump ); MITK_INFO << "Image " << imageCounter << " loads as expected."; // TODO compare against reference } return EXIT_SUCCESS; } diff --git a/Core/Code/Testing/DICOMTesting/mitkTestDICOMLoading.cpp b/Core/Code/Testing/DICOMTesting/mitkTestDICOMLoading.cpp index aa0833bdb7..731d0a88c2 100644 --- a/Core/Code/Testing/DICOMTesting/mitkTestDICOMLoading.cpp +++ b/Core/Code/Testing/DICOMTesting/mitkTestDICOMLoading.cpp @@ -1,295 +1,303 @@ //#define MBILOG_ENABLE_DEBUG #include "mitkTestDICOMLoading.h" #include mitk::TestDICOMLoading::TestDICOMLoading() { MITK_INFO << "TestDICOMLoading()"; } void mitk::TestDICOMLoading::SetDefaultLocale() { // remember old locale only once if (m_PreviousCLocale == NULL) { m_PreviousCLocale = setlocale(LC_NUMERIC, NULL); // set to "C" setlocale(LC_NUMERIC, "C"); m_PreviousCppLocale = std::cin.getloc(); std::locale l( "C" ); std::cin.imbue(l); std::cout.imbue(l); } } void mitk::TestDICOMLoading::ResetUserLocale() { if (m_PreviousCLocale) { setlocale(LC_NUMERIC, m_PreviousCLocale); std::cin.imbue(m_PreviousCppLocale); std::cout.imbue(m_PreviousCppLocale); m_PreviousCLocale = NULL; } } mitk::TestDICOMLoading::ImageList mitk::TestDICOMLoading::LoadFiles( const StringContainer& files ) { for (StringContainer::const_iterator iter = files.begin(); iter != files.end(); ++iter) { MITK_DEBUG << "File " << *iter; } - - StringContainer sortedFiles = DicomSeriesReader::SortSeriesSlices( files ); - - DataNode::Pointer node = DicomSeriesReader::LoadDicomSeries( sortedFiles ); - + ImageList result; - if (node.IsNotNull()) - { - Image::Pointer image = dynamic_cast( node->GetData() ); - - result.push_back( image ); - } - else + DicomSeriesReader::UidFileNamesMap seriesInFiles = DicomSeriesReader::GetSeries( files ); + + // TODO sort series UIDs, implementation of map iterator might differ on different platforms (or verify this is a standard topic??) + for (DicomSeriesReader::UidFileNamesMap::const_iterator seriesIter = seriesInFiles.begin(); + seriesIter != seriesInFiles.end(); + ++seriesIter) { + StringContainer files = seriesIter->second; + + DataNode::Pointer node = DicomSeriesReader::LoadDicomSeries( files ); + + if (node.IsNotNull()) + { + Image::Pointer image = dynamic_cast( node->GetData() ); + + result.push_back( image ); + } + else + { + } } return result; } // add a line to stringstream result (see DumpImageInformation #define DumpLine(field, data) DumpILine(0, field, data) // add an indented(!) line to stringstream result (see DumpImageInformation #define DumpILine(indent, field, data) \ { \ std::string DumpLine_INDENT; DumpLine_INDENT.resize(indent, ' ' ); \ result << DumpLine_INDENT << field << ": " << data << "\n"; \ } std::string mitk::TestDICOMLoading::DumpImageInformation( const Image* image ) { std::stringstream result; if (image == NULL) return result.str(); SetDefaultLocale(); // basic image data DumpLine( "Pixeltype", image->GetPixelType().GetTypeId()->name() ); DumpLine( "BitsPerPixel", image->GetPixelType().GetBpe() ); DumpLine( "Dimension", image->GetDimension() ); result << "Dimensions: "; for (unsigned int dim = 0; dim < image->GetDimension(); ++dim) result << image->GetDimension(dim) << " "; result << "\n"; // geometry data result << "Geometry: \n"; Geometry3D* geometry = image->GetGeometry(); if (geometry) { AffineTransform3D* transform = geometry->GetIndexToWorldTransform(); if (transform) { result << " " << "Matrix: "; const AffineTransform3D::MatrixType& matrix = transform->GetMatrix(); for (unsigned int i = 0; i < 3; ++i) for (unsigned int j = 0; j < 3; ++j) result << matrix[i][j] << " "; result << "\n"; result << " " << "Offset: "; const AffineTransform3D::OutputVectorType& offset = transform->GetOffset(); for (unsigned int i = 0; i < 3; ++i) result << offset[i] << " "; result << "\n"; result << " " << "Center: "; const AffineTransform3D::InputPointType& center = transform->GetCenter(); for (unsigned int i = 0; i < 3; ++i) result << center[i] << " "; result << "\n"; result << " " << "Translation: "; const AffineTransform3D::OutputVectorType& translation = transform->GetTranslation(); for (unsigned int i = 0; i < 3; ++i) result << translation[i] << " "; result << "\n"; result << " " << "Scale: "; const double* scale = transform->GetScale(); for (unsigned int i = 0; i < 3; ++i) result << scale[i] << " "; result << "\n"; result << " " << "Origin: "; const Point3D& origin = geometry->GetOrigin(); for (unsigned int i = 0; i < 3; ++i) result << origin[i] << " "; result << "\n"; result << " " << "Spacing: "; const Vector3D& spacing = geometry->GetSpacing(); for (unsigned int i = 0; i < 3; ++i) result << spacing[i] << " "; result << "\n"; result << " " << "TimeBounds: "; const TimeBounds timeBounds = geometry->GetTimeBounds(); for (unsigned int i = 0; i < 2; ++i) result << timeBounds[i] << " "; result << "\n"; } } ResetUserLocale(); return result.str(); } bool mitk::TestDICOMLoading::CompareSpacedValueFields( const std::string& reference, const std::string& test, double eps ) { // TODO this should better do a real comparison and tolerate float differences up to 'eps' return reference == test; } bool mitk::TestDICOMLoading::CompareImageInformationDumps( const std::string& referenceDump, const std::string& testDump ) { KeyValueMap reference = ParseDump(referenceDump); KeyValueMap test = ParseDump(testDump); bool testResult(true); // verify all expected values for (KeyValueMap::const_iterator refIter = reference.begin(); refIter != reference.end(); ++refIter) { const std::string& refKey = refIter->first; const std::string& refValue = refIter->second; if ( test.find(refKey) != test.end() ) { const std::string& testValue = test[refKey]; MITK_DEBUG << refKey << ": " << refValue << " == " << testValue << "?"; testResult &= CompareSpacedValueFields( refValue, testValue ); } else { MITK_ERROR << "Reference dump contains a key'" << refKey << "' (value '" << refValue << "')." ; MITK_ERROR << "This key is expected to be generated for tests (but was not). Most probably you need to update your test data."; return false; } } // now check test dump does not contain any additional keys for (KeyValueMap::const_iterator testIter = test.begin(); testIter != test.end(); ++testIter) { const std::string& key = testIter->first; const std::string& value = testIter->second; if ( reference.find(key) == reference.end() ) { MITK_ERROR << "Test dump contains an unexpected key'" << key << "' (value '" << value << "')." ; MITK_ERROR << "This key is not expected. Most probably you need to update your test data."; return false; } } return testResult; } mitk::TestDICOMLoading::KeyValueMap mitk::TestDICOMLoading::ParseDump( const std::string& dump ) { KeyValueMap parsedResult; std::string shredder(dump); std::stack surroundingKeys; std::stack expectedIndents; expectedIndents.push(0); while (true) { std::string::size_type newLinePos = shredder.find( '\n' ); if (newLinePos == std::string::npos || newLinePos == 0) break; std::string line = shredder.substr( 0, newLinePos ); shredder = shredder.erase( 0, newLinePos+1 ); std::string::size_type keyPosition = line.find_first_not_of( ' ' ); std::string::size_type colonPosition = line.find( ':' ); std::string key = line.substr(keyPosition, colonPosition - keyPosition); std::string::size_type firstSpacePosition = key.find_first_of(" "); if (firstSpacePosition != std::string::npos) { key.erase(firstSpacePosition); } if ( keyPosition > expectedIndents.top() ) { // more indent than before expectedIndents.push(keyPosition); } else if (keyPosition == expectedIndents.top() ) { if (!surroundingKeys.empty()) { surroundingKeys.pop(); // last of same length } } else { // less indent than before do expectedIndents.pop(); while (expectedIndents.top() != keyPosition); // unwind until current indent is found } if (!surroundingKeys.empty()) { key = surroundingKeys.top() + "." + key; // construct current key name } surroundingKeys.push(key); // this is the new embracing key std::string value = line.substr(colonPosition+1); MITK_DEBUG << " Key: '" << key << "' value '" << value << "'" ; parsedResult[key] = value; // store parsing result } return parsedResult; }