]> Creatis software - gdcm.git/blob - src/gdcmSerieHelper.cxx
BUG: Solve seg fault with SerieHelper by properly using sort algorithm when needed...
[gdcm.git] / src / gdcmSerieHelper.cxx
1 /*=========================================================================
2                                                                                 
3   Program:   gdcm
4   Module:    $RCSfile: gdcmSerieHelper.cxx,v $
5   Language:  C++
6   Date:      $Date: 2005/02/05 01:25:03 $
7   Version:   $Revision: 1.3 $
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          gdcmVerboseMacro(" 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       gdcmVerboseMacro("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    float step = (max - min)/(n - 1);
289    int pos;
290    n = 0;
291     
292    //VC++ don't understand what scope is !! it -> it2
293    for (GdcmFileList::const_iterator it2  = fileList->begin();
294         it2 != fileList->end(); ++it2, ++n)
295    {
296       //2*n sort algo !!
297       //Assumption: all files are present (no one missing)
298       pos = (int)( fabs( (distlist[n]-min)/step) + .5 );
299
300       // a Dicom 'Serie' may contain scout views
301       // and images may have differents directions
302       // -> More than one may have the same 'pos'
303       // Sorting has then NO meaning !
304       if (CoherentGdcmFileVector[pos]==NULL)
305          CoherentGdcmFileVector[pos] = *it2;
306       else
307       {
308          gdcmVerboseMacro( "2 files same position");
309          return false;
310       }
311    }
312
313    fileList->clear();  // doesn't delete list elements, only node
314   
315    //VC++ don't understand what scope is !! it -> it3
316    for (GdcmFileVector::const_iterator it3  = CoherentGdcmFileVector.begin();
317         it3 != CoherentGdcmFileVector.end(); ++it3)
318    {
319       fileList->push_back( *it3 );
320    }
321
322    distlist.clear();
323    CoherentGdcmFileVector.clear();
324
325    return true;
326 }
327
328 bool SerieHelper::ImageNumberLessThan(File *file1, File *file2)
329 {
330   return file1->GetImageNumber() < file2->GetImageNumber();
331 }
332
333 /**
334  * \brief sorts the images, according to their Image Number
335  * \note Works only on bona fide files  (i.e image number is a character string
336  *                                      corresponding to an integer)
337  *             within a bona fide serie (i.e image numbers are consecutive)
338  * @param fileList Coherent File list (same Serie UID) to sort 
339  * @return false if non nona fide stuff encountered
340  */
341 bool SerieHelper::ImageNumberOrdering(GdcmFileList *fileList) 
342 {
343    int min, max, pos;
344    int n = fileList->size();
345
346    GdcmFileList::const_iterator it = fileList->begin();
347    min = max = (*it)->GetImageNumber();
348
349    for (; it != fileList->end(); ++it, ++n)
350    {
351       pos = (*it)->GetImageNumber();
352       min = (min < pos) ? min : pos;
353       max = (max > pos) ? max : pos;
354    }
355
356    // Find out if image numbers are coherent (consecutive)
357    if( min == max || max == 0 || max >= (n+min))
358       return false;
359
360    std::sort(fileList->begin(), fileList->end(), SerieHelper::ImageNumberLessThan );
361
362    return true;
363 }
364
365 bool SerieHelper::FileNameLessThan(File *file1, File *file2)
366 {
367   return file1->GetFileName() < file2->GetFileName();
368 }
369
370 /**
371  * \brief sorts the images, according to their File Name
372  * @param fileList Coherent File list (same Serie UID) to sort
373  * @return false only if the header is bugged !
374  */
375 bool SerieHelper::FileNameOrdering(GdcmFileList *fileList)
376 {
377    std::sort(fileList->begin(), fileList->end(), SerieHelper::FileNameLessThan);
378    return true;
379 }
380
381 //-----------------------------------------------------------------------------
382 // Print
383 /**
384  * \brief   Canonical printer.
385  */
386 void SerieHelper::Print(std::ostream &os, std::string const & indent)
387 {
388    // For all the Coherent File lists of the gdcm::Serie
389    CoherentFileListmap::iterator itl = CoherentGdcmFileListHT.begin();
390    if ( itl == CoherentGdcmFileListHT.end() )
391    {
392       gdcmVerboseMacro( "No Coherent File list found" );
393       return;
394    }
395    while (itl != CoherentGdcmFileListHT.end())
396    { 
397       os << "Serie UID :[" << itl->first << "]" << std::endl;
398
399       // For all the files of a Coherent File list
400       for (GdcmFileList::iterator it =  (itl->second)->begin();
401                                   it != (itl->second)->end(); 
402                                 ++it)
403       {
404          os << indent << " --- " << (*it)->GetFileName() << std::endl;
405       }
406       ++itl;
407    }
408 }
409
410 //-----------------------------------------------------------------------------
411 } // end namespace gdcm