]> Creatis software - gdcm.git/blob - vtk/vtkGdcmWriter.cxx
Fix bug in windows part
[gdcm.git] / vtk / vtkGdcmWriter.cxx
1 /*=========================================================================
2                                                                                 
3   Program:   gdcm
4   Module:    $RCSfile: vtkGdcmWriter.cxx,v $
5   Language:  C++
6   Date:      $Date: 2007/12/13 15:16:19 $
7   Version:   $Revision: 1.36 $
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 #if (VTK_MAJOR_VERSION >= 5)
30 #include <vtkMedicalImageProperties.h>
31 #endif
32 #ifndef vtkFloatingPointType
33 #define vtkFloatingPointType float
34 #endif
35
36 vtkCxxRevisionMacro(vtkGdcmWriter, "$Revision: 1.36 $")
37 vtkStandardNewMacro(vtkGdcmWriter)
38
39 vtkCxxSetObjectMacro(vtkGdcmWriter,LookupTable,vtkLookupTable)
40 #if (VTK_MAJOR_VERSION >= 5)
41 vtkCxxSetObjectMacro(vtkGdcmWriter,MedicalImageProperties,vtkMedicalImageProperties)
42 #endif
43 //-----------------------------------------------------------------------------
44 // Constructor / Destructor
45 vtkGdcmWriter::vtkGdcmWriter()
46 {
47    this->LookupTable = NULL;
48    this->MedicalImageProperties = NULL;
49    this->FileDimensionality = 3;
50    this->WriteType = VTK_GDCM_WRITE_TYPE_EXPLICIT_VR;
51    this->GdcmFile = 0;
52    this->ContentType = VTK_GDCM_WRITE_TYPE_USER_OWN_IMAGE;
53 }
54
55 vtkGdcmWriter::~vtkGdcmWriter()
56 {
57    this->SetMedicalImageProperties(NULL);
58    this->SetLookupTable(NULL);
59 }
60
61 //-----------------------------------------------------------------------------
62 // Print
63 void vtkGdcmWriter::PrintSelf(ostream &os, vtkIndent indent)
64 {
65    this->Superclass::PrintSelf(os, indent);
66
67    os << indent << "Write type : " << this->GetWriteTypeAsString();
68 }
69
70 //-----------------------------------------------------------------------------
71 // Public
72 const char *vtkGdcmWriter::GetWriteTypeAsString()
73 {
74    switch(WriteType)
75    {
76       case VTK_GDCM_WRITE_TYPE_EXPLICIT_VR :
77          return "Explicit VR";
78       case VTK_GDCM_WRITE_TYPE_IMPLICIT_VR :
79          return "Implicit VR";
80       case VTK_GDCM_WRITE_TYPE_ACR :
81          return "ACR";
82       case VTK_GDCM_WRITE_TYPE_ACR_LIBIDO :
83          return "ACR Libido";
84       default :
85          return "Unknow type";
86    }
87 }
88
89 //-----------------------------------------------------------------------------
90 // Protected
91 /**
92  * Copy the image and reverse the Y axis
93  */
94 // The output data must be deleted by the user of the method !!!
95 size_t ReverseData(vtkImageData *image,unsigned char **data)
96 {
97 #if (VTK_MAJOR_VERSION >= 5)
98    vtkIdType inc[3];
99 #else
100    int inc[3];
101 #endif
102    int *extent = image->GetUpdateExtent();
103    int dim[3] = {extent[1]-extent[0]+1,
104                  extent[3]-extent[2]+1,
105                  extent[5]-extent[4]+1};
106
107    size_t lineSize = dim[0] * image->GetScalarSize()
108                    * image->GetNumberOfScalarComponents();
109    size_t planeSize = dim[1] * lineSize;
110    size_t size = dim[2] * planeSize;
111
112    if( size>0 )
113    {
114       *data = new unsigned char[size];
115
116       image->GetIncrements(inc);
117       unsigned char *src = (unsigned char *)image->GetScalarPointerForExtent(extent);
118       unsigned char *dst = *data + planeSize - lineSize;
119       for (int plane = extent[4]; plane <= extent[5]; plane++)
120       {
121          for (int line = extent[2]; line <= extent[3]; line++)
122          {
123             // Copy one line at proper destination:
124             memcpy((void*)dst, (void*)src, lineSize);
125
126             src += inc[1] * image->GetScalarSize();
127             dst -= lineSize;
128          }
129          dst += 2 * planeSize;
130       }
131    }
132    else
133    {
134       *data = NULL;
135    }
136
137    return size;
138 }
139
140 /**
141  * Set the medical informations in the file, based on the user passed
142  * vtkMedicalImageProperties
143  */
144 #if (VTK_MAJOR_VERSION >= 5)
145 void SetMedicalImageInformation(GDCM_NAME_SPACE::FileHelper *file, vtkMedicalImageProperties *medprop)
146 {
147    // For now only do:
148    // PatientName, PatientID, PatientAge, PatientSex, PatientBirthDate, StudyID
149    std::ostringstream str;
150    if( medprop )
151      {
152      if (medprop->GetPatientName())
153         {
154         str.str("");
155         str << medprop->GetPatientName();
156         file->InsertEntryString(str.str(),0x0010,0x0010,"PN"); // PN 1 Patient's Name
157         }
158
159      if (medprop->GetPatientID())
160         {
161         str.str("");
162         str << medprop->GetPatientID();
163         file->InsertEntryString(str.str(),0x0010,0x0020,"LO"); // LO 1 Patient ID
164         }
165
166      if (medprop->GetPatientAge())
167         {
168         str.str("");
169         str << medprop->GetPatientAge();
170         file->InsertEntryString(str.str(),0x0010,0x1010,"AS"); // AS 1 Patient's Age
171         }
172
173      if (medprop->GetPatientSex())
174         {
175         str.str("");
176         str << medprop->GetPatientSex();
177         file->InsertEntryString(str.str(),0x0010,0x0040,"CS"); // CS 1 Patient's Sex
178         }
179
180      if (medprop->GetPatientBirthDate())
181         {
182         str.str("");
183         str << medprop->GetPatientBirthDate();
184         file->InsertEntryString(str.str(),0x0010,0x0030,"DA"); // DA 1 Patient's Birth Date
185         }
186
187      if (medprop->GetStudyID())
188         {
189         str.str("");
190         str << medprop->GetStudyID();
191         file->InsertEntryString(str.str(),0x0020,0x0010,"SH"); // SH 1 Study ID
192         }
193      }
194 }
195 #endif
196
197 /**
198  * Set the data informations in the file
199  */
200 void SetImageInformation(GDCM_NAME_SPACE::FileHelper *file, vtkImageData *image)
201 {
202    std::ostringstream str;
203
204    // Image size
205    int *extent = image->GetUpdateExtent();
206    int dim[3] = {extent[1]-extent[0]+1,
207                  extent[3]-extent[2]+1,
208                  extent[5]-extent[4]+1};
209
210    str.str("");
211    str << dim[0];
212    file->InsertEntryString(str.str(),0x0028,0x0011,"US"); // Columns
213
214    str.str("");
215    str << dim[1];
216    file->InsertEntryString(str.str(),0x0028,0x0010,"US"); // Rows
217
218    if(dim[2]>1)
219    {
220       str.str("");
221       str << dim[2];
222       //file->Insert(str.str(),0x0028,0x0012); // Planes
223       file->InsertEntryString(str.str(),0x0028,0x0008,"IS"); // Number of Frames
224    }
225
226    // Pixel type
227    str.str("");
228    str << image->GetScalarSize()*8;
229    file->InsertEntryString(str.str(),0x0028,0x0100,"US"); // Bits Allocated
230    file->InsertEntryString(str.str(),0x0028,0x0101,"US"); // Bits Stored
231
232    str.str("");
233    str << image->GetScalarSize()*8-1;
234    file->InsertEntryString(str.str(),0x0028,0x0102,"US"); // High Bit
235
236    // Pixel Repr
237    // FIXME : what do we do when the ScalarType is 
238    // VTK_UNSIGNED_INT or VTK_UNSIGNED_LONG
239    str.str("");
240    if( image->GetScalarType() == VTK_UNSIGNED_CHAR  ||
241        image->GetScalarType() == VTK_UNSIGNED_SHORT ||
242        image->GetScalarType() == VTK_UNSIGNED_INT   ||
243        image->GetScalarType() == VTK_UNSIGNED_LONG )
244    {
245       str << "0"; // Unsigned
246    }
247    else
248    {
249       str << "1"; // Signed
250    }
251    file->InsertEntryString(str.str(),0x0028,0x0103,"US"); // Pixel Representation
252
253    // Samples per pixel
254    str.str("");
255    str << image->GetNumberOfScalarComponents();
256    file->InsertEntryString(str.str(),0x0028,0x0002,"US"); // Samples per Pixel
257
258    /// \todo : Spacing Between Slices is meaningfull ONLY for CT an MR modality
259    ///       We should perform some checkings before forcing the Entry creation
260
261    // Spacing
262    vtkFloatingPointType *sp = image->GetSpacing();
263
264    str.str("");
265    // We are about to enter floating point value. 
266    // By default ostringstream are smart and don't do fixed point
267    // thus forcing to fixed point value
268    str.setf( std::ios::fixed );
269    str << sp[1] << "\\" << sp[0];
270    file->InsertEntryString(str.str(),0x0028,0x0030,"DS"); // Pixel Spacing
271    str.str("");
272    str << sp[2];
273    file->InsertEntryString(str.str(),0x0018,0x0088,"DS"); // Spacing Between Slices
274
275    // Origin
276    vtkFloatingPointType *org = image->GetOrigin();
277
278    /// \todo : Image Position Patient is meaningfull ONLY for CT an MR modality
279    ///       We should perform some checkings before forcing the Entry creation
280
281    str.str("");
282    str << org[0] << "\\" << org[1] << "\\" << org[2];
283    file->InsertEntryString(str.str(),0x0020,0x0032,"DS"); // Image Position Patient
284    str.unsetf( std::ios::fixed ); //done with floating point values
285
286    // Window / Level
287    vtkFloatingPointType *rng = image->GetScalarRange();
288
289    str.str("");
290    str << rng[1]-rng[0];
291    file->InsertEntryString(str.str(),0x0028,0x1051,"DS"); // Window Width
292    str.str("");
293    str << (rng[1]+rng[0])/2.0;
294    file->InsertEntryString(str.str(),0x0028,0x1050,"DS"); // Window Center
295
296    // Pixels
297    unsigned char *data;
298    size_t size = ReverseData(image,&data);
299    file->SetUserData(data,size);
300 }
301
302 /**
303  * Write of the files
304  * The call to this method is recursive if there is some files to write
305  */ 
306 void vtkGdcmWriter::RecursiveWrite(int axis, vtkImageData *image, 
307                     ofstream *file)
308 {
309    if(file)
310    {
311       vtkErrorMacro( <<  "File must not be open");
312       return;
313    }
314
315    if( image->GetScalarType() == VTK_FLOAT || 
316        image->GetScalarType() == VTK_DOUBLE )
317    {
318       vtkErrorMacro(<< "Bad input type. Scalar type must not be of type "
319                     << "VTK_FLOAT or VTK_DOUBLE (found:"
320                     << image->GetScalarTypeAsString() << ")" );
321       return;
322    }
323
324    RecursiveWrite(axis,image, image, file);
325    //WriteDcmFile(this->FileName,image);
326 }
327
328 void vtkGdcmWriter::RecursiveWrite(int axis, vtkImageData *cache, 
329                                    vtkImageData *image, ofstream *file)
330 {
331    int idx, min, max;
332
333    // if the file is already open then just write to it
334    if( file )
335    {
336       vtkErrorMacro( <<  "File musn't be open");
337       return;
338    }
339
340    // if we need to open another slice, do it
341    if( (axis + 1) == this->FileDimensionality )
342    {
343       // determine the name
344       if (this->FileName)
345       {
346          sprintf(this->InternalFileName, "%s", this->FileName);
347       }
348       else 
349       {
350          if (this->FilePrefix)
351          {
352             sprintf(this->InternalFileName, this->FilePattern, 
353             this->FilePrefix, this->FileNumber);
354          }
355          else
356          {
357             sprintf(this->InternalFileName, this->FilePattern,this->FileNumber);
358          }
359 // Remove this code in case user is using VTK 4.2...
360 #if !(VTK_MAJOR_VERSION == 4 && VTK_MINOR_VERSION == 2)
361          if (this->FileNumber < this->MinimumFileNumber)
362          {
363             this->MinimumFileNumber = this->FileNumber;
364          }
365          else if (this->FileNumber > this->MaximumFileNumber)
366          {
367             this->MaximumFileNumber = this->FileNumber;
368          }
369 #endif
370       }
371
372       // Write the file
373       WriteDcmFile(this->InternalFileName,image);
374       ++this->FileNumber;
375       return;
376    }
377
378    // if the current region is too high a dimension for the file
379    // the we will split the current axis
380    cache->GetAxisUpdateExtent(axis, min, max);
381
382    // if it is the y axis then flip by default
383    if (axis == 1 && !this->FileLowerLeft)
384    {
385       for(idx = max; idx >= min; idx--)
386       {
387          cache->SetAxisUpdateExtent(axis, idx, idx);
388          this->RecursiveWrite(axis - 1, cache, image, file);
389       }
390    }
391    else
392    {
393       for(idx = min; idx <= max; idx++)
394       {
395          cache->SetAxisUpdateExtent(axis, idx, idx);
396          this->RecursiveWrite(axis - 1, cache, image, file);
397       }
398    }
399
400    // restore original extent
401    cache->SetAxisUpdateExtent(axis, min, max);
402 }
403
404 void vtkGdcmWriter::WriteDcmFile(char *fileName, vtkImageData *image)
405 {
406    GDCM_NAME_SPACE::FileHelper *dcmFile;
407    if ( GdcmFile != 0)
408       dcmFile = GDCM_NAME_SPACE::FileHelper::New(GdcmFile);
409    else
410       dcmFile = GDCM_NAME_SPACE::FileHelper::New();
411    
412    // From here, the write of the file begins
413
414    // Set the medical informations:
415 #if (VTK_MAJOR_VERSION >= 5)  
416    SetMedicalImageInformation(dcmFile, this->MedicalImageProperties);
417 #endif
418       
419    // Set the image informations
420    SetImageInformation(dcmFile, image);
421
422    // Write the image
423    switch(this->WriteType)
424    {
425       case VTK_GDCM_WRITE_TYPE_EXPLICIT_VR :
426          dcmFile->SetWriteTypeToDcmExplVR();
427          break;
428       case VTK_GDCM_WRITE_TYPE_IMPLICIT_VR :
429          dcmFile->SetWriteTypeToDcmImplVR();
430          break;
431       case VTK_GDCM_WRITE_TYPE_ACR :
432          dcmFile->SetWriteTypeToAcr();
433          break;
434       case VTK_GDCM_WRITE_TYPE_ACR_LIBIDO :
435          dcmFile->SetWriteTypeToAcrLibido();
436          break;
437       default :
438          dcmFile->SetWriteTypeToDcmExplVR();
439    }
440   
441    dcmFile->SetContentType((GDCM_NAME_SPACE::ImageContentType)ContentType);
442  
443    if(!dcmFile->Write(fileName))
444    {
445       vtkErrorMacro( << "File "  <<  this->FileName  <<  "cannot be written by "
446                      << " the gdcm library");
447    }
448    // Clean up
449    if( dcmFile->GetUserData() && dcmFile->GetUserDataSize()>0 )
450    {
451       delete[] dcmFile->GetUserData();
452    }
453    dcmFile->Delete();
454 }
455
456 //-----------------------------------------------------------------------------
457 // Private
458
459 //-----------------------------------------------------------------------------