From 6b4aa7387613d98b62ee41d18aba2dbe0306b4ba Mon Sep 17 00:00:00 2001 From: jpr Date: Fri, 25 Nov 2005 13:56:31 +0000 Subject: [PATCH] Upport itk modif (taken from gdcm1.0) around Jolinda Smith's algorithm --- src/gdcmFile.cxx | 8 +- src/gdcmFile.h | 6 +- src/gdcmOrientation.cxx | 10 +-- src/gdcmOrientation.h | 6 +- src/gdcmSerieHelper.cxx | 195 ++++++++++++++++++++++++++++------------ src/gdcmSerieHelper.h | 20 ++++- 6 files changed, 168 insertions(+), 77 deletions(-) diff --git a/src/gdcmFile.cxx b/src/gdcmFile.cxx index 466643b7..2c692895 100644 --- a/src/gdcmFile.cxx +++ b/src/gdcmFile.cxx @@ -3,8 +3,8 @@ Program: gdcm Module: $RCSfile: gdcmFile.cxx,v $ Language: C++ - Date: $Date: 2005/11/25 10:29:12 $ - Version: $Revision: 1.308 $ + Date: $Date: 2005/11/25 13:56:31 $ + Version: $Revision: 1.309 $ Copyright (c) CREATIS (Centre de Recherche et d'Applications en Traitement de l'Image). All rights reserved. See Doc/License.txt or @@ -773,10 +773,10 @@ float File::GetZOrigin() * @return true when one of the tag is found * false when nothing is found */ -bool File::GetImageOrientationPatient( float iop[6] ) +bool File::GetImageOrientationPatient( double iop[6] ) { std::string strImOriPat; - //iop is supposed to be float[6] + //iop is supposed to be double[6] iop[0] = iop[4] = 1.; iop[1] = iop[2] = iop[3] = iop[5] = 0.; diff --git a/src/gdcmFile.h b/src/gdcmFile.h index cea32f0a..84dca965 100644 --- a/src/gdcmFile.h +++ b/src/gdcmFile.h @@ -3,8 +3,8 @@ Program: gdcm Module: $RCSfile: gdcmFile.h,v $ Language: C++ - Date: $Date: 2005/11/21 16:28:07 $ - Version: $Revision: 1.120 $ + Date: $Date: 2005/11/25 13:56:31 $ + Version: $Revision: 1.121 $ Copyright (c) CREATIS (Centre de Recherche et d'Applications en Traitement de l'Image). All rights reserved. See Doc/License.txt or @@ -125,7 +125,7 @@ public: float GetYOrigin(); float GetZOrigin(); - bool GetImageOrientationPatient( float iop[6] ); + bool GetImageOrientationPatient( double iop[6] ); int GetBitsStored(); int GetBitsAllocated(); diff --git a/src/gdcmOrientation.cxx b/src/gdcmOrientation.cxx index ea95d5f4..47fed192 100644 --- a/src/gdcmOrientation.cxx +++ b/src/gdcmOrientation.cxx @@ -3,8 +3,8 @@ Program: gdcm Module: $RCSfile: gdcmOrientation.cxx,v $ Language: C++ - Date: $Date: 2005/10/23 15:12:24 $ - Version: $Revision: 1.19 $ + Date: $Date: 2005/11/25 13:56:32 $ + Version: $Revision: 1.20 $ Copyright (c) CREATIS (Centre de Recherche et d'Applications en Traitement de l'Image). All rights reserved. See Doc/License.txt or @@ -84,7 +84,7 @@ const char* Orientation::GetOrientationTypeString(OrientationType const o) /// (Axial, Coronal, Sagital, ...) of the image OrientationType Orientation::GetOrientationType( File *f ) { - float iop[6]; + double iop[6]; bool succ = f->GetImageOrientationPatient( iop ); if ( !succ ) { @@ -292,7 +292,7 @@ have multiple letters in as described under "refinements" in C.7.6.1.1.1): */ std::string Orientation::GetOrientation ( File *f ) { - float iop[6]; + double iop[6]; if ( !f->GetImageOrientationPatient( iop ) ) return GDCM_UNFOUND; @@ -304,7 +304,7 @@ std::string Orientation::GetOrientation ( File *f ) } -std::string Orientation::GetSingleOrientation ( float *iop) +std::string Orientation::GetSingleOrientation ( double *iop) { std::string orientation; diff --git a/src/gdcmOrientation.h b/src/gdcmOrientation.h index 6b2f309e..e1522fd3 100644 --- a/src/gdcmOrientation.h +++ b/src/gdcmOrientation.h @@ -3,8 +3,8 @@ Program: gdcm Module: $RCSfile: gdcmOrientation.h,v $ Language: C++ - Date: $Date: 2005/11/21 09:46:27 $ - Version: $Revision: 1.13 $ + Date: $Date: 2005/11/25 13:56:32 $ + Version: $Revision: 1.14 $ Copyright (c) CREATIS (Centre de Recherche et d'Applications en Traitement de l'Image). All rights reserved. See Doc/License.txt or @@ -117,7 +117,7 @@ private: double CalculLikelyhood2Vec(vector3D const &refA, vector3D const &refB, vector3D const &ori1, vector3D const &ori2); vector3D ProductVectorial(vector3D const &vec1, vector3D const &vec2); - std::string GetSingleOrientation ( float *iop); + std::string GetSingleOrientation ( double *iop); }; } // end namespace gdcm //----------------------------------------------------------------------------- diff --git a/src/gdcmSerieHelper.cxx b/src/gdcmSerieHelper.cxx index df093b33..5adbd99b 100644 --- a/src/gdcmSerieHelper.cxx +++ b/src/gdcmSerieHelper.cxx @@ -3,8 +3,8 @@ Program: gdcm Module: $RCSfile: gdcmSerieHelper.cxx,v $ Language: C++ - Date: $Date: 2005/11/21 09:46:27 $ - Version: $Revision: 1.35 $ + Date: $Date: 2005/11/25 13:56:32 $ + Version: $Revision: 1.36 $ Copyright (c) CREATIS (Centre de Recherche et d'Applications en Traitement de l'Image). All rights reserved. See Doc/License.txt or @@ -40,6 +40,7 @@ namespace gdcm */ SerieHelper::SerieHelper() { + m_UseSeriesDetails = false; ClearAll(); UserLessThanFunction = 0; DirectOrder = true; @@ -178,7 +179,7 @@ void SerieHelper::AddGdcmFile(File *header) ++it) { const Rule &r = *it; - const std::string s;// = header->GetEntryValue( r.first ); + const std::string s;// = header->GetEntryString( r.first ); if ( !Util::DicomStringEqual(s, r.second.c_str()) ) { // Argh ! This rule is unmatch let's just quit @@ -390,7 +391,7 @@ XCoherentFileSetmap SerieHelper::SplitOnOrientation(FileList *fileSet) int nb = fileSet->size(); if (nb == 0 ) return CoherentFileSet; - float iop[6]; + double iop[6]; std::string strOrient; std::ostringstream ossOrient; @@ -567,16 +568,15 @@ bool SerieHelper::ImagePositionPatientOrdering( FileList *fileList ) //based on Jolinda Smith's algorithm { //iop is calculated based on the file file - float cosines[6]; - float normal[3]; - float ipp[3]; - float dist; - float min = 0, max = 0; + double cosines[6]; + double normal[3]; + double ipp[3]; + double dist; + double min = 0, max = 0; bool first = true; - int n=0; - std::vector distlist; - //!\todo rewrite this for loop. + std::multimap distmultimap; + // Use a multimap to sort the distances from 0,0,0 for ( FileList::const_iterator it = fileList->begin(); it != fileList->end(); ++it ) @@ -603,7 +603,7 @@ bool SerieHelper::ImagePositionPatientOrdering( FileList *fileList ) dist += normal[i]*ipp[i]; } - distlist.push_back( dist ); + distmultimap.insert(std::pair(dist, *it)); max = min = dist; first = false; @@ -620,21 +620,13 @@ bool SerieHelper::ImagePositionPatientOrdering( FileList *fileList ) dist += normal[i]*ipp[i]; } - distlist.push_back( dist ); + distmultimap.insert(std::pair(dist, *it)); min = (min < dist) ? min : dist; max = (max > dist) ? max : dist; } - ++n; - } - // Then I order the slices according to the value "dist". Finally, once - // I've read in all the slices, I calculate the z-spacing as the difference - // between the "dist" values for the first two slices. - FileVector CoherentFileVector(n); - // CoherentFileVector.reserve( n ); - CoherentFileVector.resize( n ); - // gdcmAssertMacro( CoherentFileVector.capacity() >= n ); + } // Find out if min/max are coherent if ( min == max ) @@ -644,56 +636,50 @@ bool SerieHelper::ImagePositionPatientOrdering( FileList *fileList ) return false; } - float step = (max - min)/(n - 1); - int pos; - n = 0; - - //VC++ don't understand what scope is !! it -> it2 - for (FileList::const_iterator it2 = fileList->begin(); - it2 != fileList->end(); ++it2, ++n) - { - //2*n sort algo !! - //Assumption: all files are present (no one missing) - pos = (int)( fabs( (distlist[n]-min)/step) + .5 ); - - // a Dicom 'Serie' may contain scout views - // and images may have differents directions - // -> More than one may have the same 'pos' - // Sorting has then NO meaning ! - if (CoherentFileVector[pos]==NULL) - CoherentFileVector[pos] = *it2; - else - { - gdcmWarningMacro( "At least 2 files with same position." - << " No PositionPatientOrdering sort performed"); - return false; - } - } - + // Check to see if image shares a common position + bool ok = true; + for (std::multimap::iterator it2 = distmultimap.begin(); + it2 != distmultimap.end(); + ++it2) + { + if (distmultimap.count((*it2).first) != 1) + { + gdcmErrorMacro("File: " + << ((*it2).second->GetFileName()) + << " Distance: " + << (*it2).first + << " position is not unique"); + ok = false; + } + } + if (!ok) + { + return false; + } + fileList->clear(); // doesn't delete list elements, only nodes if (DirectOrder) { - //VC++ don't understand what scope is !! it -> it3 - for (FileVector::const_iterator it3 = CoherentFileVector.begin(); - it3 != CoherentFileVector.end(); ++it3) + for (std::multimap::iterator it3 = distmultimap.begin(); + it3 != distmultimap.end(); + ++it3) { - fileList->push_back( *it3 ); + fileList->push_back( (*it3).second ); } } else // user asked for reverse order { - FileVector::const_iterator it4; - it4 = CoherentFileVector.end(); + std::multimap::const_iterator it4; + it4 = distmultimap.end(); do { it4--; - fileList->push_back( *it4 ); - } while (it4 != CoherentFileVector.begin() ); + fileList->push_back( (*it4).second ); + } while (it4 != distmultimap.begin() ); } - distlist.clear(); - CoherentFileVector.clear(); + distmultimap.clear(); return true; } @@ -790,6 +776,97 @@ bool SerieHelper::UserOrdering(FileList *fileList) return true; } +std::string SerieHelper::CreateUniqueSeriesIdentifier( File * inFile ) +{ + if( inFile->IsReadable() ) + { + // 0020 000e UI REL Series Instance UID + std::string uid = inFile->GetEntryString (0x0020, 0x000e); + std::string id = uid.c_str(); + if(m_UseSeriesDetails) + { + // If the user requests, additional information can be appended + // to the SeriesUID to further differentiate volumes in the DICOM + // objects being processed. + + // 0020 0011 Series Number + // A scout scan prior to a CT volume scan can share the same + // SeriesUID, but they will sometimes have a different Series Number + std::string sNum = inFile->GetEntryString(0x0020, 0x0011); + if( sNum == gdcm::GDCM_UNFOUND ) + { + sNum = ""; + } + // 0018 0024 Sequence Name + // For T1-map and phase-contrast MRA, the different flip angles and + // directions are only distinguished by the Sequence Name + std::string sName = inFile->GetEntryString(0x0018, 0x0024); + if( sName == gdcm::GDCM_UNFOUND ) + { + sName = ""; + } + // 0018 0050 Slice Thickness + // On some CT systems, scout scans and subsequence volume scans will + // have the same SeriesUID and Series Number - YET the slice + // thickness will differ from the scout slice and the volume slices. + std::string sThick = inFile->GetEntryString (0x0018, 0x0050); + if( sThick == gdcm::GDCM_UNFOUND ) + { + sThick = ""; + } + // 0028 0010 Rows + // If the 2D images in a sequence don't have the same number of rows, + // then it is difficult to reconstruct them into a 3D volume. + std::string sRows = inFile->GetEntryString (0x0028, 0x0010); + if( sRows == gdcm::GDCM_UNFOUND ) + { + sRows = ""; + } + // 0028 0011 Columns + // If the 2D images in a sequence don't have the same number of columns, + // then it is difficult to reconstruct them into a 3D volume. + std::string sColumns = inFile->GetEntryString (0x0028, 0x0011); + if( sColumns == gdcm::GDCM_UNFOUND ) + { + sColumns = ""; + } + + // Concat the new info + std::string num = sNum.c_str(); + num += sName.c_str(); + num += sThick.c_str(); + num += sRows.c_str(); + num += sColumns.c_str(); + + // Append the new info to the SeriesUID + id += "."; + id += num.c_str(); + } + + // Eliminate non-alnum characters, including whitespace... + // that may have been introduced by concats. + for(unsigned int i=0; i= 'a' && id[i] <= 'z') + || (id[i] >= '0' && id[i] <= '9') + || (id[i] >= 'A' && id[i] <= 'Z'))) + { + id.erase(i, 1); + } + } + return id; + } + else // Could not open inFile + { + gdcmWarningMacro("Could not parse series info."); + std::string id = gdcm::GDCM_UNFOUND; + return id; + } +} + + //----------------------------------------------------------------------------- // Print /** diff --git a/src/gdcmSerieHelper.h b/src/gdcmSerieHelper.h index 9264eb18..72fa2d3a 100644 --- a/src/gdcmSerieHelper.h +++ b/src/gdcmSerieHelper.h @@ -3,8 +3,8 @@ Program: gdcm Module: $RCSfile: gdcmSerieHelper.h,v $ Language: C++ - Date: $Date: 2005/11/21 09:46:27 $ - Version: $Revision: 1.29 $ + Date: $Date: 2005/11/25 13:56:32 $ + Version: $Revision: 1.30 $ Copyright (c) CREATIS (Centre de Recherche et d'Applications en Traitement de l'Image). All rights reserved. See Doc/License.txt or @@ -102,6 +102,19 @@ public: /// All the following allow user to restrict DICOM file to be part /// of a particular serie void AddRestriction(TagKey const &key, std::string const &value, int op); + + /// \brief Use additional series information such as ProtocolName + /// and SeriesName to identify when a single SeriesUID contains + /// multiple 3D volumes - as can occur with perfusion and DTI imaging + void SetUseSeriesDetails( bool useSeriesDetails ) + { m_UseSeriesDetails = useSeriesDetails;} + bool GetUseSeriesDetails( ){ return m_UseSeriesDetails; } + + // \brief Create a string that uniquely identifies a series. By default + // uses the SeriesUID. If UseSeriesDetails(true) has been called, + // then additional identifying information is used. + std::string CreateUniqueSeriesIdentifier( File * inFile ); + /** * \brief Sets the LoadMode as a boolean string. @@ -161,7 +174,8 @@ private: } ExRule; typedef std::vector SerieExRestrictions; SerieExRestrictions ExRestrictions; - + bool m_UseSeriesDetails; + /// \brief Bit string integer (each one considered as a boolean) /// Bit 0 : Skip Sequences, if possible /// Bit 1 : Skip Shadow Groups if possible -- 2.45.1