+2004-12-07 Benoit Regrain <Benoit.Regrain@creatis.insa-lyon.fr>
+ * vtk/vtkGdcmWriter.[h|cxx] : add a first version of vtkGdcmWriter
+ * src/gdcmValEntry.cxx : bug fix when setting the value. Problems of odd
+ length
+ * src/gdcmHeader.cxx : Remove a useless call to Util::DicomString
+ * Add vtkGdcmWriter example and test
+
2004-12-07 Benoit Regrain <Benoit.Regrain@creatis.insa-lyon.fr>
* Test/TestUtil.cxx : reformat the source code
* vtk/vtkGdcmReader.cxx : remove and change prints
)
SET(TEST_SOURCES ${TEST_SOURCES}
ShowDicom.cxx
+ TestWriteWithVTK.cxx
)
ENDIF(GDCM_VTK)
ENDIF (GDCM_DATA_ROOT)
Program: gdcm
Module: $RCSfile: gdcmDicomDir.cxx,v $
Language: C++
- Date: $Date: 2004/12/07 13:39:33 $
- Version: $Revision: 1.88 $
+ Date: $Date: 2004/12/07 17:28:50 $
+ Version: $Revision: 1.89 $
Copyright (c) CREATIS (Centre de Recherche et d'Applications en Traitement de
l'Image). All rights reserved. See Doc/License.txt or
break;
}
-std::cerr<<"File : "<<it->c_str()<<std::endl;
header = new Header( it->c_str() );
if( !header )
{
Program: gdcm
Module: $RCSfile: gdcmFile.cxx,v $
Language: C++
- Date: $Date: 2004/12/07 13:39:33 $
- Version: $Revision: 1.173 $
+ Date: $Date: 2004/12/07 17:28:50 $
+ Version: $Revision: 1.174 $
Copyright (c) CREATIS (Centre de Recherche et d'Applications en Traitement de
l'Image). All rights reserved. See Doc/License.txt or
//-------------------------------------------------------------------------
// Constructor / Destructor
+/**
+ * \brief Constructor dedicated to deal with the *pixels* area of a ACR/DICOMV3
+ * file (Header only deals with the ... header)
+ * Opens (in read only and when possible) an existing file and checks
+ * for DICOM compliance. Returns NULL on failure.
+ * It will be up to the user to load the pixels into memory
+ * (see GetImageData, GetImageDataRaw)
+ * \note the in-memory representation of all available tags found in
+ * the DICOM header is post-poned to first header information access.
+ * This avoid a double parsing of public part of the header when
+ * one sets an a posteriori shadow dictionary (efficiency can be
+ * seen as a side effect).
+ */
+File::File( )
+{
+ HeaderInternal = new Header( );
+ SelfHeader = true;
+ Initialise();
+}
+
/**
* \brief Constructor dedicated to deal with the *pixels* area of a ACR/DICOMV3
* file (Header only deals with the ... header)
void File::Initialise()
{
WriteMode = WMODE_DECOMPRESSED;
- WriteType = ImplicitVR;
+ WriteType = ExplicitVR;
PixelReadConverter = new PixelReadConvert;
PixelWriteConverter = new PixelWriteConvert;
bool File::SetEntryByNumber(std::string const& content,
uint16_t group, uint16_t element)
{
- HeaderInternal->SetEntryByNumber(content,group,element);
- return true;
+ return HeaderInternal->SetEntryByNumber(content,group,element);
+}
+
+bool File::SetEntryByNumber(uint8_t* content, int lgth,
+ uint16_t group, uint16_t element)
+{
+ return HeaderInternal->SetEntryByNumber(content,lgth,group,element);
+}
+
+bool File::ReplaceOrCreateByNumber(std::string const& content,
+ uint16_t group, uint16_t element)
+{
+ return HeaderInternal->ReplaceOrCreateByNumber(content,group,element) != NULL;
}
/**
Program: gdcm
Module: $RCSfile: gdcmFile.h,v $
Language: C++
- Date: $Date: 2004/12/04 09:41:02 $
- Version: $Revision: 1.84 $
+ Date: $Date: 2004/12/07 17:28:50 $
+ Version: $Revision: 1.85 $
Copyright (c) CREATIS (Centre de Recherche et d'Applications en Traitement de
l'Image). All rights reserved. See Doc/License.txt or
};
public:
+ File( );
File( Header* header );
File( std::string const& filename );
bool Write(std::string const& fileName);
bool SetEntryByNumber(std::string const& content,
+ uint16_t group, uint16_t element);
+ bool SetEntryByNumber(uint8_t* content, int lgth,
+ uint16_t group, uint16_t element);
+ bool ReplaceOrCreateByNumber(std::string const& content,
uint16_t group, uint16_t element);
+ bool ReplaceOrCreateByNumber(uint8_t* binArea, int lgth,
+ uint16_t group, uint16_t elem);
+
uint8_t* GetLutRGBA();
// Write mode
Program: gdcm
Module: $RCSfile: gdcmHeader.cxx,v $
Language: C++
- Date: $Date: 2004/12/07 13:39:33 $
- Version: $Revision: 1.213 $
+ Date: $Date: 2004/12/07 17:28:50 $
+ Version: $Revision: 1.214 $
Copyright (c) CREATIS (Centre de Recherche et d'Applications en Traitement de
l'Image). All rights reserved. See Doc/License.txt or
std::string uid = Util::CreateUniqueUID();
static DICOM_DEFAULT_VALUE defaultvalue[] = {
- { "76", 0x0002, 0x0000}, // MetaElementGroup Length // FIXME: how to recompute ?
+ { "76 ", 0x0002, 0x0000}, // MetaElementGroup Length // FIXME: how to recompute ?
{ "1.2.840.10008.5.1.4.1.1.2", 0x0002, 0x0002}, // MediaStorageSOPInstanceUID (CT Image Storage)
{ uid.c_str(), 0x0002, 0x0012}, // META Implementation Class UID
{ "ISO_IR 100",0x0008, 0x0005}, // Specific Character Set
{ "", 0x0020, 0x0011}, // AcquisitionNumber
{ "1\\0\\0\\0\\1\\0", 0x0020, 0x0037}, // Image Orientation Patient
{ "1", 0x0028, 0x0002}, // Samples per pixel 1 or 3
- { "MONOCHROME2",0x0028, 0x0004}, // photochromatic interpretation
+ { "MONOCHROME1",0x0028, 0x0004}, // photochromatic interpretation
// Date and timeG
-
{ date.c_str(), 0x0008, 0x0012 } , // Instance Creation Date
{ time.c_str(), 0x0008, 0x0013 } , // Instance Creation Time
{ date.c_str(), 0x0008, 0x0020 } , // Study Date
{ "64", 0x0028, 0x0010 } , // nbRows
{ "64", 0x0028, 0x0011 } , // nbCols
{ "16", 0x0028, 0x0100 } , // BitsAllocated 8 or 16
- { "12", 0x0028, 0x0101 } , // BitsStored 8 or 12
- { "11", 0x0028, 0x0102 } , // HighBit 7 or 11
+ { "12", 0x0028, 0x0101 } , // BitsStored 8 or 12 or 16
+ { "11", 0x0028, 0x0102 } , // HighBit 7 or 11 or 15
{ "0", 0x0028, 0x0103 } , // Pixel Representation 0(unsigned) or 1(signed)
{ "1000.0", 0x0028, 0x1051 } , // Window Width
{ "500.0", 0x0028, 0x1050 } , // Window Center
DICOM_DEFAULT_VALUE current = defaultvalue[i];
while( current.value )
{
- std::string value = Util::DicomString( current.value ); //pad the string properly
- ReplaceOrCreateByNumber(value, current.group, current.elem);
+ ReplaceOrCreateByNumber(current.value, current.group, current.elem);
current = defaultvalue[++i];
}
}
Program: gdcm
Module: $RCSfile: gdcmValEntry.cxx,v $
Language: C++
- Date: $Date: 2004/12/07 13:39:33 $
- Version: $Revision: 1.39 $
+ Date: $Date: 2004/12/07 17:28:50 $
+ Version: $Revision: 1.40 $
Copyright (c) CREATIS (Centre de Recherche et d'Applications en Traitement de
l'Image). All rights reserved. See Doc/License.txt or
{
// for multivaluated items
l = (Util::CountSubstring(val, "\\") + 1) * 2;
+ SetValueOnly(val);
}
else if( vr == "UL" || vr == "SL" )
{
// for multivaluated items
l = (Util::CountSubstring(val, "\\") + 1) * 4;;
+ SetValueOnly(val);
+ }
+ else
+ {
+ std::string finalVal = Util::DicomString( val.c_str() );
+ assert( !(finalVal.size() % 2) );
+
+ l = finalVal.length();
+ SetValueOnly(finalVal);
}
- SetValueOnly(val);
}
else
{
SET(VTKGDCM_LIB_SRCS
vtkGdcmReader.cxx
+ vtkGdcmWriter.cxx
)
#-----------------------------------------------------------------------------
vtkRendering
)
+#-----------------------------------------------------------------------------
+SET(vtkWriteDicom_SOURCES
+ vtkWriteDicom.cxx
+)
+ADD_EXECUTABLE(vtkWriteDicom ${vtkWriteDicom_SOURCES})
+TARGET_LINK_LIBRARIES(vtkWriteDicom
+ vtkgdcm
+ vtkIO
+ vtkRendering
+)
+
#-----------------------------------------------------------------------------
SET(GdcmToBaseline_SOURCES
GdcmToBaseline.cxx
#include <vtkPointData.h>
#include <vtkLookupTable.h>
-vtkCxxRevisionMacro(vtkGdcmReader, "$Revision: 1.61 $");
+vtkCxxRevisionMacro(vtkGdcmReader, "$Revision: 1.62 $");
vtkStandardNewMacro(vtkGdcmReader);
//-----------------------------------------------------------------------------
this->DataSpacing[2] = GdcmHeader.GetZSpacing();
//Set image origin
- this->DataOrigin[0] = GdcmHeader.GetXOrigin();
- this->DataOrigin[1] = GdcmHeader.GetYOrigin();
- this->DataOrigin[2] = GdcmHeader.GetZOrigin();
+ //this->DataOrigin[0] = GdcmHeader.GetXOrigin();
+ //this->DataOrigin[1] = GdcmHeader.GetYOrigin();
+ //this->DataOrigin[2] = GdcmHeader.GetZOrigin();
}
} // End of loop on filename
--- /dev/null
+// vtkGdcmWriter.cxx
+//-----------------------------------------------------------------------------
+// //////////////////////////////////////////////////////////////
+// WARNING TODO CLEANME
+// Actual limitations of this code:
+//
+// /////// Redundant and unnecessary header parsing
+// In it's current state this code actually parses three times the Dicom
+// header of a file before the corresponding image gets loaded in the
+// ad-hoc vtkData !
+// Here is the process:
+// 1/ First loading happens in ExecuteInformation which in order to
+// positionate the vtk extents calls CheckFileCoherence. The purpose
+// of CheckFileCoherence is to make sure all the images in the future
+// stack are "homogenous" (same size, same representation...). This
+// can only be achieved by parsing all the Dicom headers...
+// 2/ ExecuteData is then responsible for the next two loadings:
+// 2a/ ExecuteData calls AllocateOutputData that in turn seems to
+// (indirectely call) ExecuteInformation which ends up in a second
+// header parsing
+// This is fixed by adding a test at the beginning of ExecuteInformation
+// on the modification of the object instance. If a modification have been
+// made (method Modified() ), the MTime value is increased. The fileTime
+// is compared to this new value to find a modification in the class
+// parameters
+// 2b/ the core of ExecuteData then needs gdcmFile (which in turns
+// initialises gdcmHeader in the constructor) in order to access
+// the data-image.
+//
+// Possible solution:
+// maintain a list of gdcmFiles (created by say ExecuteInformation) created
+// once and for all accross the life of vtkGdcmHeader (it would only load
+// new gdcmFile if the user changes the list). ExecuteData would then use
+// those gdcmFile and hence avoid calling the construtor:
+// - advantage: the header of the files would only be parser once.
+// - drawback: once execute information is called (i.e. on creation of
+// a vtkGdcmHeader) the gdcmFile structure is loaded in memory.
+// The average size of a gdcmHeader being of 100Ko, is one
+// loads 10 stacks of images with say 200 images each, you
+// end-up with a loss of 200Mo...
+//
+// /////// Never unallocated memory:
+// ExecuteData allocates space for the pixel data [which will get pointed
+// by the vtkPointData() through the call
+// data->GetPointData()->GetScalars()->SetVoidArray(mem, StackNumPixels, 0);]
+// This data is never "freed" neither in the destructor nor when the
+// filename list is extended, ExecuteData is called a second (or third)
+// time...
+// //////////////////////////////////////////////////////////////
+
+#include "gdcmHeader.h"
+#include "gdcmFile.h"
+#include "gdcmDebug.h"
+#include "vtkGdcmWriter.h"
+
+#include <vtkObjectFactory.h>
+#include <vtkImageData.h>
+#include <vtkPointData.h>
+#include <vtkLookupTable.h>
+
+vtkCxxRevisionMacro(vtkGdcmWriter, "$Revision: 1.1 $");
+vtkStandardNewMacro(vtkGdcmWriter);
+
+//-----------------------------------------------------------------------------
+// Constructor / Destructor
+vtkGdcmWriter::vtkGdcmWriter()
+{
+ this->LookupTable = NULL;
+}
+
+vtkGdcmWriter::~vtkGdcmWriter()
+{
+}
+
+//-----------------------------------------------------------------------------
+// Print
+void vtkGdcmWriter::PrintSelf(ostream& os, vtkIndent indent)
+{
+ this->Superclass::PrintSelf(os,indent);
+}
+
+//-----------------------------------------------------------------------------
+// Public
+
+//-----------------------------------------------------------------------------
+// Protected
+void SetImageInformation(gdcm::File *file,vtkImageData *image)
+{
+ std::ostringstream str;
+
+ // Image size
+ int *dim = image->GetDimensions();
+
+ str.str("");
+ str<<dim[0];
+ file->ReplaceOrCreateByNumber(str.str(),0x0028,0x0011);
+
+ str.str("");
+ str<<dim[1];
+ file->ReplaceOrCreateByNumber(str.str(),0x0028,0x0010);
+
+ if(dim[2]>1)
+ {
+ str.str("");
+ str<<dim[2];
+ file->ReplaceOrCreateByNumber(str.str(),0x0028,0x0012);
+ }
+
+ // Pixel type
+ str.str("");
+ str<<image->GetScalarSize()*8;
+ file->ReplaceOrCreateByNumber(str.str(),0x0028,0x0100);
+ file->ReplaceOrCreateByNumber(str.str(),0x0028,0x0101);
+
+ str.str("");
+ str<<image->GetScalarSize()*8-1;
+ file->ReplaceOrCreateByNumber(str.str(),0x0028,0x0102);
+
+ // Samples per pixel
+ str.str("");
+ str<<image->GetNumberOfScalarComponents();
+ file->ReplaceOrCreateByNumber(str.str(),0x0028,0x0002);
+ std::cout<<file->GetHeader()->GetEntryByNumber(0x0028,0x0002)<<"-"<<file->GetHeader()->GetNumberOfScalarComponents()<<"\n";
+
+ // Spacing
+ double *sp = image->GetSpacing();
+
+ str.str("");
+ str<<sp[0]<<"\\"<<sp[1];
+ file->ReplaceOrCreateByNumber(str.str(),0x0028,0x0030);
+ str.str("");
+ str<<sp[2];
+ file->ReplaceOrCreateByNumber(str.str(),0x0018,0x0088);
+
+ // Origin
+ double *org = image->GetOrigin();
+
+ str.str("");
+ str<<org[0]<<"\\"<<org[1]<<"\\"<<org[2];
+ file->ReplaceOrCreateByNumber(str.str(),0x0020,0x0032);
+
+ // Window / Level
+ double *rng=image->GetScalarRange();
+
+ str.str("");
+ str<<rng[1]-rng[0];
+ file->ReplaceOrCreateByNumber(str.str(),0x0028,0x1051);
+ str.str("");
+ str<<(rng[1]+rng[0])/2.0;
+ file->ReplaceOrCreateByNumber(str.str(),0x0028,0x1050);
+
+ // Pixels
+ size_t size = dim[0] * dim[1] * dim[2]
+ * image->GetScalarSize()
+ * image->GetNumberOfScalarComponents();
+ file->SetImageData((unsigned char *)image->GetScalarPointer(),size);
+}
+
+void vtkGdcmWriter::RecursiveWrite(int dim, vtkImageData *region, ofstream *file)
+{
+ if(file)
+ {
+ vtkErrorMacro(<< "File musn't be opened");
+ return;
+ }
+
+ if( region->GetScalarType() == VTK_FLOAT
+ || region->GetScalarType() == VTK_DOUBLE )
+ {
+ vtkErrorMacro(<< "Bad input type. Scalar type musn't be of type "
+ << "VTK_FLOAT or VTKDOUBLE (found:"
+ << region->GetScalarTypeAsString());
+ return;
+ }
+
+ gdcm::File *dcmFile = new gdcm::File();
+
+ ///////////////////////////////////////////////////////////////////////////
+ // Set the image informations
+ SetImageInformation(dcmFile,region);
+
+ ///////////////////////////////////////////////////////////////////////////
+ // Write the image
+ if(!dcmFile->Write(this->FileName))
+ {
+ vtkErrorMacro(<< "File " << this->FileName << "couldn't be written by "
+ << " the gdcm library");
+ std::cerr<<"not written \n";
+ }
+
+ delete dcmFile;
+}
+
+//-----------------------------------------------------------------------------
+// Private
+
+//-----------------------------------------------------------------------------
--- /dev/null
+// vtkGdcmWriter.h
+//-----------------------------------------------------------------------------
+#ifndef __vtkGdcmWriter_h
+#define __vtkGdcmWriter_h
+
+#include "gdcmCommon.h" // To avoid warnings concerning the std
+
+#include <vtkImageWriter.h>
+#include <vtkLookupTable.h>
+#include <list>
+#include <string>
+
+//-----------------------------------------------------------------------------
+class VTK_EXPORT vtkGdcmWriter : public vtkImageWriter
+{
+public:
+ static vtkGdcmWriter *New();
+ vtkTypeRevisionMacro(vtkGdcmWriter, vtkImageWriter);
+
+ void PrintSelf(ostream& os, vtkIndent indent);
+
+ vtkSetObjectMacro(LookupTable,vtkLookupTable);
+ vtkGetObjectMacro(LookupTable,vtkLookupTable);
+
+protected:
+ vtkGdcmWriter();
+ ~vtkGdcmWriter();
+
+ virtual void RecursiveWrite(int dim, vtkImageData *region, ofstream *file);
+
+private:
+// Variables
+ vtkLookupTable *LookupTable;
+};
+
+//-----------------------------------------------------------------------------
+#endif
+
--- /dev/null
+// This example illustrates how the vtkGdcmWriter vtk class can be
+// used in order to:
+//
+// Usage:
+//
+//----------------------------------------------------------------------------
+#include <iostream>
+
+#include <vtkImageMapToColors.h>
+#include <vtkLookupTable.h>
+#include <vtkImageData.h>
+
+#include "vtkGdcmReader.h"
+#include "vtkGdcmWriter.h"
+
+#ifndef vtkFloatingPointType
+#define vtkFloatingPointType float
+#endif
+
+//----------------------------------------------------------------------------
+int main(int argc, char *argv[])
+{
+ if( argc < 2 )
+ return 0;
+
+ vtkGdcmReader *reader = vtkGdcmReader::New();
+ reader->AllowLookupTableOff();
+
+ if( argc == 2 )
+ reader->SetFileName( argv[1] );
+ else
+ for(int i=1; i< argc; i++)
+ reader->AddFileName( argv[i] );
+
+ reader->Update();
+
+ vtkImageData *output;
+ if( reader->GetLookupTable() )
+ {
+ //convert to color:
+ vtkImageMapToColors *map = vtkImageMapToColors::New ();
+ map->SetInput (reader->GetOutput());
+ map->SetLookupTable (reader->GetLookupTable());
+ map->SetOutputFormatToRGB();
+ output = map->GetOutput();
+ map->Delete();
+ }
+ else
+ {
+ output = reader->GetOutput();
+ }
+
+ //print debug info:
+ output->Print( cout );
+
+ //////////////////////////////////////////////////////////
+ // WRITE...
+ //if you wish you can export dicom to a vtk file
+ // this file will have the add of .tmp.dcm extention
+ std::string fileName = argv[1];
+ fileName += ".tmp.dcm";
+
+ vtkGdcmWriter *writer = vtkGdcmWriter::New();
+ writer->SetFileName(fileName.c_str());
+ writer->SetInput(output);
+ writer->Write();
+ //////////////////////////////////////////////////////////
+
+ // Clean up
+ writer->Delete();
+ reader->Delete();
+
+ return 0;
+}