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