2 //-----------------------------------------------------------------------------
3 // //////////////////////////////////////////////////////////////
4 // WARNING TODO CLEANME
5 // Actual limitations of this code:
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
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
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
26 // 2b/ the core of ExecuteData then needs gdcmFile (which in turns
27 // initialises gdcmHeader in the constructor) in order to access
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...
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)
49 // //////////////////////////////////////////////////////////////
51 #include "gdcmHeader.h"
53 #include "gdcmDebug.h"
54 #include "vtkGdcmWriter.h"
56 #include <vtkObjectFactory.h>
57 #include <vtkImageData.h>
58 #include <vtkPointData.h>
59 #include <vtkLookupTable.h>
61 vtkCxxRevisionMacro(vtkGdcmWriter, "$Revision: 1.3 $");
62 vtkStandardNewMacro(vtkGdcmWriter);
64 //-----------------------------------------------------------------------------
65 // Constructor / Destructor
66 vtkGdcmWriter::vtkGdcmWriter()
68 this->LookupTable = NULL;
71 vtkGdcmWriter::~vtkGdcmWriter()
75 //-----------------------------------------------------------------------------
77 void vtkGdcmWriter::PrintSelf(ostream& os, vtkIndent indent)
79 this->Superclass::PrintSelf(os,indent);
82 //-----------------------------------------------------------------------------
85 //-----------------------------------------------------------------------------
88 * Copy the image and reverse the Y axis
90 // The output datas must be deleted by the user of the method !!!
91 size_t ReverseData(vtkImageData *img,unsigned char **data)
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;
99 *data = new unsigned char[size];
101 unsigned char *src = (unsigned char *)img->GetScalarPointer();
102 unsigned char *dst = *data + planeSize - lineSize;
103 for (int plane = 0; plane < dim[2]; plane++)
105 for (int line = 0; line < dim[1]; line++)
107 // Copy one line at proper destination:
108 memcpy((void*)dst, (void*)src, lineSize);
113 dst += 2 * planeSize;
120 * Set the datas informations in the file
122 void SetImageInformation(gdcm::File *file,vtkImageData *image)
124 std::ostringstream str;
127 int *dim = image->GetDimensions();
131 file->ReplaceOrCreateByNumber(str.str(),0x0028,0x0011);
135 file->ReplaceOrCreateByNumber(str.str(),0x0028,0x0010);
141 file->ReplaceOrCreateByNumber(str.str(),0x0028,0x0012);
146 str<<image->GetScalarSize()*8;
147 file->ReplaceOrCreateByNumber(str.str(),0x0028,0x0100);
148 file->ReplaceOrCreateByNumber(str.str(),0x0028,0x0101);
151 str<<image->GetScalarSize()*8-1;
152 file->ReplaceOrCreateByNumber(str.str(),0x0028,0x0102);
155 // FIXME : what do we do when the ScalarType is
156 // VTK_UNSIGNED_INT or VTK_UNSIGNED_LONG
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 )
163 str<<"0"; // Unsigned
169 file->ReplaceOrCreateByNumber(str.str(),0x0028,0x0103);
173 str<<image->GetNumberOfScalarComponents();
174 file->ReplaceOrCreateByNumber(str.str(),0x0028,0x0002);
177 double *sp = image->GetSpacing();
180 str<<sp[0]<<"\\"<<sp[1];
181 file->ReplaceOrCreateByNumber(str.str(),0x0028,0x0030);
184 file->ReplaceOrCreateByNumber(str.str(),0x0018,0x0088);
187 double *org = image->GetOrigin();
190 str<<org[0]<<"\\"<<org[1]<<"\\"<<org[2];
191 file->ReplaceOrCreateByNumber(str.str(),0x0020,0x0032);
194 double *rng=image->GetScalarRange();
198 file->ReplaceOrCreateByNumber(str.str(),0x0028,0x1051);
200 str<<(rng[1]+rng[0])/2.0;
201 file->ReplaceOrCreateByNumber(str.str(),0x0028,0x1050);
205 size_t size = ReverseData(image,&data);
206 file->SetImageData(data,size);
211 * The call to this method is recursive if there is some files to write
213 void vtkGdcmWriter::RecursiveWrite(int axis, vtkImageData *image, ofstream *file)
217 vtkErrorMacro(<< "File musn't be opened");
221 if( image->GetScalarType() == VTK_FLOAT ||
222 image->GetScalarType() == VTK_DOUBLE )
224 vtkErrorMacro(<< "Bad input type. Scalar type musn't be of type "
225 << "VTK_FLOAT or VTKDOUBLE (found:"
226 << image->GetScalarTypeAsString());
230 WriteFile(this->FileName,image);
233 void vtkGdcmWriter::WriteFile(char *fileName,vtkImageData *image)
235 // From here, the write of the file begins
236 gdcm::File *dcmFile = new gdcm::File();
238 // Set the image informations
239 SetImageInformation(dcmFile,image);
242 if(!dcmFile->Write(FileName))
244 vtkErrorMacro(<< "File " << this->FileName << "couldn't be written by "
245 << " the gdcm library");
246 std::cerr<<"not written \n";
252 //-----------------------------------------------------------------------------
255 //-----------------------------------------------------------------------------