X-Git-Url: https://git.creatis.insa-lyon.fr/pubgit/?a=blobdiff_plain;f=src%2FgdcmPixelReadConvert.cxx;h=c54287df626a4a6d147dbb79c7c3941cbf6af291;hb=bd7bec4c367d671a9da358584e98a8ec29bb641e;hp=32a8f81dfa4bb3d4573e4f0c14ae45964f527d99;hpb=2532a8e86e06d379d58b2fdda8e2413641d0a5f0;p=gdcm.git diff --git a/src/gdcmPixelReadConvert.cxx b/src/gdcmPixelReadConvert.cxx index 32a8f81d..c54287df 100644 --- a/src/gdcmPixelReadConvert.cxx +++ b/src/gdcmPixelReadConvert.cxx @@ -3,8 +3,8 @@ Program: gdcm Module: $RCSfile: gdcmPixelReadConvert.cxx,v $ Language: C++ - Date: $Date: 2006/03/29 16:09:48 $ - Version: $Revision: 1.111 $ + Date: $Date: 2007/10/26 16:06:57 $ + Version: $Revision: 1.127 $ 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 "gdcmDocEntry.h" #include "gdcmRLEFramesInfo.h" #include "gdcmJPEGFragmentsInfo.h" +#include "gdcmSegmentedPalette.h" #include #include //for sscanf @@ -32,10 +33,10 @@ #include // for memset #endif -namespace gdcm +namespace GDCM_NAME_SPACE { -//bool ReadMPEGFile (std::ifstream *fp, char *inputdata, size_t lenght); +//bool ReadMPEGFile (std::ifstream *fp, char *inputdata, size_t lenght); bool gdcm_read_JPEG2000_file (void* raw, char *inputdata, size_t inputlength); //----------------------------------------------------------------------------- @@ -44,7 +45,7 @@ bool gdcm_read_JPEG2000_file (void* raw, //----------------------------------------------------------------------------- // Constructor / Destructor /// Constructor -PixelReadConvert::PixelReadConvert() +PixelReadConvert::PixelReadConvert() { RGB = 0; RGBSize = 0; @@ -70,7 +71,7 @@ PixelReadConvert::~PixelReadConvert() // Public /** * \brief Predicate to know whether the image[s] (once Raw) is RGB. - * \note See comments of \ref ConvertHandleColor + * \note See comments of ConvertHandleColor */ bool PixelReadConvert::IsRawRGB() { @@ -85,6 +86,7 @@ bool PixelReadConvert::IsRawRGB() /** * \brief Gets various usefull informations from the file header * @param file gdcm::File pointer + * @param fileHelper gdcm::FileHelper pointer */ void PixelReadConvert::GrabInformationsFromFile( File *file, FileHelper *fileHelper ) @@ -96,6 +98,10 @@ void PixelReadConvert::GrabInformationsFromFile( File *file, { BitsAllocated = 16; } + else if ( BitsAllocated > 8 && BitsAllocated < 16 && BitsAllocated != 12 ) + { + BitsAllocated = 16; + } // Number of "Bits Stored", defaulted to number of "Bits Allocated" // when absent from the file. @@ -138,10 +144,10 @@ void PixelReadConvert::GrabInformationsFromFile( File *file, while (true) // shorter to write than 'if elseif elseif elseif' ... { // mind the order : check the most usual first. - if( IsRaw = Global::GetTS()->GetSpecialTransferSyntax(ts) == TS::ExplicitVRLittleEndian) break; - if( IsRaw = Global::GetTS()->GetSpecialTransferSyntax(ts) == TS::ImplicitVRLittleEndian ) break; - if( IsRaw = Global::GetTS()->GetSpecialTransferSyntax(ts) == TS::ExplicitVRBigEndian) break; - if( IsRaw = Global::GetTS()->GetSpecialTransferSyntax(ts) == TS::ImplicitVRBigEndianPrivateGE) break; + if( IsRaw = (Global::GetTS()->GetSpecialTransferSyntax(ts) == TS::ExplicitVRLittleEndian)) break; + if( IsRaw = (Global::GetTS()->GetSpecialTransferSyntax(ts) == TS::ImplicitVRLittleEndian)) break; + if( IsRaw = (Global::GetTS()->GetSpecialTransferSyntax(ts) == TS::ExplicitVRBigEndian)) break; + if( IsRaw = (Global::GetTS()->GetSpecialTransferSyntax(ts) == TS::ImplicitVRBigEndianPrivateGE)) break; // DeflatedExplicitVRLittleEndian syntax means the whole Dataset (Header + Pixels) is compressed ! // Not dealt with ! (Parser hangs) //if( IsRaw = Global::GetTS()->GetSpecialTransferSyntax(ts) == TS::DeflatedExplicitVRLittleEndian) break; @@ -149,23 +155,24 @@ void PixelReadConvert::GrabInformationsFromFile( File *file, } // cache whether this is a strange GE transfer syntax (which uses // a little endian transfer syntax for the header and a big endian - // transfer syntax for the pixel data). + // transfer syntax for the pixel data). IsPrivateGETransferSyntax = ( Global::GetTS()->GetSpecialTransferSyntax(ts) == TS::ImplicitVRBigEndianPrivateGE ); - IsMPEG = IsJPEG2000 = IsJPEGLS = IsJPEGLossy = IsJPEGLossless = IsRLELossless = false; + IsMPEG = IsJPEG2000 = IsJPEGLS = IsJPEGLossy = IsJPEGLossless = IsRLELossless = false; if (!IsRaw) - { + { while(true) { // mind the order : check the most usual first. - if( IsJPEGLossy = Global::GetTS()->IsJPEGLossy(ts) ) break; - if( IsJPEGLossless = Global::GetTS()->IsJPEGLossless(ts) ) break; - if( IsRLELossless = Global::GetTS()->IsRLELossless(ts) ) break; - if( IsJPEG2000 = Global::GetTS()->IsJPEG2000(ts) ) break; - if( IsMPEG = Global::GetTS()->IsMPEG(ts) ) break; - if( IsJPEGLS = Global::GetTS()->IsJPEGLS(ts) ) break; - // DeflatedExplicitVRLittleEndian is considered as 'Unexpected' (we don't know yet haow to process !) + if( IsJPEGLossy = (Global::GetTS()->IsJPEGLossy(ts))) break; + if( IsJPEGLossless = (Global::GetTS()->IsJPEGLossless(ts))) break; + if( IsRLELossless = (Global::GetTS()->IsRLELossless(ts))) break; + if( IsJPEG2000 = (Global::GetTS()->IsJPEG2000(ts))) break; + if( IsMPEG = (Global::GetTS()->IsMPEG(ts))) break; + if( IsJPEGLS = (Global::GetTS()->IsJPEGLS(ts))) break; + // DeflatedExplicitVRLittleEndian is considered as 'Unexpected' + // (we don't know yet how to process !) gdcmWarningMacro("Unexpected Transfer Syntax :[" << ts << "]"); break; } @@ -189,51 +196,114 @@ void PixelReadConvert::GrabInformationsFromFile( File *file, HasLUT = file->HasLUT(); if ( HasLUT ) { - // Just in case some access to a File element requires disk access. +/* + C.7.6.3.1.5 + The three values of Palette Color Lookup Table Descriptor (0028,1101-1103) + describe the format of the Lookup Table Data in the corresponding + Data Element (0028,1201-1203) or (0028,1221-1223). + + The first value is the number of entries in the lookup table. + When the number of table entries is equal to 2**16 then this value shall be 0. + + The second value is the first stored pixel value mapped. + This pixel value is mapped to the first entry in the Lookup Table Data. + All image pixel values less than the first entry value mapped are also + mapped to the first entry in the Lookup Table Data. + An image pixel value one greater than the first entry value mapped is + mapped to the second entry in the Lookup Table Data. + Subsequent image pixel values are mapped to the subsequent entries in + the Lookup Table Data up to an image pixel value equal to number of + entries + first entry value mapped - 1 which is mapped to the last entry + in the Lookup Table Data. + Image pixel values greater than or equal to number of entries + first entry + value mapped are also mapped to the last entry in the Lookup Table Data. + + The third value specifies the number of bits for each entry in the Lookup + Table Data. It shall take the value of 8 or 16. + The LUT Data shall be stored in a format equivalent to 8 or 16 bits + allocated where the high bit is equal to bits allocated-1. + + When the Palette Color Lookup Table Descriptor (0028,1101-1103) are used as + part of the Palette Color Lookup Table Module, the third value shall be + equal to 16. + + Note: A value of 16 indicates the Lookup Table Data will range from (0,0,0) + minimum intensity to (65535,65535,65535) maximum intensity. + +*/ + + // Just in case some access to a File element requires disk access. LutRedDescriptor = file->GetEntryString( 0x0028, 0x1101 ); LutGreenDescriptor = file->GetEntryString( 0x0028, 0x1102 ); LutBlueDescriptor = file->GetEntryString( 0x0028, 0x1103 ); - - // FIXME : The following comment is probabely meaningless, since LUT are *always* - // loaded at parsing time, whatever their length is. - - // 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 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. DataEntry::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 - file->LoadEntryBinArea(0x0028, 0x1201); - LutRedData = (uint8_t*)file->GetEntryBinArea( 0x0028, 0x1201 ); - if ( ! LutRedData ) - { - gdcmWarningMacro("Unable to read Red Palette Color Lookup Table data"); - } - - // //// Green round: - file->LoadEntryBinArea(0x0028, 0x1202); - LutGreenData = (uint8_t*)file->GetEntryBinArea(0x0028, 0x1202 ); - if ( ! LutGreenData) - { - gdcmWarningMacro("Unable to read Green Palette Color Lookup Table data"); - } - - // //// Blue round: - file->LoadEntryBinArea(0x0028, 0x1203); - LutBlueData = (uint8_t*)file->GetEntryBinArea( 0x0028, 0x1203 ); - if ( ! LutBlueData ) - { - gdcmWarningMacro("Unable to read Blue Palette Color Lookup Table data"); - } + // Is it a Segmented Palette ? Check if we find the red one: + if( file->GetDocEntry(0x0028,0x1221) ) // no need to check for blue & green + { + GDCM_NAME_SPACE::TagKey DCM_RedPaletteColorLookupTableDescriptor (0x0028, 0x1101); + GDCM_NAME_SPACE::TagKey DCM_GreenPaletteColorLookupTableDescriptor (0x0028, 0x1102); + GDCM_NAME_SPACE::TagKey DCM_BluePaletteColorLookupTableDescriptor (0x0028, 0x1103); + + GDCM_NAME_SPACE::TagKey DCM_SegmentedRedPaletteColorLookupTableData (0x0028, 0x1221); + GDCM_NAME_SPACE::TagKey DCM_SegmentedGreenPaletteColorLookupTableData (0x0028, 0x1222); + GDCM_NAME_SPACE::TagKey DCM_SegmentedBluePaletteColorLookupTableData (0x0028, 0x1223); + + + LutRedData = new uint8_t[65535*2]; // FIXME: leak + LutGreenData = new uint8_t[65535*2]; + LutBlueData = new uint8_t[65535*2]; + // TODO need to check file is indeed PALETTE COLOR: + ReadPaletteInto(file, DCM_RedPaletteColorLookupTableDescriptor, + DCM_SegmentedRedPaletteColorLookupTableData,LutRedData); + ReadPaletteInto(file, DCM_GreenPaletteColorLookupTableDescriptor, + DCM_SegmentedGreenPaletteColorLookupTableData,LutGreenData); + ReadPaletteInto(file, DCM_BluePaletteColorLookupTableDescriptor, + DCM_SegmentedBluePaletteColorLookupTableData,LutBlueData); + + } + else + { + + // FIXME : The following comment is probabely meaningless, since LUT are *always* + // loaded at parsing time, whatever their length is. + + // 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 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. DataEntry::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 + file->LoadEntryBinArea(0x0028, 0x1201); + LutRedData = (uint8_t*)file->GetEntryBinArea( 0x0028, 0x1201 ); + if ( ! LutRedData ) + { + gdcmWarningMacro("Unable to read Red Palette Color Lookup Table data"); + } + + // //// Green round: + file->LoadEntryBinArea(0x0028, 0x1202); + LutGreenData = (uint8_t*)file->GetEntryBinArea(0x0028, 0x1202 ); + if ( ! LutGreenData) + { + gdcmWarningMacro("Unable to read Green Palette Color Lookup Table data"); + } + + // //// Blue round: + file->LoadEntryBinArea(0x0028, 0x1203); + LutBlueData = (uint8_t*)file->GetEntryBinArea( 0x0028, 0x1203 ); + if ( ! LutBlueData ) + { + gdcmWarningMacro("Unable to read Blue Palette Color Lookup Table data"); + } + } } FileInternal = file; FH = fileHelper; @@ -271,7 +341,8 @@ bool PixelReadConvert::ReadAndDecompressPixelData( std::ifstream *fp ) unsigned int count = 0; unsigned int frameSize; unsigned int bitsAllocated = BitsAllocated; - if(bitsAllocated == 12) + //if(bitsAllocated == 12) + if(bitsAllocated > 8 && bitsAllocated < 16) bitsAllocated = 16; frameSize = XSize*YSize*SamplesPerPixel*bitsAllocated/8; @@ -317,7 +388,7 @@ bool PixelReadConvert::ReadAndDecompressPixelData( std::ifstream *fp ) } if (remainingLength !=0 ) fp->read( raw, remainingLength); - + if ( fp->fail() || fp->eof()) { gdcmWarningMacro( "Reading of Raw pixel data failed." ); @@ -375,9 +446,9 @@ void PixelReadConvert::Squeeze() delete [] Raw; Raw = 0; - if ( LutRGBA ) - delete [] LutRGBA; - LutRGBA = 0; + //if ( LutRGBA ) + // delete [] LutRGBA; + //LutRGBA = 0; } /** @@ -507,33 +578,60 @@ bool PixelReadConvert::ReadAndDecompressJPEGFile( std::ifstream *fp ) // 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 ) - { + if( ZSize == 1 ) + { + 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 ) - { + } + 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 ) ) - { + } + // Warning the inputdata buffer is deleted in the function + if ( gdcm_read_JPEG2000_file( Raw, + (char*)inputdata, inputlength ) ) + { return true; - } - // wow what happen, must be an error - gdcmWarningMacro( "gdcm_read_JPEG2000_file() failed "); - return false; + } + // wow what happen, must be an error + gdcmWarningMacro( "gdcm_read_JPEG2000_file() failed "); + return false; + } + else + { + if( (unsigned int)ZSize != JPEGInfo->GetFragmentCount() ) + { + gdcmErrorMacro( "Sorry GDCM does not handle this type of fragments" ); + return false; + } + // Hopefully every dicom fragment is *exactly* the j2k stream + JPEGFragment *jpegfrag = JPEGInfo->GetFirstFragment(); + char *praw = (char*)Raw; + while( jpegfrag ) + { + unsigned long inputlength = jpegfrag->GetLength(); + char *inputdata = new char[inputlength]; + fp->seekg( jpegfrag->GetOffset(), std::ios::beg); + fp->read(inputdata, jpegfrag->GetLength()); + // Warning the inputdata buffer is deleted in the function + gdcm_read_JPEG2000_file( praw, + inputdata, inputlength) ; + praw += XSize*YSize*SamplesPerPixel*(BitsAllocated/8); + jpegfrag = JPEGInfo->GetNextFragment(); + } + return true; + } } else if ( IsJPEGLS ) { @@ -589,7 +687,13 @@ bool PixelReadConvert::ReadAndDecompressJPEGFile( std::ifstream *fp ) int length = XSize * YSize * ZSize * SamplesPerPixel; int numberBytes = BitsAllocated / 8; - JPEGInfo->DecompressFromFile(fp, Raw, BitsStored, numberBytes, length ); + // to avoid major troubles when BitsStored == 8 && BitsAllocated==16 ! + int dummy; + if (BitsStored == 8 && BitsAllocated==16) + dummy = 16; + else + dummy = BitsStored; + JPEGInfo->DecompressFromFile(fp, Raw, dummy, numberBytes, length ); return true; } } @@ -817,7 +921,7 @@ void PixelReadConvert::BuildLUTRGBA() *a16 = 1; // Alpha component a16 += 4; } -/* Just to 'see' the LUT, at debug time +// Just to 'see' the LUT, at debug time // Don't remove this commented out code. a16=(uint16_t*)LutRGBA; @@ -827,12 +931,12 @@ void PixelReadConvert::BuildLUTRGBA() << *(a16+2) << " " << *(a16+3) << std::endl; a16+=4; } -*/ + } } /** - * \brief Swap the bytes, according to \ref SwapCode. + * \brief Swap the bytes, according to SwapCode. */ void PixelReadConvert::ConvertSwapZone() { @@ -1059,27 +1163,38 @@ bool PixelReadConvert::ConvertReArrangeBits() throw ( FormatError ) { // pmask : to mask the 'unused bits' (may contain overlays) uint16_t pmask = 0xffff; - pmask = pmask >> ( BitsAllocated - BitsStored ); + + // It's up to the user to decide if he wants to ignore overlays (if any), + // not to gdcm, without asking. + // default is NOT TO LOAD, in order not to confuse ITK users (and others!). + + if ( !FH->GetKeepOverlays() ) // mask spurious bits ! (overlay are NOT loaded!) + { + pmask = pmask >> ( BitsAllocated - BitsStored ); + } + // else : it's up to the user to manage the 'pixels + overlays' he just loaded! uint16_t *deb = (uint16_t*)Raw; if ( !PixelSign ) // Pixels are unsigned { for(int i = 0; i> (BitsStored - HighBitPosition - 1)) & pmask; deb++; } } else // Pixels are signed { + // Hope there is never ACR-NEMA-like overlays within signed pixels (?!?) + // 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 ); - + for(int i = 0; i> (BitsStored - HighBitPosition - 1); @@ -1181,9 +1296,9 @@ void PixelReadConvert::ConvertYcBcRPlanesToRGBPixels() // 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 ); @@ -1193,7 +1308,7 @@ void PixelReadConvert::ConvertYcBcRPlanesToRGBPixels() // ftp://medical.nema.org/medical/dicom/final/sup61_ft.pdf // and be *very* affraid // - + /// \todo : find an example to see how 3rd dim and 4th dim work together int l = XSize * YSize * TSize; int nbFrames = ZSize; @@ -1329,7 +1444,7 @@ void PixelReadConvert::ComputeRawAndRGBSizes() 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() ) + // ReadAndDecompress12BitsTo16Bits() ) if ( BitsAllocated == 12 ) { bitsAllocated = 16; @@ -1346,6 +1461,8 @@ void PixelReadConvert::ComputeRawAndRGBSizes() { RGBSize = RawSize; } + RawSize += RawSize%2; + RGBSize += RGBSize%2; } /// Allocates room for RGB Pixels @@ -1435,7 +1552,6 @@ void PixelReadConvert::CallEndMethod() CommandManager::ExecuteCommand(FH,CMD_ENDPROGRESS); } - //----------------------------------------------------------------------------- } // end namespace gdcm