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