/*=========================================================================
-
+
Program: gdcm
Module: $RCSfile: gdcmPixelReadConvert.cxx,v $
Language: C++
- Date: $Date: 2005/02/02 16:34:55 $
- Version: $Revision: 1.45 $
+ Date: $Date: 2005/08/19 13:15:05 $
+ Version: $Revision: 1.76 $
Copyright (c) CREATIS (Centre de Recherche et d'Applications en Traitement de
l'Image). All rights reserved. See Doc/License.txt or
=========================================================================*/
+#include "gdcmPixelReadConvert.h"
#include "gdcmDebug.h"
#include "gdcmFile.h"
#include "gdcmGlobal.h"
#include "gdcmTS.h"
-#include "gdcmPixelReadConvert.h"
#include "gdcmDocEntry.h"
#include "gdcmRLEFramesInfo.h"
#include "gdcmJPEGFragmentsInfo.h"
namespace gdcm
{
+
+//bool ReadMPEGFile (std::ifstream *fp, void *image_buffer, size_t lenght);
+bool gdcm_read_JPEG2000_file (void* raw,
+ char *inputdata, size_t inputlength);
//-----------------------------------------------------------------------------
#define str2num(str, typeNum) *((typeNum *)(str))
//-----------------------------------------------------------------------------
// Constructor / Destructor
+/// Constructor
PixelReadConvert::PixelReadConvert()
{
RGB = 0;
LutRedData = 0;
LutGreenData = 0;
LutBlueData = 0;
+ RLEInfo = 0;
+ JPEGInfo = 0;
+ UserFunction = 0;
+ FileInternal = 0;
}
+/// Canonical Destructor
PixelReadConvert::~PixelReadConvert()
{
Squeeze();
//-----------------------------------------------------------------------------
// Public
/**
- * \brief Predicate to know wether the image[s] (once Raw) is RGB.
+ * \brief Predicate to know whether the image[s] (once Raw) is RGB.
* \note See comments of \ref ConvertHandleColor
*/
bool PixelReadConvert::IsRawRGB()
}
return true;
}
-
+/**
+ * \brief Gets various usefull informations from the file header
+ * @param file gdcm::File pointer
+ */
void PixelReadConvert::GrabInformationsFromFile( File *file )
{
// Number of Bits Allocated for storing a Pixel is defaulted to 16
HighBitPosition = BitsAllocated - 1;
}
- XSize = file->GetXSize();
- YSize = file->GetYSize();
- ZSize = file->GetZSize();
+ XSize = file->GetXSize();
+ YSize = file->GetYSize();
+ ZSize = file->GetZSize();
SamplesPerPixel = file->GetSamplesPerPixel();
- PixelSize = file->GetPixelSize();
- PixelSign = file->IsSignedPixelData();
- SwapCode = file->GetSwapCode();
- std::string ts = file->GetTransferSyntax();
+ //PixelSize = file->GetPixelSize(); Useless
+ PixelSign = file->IsSignedPixelData();
+ SwapCode = file->GetSwapCode();
+ std::string ts = file->GetTransferSyntax();
IsRaw =
( ! file->IsDicomV3() )
|| Global::GetTS()->GetSpecialTransferSyntax(ts) == TS::ImplicitVRLittleEndian
|| Global::GetTS()->GetSpecialTransferSyntax(ts) == TS::ExplicitVRBigEndian
|| Global::GetTS()->GetSpecialTransferSyntax(ts) == TS::DeflatedExplicitVRLittleEndian;
+ IsMPEG = Global::GetTS()->IsMPEG(ts);
IsJPEG2000 = Global::GetTS()->IsJPEG2000(ts);
IsJPEGLS = Global::GetTS()->IsJPEGLS(ts);
IsJPEGLossy = Global::GetTS()->IsJPEGLossy(ts);
PixelOffset = file->GetPixelOffset();
PixelDataLength = file->GetPixelAreaLength();
- RLEInfo = file->GetRLEInfo();
- JPEGInfo = file->GetJPEGInfo();
+ RLEInfo = file->GetRLEInfo();
+ JPEGInfo = file->GetJPEGInfo();
+
+ IsMonochrome = file->IsMonochrome();
+ IsMonochrome1 = file->IsMonochrome1();
+ IsPaletteColor = file->IsPaletteColor();
+ IsYBRFull = file->IsYBRFull();
PlanarConfiguration = file->GetPlanarConfiguration();
- IsMonochrome = file->IsMonochrome();
- IsPaletteColor = file->IsPaletteColor();
- IsYBRFull = file->IsYBRFull();
/////////////////////////////////////////////////////////////////
// LUT section:
// [Bin|Val]Entry occurence migth have been hindered (read simply NOT
// loaded). Hence, we first try to obtain the LUTs data from the file
// and when this fails we read the LUTs data directly 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.
+ // \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
+ // //// Red round
file->LoadEntryBinArea(0x0028, 0x1201);
LutRedData = (uint8_t*)file->GetEntryBinArea( 0x0028, 0x1201 );
if ( ! LutRedData )
{
- gdcmVerboseMacro( "Unable to read Red LUT data" );
+ gdcmWarningMacro( "Unable to read Red Palette Color Lookup Table data" );
}
- ////// Green round:
+ // //// Green round:
file->LoadEntryBinArea(0x0028, 0x1202);
LutGreenData = (uint8_t*)file->GetEntryBinArea(0x0028, 0x1202 );
if ( ! LutGreenData)
{
- gdcmVerboseMacro( "Unable to read Green LUT data" );
+ gdcmWarningMacro( "Unable to read Green Palette Color Lookup Table data" );
}
- ////// Blue round:
+ // //// Blue round:
file->LoadEntryBinArea(0x0028, 0x1203);
LutBlueData = (uint8_t*)file->GetEntryBinArea( 0x0028, 0x1203 );
if ( ! LutBlueData )
{
- gdcmVerboseMacro( "Unable to read Blue LUT data" );
+ gdcmWarningMacro( "Unable to read Blue Palette Color Lookup Table data" );
}
}
+ FileInternal = file;
ComputeRawAndRGBSizes();
}
+/// \brief Reads from disk and decompresses Pixels
bool PixelReadConvert::ReadAndDecompressPixelData( std::ifstream *fp )
{
// ComputeRawAndRGBSizes is already made by
//// First stage: get our hands on the Pixel Data.
if ( !fp )
{
- gdcmVerboseMacro( "Unavailable file pointer." );
+ gdcmWarningMacro( "Unavailable file pointer." );
return false;
}
fp->seekg( PixelOffset, std::ios::beg );
- if( fp->fail() || fp->eof())
+ if ( fp->fail() || fp->eof() )
{
- gdcmVerboseMacro( "Unable to find PixelOffset in file." );
+ gdcmWarningMacro( "Unable to find PixelOffset in file." );
return false;
}
AllocateRaw();
//////////////////////////////////////////////////
- //// Second stage: read from disk dans decompress.
+ //// Second stage: read from disk and decompress.
if ( BitsAllocated == 12 )
{
ReadAndDecompress12BitsTo16Bits( 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 )
{
- gdcmVerboseMacro( "Mismatch between PixelReadConvert and RawSize." );
+ gdcmWarningMacro( "Mismatch between PixelReadConvert : "
+ << PixelDataLength << " and RawSize : " << RawSize );
}
- if( PixelDataLength > RawSize)
+ if ( PixelDataLength > RawSize )
{
fp->read( (char*)Raw, RawSize);
}
if ( fp->fail() || fp->eof())
{
- gdcmVerboseMacro( "Reading of Raw pixel data failed." );
+ gdcmWarningMacro( "Reading of Raw pixel data failed." );
return false;
}
}
{
if ( ! RLEInfo->DecompressRLEFile( fp, Raw, XSize, YSize, ZSize, BitsAllocated ) )
{
- gdcmVerboseMacro( "RLE decompressor failed." );
+ gdcmWarningMacro( "RLE decompressor failed." );
return false;
}
}
+ else if ( IsMPEG )
+ {
+ //gdcmWarningMacro( "Sorry, MPEG not yet taken into account" );
+ //return false;
+ // fp has already been seek to start of mpeg
+ //ReadMPEGFile(fp, Raw, PixelDataLength);
+ return true;
+ }
else
{
// Default case concerns JPEG family
if ( ! ReadAndDecompressJPEGFile( fp ) )
{
- gdcmVerboseMacro( "JPEG decompressor failed." );
+ gdcmWarningMacro( "JPEG decompressor failed." );
return false;
}
}
//// Third stage: twigle the bytes and bits.
ConvertReorderEndianity();
ConvertReArrangeBits();
+ ConvertFixGreyLevels();
+ if (UserFunction) // user is allowed to Mirror, TopDown, Rotate,...the image
+ UserFunction( Raw, FileInternal);
ConvertHandleColor();
return true;
}
+/// Deletes Pixels Area
void PixelReadConvert::Squeeze()
{
if ( RGB )
}
/**
- * \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()
{
// 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;
}
{
if ( IsJPEG2000 )
{
- gdcmVerboseMacro( "Sorry, JPEG2000 not yet taken into account" );
- fp->seekg( JPEGInfo->GetFirstFragment()->GetOffset(), std::ios::beg);
-// if ( ! gdcm_read_JPEG2000_file( fp,Raw ) )
- return false;
+ // 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
+
+ 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();
+ }
+ // Warning the inputdata buffer is delete in the function
+ if ( ! gdcm_read_JPEG2000_file( Raw,
+ (char*)inputdata, inputlength ) )
+ {
+ return true;
+ }
+ // wow what happen, must be an error
+ return false;
}
-
- if ( IsJPEGLS )
+ else if ( IsJPEGLS )
{
- gdcmVerboseMacro( "Sorry, JPEG-LS not yet taken into account" );
+ // 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
+ // is based on the LOCO-I algorithm (LOw COmplexity LOssless COmpression
+ // for Images) developed at Hewlett-Packard Laboratories]
+ //
+ // 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);
// if ( ! gdcm_read_JPEGLS_file( fp,Raw ) )
return false;
}
-
- // else ??
- // 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;
+ 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;
+ }
}
/**
|| LutGreenDescriptor == GDCM_UNFOUND
|| LutBlueDescriptor == GDCM_UNFOUND )
{
+ gdcmWarningMacro( "(At least) a LUT Descriptor is missing" );
return;
}
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 )
{
- gdcmVerboseMacro( "Wrong Red LUT descriptor" );
- }
-
+ 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 )
{
- gdcmVerboseMacro( "Wrong Green LUT descriptor" );
+ gdcmWarningMacro( "Wrong Green LUT descriptor" );
}
int lengthB; // Blue LUT length in Bytes
nbRead = sscanf( LutRedDescriptor.c_str(),
"%d\\%d\\%d",
&lengthB, &debB, &nbitsB );
- if( nbRead != 3 )
+ if ( nbRead != 3 )
{
- gdcmVerboseMacro( "Wrong Blue LUT descriptor" );
+ 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 ( !lengthR ) // if = 2^16, this shall be 0 see : CP-143
+ lengthR=65536;
+ if ( !lengthG ) // if = 2^16, this shall be 0
+ lengthG=65536;
+ if ( !lengthB ) // if = 2^16, this shall be 0
+ lengthB=65536;
////////////////////////////////////////////////////////
+
if ( ( ! LutRedData ) || ( ! LutGreenData ) || ( ! LutBlueData ) )
{
+ gdcmWarningMacro( "(At least) a LUT is missing" );
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
+ // -------------------------------------------------------------
+
+ if ( BitsAllocated <= 8 )
{
- // See PS 3.3-2003 C.11.1.1.2 p 619
- mult = 1;
- }
+ // 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 );
- // 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;
- }
+ 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;
+ }
- a = LutRGBA + 1;
- for( i=0; i < lengthG; ++i)
- {
- *a = LutGreenData[i*mult+1];
- a += 4;
- }
+ // 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;
+
+ //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 += 4;
+ }
- a = LutRGBA + 2;
- for(i=0; i < lengthB; ++i)
- {
- *a = LutBlueData[i*mult+1];
- a += 4;
- }
+ a = LutRGBA + 1 + debG;
+ for( i=0; i < lengthG; ++i)
+ {
+ *a = LutGreenData[i*mult+1];
+ a += 4;
+ }
- a = LutRGBA + 3;
- for(i=0; i < 256; ++i)
+ a = LutRGBA + 2 + debB;
+ 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;
+ }
+ }
+ else
{
- *a = 1; // Alpha component
- a += 4;
+ // 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" );
+
+ // 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;
+ }
+*/
}
}
{
unsigned int i;
- if( BitsAllocated == 16 )
+ if ( BitsAllocated == 16 )
{
uint16_t *im16 = (uint16_t*)Raw;
switch( SwapCode )
}
break;
default:
- gdcmVerboseMacro("SwapCode value (16 bits) not allowed.");
+ gdcmWarningMacro("SwapCode value (16 bits) not allowed.");
}
}
- else if( BitsAllocated == 32 )
+ else if ( BitsAllocated == 32 )
{
uint32_t s32;
uint16_t high;
}
break;
default:
- gdcmVerboseMacro("SwapCode value (32 bits) not allowed." );
+ gdcmWarningMacro("SwapCode value (32 bits) not allowed." );
}
}
}
uint16_t *deb = (uint16_t *)Raw;
for(int i = 0; i<l; i++)
{
- if( *deb == 0xffff )
+ if ( *deb == 0xffff )
{
*deb = 0;
}
}
}
+/**
+ * \brief Deal with Grey levels i.e. re-arange them
+ * to have low values = dark, high values = bright
+ */
+void PixelReadConvert::ConvertFixGreyLevels()
+{
+ if (!IsMonochrome1)
+ return;
+
+ uint32_t i; // to please M$VC6
+ int16_t j;
+
+ if (!PixelSign)
+ {
+ if ( BitsAllocated == 8 )
+ {
+ uint8_t *deb = (uint8_t *)Raw;
+ for (i=0; i<RawSize; i++)
+ {
+ *deb = 255 - *deb;
+ deb++;
+ }
+ return;
+ }
+
+ if ( BitsAllocated == 16 )
+ {
+ uint16_t mask =1;
+ for (j=0; j<BitsStored-1; j++)
+ {
+ mask = (mask << 1) +1; // will be fff when BitsStored=12
+ }
+
+ uint16_t *deb = (uint16_t *)Raw;
+ for (i=0; i<RawSize/2; i++)
+ {
+ *deb = mask - *deb;
+ deb++;
+ }
+ return;
+ }
+ }
+ else
+ {
+ if ( BitsAllocated == 8 )
+ {
+ uint8_t smask8 = 255;
+ uint8_t *deb = (uint8_t *)Raw;
+ for (i=0; i<RawSize; i++)
+ {
+ *deb = smask8 - *deb;
+ deb++;
+ }
+ return;
+ }
+ if ( BitsAllocated == 16 )
+ {
+ uint16_t smask16 = 65535;
+ uint16_t *deb = (uint16_t *)Raw;
+ for (i=0; i<RawSize/2; i++)
+ {
+ *deb = smask16 - *deb;
+ deb++;
+ }
+ return;
+ }
+ }
+}
+
/**
* \brief Re-arrange the bits within the bytes.
- * @return Boolean
+ * @return Boolean always true
*/
bool PixelReadConvert::ConvertReArrangeBits() throw ( FormatError )
{
+
if ( BitsStored != BitsAllocated )
{
int l = (int)( RawSize / ( BitsAllocated / 8 ) );
if ( BitsAllocated == 16 )
{
- uint16_t mask = 0xffff;
- mask = mask >> ( 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<l; i++)
+
+ if ( !PixelSign ) // Pixels are unsigned
{
- *deb = (*deb >> (BitsStored - HighBitPosition - 1)) & mask;
- deb++;
+ for(int i = 0; i<l; i++)
+ {
+ *deb = (*deb >> (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<l; i++)
+ {
+ *deb = *deb >> (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<l; i++)
+
+ if ( !PixelSign )
{
- *deb = (*deb >> (BitsStored - HighBitPosition - 1)) & mask;
- deb++;
+ for(int i = 0; i<l; i++)
+ {
+ *deb = (*deb >> (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<l; i++)
+ {
+ *deb = *deb >> (BitsStored - HighBitPosition - 1);
+ if ( *deb & smask )
+ *deb = *deb | nmask;
+ else
+ *deb = *deb & pmask;
+ deb++;
+ }
}
}
else
{
- gdcmVerboseMacro("Weird image");
+ gdcmWarningMacro("Weird image (BitsAllocated !=8, 12, 16, 32)");
throw FormatError( "Weird image !?" );
}
}
*/
void PixelReadConvert::ConvertRGBPlanesToRGBPixels()
{
+ gdcmWarningMacro("--> ConvertRGBPlanesToRGBPixels");
+
uint8_t *localRaw = Raw;
uint8_t *copyRaw = new uint8_t[ RawSize ];
memmove( copyRaw, localRaw, RawSize );
*/
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 );
int l = XSize * YSize;
int nbFrames = ZSize;
- uint8_t *a = copyRaw;
+ uint8_t *a = copyRaw + 0;
uint8_t *b = copyRaw + l;
- uint8_t *c = copyRaw + l + l;
- double R, G, B;
+ 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++ )
{
for ( int j = 0; j < l; j++ )
{
- R = 1.164 *(*a-16) + 1.596 *(*c -128) + 0.5;
- G = 1.164 *(*a-16) - 0.813 *(*c -128) - 0.392 *(*b -128) + 0.5;
- B = 1.164 *(*a-16) + 2.017 *(*b -128) + 0.5;
+ R = 38142 *(*a-16) + 52298 *(*c -128);
+ G = 38142 *(*a-16) - 26640 *(*c -128) - 12845 *(*b -128);
+ B = 38142 *(*a-16) + 66093 *(*b -128);
+
+ R = (R+16384)>>15;
+ G = (G+16384)>>15;
+ B = (B+16384)>>15;
- if (R < 0.0) R = 0.0;
- if (G < 0.0) G = 0.0;
- if (B < 0.0) B = 0.0;
- if (R > 255.0) R = 255.0;
- if (G > 255.0) G = 255.0;
- if (B > 255.0) B = 255.0;
+ if (R < 0) R = 0;
+ if (G < 0) G = 0;
+ if (B < 0) B = 0;
+ if (R > 255) R = 255;
+ if (G > 255) G = 255;
+ if (B > 255) B = 255;
*(localRaw++) = (uint8_t)R;
*(localRaw++) = (uint8_t)G;
delete[] copyRaw;
}
+/// \brief Deals with the color decoding i.e. handle:
+/// - R, G, B planes (as opposed to RGB pixels)
+/// - YBR (various) encodings.
+/// - LUT[s] (or "PALETTE COLOR").
+
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 >>
// - [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;
}
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
}
+/// Computes the Pixels Size
void PixelReadConvert::ComputeRawAndRGBSizes()
{
int bitsAllocated = BitsAllocated;
* SamplesPerPixel;
if ( HasLUT )
{
- RGBSize = 3 * RawSize;
+ RGBSize = 3 * RawSize; // works for 8 and 16 bits per Pixel
}
else
{
}
}
+/// Allocates room for RGB Pixels
void PixelReadConvert::AllocateRGB()
{
if ( RGB )
RGB = new uint8_t[RGBSize];
}
+/// Allocates room for RAW Pixels
void PixelReadConvert::AllocateRaw()
{
if ( Raw )
}
else
{
- gdcmVerboseMacro("Set as RLE file but NO RLEinfo present.");
+ gdcmWarningMacro("Set as RLE file but NO RLEinfo present.");
}
}
}
else
{
- gdcmVerboseMacro("Set as JPEG file but NO JPEGinfo present.");
+ gdcmWarningMacro("Set as JPEG file but NO JPEGinfo present.");
}
}
}
//-----------------------------------------------------------------------------
} // end namespace gdcm
-
-// NOTES on File internal calls
-// User
-// ---> GetImageData
-// ---> GetImageDataIntoVector
-// |---> GetImageDataIntoVectorRaw
-// | lut intervention
-// User
-// ---> GetImageDataRaw
-// ---> GetImageDataIntoVectorRaw
-