X-Git-Url: https://git.creatis.insa-lyon.fr/pubgit/?a=blobdiff_plain;f=vtk%2FvtkGdcmReader.cxx;h=1b86f6fff04b604a023ab43fd53d2c72129fe97f;hb=7c0064682f6082ac5e91abf1b4519d891aeb6095;hp=eaba22fe655e3b7cbf7a4ce250da8e55aa3e8488;hpb=90a2b8eeadb9d5afb2bc165b080f8a4622f5deb4;p=gdcm.git diff --git a/vtk/vtkGdcmReader.cxx b/vtk/vtkGdcmReader.cxx index eaba22fe..1b86f6ff 100644 --- a/vtk/vtkGdcmReader.cxx +++ b/vtk/vtkGdcmReader.cxx @@ -1,9 +1,10 @@ -// $Header: /cvs/public/gdcm/vtk/vtkGdcmReader.cxx,v 1.1 2003/05/05 14:13:59 frog Exp $ -#include "vtkGdcmReader.h" -#include "vtkByteSwap.h" +// $Header: /cvs/public/gdcm/vtk/vtkGdcmReader.cxx,v 1.9 2003/06/03 10:26:07 frog Exp $ +//CLEANME#include #include -#include "vtkObjectFactory.h" -#include "vtkImageFlip.h" +#include +#include +#include +#include "vtkGdcmReader.h" #include "gdcm.h" vtkGdcmReader::vtkGdcmReader() @@ -17,6 +18,174 @@ 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; + } + + // We don't know how to handle multiple images in one file yet: + int NZ = GdcmHeader.GetZSize(); + if (NZ > 1) + { + vtkErrorMacro("This file contains multiple planes (images)" + << *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(); + 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 +199,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(); @@ -57,8 +213,6 @@ void vtkGdcmReader::ExecuteInformation() vtkDebugMacro("Image dimension as read from Gdcm:" << NX << " " << NY << " " << NZ); - if(NZ>1) this->SetFileDimensionality(3); - // When the user has set the VOI, check it's coherence with the file content. if (this->DataVOI[0] || this->DataVOI[1] || this->DataVOI[2] || this->DataVOI[3] || @@ -69,16 +223,16 @@ void vtkGdcmReader::ExecuteInformation() (this->DataVOI[2] < 0) || (this->DataVOI[3] >= NY) || (this->DataVOI[4] < 0) || - (this->DataVOI[5] >= NZ)) + (this->DataVOI[5] >= this->FileNameList.size())) { 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; this->DataVOI[3] = NY - 1; this->DataVOI[4] = 0; - this->DataVOI[5] = NZ - 1; + this->DataVOI[5] = this->FileNameList.size() - 1; } } @@ -87,18 +241,18 @@ void vtkGdcmReader::ExecuteInformation() this->DataExtent[1] = NX - 1; this->DataExtent[2] = 0; this->DataExtent[3] = NY - 1; - if(this->GetFileDimensionality()==3) + if(this->FileNameList.size() > 1) { - this->DataExtent[4] = 0; - this->DataExtent[5] = NZ - 1; + this->DataExtent[4] = 0; + this->DataExtent[5] = this->FileNameList.size() - 1; } + // 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 +287,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,43 +343,64 @@ 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"); - - int size = - (this->DataExtent[1] - this->DataExtent[0]+1) * - (this->DataExtent[3] - this->DataExtent[2]+1) * - (this->DataExtent[5] - this->DataExtent[4]+1) * - 2; - gdcmFile GdcmFile(this->InternalFileName); - size = GdcmFile.GetImageDataSize(); - unsigned char *mem = new unsigned char [size]; + 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) + // 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 = 1; // This has been checked in CheckFileCoherence + size_t size = NumColumns * NumLines * NumPlanes * GdcmFile.GetPixelSize(); if ( size != GdcmFile.GetImageDataSize() ) { vtkDebugMacro("Inconsistency with GetImageDataSize"); vtkDebugMacro("Number of scalar components" << this->NumberOfScalarComponents); } - GdcmFile.GetImageDataIntoVector((void*)mem, size); - data->GetPointData()->GetScalars()->SetVoidArray(mem, size, 0); - //vtkImageFlip * Flip = vtkImageFlip::New(); - //Flip->SetInput(data); - //Flip->Update(); - //data = Flip->GetOutput(); - //Flip->SetInput(NULL); - this->Modified(); + // 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 [stack_size]; + + unsigned char * Dest = mem; + for (std::list::iterator FileName = FileNameList.begin(); + FileName != FileNameList.end(); + ++FileName) + { + this->LoadImageInMemory(*FileName, Dest, size); + Dest += size; + } + + // 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 ; + } }