]> Creatis software - gdcm.git/blob - vtk/vtkGdcmWriter.cxx
ENH: Some minor enhance:
[gdcm.git] / vtk / vtkGdcmWriter.cxx
1 /*=========================================================================
2                                                                                 
3   Program:   gdcm
4   Module:    $RCSfile: vtkGdcmWriter.cxx,v $
5   Language:  C++
6   Date:      $Date: 2005/05/11 14:40:58 $
7   Version:   $Revision: 1.22 $
8                                                                                 
9   Copyright (c) CREATIS (Centre de Recherche et d'Applications en Traitement de
10   l'Image). All rights reserved. See Doc/License.txt or
11   http://www.creatis.insa-lyon.fr/Public/Gdcm/License.html for details.
12                                                                                 
13      This software is distributed WITHOUT ANY WARRANTY; without even
14      the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15      PURPOSE.  See the above copyright notices for more information.
16                                                                                 
17 =========================================================================*/
18                                                                                 
19 #include "gdcmFile.h"
20 #include "gdcmFileHelper.h"
21 #include "gdcmDebug.h"
22 #include "gdcmUtil.h"
23 #include "vtkGdcmWriter.h"
24
25 #include <vtkObjectFactory.h>
26 #include <vtkImageData.h>
27 #include <vtkPointData.h>
28 #include <vtkLookupTable.h>
29
30 #ifndef vtkFloatingPointType
31 #define vtkFloatingPointType float
32 #endif
33
34 vtkCxxRevisionMacro(vtkGdcmWriter, "$Revision: 1.22 $");
35 vtkStandardNewMacro(vtkGdcmWriter);
36
37 //-----------------------------------------------------------------------------
38 // Constructor / Destructor
39 vtkGdcmWriter::vtkGdcmWriter()
40 {
41    this->LookupTable = NULL;
42    this->FileDimensionality = 3;
43    this->WriteType = VTK_GDCM_WRITE_TYPE_EXPLICIT_VR;
44 }
45
46 vtkGdcmWriter::~vtkGdcmWriter()
47 {
48 }
49
50 //-----------------------------------------------------------------------------
51 // Print
52 void vtkGdcmWriter::PrintSelf(ostream &os, vtkIndent indent)
53 {
54    this->Superclass::PrintSelf(os, indent);
55
56    os << indent << "Write type : " << this->GetWriteTypeAsString();
57 }
58
59 //-----------------------------------------------------------------------------
60 // Public
61 const char *vtkGdcmWriter::GetWriteTypeAsString()
62 {
63    switch(WriteType)
64    {
65       case VTK_GDCM_WRITE_TYPE_EXPLICIT_VR :
66          return "Explicit VR";
67       case VTK_GDCM_WRITE_TYPE_IMPLICIT_VR :
68          return "Implicit VR";
69       case VTK_GDCM_WRITE_TYPE_ACR :
70          return "ACR";
71       case VTK_GDCM_WRITE_TYPE_ACR_LIBIDO :
72          return "ACR Libido";
73       default :
74          return "Unknow type";
75    }
76 }
77
78 //-----------------------------------------------------------------------------
79 // Protected
80 /**
81  * Copy the image and reverse the Y axis
82  */
83 // The output data must be deleted by the user of the method !!!
84 size_t ReverseData(vtkImageData *image,unsigned char **data)
85 {
86    int inc[3];
87    int *extent = image->GetUpdateExtent();
88    int dim[3] = {extent[1]-extent[0]+1,
89                  extent[3]-extent[2]+1,
90                  extent[5]-extent[4]+1};
91
92    size_t lineSize = dim[0] * image->GetScalarSize()
93                    * image->GetNumberOfScalarComponents();
94    size_t planeSize = dim[1] * lineSize;
95    size_t size = dim[2] * planeSize;
96
97    if( size>0 )
98    {
99       *data = new unsigned char[size];
100
101       image->GetIncrements(inc);
102       unsigned char *src = (unsigned char *)image->GetScalarPointerForExtent(extent);
103       unsigned char *dst = *data + planeSize - lineSize;
104       for (int plane = extent[4]; plane <= extent[5]; plane++)
105       {
106          for (int line = extent[2]; line <= extent[3]; line++)
107          {
108             // Copy one line at proper destination:
109             memcpy((void*)dst, (void*)src, lineSize);
110
111             src += inc[1] * image->GetScalarSize();
112             dst -= lineSize;
113          }
114          dst += 2 * planeSize;
115       }
116    }
117    else
118    {
119       *data = NULL;
120    }
121
122    return size;
123 }
124
125 /**
126  * Set the data informations in the file
127  */
128 void SetImageInformation(gdcm::FileHelper *file, vtkImageData *image)
129 {
130    std::ostringstream str;
131
132    // Image size
133    int *extent = image->GetUpdateExtent();
134    int dim[3] = {extent[1]-extent[0]+1,
135                  extent[3]-extent[2]+1,
136                  extent[5]-extent[4]+1};
137
138    str.str("");
139    str << dim[0];
140    file->InsertValEntry(str.str(),0x0028,0x0011); // Columns
141
142    str.str("");
143    str << dim[1];
144    file->InsertValEntry(str.str(),0x0028,0x0010); // Rows
145
146    if(dim[2]>1)
147    {
148       str.str("");
149       str << dim[2];
150       //file->Insert(str.str(),0x0028,0x0012); // Planes
151       file->InsertValEntry(str.str(),0x0028,0x0008); // Number of Frames
152    }
153
154    // Pixel type
155    str.str("");
156    str << image->GetScalarSize()*8;
157    file->InsertValEntry(str.str(),0x0028,0x0100); // Bits Allocated
158    file->InsertValEntry(str.str(),0x0028,0x0101); // Bits Stored
159
160    str.str("");
161    str << image->GetScalarSize()*8-1;
162    file->InsertValEntry(str.str(),0x0028,0x0102); // High Bit
163
164    // Pixel Repr
165    // FIXME : what do we do when the ScalarType is 
166    // VTK_UNSIGNED_INT or VTK_UNSIGNED_LONG
167    str.str("");
168    if( image->GetScalarType() == VTK_UNSIGNED_CHAR ||
169        image->GetScalarType() == VTK_UNSIGNED_SHORT ||
170        image->GetScalarType() == VTK_UNSIGNED_INT ||
171        image->GetScalarType() == VTK_UNSIGNED_LONG )
172    {
173       str << "0"; // Unsigned
174    }
175    else
176    {
177       str << "1"; // Signed
178    }
179    file->InsertValEntry(str.str(),0x0028,0x0103); // Pixel Representation
180
181    // Samples per pixel
182    str.str("");
183    str << image->GetNumberOfScalarComponents();
184    file->InsertValEntry(str.str(),0x0028,0x0002); // Samples per Pixel
185
186    /// \todo : Spacing Between Slices is meaningfull ONLY for CT an MR modality
187    ///       We should perform some checkings before forcing the Entry creation
188
189    // Spacing
190    vtkFloatingPointType *sp = image->GetSpacing();
191
192    str.str("");
193    // We are about to enter floating point value. By default ostringstream are smart and don't do fixed point
194    // thus forcing to fixed point value
195    str.setf( std::ios::fixed );
196    str << sp[1] << "\\" << sp[0];
197    file->InsertValEntry(str.str(),0x0028,0x0030); // Pixel Spacing
198    str.str("");
199    str << sp[2];
200    file->InsertValEntry(str.str(),0x0018,0x0088); // Spacing Between Slices
201
202    // Origin
203    vtkFloatingPointType *org = image->GetOrigin();
204
205    /// \todo : Image Position Patient is meaningfull ONLY for CT an MR modality
206    ///       We should perform some checkings before forcing the Entry creation
207
208    str.str("");
209    str << org[0] << "\\" << org[1] << "\\" << org[2];
210    file->InsertValEntry(str.str(),0x0020,0x0032); // Image Position Patient
211    str.unsetf( std::ios::fixed ); //done with floating point values
212
213    // Window / Level
214    vtkFloatingPointType *rng = image->GetScalarRange();
215
216    str.str("");
217    str << rng[1]-rng[0];
218    file->InsertValEntry(str.str(),0x0028,0x1051); // Window Width
219    str.str("");
220    str << (rng[1]+rng[0])/2.0;
221    file->InsertValEntry(str.str(),0x0028,0x1050); // Window Center
222
223    // Pixels
224    unsigned char *data;
225    size_t size = ReverseData(image,&data);
226    file->SetUserData(data,size);
227 }
228
229 /**
230  * Write of the files
231  * The call to this method is recursive if there is some files to write
232  */ 
233 void vtkGdcmWriter::RecursiveWrite(int axis, vtkImageData *image, 
234                     ofstream *file)
235 {
236    if(file)
237    {
238       vtkErrorMacro( <<  "File must not be open");
239       return;
240    }
241
242    if( image->GetScalarType() == VTK_FLOAT || 
243        image->GetScalarType() == VTK_DOUBLE )
244    {
245       vtkErrorMacro(<< "Bad input type. Scalar type must not be of type "
246                     << "VTK_FLOAT or VTKDOUBLE (found:"
247                     << image->GetScalarTypeAsString());
248       return;
249    }
250
251    RecursiveWrite(axis,image, image, file);
252    //WriteDcmFile(this->FileName,image);
253 }
254
255 void vtkGdcmWriter::RecursiveWrite(int axis, vtkImageData *cache, 
256                                    vtkImageData *image, ofstream *file)
257 {
258    int idx, min, max;
259
260    // if the file is already open then just write to it
261    if( file )
262    {
263       vtkErrorMacro( <<  "File musn't be open");
264       return;
265    }
266
267    // if we need to open another slice, do it
268    if( (axis + 1) == this->FileDimensionality )
269    {
270       // determine the name
271       if (this->FileName)
272       {
273          sprintf(this->InternalFileName, "%s", this->FileName);
274       }
275       else 
276       {
277          if (this->FilePrefix)
278          {
279             sprintf(this->InternalFileName, this->FilePattern, 
280             this->FilePrefix, this->FileNumber);
281          }
282          else
283          {
284             sprintf(this->InternalFileName, this->FilePattern,this->FileNumber);
285          }
286 // Remove this code in case user is using VTK 4.2...
287 #if !(VTK_MAJOR_VERSION == 4 && VTK_MINOR_VERSION == 2)
288          if (this->FileNumber < this->MinimumFileNumber)
289          {
290             this->MinimumFileNumber = this->FileNumber;
291          }
292          else if (this->FileNumber > this->MaximumFileNumber)
293          {
294             this->MaximumFileNumber = this->FileNumber;
295          }
296 #endif
297       }
298
299       // Write the file
300       WriteDcmFile(this->InternalFileName,image);
301       ++this->FileNumber;
302       return;
303    }
304
305    // if the current region is too high a dimension for the file
306    // the we will split the current axis
307    cache->GetAxisUpdateExtent(axis, min, max);
308
309    // if it is the y axis then flip by default
310    if (axis == 1 && !this->FileLowerLeft)
311    {
312       for(idx = max; idx >= min; idx--)
313       {
314          cache->SetAxisUpdateExtent(axis, idx, idx);
315          this->RecursiveWrite(axis - 1, cache, image, file);
316       }
317    }
318    else
319    {
320       for(idx = min; idx <= max; idx++)
321       {
322          cache->SetAxisUpdateExtent(axis, idx, idx);
323          this->RecursiveWrite(axis - 1, cache, image, file);
324       }
325    }
326
327    // restore original extent
328    cache->SetAxisUpdateExtent(axis, min, max);
329 }
330
331 void vtkGdcmWriter::WriteDcmFile(char *fileName, vtkImageData *image)
332 {
333    // From here, the write of the file begins
334    gdcm::FileHelper *dcmFile = new gdcm::FileHelper();
335
336    // Set the image informations
337    SetImageInformation(dcmFile, image);
338
339    // Write the image
340    switch(this->WriteType)
341    {
342       case VTK_GDCM_WRITE_TYPE_EXPLICIT_VR :
343          dcmFile->SetWriteTypeToDcmExplVR();
344          break;
345       case VTK_GDCM_WRITE_TYPE_IMPLICIT_VR :
346          dcmFile->SetWriteTypeToDcmImplVR();
347          break;
348       case VTK_GDCM_WRITE_TYPE_ACR :
349          dcmFile->SetWriteTypeToAcr();
350          break;
351       case VTK_GDCM_WRITE_TYPE_ACR_LIBIDO :
352          dcmFile->SetWriteTypeToAcrLibido();
353          break;
354       default :
355          dcmFile->SetWriteTypeToDcmExplVR();
356    }
357
358    if(!dcmFile->Write(fileName))
359    {
360       vtkErrorMacro( << "File "  <<  this->FileName  <<  "cannot be written by "
361                      << " the gdcm library");
362    }
363
364    // Clean up
365    if( dcmFile->GetUserData() && dcmFile->GetUserDataSize()>0 )
366    {
367       delete[] dcmFile->GetUserData();
368    }
369    delete dcmFile;
370 }
371
372 //-----------------------------------------------------------------------------
373 // Private
374
375 //-----------------------------------------------------------------------------