]> Creatis software - gdcm.git/blob - vtk/vtkGdcmWriter.cxx
6640ea5b774d1a309cc711d3b3ff5d32b87e2540
[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.2 $");
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 void SetImageInformation(gdcm::File *file,vtkImageData *image)
88 {
89    std::ostringstream str;
90
91    // Image size
92    int *dim = image->GetDimensions();
93
94    str.str("");
95    str<<dim[0];
96    file->ReplaceOrCreateByNumber(str.str(),0x0028,0x0011);
97
98    str.str("");
99    str<<dim[1];
100    file->ReplaceOrCreateByNumber(str.str(),0x0028,0x0010);
101
102    if(dim[2]>1)
103    {
104       str.str("");
105       str<<dim[2];
106       file->ReplaceOrCreateByNumber(str.str(),0x0028,0x0012);
107    }
108
109    // Pixel type
110    str.str("");
111    str<<image->GetScalarSize()*8;
112    file->ReplaceOrCreateByNumber(str.str(),0x0028,0x0100);
113    file->ReplaceOrCreateByNumber(str.str(),0x0028,0x0101);
114
115    str.str("");
116    str<<image->GetScalarSize()*8-1;
117    file->ReplaceOrCreateByNumber(str.str(),0x0028,0x0102);
118
119    // Pixel Repr
120    // FIXME : what do we do when the ScalarType is 
121    // VTK_UNSIGNED_INT or VTK_UNSIGNED_LONG
122    str.str("");
123    if( image->GetScalarType() == VTK_UNSIGNED_CHAR ||
124        image->GetScalarType() == VTK_UNSIGNED_SHORT ||
125        image->GetScalarType() == VTK_UNSIGNED_INT ||
126        image->GetScalarType() == VTK_UNSIGNED_LONG )
127    {
128       str<<"0"; // Unsigned
129    }
130    else
131    {
132       str<<"1"; // Signed
133    }
134    file->ReplaceOrCreateByNumber(str.str(),0x0028,0x0103);
135
136    // Samples per pixel
137    str.str("");
138    str<<image->GetNumberOfScalarComponents();
139    file->ReplaceOrCreateByNumber(str.str(),0x0028,0x0002);
140
141    // Spacing
142    double *sp = image->GetSpacing();
143
144    str.str("");
145    str<<sp[0]<<"\\"<<sp[1];
146    file->ReplaceOrCreateByNumber(str.str(),0x0028,0x0030);
147    str.str("");
148    str<<sp[2];
149    file->ReplaceOrCreateByNumber(str.str(),0x0018,0x0088);
150
151    // Origin
152    double *org = image->GetOrigin();
153
154    str.str("");
155    str<<org[0]<<"\\"<<org[1]<<"\\"<<org[2];
156    file->ReplaceOrCreateByNumber(str.str(),0x0020,0x0032);
157
158    // Window / Level
159    double *rng=image->GetScalarRange();
160
161    str.str("");
162    str<<rng[1]-rng[0];
163    file->ReplaceOrCreateByNumber(str.str(),0x0028,0x1051);
164    str.str("");
165    str<<(rng[1]+rng[0])/2.0;
166    file->ReplaceOrCreateByNumber(str.str(),0x0028,0x1050);
167
168    // Pixels
169    size_t size = dim[0] * dim[1] * dim[2] 
170                * image->GetScalarSize()
171                * image->GetNumberOfScalarComponents();
172    file->SetImageData((unsigned char *)image->GetScalarPointer(),size);
173 }
174
175 void vtkGdcmWriter::RecursiveWrite(int dim, vtkImageData *region, ofstream *file)
176 {
177    if(file)
178    {
179       vtkErrorMacro(<< "File musn't be opened");
180       return;
181    }
182
183    if( region->GetScalarType() == VTK_FLOAT 
184      || region->GetScalarType() == VTK_DOUBLE )
185    {
186       vtkErrorMacro(<< "Bad input type. Scalar type musn't be of type "
187                     << "VTK_FLOAT or VTKDOUBLE (found:"
188                     << region->GetScalarTypeAsString());
189       return;
190    }
191
192    gdcm::File *dcmFile = new gdcm::File();
193
194    ///////////////////////////////////////////////////////////////////////////
195    // Set the image informations
196    SetImageInformation(dcmFile,region);
197
198    ///////////////////////////////////////////////////////////////////////////
199    // Write the image
200    if(!dcmFile->Write(this->FileName))
201    {
202       vtkErrorMacro(<< "File " << this->FileName << "couldn't be written by "
203                     << " the gdcm library");
204       std::cerr<<"not written \n";
205    }
206
207    delete dcmFile;
208 }
209
210 //-----------------------------------------------------------------------------
211 // Private
212
213 //-----------------------------------------------------------------------------