]> Creatis software - gdcm.git/blob - src/gdcmSerieHelper.cxx
BUG: When Image Position Patient was exactly 0,0,0 the strategy was failing... cannot...
[gdcm.git] / src / gdcmSerieHelper.cxx
1 /*=========================================================================
2                                                                                 
3   Program:   gdcm
4   Module:    $RCSfile: gdcmSerieHelper.cxx,v $
5   Language:  C++
6   Date:      $Date: 2005/05/11 17:15:18 $
7   Version:   $Revision: 1.6 $
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          distlist.push_back( dist );
246
247          max = min = dist;
248          first = false;
249       }
250       else 
251       {
252          ipp[0] = (*it)->GetXOrigin();
253          ipp[1] = (*it)->GetYOrigin();
254          ipp[2] = (*it)->GetZOrigin();
255   
256          dist = 0;
257          for ( int i = 0; i < 3; ++i )
258          {
259             dist += normal[i]*ipp[i];
260          }
261
262          distlist.push_back( dist );
263
264          min = (min < dist) ? min : dist;
265          max = (max > dist) ? max : dist;
266       }
267       ++n;
268    }
269
270    // Then I order the slices according to the value "dist". Finally, once
271    // I've read in all the slices, I calculate the z-spacing as the difference
272    // between the "dist" values for the first two slices.
273    GdcmFileVector CoherentGdcmFileVector(n);
274    // CoherentGdcmFileVector.reserve( n );
275    CoherentGdcmFileVector.resize( n );
276    // gdcmAssertMacro( CoherentGdcmFileVector.capacity() >= n );
277
278    // Find out if min/max are coherent
279    if( min == max )
280      {
281      gdcmWarningMacro( "Looks like all images have the exact same image position...");
282      return false;
283      }
284
285    float step = (max - min)/(n - 1);
286    int pos;
287    n = 0;
288     
289    //VC++ don't understand what scope is !! it -> it2
290    for (GdcmFileList::const_iterator it2  = fileList->begin();
291         it2 != fileList->end(); ++it2, ++n)
292    {
293       //2*n sort algo !!
294       //Assumption: all files are present (no one missing)
295       pos = (int)( fabs( (distlist[n]-min)/step) + .5 );
296
297       // a Dicom 'Serie' may contain scout views
298       // and images may have differents directions
299       // -> More than one may have the same 'pos'
300       // Sorting has then NO meaning !
301       if (CoherentGdcmFileVector[pos]==NULL)
302          CoherentGdcmFileVector[pos] = *it2;
303       else
304       {
305          gdcmWarningMacro( "2 files same position");
306          return false;
307       }
308    }
309
310    fileList->clear();  // doesn't delete list elements, only node
311   
312    //VC++ don't understand what scope is !! it -> it3
313    for (GdcmFileVector::const_iterator it3  = CoherentGdcmFileVector.begin();
314         it3 != CoherentGdcmFileVector.end(); ++it3)
315    {
316       fileList->push_back( *it3 );
317    }
318
319    distlist.clear();
320    CoherentGdcmFileVector.clear();
321
322    return true;
323 }
324
325 bool SerieHelper::ImageNumberLessThan(File *file1, File *file2)
326 {
327   return file1->GetImageNumber() < file2->GetImageNumber();
328 }
329
330 /**
331  * \brief sorts the images, according to their Image Number
332  * \note Works only on bona fide files  (i.e image number is a character string
333  *                                      corresponding to an integer)
334  *             within a bona fide serie (i.e image numbers are consecutive)
335  * @param fileList Coherent File list (same Serie UID) to sort 
336  * @return false if non nona fide stuff encountered
337  */
338 bool SerieHelper::ImageNumberOrdering(GdcmFileList *fileList) 
339 {
340    int min, max, pos;
341    int n = fileList->size();
342
343    GdcmFileList::const_iterator it = fileList->begin();
344    min = max = (*it)->GetImageNumber();
345
346    for (; it != fileList->end(); ++it, ++n)
347    {
348       pos = (*it)->GetImageNumber();
349       min = (min < pos) ? min : pos;
350       max = (max > pos) ? max : pos;
351    }
352
353    // Find out if image numbers are coherent (consecutive)
354    if( min == max || max == 0 || max >= (n+min))
355       return false;
356
357    std::sort(fileList->begin(), fileList->end(), SerieHelper::ImageNumberLessThan );
358
359    return true;
360 }
361
362 bool SerieHelper::FileNameLessThan(File *file1, File *file2)
363 {
364   return file1->GetFileName() < file2->GetFileName();
365 }
366
367 /**
368  * \brief sorts the images, according to their File Name
369  * @param fileList Coherent File list (same Serie UID) to sort
370  * @return false only if the header is bugged !
371  */
372 bool SerieHelper::FileNameOrdering(GdcmFileList *fileList)
373 {
374    std::sort(fileList->begin(), fileList->end(), SerieHelper::FileNameLessThan);
375    return true;
376 }
377
378 //-----------------------------------------------------------------------------
379 // Print
380 /**
381  * \brief   Canonical printer.
382  */
383 void SerieHelper::Print(std::ostream &os, std::string const & indent)
384 {
385    // For all the Coherent File lists of the gdcm::Serie
386    CoherentFileListmap::iterator itl = CoherentGdcmFileListHT.begin();
387    if ( itl == CoherentGdcmFileListHT.end() )
388    {
389       gdcmWarningMacro( "No Coherent File list found" );
390       return;
391    }
392    while (itl != CoherentGdcmFileListHT.end())
393    { 
394       os << "Serie UID :[" << itl->first << "]" << std::endl;
395
396       // For all the files of a Coherent File list
397       for (GdcmFileList::iterator it =  (itl->second)->begin();
398                                   it != (itl->second)->end(); 
399                                 ++it)
400       {
401          os << indent << " --- " << (*it)->GetFileName() << std::endl;
402       }
403       ++itl;
404    }
405 }
406
407 //-----------------------------------------------------------------------------
408 } // end namespace gdcm