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