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