]> Creatis software - gdcm.git/blob - vtk/vtkGdcmReader.cxx
* vtk/vtkGdcmReader[cxx|h] preparation addons for loading volumes.
[gdcm.git] / vtk / vtkGdcmReader.cxx
1 // $Header: /cvs/public/gdcm/vtk/vtkGdcmReader.cxx,v 1.6 2003/05/29 16:58:24 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    // Starting from two files we have a stack of images:
31    if(this->FileNameList.size() >= 2)
32       this->SetFileDimensionality(3);
33 }
34
35 //----------------------------------------------------------------------------
36 // Sets up a filename to be read.
37 void vtkGdcmReader::SetFileName(const char *name) {
38    vtkImageReader2::SetFileName(name);
39    // Since we maintain a list of filenames (when building a volume)
40    // we additionaly need to maintain this list. First we clean-up the
41    // list and then positionate the incoming filename:
42    this->FileNameList.empty();
43    this->AddFileName(name);
44 }
45
46 //----------------------------------------------------------------------------
47 // vtkGdcmReader can have the file names specified through two ways:
48 // (1) by calling the vtkImageReader2::SetFileName(), SetFilePrefix() and
49 //     SetFilePattern()
50 // (2) By successive calls to vtkGdcmReader::SetFileName()
51 // When the first method was used by caller we need to update the local
52 // filename list
53 void vtkGdcmReader::BuilFileListFromPattern()
54 {
55    if (! this->FileNameList.empty())
56       return;
57    if (!this->FileName && !this->FilePattern)
58      {
59      vtkErrorMacro("FileNames are not set. Either use AddFileName() or");
60      vtkErrorMacro("specify a FileName or FilePattern.");
61      return;
62      }
63    for (int idx = this->DataExtent[4]; idx <= this->DataExtent[5]; ++idx)
64      {
65      this->ComputeInternalFileName(idx);
66      vtkDebugMacro("Adding file " << this->InternalFileName);
67      this->AddFileName(this->InternalFileName);
68      }
69 }
70
71 //----------------------------------------------------------------------------
72 // When more than one filename is specified (i.e. we expect loading
73 // a stack or volume) we need to check the corresponding images are
74 // coherent:
75 //  - they all share the same X dimensions
76 //  - they all share the same Y dimensions
77 //  - each file a Z dimension of 1
78 //  - they all share the same type ( 8 bit signed, or unsigned...)
79 bool vtkGdcmReader::CheckFileCoherence()
80 {
81    this->BuilFileListFromPattern();
82    if (this->FileNameList.empty())
83      {
84      vtkErrorMacro("FileNames are not set.");
85      return false;
86      }
87    if (this->FileNameList.size() == 1)
88      {
89      vtkDebugMacro("Single file specified.");
90      return true;
91      }
92
93    // Loop on the filenames:
94    // - check for their existence and gdcm "parasability"
95    // - get the coherence check done:
96    bool FoundReferenceFile = false;
97    int ReferenceNX;
98    int ReferenceNY;
99    int ReferenceNZ;
100    std::string ReferenceType;
101    for (std::list<std::string>::iterator FileName  = FileNameList.begin();
102                                         FileName != FileNameList.end();
103                                       ++FileName)
104      {
105      // Check for file existence.
106      FILE *fp;
107      fp = fopen(FileName->c_str(),"rb");
108      if (!fp)
109        {
110        vtkErrorMacro("Unable to open file " << *FileName);
111        vtkErrorMacro("Removing this file from readed files " << *FileName);
112        FileNameList.remove(*FileName);
113        continue;
114        }
115      fclose(fp);
116    
117      // Check for Gdcm parsability
118      gdcmHeader GdcmHeader(FileName->c_str());
119      if (!GdcmHeader.IsReadable())
120        {
121        vtkErrorMacro("Gdcm cannot parse file " << *FileName);
122        vtkErrorMacro("Removing this file from readed files " << *FileName);
123        FileNameList.remove(*FileName);
124        continue;
125        }
126
127      // Coherence stage:
128      int NX = GdcmHeader.GetXSize();
129      int NY = GdcmHeader.GetYSize();
130      int NZ = GdcmHeader.GetZSize();
131      std::string type = GdcmHeader.GetPixelType();
132      if (FoundReferenceFile) 
133        {
134        if (   ( NX != ReferenceNX )
135            || ( NY != ReferenceNY )
136            || ( NZ != ReferenceNZ )
137            || ( type != ReferenceType ) ) 
138          {
139             vtkErrorMacro("This file is not coherent with previous ones"
140                           << *FileName);
141             vtkErrorMacro("Removing this file from readed files " << *FileName);
142             FileNameList.remove(*FileName);
143             continue;
144          } else {
145             vtkDebugMacro("File is coherent with previous ones" << *FileName);
146          }
147        } else {
148          // This file shall be the reference:
149          FoundReferenceFile = true;
150          ReferenceNX = NX;
151          ReferenceNY = NY;
152          ReferenceNZ = NZ;
153          ReferenceType = type;
154          vtkDebugMacro("File is taken a coherence references" << *FileName);
155        }
156      } // End of loop on FileName
157
158    if (this->FileNameList.empty())
159      {
160      vtkDebugMacro("No gdcm parsable file.");
161      return false;
162      }
163    if (this->FileNameList.size() == 1)
164      {
165      vtkDebugMacro("Single parsable file left after coherence test.");
166      return true;
167      }
168    return true;
169 }
170
171 //----------------------------------------------------------------------------
172 // Configure the output e.g. WholeExtent, spacing, origin, scalar type...
173 void vtkGdcmReader::ExecuteInformation()
174 {
175   //FIXME free any old memory
176       
177   // if the user has not set the extent, but has set the VOI
178   // set the zaxis extent to the VOI z axis
179   if (this->DataExtent[4]==0 && this->DataExtent[5] == 0 &&
180      (this->DataVOI[4] || this->DataVOI[5]))
181     {
182     this->DataExtent[4] = this->DataVOI[4];
183     this->DataExtent[5] = this->DataVOI[5];
184     }
185
186   this->ComputeInternalFileName(this->DataExtent[4]);
187   
188   // Check for file existence.
189   FILE *fp;
190   fp = fopen(this->InternalFileName,"rb");
191   if (!fp)
192     {
193     vtkErrorMacro("Unable to open file " << this->InternalFileName);
194     return;
195     }
196   fclose(fp);
197
198   // Check for Gdcm parsability
199   gdcmHeader GdcmHeader(this->InternalFileName);
200   if (!GdcmHeader.IsReadable())
201     {
202     vtkErrorMacro("Gdcm cannot parse file " << this->InternalFileName);
203     return;
204     }
205
206   int NX = GdcmHeader.GetXSize();
207   int NY = GdcmHeader.GetYSize();
208   int NZ = GdcmHeader.GetZSize();
209   vtkDebugMacro("Image dimension as read from Gdcm:" <<
210                 NX << " " << NY << " " << NZ);
211
212   if(NZ>1) this->SetFileDimensionality(3);
213
214   // When the user has set the VOI, check it's coherence with the file content.
215   if (this->DataVOI[0] || this->DataVOI[1] || 
216       this->DataVOI[2] || this->DataVOI[3] ||
217       this->DataVOI[4] || this->DataVOI[5])
218     { 
219     if ((this->DataVOI[0] < 0) ||
220         (this->DataVOI[1] >= NX) ||
221         (this->DataVOI[2] < 0) ||
222         (this->DataVOI[3] >= NY) ||
223         (this->DataVOI[4] < 0) ||
224         (this->DataVOI[5] >= NZ))
225       {
226       vtkWarningMacro("The requested VOI is larger than the file's ("
227                       << this->InternalFileName << ") extent ");
228       this->DataVOI[0] = 0;
229       this->DataVOI[1] = NX - 1;
230       this->DataVOI[2] = 0;
231       this->DataVOI[3] = NY - 1;
232       this->DataVOI[4] = 0;
233       this->DataVOI[5] = NZ - 1;
234       }
235     }
236
237   // Positionate the Extent.
238   this->DataExtent[0] = 0;
239   this->DataExtent[1] = NX - 1;
240   this->DataExtent[2] = 0;
241   this->DataExtent[3] = NY - 1;
242   if(this->GetFileDimensionality()==3)
243     {
244       this->DataExtent[4] = 0;
245       this->DataExtent[5] = NZ - 1;
246     }
247   
248   // We don't need to positionate the Endian related stuff (by using
249   // this->SetDataByteOrderToBigEndian() or SetDataByteOrderToLittleEndian()
250   // since the reading of the file is done by gdcm.
251   // But we do need to set up the data type for downstream filters:
252   std::string type = GdcmHeader.GetPixelType();
253   if      ( type == "8U" )
254     {
255     vtkDebugMacro("8 bits unsigned image");
256     this->SetDataScalarTypeToUnsignedChar(); 
257     }
258   else if ( type == "8S" )
259     {
260     vtkErrorMacro("Cannot handle 8 bit signed files");
261     return;
262     }
263   else if ( type == "16U" )
264     {
265     vtkDebugMacro("16 bits unsigned image");
266     this->SetDataScalarTypeToUnsignedShort();
267     }
268   else if ( type == "16S" )
269     {
270     vtkDebugMacro("16 bits signed image");
271     this->SetDataScalarTypeToShort();
272     //vtkErrorMacro("Cannot handle 16 bit signed files");
273     }
274   else if ( type == "32U" )
275     {
276     vtkDebugMacro("32 bits unsigned image");
277     vtkDebugMacro("WARNING: forced to signed int !");
278     this->SetDataScalarTypeToInt();
279     }
280   else if ( type == "32S" )
281     {
282     vtkDebugMacro("32 bits signed image");
283     this->SetDataScalarTypeToInt();
284     }
285   else
286     {
287     vtkErrorMacro("Bad File Type " << this->InternalFileName
288                                    << "Type " << type.c_str());
289     return;
290     }
291
292   vtkImageReader::ExecuteInformation();
293 }
294
295 //----------------------------------------------------------------------------
296 // Update -> UpdateData -> Execute -> ExecuteData (see vtkSource.cxx for
297 // last step.
298 // This function (redefinition of vtkImageReader::ExecuteData, see 
299 // VTK/IO/vtkImageReader.cxx) reads a data from a file. The datas
300 // extent/axes are assumed to be the
301 // same as the file extent/order.
302 void vtkGdcmReader::ExecuteData(vtkDataObject *output)
303 {
304   if (!this->FileName)
305     {
306     vtkErrorMacro("A valid FileName must be specified.");
307     return;
308     }
309
310   vtkImageData *data = this->AllocateOutputData(output);
311   data->SetExtent(this->DataExtent);
312   data->GetPointData()->GetScalars()->SetName("ImageFile");
313
314   // First check the coherence between the DataExtent and the
315   // size of the pixel data as annouced by gdcm (looks a bit paranoid).
316   gdcmFile GdcmFile(this->InternalFileName);
317   int NumColumns = this->DataExtent[1] - this->DataExtent[0] + 1;
318   int NumLines   = this->DataExtent[3] - this->DataExtent[2] + 1;
319   int NumPlanes  = this->DataExtent[5] - this->DataExtent[4] + 1;
320   size_t size = NumColumns * NumLines * NumPlanes * GdcmFile.GetPixelSize();
321   if ( size != GdcmFile.GetImageDataSize() )
322     {
323     vtkDebugMacro("Inconsistency with GetImageDataSize");
324     vtkDebugMacro("Number of scalar components"
325                   << this->NumberOfScalarComponents);
326     }
327   // Allocate pixel data space itself.
328   unsigned char *mem = new unsigned char [size];
329
330   // If the data structure of vtk for image/volume representation
331   // were straigthforwards the following would suffice:
332   //    GdcmFile.GetImageDataIntoVector((void*)mem, size);
333   // But vtk chose to invert the lines of an image, that is the last
334   // line comes first (for some axis related reasons?). Hence we need
335   // to load the image line by line, starting from the end:
336   int LineSize = NumColumns * GdcmFile.GetPixelSize();
337   unsigned char * Source      = (unsigned char*)GdcmFile.GetImageData();
338   unsigned char * Destination = mem + size - LineSize;
339   for (int i = 0; i < NumLines; i++)
340     {
341     memcpy((void*)Destination, (void*)Source, LineSize);
342     Source      += LineSize;
343     Destination -= LineSize;
344     }
345   // The "size" of the vtkScalars data is expressed in number of points,
346   // and is not the memory size representing those points:
347   size = size / GdcmFile.GetPixelSize();
348   data->GetPointData()->GetScalars()->SetVoidArray(mem, size, 0);
349   this->Modified();
350
351 }
352
353 //----------------------------------------------------------------------------
354 void vtkGdcmReader::PrintSelf(ostream& os, vtkIndent indent)
355 {
356   vtkImageReader::PrintSelf(os,indent);
357   os << indent << "Filenames  : " << endl;
358   vtkIndent nextIndent = indent.GetNextIndent();
359   for (std::list<std::string>::iterator FileName  = FileNameList.begin();
360                                         FileName != FileNameList.end();
361                                       ++FileName)
362     {
363     os << nextIndent << *FileName << endl ;
364     }
365 }