]> Creatis software - gdcm.git/blob - vtk/vtkGdcmReader.cxx
Memory fixes (valgrind need to be run on .libs/vtkgdcmdemo and not the script).
[gdcm.git] / vtk / vtkGdcmReader.cxx
1 // $Header: /cvs/public/gdcm/vtk/vtkGdcmReader.cxx,v 1.11 2003/06/12 16:58:31 malaterre Exp $
2 //CLEANME#include <vtkByteSwap.h>
3 #include <stdio.h>
4 #include <vtkObjectFactory.h>
5 #include <vtkImageData.h>
6 #include <vtkPointData.h>
7 #include "vtkGdcmReader.h"
8 #include "gdcm.h"
9
10 vtkGdcmReader::vtkGdcmReader()
11 {
12   // Constructor
13 }
14
15 //----------------------------------------------------------------------------
16 vtkGdcmReader::~vtkGdcmReader()
17
18   // FIXME free memory
19   this->FileNameList.clear();
20 }
21
22 //----------------------------------------------------------------------------
23 // Adds a file name to the list of images to read.
24 void vtkGdcmReader::AddFileName(const char* name)
25 {
26   // We need to bypass the const pointer [since list<>.push_bash() only
27   // takes a char* (but not a const char*)] by making a local copy:
28   char * LocalName = new char[strlen(name) + 1];
29   strcpy(LocalName, name);
30   this->FileNameList.push_back(LocalName);
31   this->Modified();
32   delete[] LocalName;
33 }
34
35 //----------------------------------------------------------------------------
36 // Sets up a filename to be read.
37 void vtkGdcmReader::SetFileName(const char *name) {
38   vtkImageReader2::SetFileName(name);
39   // Since we maintain a list of filenames, when building a volume,
40   // (see vtkGdcmReader::AddFileName), we additionaly need to purge
41   // this list when we manually positionate the filename:
42   this->FileNameList.clear();
43   this->Modified();
44 }
45
46 //----------------------------------------------------------------------------
47 // vtkGdcmReader can have the file names specified through two ways:
48 // (1) by calling the vtkImageReader2::SetFileName(), SetFilePrefix() and
49 //     SetFilePattern()
50 // (2) By successive calls to vtkGdcmReader::AddFileName()
51 // When the first method was used by caller we need to update the local
52 // filename list
53 void vtkGdcmReader::BuildFileListFromPattern()
54 {
55    if ((! this->FileNameList.empty()) && this->FileName )
56      {
57      vtkErrorMacro("Both file patterns and AddFileName schemes were used");
58      vtkErrorMacro("Only the files specified with AddFileName shall be used");
59      return;
60      }
61
62    if (! this->FileNameList.empty()  )
63      {
64      vtkDebugMacro("Using the AddFileName specified files");
65      return;
66      }
67
68    if (!this->FileName && !this->FilePattern)
69      {
70      vtkErrorMacro("FileNames are not set. Either use AddFileName() or");
71      vtkErrorMacro("specify a FileName or FilePattern.");
72      return;
73      }
74
75    for (int idx = this->DataExtent[4]; idx <= this->DataExtent[5]; ++idx)
76      {
77      this->ComputeInternalFileName(idx);
78      vtkDebugMacro("Adding file " << this->InternalFileName);
79      this->AddFileName(this->InternalFileName);
80      }
81 }
82
83 //----------------------------------------------------------------------------
84 // When more than one filename is specified (i.e. we expect loading
85 // a stack or volume) we need to check that the corresponding images/volumes
86 // to be loaded are coherent i.e. to make sure:
87 //     - they all share the same X dimensions
88 //     - they all share the same Y dimensions
89 //     - they all share the same ImageType ( 8 bit signed, or unsigned...)
90 //
91 // Eventually, we emit a warning when all the files do NOT share the
92 // Z dimension, since we can still build a stack but the
93 // files are not coherent in Z, which is probably a source a trouble...
94 //   When files are not readable (either the file cannot be opened or
95 // because gdcm cannot parse it), they are flagged as "GDCM_UNREADABLE".  
96 //   This method returns the total number of planar images to be loaded
97 // (i.e. an image represents one plane, but a volume represents many planes)
98 int vtkGdcmReader::CheckFileCoherence()
99 {
100    int ReturnedTotalNumberOfPlanes = 0;   // The returned value.
101
102    this->BuildFileListFromPattern();
103    if (this->FileNameList.empty())
104      {
105      vtkErrorMacro("FileNames are not set.");
106      return 0;
107      }
108
109    bool FoundReferenceFile = false;
110    int  ReferenceNZ = 0;
111
112    // Loop on the filenames:
113    // - check for their existence and gdcm "parasability"
114    // - get the coherence check done:
115    for (std::list<std::string>::iterator FileName  = FileNameList.begin();
116                                         FileName != FileNameList.end();
117                                       ++FileName)
118      {
119      /////// Stage 1: check for file readability:
120      // Stage 1.1: check for file existence.
121      FILE *fp;
122      fp = fopen(FileName->c_str(),"rb");
123      if (!fp)
124        {
125        vtkErrorMacro("Unable to open file " << FileName->c_str());
126        vtkErrorMacro("Removing this file from readed files "
127                      << FileName->c_str());
128        *FileName = "GDCM_UNREADABLE";
129        continue;
130        }
131      fclose(fp);
132    
133      // Stage 1.2: check for Gdcm parsability
134      gdcmHeader GdcmHeader(FileName->c_str());
135      if (!GdcmHeader.IsReadable())
136        {
137        vtkErrorMacro("Gdcm cannot parse file " << FileName->c_str());
138        vtkErrorMacro("Removing this file from readed files "
139                      << FileName->c_str());
140        *FileName = "GDCM_UNREADABLE";
141        continue;
142        }
143
144      // Stage 1.3: further gdcm compatibility on PixelType
145      std::string type = GdcmHeader.GetPixelType();
146      if (   (type !=  "8U") && (type !=  "8S")
147          && (type != "16U") && (type != "16S")
148          && (type != "32U") && (type != "32S") )
149        {
150        vtkErrorMacro("Bad File Type for file" << FileName->c_str());
151        vtkErrorMacro("Removing this file from readed files "
152                      << FileName->c_str());
153        *FileName = "GDCM_UNREADABLE";
154        continue;
155        }
156
157      /////// Stage 2: check coherence of the set of files
158      int NX = GdcmHeader.GetXSize();
159      int NY = GdcmHeader.GetYSize();
160      int NZ = GdcmHeader.GetZSize();
161      if (FoundReferenceFile) 
162        {
163         
164        // Stage 2.1: mandatory coherence stage:
165        if (   ( NX   != this->NumColumns )
166            || ( NY   != this->NumLines )
167            || ( type != this->ImageType ) ) 
168          {
169          vtkErrorMacro("This file is not coherent with previous ones"
170                        << FileName->c_str());
171          vtkErrorMacro("Removing this file from readed files "
172                        << FileName->c_str());
173          *FileName = "GDCM_UNREADABLE";
174          continue;
175          }
176
177        // Stage 2.2: optional coherence stage
178        if ( NZ != ReferenceNZ )
179          {
180          vtkErrorMacro("File is not coherent in Z with previous ones"
181                        << FileName->c_str());
182          }
183        else
184          {
185          vtkDebugMacro("File is coherent with previous ones"
186                        << FileName->c_str());
187          }
188
189        // Stage 2.3: when the file contains a volume (as opposed to an image),
190        // notify the caller.
191        if (NZ > 1)
192          {
193          vtkErrorMacro("This file contains multiple planes (images)"
194                        << FileName->c_str());
195          vtkErrorMacro("Removing this file from readed files " 
196                        << FileName->c_str());
197          }
198
199        // Eventually, this file can be added on the stack. Update the
200        // full size of the stack
201        vtkDebugMacro("Number of planes added to the stack: " << NZ);
202        ReturnedTotalNumberOfPlanes += NZ;
203        continue;
204
205        } else {
206        // We didn't have a workable reference file yet. Set this one
207        // as the reference.
208        FoundReferenceFile = true;
209        vtkDebugMacro("This file taken as coherence reference:"
210                      << FileName->c_str());
211        vtkDebugMacro("Image dimension of reference file as read from Gdcm:" <<
212                      NX << " " << NY << " " << NZ);
213        vtkDebugMacro("Number of planes added to the stack: " << NZ);
214        // Set aside the size of the image
215        this->NumColumns = NX;
216        this->NumLines   = NY;
217        ReferenceNZ      = NZ;
218        ReturnedTotalNumberOfPlanes += NZ;
219        this->ImageType = type;
220        this->PixelSize = GdcmHeader.GetPixelSize();
221        }
222      } // End of loop on FileName
223
224    ///////// The files we CANNOT load are flaged. On debugging purposes
225    // count the loadable number of files and display thir number:
226    int NumberCoherentFiles = 0;
227    for (std::list<std::string>::iterator FileName  = FileNameList.begin();
228                                         FileName != FileNameList.end();
229                                       ++FileName)
230      {
231      if (*FileName != "GDCM_UNREADABLE")
232         NumberCoherentFiles++;    
233      }
234    vtkDebugMacro("Number of coherent files: " << NumberCoherentFiles);
235
236    if (ReturnedTotalNumberOfPlanes == 0)
237      {
238      vtkErrorMacro("No loadable file.");
239      }
240
241    vtkDebugMacro("Total number of planes on the stack: "
242                  << ReturnedTotalNumberOfPlanes);
243    return ReturnedTotalNumberOfPlanes;
244 }
245
246 //----------------------------------------------------------------------------
247 // Configure the output e.g. WholeExtent, spacing, origin, scalar type...
248 void vtkGdcmReader::ExecuteInformation()
249 {
250   //FIXME free any old memory
251   this->TotalNumberOfPlanes = this->CheckFileCoherence();
252   if ( this->TotalNumberOfPlanes == 0)
253     {
254        vtkErrorMacro("File set is not coherent. Exiting...");
255        return;
256     }
257       
258   // if the user has not set the extent, but has set the VOI
259   // set the zaxis extent to the VOI z axis
260   if (this->DataExtent[4]==0 && this->DataExtent[5] == 0 &&
261      (this->DataVOI[4] || this->DataVOI[5]))
262     {
263     this->DataExtent[4] = this->DataVOI[4];
264     this->DataExtent[5] = this->DataVOI[5];
265     }
266
267   // When the user has set the VOI, check it's coherence with the file content.
268   if (this->DataVOI[0] || this->DataVOI[1] || 
269       this->DataVOI[2] || this->DataVOI[3] ||
270       this->DataVOI[4] || this->DataVOI[5])
271     { 
272     if ((this->DataVOI[0] < 0) ||
273         (this->DataVOI[1] >= this->NumColumns) ||
274         (this->DataVOI[2] < 0) ||
275         (this->DataVOI[3] >= this->NumLines) ||
276         (this->DataVOI[4] < 0) ||
277         (this->DataVOI[5] >= this->TotalNumberOfPlanes ))
278       {
279       vtkWarningMacro("The requested VOI is larger than expected extent.");
280       this->DataVOI[0] = 0;
281       this->DataVOI[1] = this->NumColumns - 1;
282       this->DataVOI[2] = 0;
283       this->DataVOI[3] = this->NumLines - 1;
284       this->DataVOI[4] = 0;
285       this->DataVOI[5] = this->TotalNumberOfPlanes - 1;
286       }
287     }
288
289   // Positionate the Extent.
290   this->DataExtent[0] = 0;
291   this->DataExtent[1] = this->NumColumns - 1;
292   this->DataExtent[2] = 0;
293   this->DataExtent[3] = this->NumLines - 1;
294   if(this->FileNameList.size() > 1)
295     {
296     this->DataExtent[4] = 0;
297     this->DataExtent[5] = this->TotalNumberOfPlanes - 1;
298     }
299   
300   // We don't need to positionate the Endian related stuff (by using
301   // this->SetDataByteOrderToBigEndian() or SetDataByteOrderToLittleEndian()
302   // since the reading of the file is done by gdcm.
303   // But we do need to set up the data type for downstream filters:
304   if      ( ImageType == "8U" )
305     {
306     vtkDebugMacro("8 bits unsigned image");
307     this->SetDataScalarTypeToUnsignedChar(); 
308     }
309   else if ( ImageType == "8S" )
310     {
311     vtkErrorMacro("Cannot handle 8 bit signed files");
312     return;
313     }
314   else if ( ImageType == "16U" )
315     {
316     vtkDebugMacro("16 bits unsigned image");
317     this->SetDataScalarTypeToUnsignedShort();
318     }
319   else if ( ImageType == "16S" )
320     {
321     vtkDebugMacro("16 bits signed image");
322     this->SetDataScalarTypeToShort();
323     //vtkErrorMacro("Cannot handle 16 bit signed files");
324     }
325   else if ( ImageType == "32U" )
326     {
327     vtkDebugMacro("32 bits unsigned image");
328     vtkDebugMacro("WARNING: forced to signed int !");
329     this->SetDataScalarTypeToInt();
330     }
331   else if ( ImageType == "32S" )
332     {
333     vtkDebugMacro("32 bits signed image");
334     this->SetDataScalarTypeToInt();
335     }
336
337   vtkImageReader::ExecuteInformation();
338 }
339
340 //----------------------------------------------------------------------------
341 // Loads the contents of the image/volume contained by Filename at
342 // the Dest memory address. Returns the size of the data loaded.
343 size_t vtkGdcmReader::LoadImageInMemory(
344              std::string FileName, 
345              unsigned char * Dest,
346              const unsigned long UpdateProgressTarget,
347              unsigned long & UpdateProgressCount)
348 {
349   vtkDebugMacro("Copying to memmory image" << FileName.c_str());
350   gdcmFile GdcmFile(FileName.c_str());
351   size_t size = GdcmFile.GetImageDataSize();
352
353   // If the data structure of vtk for image/volume representation
354   // were straigthforwards the following would suffice:
355   //    GdcmFile.GetImageDataIntoVector((void*)Dest, size);
356   // But vtk chose to invert the lines of an image, that is the last
357   // line comes first (for some axis related reasons?). Hence we need
358   // to load the image line by line, starting from the end:
359   int NumColumns = GdcmFile.GetXSize();
360   int NumLines   = GdcmFile.GetYSize();
361   int NumPlanes  = GdcmFile.GetZSize();
362   int LineSize   = NumColumns * GdcmFile.GetPixelSize();
363   unsigned char * Source      = (unsigned char*)GdcmFile.GetImageData();
364   unsigned char * Destination = Dest + size - LineSize;
365
366   for (int plane = 0; plane < NumPlanes; plane++)
367     {
368     for (int line = 0; line < NumLines; line++)
369       {
370       // Copy one line at proper destination:
371       memcpy((void*)Destination, (void*)Source, LineSize);
372       Source      += LineSize;
373       Destination -= LineSize;
374       // Update progress related:
375       if (!(UpdateProgressCount%UpdateProgressTarget))
376         {
377         this->UpdateProgress(UpdateProgressCount/(50.0*UpdateProgressTarget));
378         }
379       UpdateProgressCount++;
380       }
381     }
382   return size;
383 }
384
385 //----------------------------------------------------------------------------
386 // Update -> UpdateData -> Execute -> ExecuteData (see vtkSource.cxx for
387 // last step.
388 // This function (redefinition of vtkImageReader::ExecuteData, see 
389 // VTK/IO/vtkImageReader.cxx) reads a data from a file. The datas
390 // extent/axes are assumed to be the
391 // same as the file extent/order.
392 void vtkGdcmReader::ExecuteData(vtkDataObject *output)
393 {
394   if (this->FileNameList.empty())
395     {
396     vtkErrorMacro("A least a valid FileName must be specified.");
397     return;
398     }
399
400   vtkImageData *data = this->AllocateOutputData(output);
401   data->SetExtent(this->DataExtent);
402   data->GetPointData()->GetScalars()->SetName("DicomImage-Volume");
403
404   // The memory size for a full stack of images of course depends
405   // on the number of planes and the size of each image:
406   size_t StackNumPixels = this->NumColumns * this->NumLines
407                         * this->TotalNumberOfPlanes;
408   size_t stack_size = StackNumPixels * this->PixelSize;
409   // Allocate pixel data space itself.
410   unsigned char *mem = new unsigned char [stack_size];
411
412   // Variables for the UpdateProgress. We shall use 50 steps to signify
413   // the advance of the process:
414   unsigned long UpdateProgressTarget = (unsigned long) this->NumLines
415                                      * this->TotalNumberOfPlanes
416                                      / 50.0;
417   // The actual advance measure:
418   unsigned long UpdateProgressCount = 0;
419
420   // Feeling the allocated memory space with each image/volume:
421   unsigned char * Dest = mem;
422   for (std::list<std::string>::iterator FileName  = FileNameList.begin();
423                                         FileName != FileNameList.end();
424                                       ++FileName)
425     { 
426     // Images that were tagged as unreadable in CheckFileCoherence()
427     // are substituted with a black image to let the caller visually
428     // notice something wrong is going on:
429     if (*FileName != "GDCM_UNREADABLE")
430       {
431       Dest += this->LoadImageInMemory(*FileName, Dest,
432                                       UpdateProgressTarget,
433                                       UpdateProgressCount);
434       } else {
435       // We insert a black image in the stack for the user to be aware that
436       // this image/volume couldn't be loaded. We simply skip one image
437       // size:
438       Dest += this->NumColumns * this->NumLines * this->PixelSize;
439       // Update progress related:
440       UpdateProgressCount += this->NumLines;
441       if (!(UpdateProgressCount%UpdateProgressTarget))
442         {
443         this->UpdateProgress(UpdateProgressCount/(50.0*UpdateProgressTarget));
444         }
445       } // Else, file not loadable
446     } // Loop on files
447
448   // The "size" of the vtkScalars data is expressed in number of points,
449   // and is not the memory size representing those points:
450   data->GetPointData()->GetScalars()->SetVoidArray(mem, StackNumPixels, 0);
451   this->Modified();
452 }
453
454 //----------------------------------------------------------------------------
455 void vtkGdcmReader::PrintSelf(ostream& os, vtkIndent indent)
456 {
457   vtkImageReader::PrintSelf(os,indent);
458   os << indent << "Filenames  : " << endl;
459   vtkIndent nextIndent = indent.GetNextIndent();
460   for (std::list<std::string>::iterator FileName  = FileNameList.begin();
461                                         FileName != FileNameList.end();
462                                       ++FileName)
463     {
464     os << nextIndent << FileName->c_str() << endl ;
465     }
466 }