X-Git-Url: https://git.creatis.insa-lyon.fr/pubgit/?a=blobdiff_plain;f=src%2FgdcmPixelConvert.cxx;h=2afdb86c089c44217172bbf3344103314c46f655;hb=80f7a0ffbc39a8a7c47c2a8c6c9a272515d55d6f;hp=330e0f823cef9139cc89b6548af32b7b5be92d94;hpb=91e773ef94e63d181ea8e2a322ced8bf0427aa27;p=gdcm.git diff --git a/src/gdcmPixelConvert.cxx b/src/gdcmPixelConvert.cxx index 330e0f82..2afdb86c 100644 --- a/src/gdcmPixelConvert.cxx +++ b/src/gdcmPixelConvert.cxx @@ -3,8 +3,8 @@ Program: gdcm Module: $RCSfile: gdcmPixelConvert.cxx,v $ Language: C++ - Date: $Date: 2004/10/14 22:35:02 $ - Version: $Revision: 1.13 $ + Date: $Date: 2004/10/22 13:56:46 $ + Version: $Revision: 1.20 $ Copyright (c) CREATIS (Centre de Recherche et d'Applications en Traitement de l'Image). All rights reserved. See Doc/License.txt or @@ -24,6 +24,7 @@ #include "gdcmDebug.h" #include "gdcmPixelConvert.h" +#include namespace gdcm { @@ -31,18 +32,18 @@ namespace gdcm #define str2num(str, typeNum) *((typeNum *)(str)) // For JPEG 2000, body in file gdcmJpeg2000.cxx -bool gdcm_read_JPEG2000_file (FILE* fp, void* image_buffer); +bool gdcm_read_JPEG2000_file (std::ifstream* fp, void* image_buffer); // For JPEG 8 Bits, body in file gdcmJpeg8.cxx -bool gdcm_read_JPEG_file8 (FILE* fp, void* image_buffer); +bool gdcm_read_JPEG_file8 (std::ifstream* fp, void* image_buffer); // For JPEG 12 Bits, body in file gdcmJpeg12.cxx -bool gdcm_read_JPEG_file12 (FILE* fp, void* image_buffer); +bool gdcm_read_JPEG_file12 (std::ifstream* fp, void* image_buffer); // For JPEG 16 Bits, body in file gdcmJpeg16.cxx // Beware this is misleading there is no 16bits DCT algorithm, only // jpeg lossless compression exist in 16bits. -bool gdcm_read_JPEG_file16 (FILE* fp, void* image_buffer); +bool gdcm_read_JPEG_file16 (std::ifstream* fp, void* image_buffer); //----------------------------------------------------------------------------- @@ -53,16 +54,26 @@ PixelConvert::PixelConvert() RGBSize = 0; Decompressed = 0; DecompressedSize = 0; + LutRGBA = 0; + LutRedData = 0; + LutGreenData = 0; + LutBlueData =0; } void PixelConvert::Squeeze() { - if ( RGB ) { + if ( RGB ) + { delete [] RGB; } - if ( Decompressed ) { + if ( Decompressed ) + { delete [] Decompressed; } + if ( LutRGBA ) + { + delete [] LutRGBA; + } } PixelConvert::~PixelConvert() @@ -75,7 +86,7 @@ void PixelConvert::AllocateRGB() if ( RGB ) { delete [] RGB; } - RGB = new uint8_t[RGBSize]; + RGB = new uint8_t[ RGBSize ]; } void PixelConvert::AllocateDecompressed() @@ -87,40 +98,37 @@ void PixelConvert::AllocateDecompressed() } /** - * \brief Read from file a 12 bits per pixel image and uncompress it + * \brief Read from file a 12 bits per pixel image and decompress it * into a 16 bits per pixel image. */ -void PixelConvert::ReadAndDecompress12BitsTo16Bits( - uint8_t* pixelZone, - FILE* filePtr) +void PixelConvert::ReadAndDecompress12BitsTo16Bits( std::ifstream* fp ) throw ( FormatError ) { int nbPixels = XSize * YSize; - uint16_t* destination = (uint16_t*)pixelZone; + uint16_t* localDecompres = (uint16_t*)Decompressed; for( int p = 0; p < nbPixels; p += 2 ) { uint8_t b0, b1, b2; - size_t ItemRead; - - ItemRead = fread( &b0, 1, 1, filePtr); - if ( ItemRead != 1 ) + + fp->read( (char*)&b0, 1); + if ( fp->fail() || fp->eof() )//Fp->gcount() == 1 { - throw FormatError( "File::ReadAndDecompress12BitsTo16Bits()", + throw FormatError( "PixelConvert::ReadAndDecompress12BitsTo16Bits()", "Unfound first block" ); } - - ItemRead = fread( &b1, 1, 1, filePtr); - if ( ItemRead != 1 ) + + fp->read( (char*)&b1, 1 ); + if ( fp->fail() || fp->eof())//Fp->gcount() == 1 { - throw FormatError( "File::ReadAndDecompress12BitsTo16Bits()", + throw FormatError( "PixelConvert::ReadAndDecompress12BitsTo16Bits()", "Unfound second block" ); } - - ItemRead = fread( &b2, 1, 1, filePtr); - if ( ItemRead != 1 ) + + fp->read( (char*)&b2, 1 ); + if ( fp->fail() || fp->eof())//Fp->gcount() == 1 { - throw FormatError( "File::ReadAndDecompress12BitsTo16Bits()", + throw FormatError( "PixelConvert::ReadAndDecompress12BitsTo16Bits()", "Unfound second block" ); } @@ -128,10 +136,10 @@ void PixelConvert::ReadAndDecompress12BitsTo16Bits( // // 2 pixels 12bit = [0xABCDEF] // 2 pixels 16bit = [0x0ABD] + [0x0FCE] - // A B D - *destination++ = ((b0 >> 4) << 8) + ((b0 & 0x0f) << 4) + (b1 & 0x0f); - // F C E - *destination++ = ((b2 & 0x0f) << 8) + ((b1 >> 4) << 4) + (b2 >> 4); + // A B D + *localDecompres++ = ((b0 >> 4) << 8) + ((b0 & 0x0f) << 4) + (b1 & 0x0f); + // F C E + *localDecompres++ = ((b2 & 0x0f) << 8) + ((b1 >> 4) << 4) + (b2 >> 4); /// \todo JPR Troubles expected on Big-Endian processors ? } @@ -140,28 +148,26 @@ void PixelConvert::ReadAndDecompress12BitsTo16Bits( /** * \brief Try to deal with RLE 16 Bits. * We assume the RLE has allready been parsed and loaded in - * Uncompressed (through \ref ReadAndDecompressJPEGFile ). + * Decompressed (through \ref ReadAndDecompressJPEGFile ). * We here need to make 16 Bits Pixels from Low Byte and * High Byte 'Planes'...(for what it may mean) * @return Boolean */ -bool PixelConvert::UncompressRLE16BitsFromRLE8Bits( - int NumberOfFrames, - uint8_t* fixMemUncompressed ) +bool PixelConvert::DecompressRLE16BitsFromRLE8Bits( int NumberOfFrames ) { size_t PixelNumber = XSize * YSize; - size_t fixMemUncompressedSize = XSize * YSize * NumberOfFrames; + size_t decompressedSize = XSize * YSize * NumberOfFrames; - // We assumed Uncompressed contains the decoded RLE pixels but as + // We assumed Decompressed contains the decoded RLE pixels but as // 8 bits per pixel. In order to convert those pixels to 16 bits - // per pixel we cannot work in place within Uncompressed and hence - // we copy Uncompressed in a safe place, say OldUncompressed. + // per pixel we cannot work in place within Decompressed and hence + // we copy it in a safe place, say copyDecompressed. - uint8_t* OldUncompressed = new uint8_t[ fixMemUncompressedSize * 2 ]; - memmove( OldUncompressed, fixMemUncompressed, fixMemUncompressedSize * 2); + uint8_t* copyDecompressed = new uint8_t[ decompressedSize * 2 ]; + memmove( copyDecompressed, Decompressed, decompressedSize * 2 ); - uint8_t* x = fixMemUncompressed; - uint8_t* a = OldUncompressed; + uint8_t* x = Decompressed; + uint8_t* a = copyDecompressed; uint8_t* b = a + PixelNumber; for ( int i = 0; i < NumberOfFrames; i++ ) @@ -173,28 +179,35 @@ bool PixelConvert::UncompressRLE16BitsFromRLE8Bits( } } - delete[] OldUncompressed; + delete[] copyDecompressed; /// \todo check that operator new []didn't fail, and sometimes return false return true; } /** - * \brief Implementation of the RLE decoding algorithm for uncompressing + * \brief Implementation of the RLE decoding algorithm for decompressing * a RLE fragment. [refer to PS 3.5-2003, section G.3.2 p 86] + * @param subDecompressed Sub region of \ref Decompressed where the de + * decoded fragment should be placed. + * @param fragmentSize The length of the binary fragment as found on the disk. + * @param decompressedSegmentSize The expected length of the fragment ONCE + * decompressed. + * @param fp File Pointer: on entry the position should be the one of + * the fragment to be decoded. */ -bool PixelConvert::ReadAndUncompressRLEFragment( uint8_t* decodedZone, - long fragmentSize, - long uncompressedSegmentSize, - FILE* fp ) +bool PixelConvert::ReadAndDecompressRLEFragment( uint8_t* subDecompressed, + long fragmentSize, + long decompressedSegmentSize, + std::ifstream* fp ) { int8_t count; long numberOfOutputBytes = 0; long numberOfReadBytes = 0; - while( numberOfOutputBytes < uncompressedSegmentSize ) + while( numberOfOutputBytes < decompressedSegmentSize ) { - fread( &count, 1, 1, fp ); + fp->read( (char*)&count, 1 ); numberOfReadBytes += 1; if ( count >= 0 ) // Note: count <= 127 comparison is always true due to limited range @@ -202,9 +215,9 @@ bool PixelConvert::ReadAndUncompressRLEFragment( uint8_t* decodedZone, // signed integer of width N is 2^(N-1) - 1, which for int8_t // is 127]. { - fread( decodedZone, count + 1, 1, fp); - numberOfReadBytes += count + 1; - decodedZone += count + 1; + fp->read( (char*)subDecompressed, count + 1); + numberOfReadBytes += count + 1; + subDecompressed += count + 1; numberOfOutputBytes += count + 1; } else @@ -212,13 +225,13 @@ bool PixelConvert::ReadAndUncompressRLEFragment( uint8_t* decodedZone, if ( ( count <= -1 ) && ( count >= -127 ) ) { int8_t newByte; - fread( &newByte, 1, 1, fp); + fp->read( (char*)&newByte, 1); numberOfReadBytes += 1; for( int i = 0; i < -count + 1; i++ ) { - decodedZone[i] = newByte; + subDecompressed[i] = newByte; } - decodedZone += -count + 1; + subDecompressed += -count + 1; numberOfOutputBytes += -count + 1; } } @@ -226,8 +239,8 @@ bool PixelConvert::ReadAndUncompressRLEFragment( uint8_t* decodedZone, if ( numberOfReadBytes > fragmentSize ) { - dbg.Verbose(0, "File::gdcm_read_RLE_fragment: we read more " - "bytes than the segment size."); + dbg.Verbose(0, "PixelConvert::ReadAndDecompressRLEFragment: we " + "read more bytes than the segment size."); return false; } } @@ -236,18 +249,15 @@ bool PixelConvert::ReadAndUncompressRLEFragment( uint8_t* decodedZone, /** * \brief Reads from disk the Pixel Data of 'Run Length Encoded' - * Dicom encapsulated file and uncompress it. + * Dicom encapsulated file and decompress it. * @param fp already open File Pointer - * @param image_buffer destination Address (in caller's memory space) * at which the pixel data should be copied * @return Boolean */ -bool PixelConvert::ReadAndDecompressRLEFile( - void* image_buffer, - FILE* fp ) +bool PixelConvert::ReadAndDecompressRLEFile( std::ifstream* fp ) { - uint8_t* im = (uint8_t*)image_buffer; - long uncompressedSegmentSize = XSize * YSize; + uint8_t* subDecompressed = Decompressed; + long decompressedSegmentSize = XSize * YSize; // Loop on the frame[s] for( RLEFramesInfo::RLEFrameList::iterator @@ -256,38 +266,37 @@ bool PixelConvert::ReadAndDecompressRLEFile( ++it ) { // Loop on the fragments - for( int k = 1; k <= (*it)->NumberFragments; k++ ) + for( unsigned int k = 1; k <= (*it)->NumberFragments; k++ ) { - fseek( fp, (*it)->Offset[k] ,SEEK_SET ); - (void)PixelConvert::ReadAndUncompressRLEFragment( - (uint8_t*) im, (*it)->Length[k], - uncompressedSegmentSize, fp ); - im += uncompressedSegmentSize; + //fseek( fp, (*it)->Offset[k] ,SEEK_SET ); + fp->seekg( (*it)->Offset[k] , std::ios_base::beg ); + (void)ReadAndDecompressRLEFragment( subDecompressed, + (*it)->Length[k], + decompressedSegmentSize, + fp ); + subDecompressed += decompressedSegmentSize; } } if ( BitsAllocated == 16 ) { // Try to deal with RLE 16 Bits - (void)UncompressRLE16BitsFromRLE8Bits( ZSize, - (uint8_t*) image_buffer); + (void)DecompressRLE16BitsFromRLE8Bits( ZSize ); } return true; } /** - * \brief Swap the bytes, according to swap code. - * \warning not end user intended - * @param im area to deal with + * \brief Swap the bytes, according to \ref SwapCode. */ -void PixelConvert::SwapZone( uint8_t* im ) +void PixelConvert::ConvertSwapZone() { unsigned int i; if( BitsAllocated == 16 ) { - uint16_t* im16 = (uint16_t*)im; + uint16_t* im16 = (uint16_t*)Decompressed; switch( SwapCode ) { case 0: @@ -304,15 +313,16 @@ void PixelConvert::SwapZone( uint8_t* im ) } break; default: - dbg.Verbose( 0, "PixelConvert::SwapZone: SwapCode value " + dbg.Verbose( 0, "PixelConvert::ConvertSwapZone: SwapCode value " "(16 bits) not allowed." ); } } else if( BitsAllocated == 32 ) { uint32_t s32; - uint16_t fort, faible; - uint32_t* im32 = (uint32_t*)im; + uint16_t high; + uint16_t low; + uint32_t* im32 = (uint32_t*)Decompressed; switch ( SwapCode ) { case 0: @@ -321,36 +331,36 @@ void PixelConvert::SwapZone( uint8_t* im ) case 4321: for( i = 0; i < DecompressedSize / 4; i++ ) { - faible = im32[i] & 0x0000ffff; // 4321 - fort = im32[i] >> 16; - fort = ( fort >> 8 ) | ( fort << 8 ); - faible = ( faible >> 8 ) | ( faible << 8); - s32 = faible; - im32[i] = ( s32 << 16 ) | fort; + low = im32[i] & 0x0000ffff; // 4321 + high = im32[i] >> 16; + high = ( high >> 8 ) | ( high << 8 ); + low = ( low >> 8 ) | ( low << 8 ); + s32 = low; + im32[i] = ( s32 << 16 ) | high; } break; case 2143: for( i = 0; i < DecompressedSize / 4; i++ ) { - faible = im32[i] & 0x0000ffff; // 2143 - fort = im32[i] >> 16; - fort = ( fort >> 8 ) | ( fort << 8 ); - faible = ( faible >> 8) | ( faible << 8); - s32 = fort; - im32[i] = ( s32 << 16 ) | faible; + low = im32[i] & 0x0000ffff; // 2143 + high = im32[i] >> 16; + high = ( high >> 8 ) | ( high << 8 ); + low = ( low >> 8 ) | ( low << 8 ); + s32 = high; + im32[i] = ( s32 << 16 ) | low; } break; case 3412: for( i = 0; i < DecompressedSize / 4; i++ ) { - faible = im32[i] & 0x0000ffff; // 3412 - fort = im32[i] >> 16; - s32 = faible; - im32[i] = ( s32 << 16 ) | fort; + low = im32[i] & 0x0000ffff; // 3412 + high = im32[i] >> 16; + s32 = low; + im32[i] = ( s32 << 16 ) | high; } break; default: - dbg.Verbose( 0, "PixelConvert::SwapZone: SwapCode value " + dbg.Verbose( 0, "PixelConvert::ConvertSwapZone: SwapCode value " "(32 bits) not allowed." ); } } @@ -359,11 +369,11 @@ void PixelConvert::SwapZone( uint8_t* im ) /** * \brief Deal with endianity i.e. re-arange bytes inside the integer */ -void PixelConvert::ReorderEndianity( uint8_t* pixelZone ) +void PixelConvert::ConvertReorderEndianity() { if ( BitsAllocated != 8 ) { - SwapZone( pixelZone ); + ConvertSwapZone(); } // Special kludge in order to deal with xmedcon broken images: @@ -372,7 +382,7 @@ void PixelConvert::ReorderEndianity( uint8_t* pixelZone ) && ( ! PixelSign ) ) { int l = (int)( DecompressedSize / ( BitsAllocated / 8 ) ); - uint16_t *deb = (uint16_t *)pixelZone; + uint16_t *deb = (uint16_t *)Decompressed; for(int i = 0; iFragments.begin(); it != JPEGInfo->Fragments.end(); ++it ) { - fseek( fp, (*it)->Offset, SEEK_SET ); + //fseek( fp, (*it)->Offset, SEEK_SET ); + fp->seekg( (*it)->Offset, std::ios_base::beg); if ( IsJPEG2000 ) { - if ( ! gdcm_read_JPEG2000_file( fp, destination ) ) + if ( ! gdcm_read_JPEG2000_file( fp,localDecompressed ) ) { return false; } @@ -413,7 +422,7 @@ bool PixelConvert::ReadAndDecompressJPEGFile( else if ( BitsStored == 8) { // JPEG Lossy : call to IJG 6b - if ( ! gdcm_read_JPEG_file8( fp, destination ) ) + if ( ! gdcm_read_JPEG_file8( fp, localDecompressed ) ) { return false; } @@ -421,7 +430,7 @@ bool PixelConvert::ReadAndDecompressJPEGFile( else if ( BitsStored == 12) { // Reading Fragment pixels - if ( ! gdcm_read_JPEG_file12 ( fp, destination ) ) + if ( ! gdcm_read_JPEG_file12 ( fp, localDecompressed ) ) { return false; } @@ -429,7 +438,7 @@ bool PixelConvert::ReadAndDecompressJPEGFile( else if ( BitsStored == 16) { // Reading Fragment pixels - if ( ! gdcm_read_JPEG_file16 ( fp, destination ) ) + if ( ! gdcm_read_JPEG_file16 ( fp, localDecompressed ) ) { return false; } @@ -438,28 +447,26 @@ bool PixelConvert::ReadAndDecompressJPEGFile( else { // other JPEG lossy not supported - dbg.Error(" File::ReadAndDecompressJPEGFile: unknown jpeg lossy " - " compression "); + dbg.Error("PixelConvert::ReadAndDecompressJPEGFile: unknown " + "jpeg lossy compression "); return false; } - // Advance to next free location in destination + // Advance to next free location in Decompressed // for next fragment decompression (if any) int length = XSize * YSize * SamplesPerPixel; int numberBytes = BitsAllocated / 8; - - destination += length * numberBytes; + + localDecompressed += length * numberBytes; } return true; } /** * \brief Re-arrange the bits within the bytes. - * @param pixelZone zone * @return Boolean */ -bool PixelConvert::ReArrangeBits( uint8_t* pixelZone ) - throw ( FormatError ) +bool PixelConvert::ConvertReArrangeBits() throw ( FormatError ) { if ( BitsStored != BitsAllocated ) { @@ -468,7 +475,7 @@ bool PixelConvert::ReArrangeBits( uint8_t* pixelZone ) { uint16_t mask = 0xffff; mask = mask >> ( BitsAllocated - BitsStored ); - uint16_t* deb = (uint16_t*)pixelZone; + uint16_t* deb = (uint16_t*)Decompressed; for(int i = 0; i> (BitsStored - HighBitPosition - 1)) & mask; @@ -479,7 +486,7 @@ bool PixelConvert::ReArrangeBits( uint8_t* pixelZone ) { uint32_t mask = 0xffffffff; mask = mask >> ( BitsAllocated - BitsStored ); - uint32_t* deb = (uint32_t*)pixelZone; + uint32_t* deb = (uint32_t*)Decompressed; for(int i = 0; i> (BitsStored - HighBitPosition - 1)) & mask; @@ -488,8 +495,8 @@ bool PixelConvert::ReArrangeBits( uint8_t* pixelZone ) } else { - dbg.Verbose(0, "PixelConvert::ReArrangeBits: weird image"); - throw FormatError( "File::ReArrangeBits()", + dbg.Verbose(0, "PixelConvert::ConvertReArrangeBits: weird image"); + throw FormatError( "PixelConvert::ConvertReArrangeBits()", "weird image !?" ); } } @@ -500,10 +507,11 @@ bool PixelConvert::ReArrangeBits( uint8_t* pixelZone ) * \brief Convert (Y plane, cB plane, cR plane) to RGB pixels * \warning Works on all the frames at a time */ -void PixelConvert::ConvertYcBcRPlanesToRGBPixels( uint8_t* destination ) +void PixelConvert::ConvertYcBcRPlanesToRGBPixels() { - uint8_t* oldPixelZone = new uint8_t[ DecompressedSize ]; - memmove( oldPixelZone, destination, DecompressedSize ); + uint8_t* localDecompressed = Decompressed; + uint8_t* copyDecompressed = new uint8_t[ DecompressedSize ]; + memmove( copyDecompressed, localDecompressed, DecompressedSize ); // to see the tricks about YBR_FULL, YBR_FULL_422, // YBR_PARTIAL_422, YBR_ICT, YBR_RCT have a look at : @@ -513,9 +521,9 @@ void PixelConvert::ConvertYcBcRPlanesToRGBPixels( uint8_t* destination ) int l = XSize * YSize; int nbFrames = ZSize; - uint8_t* a = oldPixelZone; - uint8_t* b = oldPixelZone + l; - uint8_t* c = oldPixelZone + l + l; + uint8_t* a = copyDecompressed; + uint8_t* b = copyDecompressed + l; + uint8_t* c = copyDecompressed + l + l; double R, G, B; /// \todo : Replace by the 'well known' integer computation @@ -538,43 +546,46 @@ void PixelConvert::ConvertYcBcRPlanesToRGBPixels( uint8_t* destination ) if (G > 255.0) G = 255.0; if (B > 255.0) B = 255.0; - *(destination++) = (uint8_t)R; - *(destination++) = (uint8_t)G; - *(destination++) = (uint8_t)B; + *(localDecompressed++) = (uint8_t)R; + *(localDecompressed++) = (uint8_t)G; + *(localDecompressed++) = (uint8_t)B; a++; b++; c++; } } - delete[] oldPixelZone; + delete[] copyDecompressed; } /** * \brief Convert (Red plane, Green plane, Blue plane) to RGB pixels * \warning Works on all the frames at a time */ -void PixelConvert::ConvertRGBPlanesToRGBPixels( uint8_t* destination ) +void PixelConvert::ConvertRGBPlanesToRGBPixels() { - uint8_t* oldPixelZone = new uint8_t[ DecompressedSize ]; - memmove( oldPixelZone, destination, DecompressedSize ); + uint8_t* localDecompressed = Decompressed; + uint8_t* copyDecompressed = new uint8_t[ DecompressedSize ]; + memmove( copyDecompressed, localDecompressed, DecompressedSize ); int l = XSize * YSize * ZSize; - uint8_t* a = oldPixelZone; - uint8_t* b = oldPixelZone + l; - uint8_t* c = oldPixelZone + l + l; + uint8_t* a = copyDecompressed; + uint8_t* b = copyDecompressed + l; + uint8_t* c = copyDecompressed + l + l; for (int j = 0; j < l; j++) { - *(destination++) = *(a++); - *(destination++) = *(b++); - *(destination++) = *(c++); + *(localDecompressed++) = *(a++); + *(localDecompressed++) = *(b++); + *(localDecompressed++) = *(c++); } - delete[] oldPixelZone; + delete[] copyDecompressed; } -bool PixelConvert::ReadAndDecompressPixelData( void* destination, FILE* fp ) +bool PixelConvert::ReadAndDecompressPixelData( std::ifstream* fp ) { + ComputeDecompressedAndRGBSizes(); + AllocateDecompressed(); ////////////////////////////////////////////////// //// First stage: get our hands on the Pixel Data. if ( !fp ) @@ -584,7 +595,9 @@ bool PixelConvert::ReadAndDecompressPixelData( void* destination, FILE* fp ) return false; } - if ( fseek(fp, PixelOffset, SEEK_SET) == -1 ) + //if ( fseek( fp, PixelOffset, SEEK_SET ) == -1 ) + fp->seekg( PixelOffset, std::ios_base::beg ); + if( fp->fail() || fp->eof()) //Fp->gcount() == 1 { dbg.Verbose( 0, "PixelConvert::ReadAndDecompressPixelData: " "unable to find PixelOffset in file." ); @@ -592,24 +605,24 @@ bool PixelConvert::ReadAndDecompressPixelData( void* destination, FILE* fp ) } ////////////////////////////////////////////////// - //// Second stage: read from disk dans uncompress. + //// Second stage: read from disk dans decompress. if ( BitsAllocated == 12 ) { - ReadAndDecompress12BitsTo16Bits( (uint8_t*)destination, fp); + ReadAndDecompress12BitsTo16Bits( fp); } - else if ( IsUncompressed ) + else if ( IsDecompressed ) { - size_t ItemRead = fread( destination, PixelDataLength, 1, fp); - if ( ItemRead != 1 ) + fp->read( (char*)Decompressed, PixelDataLength); + if ( fp->fail() || fp->eof())//Fp->gcount() == 1 { dbg.Verbose( 0, "PixelConvert::ReadAndDecompressPixelData: " - "reading of uncompressed pixel data failed." ); + "reading of decompressed pixel data failed." ); return false; } } else if ( IsRLELossless ) { - if ( ! ReadAndDecompressRLEFile( destination, fp ) ) + if ( ! ReadAndDecompressRLEFile( fp ) ) { dbg.Verbose( 0, "PixelConvert::ReadAndDecompressPixelData: " "RLE decompressor failed." ); @@ -619,7 +632,7 @@ bool PixelConvert::ReadAndDecompressPixelData( void* destination, FILE* fp ) else { // Default case concerns JPEG family - if ( ! ReadAndDecompressJPEGFile( (uint8_t*)destination, fp ) ) + if ( ! ReadAndDecompressJPEGFile( fp ) ) { dbg.Verbose( 0, "PixelConvert::ReadAndDecompressPixelData: " "JPEG decompressor failed." ); @@ -629,13 +642,14 @@ bool PixelConvert::ReadAndDecompressPixelData( void* destination, FILE* fp ) //////////////////////////////////////////// //// Third stage: twigle the bytes and bits. - ReorderEndianity( (uint8_t*) destination ); - ReArrangeBits( (uint8_t*) destination ); + ConvertReorderEndianity(); + ConvertReArrangeBits(); + ConvertHandleColor(); return true; } -bool PixelConvert::HandleColor( uint8_t* destination ) +void PixelConvert::ConvertHandleColor() { ////////////////////////////////// // Deal with the color decoding i.e. handle: @@ -675,12 +689,10 @@ bool PixelConvert::HandleColor( uint8_t* destination ) // - [Planar 1] AND [Photo C] handled with ConvertYcBcRPlanesToRGBPixels() // - [Planar 2] OR [Photo D] requires LUT intervention. - if ( IsMonochrome - || ( PlanarConfiguration == 2 ) - || IsPaletteColor ) + if ( ! IsDecompressedRGB() ) { // [Planar 2] OR [Photo D]: LUT intervention done outside - return false; + return; } if ( PlanarConfiguration == 1 ) @@ -688,22 +700,36 @@ bool PixelConvert::HandleColor( uint8_t* destination ) if ( IsYBRFull ) { // [Planar 1] AND [Photo C] (remember YBR_FULL_422 acts as RGB) - ConvertYcBcRPlanesToRGBPixels( (uint8_t*)destination ); + ConvertYcBcRPlanesToRGBPixels(); } else { // [Planar 1] AND [Photo C] - ConvertRGBPlanesToRGBPixels( (uint8_t*)destination ); + ConvertRGBPlanesToRGBPixels(); } } // When planarConf is 0, pixels are allready in RGB +} + +/** + * \brief Predicate to know wether the image[s] (once decompressed) is RGB. + * \note See comments of \ref ConvertHandleColor + */ +bool PixelConvert::IsDecompressedRGB() +{ + if ( IsMonochrome + || ( PlanarConfiguration == 2 ) + || IsPaletteColor ) + { + return false; + } return true; } -void PixelConvert::ComputeDecompressedImageDataSize() +void PixelConvert::ComputeDecompressedAndRGBSizes() { - int bitsAllocated; + int bitsAllocated = BitsAllocated; // Number of "Bits Allocated" is fixed to 16 when it's 12, since // in this case we will expand the image to 16 bits (see // \ref ReadAndDecompress12BitsTo16Bits() ) @@ -712,9 +738,372 @@ void PixelConvert::ComputeDecompressedImageDataSize() bitsAllocated = 16; } - DecompressedSize = XSize * YSize * ZSize - * ( bitsAllocated / 8 ) - * SamplesPerPixel; + DecompressedSize = XSize * YSize * ZSize + * ( bitsAllocated / 8 ) + * SamplesPerPixel; + if ( HasLUT ) + { + RGBSize = 3 * DecompressedSize; + } + +} + +void PixelConvert::GrabInformationsFromHeader( Header* header ) +{ + // Just in case some access to a Header element requires disk access. + // Note: gdcmDocument::Fp is leaved open after OpenFile. + std::ifstream* fp = header->OpenFile(); + // Number of Bits Allocated for storing a Pixel is defaulted to 16 + // when absent from the header. + BitsAllocated = header->GetBitsAllocated(); + if ( BitsAllocated == 0 ) + { + BitsAllocated = 16; + } + + // Number of "Bits Stored" defaulted to number of "Bits Allocated" + // when absent from the header. + BitsStored = header->GetBitsStored(); + if ( BitsStored == 0 ) + { + BitsStored = BitsAllocated; + } + + // High Bit Position + HighBitPosition = header->GetHighBitPosition(); + if ( HighBitPosition == 0 ) + { + HighBitPosition = BitsAllocated - 1; + } + + XSize = header->GetXSize(); + YSize = header->GetYSize(); + ZSize = header->GetZSize(); + SamplesPerPixel = header->GetSamplesPerPixel(); + PixelSize = header->GetPixelSize(); + PixelSign = header->IsSignedPixelData(); + SwapCode = header->GetSwapCode(); + TransferSyntaxType ts = header->GetTransferSyntax(); + IsDecompressed = + ( ! header->IsDicomV3() ) + || ts == ImplicitVRLittleEndian + || ts == ExplicitVRLittleEndian + || ts == ExplicitVRBigEndian + || ts == DeflatedExplicitVRLittleEndian; + IsJPEG2000 = header->IsJPEG2000(); + IsJPEGLossless = header->IsJPEGLossless(); + IsRLELossless = ( ts == RLELossless ); + PixelOffset = header->GetPixelOffset(); + PixelDataLength = header->GetPixelAreaLength(); + RLEInfo = header->GetRLEInfo(); + JPEGInfo = header->GetJPEGInfo(); + + PlanarConfiguration = header->GetPlanarConfiguration(); + IsMonochrome = header->IsMonochrome(); + IsPaletteColor = header->IsPaletteColor(); + IsYBRFull = header->IsYBRFull(); + + ///////////////////////////////////////////////////////////////// + // LUT section: + HasLUT = header->HasLUT(); + if ( HasLUT ) + { + LutRedDescriptor = header->GetEntryByNumber( 0x0028, 0x1101 ); + LutGreenDescriptor = header->GetEntryByNumber( 0x0028, 0x1102 ); + LutBlueDescriptor = header->GetEntryByNumber( 0x0028, 0x1103 ); + + // Depending on the value of Document::MAX_SIZE_LOAD_ELEMENT_VALUE + // [ refer to invocation of Document::SetMaxSizeLoadEntry() in + // Document::Document() ], the loading of the value (content) of a + // [Bin|Val]Entry occurence migth have been hindered (read simply NOT + // loaded). Hence, we first try to obtain the LUTs data from the header + // and when this fails we read the LUTs data directely from disk. + /// \todo Reading a [Bin|Val]Entry directly from disk is a kludge. + /// We should NOT bypass the [Bin|Val]Entry class. Instead + /// an access to an UNLOADED content of a [Bin|Val]Entry occurence + /// (e.g. BinEntry::GetBinArea()) should force disk access from + /// within the [Bin|Val]Entry class itself. The only problem + /// is that the [Bin|Val]Entry is unaware of the FILE* is was + /// parsed from. Fix that. FIXME. + + ////// Red round: + LutRedData = (uint8_t*)header->GetEntryBinAreaByNumber( 0x0028, 0x1201 ); + if ( ! LutRedData ) + { + // Read the Lut Data from disk + DocEntry* lutRedDataEntry = header->GetDocEntryByNumber( 0x0028, + 0x1201 ); + LutRedData = new uint8_t[ lutRedDataEntry->GetLength() ]; + //fseek( fp, lutRedDataEntry->GetOffset() ,SEEK_SET ); + fp->seekg( lutRedDataEntry->GetOffset() ,std::ios_base::beg ); + fp->read( (char*)LutRedData, (size_t)lutRedDataEntry->GetLength()); + //if ( numberItem != 1 ) + if ( fp->fail() || fp->eof())//Fp->gcount() == 1 + { + dbg.Verbose(0, "PixelConvert::GrabInformationsFromHeader: " + "unable to read red LUT data" ); + return; + } + } + + ////// Green round: + LutGreenData = (uint8_t*)header->GetEntryBinAreaByNumber(0x0028, 0x1202 ); + if ( ! LutGreenData) + { + // Read the Lut Data from disk + DocEntry* lutGreenDataEntry = header->GetDocEntryByNumber( 0x0028, + 0x1202 ); + LutGreenData = new uint8_t[ lutGreenDataEntry->GetLength() ]; + //fseek( fp, lutGreenDataEntry->GetOffset() ,SEEK_SET ); + fp->seekg( lutGreenDataEntry->GetOffset() , std::ios_base::beg ); + fp->read( (char*)LutGreenData, (size_t)lutGreenDataEntry->GetLength() ); + //if ( numberItem != 1 ) + if ( fp->fail() || fp->eof())//Fp->gcount() == 1 + { + dbg.Verbose(0, "PixelConvert::GrabInformationsFromHeader: " + "unable to read green LUT data" ); + return; + } + } + + ////// Blue round: + LutBlueData = (uint8_t*)header->GetEntryBinAreaByNumber( 0x0028, 0x1203 ); + if ( ! LutBlueData ) + { + // Read the Lut Data from disk + DocEntry* lutBlueDataEntry = header->GetDocEntryByNumber( 0x0028, + 0x1203 ); + LutBlueData = new uint8_t[ lutBlueDataEntry->GetLength() ]; + //fseek( fp, lutBlueDataEntry->GetOffset() ,SEEK_SET ); + fp->seekg( lutBlueDataEntry->GetOffset() , std::ios_base::beg ); + fp->read( (char*)LutBlueData, (size_t)lutBlueDataEntry->GetLength() ); + //if ( numberItem != 1 ) + if ( fp->fail() || fp->eof())//Fp->gcount() == 1 + { + dbg.Verbose(0, "PixelConvert::GrabInformationsFromHeader: " + "unable to read blue LUT data" ); + return; + } + } + } + + header->CloseFile(); +} + +/** + * \brief Build Red/Green/Blue/Alpha LUT from Header + * when (0028,0004),Photometric Interpretation = [PALETTE COLOR ] + * and (0028,1101),(0028,1102),(0028,1102) + * - xxx Palette Color Lookup Table Descriptor - are found + * and (0028,1201),(0028,1202),(0028,1202) + * - xxx Palette Color Lookup Table Data - are found + * \warning does NOT deal with : + * 0028 1100 Gray Lookup Table Descriptor (Retired) + * 0028 1221 Segmented Red Palette Color Lookup Table Data + * 0028 1222 Segmented Green Palette Color Lookup Table Data + * 0028 1223 Segmented Blue Palette Color Lookup Table Data + * no known Dicom reader deals with them :-( + * @return a RGBA Lookup Table + */ +void PixelConvert::BuildLUTRGBA() +{ + if ( LutRGBA ) + { + return; + } + // Not so easy : see + // http://www.barre.nom.fr/medical/dicom2/limitations.html#Color%20Lookup%20Tables + + if ( ! IsPaletteColor ) + { + return; + } + + if ( ( LutRedDescriptor == GDCM_UNFOUND ) + || ( LutGreenDescriptor == GDCM_UNFOUND ) + || ( LutBlueDescriptor == GDCM_UNFOUND ) ) + { + return; + } + + //////////////////////////////////////////// + // Extract the info from the LUT descriptors + int lengthR; // Red LUT length in Bytes + int debR; // Subscript of the first Lut Value + int nbitsR; // Lut item size (in Bits) + int nbRead = sscanf( LutRedDescriptor.c_str(), + "%d\\%d\\%d", + &lengthR, &debR, &nbitsR ); + if( nbRead != 3 ) + { + dbg.Verbose(0, "PixelConvert::BuildLUTRGBA: wrong red LUT descriptor"); + } + + int lengthG; // Green LUT length in Bytes + int debG; // Subscript of the first Lut Value + int nbitsG; // Lut item size (in Bits) + nbRead = sscanf( LutGreenDescriptor.c_str(), + "%d\\%d\\%d", + &lengthG, &debG, &nbitsG ); + if( nbRead != 3 ) + { + dbg.Verbose(0, "PixelConvert::BuildLUTRGBA: wrong green LUT descriptor"); + } + + int lengthB; // Blue LUT length in Bytes + int debB; // Subscript of the first Lut Value + int nbitsB; // Lut item size (in Bits) + nbRead = sscanf( LutRedDescriptor.c_str(), + "%d\\%d\\%d", + &lengthB, &debB, &nbitsB ); + if( nbRead != 3 ) + { + dbg.Verbose(0, "PixelConvert::BuildLUTRGBA: wrong blue LUT descriptor"); + } + + //////////////////////////////////////////////////////// + if ( ( ! LutRedData ) || ( ! LutGreenData ) || ( ! LutBlueData ) ) + { + return; + } + + //////////////////////////////////////////////// + // forge the 4 * 8 Bits Red/Green/Blue/Alpha LUT + LutRGBA = new uint8_t[ 1024 ]; // 256 * 4 (R, G, B, Alpha) + if ( !LutRGBA ) + { + return; + } + memset( LutRGBA, 0, 1024 ); + + int mult; + if ( ( nbitsR == 16 ) && ( BitsAllocated == 8 ) ) + { + // when LUT item size is different than pixel size + mult = 2; // high byte must be = low byte + } + else + { + // See PS 3.3-2003 C.11.1.1.2 p 619 + mult = 1; + } + + // if we get a black image, let's just remove the '+1' + // from 'i*mult+1' and check again + // if it works, we shall have to check the 3 Palettes + // to see which byte is ==0 (first one, or second one) + // and fix the code + // We give up the checking to avoid some (useless ?)overhead + // (optimistic asumption) + int i; + uint8_t* a = LutRGBA + 0; + for( i=0; i < lengthR; ++i ) + { + *a = LutRedData[i*mult+1]; + a += 4; + } + + a = LutRGBA + 1; + for( i=0; i < lengthG; ++i) + { + *a = LutGreenData[i*mult+1]; + a += 4; + } + + a = LutRGBA + 2; + for(i=0; i < lengthB; ++i) + { + *a = LutBlueData[i*mult+1]; + a += 4; + } + + a = LutRGBA + 3; + for(i=0; i < 256; ++i) + { + *a = 1; // Alpha component + a += 4; + } +} + +/** + * \brief Build the RGB image from the Decompressed imagage and the LUTs. + */ +bool PixelConvert::BuildRGBImage() +{ + if ( RGB ) + { + // The job is allready done. + return true; + } + + if ( ! Decompressed ) + { + // The job can't be done + return false; + } + + BuildLUTRGBA(); + if ( ! LutRGBA ) + { + // The job can't be done + return false; + } + + // Build RGB Pixels + AllocateRGB(); + uint8_t* localRGB = RGB; + for (size_t i = 0; i < DecompressedSize; ++i ) + { + int j = Decompressed[i] * 4; + *localRGB++ = LutRGBA[j]; + *localRGB++ = LutRGBA[j+1]; + *localRGB++ = LutRGBA[j+2]; + } + return true; +} + +/** + * \brief Print self. + * @param indent Indentation string to be prepended during printing. + * @param os Stream to print to. + */ +void PixelConvert::Print( std::string indent, std::ostream &os ) +{ + os << indent + << "--- Pixel information -------------------------" + << std::endl; + os << indent + << "Pixel Data: offset " << PixelOffset + << " x" << std::hex << PixelOffset << std::dec + << " length " << PixelDataLength + << " x" << std::hex << PixelDataLength << std::dec + << std::endl; + + if ( IsRLELossless ) + { + if ( RLEInfo ) + { + RLEInfo->Print( indent, os ); + } + else + { + dbg.Verbose(0, "PixelConvert::Print: set as RLE file " + "but NO RLEinfo present."); + } + } + + if ( IsJPEG2000 || IsJPEGLossless ) + { + if ( JPEGInfo ) + { + JPEGInfo->Print( indent, os ); + } + else + { + dbg.Verbose(0, "PixelConvert::Print: set as JPEG file " + "but NO JPEGinfo present."); + } + } } } // end namespace gdcm