X-Git-Url: https://git.creatis.insa-lyon.fr/pubgit/?a=blobdiff_plain;f=src%2FgdcmFile.cxx;h=a2ddfc70a88fad33cfa402db1f869fb346c057ff;hb=912de5d1cc942f7386c83d32c5d847ef5bb2bf17;hp=ade5daf0a49c2aef3ceaf8ec30f729ac101e0463;hpb=e40fc77cef3155aab87305ce2f8f14d1acbf158f;p=gdcm.git diff --git a/src/gdcmFile.cxx b/src/gdcmFile.cxx index ade5daf0..a2ddfc70 100644 --- a/src/gdcmFile.cxx +++ b/src/gdcmFile.cxx @@ -3,8 +3,8 @@ Program: gdcm Module: $RCSfile: gdcmFile.cxx,v $ Language: C++ - Date: $Date: 2004/07/02 13:55:28 $ - Version: $Revision: 1.115 $ + Date: $Date: 2004/09/01 16:23:59 $ + Version: $Revision: 1.122 $ Copyright (c) CREATIS (Centre de Recherche et d'Applications en Traitement de l'Image). All rights reserved. See Doc/License.txt or @@ -22,28 +22,38 @@ typedef std::pair IterHT; -//----------------------------------------------------------------------------- +//------------------------------------------------------------------------- // Constructor / Destructor /** * \ingroup gdcmFile - * \brief Constructor dedicated to writing a new DICOMV3 part10 compliant - * file (see SetFileName, SetDcmTag and Write) + * \brief Constructor dedicated to deal with the *pixels* area of a ACR/DICOMV3 + * file (gdcmHeader 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 + * user sets an a posteriori shadow dictionary (efficiency can be * seen as a side effect). - * @param header file to be opened for reading datas + * @param header already built gdcmHeader * @return */ gdcmFile::gdcmFile(gdcmHeader *header) { Header = header; SelfHeader = false; + PixelRead = -1; // no ImageData read yet. - + LastAllocatedPixelDataLength = 0; + PixelData = NULL; + + InitialSpp = ""; + InitialPhotInt = ""; + InitialPlanConfig = ""; + InitialBitsAllocated = ""; + if (Header->IsReadable()) { SetPixelDataSizeFromHeader(); @@ -52,39 +62,33 @@ gdcmFile::gdcmFile(gdcmHeader *header) /** * \ingroup gdcmFile - * \brief Constructor dedicated to writing a new DICOMV3 part10 compliant - * file (see SetFileName, SetDcmTag and Write) + * \brief Constructor dedicated to deal with the *pixels* area of a ACR/DICOMV3 + * file (gdcmHeader 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). * @param filename file to be opened for parsing - * @param exception_on_error whether we throw an exception or not - * @param enable_sequences = true to allow the header - * to be parsed *inside* the SeQuences, - * when they have an actual length - * \warning enable_sequences *has to be* true for reading PAPYRUS 3.0 files - * @param ignore_shadow to allow skipping the shadow elements, - * to save memory space. - * \warning The TRUE value for this param has to be used - * with a FALSE value for the 'enable_sequence' param. - * ('public elements' may be embedded in 'shadow Sequences') */ -gdcmFile::gdcmFile(std::string const & filename, - bool exception_on_error, - bool enable_sequences, - bool ignore_shadow) +gdcmFile::gdcmFile(std::string const & filename ) { - Header = new gdcmHeader( filename, - exception_on_error, - enable_sequences, - ignore_shadow ); + Header = new gdcmHeader( filename ); SelfHeader = true; + PixelRead = -1; // no ImageData read yet. - + LastAllocatedPixelDataLength = 0; + PixelData = NULL; + + InitialSpp = ""; + InitialPhotInt = ""; + InitialPlanConfig = ""; + InitialBitsAllocated = ""; + if ( Header->IsReadable() ) { SetPixelDataSizeFromHeader(); @@ -94,8 +98,8 @@ gdcmFile::gdcmFile(std::string const & filename, /** * \ingroup gdcmFile * \brief canonical destructor - * \note If the gdcmHeader is created by the gdcmFile, it is destroyed - * by the gdcmFile + * \note If the gdcmHeader was created by the gdcmFile constructor, + * it is destroyed by the gdcmFile */ gdcmFile::~gdcmFile() { @@ -114,11 +118,10 @@ gdcmFile::~gdcmFile() /** * \ingroup gdcmFile - * \brief computes the length (in bytes) to ALLOCATE to receive the + * \brief computes the length (in bytes) we must ALLOCATE to receive the * image(s) pixels (multiframes taken into account) * \warning : it is NOT the group 7FE0 length * (no interest for compressed images). - * @return length to allocate */ void gdcmFile::SetPixelDataSizeFromHeader() { @@ -169,7 +172,7 @@ void gdcmFile::SetPixelDataSizeFromHeader() std::string str_PhotometricInterpretation = Header->GetEntryByNumber(0x0028,0x0004); - /*if ( str_PhotometricInterpretation == "PALETTE COLOR " )*/ + // if ( str_PhotometricInterpretation == "PALETTE COLOR " ), // pb when undealt Segmented Palette Color if ( Header->HasLUT() ) @@ -178,23 +181,13 @@ void gdcmFile::SetPixelDataSizeFromHeader() } } -/** - * \ingroup gdcmFile - * \brief Returns the size (in bytes) of required memory to hold - * the pixel data represented in this file. - * @return The size of pixel data in bytes. - */ -size_t gdcmFile::GetImageDataSize() -{ - return ImageDataSize; -} /** * \ingroup gdcmFile * \brief Returns the size (in bytes) of required memory to hold - * the pixel data represented in this file, when user DOESN'T want + * the pixel data represented in this file, if user DOESN'T want * to get RGB pixels image when it's stored as a PALETTE COLOR image - * -the (vtk) user is supposed to know how deal with LUTs- + * -the (vtk) user is supposed to know how to deal with LUTs- * \warning to be used with GetImagePixelsRaw() * @return The size of pixel data in bytes. */ @@ -205,22 +198,30 @@ size_t gdcmFile::GetImageDataSizeRaw() /** * \ingroup gdcmFile - * \brief Allocates necessary memory, copies the pixel data - * (image[s]/volume[s]) to newly allocated zone. - * Transforms YBR pixels into RGB pixels if any - * Transforms 3 planes R, G, B into a single RGB Plane - * Transforms single Grey plane + 3 Palettes into a RGB Plane + * \brief - Allocates necessary memory, + * - Reads the pixels from disk (uncompress if necessary), + * - Transforms YBR pixels, if any, into RGB pixels + * - Transforms 3 planes R, G, B, if any, into a single RGB Plane + * - Transforms single Grey plane + 3 Palettes into a RGB Plane + * - Copies the pixel data (image[s]/volume[s]) to newly allocated zone. * @return Pointer to newly allocated pixel data. * NULL if alloc fails */ void *gdcmFile::GetImageData() { - // FIXME + // FIXME (Mathieu) // I need to deallocate PixelData before doing any allocation: + + if ( PixelData ) + if ( LastAllocatedPixelDataLength != ImageDataSize) + free (PixelData); PixelData = new uint8_t[ImageDataSize]; if ( PixelData ) { + LastAllocatedPixelDataLength = ImageDataSize; + GetImageDataIntoVector(PixelData, ImageDataSize); + // Will be 7fe0, 0010 in standard case GetHeader()->SetEntryVoidAreaByNumber( PixelData, GetHeader()->GetGrPixel(), GetHeader()->GetNumPixel()); } @@ -231,10 +232,15 @@ void *gdcmFile::GetImageData() /** * \ingroup gdcmFile - * \brief Copies at most MaxSize bytes of pixel data to caller's + * \brief + * Read the pixels from disk (uncompress if necessary), + * Transforms YBR pixels, if any, into RGB pixels + * Transforms 3 planes R, G, B, if any, into a single RGB Plane + * Transforms single Grey plane + 3 Palettes into a RGB Plane + * Copies at most MaxSize bytes of pixel data to caller allocated * memory space. - * \warning This function was designed to avoid people that want to build - * a volume from an image stack to need first to get the image pixels + * \warning This function allows people that want to build a volume + * from an image stack *not to* have, first to get the image pixels, * and then move them to the volume area. * It's absolutely useless for any VTK user since vtk chooses * to invert the lines of an image, that is the last line comes first @@ -252,9 +258,8 @@ void *gdcmFile::GetImageData() */ size_t gdcmFile::GetImageDataIntoVector (void* destination, size_t maxSize) { - //size_t l = GetImageDataIntoVectorRaw (destination, maxSize); GetImageDataIntoVectorRaw (destination, maxSize); - PixelRead = 0 ; // no PixelRaw + PixelRead = 0 ; // =0 : no ImageDataRaw if ( !Header->HasLUT() ) { return ImageDataSize; @@ -263,12 +268,11 @@ size_t gdcmFile::GetImageDataIntoVector (void* destination, size_t maxSize) // from Lut R + Lut G + Lut B uint8_t *newDest = new uint8_t[ImageDataSize]; uint8_t *a = (uint8_t *)destination; - uint8_t *lutRGBA = Header->GetLUTRGBA(); + uint8_t *lutRGBA = Header->GetLUTRGBA(); if ( lutRGBA ) { int j; - //int l = ImageDataSizeRaw; //loss of precision // move Gray pixels to temp area memmove(newDest, destination, ImageDataSizeRaw); for (size_t i=0; iSetEntryByNumber(spp,0x0028,0x0002); - std::string rgb= "RGB "; // Photometric Interpretation + std::string rgb = "RGB "; // Photometric Interpretation Header->SetEntryByNumber(rgb,0x0028,0x0004); std::string planConfig = "0"; // Planar Configuration Header->SetEntryByNumber(planConfig,0x0028,0x0006); } - else //why is there a 'else' when an allocation failed ? - { - // need to make RGB Pixels (?) - // from grey Pixels (?!) - // and Gray Lut (!?!) - // or Segmented xxx Palette Color Lookup Table Data and so on - - // Oops! I get one (gdcm-US-ALOKA-16.dcm) - // No idea how to manage such an image + else // GetLUTRGBA() failed + { + // (gdcm-US-ALOKA-16.dcm), contains Segmented xxx Palette Color + // that are *more* than 65535 long ?!? + // No idea how to manage such an image ! + // Need to make RGB Pixels (?) from grey Pixels (?!) and Gray Lut (!?!) // It seems that *no Dicom Viewer* has any idea :-( - // Segmented xxx Palette Color are *more* than 65535 long ?!? - - std::string rgb = "MONOCHROME1 "; // Photometric Interpretation - Header->SetEntryByNumber(rgb,0x0028,0x0004); + + std::string photomInterp = "MONOCHROME1 "; // Photometric Interpretation + Header->SetEntryByNumber(photomInterp,0x0028,0x0004); } /// \todo Drop Palette Color out of the Header? @@ -316,30 +316,36 @@ size_t gdcmFile::GetImageDataIntoVector (void* destination, size_t maxSize) /** * \ingroup gdcmFile - * \brief Allocates necessary memory, copies the pixel data - * (image[s]/volume[s]) to newly allocated zone. - * Transforms YBR pixels into RGB pixels if any - * Transforms 3 planes R, G, B into a single RGB Plane + * \brief Allocates necessary memory, + * Transforms YBR pixels (if any) into RGB pixels + * Transforms 3 planes R, G, B (if any) into a single RGB Plane + * Copies the pixel data (image[s]/volume[s]) to newly allocated zone. * DOES NOT transform Grey plane + 3 Palettes into a RGB Plane * @return Pointer to newly allocated pixel data. * \ NULL if alloc fails */ void * gdcmFile::GetImageDataRaw () { - size_t imgDataSize = ImageDataSize; + size_t imgDataSize; if ( Header->HasLUT() ) - { /// \todo Let gdcmHeader user a chance to get the right value - // ImageDataSize /= 3; //dangerous imgDataSize = ImageDataSizeRaw; - } - - // FIXME + else + imgDataSize = ImageDataSize; + + // FIXME (Mathieu) // I need to deallocate PixelData before doing any allocation: + + if ( PixelData ) + if ( LastAllocatedPixelDataLength != imgDataSize) + free (PixelData); PixelData = new uint8_t[imgDataSize]; if ( PixelData ) { - GetImageDataIntoVectorRaw(PixelData, ImageDataSize); + LastAllocatedPixelDataLength = imgDataSize; + + GetImageDataIntoVectorRaw(PixelData, imgDataSize); + // will be 7fe0, 0010 in standard cases GetHeader()->SetEntryVoidAreaByNumber(PixelData, GetHeader()->GetGrPixel(), GetHeader()->GetNumPixel()); } @@ -375,8 +381,30 @@ void * gdcmFile::GetImageDataRaw () size_t gdcmFile::GetImageDataIntoVectorRaw (void *destination, size_t maxSize) { int nb, nbu, highBit, sign; + + // we save the initial values of the following + // in order to be able to restore the header in a disk-consistent state + // (if user asks twice to get the pixels from disk) + + if ( PixelRead == -1 ) // File was never "read" before + { + InitialSpp = Header->GetEntryByNumber(0x0028,0x0002); + InitialPhotInt = Header->GetEntryByNumber(0x0028,0x0004); + InitialPlanConfig = Header->GetEntryByNumber(0x0028,0x0006); + InitialBitsAllocated = Header->GetEntryByNumber(0x0028,0x0100); + } + else // File was already "read", the following *may* have been modified + // we restore them to be in a disk-consistent state + { + // FIXME : What happened with the LUTs ? + Header->SetEntryByNumber(InitialSpp,0x0028,0x0002); + Header->SetEntryByNumber(InitialPhotInt,0x0028,0x0004); + Header->SetEntryByNumber(InitialPlanConfig,0x0028,0x0006); + Header->SetEntryByNumber(InitialBitsAllocated,0x0028,0x0100); + } + PixelRead = 1 ; // PixelRaw - + if ( ImageDataSize > maxSize ) { dbg.Verbose(0, "gdcmFile::GetImageDataIntoVector: pixel data bigger" @@ -615,12 +643,14 @@ size_t gdcmFile::GetImageDataIntoVectorRaw (void *destination, size_t maxSize) // CreateOrReplaceIfExist ? - std::string spp = "3"; // Samples Per Pixel - std::string rgb = "RGB "; // Photometric Interpretation - std::string planConfig = "0"; // Planar Configuration + std::string spp = "3"; // Samples Per Pixel + std::string photInt = "RGB "; // Photometric Interpretation + std::string planConfig = "0"; // Planar Configuration + + Header->SetEntryByNumber(spp,0x0028,0x0002); - Header->SetEntryByNumber(rgb,0x0028,0x0004); + Header->SetEntryByNumber(photInt,0x0028,0x0004); Header->SetEntryByNumber(planConfig,0x0028,0x0006); /// \todo Drop Palette Color out of the Header? @@ -632,7 +662,7 @@ size_t gdcmFile::GetImageDataIntoVectorRaw (void *destination, size_t maxSize) * \brief performs a shalow copy (not a deep copy) of the user given * pixel area. * 'image' Pixels are presented as C-like 2D arrays : line per line. - * 'volume'Pixels are presented as C-like 3D arrays : lane per plane + * 'volume'Pixels are presented as C-like 3D arrays : plane per plane * \warning user is kindly requested NOT TO 'free' the Pixel area * @param inData user supplied pixel area * @param expectedSize total image size, in Bytes @@ -642,10 +672,11 @@ size_t gdcmFile::GetImageDataIntoVectorRaw (void *destination, size_t maxSize) bool gdcmFile::SetImageData(void *inData, size_t expectedSize) { Header->SetImageDataSize( expectedSize ); +// FIXME : if already allocated, memory leak ! PixelData = inData; - ImageDataSize = expectedSize; + ImageDataSize = ImageDataSizeRaw = expectedSize; PixelRead = 1; - +// FIXME : 7fe0, 0010 IS NOT set ... return true; } @@ -661,14 +692,13 @@ bool gdcmFile::SetImageData(void *inData, size_t expectedSize) bool gdcmFile::WriteRawData(std::string const & fileName) { - FILE *fp1; - fp1 = fopen(fileName.c_str(), "wb"); + FILE *fp1 = fopen(fileName.c_str(), "wb"); if (fp1 == NULL) { printf("Fail to open (write) file [%s] \n", fileName.c_str()); return false; } - fwrite (PixelData,ImageDataSize, 1, fp1); + fwrite (PixelData, ImageDataSize, 1, fp1); fclose (fp1); return true; @@ -736,14 +766,12 @@ bool gdcmFile::WriteAcr (std::string const & fileName) */ bool gdcmFile::WriteBase (std::string const & fileName, FileType type) { - FILE *fp1; - if ( PixelRead == -1 && type != gdcmExplicitVR) { return false; } - fp1 = fopen(fileName.c_str(), "wb"); + FILE *fp1 = fopen(fileName.c_str(), "wb"); if (fp1 == NULL) { printf("Failed to open (write) File [%s] \n", fileName.c_str()); @@ -785,9 +813,8 @@ bool gdcmFile::WriteBase (std::string const & fileName, FileType type) uint16_t grPixel = Header->GetGrPixel(); uint16_t numPixel = Header->GetNumPixel();; - gdcmDocEntry* PixelElement; - - PixelElement = GetHeader()->GetDocEntryByNumber(grPixel, numPixel); + gdcmDocEntry* PixelElement = + GetHeader()->GetDocEntryByNumber(grPixel, numPixel); if ( PixelRead == 1 ) { @@ -851,7 +878,7 @@ void gdcmFile::SwapZone(void *im, int swap, int lgr, int nb) case 4321: for(i=0; i < lgr/2; i++) { - im16[i]= (im16[i] >> 8) | (im16[i] << 8 ); + im16[i]= (im16[i] >> 8) | (im16[i] << 8 ); } break; default: @@ -980,7 +1007,7 @@ bool gdcmFile::ReadPixelData(void *destination) // ---------------------- Run Length Encoding if ( Header->IsRLELossLessTransferSyntax() ) { - bool res = (bool)gdcm_read_RLE_file (fp,destination); + bool res = gdcm_read_RLE_file (fp,destination); Header->CloseFile(); return res; } @@ -1060,7 +1087,7 @@ bool gdcmFile::ReadPixelData(void *destination) if ( jpg2000 ) { // JPEG 2000 : call to ??? - res = (bool)gdcm_read_JPEG2000_file (fp,destination); // Not Yet written + res = gdcm_read_JPEG2000_file (fp,destination); // Not Yet written // ------------------------------------- endif (JPEG2000) } else if (jpgLossless) @@ -1078,12 +1105,12 @@ bool gdcmFile::ReadPixelData(void *destination) if ( Header->GetBitsStored() == 8) { // Reading Fragment pixels - res = (bool)gdcm_read_JPEG_file (fp,destination); + res = gdcm_read_JPEG_file (fp,destination); } else { // Reading Fragment pixels - res = (bool)gdcm_read_JPEG_file12 (fp,destination); + res = gdcm_read_JPEG_file12 (fp,destination); } // ------------------------------------- endif (JPEGLossy) }