]> Creatis software - gdcm.git/blob - vtk/vtkGdcmReader.cxx
b8464b40b1d1271f6ea84da2622e7ef03dfdb732
[gdcm.git] / vtk / vtkGdcmReader.cxx
1 // $Header: /cvs/public/gdcm/vtk/vtkGdcmReader.cxx,v 1.21 2003/10/24 15:38:56 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 #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 "parasability"
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 place 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("Removing this file from readed files "
243                      << FileName->c_str());
244        *FileName = "GDCM_UNREADABLE";
245        continue;
246        }
247
248      /////// Stage 2: check coherence of the set of files
249      int NX = GdcmHeader.GetXSize();
250      int NY = GdcmHeader.GetYSize();
251      int NZ = GdcmHeader.GetZSize();
252      if (FoundReferenceFile) 
253        {
254         
255        // Stage 2.1: mandatory coherence stage:
256        if (   ( NX   != this->NumColumns )
257            || ( NY   != this->NumLines )
258            || ( type != this->ImageType ) ) 
259          {
260          vtkErrorMacro("This file is not coherent with previous ones"
261                        << FileName->c_str());
262          vtkErrorMacro("Removing this file from readed files "
263                        << FileName->c_str());
264          *FileName = "GDCM_UNREADABLE";
265          continue;
266          }
267
268        // Stage 2.2: optional coherence stage
269        if ( NZ != ReferenceNZ )
270          {
271          vtkErrorMacro("File is not coherent in Z with previous ones"
272                        << FileName->c_str());
273          }
274        else
275          {
276          vtkDebugMacro("File is coherent with previous ones"
277                        << FileName->c_str());
278          }
279
280        // Stage 2.3: when the file contains a volume (as opposed to an image),
281        // notify the caller.
282        if (NZ > 1)
283          {
284          vtkErrorMacro("This file contains multiple planes (images)"
285                        << FileName->c_str());
286          }
287
288        // Eventually, this file can be added on the stack. Update the
289        // full size of the stack
290        vtkDebugMacro("Number of planes added to the stack: " << NZ);
291        ReturnedTotalNumberOfPlanes += NZ - 1; // First plane already added
292        continue;
293
294        } else {
295        // We didn't have a workable reference file yet. Set this one
296        // as the reference.
297        FoundReferenceFile = true;
298        vtkDebugMacro("This file taken as coherence reference:"
299                      << FileName->c_str());
300        vtkDebugMacro("Image dimension of reference file as read from Gdcm:" <<
301                      NX << " " << NY << " " << NZ);
302        vtkDebugMacro("Number of planes added to the stack: " << NZ);
303        // Set aside the size of the image
304        this->NumColumns = NX;
305        this->NumLines   = NY;
306        ReferenceNZ      = NZ;
307        ReturnedTotalNumberOfPlanes += NZ - 1; // First plane already added
308        this->ImageType = type;
309        this->PixelSize = GdcmHeader.GetPixelSize();
310        this->NumComponents = GdcmHeader.GetNumberOfScalarComponents(); //rgb or mono
311        
312        //Set image spacing
313        this->DataSpacing[0] = GdcmHeader.GetXSpacing();
314        this->DataSpacing[1] = GdcmHeader.GetYSpacing();
315        this->DataSpacing[2] = GdcmHeader.GetZSpacing();
316
317        //Set image origin
318        this->DataOrigin[0] = GdcmHeader.GetXOrigin();
319        this->DataOrigin[1] = GdcmHeader.GetYOrigin();
320        this->DataOrigin[2] = GdcmHeader.GetZOrigin();
321        
322        }
323      } // End of loop on FileName
324
325    ///////// The files we CANNOT load are flaged. On debugging purposes
326    // count the loadable number of files and display thir number:
327    int NumberCoherentFiles = 0;
328    for (std::list<std::string>::iterator Filename  = InternalFileNameList.begin();
329                                         Filename != InternalFileNameList.end();
330                                       ++Filename)
331      {
332      if (*Filename != "GDCM_UNREADABLE")
333         NumberCoherentFiles++;    
334      }
335    vtkDebugMacro("Number of coherent files: " << NumberCoherentFiles);
336
337    if (ReturnedTotalNumberOfPlanes == 0)
338      {
339      vtkErrorMacro("No loadable file.");
340      }
341
342    vtkDebugMacro("Total number of planes on the stack: "
343                  << ReturnedTotalNumberOfPlanes);
344    
345         return ReturnedTotalNumberOfPlanes;
346 }
347
348 //----------------------------------------------------------------------------
349 // Configure the output e.g. WholeExtent, spacing, origin, scalar type...
350 void vtkGdcmReader::ExecuteInformation()
351 {
352   this->TotalNumberOfPlanes = this->CheckFileCoherence();
353   if ( this->TotalNumberOfPlanes == 0)
354     {
355        vtkErrorMacro("File set is not coherent. Exiting...");
356        return;
357     }
358       
359   // if the user has not set the extent, but has set the VOI
360   // set the zaxis extent to the VOI z axis
361   if (this->DataExtent[4]==0 && this->DataExtent[5] == 0 &&
362      (this->DataVOI[4] || this->DataVOI[5]))
363     {
364     this->DataExtent[4] = this->DataVOI[4];
365     this->DataExtent[5] = this->DataVOI[5];
366     }
367
368   // When the user has set the VOI, check it's coherence with the file content.
369   if (this->DataVOI[0] || this->DataVOI[1] || 
370       this->DataVOI[2] || this->DataVOI[3] ||
371       this->DataVOI[4] || this->DataVOI[5])
372     { 
373     if ((this->DataVOI[0] < 0) ||
374         (this->DataVOI[1] >= this->NumColumns) ||
375         (this->DataVOI[2] < 0) ||
376         (this->DataVOI[3] >= this->NumLines) ||
377         (this->DataVOI[4] < 0) ||
378         (this->DataVOI[5] >= this->TotalNumberOfPlanes ))
379       {
380       vtkWarningMacro("The requested VOI is larger than expected extent.");
381       this->DataVOI[0] = 0;
382       this->DataVOI[1] = this->NumColumns - 1;
383       this->DataVOI[2] = 0;
384       this->DataVOI[3] = this->NumLines - 1;
385       this->DataVOI[4] = 0;
386       this->DataVOI[5] = this->TotalNumberOfPlanes - 1;
387       }
388     }
389
390   // Positionate the Extent.
391   this->DataExtent[0] = 0;
392   this->DataExtent[1] = this->NumColumns - 1;
393   this->DataExtent[2] = 0;
394   this->DataExtent[3] = this->NumLines - 1;
395   this->DataExtent[4] = 0;
396   this->DataExtent[5] = this->TotalNumberOfPlanes - 1;
397   
398   // We don't need to positionate the Endian related stuff (by using
399   // this->SetDataByteOrderToBigEndian() or SetDataByteOrderToLittleEndian()
400   // since the reading of the file is done by gdcm.
401   // But we do need to set up the data type for downstream filters:
402   if      ( ImageType == "8U" )
403     {
404     vtkDebugMacro("8 bits unsigned image");
405     this->SetDataScalarTypeToUnsignedChar(); 
406     }
407   else if ( ImageType == "8S" )
408     {
409     vtkErrorMacro("Cannot handle 8 bit signed files");
410     return;
411     }
412   else if ( ImageType == "16U" )
413     {
414     vtkDebugMacro("16 bits unsigned image");
415     this->SetDataScalarTypeToUnsignedShort();
416     }
417   else if ( ImageType == "16S" )
418     {
419     vtkDebugMacro("16 bits signed image");
420     this->SetDataScalarTypeToShort();
421     //vtkErrorMacro("Cannot handle 16 bit signed files");
422     }
423   else if ( ImageType == "32U" )
424     {
425     vtkDebugMacro("32 bits unsigned image");
426     vtkDebugMacro("WARNING: forced to signed int !");
427     this->SetDataScalarTypeToInt();
428     }
429   else if ( ImageType == "32S" )
430     {
431     vtkDebugMacro("32 bits signed image");
432     this->SetDataScalarTypeToInt();
433     }
434
435   //Set number of scalar components:
436   this->SetNumberOfScalarComponents(this->NumComponents);
437
438   vtkImageReader::ExecuteInformation();
439 }
440
441 //----------------------------------------------------------------------------
442 // Loads the contents of the image/volume contained by Filename at
443 // the Dest memory address. Returns the size of the data loaded.
444 size_t vtkGdcmReader::LoadImageInMemory(
445              std::string FileName, 
446              unsigned char * Dest,
447              const unsigned long UpdateProgressTarget,
448              unsigned long & UpdateProgressCount)
449 {
450   vtkDebugMacro("Copying to memory image" << FileName.c_str());
451   gdcmFile GdcmFile(FileName.c_str());
452   size_t size = GdcmFile.GetImageDataSize();
453
454   // If the data structure of vtk for image/volume representation
455   // were straigthforwards the following would suffice:
456   //    GdcmFile.GetImageDataIntoVector((void*)Dest, size);
457   // But vtk chose to invert the lines of an image, that is the last
458   // line comes first (for some axis related reasons?). Hence we need
459   // to load the image line by line, starting from the end:
460   int NumColumns = GdcmFile.GetXSize();
461   int NumLines   = GdcmFile.GetYSize();
462   int NumPlanes  = GdcmFile.GetZSize();
463   int LineSize   = NumComponents * NumColumns * GdcmFile.GetPixelSize();
464   unsigned char * Source      = (unsigned char*)GdcmFile.GetImageData();
465   unsigned char * pSource     = Source; //pointer for later deletion
466   unsigned char * Destination = Dest + size - LineSize;
467
468   for (int plane = 0; plane < NumPlanes; plane++)
469     {
470     for (int line = 0; line < NumLines; line++)
471       {
472       // Copy one line at proper destination:
473       memcpy((void*)Destination, (void*)Source, LineSize);
474       Source      += LineSize;
475       Destination -= LineSize;
476       // Update progress related:
477       if (!(UpdateProgressCount%UpdateProgressTarget))
478         {
479         this->UpdateProgress(UpdateProgressCount/(50.0*UpdateProgressTarget));
480         }
481       UpdateProgressCount++;
482       }
483     }
484   //GetImageData allocate a (void*)malloc, remove it:
485   free(pSource);
486
487   return size;
488 }
489
490 //----------------------------------------------------------------------------
491 // Update => ouput->Update => UpdateData => Execute => ExecuteData 
492 // (see vtkSource.cxx for last step).
493 // This function (redefinition of vtkImageReader::ExecuteData, see 
494 // VTK/IO/vtkImageReader.cxx) reads a data from a file. The datas
495 // extent/axes are assumed to be the same as the file extent/order.
496 void vtkGdcmReader::ExecuteData(vtkDataObject *output)
497 {
498   if (this->InternalFileNameList.empty())
499     {
500     vtkErrorMacro("A least a valid FileName must be specified.");
501     return;
502     }
503
504   // FIXME : extraneous parsing of header is made when allocating OuputData
505   vtkImageData *data = this->AllocateOutputData(output);
506   data->SetExtent(this->DataExtent);
507   data->GetPointData()->GetScalars()->SetName("DicomImage-Volume");
508
509   // Test if output has valid extent
510   // Prevent memory errors
511   if((this->DataExtent[1]-this->DataExtent[0]>=0) &&
512      (this->DataExtent[3]-this->DataExtent[2]>=0) &&
513      (this->DataExtent[5]-this->DataExtent[4]>=0))
514     {
515     // The memory size for a full stack of images of course depends
516     // on the number of planes and the size of each image:
517     size_t StackNumPixels = this->NumColumns * this->NumLines
518                           * this->TotalNumberOfPlanes * this->NumComponents;
519     size_t stack_size = StackNumPixels * this->PixelSize;
520     // Allocate pixel data space itself.
521     unsigned char *mem = new unsigned char [stack_size];
522
523     // Variables for the UpdateProgress. We shall use 50 steps to signify
524     // the advance of the process:
525     unsigned long UpdateProgressTarget = (unsigned long) ceil (this->NumLines
526                                        * this->TotalNumberOfPlanes
527                                        / 50.0);
528     // The actual advance measure:
529     unsigned long UpdateProgressCount = 0;
530
531     // Feeling the allocated memory space with each image/volume:
532     unsigned char * Dest = mem;
533     for (std::list<std::string>::iterator FileName  = InternalFileNameList.begin();
534                                           FileName != InternalFileNameList.end();
535                                         ++FileName)
536       { 
537       // Images that were tagged as unreadable in CheckFileCoherence()
538       // are substituted with a black image to let the caller visually
539       // notice something wrong is going on:
540       if (*FileName != "GDCM_UNREADABLE")
541         {
542         // Update progress related for good files is made in LoadImageInMemory
543         Dest += this->LoadImageInMemory(*FileName, Dest,
544                                         UpdateProgressTarget,
545                                         UpdateProgressCount);
546         } else {
547         // We insert a black image in the stack for the user to be aware that
548         // this image/volume couldn't be loaded. We simply skip one image
549         // size:
550         Dest += this->NumColumns * this->NumLines * this->PixelSize;
551
552         // Update progress related for bad files:
553         UpdateProgressCount += this->NumLines;
554         if (UpdateProgressTarget > 0)
555                       {
556           if (!(UpdateProgressCount%UpdateProgressTarget))
557             {
558                         this->UpdateProgress(UpdateProgressCount/(50.0*UpdateProgressTarget));
559             }
560                       }
561         } // Else, file not loadable
562       } // Loop on files
563
564     // The "size" of the vtkScalars data is expressed in number of points,
565     // and is not the memory size representing those points:
566     data->GetPointData()->GetScalars()->SetVoidArray(mem, StackNumPixels, 0);
567     //don't know why it's here, it's calling one more time ExecuteInformation:
568     //this->Modified();
569     }
570 }
571
572 //----------------------------------------------------------------------------
573 void vtkGdcmReader::PrintSelf(ostream& os, vtkIndent indent)
574 {
575   vtkImageReader::PrintSelf(os,indent);
576   os << indent << "Filenames  : " << endl;
577   vtkIndent nextIndent = indent.GetNextIndent();
578   for (std::list<std::string>::iterator FileName  = FileNameList.begin();
579                                         FileName != FileNameList.end();
580                                       ++FileName)
581     {
582     os << nextIndent << FileName->c_str() << endl ;
583     }
584 }