]> Creatis software - gdcm.git/blob - src/gdcmSerieHelper.cxx
Comments
[gdcm.git] / src / gdcmSerieHelper.cxx
1 /*=========================================================================
2                                                                                 
3   Program:   gdcm
4   Module:    $RCSfile: gdcmSerieHelper.cxx,v $
5   Language:  C++
6   Date:      $Date: 2005/10/21 16:02:01 $
7   Version:   $Revision: 1.27 $
8                                                                                 
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.
12                                                                                 
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.
16                                                                                 
17 =========================================================================*/
18
19 #include "gdcmSerieHelper.h"
20 #include "gdcmDirList.h"
21 #include "gdcmFile.h"
22 #include "gdcmDictEntry.h" // for TranslateToKey
23 #include "gdcmDebug.h"
24 #include "gdcmUtil.h"
25
26 #include <math.h>
27 #include <vector>
28 #include <map>
29 #include <algorithm>
30 #include <stdio.h>  //for sscanf
31
32 namespace gdcm 
33 {
34 //-----------------------------------------------------------------------------
35
36 //-----------------------------------------------------------------------------
37 // Constructor / Destructor
38 /**
39  * \brief   Constructor from a given SerieHelper
40  */
41 SerieHelper::SerieHelper()
42 {
43    ClearAll();
44    UserLessThanFunction = 0;
45    DirectOrder = true;
46 }
47
48 /**
49  * \brief   Canonical destructor.
50  */
51 SerieHelper::~SerieHelper()
52 {
53    ClearAll();
54 }
55
56 /**
57  * \brief  - Preventively, clear everything at constructor time.
58  *         - use it at destructor time.
59  */
60 void SerieHelper::ClearAll()
61 {
62    // For all the 'Single SerieUID' Filesets that may already exist 
63    FileList *l = GetFirstSingleSerieUIDFileSet();
64    while (l)
65    { 
66       // For all the gdcm::File of a File set
67       for (gdcm::FileList::iterator it  = l->begin();
68                                     it != l->end(); 
69                                   ++it)
70       {
71          delete *it; // remove each entry
72       }
73       l->clear();
74       delete l;     // remove the container
75       l = GetNextSingleSerieUIDFileSet();
76    }
77 }
78
79 //-----------------------------------------------------------------------------
80
81 //-----------------------------------------------------------------------------
82
83 // Public
84 /**
85  * \brief add a gdcm::File to the Fileset corresponding to its Serie UID
86  * @param   filename Name of the file to deal with
87  */
88 void SerieHelper::AddFileName(std::string const &filename)
89 {
90    // Create a DICOM file
91    File *header = new File ();
92    header->SetLoadMode(LoadMode);
93    header->SetFileName( filename ); 
94    header->Load();
95
96    if ( header->IsReadable() )
97    {
98       int allrules = 1;
99       // First step : the user defined a set of rules for the DICOM file
100       // he is looking for.
101       // Make sure the file corresponds to his set of rules:
102
103       std::string s;
104       for(SerieExRestrictions::iterator it2 = ExRestrictions.begin();
105           it2 != ExRestrictions.end();
106           ++it2)
107       {
108          const ExRule &r = *it2;
109          s = header->GetEntryString( r.group, r.elem );
110          if ( !Util::CompareDicomString(s, r.value.c_str(), r.op) )
111          {
112            // Argh ! This rule is unmatched; let's just quit
113            allrules = 0;
114            break;
115          }
116       }
117
118       if ( allrules ) // all rules are respected:
119       {
120          // Allright! we have a found a DICOM that matches the user expectation. 
121          // Let's add it!
122
123          // 0020 000e UI REL Series Instance UID
124          const std::string &uid = header->GetEntryString(0x0020, 0x000e);
125          // if uid == GDCM_UNFOUND then consistently we should find GDCM_UNFOUND
126          // no need here to do anything special
127
128          if ( SingleSerieUIDFileSetHT.count(uid) == 0 )
129          {
130             gdcmDebugMacro(" New Serie UID :[" << uid << "]");
131             // create a std::list in 'uid' position
132             SingleSerieUIDFileSetHT[uid] = new FileList;
133          }
134          // Current Serie UID and DICOM header seems to match; add the file:
135          SingleSerieUIDFileSetHT[uid]->push_back( header );
136       }
137       else
138       {
139          // at least one rule was unmatched we need to deallocate the file:
140          delete header;
141       }
142    }
143    else
144    {
145       gdcmWarningMacro("Could not read file: " << filename );
146       delete header;
147    }
148 }
149
150 /**
151  * \brief add a gdcm::File to the first (and supposed to be unique) file set
152  *        of the gdcm::SerieHelper.
153  * \warning : this method should be used by aware users only!
154  *           Passing a gdcm::File* has the same effect than passing a file name!
155  * \todo : decide which one is wrong (the method, or the commentary)!
156  *           the following comment doesn't match the method :-(
157  *            User is supposed to know the files he want to deal with
158  *           and consider them they belong to the same Serie
159  *           (even if their Serie UID is different)
160  *           user will probabely OrderFileList() this list (actually, ordering
161  *           user choosen gdm::File is the sole interest of this method)
162  *           Moreover, using vtkGdcmReader::SetCoherentFileList() will avoid
163  *           vtkGdcmReader parsing twice the same files. 
164  *           *no* coherence check is performed, but those specified
165  *           by SerieHelper::AddRestriction()
166  * @param   header gdcm::File* of the file to deal with
167  */
168 void SerieHelper::AddGdcmFile(File *header)
169 {
170       int allrules = 1;
171       // First step the user has defined a set of rules for the DICOM 
172       // he is looking for.
173       // make sure the file correspond to his set of rules:
174       for(SerieRestrictions::iterator it =  Restrictions.begin();
175                                       it != Restrictions.end();
176                                     ++it)
177       {
178          const Rule &r = *it;
179          const std::string s;// = header->GetEntryValue( r.first );
180          if ( !Util::DicomStringEqual(s, r.second.c_str()) )
181          {
182            // Argh ! This rule is unmatch let's just quit
183            allrules = 0;
184            break;
185          }
186       }
187       if ( allrules ) // all rules are respected:
188       {
189          // Allright ! we have a found a DICOM that match the user expectation. 
190          // Let's add it !
191
192          const std::string &uid = "0";
193          // Serie UID of the gdcm::File* may be different.
194          // User is supposed to know what he wants
195
196          if ( SingleSerieUIDFileSetHT.count(uid) == 0 )
197          {
198             gdcmDebugMacro(" New Serie UID :[" << uid << "]");
199             // create a std::list in 'uid' position
200             SingleSerieUIDFileSetHT[uid] = new FileList;
201          }
202          // Current Serie UID and DICOM header seems to match; add the file:
203          SingleSerieUIDFileSetHT[uid]->push_back( header );
204       }
205          // Even if a rule was unmatch we don't deallocate the gdcm::File:
206 }
207
208 /**
209  * \brief add a rules for restricting a DICOM file to be in the serie we are
210  * trying to find. For example you can select only the DICOM file from a
211  * directory which would have a particular EchoTime==4.0.
212  * This method is a user level, value is not required to be formatted as a DICOM
213  * string
214  * @param   group  Group number of the target tag.
215  * @param   elem Element number of the target tag.
216  * @param value value to be checked to exclude File
217  * @param op  operator we want to use to check
218  */
219 void SerieHelper::AddRestriction(uint16_t group, uint16_t elem, 
220                                  std::string const &value, int op)
221 {
222    ExRule r;
223    r.group = group;
224    r.elem  = elem;
225    r.value = value;
226    r.op    = op;
227    ExRestrictions.push_back( r ); 
228 }
229
230 #ifndef GDCM_LEGACY_REMOVE
231 /* *
232  * \brief add a rules for restricting a DICOM file to be in the serie we are
233  * trying to find. For example you can select only the DICOM file from a
234  * directory which would have a particular EchoTime==4.0.
235  * This method is a user level, value is not required to be formatted as a DICOM
236  * string
237  * @param   group  Group number of the target tag.
238  * @param   elem Element number of the target tag.
239  * @param value value to be checked to exclude File 
240  * @deprecated use : AddRestriction(uint16_t group, uint16_t elem, 
241  *                                 std::string const &value, int op);
242  */
243 void SerieHelper::AddRestriction(TagKey const &key, std::string const &value)
244 {
245    Rule r;
246    r.first = key;
247    r.second = value;
248    Restrictions.push_back( r ); 
249 }
250 #endif
251
252 /**
253  * \brief Sets the root Directory
254  * @param   dir Name of the directory to deal with
255  * @param recursive whether we want explore recursively the root Directory
256  */
257 void SerieHelper::SetDirectory(std::string const &dir, bool recursive)
258 {
259    DirList dirList(dir, recursive); // OS specific
260   
261    DirListType filenames_list = dirList.GetFilenames();
262    for( DirListType::const_iterator it = filenames_list.begin(); 
263         it != filenames_list.end(); ++it)
264    {
265       AddFileName( *it );
266    }
267 }
268
269 /**
270  * \brief Sorts the given Fileset
271  * \warning This could be implemented in a 'Strategy Pattern' approach
272  *          But as I don't know how to do it, I leave it this way
273  *          BTW, this is also a Strategy, I don't know this is 
274  *          the best approach :)
275  */
276 void SerieHelper::OrderFileList(FileList *fileSet)
277 {
278
279    if ( SerieHelper::UserLessThanFunction )
280    {
281       UserOrdering( fileSet );
282       return; 
283    }
284    else if ( ImagePositionPatientOrdering( fileSet ) )
285    {
286       return ;
287    }
288    else if ( ImageNumberOrdering(fileSet ) )
289    {
290       return ;
291    }
292    else  
293    {
294       FileNameOrdering(fileSet );
295    }
296 }
297
298 /**
299  * \brief Elementary coherence checking of the files with the same Serie UID
300  * Only sizes and pixel type are checked right now ...
301  */ 
302 bool SerieHelper::IsCoherent(FileList *fileSet)
303 {
304    if(fileSet->size() == 1)
305    return true;
306
307    FileList::const_iterator it = fileSet->begin();
308
309    int nX = (*it)->GetXSize();
310    int nY = (*it)->GetYSize();
311    int pixelSize = (*it)->GetPixelSize();
312
313    it ++;
314    for ( ;
315          it != fileSet->end();
316        ++it)
317    {
318       if ( (*it)->GetXSize() != nX )
319          return false;
320       if ( (*it)->GetYSize() != nY )
321          return false;
322       if ( (*it)->GetPixelSize() != pixelSize )
323          return false;
324       // probabely more is to be checked (?)
325    }
326    return true;
327 }
328
329 #ifndef GDCM_LEGACY_REMOVE
330 /* *
331  * \ brief   accessor (DEPRECATED :  use GetFirstSingleSerieUIDFileSet )
332  *          Warning : 'coherent' means here they have the same Serie UID
333  * @ return  The first FileList if found, otherwhise NULL
334  */
335 FileList *SerieHelper::GetFirstCoherentFileList()
336 {
337    ItFileSetHt = SingleSerieUIDFileSetHT.begin();
338    if ( ItFileSetHt != SingleSerieUIDFileSetHT.end() )
339       return ItFileSetHt->second;
340    return NULL;
341 }
342
343 /* *
344  * \ brief   accessor (DEPRECATED :  use GetNextSingleSerieUIDFileSet )
345  *          Warning : 'coherent' means here they have the same Serie UID
346  * \ note : meaningfull only if GetFirstCoherentFileList() already called 
347  * @ return  The next FileList if found, otherwhise NULL
348  */
349 FileList *SerieHelper::GetNextCoherentFileList()
350 {
351    gdcmAssertMacro (ItFileSetHt != SingleSerieUIDFileSetHT.end());
352   
353    ++ItFileSetHt;
354    if ( ItFileSetHt != SingleSerieUIDFileSetHT.end() )
355       return ItFileSetHt->second;
356    return NULL;
357 }
358
359 /* *
360  * \ brief   accessor (DEPRECATED :  use GetSingleSerieUIDFileSet )
361   *          Warning : 'coherent' means here they have the same Serie UID
362  * @ param SerieUID SerieUID
363  * \ return  pointer to the FileList if found, otherwhise NULL
364  */
365 FileList *SerieHelper::GetCoherentFileList(std::string SerieUID)
366 {
367    if ( SingleSerieUIDFileSetHT.count(SerieUID) == 0 )
368       return 0;     
369    return SingleSerieUIDFileSetHT[SerieUID];
370 }
371 #endif
372
373
374 /**
375  * \brief   Get the first Fileset while visiting the SingleSerieUIDFileSetmap
376  * @return  The first FileList (SingleSerieUIDFileSet) if found, otherwhise 0
377  */
378 FileList *SerieHelper::GetFirstSingleSerieUIDFileSet()
379 {
380    ItFileSetHt = SingleSerieUIDFileSetHT.begin();
381    if ( ItFileSetHt != SingleSerieUIDFileSetHT.end() )
382       return ItFileSetHt->second;
383    return NULL;
384 }
385
386 /**
387  * \brief   Get the next Fileset while visiting the SingleSerieUIDFileSetmap
388  * \note : meaningfull only if GetNextSingleSerieUIDFileSet() already called 
389  * @return  The next FileList (SingleSerieUIDFileSet) if found, otherwhise 0
390  */
391 FileList *SerieHelper::GetNextSingleSerieUIDFileSet()
392 {
393    gdcmAssertMacro (ItFileSetHt != SingleSerieUIDFileSetHT.end());
394   
395    ++ItFileSetHt;
396    if ( ItFileSetHt != SingleSerieUIDFileSetHT.end() )
397       return ItFileSetHt->second;
398    return NULL;
399 }
400
401 /**
402  * \brief   Get the SingleSerieUIDFileSet according to its Serie UID
403  * @param SerieUID SerieUID to retrieve
404  * \return pointer to the FileList (SingleSerieUIDFileSet) if found, otherwhise 0
405  */
406 FileList *SerieHelper::GetSingleSerieUIDFileSet(std::string SerieUID)
407 {
408    if ( SingleSerieUIDFileSetHT.count(SerieUID) == 0 )
409       return 0;     
410    return SingleSerieUIDFileSetHT[SerieUID];
411 }
412
413 /**
414  * \brief   Splits a Single SerieUID Fileset according to the Orientations
415  * @param fileSet File Set to be splitted
416  * \return  std::map of 'Xcoherent' File sets
417  */
418
419 XCoherentFileSetmap SerieHelper::SplitOnOrientation(FileList *fileSet)
420 {
421    XCoherentFileSetmap CoherentFileSet;
422
423    int nb = fileSet->size();
424    if (nb == 0 )
425       return CoherentFileSet;
426    float iop[6];
427    std::ostringstream ossOrient;
428    std::string strOrient;
429    
430    FileList::const_iterator it = fileSet->begin();
431    it ++;
432    for ( ;
433          it != fileSet->end();
434        ++it)
435    {     
436       // Information is in :      
437       // 0020 0037 : Image Orientation (Patient) or
438       // 0020 0035 : Image Orientation (RET)
439
440       // Let's build again the 'cosines' string, to be sure of it's format      
441       (*it)->GetImageOrientationPatient(iop);
442       ossOrient << iop[0];      
443       for (int i = 1; i < 6; i++)
444       {
445         ossOrient << "\\";
446         ossOrient << iop[i]; 
447       }      
448       strOrient = ossOrient.str();
449       
450       if ( CoherentFileSet.count(strOrient) == 0 )
451       {
452          gdcmDebugMacro(" New Orientation :[" << strOrient << "]");
453          // create a File set in 'orientation' position
454          CoherentFileSet[strOrient] = new FileList;
455       }
456       // Current Orientation and DICOM header match; add the file:
457       CoherentFileSet[strOrient]->push_back( (*it) );
458    }   
459    return CoherentFileSet;
460 }
461
462 /**
463  * \brief   Splits a Single SerieUID Fileset according to the Positions
464  * @param fileSet File Set to be splitted
465  * \return  std::map of 'Xcoherent' File sets
466  */
467
468 XCoherentFileSetmap SerieHelper::SplitOnPosition(FileList *fileSet)
469 {
470    XCoherentFileSetmap CoherentFileSet;
471
472    int nb = fileSet->size();
473    if (nb == 0 )
474       return CoherentFileSet;
475    float pos[3];
476    std::string strImPos;  // read on disc
477    std::ostringstream ossPosition;
478    std::string strPosition; // re computed
479    FileList::const_iterator it = fileSet->begin();
480    it ++;
481    for ( ;
482          it != fileSet->end();
483        ++it)
484    {     
485       // Information is in :      
486       // 0020,0032 : Image Position Patient
487       // 0020,0030 : Image Position (RET)
488
489       strImPos = (*it)->GetEntryString(0x0020,0x0032);
490       if ( strImPos == GDCM_UNFOUND)
491       {
492          gdcmWarningMacro( "Unfound Image Position Patient (0020,0032)");
493          strImPos = (*it)->GetEntryString(0x0020,0x0030); // For ACR-NEMA images
494          if ( strImPos == GDCM_UNFOUND )
495          {
496             gdcmWarningMacro( "Unfound Image Position (RET) (0020,0030)");
497             // User wants to split on the 'Position'
498             // No 'Position' info found.
499             // We return an empty Htable !
500             return CoherentFileSet;
501          }  
502       }
503
504       if ( sscanf( strImPos.c_str(), "%f \\%f \\%f ", 
505                                               &pos[0], &pos[1], &pos[2]) != 3 )
506       {
507             gdcmWarningMacro( "Wrong number for Position : ["
508                        << strImPos << "]" );
509              return CoherentFileSet;
510       }
511
512       // Let's build again the 'position' string, to be sure of it's format      
513
514       ossPosition << pos[0];      
515       for (int i = 1; i < 3; i++)
516       {
517         ossPosition << "\\";
518         ossPosition << pos[i]; 
519       }      
520       strPosition = ossPosition.str();
521       
522       if ( CoherentFileSet.count(strPosition) == 0 )
523       {
524          gdcmDebugMacro(" New Position :[" << strPosition << "]");
525          // create a File set in 'position' position
526          CoherentFileSet[strPosition] = new FileList;
527       }
528       // Current Position and DICOM header match; add the file:
529       CoherentFileSet[strPosition]->push_back( (*it) );
530    }   
531    return CoherentFileSet;
532 }
533
534 /**
535  * \brief   Splits a SingleSerieUID File set Coherent according to the
536  *          value of a given Tag
537  * @param fileSet File Set to be splitted
538  * @param   group  group number of the target Element
539  * @param   elem element number of the target Element
540  * \return  std::map of 'Xcoherent' File sets
541  */
542
543 XCoherentFileSetmap SerieHelper::SplitOnTagValue(FileList *fileSet, 
544                                                uint16_t group, uint16_t elem)
545 {
546    XCoherentFileSetmap CoherentFileSet;
547
548    int nb = fileSet->size();
549    if (nb == 0 )
550       return CoherentFileSet;
551
552    std::string strTagValue;  // read on disc
553
554    FileList::const_iterator it = fileSet->begin();
555    it ++;
556    for ( ;
557          it != fileSet->end();
558        ++it)
559    {     
560       // Information is in :      
561       // 0020,0032 : Image Position Patient
562       // 0020,0030 : Image Position (RET)
563
564       strTagValue = (*it)->GetEntryString(group,elem);
565       
566       if ( CoherentFileSet.count(strTagValue) == 0 )
567       {
568          gdcmDebugMacro(" New Tag Value :[" << strTagValue << "]");
569          // create a File set in 'position' position
570          CoherentFileSet[strTagValue] = new FileList;
571       }
572       // Current Tag value and DICOM header match; add the file:
573       CoherentFileSet[strTagValue]->push_back( (*it) );
574    }
575    return CoherentFileSet;
576 }
577
578 //-----------------------------------------------------------------------------
579 // Protected
580
581 //-----------------------------------------------------------------------------
582 // Private
583 /**
584  * \brief sorts the images, according to their Patient Position
585  *  We may order, considering :
586  *   -# Image Position Patient
587  *   -# Image Number
588  *   -# More to come :-)
589  * WARNING : FileList = std::vector<File* >
590  * @param fileList Coherent File list (same Serie UID) to sort
591  * @return false only if the header is bugged !
592  */
593 bool SerieHelper::ImagePositionPatientOrdering( FileList *fileList )
594 //based on Jolinda Smith's algorithm
595 {
596    //iop is calculated based on the file file
597    float cosines[6];
598    float normal[3];
599    float ipp[3];
600    float dist;
601    float min = 0, max = 0;
602    bool first = true;
603    int n=0;
604    std::vector<float> distlist;
605
606    //!\todo rewrite this for loop.
607    for ( FileList::const_iterator 
608          it = fileList->begin();
609          it != fileList->end(); ++it )
610    {
611       if ( first ) 
612       {
613          (*it)->GetImageOrientationPatient( cosines );
614       
615          // You only have to do this once for all slices in the volume. Next, 
616          // for each slice, calculate the distance along the slice normal 
617          // using the IPP ("Image Position Patient") tag.
618          // ("dist" is initialized to zero before reading the first slice) :
619          normal[0] = cosines[1]*cosines[5] - cosines[2]*cosines[4];
620          normal[1] = cosines[2]*cosines[3] - cosines[0]*cosines[5];
621          normal[2] = cosines[0]*cosines[4] - cosines[1]*cosines[3];
622   
623          ipp[0] = (*it)->GetXOrigin();
624          ipp[1] = (*it)->GetYOrigin();
625          ipp[2] = (*it)->GetZOrigin();
626
627          dist = 0;
628          for ( int i = 0; i < 3; ++i )
629          {
630             dist += normal[i]*ipp[i];
631          }
632     
633          distlist.push_back( dist );
634
635          max = min = dist;
636          first = false;
637       }
638       else 
639       {
640          ipp[0] = (*it)->GetXOrigin();
641          ipp[1] = (*it)->GetYOrigin();
642          ipp[2] = (*it)->GetZOrigin();
643   
644          dist = 0;
645          for ( int i = 0; i < 3; ++i )
646          {
647             dist += normal[i]*ipp[i];
648          }
649
650          distlist.push_back( dist );
651
652          min = (min < dist) ? min : dist;
653          max = (max > dist) ? max : dist;
654       }
655       ++n;
656    }
657
658    // Then I order the slices according to the value "dist". Finally, once
659    // I've read in all the slices, I calculate the z-spacing as the difference
660    // between the "dist" values for the first two slices.
661    FileVector CoherentFileVector(n);
662    // CoherentFileVector.reserve( n );
663    CoherentFileVector.resize( n );
664    // gdcmAssertMacro( CoherentFileVector.capacity() >= n );
665
666    // Find out if min/max are coherent
667    if ( min == max )
668    {
669      gdcmWarningMacro("Looks like all images have the exact same image position"
670                       << ". No PositionPatientOrdering sort performed" );
671      return false;
672    }
673
674    float step = (max - min)/(n - 1);
675    int pos;
676    n = 0;
677     
678    //VC++ don't understand what scope is !! it -> it2
679    for (FileList::const_iterator it2  = fileList->begin();
680         it2 != fileList->end(); ++it2, ++n)
681    {
682       //2*n sort algo !!
683       //Assumption: all files are present (no one missing)
684       pos = (int)( fabs( (distlist[n]-min)/step) + .5 );
685
686       // a Dicom 'Serie' may contain scout views
687       // and images may have differents directions
688       // -> More than one may have the same 'pos'
689       // Sorting has then NO meaning !
690       if (CoherentFileVector[pos]==NULL)
691          CoherentFileVector[pos] = *it2;
692       else
693       {
694          gdcmWarningMacro( "At least 2 files with same position."
695                         << " No PositionPatientOrdering sort performed");
696          return false;
697       }
698    }
699
700    fileList->clear();  // doesn't delete list elements, only nodes
701
702    if (DirectOrder)
703    {  
704       //VC++ don't understand what scope is !! it -> it3
705       for (FileVector::const_iterator it3  = CoherentFileVector.begin();
706            it3 != CoherentFileVector.end(); ++it3)
707       {
708          fileList->push_back( *it3 );
709       }
710    }
711    else // user asked for reverse order
712    {
713       FileVector::const_iterator it4;
714       it4 = CoherentFileVector.end();
715       do
716       {
717          it4--;
718          fileList->push_back( *it4 );
719       } while (it4 != CoherentFileVector.begin() );
720    } 
721
722    distlist.clear();
723    CoherentFileVector.clear();
724
725    return true;
726 }
727
728 bool SerieHelper::ImageNumberLessThan(File *file1, File *file2)
729 {
730   return file1->GetImageNumber() < file2->GetImageNumber();
731 }
732
733 bool SerieHelper::ImageNumberGreaterThan(File *file1, File *file2)
734 {
735   return file1->GetImageNumber() > file2->GetImageNumber();
736 }
737
738 /**
739  * \brief sorts the images, according to their Image Number
740  * \note Works only on bona fide files  (i.e image number is a character string
741  *                                      corresponding to an integer)
742  *             within a bona fide serie (i.e image numbers are consecutive)
743  * @param fileList Coherent File list (same Serie UID) to sort 
744  * @return false if non bona fide stuff encountered
745  */
746 bool SerieHelper::ImageNumberOrdering(FileList *fileList) 
747 {
748    int min, max, pos;
749    int n = fileList->size();
750
751    FileList::const_iterator it = fileList->begin();
752    min = max = (*it)->GetImageNumber();
753
754    for (; it != fileList->end(); ++it, ++n)
755    {
756       pos = (*it)->GetImageNumber();
757       min = (min < pos) ? min : pos;
758       max = (max > pos) ? max : pos;
759    }
760
761    // Find out if image numbers are coherent (consecutive)
762    if ( min == max || max == 0 || max >= (n+min) )
763    {
764       gdcmWarningMacro( " 'Image numbers' not coherent. "
765                         << " No ImageNumberOrdering sort performed.");
766       return false;
767    }
768    if (DirectOrder) 
769       std::sort(fileList->begin(), fileList->end(), 
770                                           SerieHelper::ImageNumberLessThan );
771    else
772       std::sort(fileList->begin(), fileList->end(),
773                                           SerieHelper::ImageNumberGreaterThan );
774
775    return true;
776 }
777
778 bool SerieHelper::FileNameLessThan(File *file1, File *file2)
779 {
780    return file1->GetFileName() < file2->GetFileName();
781 }
782
783 bool SerieHelper::FileNameGreaterThan(File *file1, File *file2)
784 {
785    return file1->GetFileName() > file2->GetFileName();
786 }
787 /**
788  * \brief sorts the images, according to their File Name
789  * @param fileList Coherent File list (same Serie UID) to sort
790  * @return false only if the header is bugged !
791  */
792 bool SerieHelper::FileNameOrdering(FileList *fileList)
793 {
794    if (DirectOrder) 
795       std::sort(fileList->begin(), fileList->end(), 
796                                        SerieHelper::FileNameLessThan);
797    else
798       std::sort(fileList->begin(), fileList->end(), 
799                                        SerieHelper::FileNameGreaterThan);
800
801    return true;
802 }
803
804 /**
805  * \brief sorts the images, according to user supplied function
806  * @param fileList Coherent File list (same Serie UID) to sort
807  * @return false only if the header is bugged !
808  */
809 bool SerieHelper::UserOrdering(FileList *fileList)
810 {
811    std::sort(fileList->begin(), fileList->end(), 
812                                     SerieHelper::UserLessThanFunction);
813    if (!DirectOrder) 
814    {
815       std::reverse(fileList->begin(), fileList->end());
816    }
817    return true;
818 }
819
820 //-----------------------------------------------------------------------------
821 // Print
822 /**
823  * \brief   Canonical printer.
824  */
825 void SerieHelper::Print(std::ostream &os, std::string const &indent)
826 {
827    // For all the Coherent File lists of the gdcm::Serie
828    SingleSerieUIDFileSetmap::iterator itl = SingleSerieUIDFileSetHT.begin();
829    if ( itl == SingleSerieUIDFileSetHT.end() )
830    {
831       gdcmWarningMacro( "No SingleSerieUID File set found" );
832       return;
833    }
834    while (itl != SingleSerieUIDFileSetHT.end())
835    { 
836       os << "Serie UID :[" << itl->first << "]" << std::endl;
837
838       // For all the files of a SingleSerieUID File set
839       for (FileList::iterator it =  (itl->second)->begin();
840                                   it != (itl->second)->end(); 
841                                 ++it)
842       {
843          os << indent << " --- " << (*it)->GetFileName() << std::endl;
844       }
845       ++itl;
846    }
847 }
848
849 //-----------------------------------------------------------------------------
850 } // end namespace gdcm