X-Git-Url: https://git.creatis.insa-lyon.fr/pubgit/?a=blobdiff_plain;f=src%2FgdcmPixelReadConvert.cxx;h=9b97cbb171e525e98fc0ea176a36a55d2e208ddc;hb=fdca8d2cc7f1a716e591540de386c88dabbf6031;hp=1d1c01343ded8fa962503f9ed810180b8a7a8d99;hpb=3791e733902218574e4f819435360e4692518561;p=gdcm.git diff --git a/src/gdcmPixelReadConvert.cxx b/src/gdcmPixelReadConvert.cxx index 1d1c0134..9b97cbb1 100644 --- a/src/gdcmPixelReadConvert.cxx +++ b/src/gdcmPixelReadConvert.cxx @@ -3,8 +3,8 @@ Program: gdcm Module: $RCSfile: gdcmPixelReadConvert.cxx,v $ Language: C++ - Date: $Date: 2005/06/13 15:43:48 $ - Version: $Revision: 1.64 $ + Date: $Date: 2005/07/01 11:25:51 $ + Version: $Revision: 1.74 $ Copyright (c) CREATIS (Centre de Recherche et d'Applications en Traitement de l'Image). All rights reserved. See Doc/License.txt or @@ -50,6 +50,8 @@ PixelReadConvert::PixelReadConvert() LutRedData = 0; LutGreenData = 0; LutBlueData = 0; + RLEInfo = 0; + JPEGInfo = 0; } /// Canonical Destructor @@ -167,7 +169,7 @@ void PixelReadConvert::GrabInformationsFromFile( File *file ) LutRedData = (uint8_t*)file->GetEntryBinArea( 0x0028, 0x1201 ); if ( ! LutRedData ) { - gdcmWarningMacro( "Unable to read Red LUT data" ); + gdcmWarningMacro( "Unable to read Red Palette Color Lookup Table data" ); } // //// Green round: @@ -175,7 +177,7 @@ void PixelReadConvert::GrabInformationsFromFile( File *file ) LutGreenData = (uint8_t*)file->GetEntryBinArea(0x0028, 0x1202 ); if ( ! LutGreenData) { - gdcmWarningMacro( "Unable to read Green LUT data" ); + gdcmWarningMacro( "Unable to read Green Palette Color Lookup Table data" ); } // //// Blue round: @@ -183,7 +185,7 @@ void PixelReadConvert::GrabInformationsFromFile( File *file ) LutBlueData = (uint8_t*)file->GetEntryBinArea( 0x0028, 0x1203 ); if ( ! LutBlueData ) { - gdcmWarningMacro( "Unable to read Blue LUT data" ); + gdcmWarningMacro( "Unable to read Blue Palette Color Lookup Table data" ); } } @@ -207,7 +209,7 @@ bool PixelReadConvert::ReadAndDecompressPixelData( std::ifstream *fp ) } fp->seekg( PixelOffset, std::ios::beg ); - if( fp->fail() || fp->eof()) + if ( fp->fail() || fp->eof() ) { gdcmWarningMacro( "Unable to find PixelOffset in file." ); return false; @@ -216,7 +218,7 @@ bool PixelReadConvert::ReadAndDecompressPixelData( std::ifstream *fp ) AllocateRaw(); ////////////////////////////////////////////////// - //// Second stage: read from disk dans decompress. + //// Second stage: read from disk and decompress. if ( BitsAllocated == 12 ) { ReadAndDecompress12BitsTo16Bits( fp); @@ -227,12 +229,12 @@ bool PixelReadConvert::ReadAndDecompressPixelData( std::ifstream *fp ) // after the field containing the image data. In this case, these // bad data are added to the size of the image (in the PixelDataLength // variable). But RawSize is the right size of the image ! - if( PixelDataLength != RawSize) + if ( PixelDataLength != RawSize ) { gdcmWarningMacro( "Mismatch between PixelReadConvert : " << PixelDataLength << " and RawSize : " << RawSize ); } - if( PixelDataLength > RawSize) + if ( PixelDataLength > RawSize ) { fp->read( (char*)Raw, RawSize); } @@ -259,7 +261,7 @@ bool PixelReadConvert::ReadAndDecompressPixelData( std::ifstream *fp ) { //gdcmWarningMacro( "Sorry, MPEG not yet taken into account" ); //return false; -// ReadMPEGFile(fp, Raw, PixelDataLength); // fp has already been seek to start of mpeg + //ReadMPEGFile(fp, Raw, PixelDataLength); // fp has already been seek to start of mpeg return true; } else @@ -299,7 +301,7 @@ void PixelReadConvert::Squeeze() } /** - * \brief Build the RGB image from the Raw imagage and the LUTs. + * \brief Build the RGB image from the Raw image and the LUTs. */ bool PixelReadConvert::BuildRGBImage() { @@ -321,17 +323,37 @@ bool PixelReadConvert::BuildRGBImage() // The job can't be done return false; } + + gdcmWarningMacro( "--> BuildRGBImage" ); // Build RGB Pixels AllocateRGB(); - uint8_t *localRGB = RGB; - for (size_t i = 0; i < RawSize; ++i ) + + int j; + if ( BitsAllocated <= 8 ) { - int j = Raw[i] * 4; - *localRGB++ = LutRGBA[j]; - *localRGB++ = LutRGBA[j+1]; - *localRGB++ = LutRGBA[j+2]; - } + uint8_t *localRGB = RGB; + for (size_t i = 0; i < RawSize; ++i ) + { + j = Raw[i] * 4; + *localRGB++ = LutRGBA[j]; + *localRGB++ = LutRGBA[j+1]; + *localRGB++ = LutRGBA[j+2]; + } + } + + else // deal with 16 bits pixels and 16 bits Palette color + { + uint16_t *localRGB = (uint16_t *)RGB; + for (size_t i = 0; i < RawSize/2; ++i ) + { + j = ((uint16_t *)Raw)[i] * 4; + *localRGB++ = ((uint16_t *)LutRGBA)[j]; + *localRGB++ = ((uint16_t *)LutRGBA)[j+1]; + *localRGB++ = ((uint16_t *)LutRGBA)[j+2]; + } + } + return true; } @@ -398,9 +420,11 @@ bool PixelReadConvert::ReadAndDecompressJPEGFile( std::ifstream *fp ) { if ( IsJPEG2000 ) { + // make sure this is the right JPEG compression + assert( !IsJPEGLossless || !IsJPEGLossy || !IsJPEGLS ); // FIXME this is really ugly but it seems I have to load the complete // jpeg2000 stream to use jasper: - // I don't think we'll ever be able to deal with multiple fragments properly + // I don't think we'll ever be able to deal with multiple fragments properly unsigned long inputlength = 0; JPEGFragment *jpegfrag = JPEGInfo->GetFirstFragment(); @@ -426,10 +450,13 @@ bool PixelReadConvert::ReadAndDecompressJPEGFile( std::ifstream *fp ) { return true; } + // wow what happen, must be an error + return false; } - - if ( IsJPEGLS ) + else if ( IsJPEGLS ) { + // make sure this is the right JPEG compression + assert( !IsJPEGLossless || !IsJPEGLossy || !IsJPEG2000 ); // WARNING : JPEG-LS is NOT the 'classical' Jpeg Lossless : // [JPEG-LS is the basis for new lossless/near-lossless compression // standard for continuous-tone images intended for JPEG2000. The standard @@ -438,6 +465,34 @@ bool PixelReadConvert::ReadAndDecompressJPEGFile( std::ifstream *fp ) // // see http://datacompression.info/JPEGLS.shtml // +#if 0 + std::cerr << "count:" << JPEGInfo->GetFragmentCount() << std::endl; + unsigned long inputlength = 0; + JPEGFragment *jpegfrag = JPEGInfo->GetFirstFragment(); + while( jpegfrag ) + { + inputlength += jpegfrag->GetLength(); + jpegfrag = JPEGInfo->GetNextFragment(); + } + gdcmAssertMacro( inputlength != 0); + uint8_t *inputdata = new uint8_t[inputlength]; + char *pinputdata = (char*)inputdata; + jpegfrag = JPEGInfo->GetFirstFragment(); + while( jpegfrag ) + { + fp->seekg( jpegfrag->GetOffset(), std::ios::beg); + fp->read(pinputdata, jpegfrag->GetLength()); + pinputdata += jpegfrag->GetLength(); + jpegfrag = JPEGInfo->GetNextFragment(); + } + + //fp->read((char*)Raw, PixelDataLength); + + std::ofstream out("/tmp/jpegls.jpg"); + out.write((char*)inputdata, inputlength); + out.close(); + delete[] inputdata; +#endif gdcmWarningMacro( "Sorry, JPEG-LS not yet taken into account" ); fp->seekg( JPEGInfo->GetFirstFragment()->GetOffset(), std::ios::beg); @@ -445,14 +500,16 @@ bool PixelReadConvert::ReadAndDecompressJPEGFile( std::ifstream *fp ) return false; } else - { + { + // make sure this is the right JPEG compression + assert( !IsJPEGLS || !IsJPEG2000 ); // Precompute the offset localRaw will be shifted with int length = XSize * YSize * SamplesPerPixel; int numberBytes = BitsAllocated / 8; JPEGInfo->DecompressFromFile(fp, Raw, BitsStored, numberBytes, length ); return true; - } + } } /** @@ -497,22 +554,23 @@ void PixelReadConvert::BuildLUTRGBA() 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(), + int nbRead; // nb of items in LUT descriptor (must be = 3) + + nbRead = sscanf( LutRedDescriptor.c_str(), "%d\\%d\\%d", &lengthR, &debR, &nbitsR ); - if( nbRead != 3 ) + if ( nbRead != 3 ) { gdcmWarningMacro( "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 ) + &lengthG, &debG, &nbitsG ); + if ( nbRead != 3 ) { gdcmWarningMacro( "Wrong Green LUT descriptor" ); } @@ -523,20 +581,21 @@ void PixelReadConvert::BuildLUTRGBA() nbRead = sscanf( LutRedDescriptor.c_str(), "%d\\%d\\%d", &lengthB, &debB, &nbitsB ); + if ( nbRead != 3 ) + { + gdcmWarningMacro( "Wrong Blue LUT descriptor" ); + } + gdcmWarningMacro(" lengthR " << lengthR << " debR " << debR << " nbitsR " << nbitsR); gdcmWarningMacro(" lengthG " << lengthG << " debG " << debG << " nbitsG " << nbitsG); gdcmWarningMacro(" lengthB " << lengthB << " debB " << debB << " nbitsB " << nbitsB); - if( nbRead != 3 ) - { - gdcmWarningMacro( "Wrong Blue LUT descriptor" ); - } if ( !lengthR ) // if = 2^16, this shall be 0 see : CP-143 lengthR=65536; - if( !lengthG ) // if = 2^16, this shall be 0 + if ( !lengthG ) // if = 2^16, this shall be 0 lengthG=65536; if ( !lengthB ) // if = 2^16, this shall be 0 lengthB=65536; @@ -551,14 +610,14 @@ void PixelReadConvert::BuildLUTRGBA() // ------------------------------------------------------------- - if ( BitsAllocated <= 8) + if ( BitsAllocated <= 8 ) { - // forge the 4 * 8 Bits Red/Green/Blue/Alpha LUT LutRGBA = new uint8_t[ 1024 ]; // 256 * 4 (R, G, B, Alpha) if ( !LutRGBA ) return; - + LutItemNumber = 256; + LutItemSize = 8; memset( LutRGBA, 0, 1024 ); int mult; @@ -585,10 +644,14 @@ void PixelReadConvert::BuildLUTRGBA() //take "Subscript of the first Lut Value" (debR,debG,debB) into account! + //FIXME : +1 : to get 'low value' byte + // Trouble expected on Big Endian Processors ? + // 16 BIts Per Pixel Palette Color to be swapped? + a = LutRGBA + 0 + debR; for( i=0; i < lengthR; ++i ) { - *a = LutRedData[i*mult+1]; + *a = LutRedData[i*mult+1]; a += 4; } @@ -605,7 +668,7 @@ void PixelReadConvert::BuildLUTRGBA() *a = LutBlueData[i*mult+1]; a += 4; } - + a = LutRGBA + 3 ; for(i=0; i < 256; ++i) { @@ -615,8 +678,65 @@ void PixelReadConvert::BuildLUTRGBA() } else { + // Probabely the same stuff is to be done for 16 Bits Pixels + // with 65536 entries LUT ?!? + // Still looking for accurate info on the web :-( + gdcmWarningMacro( "Sorry Palette Color Lookup Tables not yet dealt with" - << "for 16 Bits Per Pixel images" ); + << " for 16 Bits Per Pixel images" ); + + // forge the 4 * 16 Bits Red/Green/Blue/Alpha LUT + + LutRGBA = (uint8_t *)new uint16_t[ 65536*4 ]; // 2^16 * 4 (R, G, B, Alpha) + if ( !LutRGBA ) + return; + memset( LutRGBA, 0, 65536*4*2 ); // 16 bits = 2 bytes ;-) + + LutItemNumber = 65536; + LutItemSize = 16; + + int i; + uint16_t *a16; + + //take "Subscript of the first Lut Value" (debR,debG,debB) into account! + + a16 = (uint16_t*)LutRGBA + 0 + debR; + for( i=0; i < lengthR; ++i ) + { + *a16 = ((uint16_t*)LutRedData)[i]; + a16 += 4; + } + + a16 = (uint16_t*)LutRGBA + 1 + debG; + for( i=0; i < lengthG; ++i) + { + *a16 = ((uint16_t*)LutGreenData)[i]; + a16 += 4; + } + + a16 = (uint16_t*)LutRGBA + 2 + debB; + for(i=0; i < lengthB; ++i) + { + *a16 = ((uint16_t*)LutBlueData)[i]; + a16 += 4; + } + + a16 = (uint16_t*)LutRGBA + 3 ; + for(i=0; i < 65536; ++i) + { + *a16 = 1; // Alpha component + a16 += 4; + } +/* Just to 'see' the LUT, at debug time + + a16=(uint16_t*)LutRGBA; + for (int j=0;j<65536;j++) + { + std::cout << *a16 << " " << *(a16+1) << " " + << *(a16+2) << " " << *(a16+3) << std::endl; + a16+=4; + } +*/ } } @@ -627,7 +747,7 @@ void PixelReadConvert::ConvertSwapZone() { unsigned int i; - if( BitsAllocated == 16 ) + if ( BitsAllocated == 16 ) { uint16_t *im16 = (uint16_t*)Raw; switch( SwapCode ) @@ -646,7 +766,7 @@ void PixelReadConvert::ConvertSwapZone() gdcmWarningMacro("SwapCode value (16 bits) not allowed."); } } - else if( BitsAllocated == 32 ) + else if ( BitsAllocated == 32 ) { uint32_t s32; uint16_t high; @@ -712,7 +832,7 @@ void PixelReadConvert::ConvertReorderEndianity() uint16_t *deb = (uint16_t *)Raw; for(int i = 0; i> ( BitsAllocated - BitsStored ); + // pmask : to mask the 'unused bits' (may contain overlays) + uint16_t pmask = 0xffff; + pmask = pmask >> ( BitsAllocated - BitsStored ); + uint16_t *deb = (uint16_t*)Raw; - for(int i = 0; i> (BitsStored - HighBitPosition - 1)) & mask; - deb++; + for(int i = 0; i> (BitsStored - HighBitPosition - 1)) & pmask; + deb++; + } + } + else // Pixels are signed + { + // smask : to check the 'sign' when BitsStored != BitsAllocated + uint16_t smask = 0x0001; + smask = smask << ( 16 - (BitsAllocated - BitsStored + 1) ); + // nmask : to propagate sign bit on negative values + int16_t nmask = (int16_t)0x8000; + nmask = nmask >> ( BitsAllocated - BitsStored - 1 ); +/* +std::cout << "BitsStored " << BitsStored + << " BitsAllocated " << BitsAllocated + << std::endl; +std::cout << std::hex << "pmask " << pmask + << " smask " << smask + << " nmask " << nmask + << std::endl; +*/ + for(int i = 0; i> (BitsStored - HighBitPosition - 1); + if ( *deb & smask ) + { + *deb = *deb | nmask; + } + else + { + *deb = *deb & pmask; + } + deb++; + } } } else if ( BitsAllocated == 32 ) { - uint32_t mask = 0xffffffff; - mask = mask >> ( BitsAllocated - BitsStored ); + // pmask : to mask the 'unused bits' (may contain overlays) + uint32_t pmask = 0xffffffff; + pmask = pmask >> ( BitsAllocated - BitsStored ); + uint32_t *deb = (uint32_t*)Raw; - for(int i = 0; i> (BitsStored - HighBitPosition - 1)) & mask; - deb++; + for(int i = 0; i> (BitsStored - HighBitPosition - 1)) & pmask; + deb++; + } + } + else + { + // smask : to check the 'sign' when BitsStored != BitsAllocated + uint32_t smask = 0x00000001; + smask = smask >> ( 32 - (BitsAllocated - BitsStored +1 )); + // nmask : to propagate sign bit on negative values + int32_t nmask = 0x80000000; + nmask = nmask >> ( BitsAllocated - BitsStored -1 ); + + for(int i = 0; i> (BitsStored - HighBitPosition - 1); + if ( *deb & smask ) + *deb = *deb | nmask; + else + *deb = *deb & pmask; + deb++; + } } } else { - gdcmWarningMacro("Weird image"); + gdcmWarningMacro("Weird image (BitsAllocated !=8, 12, 16, 32)"); throw FormatError( "Weird image !?" ); } } @@ -836,6 +1019,8 @@ bool PixelReadConvert::ConvertReArrangeBits() throw ( FormatError ) */ void PixelReadConvert::ConvertRGBPlanesToRGBPixels() { + gdcmWarningMacro("--> ConvertRGBPlanesToRGBPixels"); + uint8_t *localRaw = Raw; uint8_t *copyRaw = new uint8_t[ RawSize ]; memmove( copyRaw, localRaw, RawSize ); @@ -861,6 +1046,17 @@ void PixelReadConvert::ConvertRGBPlanesToRGBPixels() */ void PixelReadConvert::ConvertYcBcRPlanesToRGBPixels() { + // Remarks for YBR newbees : + // YBR_FULL works very much like RGB, i.e. three samples per pixel, + // just the color space is YCbCr instead of RGB. This is particularly useful + // for doppler ultrasound where most of the image is grayscale + // (i.e. only populates the Y components) and Cb and Cr are mostly zero, + // except for the few patches of color on the image. + // On such images, RLE achieves a compression ratio that is much better + // than the compression ratio on an equivalent RGB image. + + gdcmWarningMacro("--> ConvertYcBcRPlanesToRGBPixels"); + uint8_t *localRaw = Raw; uint8_t *copyRaw = new uint8_t[ RawSize ]; memmove( copyRaw, localRaw, RawSize ); @@ -878,10 +1074,11 @@ void PixelReadConvert::ConvertYcBcRPlanesToRGBPixels() uint8_t *c = copyRaw + l+ l; int32_t R, G, B; - /// \todo : Replace by the 'well known' integer computation - /// counterpart. Refer to + /// We replaced easy to understand but time consuming floating point + /// computations by the 'well known' integer computation counterpart + /// Refer to : /// http://lestourtereaux.free.fr/papers/data/yuvrgb.pdf - /// for code optimisation. + /// for code optimisation. for ( int i = 0; i < nbFrames; i++ ) { @@ -946,7 +1143,7 @@ void PixelReadConvert::ConvertHandleColor() // - "Planar Configuration" = 0, // - "Photometric Interpretation" = "PALETTE COLOR". // Hence gdcm will use the folowing "heuristic" in order to be tolerant - // towards Dicom-non-conformance files: + // towards Dicom-non-conformant files: // << whatever the "Planar Configuration" value might be, a // "Photometric Interpretation" set to "PALETTE COLOR" forces // a LUT intervention >> @@ -958,9 +1155,13 @@ void PixelReadConvert::ConvertHandleColor() // - [Planar 1] AND [Photo C] handled with ConvertYcBcRPlanesToRGBPixels() // - [Planar 2] OR [Photo D] requires LUT intervention. + gdcmWarningMacro("--> ConvertHandleColor" + << "Planar Configuration " << PlanarConfiguration ); + if ( ! IsRawRGB() ) { // [Planar 2] OR [Photo D]: LUT intervention done outside + gdcmWarningMacro("--> RawRGB : LUT intervention done outside"); return; } @@ -969,22 +1170,27 @@ void PixelReadConvert::ConvertHandleColor() if ( IsYBRFull ) { // [Planar 1] AND [Photo C] (remember YBR_FULL_422 acts as RGB) + gdcmWarningMacro("--> YBRFull"); ConvertYcBcRPlanesToRGBPixels(); } else { // [Planar 1] AND [Photo C] + gdcmWarningMacro("--> YBRFull"); ConvertRGBPlanesToRGBPixels(); } return; } // When planarConf is 0, and RLELossless (forbidden by Dicom norm) - // pixels need to be RGB-fied anyway + // pixels need to be RGB-fyied anyway + if (IsRLELossless) - { + { + gdcmWarningMacro("--> RLE Lossless"); ConvertRGBPlanesToRGBPixels(); } + // In *normal *case, when planarConf is 0, pixels are already in RGB } @@ -1005,7 +1211,7 @@ void PixelReadConvert::ComputeRawAndRGBSizes() * SamplesPerPixel; if ( HasLUT ) { - RGBSize = 3 * RawSize; + RGBSize = 3 * RawSize; // works for 8 and 16 bits per Pixel } else {