X-Git-Url: https://git.creatis.insa-lyon.fr/pubgit/?a=blobdiff_plain;ds=inline;f=vtk%2FvtkGdcmReader.cxx;h=9f260a69b02fedb2a60047a3be16b5dfed5c4e38;hb=208af9d6c03679fd7eb2045badc0f5ee8e263aff;hp=6f438ec783c684d1990822c3bbacc71fb27b369b;hpb=6cf458fe8d13f8b577a62802650afc6f67c720a0;p=gdcm.git diff --git a/vtk/vtkGdcmReader.cxx b/vtk/vtkGdcmReader.cxx index 6f438ec7..9f260a69 100644 --- a/vtk/vtkGdcmReader.cxx +++ b/vtk/vtkGdcmReader.cxx @@ -1,9 +1,10 @@ -// $Header: /cvs/public/gdcm/vtk/vtkGdcmReader.cxx,v 1.2 2003/05/12 14:32:43 frog Exp $ -#include "vtkGdcmReader.h" -#include "vtkByteSwap.h" +// $Header: /cvs/public/gdcm/vtk/vtkGdcmReader.cxx,v 1.8 2003/06/02 07:42:17 regrain Exp $ +//CLEANME#include #include -#include "vtkObjectFactory.h" -#include "vtkImageFlip.h" +#include +#include +#include +#include "vtkGdcmReader.h" #include "gdcm.h" vtkGdcmReader::vtkGdcmReader() @@ -17,6 +18,158 @@ vtkGdcmReader::~vtkGdcmReader() // FIXME free memory } +//---------------------------------------------------------------------------- +// Adds a file name to the list of images to read. +void vtkGdcmReader::AddFileName(const char* name) +{ + // We need to bypass the const pointer [since list<>.push_bash() only + // takes a char* (but not a const char*)] by making a local copy: + char * LocalName = new char[strlen(name) + 1]; + strcpy(LocalName, name); + this->FileNameList.push_back(LocalName); + // Starting from two files we have a stack of images: + if(this->FileNameList.size() >= 2) + this->SetFileDimensionality(3); + this->Modified(); +} + +//---------------------------------------------------------------------------- +// Sets up a filename to be read. +void vtkGdcmReader::SetFileName(const char *name) { + vtkImageReader2::SetFileName(name); + // Since we maintain a list of filenames (when building a volume) + // we additionaly need to maintain this list. First we clean-up the + // list and then positionate the incoming filename: + this->FileNameList.empty(); + this->AddFileName(name); +} + +//---------------------------------------------------------------------------- +// vtkGdcmReader can have the file names specified through two ways: +// (1) by calling the vtkImageReader2::SetFileName(), SetFilePrefix() and +// SetFilePattern() +// (2) By successive calls to vtkGdcmReader::SetFileName() +// When the first method was used by caller we need to update the local +// filename list +void vtkGdcmReader::BuilFileListFromPattern() +{ + if (! this->FileNameList.empty()) + return; + if (!this->FileName && !this->FilePattern) + { + vtkErrorMacro("FileNames are not set. Either use AddFileName() or"); + vtkErrorMacro("specify a FileName or FilePattern."); + return; + } + for (int idx = this->DataExtent[4]; idx <= this->DataExtent[5]; ++idx) + { + this->ComputeInternalFileName(idx); + vtkDebugMacro("Adding file " << this->InternalFileName); + this->AddFileName(this->InternalFileName); + } +} + +//---------------------------------------------------------------------------- +// When more than one filename is specified (i.e. we expect loading +// a stack or volume) we need to check the corresponding images are +// coherent: +// - they all share the same X dimensions +// - they all share the same Y dimensions +// - each file a Z dimension of 1 +// - they all share the same type ( 8 bit signed, or unsigned...) +bool vtkGdcmReader::CheckFileCoherence() +{ + this->BuilFileListFromPattern(); + if (this->FileNameList.empty()) + { + vtkErrorMacro("FileNames are not set."); + return false; + } + if (this->FileNameList.size() == 1) + { + vtkDebugMacro("Single file specified."); + return true; + } + + // Loop on the filenames: + // - check for their existence and gdcm "parasability" + // - get the coherence check done: + bool FoundReferenceFile = false; + int ReferenceNX; + int ReferenceNY; + int ReferenceNZ; + std::string ReferenceType; + for (std::list::iterator FileName = FileNameList.begin(); + FileName != FileNameList.end(); + ++FileName) + { + // Check for file existence. + FILE *fp; + fp = fopen(FileName->c_str(),"rb"); + if (!fp) + { + vtkErrorMacro("Unable to open file " << *FileName->c_str()); + vtkErrorMacro("Removing this file from readed files " << *FileName->c_str()); + FileNameList.remove(*FileName); + continue; + } + fclose(fp); + + // Check for Gdcm parsability + gdcmHeader GdcmHeader(FileName->c_str()); + if (!GdcmHeader.IsReadable()) + { + vtkErrorMacro("Gdcm cannot parse file " << *FileName->c_str()); + vtkErrorMacro("Removing this file from readed files " << *FileName->c_str()); + FileNameList.remove(*FileName); + continue; + } + + // Coherence stage: + int NX = GdcmHeader.GetXSize(); + int NY = GdcmHeader.GetYSize(); + int NZ = GdcmHeader.GetZSize(); + std::string type = GdcmHeader.GetPixelType(); + if (FoundReferenceFile) + { + if ( ( NX != ReferenceNX ) + || ( NY != ReferenceNY ) + || ( NZ != ReferenceNZ ) + || ( type != ReferenceType ) ) + { + vtkErrorMacro("This file is not coherent with previous ones" + << *FileName->c_str()); + vtkErrorMacro("Removing this file from readed files " << *FileName->c_str()); + FileNameList.remove(*FileName); + continue; + } else { + vtkDebugMacro("File is coherent with previous ones" << *FileName->c_str()); + } + } else { + // This file shall be the reference: + FoundReferenceFile = true; + ReferenceNX = NX; + ReferenceNY = NY; + ReferenceNZ = NZ; + ReferenceType = type; + vtkDebugMacro("This file taken as coherence reference:" << *FileName->c_str()); + } + } // End of loop on FileName + + if (this->FileNameList.empty()) + { + vtkDebugMacro("No gdcm parsable file."); + return false; + } + if (this->FileNameList.size() == 1) + { + vtkDebugMacro("Single parsable file left after coherence test."); + return true; + } + return true; +} + +//---------------------------------------------------------------------------- // Configure the output e.g. WholeExtent, spacing, origin, scalar type... void vtkGdcmReader::ExecuteInformation() { @@ -30,26 +183,13 @@ void vtkGdcmReader::ExecuteInformation() this->DataExtent[4] = this->DataVOI[4]; this->DataExtent[5] = this->DataVOI[5]; } - - this->ComputeInternalFileName(this->DataExtent[4]); - - // Check for file existence. - FILE *fp; - fp = fopen(this->InternalFileName,"rb"); - if (!fp) + if ( ! this->CheckFileCoherence() ) { - vtkErrorMacro("Unable to open file " << this->InternalFileName); - return; - } - fclose(fp); - - // Check for Gdcm parsability - gdcmHeader GdcmHeader(this->InternalFileName); - if (!GdcmHeader.IsReadable()) - { - vtkErrorMacro("Gdcm cannot parse file " << this->InternalFileName); - return; + vtkErrorMacro("File set is not coherent. Exiting..."); + return; } + std::string ReferenceFile = this->FileNameList.front(); + gdcmHeader GdcmHeader(ReferenceFile.c_str()); int NX = GdcmHeader.GetXSize(); int NY = GdcmHeader.GetYSize(); @@ -72,7 +212,7 @@ void vtkGdcmReader::ExecuteInformation() (this->DataVOI[5] >= NZ)) { vtkWarningMacro("The requested VOI is larger than the file's (" - << this->InternalFileName << ") extent "); + << ReferenceFile.c_str() << ") extent "); this->DataVOI[0] = 0; this->DataVOI[1] = NX - 1; this->DataVOI[2] = 0; @@ -95,10 +235,9 @@ void vtkGdcmReader::ExecuteInformation() // We don't need to positionate the Endian related stuff (by using // this->SetDataByteOrderToBigEndian() or SetDataByteOrderToLittleEndian() - // since the // reading of the file is done by gdcm - - // But we need to set up the data type for downstream filters: - string type = GdcmHeader.GetPixelType(); + // since the reading of the file is done by gdcm. + // But we do need to set up the data type for downstream filters: + std::string type = GdcmHeader.GetPixelType(); if ( type == "8U" ) { vtkDebugMacro("8 bits unsigned image"); @@ -133,14 +272,54 @@ void vtkGdcmReader::ExecuteInformation() } else { - vtkErrorMacro("Bad File Type " << this->InternalFileName - << "Type " << type); + vtkErrorMacro("Bad File Type " << ReferenceFile.c_str() + << "Type " << type.c_str()); return; } vtkImageReader::ExecuteInformation(); } +//---------------------------------------------------------------------------- +void vtkGdcmReader::LoadImageInMemory(std::string FileName, + unsigned char * Dest, + size_t size) +{ + vtkDebugMacro("Copying to memmory image" << FileName.c_str()); + gdcmFile GdcmFile(FileName.c_str()); + + if (GdcmFile.GetZSize() != 1 ) + vtkErrorMacro("Cannot handle images with multiple planes"); + + // First check the expected size of the image is the one found by gdcm. + if ( size != GdcmFile.GetImageDataSize() ) + { + vtkErrorMacro("Inconsistency with GetImageDataSize for file" + << FileName.c_str()); + vtkErrorMacro("Number of scalar components" + << this->NumberOfScalarComponents); + } + + // If the data structure of vtk for image/volume representation + // were straigthforwards the following would suffice: + // GdcmFile.GetImageDataIntoVector((void*)Dest, size); + // But vtk chose to invert the lines of an image, that is the last + // line comes first (for some axis related reasons?). Hence we need + // to load the image line by line, starting from the end: + int NumColumns = GdcmFile.GetXSize(); + int NumLines = GdcmFile.GetYSize(); + int LineSize = NumColumns * GdcmFile.GetPixelSize(); + unsigned char * Source = (unsigned char*)GdcmFile.GetImageData(); + unsigned char * Destination = Dest + size - LineSize; + for (int i = 0; i < NumLines; i++) + { + memcpy((void*)Destination, (void*)Source, LineSize); + Source += LineSize; + Destination -= LineSize; + } +} + +//---------------------------------------------------------------------------- // Update -> UpdateData -> Execute -> ExecuteData (see vtkSource.cxx for // last step. // This function (redefinition of vtkImageReader::ExecuteData, see @@ -149,55 +328,66 @@ void vtkGdcmReader::ExecuteInformation() // same as the file extent/order. void vtkGdcmReader::ExecuteData(vtkDataObject *output) { - if (!this->FileName) + if (this->FileNameList.empty()) { - vtkErrorMacro("A valid FileName must be specified."); + vtkErrorMacro("A least a valid FileName must be specified."); return; } vtkImageData *data = this->AllocateOutputData(output); data->SetExtent(this->DataExtent); - data->GetPointData()->GetScalars()->SetName("ImageFile"); + data->GetPointData()->GetScalars()->SetName("DicomImage-Volume"); // First check the coherence between the DataExtent and the - // size of the pixel data as annouced by gdcm (looks a bit paranoid). - gdcmFile GdcmFile(this->InternalFileName); + // size of the pixel data as annouced by gdcm (looks a bit paranoid) + // for the reference file (i.e. the first one in the list): + std::string ReferenceFile = this->FileNameList.front(); + gdcmFile GdcmFile(ReferenceFile.c_str()); int NumColumns = this->DataExtent[1] - this->DataExtent[0] + 1; int NumLines = this->DataExtent[3] - this->DataExtent[2] + 1; int NumPlanes = this->DataExtent[5] - this->DataExtent[4] + 1; - int size = NumColumns * NumLines * NumPlanes * GdcmFile.GetPixelSize(); + size_t size = NumColumns * NumLines * NumPlanes * GdcmFile.GetPixelSize(); if ( size != GdcmFile.GetImageDataSize() ) { vtkDebugMacro("Inconsistency with GetImageDataSize"); vtkDebugMacro("Number of scalar components" << this->NumberOfScalarComponents); } + + // The memory size for a full stack of images of course depends + // on the number of images: + size_t stack_size = size * this->FileNameList.size(); // Allocate pixel data space itself. - unsigned char *mem = new unsigned char [size]; + unsigned char *mem = new unsigned char [stack_size]; - // If the data structure of vtk for image/volume representation - // were straigthforwards the following would suffice: - // GdcmFile.GetImageDataIntoVector((void*)mem, size); - // But vtk chose to invert the lines of an image, that is the last - // line comes first (for some axis related reasons?). Hence we need - // to load the image line by line, starting from the end: - int LineSize = NumColumns * GdcmFile.GetPixelSize(); - unsigned char * Source = (unsigned char*)GdcmFile.GetImageData(); - unsigned char * Destination = mem + size - LineSize; - for (int i = 0; i < NumLines; i++) + unsigned char * Dest = mem; + for (std::list::iterator FileName = FileNameList.begin(); + FileName != FileNameList.end(); + ++FileName) { - memcpy((void*)Destination, (void*)Source, LineSize); - Source += LineSize; - Destination -= LineSize; + this->LoadImageInMemory(*FileName, Dest, size); + Dest += size; } - data->GetPointData()->GetScalars()->SetVoidArray(mem, size, 0); + + // The "size" of the vtkScalars data is expressed in number of points, + // and is not the memory size representing those points: + stack_size = stack_size / GdcmFile.GetPixelSize(); + data->GetPointData()->GetScalars()->SetVoidArray(mem, stack_size, 0); this->Modified(); } +//---------------------------------------------------------------------------- void vtkGdcmReader::PrintSelf(ostream& os, vtkIndent indent) { vtkImageReader::PrintSelf(os,indent); - //CLEANME os << indent << "TypeSize: " << this->TypeSize << "\n"; + os << indent << "Filenames : " << endl; + vtkIndent nextIndent = indent.GetNextIndent(); + for (std::list::iterator FileName = FileNameList.begin(); + FileName != FileNameList.end(); + ++FileName) + { + os << nextIndent << *FileName->c_str() << endl ; + } }