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