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