X-Git-Url: https://git.creatis.insa-lyon.fr/pubgit/?a=blobdiff_plain;f=src%2FgdcmFile.cxx;h=b3eb62e680299e2f8d0ba6a44de98e652057a7ed;hb=152194ed48de20a5189b610bd8d3d26fea95c2ce;hp=c7a8b5d17bf833122a67576f48cd283cf3a21c56;hpb=00b6e0ddcbdbd41252e03732783f65efe5f52526;p=gdcm.git diff --git a/src/gdcmFile.cxx b/src/gdcmFile.cxx index c7a8b5d1..b3eb62e6 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/06/26 04:09:33 $ - Version: $Revision: 1.111 $ + Date: $Date: 2004/09/10 18:54:38 $ + Version: $Revision: 1.124 $ Copyright (c) CREATIS (Centre de Recherche et d'Applications en Traitement de l'Image). All rights reserved. See Doc/License.txt or @@ -22,88 +22,145 @@ 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 - * @return + * @param header already built gdcmHeader */ gdcmFile::gdcmFile(gdcmHeader *header) { Header = header; SelfHeader = false; - PixelRead = -1; // no ImageData read yet. - - if (Header->IsReadable()) - { - SetPixelDataSizeFromHeader(); - } + SetInitialValues(); } /** * \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; + SetInitialValues(); +} + +/** + * \ingroup gdcmFile + * \brief Sets some initial for the Constructor + */ +void gdcmFile::SetInitialValues() +{ PixelRead = -1; // no ImageData read yet. + LastAllocatedPixelDataLength = 0; + Pixel_Data = 0; + InitialSpp = ""; + InitialPhotInt = ""; + InitialPlanConfig = ""; + InitialBitsAllocated = ""; + + InitialRedLUTDescr = 0; + InitialGreenLUTDescr = 0; + InitialBlueLUTDescr = 0; + InitialRedLUTData = 0; + InitialGreenLUTData = 0; + InitialBlueLUTData = 0; + if ( Header->IsReadable() ) { SetPixelDataSizeFromHeader(); + + // the following values *may* be modified + // by gdcmFile::GetImageDataIntoVectorRaw + // we save their initial value. + InitialSpp = Header->GetEntryByNumber(0x0028,0x0002); + InitialPhotInt = Header->GetEntryByNumber(0x0028,0x0004); + InitialPlanConfig = Header->GetEntryByNumber(0x0028,0x0006); + InitialBitsAllocated = Header->GetEntryByNumber(0x0028,0x0100); + + // the following entries *may* be removed + // by gdcmFile::GetImageDataIntoVectorRaw + // we save them. + + // we SHALL save them ! + // (some troubles, now) + /* + InitialRedLUTDescr = Header->GetDocEntryByNumber(0x0028,0x1101); + InitialGreenLUTDescr = Header->GetDocEntryByNumber(0x0028,0x1102); + InitialBlueLUTDescr = Header->GetDocEntryByNumber(0x0028,0x1103); + InitialRedLUTData = Header->GetDocEntryByNumber(0x0028,0x1201); + InitialGreenLUTData = Header->GetDocEntryByNumber(0x0028,0x1202); + InitialBlueLUTData = Header->GetDocEntryByNumber(0x0028,0x1203); + + if (InitialRedLUTData == NULL) + std::cout << "echec InitialRedLUTData " << std::endl; + else + { + printf("%p\n",InitialRedLUTData); + InitialRedLUTData->Print(); std::cout <GetXSize() * Header->GetYSize() + ImageDataSize = + ImageDataSizeRaw = Header->GetXSize() * Header->GetYSize() * Header->GetZSize() * (nb/8) * Header->GetSamplesPerPixel(); 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,63 +236,59 @@ 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 - * 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- - * \warning to be used with GetImagePixelsRaw() - * @return The size of pixel data in bytes. - */ -size_t gdcmFile::GetImageDataSizeRaw() -{ - return ImageDataSizeRaw; -} - /** * \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 - // I need to deallocate PixelData before doing any allocation: - PixelData = new uint8_t[ImageDataSize]; - if ( PixelData ) + // FIXME (Mathieu) + // I need to deallocate Pixel_Data before doing any allocation: + + if ( Pixel_Data ) + if ( LastAllocatedPixelDataLength != ImageDataSize ) + free(Pixel_Data); + if ( !Pixel_Data ) + Pixel_Data = new uint8_t[ImageDataSize]; + + if ( Pixel_Data ) { - GetImageDataIntoVector(PixelData, ImageDataSize); - GetHeader()->SetEntryVoidAreaByNumber( PixelData, + LastAllocatedPixelDataLength = ImageDataSize; + + // we load the pixels (and transform grey level + LUT into RGB) + GetImageDataIntoVector(Pixel_Data, ImageDataSize); + + // We say the value *is* loaded. + GetHeader()->SetEntryByNumber( GDCM_BINLOADED, + GetHeader()->GetGrPixel(), GetHeader()->GetNumPixel()); + + // Will be 7fe0, 0010 in standard case + GetHeader()->SetEntryVoidAreaByNumber( Pixel_Data, GetHeader()->GetGrPixel(), GetHeader()->GetNumPixel()); } PixelRead = 0; // no PixelRaw - return PixelData; + return Pixel_Data; } /** * \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 @@ -244,7 +298,7 @@ void *gdcmFile::GetImageData() * * @param destination Address (in caller's memory space) at which the * pixel data should be copied - * @param MaxSize Maximum number of bytes to be copied. When MaxSize + * @param maxSize Maximum number of bytes to be copied. When MaxSize * is not sufficient to hold the pixel data the copy is not * executed (i.e. no partial copy). * @return On success, the number of bytes actually copied. Zero on @@ -252,9 +306,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 +316,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,10 +364,10 @@ 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 @@ -328,24 +376,38 @@ void * gdcmFile::GetImageDataRaw () { size_t imgDataSize; if ( Header->HasLUT() ) - { /// \todo Let gdcmHeader user a chance to get the right value - // ImageDataSize /= 3; //dangerous imgDataSize = ImageDataSizeRaw; - } + else + imgDataSize = ImageDataSize; + + // FIXME (Mathieu) + // I need to deallocate Pixel_Data before doing any allocation: + + if ( Pixel_Data ) + if ( LastAllocatedPixelDataLength != imgDataSize ) + free(Pixel_Data); + if ( !Pixel_Data ) + Pixel_Data = new uint8_t[imgDataSize]; - // FIXME - // I need to deallocate PixelData before doing any allocation: - PixelData = new uint8_t[imgDataSize]; - if ( PixelData ) + if ( Pixel_Data ) { - GetImageDataIntoVectorRaw(PixelData, ImageDataSize); - GetHeader()->SetEntryVoidAreaByNumber(PixelData, - GetHeader()->GetGrPixel(), GetHeader()->GetNumPixel()); + LastAllocatedPixelDataLength = imgDataSize; + + // we load the pixels ( grey level or RGB, but NO transformation) + GetImageDataIntoVectorRaw(Pixel_Data, imgDataSize); + + // We say the value *is* loaded. + GetHeader()->SetEntryByNumber( GDCM_BINLOADED, + GetHeader()->GetGrPixel(), GetHeader()->GetNumPixel()); + + // will be 7fe0, 0010 in standard cases + GetHeader()->SetEntryVoidAreaByNumber(Pixel_Data, + GetHeader()->GetGrPixel(), GetHeader()->GetNumPixel()); } PixelRead = 1; // PixelRaw - return PixelData; + return Pixel_Data; } /** @@ -366,7 +428,7 @@ void * gdcmFile::GetImageDataRaw () * * @param destination Address (in caller's memory space) at which the * pixel data should be copied - * @param MaxSize Maximum number of bytes to be copied. When MaxSize + * @param maxSize Maximum number of bytes to be copied. When MaxSize * is not sufficient to hold the pixel data the copy is not * executed (i.e. no partial copy). * @return On success, the number of bytes actually copied. Zero on @@ -375,8 +437,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" @@ -473,9 +557,9 @@ size_t gdcmFile::GetImageDataIntoVectorRaw (void *destination, size_t maxSize) } else if ( nb == 32 ) { - guint32 mask = 0xffffffff; + uint32_t mask = 0xffffffff; mask = mask >> (nb - nbu); - guint32 *deb = (guint32 *)destination; + uint32_t *deb = (uint32_t *)destination; for(int i = 0; i> (nbu - highBit - 1)) & mask; @@ -615,12 +699,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,20 +718,21 @@ 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 + * @param expectedSize total image size, in Bytes * * @return boolean */ bool gdcmFile::SetImageData(void *inData, size_t expectedSize) { Header->SetImageDataSize( expectedSize ); - PixelData = inData; - ImageDataSize = expectedSize; +// FIXME : if already allocated, memory leak ! + Pixel_Data = inData; + ImageDataSize = ImageDataSizeRaw = expectedSize; PixelRead = 1; - +// FIXME : 7fe0, 0010 IS NOT set ... return true; } @@ -661,14 +748,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 (Pixel_Data, ImageDataSize, 1, fp1); fclose (fp1); return true; @@ -736,14 +822,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()); @@ -781,45 +865,24 @@ bool gdcmFile::WriteBase (std::string const & fileName, FileType type) Header->SetEntryByNumber(rows , 0x0028, 0x0011); } // ----------------- End of Special Patch ---------------- - - /// \todo get the grPixel, numPixel values (for some ACR-NEMA images only) - + uint16_t grPixel = Header->GetGrPixel(); uint16_t numPixel = Header->GetNumPixel();; - - // Update Pixel Data Length - // the *last* of the (GrPixel, NumPixel), if many. - TagKey key = gdcmDictEntry::TranslateToKey(grPixel, numPixel); - TagDocEntryHT::iterator p2; - gdcmDocEntry* PixelElement; - - IterHT it = Header->GetEntry().equal_range(key); // get a pair of iterators first-last synonym - - if ( Header->GetEntry().count(key) == 1 ) // only the first is significant - { - p2 = it.first; // iterator on the first (unique) synonym - } - else - { - p2 = it.second;// iterator on the last synonym - } - - PixelElement = p2->second; // H Table target column (2-nd col) - // PixelElement->SetPrintLevel(2); - // PixelElement->Print(); + gdcmDocEntry* PixelElement = + GetHeader()->GetDocEntryByNumber(grPixel, numPixel); if ( PixelRead == 1 ) { + // we read pixel 'as is' (no tranformation LUT -> RGB) PixelElement->SetLength( ImageDataSizeRaw ); } else if ( PixelRead == 0 ) { + // we tranformed GrayLevel pixels + LUT into RGB Pixel PixelElement->SetLength( ImageDataSize ); } - - //PixelElement->SetPrintLevel(2); - //PixelElement->Print(); + Header->Write(fp1, type); // -------------------------------------------------------------- @@ -835,7 +898,7 @@ bool gdcmFile::WriteBase (std::string const & fileName, FileType type) } // ----------------- End of Special Patch ---------------- - // fwrite(PixelData, ImageDataSize, 1, fp1); // should be useless, now + // fwrite(Pixel_Data, ImageDataSize, 1, fp1); // should be useless, now fclose (fp1); return true; @@ -871,7 +934,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: @@ -1000,7 +1063,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; } @@ -1080,7 +1143,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) @@ -1098,12 +1161,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) }