]> Creatis software - gdcm.git/blob - vtk/vtkGdcmWriter.cxx
ENH: I believe we have to enter floating point values with fixed number instead of...
[gdcm.git] / vtk / vtkGdcmWriter.cxx
1 /*=========================================================================
2                                                                                 
3   Program:   gdcm
4   Module:    $RCSfile: vtkGdcmWriter.cxx,v $
5   Language:  C++
6   Date:      $Date: 2005/02/25 19:54:06 $
7   Version:   $Revision: 1.17 $
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 vtkCxxRevisionMacro(vtkGdcmWriter, "$Revision: 1.17 $");
31 vtkStandardNewMacro(vtkGdcmWriter);
32
33 //-----------------------------------------------------------------------------
34 // Constructor / Destructor
35 vtkGdcmWriter::vtkGdcmWriter()
36 {
37    this->LookupTable = NULL;
38    this->FileDimensionality = 3;
39    this->WriteType = VTK_GDCM_WRITE_TYPE_EXPLICIT_VR;
40
41    UIDPrefix = "";
42    StudyInstanceUID = "";
43    SeriesInstanceUID = "";
44    FrameOfReferenceInstanceUID = "";
45 }
46
47 vtkGdcmWriter::~vtkGdcmWriter()
48 {
49 }
50
51 //-----------------------------------------------------------------------------
52 // Print
53 void vtkGdcmWriter::PrintSelf(ostream& os, vtkIndent indent)
54 {
55    this->Superclass::PrintSelf(os,indent);
56
57    os << indent << "Write type : " << this->GetWriteTypeAsString();
58 }
59
60 //-----------------------------------------------------------------------------
61 // Public
62 const char *vtkGdcmWriter::GetWriteTypeAsString()
63 {
64    switch(WriteType)
65    {
66       case VTK_GDCM_WRITE_TYPE_EXPLICIT_VR :
67          return "Explicit VR";
68       case VTK_GDCM_WRITE_TYPE_IMPLICIT_VR :
69          return "Implicit VR";
70       case VTK_GDCM_WRITE_TYPE_ACR :
71          return "ACR";
72       case VTK_GDCM_WRITE_TYPE_ACR_LIBIDO :
73          return "ACR Libido";
74       default :
75          return "Unknow type";
76    }
77 }
78
79 void vtkGdcmWriter::SetUIDPrefix(const char *prefix)
80 {
81    UIDPrefix = prefix;
82 }
83
84 const char *vtkGdcmWriter::GetUIDPrefix()
85 {
86    return UIDPrefix.c_str();
87 }
88
89 void vtkGdcmWriter::NewStudyInstanceUID()
90 {
91    StudyInstanceUID = "";
92 }
93
94 void vtkGdcmWriter::NewSeriesInstanceUID()
95 {
96    SeriesInstanceUID = "";
97 }
98
99 void vtkGdcmWriter::NewFrameOfReferenceInstanceUID()
100 {
101    FrameOfReferenceInstanceUID = "";
102 }
103
104 //-----------------------------------------------------------------------------
105 // Protected
106 /**
107  * Copy the image and reverse the Y axis
108  */
109 // The output datas must be deleted by the user of the method !!!
110 size_t ReverseData(vtkImageData *image,unsigned char **data)
111 {
112    int inc[3];
113    int *extent = image->GetUpdateExtent();
114    int dim[3] = {extent[1]-extent[0]+1,
115                  extent[3]-extent[2]+1,
116                  extent[5]-extent[4]+1};
117
118    size_t lineSize = dim[0] * image->GetScalarSize()
119                    * image->GetNumberOfScalarComponents();
120    size_t planeSize = dim[1] * lineSize;
121    size_t size = dim[2] * planeSize;
122
123    if( size>0 )
124    {
125       *data = new unsigned char[size];
126
127       image->GetIncrements(inc);
128       unsigned char *src = (unsigned char *)image->GetScalarPointerForExtent(extent);
129       unsigned char *dst = *data + planeSize - lineSize;
130       for (int plane = extent[4]; plane <= extent[5]; plane++)
131       {
132          for (int line = extent[2]; line <= extent[3]; line++)
133          {
134             // Copy one line at proper destination:
135             memcpy((void*)dst, (void*)src, lineSize);
136
137             src += inc[1] * image->GetScalarSize();
138             dst -= lineSize;
139          }
140          dst += 2 * planeSize;
141       }
142    }
143    else
144    {
145       *data = NULL;
146    }
147
148    return size;
149 }
150
151 /**
152  * Set the datas informations in the file
153  */
154 void SetImageInformation(gdcm::FileHelper *file,vtkImageData *image)
155 {
156    std::ostringstream str;
157
158    // Image size
159    int *extent = image->GetUpdateExtent();
160    int dim[3] = {extent[1]-extent[0]+1,
161                  extent[3]-extent[2]+1,
162                  extent[5]-extent[4]+1};
163
164    str.str("");
165    str << dim[0];
166    file->InsertValEntry(str.str(),0x0028,0x0011); // Columns
167
168    str.str("");
169    str << dim[1];
170    file->InsertValEntry(str.str(),0x0028,0x0010); // Rows
171
172    if(dim[2]>1)
173    {
174       str.str("");
175       str << dim[2];
176       //file->Insert(str.str(),0x0028,0x0012); // Planes
177       file->InsertValEntry(str.str(),0x0028,0x0008); // Number of Frames
178    }
179
180    // Pixel type
181    str.str("");
182    str << image->GetScalarSize()*8;
183    file->InsertValEntry(str.str(),0x0028,0x0100); // Bits Allocated
184    file->InsertValEntry(str.str(),0x0028,0x0101); // Bits Stored
185
186    str.str("");
187    str << image->GetScalarSize()*8-1;
188    file->InsertValEntry(str.str(),0x0028,0x0102); // High Bit
189
190    // Pixel Repr
191    // FIXME : what do we do when the ScalarType is 
192    // VTK_UNSIGNED_INT or VTK_UNSIGNED_LONG
193    str.str("");
194    if( image->GetScalarType() == VTK_UNSIGNED_CHAR ||
195        image->GetScalarType() == VTK_UNSIGNED_SHORT ||
196        image->GetScalarType() == VTK_UNSIGNED_INT ||
197        image->GetScalarType() == VTK_UNSIGNED_LONG )
198    {
199       str << "0"; // Unsigned
200    }
201    else
202    {
203       str << "1"; // Signed
204    }
205    file->InsertValEntry(str.str(),0x0028,0x0103); // Pixel Representation
206
207    // Samples per pixel
208    str.str("");
209    str << image->GetNumberOfScalarComponents();
210    file->InsertValEntry(str.str(),0x0028,0x0002); // Samples per Pixel
211
212    // Spacing
213    double *sp = image->GetSpacing();
214
215    str.str("");
216    // We are about to enter floating point value. By default ostringstream are smart and don't do fixed point
217    // thus forcing to fixed point value
218    str.setf( std::ios::fixed );
219    str << sp[0] << "\\" << sp[1];
220    file->InsertValEntry(str.str(),0x0028,0x0030); // Pixel Spacing
221    str.str("");
222    str << sp[2];
223    file->InsertValEntry(str.str(),0x0018,0x0088); // Spacing Between Slices
224
225    // Origin
226    double *org = image->GetOrigin();
227
228    str.str("");
229    str << org[0] << "\\" << org[1] << "\\" << org[2];
230    file->InsertValEntry(str.str(),0x0020,0x0032); // Image Position Patient
231    str.unsetf( std::ios::fixed ); //done with floating point values
232
233    // Window / Level
234    double *rng=image->GetScalarRange();
235
236    str.str("");
237    str << rng[1]-rng[0];
238    file->InsertValEntry(str.str(),0x0028,0x1051); // Window Width
239    str.str("");
240    str << (rng[1]+rng[0])/2.0;
241    file->InsertValEntry(str.str(),0x0028,0x1050); // Window Center
242
243    // Pixels
244    unsigned char *data;
245    size_t size = ReverseData(image,&data);
246    file->SetUserData(data,size);
247 }
248
249 /**
250  * Write of the files
251  * The call to this method is recursive if there is some files to write
252  */ 
253 void vtkGdcmWriter::RecursiveWrite(int axis, vtkImageData *image, ofstream *file)
254 {
255    if(file)
256    {
257       vtkErrorMacro( <<  "File musn't be opened");
258       return;
259    }
260
261    if( image->GetScalarType() == VTK_FLOAT || 
262        image->GetScalarType() == VTK_DOUBLE )
263    {
264       vtkErrorMacro(<< "Bad input type. Scalar type musn't be of type "
265                     << "VTK_FLOAT or VTKDOUBLE (found:"
266                     << image->GetScalarTypeAsString());
267       return;
268    }
269
270    RecursiveWrite(axis,image,image,file);
271    //WriteDcmFile(this->FileName,image);
272 }
273
274 void vtkGdcmWriter::RecursiveWrite(int axis, vtkImageData *cache, 
275                                    vtkImageData *image, ofstream *file)
276 {
277    int idx, min, max;
278
279    // if the file is already open then just write to it
280    if( file )
281    {
282       vtkErrorMacro( <<  "File musn't be opened");
283       return;
284    }
285
286    // if we need to open another slice, do it
287    if( (axis + 1) == this->FileDimensionality )
288    {
289       // determine the name
290       if (this->FileName)
291       {
292          sprintf(this->InternalFileName,"%s",this->FileName);
293       }
294       else 
295       {
296          if (this->FilePrefix)
297          {
298             sprintf(this->InternalFileName, this->FilePattern, 
299             this->FilePrefix, this->FileNumber);
300          }
301          else
302          {
303             sprintf(this->InternalFileName, this->FilePattern,this->FileNumber);
304          }
305          if (this->FileNumber < this->MinimumFileNumber)
306          {
307             this->MinimumFileNumber = this->FileNumber;
308          }
309          else if (this->FileNumber > this->MaximumFileNumber)
310          {
311             this->MaximumFileNumber = this->FileNumber;
312          }
313       }
314
315       // Write the file
316       WriteDcmFile(this->InternalFileName,image);
317       ++this->FileNumber;
318       return;
319    }
320
321    // if the current region is too high a dimension forthe file
322    // the we will split the current axis
323    cache->GetAxisUpdateExtent(axis, min, max);
324
325    // if it is the y axis then flip by default
326    if (axis == 1 && !this->FileLowerLeft)
327    {
328       for(idx = max; idx >= min; idx--)
329       {
330          cache->SetAxisUpdateExtent(axis, idx, idx);
331          this->RecursiveWrite(axis - 1, cache, image, file);
332       }
333    }
334    else
335    {
336       for(idx = min; idx <= max; idx++)
337       {
338          cache->SetAxisUpdateExtent(axis, idx, idx);
339          this->RecursiveWrite(axis - 1, cache, image, file);
340       }
341    }
342
343    // restore original extent
344    cache->SetAxisUpdateExtent(axis, min, max);
345 }
346
347 void vtkGdcmWriter::WriteDcmFile(char *fileName,vtkImageData *image)
348 {
349    // From here, the write of the file begins
350    gdcm::FileHelper *dcmFile = new gdcm::FileHelper();
351
352    // Set the image UID
353    if( StudyInstanceUID.empty() )
354       StudyInstanceUID = gdcm::Util::CreateUniqueUID( UIDPrefix );
355    if( SeriesInstanceUID.empty() )
356       SeriesInstanceUID = gdcm::Util::CreateUniqueUID( UIDPrefix );
357    if( FrameOfReferenceInstanceUID.empty() )
358       FrameOfReferenceInstanceUID = gdcm::Util::CreateUniqueUID( UIDPrefix );
359    std::string uid = gdcm::Util::CreateUniqueUID( UIDPrefix );
360
361    dcmFile->InsertValEntry(uid,0x0008,0x0018); //[SOP Instance UID]
362    dcmFile->InsertValEntry(uid,0x0002,0x0003); //[Media Stored SOP Instance UID]
363    dcmFile->InsertValEntry(StudyInstanceUID,0x0020,0x000d); //[Study Instance UID]
364    dcmFile->InsertValEntry(SeriesInstanceUID,0x0020,0x000e); //[Series Instance UID]
365    dcmFile->InsertValEntry(FrameOfReferenceInstanceUID,0x0020, 0x0052); //[Frame of Reference UID] 
366
367    // Set the image informations
368    SetImageInformation(dcmFile,image);
369
370    // Write the image
371    switch(this->WriteType)
372    {
373       case VTK_GDCM_WRITE_TYPE_EXPLICIT_VR :
374          dcmFile->SetWriteTypeToDcmExplVR();
375          break;
376       case VTK_GDCM_WRITE_TYPE_IMPLICIT_VR :
377          dcmFile->SetWriteTypeToDcmImplVR();
378          break;
379       case VTK_GDCM_WRITE_TYPE_ACR :
380          dcmFile->SetWriteTypeToAcr();
381          break;
382       case VTK_GDCM_WRITE_TYPE_ACR_LIBIDO :
383          dcmFile->SetWriteTypeToAcrLibido();
384          break;
385       default :
386          dcmFile->SetWriteTypeToDcmExplVR();
387    }
388
389    if(!dcmFile->Write(fileName))
390    {
391       vtkErrorMacro( << "File "  <<  this->FileName  <<  "couldn't be written by "
392                      << " the gdcm library");
393    }
394
395    // Clean up
396    if( dcmFile->GetUserData() && dcmFile->GetUserDataSize()>0 )
397    {
398       delete[] dcmFile->GetUserData();
399    }
400    delete dcmFile;
401 }
402
403 //-----------------------------------------------------------------------------
404 // Private
405
406 //-----------------------------------------------------------------------------