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