]> Creatis software - gdcm.git/blob - vtk/vtkGdcmWriter.cxx
* vtk/vtkGdcmReader.[h|cxx] : bug fix. Plane order isn't inverted
[gdcm.git] / vtk / vtkGdcmWriter.cxx
1 // vtkGdcmWriter.cxx
2 //-----------------------------------------------------------------------------
3 // //////////////////////////////////////////////////////////////
4 // WARNING TODO CLEANME 
5 // Actual limitations of this code:
6 //
7 // /////// Redundant and unnecessary header parsing
8 // In it's current state this code actually parses three times the Dicom
9 // header of a file before the corresponding image gets loaded in the
10 // ad-hoc vtkData !
11 // Here is the process:
12 //  1/ First loading happens in ExecuteInformation which in order to
13 //     positionate the vtk extents calls CheckFileCoherence. The purpose
14 //     of CheckFileCoherence is to make sure all the images in the future
15 //     stack are "homogenous" (same size, same representation...). This
16 //     can only be achieved by parsing all the Dicom headers...
17 //  2/ ExecuteData is then responsible for the next two loadings:
18 //  2a/ ExecuteData calls AllocateOutputData that in turn seems to 
19 //      (indirectely call) ExecuteInformation which ends up in a second
20 //      header parsing
21 //      This is fixed by adding a test at the beginning of ExecuteInformation
22 //      on the modification of the object instance. If a modification have been
23 //      made (method Modified() ), the MTime value is increased. The fileTime
24 //      is compared to this new value to find a modification in the class
25 //      parameters
26 //  2b/ the core of ExecuteData then needs gdcmFile (which in turns
27 //      initialises gdcmHeader in the constructor) in order to access
28 //      the data-image.
29 //
30 // Possible solution:
31 // maintain a list of gdcmFiles (created by say ExecuteInformation) created
32 // once and for all accross the life of vtkGdcmHeader (it would only load
33 // new gdcmFile if the user changes the list). ExecuteData would then use 
34 // those gdcmFile and hence avoid calling the construtor:
35 //  - advantage: the header of the files would only be parser once.
36 //  - drawback: once execute information is called (i.e. on creation of
37 //              a vtkGdcmHeader) the gdcmFile structure is loaded in memory.
38 //              The average size of a gdcmHeader being of 100Ko, is one
39 //              loads 10 stacks of images with say 200 images each, you
40 //              end-up with a loss of 200Mo...
41 //
42 // /////// Never unallocated memory:
43 // ExecuteData allocates space for the pixel data [which will get pointed
44 // by the vtkPointData() through the call
45 // data->GetPointData()->GetScalars()->SetVoidArray(mem, StackNumPixels, 0);]
46 // This data is never "freed" neither in the destructor nor when the
47 // filename list is extended, ExecuteData is called a second (or third)
48 // time...
49 // //////////////////////////////////////////////////////////////
50
51 #include "gdcmHeader.h"
52 #include "gdcmFile.h"
53 #include "gdcmDebug.h"
54 #include "vtkGdcmWriter.h"
55
56 #include <vtkObjectFactory.h>
57 #include <vtkImageData.h>
58 #include <vtkPointData.h>
59 #include <vtkLookupTable.h>
60
61 vtkCxxRevisionMacro(vtkGdcmWriter, "$Revision: 1.3 $");
62 vtkStandardNewMacro(vtkGdcmWriter);
63
64 //-----------------------------------------------------------------------------
65 // Constructor / Destructor
66 vtkGdcmWriter::vtkGdcmWriter()
67 {
68    this->LookupTable = NULL;
69 }
70
71 vtkGdcmWriter::~vtkGdcmWriter()
72 {
73 }
74
75 //-----------------------------------------------------------------------------
76 // Print
77 void vtkGdcmWriter::PrintSelf(ostream& os, vtkIndent indent)
78 {
79    this->Superclass::PrintSelf(os,indent);
80 }
81
82 //-----------------------------------------------------------------------------
83 // Public
84
85 //-----------------------------------------------------------------------------
86 // Protected
87 /**
88  * Copy the image and reverse the Y axis
89  */
90 // The output datas must be deleted by the user of the method !!!
91 size_t ReverseData(vtkImageData *img,unsigned char **data)
92 {
93    int *dim = img->GetDimensions();
94    size_t lineSize = dim[0] * img->GetScalarSize()
95                    * img->GetNumberOfScalarComponents();
96    size_t planeSize = dim[1] * lineSize;
97    size_t size = dim[2] * planeSize;
98
99    *data = new unsigned char[size];
100
101    unsigned char *src = (unsigned char *)img->GetScalarPointer();
102    unsigned char *dst = *data + planeSize - lineSize;
103    for (int plane = 0; plane < dim[2]; plane++)
104    {
105       for (int line = 0; line < dim[1]; line++)
106       {
107          // Copy one line at proper destination:
108          memcpy((void*)dst, (void*)src, lineSize);
109
110          src += lineSize;
111          dst -= lineSize;
112       }
113       dst += 2 * planeSize;
114    }
115
116    return size;
117 }
118
119 /**
120  * Set the datas informations in the file
121  */
122 void SetImageInformation(gdcm::File *file,vtkImageData *image)
123 {
124    std::ostringstream str;
125
126    // Image size
127    int *dim = image->GetDimensions();
128
129    str.str("");
130    str<<dim[0];
131    file->ReplaceOrCreateByNumber(str.str(),0x0028,0x0011);
132
133    str.str("");
134    str<<dim[1];
135    file->ReplaceOrCreateByNumber(str.str(),0x0028,0x0010);
136
137    if(dim[2]>1)
138    {
139       str.str("");
140       str<<dim[2];
141       file->ReplaceOrCreateByNumber(str.str(),0x0028,0x0012);
142    }
143
144    // Pixel type
145    str.str("");
146    str<<image->GetScalarSize()*8;
147    file->ReplaceOrCreateByNumber(str.str(),0x0028,0x0100);
148    file->ReplaceOrCreateByNumber(str.str(),0x0028,0x0101);
149
150    str.str("");
151    str<<image->GetScalarSize()*8-1;
152    file->ReplaceOrCreateByNumber(str.str(),0x0028,0x0102);
153
154    // Pixel Repr
155    // FIXME : what do we do when the ScalarType is 
156    // VTK_UNSIGNED_INT or VTK_UNSIGNED_LONG
157    str.str("");
158    if( image->GetScalarType() == VTK_UNSIGNED_CHAR ||
159        image->GetScalarType() == VTK_UNSIGNED_SHORT ||
160        image->GetScalarType() == VTK_UNSIGNED_INT ||
161        image->GetScalarType() == VTK_UNSIGNED_LONG )
162    {
163       str<<"0"; // Unsigned
164    }
165    else
166    {
167       str<<"1"; // Signed
168    }
169    file->ReplaceOrCreateByNumber(str.str(),0x0028,0x0103);
170
171    // Samples per pixel
172    str.str("");
173    str<<image->GetNumberOfScalarComponents();
174    file->ReplaceOrCreateByNumber(str.str(),0x0028,0x0002);
175
176    // Spacing
177    double *sp = image->GetSpacing();
178
179    str.str("");
180    str<<sp[0]<<"\\"<<sp[1];
181    file->ReplaceOrCreateByNumber(str.str(),0x0028,0x0030);
182    str.str("");
183    str<<sp[2];
184    file->ReplaceOrCreateByNumber(str.str(),0x0018,0x0088);
185
186    // Origin
187    double *org = image->GetOrigin();
188
189    str.str("");
190    str<<org[0]<<"\\"<<org[1]<<"\\"<<org[2];
191    file->ReplaceOrCreateByNumber(str.str(),0x0020,0x0032);
192
193    // Window / Level
194    double *rng=image->GetScalarRange();
195
196    str.str("");
197    str<<rng[1]-rng[0];
198    file->ReplaceOrCreateByNumber(str.str(),0x0028,0x1051);
199    str.str("");
200    str<<(rng[1]+rng[0])/2.0;
201    file->ReplaceOrCreateByNumber(str.str(),0x0028,0x1050);
202
203    // Pixels
204    unsigned char *data;
205    size_t size = ReverseData(image,&data);
206    file->SetImageData(data,size);
207 }
208
209 /**
210  * Write of the files
211  * The call to this method is recursive if there is some files to write
212  */ 
213 void vtkGdcmWriter::RecursiveWrite(int axis, vtkImageData *image, ofstream *file)
214 {
215    if(file)
216    {
217       vtkErrorMacro(<< "File musn't be opened");
218       return;
219    }
220
221    if( image->GetScalarType() == VTK_FLOAT || 
222        image->GetScalarType() == VTK_DOUBLE )
223    {
224       vtkErrorMacro(<< "Bad input type. Scalar type musn't be of type "
225                     << "VTK_FLOAT or VTKDOUBLE (found:"
226                     << image->GetScalarTypeAsString());
227       return;
228    }
229
230    WriteFile(this->FileName,image);
231 }
232
233 void vtkGdcmWriter::WriteFile(char *fileName,vtkImageData *image)
234 {
235    // From here, the write of the file begins
236    gdcm::File *dcmFile = new gdcm::File();
237
238    // Set the image informations
239    SetImageInformation(dcmFile,image);
240
241    // Write the image
242    if(!dcmFile->Write(FileName))
243    {
244       vtkErrorMacro(<< "File " << this->FileName << "couldn't be written by "
245                     << " the gdcm library");
246       std::cerr<<"not written \n";
247    }
248
249    delete dcmFile;
250 }
251
252 //-----------------------------------------------------------------------------
253 // Private
254
255 //-----------------------------------------------------------------------------