]> Creatis software - gdcm.git/blob - src/gdcmSerieHelper.cxx
Use new style for Loading files
[gdcm.git] / src / gdcmSerieHelper.cxx
1 /*=========================================================================
2                                                                                 
3   Program:   gdcm
4   Module:    $RCSfile: gdcmSerieHelper.cxx,v $
5   Language:  C++
6   Date:      $Date: 2005/07/17 04:27:49 $
7   Version:   $Revision: 1.11 $
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 "gdcmDebug.h"
23 #include "gdcmUtil.h"
24
25 #include <math.h>
26 #include <vector>
27 #include <algorithm>
28
29 namespace gdcm 
30 {
31
32 //-----------------------------------------------------------------------------
33 // Constructor / Destructor
34 /**
35  * \brief   Constructor from a given SerieHelper
36  */
37 SerieHelper::SerieHelper()
38 {
39    // For all the File lists of the gdcm::Serie
40    FileList *l = GetFirstCoherentFileList();
41    while (l)
42    { 
43       // For all the files of a File list
44       for (gdcm::FileList::iterator it  = l->begin();
45                               it != l->end(); 
46                             ++it)
47       {
48          delete *it;
49       }
50       l->clear();
51       delete l;;
52       l = GetNextCoherentFileList();
53    }
54 }
55
56 /**
57  * \brief   Canonical destructor.
58  */
59 SerieHelper::~SerieHelper()
60 {
61    // For all the Coherent File lists of the gdcm::Serie
62    FileList *l = GetFirstCoherentFileList();
63    while (l)
64    { 
65       // For all the files of a Coherent File list
66       for (FileList::iterator it  = l->begin();
67                               it != l->end(); 
68                             ++it)
69       {
70          delete *it;
71       }
72       l->clear();
73       delete l;
74       l = GetNextCoherentFileList();
75    }
76 }
77
78 //-----------------------------------------------------------------------------
79
80 //-----------------------------------------------------------------------------
81
82 // Public
83 /**
84  * \brief add a gdcm::File to the list corresponding to its Serie UID
85  * @param   filename Name of the file to deal with
86  */
87 void SerieHelper::AddFileName(std::string const &filename)
88 {
89    // Create a DICOM file
90    File *header = new File ();
91    header->SetLoadMode(LoadMode);
92    header->SetFileName( filename ); 
93    header->Load();
94    //File *header = new File( filename ); // Deprecated old style 
95    if ( header->IsReadable() )
96    {
97       int allrules = 1;
98       // First step the user has defined s set of rules for the DICOM he is looking for
99       // make sure the file correspond to his set of rules:
100       for(SerieRestrictions::iterator it = Restrictions.begin();
101           it != Restrictions.end();
102           ++it)
103       {
104          const Rule &r = *it;
105          const std::string s;// = header->GetEntryValue( r.first );
106          if ( !Util::DicomStringEqual(s, r.second.c_str()) )
107          {
108            // Argh ! This rule is unmatch let's just quit
109            allrules = 0;
110            break;
111          }
112       }
113       if ( allrules ) // all rules are respected:
114       {
115          // Alright ! we have a found a DICOM that match the user expectation. 
116          // Let's add it !
117
118          // 0020 000e UI REL Series Instance UID
119          const std::string &uid = header->GetEntryValue (0x0020, 0x000e);
120          // if uid == GDCM_UNFOUND then consistently we should find GDCM_UNFOUND
121          // no need here to do anything special
122
123          if ( CoherentFileListHT.count(uid) == 0 )
124          {
125             gdcmDebugMacro(" New Serie UID :[" << uid << "]");
126             // create a std::list in 'uid' position
127             CoherentFileListHT[uid] = new FileList;
128          }
129          // Current Serie UID and DICOM header seems to match add the file:
130          CoherentFileListHT[uid]->push_back( header );
131       }
132       else
133       {
134          // at least one rule was unmatch we need to deallocate the file:
135          delete header;
136       }
137    }
138    else
139    {
140       gdcmWarningMacro("Could not read file: " << filename );
141       delete header;
142    }
143 }
144 /**
145  * \brief add a rules for restricting a DICOM file to be in the serie we are
146  * trying to find. For example you can select only the DICOM file from a
147  * directory which would have a particular EchoTime==4.0.
148  * This method is a user level, value is not required to be formatted as a DICOM
149  * string
150  */
151 void SerieHelper::AddRestriction(TagKey const &key, std::string const &value)
152 {
153    Rule r;
154    r.first = key;
155    r.second = value;
156    Restrictions.push_back( r ); 
157 }
158
159 /**
160  * \brief Sets the root Directory
161  * @param   dir Name of the directory to deal with
162  * @param recursive whether we want explore recursively the Directory
163  */
164 void SerieHelper::SetDirectory(std::string const &dir, bool recursive)
165 {
166    DirList dirList(dir, recursive); // OS specific
167   
168    DirListType filenames_list = dirList.GetFilenames();
169    for( DirListType::const_iterator it = filenames_list.begin(); 
170         it != filenames_list.end(); ++it)
171    {
172       AddFileName( *it );
173    }
174 }
175
176 /**
177  * \brief Sorts the given File List
178  * \warning This could be implemented in a 'Strategy Pattern' approach
179  *          But as I don't know how to do it, I leave it this way
180  *          BTW, this is also a Strategy, I don't know this is the best approach :)
181  */
182 void SerieHelper::OrderFileList(FileList *coherentFileList)
183 {
184    if ( ImagePositionPatientOrdering( coherentFileList ) )
185    {
186       return ;
187    }
188    else if ( ImageNumberOrdering(coherentFileList ) )
189    {
190       return ;
191    }
192    else  
193    {
194       FileNameOrdering(coherentFileList );
195    }
196 }
197
198 /**
199  * \brief   Get the first List while visiting the CoherentFileListHT
200  * @return  The first FileList if found, otherwhise NULL
201  */
202 FileList *SerieHelper::GetFirstCoherentFileList()
203 {
204    ItListHt = CoherentFileListHT.begin();
205    if ( ItListHt != CoherentFileListHT.end() )
206       return ItListHt->second;
207    return NULL;
208 }
209
210 /**
211  * \brief   Get the next List while visiting the CoherentFileListHT
212  * \note : meaningfull only if GetFirstCoherentFileList already called
213  * @return  The next FileList if found, otherwhise NULL
214  */
215 FileList *SerieHelper::GetNextCoherentFileList()
216 {
217    gdcmAssertMacro (ItListHt != CoherentFileListHT.end());
218   
219    ++ItListHt;
220    if ( ItListHt != CoherentFileListHT.end() )
221       return ItListHt->second;
222    return NULL;
223 }
224
225 /**
226  * \brief   Get the Coherent Files list according to its Serie UID
227  * @param SerieUID SerieUID
228  * \return  pointer to the Coherent Files list if found, otherwhise NULL
229  */
230 FileList *SerieHelper::GetCoherentFileList(std::string SerieUID)
231 {
232    if ( CoherentFileListHT.count(SerieUID) == 0 )
233       return 0;     
234    return CoherentFileListHT[SerieUID];
235 }
236
237 //-----------------------------------------------------------------------------
238 // Protected
239
240 //-----------------------------------------------------------------------------
241 // Private
242 /**
243  * \brief sorts the images, according to their Patient Position
244  *  We may order, considering :
245  *   -# Image Position Patient
246  *   -# Image Number
247  *   -# More to come :-)
248  * WARNING : FileList = std::vector<File* >
249  * @param fileList Coherent File list (same Serie UID) to sort
250  * @return false only if the header is bugged !
251  */
252 bool SerieHelper::ImagePositionPatientOrdering( FileList *fileList )
253 //based on Jolinda's algorithm
254 {
255    //iop is calculated based on the file file
256    float cosines[6];
257    float normal[3];
258    float ipp[3];
259    float dist;
260    float min = 0, max = 0;
261    bool first = true;
262    int n=0;
263    std::vector<float> distlist;
264
265    //!\todo rewrite this for loop.
266    for ( FileList::const_iterator 
267          it = fileList->begin();
268          it != fileList->end(); ++it )
269    {
270       if ( first ) 
271       {
272          (*it)->GetImageOrientationPatient( cosines );
273       
274          // You only have to do this once for all slices in the volume. Next, 
275          // for each slice, calculate the distance along the slice normal 
276          // using the IPP tag ("dist" is initialized to zero before reading 
277          // the first slice) :
278          normal[0] = cosines[1]*cosines[5] - cosines[2]*cosines[4];
279          normal[1] = cosines[2]*cosines[3] - cosines[0]*cosines[5];
280          normal[2] = cosines[0]*cosines[4] - cosines[1]*cosines[3];
281   
282          ipp[0] = (*it)->GetXOrigin();
283          ipp[1] = (*it)->GetYOrigin();
284          ipp[2] = (*it)->GetZOrigin();
285
286          dist = 0;
287          for ( int i = 0; i < 3; ++i )
288          {
289             dist += normal[i]*ipp[i];
290          }
291     
292          distlist.push_back( dist );
293
294          max = min = dist;
295          first = false;
296       }
297       else 
298       {
299          ipp[0] = (*it)->GetXOrigin();
300          ipp[1] = (*it)->GetYOrigin();
301          ipp[2] = (*it)->GetZOrigin();
302   
303          dist = 0;
304          for ( int i = 0; i < 3; ++i )
305          {
306             dist += normal[i]*ipp[i];
307          }
308
309          distlist.push_back( dist );
310
311          min = (min < dist) ? min : dist;
312          max = (max > dist) ? max : dist;
313       }
314       ++n;
315    }
316
317    // Then I order the slices according to the value "dist". Finally, once
318    // I've read in all the slices, I calculate the z-spacing as the difference
319    // between the "dist" values for the first two slices.
320    FileVector CoherentFileVector(n);
321    // CoherentFileVector.reserve( n );
322    CoherentFileVector.resize( n );
323    // gdcmAssertMacro( CoherentFileVector.capacity() >= n );
324
325    // Find out if min/max are coherent
326    if ( min == max )
327      {
328      gdcmWarningMacro( "Looks like all images have the exact same image position."
329                        << "No PositionPatientOrdering sort performed" );
330      return false;
331      }
332
333    float step = (max - min)/(n - 1);
334    int pos;
335    n = 0;
336     
337    //VC++ don't understand what scope is !! it -> it2
338    for (FileList::const_iterator it2  = fileList->begin();
339         it2 != fileList->end(); ++it2, ++n)
340    {
341       //2*n sort algo !!
342       //Assumption: all files are present (no one missing)
343       pos = (int)( fabs( (distlist[n]-min)/step) + .5 );
344
345       // a Dicom 'Serie' may contain scout views
346       // and images may have differents directions
347       // -> More than one may have the same 'pos'
348       // Sorting has then NO meaning !
349       if (CoherentFileVector[pos]==NULL)
350          CoherentFileVector[pos] = *it2;
351       else
352       {
353          gdcmWarningMacro( "At least 2 files with same position. No PositionPatientOrdering sort performed");
354          return false;
355       }
356    }
357
358    fileList->clear();  // doesn't delete list elements, only nodes
359   
360    //VC++ don't understand what scope is !! it -> it3
361    for (FileVector::const_iterator it3  = CoherentFileVector.begin();
362         it3 != CoherentFileVector.end(); ++it3)
363    {
364       fileList->push_back( *it3 );
365    }
366
367    distlist.clear();
368    CoherentFileVector.clear();
369
370    return true;
371 }
372
373 bool SerieHelper::ImageNumberLessThan(File *file1, File *file2)
374 {
375   return file1->GetImageNumber() < file2->GetImageNumber();
376 }
377
378 /**
379  * \brief sorts the images, according to their Image Number
380  * \note Works only on bona fide files  (i.e image number is a character string
381  *                                      corresponding to an integer)
382  *             within a bona fide serie (i.e image numbers are consecutive)
383  * @param fileList Coherent File list (same Serie UID) to sort 
384  * @return false if non bona fide stuff encountered
385  */
386 bool SerieHelper::ImageNumberOrdering(FileList *fileList) 
387 {
388    int min, max, pos;
389    int n = fileList->size();
390
391    FileList::const_iterator it = fileList->begin();
392    min = max = (*it)->GetImageNumber();
393
394    for (; it != fileList->end(); ++it, ++n)
395    {
396       pos = (*it)->GetImageNumber();
397       min = (min < pos) ? min : pos;
398       max = (max > pos) ? max : pos;
399    }
400
401    // Find out if image numbers are coherent (consecutive)
402    if ( min == max || max == 0 || max >= (n+min) )
403    {
404       gdcmWarningMacro( " 'Image numbers' not coherent. No ImageNumberOrdering sort performed.");
405       return false;
406    }
407    std::sort(fileList->begin(), fileList->end(), SerieHelper::ImageNumberLessThan );
408
409    return true;
410 }
411
412 bool SerieHelper::FileNameLessThan(File *file1, File *file2)
413 {
414   return file1->GetFileName() < file2->GetFileName();
415 }
416
417 /**
418  * \brief sorts the images, according to their File Name
419  * @param fileList Coherent File list (same Serie UID) to sort
420  * @return false only if the header is bugged !
421  */
422 bool SerieHelper::FileNameOrdering(FileList *fileList)
423 {
424    std::sort(fileList->begin(), fileList->end(), SerieHelper::FileNameLessThan);
425    return true;
426 }
427
428 //-----------------------------------------------------------------------------
429 // Print
430 /**
431  * \brief   Canonical printer.
432  */
433 void SerieHelper::Print(std::ostream &os, std::string const &indent)
434 {
435    // For all the Coherent File lists of the gdcm::Serie
436    CoherentFileListmap::iterator itl = CoherentFileListHT.begin();
437    if ( itl == CoherentFileListHT.end() )
438    {
439       gdcmWarningMacro( "No Coherent File list found" );
440       return;
441    }
442    while (itl != CoherentFileListHT.end())
443    { 
444       os << "Serie UID :[" << itl->first << "]" << std::endl;
445
446       // For all the files of a Coherent File list
447       for (FileList::iterator it =  (itl->second)->begin();
448                                   it != (itl->second)->end(); 
449                                 ++it)
450       {
451          os << indent << " --- " << (*it)->GetFileName() << std::endl;
452       }
453       ++itl;
454    }
455 }
456
457 //-----------------------------------------------------------------------------
458 } // end namespace gdcm