1 /*=========================================================================
4 Module: $RCSfile: vtkGdcm4DSplitter.cxx,v $
6 Date: $Date: 2011/04/15 15:23:14 $
7 Version: $Revision: 1.12 $
9 Copyright (c) CREATIS (Centre de Recherche et d'Applications en Traitement de
10 l'Image). All rights reserved. See Doc/License.txt or
11 http://www.creatis.insa-lyon.fr/Public/Gdcm/License.html for details.
13 This software is distributed WITHOUT ANY WARRANTY; without even
14 the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15 PURPOSE. See the above copyright notices for more information.
17 =========================================================================*/
19 /* Raisons ne pas utiliser itkImageSeriesReader:
21 On Wed, Feb 16, 2011 at 11:51 AM, Roger Bramon Feixas <rogerbramon@gmail.com>
23 I'm developing with ITK 3.20 + GDCM 2.0.17 + VTK 5.6 and I've noticed
24 itkImageSeriesReader is ~2x slower than vtkGDCMImageReader (from GDCM2).
25 I compared both codes and I think the difference is the extra copy which
26 itkImageSeriesReader makes from ImageFileReader's output to its own output
27 (ImageSeriesReader::GenerateData() line 393).
31 /* ====================================================================
34 3D, 2D+T, 3D+T, n*2D+T, 4D images are not always stored the same way :
35 a single 'Dicom Serie',
36 several 'Dicom series' within a single directory
37 several 'Dicom series' within several directories
38 A 'Dicom Serie' doesn't mean always the same thing :
39 a given Slice along the time
40 a given Volume at a given time
41 Sometimes, an image within a serie is so artefacted than user decides to replace it
44 User needs to be aware, *only him* knows want he wants to do.
45 vtkGdcm4DSplitter class does the job for him
46 (despite its name, it works on 3D or 2D+T images too)
48 ==> To (try to) understand how the file store is organised, user is deeply encourage to use something like:
50 PrintFile dirin=Root_Directory_holding_the_images rec > 1.txt
52 open it with any test editor, and/or (Linux users)
53 grep a_supposed_to_be_string_of_interest 1.txt
56 Aware user will have to specify some points :
62 bool setDirName(std::string &dirName);
63 - a list of directories
64 bool setVectDirName(std::vector<std::string> &vectDirName);
66 bool setVectFileName(std::vector<std::string> &vectFileName);
67 - a list of gdcm::File*
68 bool setVectGdcmFile(std::vector<GDCM_NAME_SPACE::File *> &vectGdcmFile);
70 - Recursive directory exploration
71 void setRecursive(bool recursive);
73 . Choose 'split' criterion :
74 ---------------------------
76 - ImagePositionPatient
77 void setSplitOnPosition();
78 - ImageOrientationPatient
79 void setSplitOnOrientation();
81 void setSplitOnTag(unsigned short splitGroup, unsigned short splitElem);
82 void setSplitConvertToFloat(bool conv);
83 - UserDefined Function
84 void setSortOnUserFunction (FoncComp f);
86 for 'true' 3D image sets :
87 - if you want to get a single 3D vtkImageData, use SplitOnOrientation -i.e. no split-
88 - if you want to get a vector of 2D vtkImageData, use SplitOnPosition -i.e. one slice in each 'XCoherent fileset'-
90 for 'true' 4D multi-orientation image sets (i.e. a stack of n axial + m sagital + p coronal images, at different instants ...)
91 --> this is 5D, right?
92 (almost) nothing done, yet :
94 . Use a first time vtkGdcm4DSplitter with setSplitOnOrientation();
95 . Get the VectGdcmFileLists (a std::vector of 'XCoherent fileset')
96 . use vtkGdcm4DSplitter, with as many setVectGdcmFile(std::vector<GDCM_NAME_SPACE::File *> &vectGdcmFile) you need
97 one per element of std::vector<GDCM_NAME_SPACE::File *>
98 think on 'spliting' and 'sorting' it, according to your needs.
100 . Choose 'sort' criterion :
101 --------------------------
103 - ImagePositionPatient
104 void setSortOnPosition();
105 - ImageOrientationPatient
106 ==> Only in your dreams!
107 ==> or, please, write a IOP sorter ...
108 - UserDefined Function
109 void setSortOnUserFunction (FoncComp f);
111 void setSortOnFileName()
113 - Choose 'flip' option
114 ----------------------
115 VTK reader 'flop upside down' the images to display them correctly ?!?
116 When a 'math guy' gets the image, the first thing he does is flipping it again.
117 Avoid loosing CPU time using :
118 void SetFlipY(bool); // Wrong = no flip
127 // -a single vtkImageData:
128 // vtkImageData *GetImageData();
129 - a vector of vtkImageData
130 std::vector<vtkImageData*> *GetImageDataVector();
132 ===================================================================== */
134 #include "vtkGdcmReader.h"
135 #include "vtkGdcm4DSplitter.h"
137 #include "gdcmSerieHelper.h" // for ImagePositionPatientOrdering()
138 #include <stdlib.h> // for atof
140 // Constructor / Destructor
142 * \brief Constructor from a given vtkGdcm4DSplitter
144 vtkGdcm4DSplitter::vtkGdcm4DSplitter() :
145 SplitOnPosition(false), SplitOnOrientation(false), SplitOnTag(false),
146 SplitGroup(0), SplitElem(0), SplitConvertToFloat(false),
148 SortOnPosition(false), SortOnOrientation(false), SortOnTag(false), SortOnFileName(false), SortOnUserFunction(false),
149 SortGroup(0), SortElem(0), SortConvertToFloat(false),
151 Recursive(false), TypeDir(0),
158 * \brief Canonical destructor.
160 vtkGdcm4DSplitter::~vtkGdcm4DSplitter()
162 /// \TODO : delete everything that must be!
165 // Locate Data to process
166 // ======================
168 * \brief sets the directories exploration mode
169 * @param recursive whether we want explore recursively the root Directory
171 void vtkGdcm4DSplitter::setRecursive(bool recursive)
177 * \brief Sets the root Directory to get the images from
178 * @param dirName name of the directory to deal with
180 bool vtkGdcm4DSplitter::setDirName(std::string &dirName)
182 if ( ! GDCM_NAME_SPACE::DirList::IsDirectory(dirName) )
184 std::cout << "[" << dirName << "] is NOT a directory" << std::endl;
193 * \brief Sets a list of Directories to get the images from
194 * @param vectDirName vector of directory names to deal with
196 bool vtkGdcm4DSplitter::setVectDirName(std::vector<std::string> &vectDirName)
198 int nbDir = vectDirName.size();
199 for (int iDir=0; iDir<nbDir; iDir++)
201 if ( ! GDCM_NAME_SPACE::DirList::IsDirectory(vectDirName[iDir]) )
203 std::cout << "[" << vectDirName[iDir] << "] is NOT a directory" << std::endl;
208 VectDirName = vectDirName;
214 * \brief Sets a list of files read
215 * @param vectFileName vector of file names to deal with
217 bool vtkGdcm4DSplitter::setVectFileName(std::vector<std::string> &vectFileName)
219 if ( vectFileName.size() == 0)
221 std::cout << "[ vectFileName ] : empty list" << std::endl;
224 VectFileName = vectFileName;
230 * \brief Sets a vector of gdcm::File *
231 * @param vectGdcmFileName vector of gdcm::File *
234 bool vtkGdcm4DSplitter::setVectGdcmFile(GDCM_NAME_SPACE::FileList *vectGdcmFile)
236 if ( vectGdcmFile->size() == 0)
238 std::cout << "[ vectGdcmFile ] : empty list" << std::endl;
242 VectGdcmFile = vectGdcmFile;
250 * \brief asks for splitting Filesets according to the Position
253 void vtkGdcm4DSplitter::setSplitOnPosition()
255 SplitOnPosition=true;
256 SplitOnOrientation=false;
260 * \brief asks for splitting Filesets according to the Orientation
262 void vtkGdcm4DSplitter::setSplitOnOrientation()
264 SplitOnPosition=false;
265 SplitOnOrientation=true;
269 * \brief asks for splitting Filesets according to the value of a given Tag
270 * @param group group number of the target Element
271 * @param element element number of the target Element
273 void vtkGdcm4DSplitter::setSplitOnTag(unsigned short int splitGroup, unsigned short int splitElem)
275 SplitOnPosition=false;
276 SplitOnOrientation=false;
278 SplitGroup=splitGroup;
282 * \brief asks for converting to 'float' the tag values used as a splitting criteria (lexicographic order may not be suitable)
284 void vtkGdcm4DSplitter::setSplitConvertToFloat(bool conv) {SplitConvertToFloat=conv;}
287 * \brief asks for splitting Filesets according to what was asked for (no sorting, no reading data)
289 void vtkGdcm4DSplitter::setSplitOnly(bool s)
296 void vtkGdcm4DSplitter::setSortOnPosition()
299 SortOnOrientation=false;
301 SortOnFileName=false;
302 SortOnUserFunction=false;
306 // use setSortOnUserFunction, instead!
307 // void setSortOnTag(unsigned short int sortGroup, unsigned short int sortElem)
309 // SortOnPosition=false;
310 // SortOnOrientation=false;
312 // SortOnFileName=false;
313 // SortOnUserFunction=false;
314 // SortGroup=sortGroup; SortElem=sortElem;
319 * \brief sets a user supplied function (comparison)
320 * @param f comparison function
322 void vtkGdcm4DSplitter::setSortOnUserFunction (FoncComp f)
324 UserCompareFunction=f;
325 SortOnPosition=false;
326 SortOnOrientation=false;
328 SortOnFileName=false;
329 SortOnUserFunction=true;
333 // void setSortConvertToFloat(bool conv)
335 // SortConvertToFloat=conv;
339 * \brief asks for sorting the images, according to their File Name
341 void vtkGdcm4DSplitter::setSortOnFileName()
343 SortOnPosition=false;
344 SortOnOrientation=false;
347 SortOnUserFunction=false;
351 std::vector<vtkImageData*> * vtkGdcm4DSplitter::GetImageDataVector()
356 return ImageDataVector;
359 std::vector<GDCM_NAME_SPACE::FileList *> *vtkGdcm4DSplitter::GetVectGdcmFileLists()
364 GDCM_NAME_SPACE::XCoherentFileSetmap::iterator it;
365 for ( it = xcm.begin();
369 VectGdcmFileLists.push_back((*it).second);
371 return &VectGdcmFileLists;
375 vtkImageData *vtkGdcm4DSplitter::GetImageData()
379 return (*ImageDataVector)[0];
383 bool vtkGdcm4DSplitter::CompareOnSortTagConvertToFloat(GDCM_NAME_SPACE::File *file1, GDCM_NAME_SPACE::File *file2)
385 /* if (verbose) printf ("%04x %04x\n", this->SortGroup,this->SortElem);
386 if (verbose) std :: cout << file1->GetEntryString(SortGroup,SortElem).c_str() << " : "
387 << atof(file1->GetEntryString(SortGroup,SortElem).c_str())
390 // return atof(file1->GetEntryString(vtkGdcm4DSplitter::SortGroup,vtkGdcm4DSplitter::SortElem).c_str()) < atof(file2->GetEntryString(vtkGdcm4DSplitter::SortGroup,vtkGdcm4DSplitter::SortElem).c_str());
391 return atof(file1->GetEntryString(SortGroup,SortElem).c_str()) < atof(file2->GetEntryString(SortGroup,SortElem).c_str());
394 bool vtkGdcm4DSplitter::CompareOnSortTag(GDCM_NAME_SPACE::File *file1, GDCM_NAME_SPACE::File *file2)
396 return file1->GetEntryString(vtkGdcm4DSplitter::SortGroup,vtkGdcm4DSplitter::SortElem) < file2->GetEntryString(vtkGdcm4DSplitter::SortGroup,vtkGdcm4DSplitter::SortElem);
400 bool vtkGdcm4DSplitter::Go()
402 if (!SplitOnPosition && !SplitOnOrientation && !SplitOnTag)
404 ///\TODO (?) Throw an exception "Choose Splitting mode before!"
405 std::cout << "Choose Splitting mode before!" << std::endl;
411 entree nom de directory / Vecteur de noms?
413 recuperer la liste des gdcm::File*
414 passer a SerieHelper (pas de check du Serie UID)
417 trier chaque Coherent file set
418 passer chacun a un vtkGcdmReader
419 retourner le (vecteur de) vtkImageData
422 GDCM_NAME_SPACE::SerieHelper *s;
423 s = GDCM_NAME_SPACE::SerieHelper::New();
425 GDCM_NAME_SPACE::File *f;
426 GDCM_NAME_SPACE::DirListType fileNames;
429 // Fill fileNames with the user supplied file names (in any)
430 // --------------------------------------
434 ///\TODO (?) Throw an exception "Set input Directory name(s) / file names before!"
435 std::cout << "Set input Directory name(s) / file names before!" << std::endl;
438 else if (TypeDir == 1)
440 GDCM_NAME_SPACE::DirList dirlist(DirName, Recursive); // NO recursive exploration
441 fileNames = dirlist.GetFilenames(); // all the file names
444 else if (TypeDir == 2)
446 int nbDir = VectDirName.size();
447 GDCM_NAME_SPACE::DirListType tmpFileNames;
448 for (int iDir=0; iDir<nbDir; iDir++)
450 GDCM_NAME_SPACE::DirList dirlist(VectDirName[iDir], Recursive);
451 tmpFileNames = dirlist.GetFilenames();
452 // Concat two std::vector
453 //vector1.insert( vector1.end(), vector2.begin(), vector2.end() );
454 fileNames.insert( fileNames.end(), tmpFileNames.begin(), tmpFileNames.end() );
457 else if (TypeDir == 3)
459 fileNames=VectFileName;
463 // Fill l with the gdcm::File* corresponding to the files
464 // --------------------------------------
467 GDCM_NAME_SPACE::FileList *l = new GDCM_NAME_SPACE::FileList; // (set of gdcm::File)
471 // User passed a vector of gdcm::File*
476 double floatTagvalue;
477 // Loop on all the gdcm-readable files
478 for (GDCM_NAME_SPACE::DirListType::iterator it = fileNames.begin();
479 it != fileNames.end();
482 int maxSize = 0x7fff; // load Elements of any length
483 f = GDCM_NAME_SPACE::File::New();
484 f->SetMaxSizeLoadEntry(maxSize);
485 f->SetFileName( *it );
489 std::cout << " Fail to load [" << *it << "]" << std::endl;
495 // Split the gdcm::File* set, according to user's requierements
496 // ------------------------------------------------------------
498 if (SplitOnOrientation)
500 s->SetDropDuplicatePositions(false);
501 xcm = s->SplitOnOrientation(l);
503 else if (SplitOnPosition)
505 s->SetDropDuplicatePositions(true);
506 xcm = s->SplitOnPosition(l);
508 // the key of xcm follows lexicographical order
509 // (that may be different than the 'distance' order)
510 // we have to reorganize it!
512 reorgXCoherentFileSetmap(xcm);
516 s->SetDropDuplicatePositions(false);
518 // Crashes if DataElement not found
519 //std:: cout << GDCM_NAME_SPACE::Global::GetDicts()->GetDefaultPubDict()->GetEntry(groupelem[0],groupelem[1])->GetName() << std::endl;
520 if ( ! SplitConvertToFloat )
521 xcm = s->SplitOnTagValue(l, SplitGroup, SplitElem);
524 xcm = s->SplitOnTagValueConvertToFloat(l, SplitGroup, SplitElem);
530 if(verbose) std::cout << "Empty XCoherent File Set after 'split' ?!?" << std::endl;
534 else if (xcm.size() == 1)
544 // ------------------------------------------------------------
546 ImageDataVector = new std::vector<vtkImageData*>;
547 /// \TODO move inside the loop, or be clever using vtk!
548 // vtkGdcmReader *reader = vtkGdcmReader::New(); // move inside the loop, or be clever using vtk!
550 // XCoherentFileSetmap map < critère de split, FileList (= std::vector de gdcm::File*) >
552 for (GDCM_NAME_SPACE::XCoherentFileSetmap::iterator i = xcm.begin();
556 vtkGdcmReader *reader = vtkGdcmReader::New(); /// \TODO FIXME : unable to delete!
558 reader->SetFlipY(FlipY);
559 // better user SetFileLowerLeft()
560 /// \TODO : modify vtkGdcmReader !
562 std::cout << " --- xCoherentName = [" << (*i).first << "]" << std::endl;
566 if (verbose) std::cout << "SortOnPosition" << std::endl;
567 // (will be IPPSorter, in GDCM2)
568 s->ImagePositionPatientOrdering((*i).second);
569 if (verbose) std::cout << "out of SortOnPosition" << std::endl;
572 else if (SortOnOrientation)
574 if (verbose) std::cout << "SortOnOrientation" << std::endl;
575 /// \TODO (?) SortOnOrientation()
577 // we still miss an algo to sort an Orientation, given by 6 cosines!
578 // Anything like this, in GDCM2?
579 std::cout << "SortOnOrientation : not so easy - I(mage)O(rientation)P(atient)Sorter still missing! -" << std::endl;
580 // have a look at SerieHelper::SplitOnOrientation() to have an idea of the mess!
582 //Better sort on the file name, right now...
583 s->FileNameOrdering((*i).second);
586 else if (SortOnFileName)
588 if (verbose) std::cout << "SortOnFileName" << std::endl;
589 if (verbose) std::cout << "taille " << ((*i).second)->size() << std::endl;
591 s->FileNameOrdering((*i).second);
592 if (verbose) std::cout << "Out of SortOnFileName" << std::endl;
597 if (verbose) std::cout << "SortOnTag" << std::endl;
598 printf ("--> %04x %04x\n", SortGroup,SortElem);
599 std::cout << "Sorry, troubles not solved yet; use SortOnUserFunction, right now!" << std::endl;
601 /* ==> WARNING : This one has troubles; do NOT use it, right now!
602 // a pointer to fonction cannot be casted as a pointer to member function!
603 // Use SortOnUserFunction, instead!
605 // if ( SortConvertToFloat )
606 // s->SetUserLessThanFunction( reinterpret_cast<bool (*)(gdcm13::File*, gdcm13::File*)>
607 ( &vtkGdcm4DSplitter::CompareOnSortTagConvertToFloat));
609 // s->SetUserLessThanFunction( reinterpret_cast<bool (*)(gdcm13::File*, gdcm13::File*)>
610 ( &vtkGdcm4DSplitter::CompareOnSortTag));
612 // Anything like this, in GDCM2?
613 // s->UserOrdering((*i).second);
616 //if (verbose) std::cout << "Out of SortOnTag" << std::endl;
617 std::cout << "NO ordering performed :-( " << std::endl;
620 else if (SortOnUserFunction)
622 if (verbose) std::cout << "SortOnUserFunction" << std::endl;
623 s->SetUserLessThanFunction( UserCompareFunction );
624 // Anything like this, in GDCM2?
625 s->UserOrdering((*i).second);
626 if (verbose) std::cout << "Out of SortOnUserFunction" << std::endl;
629 reader->SetCoherentFileList((*i).second);
632 /// \TODO : remove the following
634 std::cout << "reader->GetOutput() :" << std::endl;
635 reader->GetOutput()->PrintSelf(std::cout, vtkIndent(2));
638 ImageDataVector->push_back(reader->GetOutput() );
640 std::vector<vtkImageData*>::iterator it;
642 for(it=ImageDataVector->begin(); it!=ImageDataVector->end(); ++it) {
643 std::cout << "-in vtkGdcm4DSplitter--------------------------" << std::endl;
644 (*it)->PrintSelf(std::cout, vtkIndent(2));
646 std::cout << std::endl;
649 //reader->Delete(); // \TODO : fix
658 void vtkGdcm4DSplitter::reorgXCoherentFileSetmap(GDCM_NAME_SPACE::XCoherentFileSetmap &xcm)
661 the key of the 'XCoherentFileSetmap', is a std::string, used as if it was found in the Dicom header
662 Normaly(?), it's suitable for almost everything ...
663 ... but the 'Image Position Patient'.
664 We need to order the 'XCoherentFileSetmap' (NOT the content of each XCoherentFileSet!) according to the IPP,
665 using Jolinda Smith's algorithm.
666 (we use a subset of the one defined in gdcm::SerieHelper)
671 std::vector<ELEM> vectElem;
678 GDCM_NAME_SPACE::File *file;
684 for (GDCM_NAME_SPACE::XCoherentFileSetmap::iterator i = xcm.begin();
689 std::cout << "--- xCoherentName = [" << (*i).first << "]" << std::endl;
691 e.strIPP = (*i).first;
692 e.file = *(((*i).second)->begin()); // all the gdcm::File of a given xcm item *have* the same IPP; first one is enough
694 vectElem.push_back(e);
696 sortVectElem(&vectElem);
698 // now, create the final std::map !
699 // final_xcm<to_str(e.dist , xcm[e.strIPP]>
701 // check what we need to free !
705 std::string str_dist;
706 int lgr=vectElem.size();
707 GDCM_NAME_SPACE::XCoherentFileSetmap final_xcm;
708 for (int i2=0; i2<lgr; i2++)
710 dist = (vectElem[i2].dist*1000);
711 sprintf(c_dist,"%010d",dist);
714 std::cout << "dist " << vectElem[i2].dist
715 << " str_dist " << str_dist
716 << " IPP " << vectElem[i2].strIPP
720 final_xcm[str_dist] = xcm[vectElem[i2].strIPP];
723 /// \TODO : check what needs to be cleared // JPR
730 bool vtkGdcm4DSplitter::sortVectElem(std::vector<ELEM> *fileList)
732 //based on Jolinda Smith's algorithm
733 // NOTE : if you need to use Jolinda Smith's algorithm, get the one inside gdcm::SerieHelper
734 // this one is a light version.
736 //Tags always use the same coordinate system, where "x" is left
737 //to right, "y" is posterior to anterior, and "z" is foot to head (RAH).
739 //iop is calculated based on the file file
744 double min = 0, max = 0;
747 //double ZSpacing; // useless here! // JPR
748 bool DirectOrder = true; // remove it!
750 // ZSpacing = -1.0; // will be updated if process doesn't fail
752 //std::multimap<double,File *> distmultimap; // JPR
753 std::multimap<double,ELEM> distmultimap; // JPR
755 // Use a multimap to sort the distances from 0,0,0
756 //for ( FileList::const_iterator // JPR
757 for ( std::vector<ELEM>::iterator // JPR
758 it = fileList->begin();
759 it != fileList->end(); ++it )
761 //gdcmDebugMacro("deal with " << (*it)->file->GetFileName() );
764 (*it).file->GetImageOrientationPatient( cosines );
766 // The "Image Orientation Patient" tag gives the direction cosines
767 // for the rows and columns for the three axes defined above.
768 // Typical axial slices will have a value 1/0/0/0/1/0:
769 // rows increase from left to right,
770 // columns increase from posterior to anterior. This is your everyday
771 // "looking up from the bottom of the head with the eyeballs up" image.
773 // The "Image Position Patient" tag gives the coordinates of the first
774 // voxel in the image in the "RAH" coordinate system, relative to some
777 // First, calculate the slice normal from IOP :
779 // You only have to do this once for all slices in the volume. Next,
780 // for each slice, calculate the distance along the slice normal
781 // using the IPP ("Image Position Patient") tag.
782 // ("dist" is initialized to zero before reading the first slice) :
784 normal[0] = cosines[1]*cosines[5] - cosines[2]*cosines[4];
785 normal[1] = cosines[2]*cosines[3] - cosines[0]*cosines[5];
786 normal[2] = cosines[0]*cosines[4] - cosines[1]*cosines[3];
788 // For each slice (here : the first), calculate the distance along
789 // the slice normal using the IPP tag
791 ipp[0] = (*it).file->GetXOrigin();
792 ipp[1] = (*it).file->GetYOrigin();
793 ipp[2] = (*it).file->GetZOrigin();
796 for ( int i = 0; i < 3; ++i )
798 dist += normal[i]*ipp[i];
801 //gdcmDebugMacro("dist : " << dist);
802 distmultimap.insert(std::pair<const double,ELEM>(dist, *it));
809 // Next, for each slice, calculate the distance along the slice normal
811 ipp[0] = (*it).file->GetXOrigin();
812 ipp[1] = (*it).file->GetYOrigin();
813 ipp[2] = (*it).file->GetZOrigin();
816 for ( int i = 0; i < 3; ++i )
818 dist += normal[i]*ipp[i];
821 (*it).dist = dist; // JPR
823 distmultimap.insert(std::pair<const double,ELEM>(dist, *it));
824 //gdcmDebugMacro("dist : " << dist);
825 min = (min < dist) ? min : dist;
826 max = (max > dist) ? max : dist;
830 // gdcmDebugMacro("After parsing vector, nb of elements : " << fileList->size() );
832 /*Useless here. // JPR
834 // Find out if min/max are coherent
837 gdcmWarningMacro("Looks like all images have the exact same image position. "
838 << "No PositionPatientOrdering sort performed. "
839 << "No 'ZSpacing' calculated! ");
844 /* Useless here, 'split' already done. // JPR
846 // Check to see if image shares a common position
848 for (std::multimap<double, File *>::iterator it2 = distmultimap.begin();
849 it2 != distmultimap.end();
853 gdcmDebugMacro("Check if image shares a common position : " << ((*it2).second).file->GetFileName() );
855 if (distmultimap.count((*it2).first) != 1)
857 gdcmWarningMacro("File: ["
858 << ((*it2).second->GetFileName())
859 << "] : more than ONE file at distance: '"
861 << " (position is not unique!) "
862 << "No PositionPatientOrdering sort performed. "
863 << "No 'ZSpacing' calculated! ");
870 if (! DropDuplicatePositions)
876 // Now, we can calculate Z Spacing as the difference
877 // between the "dist" values for the first two slices.
879 // The following (un)-commented out code is let here
880 // to be re-used by whomsoever is interested...
882 //std::multimap<double, ELEM>::iterator it5 = distmultimap.begin();
883 //double d1 = (*it5).first;
885 //double d2 = (*it5).first;
887 //if (ZSpacing < 0.0)
888 // ZSpacing = - ZSpacing;
890 fileList->clear(); // doesn't delete list elements, only nodes
892 // Acording to user requierement, we sort direct order or reverse order.
895 for (std::multimap<double, ELEM>::iterator it3 = distmultimap.begin();
896 it3 != distmultimap.end();
899 fileList->push_back( (*it3).second );
901 /* useless here! // JPR
903 if (DropDuplicatePositions)
905 // ImagePositionPatientOrdering wrong duplicates are found ???
906 // --> fixed. See comment
908 it3 = distmultimap.upper_bound((*it3).first); // skip all duplicates
909 // the upper_bound function increments the iterator to the next non-duplicate entry
910 // The for loop iteration also increments the iterator, which causes the code to skip every other image
911 // --> decrement the iterator after the upper_bound function call
913 if (it3 == distmultimap.end() ) // if last image, stop iterate
919 else // user asked for reverse order
921 std::multimap<double, ELEM>::const_iterator it4;
922 it4 = distmultimap.end();
926 fileList->push_back( (*it4).second );
928 /* useless here // JPR
930 if (DropDuplicatePositions) // skip all duplicates
932 // lower_bound finds the next element that is
933 // less than or *equal to* the current value!
934 //it4 = distmultimap.lower_bound((*it4).first);
937 std::multimap<double, ELEM>::const_iterator itPrev = it4;
938 while (itPrev->first == it4->first)
942 if (it4 == distmultimap.begin() ) // if first image, stop iterate
946 } while (it4 != distmultimap.begin() );
949 distmultimap.clear();