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