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