/*========================================================================= Program: gdcmdiff for GDCM (Grassroots DICOM) Copyright (c) 2011 Andy Buckle All rights reserved. See Copyright.txt or http://gdcm.sourceforge.net/Copyright.html for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notice for more information. =========================================================================*/ #include "gdcmReader.h" #include "gdcmAttribute.h" #include "gdcmDataSet.h" #include "gdcmDicts.h" #include "gdcmDict.h" #include "gdcmGlobal.h" #include "gdcmCSAHeader.h" #include "gdcmPrivateTag.h" #include static void usage(); static void difference_of_datasets(const gdcm::DataSet& ds1, const gdcm::DataSet& ds2, int depthSQ); static void display_element(std::ostream& os, const gdcm::DataElement& de, const gdcm::DictEntry& dictentry, const char *note, int depthSQ); static void underline(int depthSQ); static void difference_of_sequences(const gdcm::DataElement& sqde1, const gdcm::DataElement& sqde2, const gdcm::DictEntry& dictentry, int depthSQ); // previous declaration of 'int truncate(const char*, __off_t)' static uint32_t Truncate=30; // trim dumped string values to this number of chars. zero means no trimming. static std::stringstream sq_disp; // store SQ output while recursing through: only displayed if a difference is found within SQ int main( int argc, const char* argv[] ) { // Check number of args if (3 > argc) { std::cerr << "Must supply the filenames of 2 DICOM files\n" << std::endl; usage(); return 1; } // Check last 2 args are readable DICOM files gdcm::Reader reader1, reader2; reader1.SetFileName( argv[argc-2] ); reader2.SetFileName( argv[argc-1] ); if( !reader1.Read() || !reader2.Read() ) { std::cerr << "At least one of the DICOM files could not be read: " << '\"' << argv[argc-2] << "\", \"" << argv[argc-1] << '\"' << std::endl; return 1; } // Parse other args bool include_meta = false; for(int i=1; i<(argc-2) ;i++) { std::string arg = std::string(argv[i]); if ("-h" == arg || "--help" == arg) { usage(); return 0; } else if ("-m" == arg || "--meta" == arg) { include_meta = true; } else if ("-t" == arg || "--truncate" == arg) { Truncate = atoi(argv[++i]); } else { std::cerr << "Warning: command argument not understood: " << arg << std::endl; usage(); return 1; } } // Start comparison const gdcm::File &file1 = reader1.GetFile(); const gdcm::File &file2 = reader2.GetFile(); const gdcm::FileMetaInformation &hds1 = file1.GetHeader(); const gdcm::FileMetaInformation &hds2 = file2.GetHeader(); const gdcm::DataSet &ds1 = file1.GetDataSet(); const gdcm::DataSet &ds2 = file2.GetDataSet(); if(include_meta) { difference_of_datasets(hds1, hds2, 0); } difference_of_datasets(ds1, ds2, 0); return 0; } static void usage() { std::cout << "Usage: gdcmdiff [OPTIONS] DICOM_FILE1 DICOM_FILE2\n\n" " -h --help (This) help and exit.\n" " -m --meta Compare metainformation. Default is off.\n" " -t --truncate String values trimmed to n characters.\n" " 0 means no trimmming. Default 30." << std::endl; } static void difference_of_datasets(const gdcm::DataSet& ds1, const gdcm::DataSet& ds2, int depthSQ) { gdcm::DataSet::ConstIterator it1 = ds1.Begin(); gdcm::DataSet::ConstIterator it2 = ds2.Begin(); do { // find lowest value tag, being careful not to pick one for an iterator that has finished const gdcm::Tag tag1 = (it1!=ds1.End()) ? it1->GetTag() : gdcm::Tag(0xffff,0xffff); const gdcm::Tag tag2 = (it2!=ds2.End()) ? it2->GetTag() : gdcm::Tag(0xffff,0xffff); gdcm::Tag tag= (tag1GetLength(); // error: operands to ?: have different types 'gdcm::VL' and 'uint32_t' uint32_t val_vl = vl; uint32_t trimto = (Truncate > val_vl ) ? val_vl : Truncate; if (0 == Truncate) { trimto = de.GetByteValue()->GetLength(); } os << " [" << std::string(de.GetByteValue()->GetPointer(),trimto) << ']'; } else { os << " null"; } } else { os << " VR unknown"; } os << " # " << dictentry.GetName() << std::endl ; } static void underline(int depthSQ) { std::cout << std::string(depthSQ, ' '); //indent for SQ std::cout << " -------------" << std::endl; } static void difference_of_sequences(const gdcm::DataElement& sqde1, const gdcm::DataElement& sqde2, const gdcm::DictEntry& dictentry, int depthSQ) { gdcm::SmartPointer sqi1 = sqde1.GetValueAsSQ(); gdcm::SmartPointer sqi2 = sqde2.GetValueAsSQ(); size_t n1 = sqi1 ? sqi1->GetNumberOfItems() : 0; size_t n2 = sqi2 ? sqi2->GetNumberOfItems() : 0; size_t n = (n1 < n2) ? n1 : n2 ; std::stringstream sq_note; if (n1 != n2) { sq_note << "[sequence, file 1 has " << n1 << " datasets, file 2 has " << n2 << " datasets]"; } else { sq_note << "[sequence]"; } display_element(sq_disp, sqde1, dictentry, sq_note.str().c_str(),depthSQ); for(size_t i=1; i <= n; i++) { difference_of_datasets( sqi1->GetItem(i).GetNestedDataSet(), sqi2->GetItem(i).GetNestedDataSet(), depthSQ+1); } sq_disp.str(std::string()); // clear stringstream }