1 /*=========================================================================
4 Module: $RCSfile: gdcmPixelReadConvert.cxx,v $
6 Date: $Date: 2007/10/03 09:35:27 $
7 Version: $Revision: 1.125 $
9 Copyright (c) CREATIS (Centre de Recherche et d'Applications en Traitement de
10 l'Image). All rights reserved. See Doc/License.txt or
11 http://www.creatis.insa-lyon.fr/Public/Gdcm/License.html for details.
13 This software is distributed WITHOUT ANY WARRANTY; without even
14 the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15 PURPOSE. See the above copyright notices for more information.
17 =========================================================================*/
19 #include "gdcmPixelReadConvert.h"
20 #include "gdcmDebug.h"
22 #include "gdcmGlobal.h"
24 #include "gdcmDocEntry.h"
25 #include "gdcmRLEFramesInfo.h"
26 #include "gdcmJPEGFragmentsInfo.h"
27 #include "gdcmSegmentedPalette.h"
30 #include <stdio.h> //for sscanf
32 #if defined(__BORLANDC__)
33 #include <mem.h> // for memset
36 namespace GDCM_NAME_SPACE
39 //bool ReadMPEGFile (std::ifstream *fp, char *inputdata, size_t lenght);
40 bool gdcm_read_JPEG2000_file (void* raw,
41 char *inputdata, size_t inputlength);
42 //-----------------------------------------------------------------------------
43 #define str2num(str, typeNum) *((typeNum *)(str))
45 //-----------------------------------------------------------------------------
46 // Constructor / Destructor
48 PixelReadConvert::PixelReadConvert()
64 /// Canonical Destructor
65 PixelReadConvert::~PixelReadConvert()
70 //-----------------------------------------------------------------------------
73 * \brief Predicate to know whether the image[s] (once Raw) is RGB.
74 * \note See comments of ConvertHandleColor
76 bool PixelReadConvert::IsRawRGB()
79 || PlanarConfiguration == 2
87 * \brief Gets various usefull informations from the file header
88 * @param file gdcm::File pointer
89 * @param fileHelper gdcm::FileHelper pointer
91 void PixelReadConvert::GrabInformationsFromFile( File *file,
92 FileHelper *fileHelper )
94 // Number of Bits Allocated for storing a Pixel is defaulted to 16
95 // when absent from the file.
96 BitsAllocated = file->GetBitsAllocated();
97 if ( BitsAllocated == 0 )
101 else if ( BitsAllocated > 8 && BitsAllocated < 16 && BitsAllocated != 12 )
106 // Number of "Bits Stored", defaulted to number of "Bits Allocated"
107 // when absent from the file.
108 BitsStored = file->GetBitsStored();
109 if ( BitsStored == 0 )
111 BitsStored = BitsAllocated;
114 // High Bit Position, defaulted to "Bits Allocated" - 1
115 HighBitPosition = file->GetHighBitPosition();
116 if ( HighBitPosition == 0 )
118 HighBitPosition = BitsAllocated - 1;
121 XSize = file->GetXSize();
122 YSize = file->GetYSize();
123 ZSize = file->GetZSize();
124 TSize = file->GetTSize();
125 SamplesPerPixel = file->GetSamplesPerPixel();
126 //PixelSize = file->GetPixelSize(); Useless
127 PixelSign = file->IsSignedPixelData();
128 SwapCode = file->GetSwapCode();
130 IsPrivateGETransferSyntax = IsMPEG
131 = IsJPEG2000 = IsJPEGLS = IsJPEGLossy
132 = IsJPEGLossless = IsRLELossless
135 if (! file->IsDicomV3() ) // Should be ACR-NEMA file
141 std::string ts = file->GetTransferSyntax();
144 while (true) // shorter to write than 'if elseif elseif elseif' ...
146 // mind the order : check the most usual first.
147 if( IsRaw = Global::GetTS()->GetSpecialTransferSyntax(ts) == TS::ExplicitVRLittleEndian) break;
148 if( IsRaw = Global::GetTS()->GetSpecialTransferSyntax(ts) == TS::ImplicitVRLittleEndian ) break;
149 if( IsRaw = Global::GetTS()->GetSpecialTransferSyntax(ts) == TS::ExplicitVRBigEndian) break;
150 if( IsRaw = Global::GetTS()->GetSpecialTransferSyntax(ts) == TS::ImplicitVRBigEndianPrivateGE) break;
151 // DeflatedExplicitVRLittleEndian syntax means the whole Dataset (Header + Pixels) is compressed !
152 // Not dealt with ! (Parser hangs)
153 //if( IsRaw = Global::GetTS()->GetSpecialTransferSyntax(ts) == TS::DeflatedExplicitVRLittleEndian) break;
156 // cache whether this is a strange GE transfer syntax (which uses
157 // a little endian transfer syntax for the header and a big endian
158 // transfer syntax for the pixel data).
159 IsPrivateGETransferSyntax =
160 ( Global::GetTS()->GetSpecialTransferSyntax(ts) == TS::ImplicitVRBigEndianPrivateGE );
162 IsMPEG = IsJPEG2000 = IsJPEGLS = IsJPEGLossy = IsJPEGLossless = IsRLELossless = false;
167 // mind the order : check the most usual first.
168 if( IsJPEGLossy = Global::GetTS()->IsJPEGLossy(ts) ) break;
169 if( IsJPEGLossless = Global::GetTS()->IsJPEGLossless(ts) ) break;
170 if( IsRLELossless = Global::GetTS()->IsRLELossless(ts) ) break;
171 if( IsJPEG2000 = Global::GetTS()->IsJPEG2000(ts) ) break;
172 if( IsMPEG = Global::GetTS()->IsMPEG(ts) ) break;
173 if( IsJPEGLS = Global::GetTS()->IsJPEGLS(ts) ) break;
174 // DeflatedExplicitVRLittleEndian is considered as 'Unexpected' (we don't know yet how to process !)
175 gdcmWarningMacro("Unexpected Transfer Syntax :[" << ts << "]");
181 PixelOffset = file->GetPixelOffset();
182 PixelDataLength = file->GetPixelAreaLength();
183 RLEInfo = file->GetRLEInfo();
184 JPEGInfo = file->GetJPEGInfo();
186 IsMonochrome = file->IsMonochrome();
187 IsMonochrome1 = file->IsMonochrome1();
188 IsPaletteColor = file->IsPaletteColor();
189 IsYBRFull = file->IsYBRFull();
191 PlanarConfiguration = file->GetPlanarConfiguration();
193 /////////////////////////////////////////////////////////////////
195 HasLUT = file->HasLUT();
198 // Just in case some access to a File element requires disk access.
199 LutRedDescriptor = file->GetEntryString( 0x0028, 0x1101 );
200 LutGreenDescriptor = file->GetEntryString( 0x0028, 0x1102 );
201 LutBlueDescriptor = file->GetEntryString( 0x0028, 0x1103 );
202 // Is it a Segmented Palette ? Check if we find the red one:
203 if( file->GetDocEntry(0x0028,0x1221) ) // no need to check for blue & green
205 GDCM_NAME_SPACE::TagKey DCM_RedPaletteColorLookupTableDescriptor (0x0028, 0x1101);
206 GDCM_NAME_SPACE::TagKey DCM_GreenPaletteColorLookupTableDescriptor (0x0028, 0x1102);
207 GDCM_NAME_SPACE::TagKey DCM_BluePaletteColorLookupTableDescriptor (0x0028, 0x1103);
209 GDCM_NAME_SPACE::TagKey DCM_SegmentedRedPaletteColorLookupTableData (0x0028, 0x1221);
210 GDCM_NAME_SPACE::TagKey DCM_SegmentedGreenPaletteColorLookupTableData (0x0028, 0x1222);
211 GDCM_NAME_SPACE::TagKey DCM_SegmentedBluePaletteColorLookupTableData (0x0028, 0x1223);
214 LutRedData = new uint8_t[65535*2]; // FIXME: leak
215 LutGreenData = new uint8_t[65535*2];
216 LutBlueData = new uint8_t[65535*2];
217 // TODO need to check file is indeed PALETTE COLOR:
218 ReadPaletteInto(file, DCM_RedPaletteColorLookupTableDescriptor,
219 DCM_SegmentedRedPaletteColorLookupTableData,LutRedData);
220 ReadPaletteInto(file, DCM_GreenPaletteColorLookupTableDescriptor,
221 DCM_SegmentedGreenPaletteColorLookupTableData,LutGreenData);
222 ReadPaletteInto(file, DCM_BluePaletteColorLookupTableDescriptor,
223 DCM_SegmentedBluePaletteColorLookupTableData,LutBlueData);
229 // FIXME : The following comment is probabely meaningless, since LUT are *always*
230 // loaded at parsing time, whatever their length is.
232 // Depending on the value of Document::MAX_SIZE_LOAD_ELEMENT_VALUE
233 // [ refer to invocation of Document::SetMaxSizeLoadEntry() in
234 // Document::Document() ], the loading of the value (content) of a
235 // [Bin|Val]Entry occurence migth have been hindered (read simply NOT
236 // loaded). Hence, we first try to obtain the LUTs data from the file
237 // and when this fails we read the LUTs data directly from disk.
238 // \TODO Reading a [Bin|Val]Entry directly from disk is a kludge.
239 // We should NOT bypass the [Bin|Val]Entry class. Instead
240 // an access to an UNLOADED content of a [Bin|Val]Entry occurence
241 // (e.g. DataEntry::GetBinArea()) should force disk access from
242 // within the [Bin|Val]Entry class itself. The only problem
243 // is that the [Bin|Val]Entry is unaware of the FILE* is was
244 // parsed from. Fix that. FIXME.
247 file->LoadEntryBinArea(0x0028, 0x1201);
248 LutRedData = (uint8_t*)file->GetEntryBinArea( 0x0028, 0x1201 );
251 gdcmWarningMacro("Unable to read Red Palette Color Lookup Table data");
255 file->LoadEntryBinArea(0x0028, 0x1202);
256 LutGreenData = (uint8_t*)file->GetEntryBinArea(0x0028, 0x1202 );
259 gdcmWarningMacro("Unable to read Green Palette Color Lookup Table data");
263 file->LoadEntryBinArea(0x0028, 0x1203);
264 LutBlueData = (uint8_t*)file->GetEntryBinArea( 0x0028, 0x1203 );
267 gdcmWarningMacro("Unable to read Blue Palette Color Lookup Table data");
273 ComputeRawAndRGBSizes();
276 /// \brief Reads from disk and decompresses Pixels
277 bool PixelReadConvert::ReadAndDecompressPixelData( std::ifstream *fp )
279 // ComputeRawAndRGBSizes is already made by
280 // ::GrabInformationsFromfile. So, the structure sizes are
284 //////////////////////////////////////////////////
285 //// First stage: get our hands on the Pixel Data.
288 gdcmWarningMacro( "Unavailable file pointer." );
292 fp->seekg( PixelOffset, std::ios::beg );
293 if ( fp->fail() || fp->eof() )
295 gdcmWarningMacro( "Unable to find PixelOffset in file." );
301 //////////////////////////////////////////////////
303 CallStartMethod(); // for progress bar
304 unsigned int count = 0;
305 unsigned int frameSize;
306 unsigned int bitsAllocated = BitsAllocated;
307 //if(bitsAllocated == 12)
308 if(bitsAllocated > 8 && bitsAllocated < 16)
310 frameSize = XSize*YSize*SamplesPerPixel*bitsAllocated/8;
312 //// Second stage: read from disk and decompress.
314 if ( BitsAllocated == 12 ) // We suppose 'BitsAllocated' = 12 only exist for uncompressed files
316 ReadAndDecompress12BitsTo16Bits( fp);
320 // This problem can be found when some obvious informations are found
321 // after the field containing the image data. In this case, these
322 // bad data are added to the size of the image (in the PixelDataLength
323 // variable). But RawSize is the right size of the image !
324 if ( PixelDataLength != RawSize )
326 gdcmWarningMacro( "Mismatch between PixelReadConvert : "
327 << PixelDataLength << " and RawSize : " << RawSize );
330 //todo : is it the right patch?
331 char *raw = (char*)Raw;
332 uint32_t remainingLength;
334 unsigned int lengthToRead;
336 if ( PixelDataLength > RawSize )
337 lengthToRead = RawSize;
339 lengthToRead = PixelDataLength;
341 // perform a frame by frame reading
342 remainingLength = lengthToRead;
343 unsigned int nbFrames = lengthToRead / frameSize;
344 for (i=0;i<nbFrames; i++)
346 Progress = (float)(count+1)/(float)nbFrames;
347 fp->read( raw, frameSize);
349 remainingLength -= frameSize;
352 if (remainingLength !=0 )
353 fp->read( raw, remainingLength);
355 if ( fp->fail() || fp->eof())
357 gdcmWarningMacro( "Reading of Raw pixel data failed." );
361 else if ( IsRLELossless )
363 if ( ! RLEInfo->DecompressRLEFile
364 ( fp, Raw, XSize, YSize, ZSize, TSize, BitsAllocated ) )
366 gdcmWarningMacro( "RLE decompressor failed." );
372 //gdcmWarningMacro( "Sorry, MPEG not yet taken into account" );
374 // fp has already been seek to start of mpeg
375 //ReadMPEGFile(fp, (char*)Raw, PixelDataLength);
380 // Default case concerns JPEG family
381 if ( ! ReadAndDecompressJPEGFile( fp ) )
383 gdcmWarningMacro( "JPEG decompressor ( ReadAndDecompressJPEGFile()"
384 << " method ) failed." );
389 ////////////////////////////////////////////
390 //// Third stage: twigle the bytes and bits.
391 ConvertReorderEndianity();
392 ConvertReArrangeBits();
393 ConvertFixGreyLevels();
394 if (UserFunction) // user is allowed to Mirror, TopDown, Rotate,...the image
395 UserFunction( Raw, FileInternal);
396 ConvertHandleColor();
401 /// Deletes Pixels Area
402 void PixelReadConvert::Squeeze()
413 // delete [] LutRGBA;
418 * \brief Build the RGB image from the Raw image and the LUTs.
420 bool PixelReadConvert::BuildRGBImage()
424 // The job is already done.
430 // The job can't be done
437 // The job can't be done
441 gdcmDebugMacro( "--> BuildRGBImage" );
447 if ( BitsAllocated <= 8 )
449 uint8_t *localRGB = RGB;
450 for (size_t i = 0; i < RawSize; ++i )
453 *localRGB++ = LutRGBA[j];
454 *localRGB++ = LutRGBA[j+1];
455 *localRGB++ = LutRGBA[j+2];
459 else // deal with 16 bits pixels and 16 bits Palette color
461 uint16_t *localRGB = (uint16_t *)RGB;
462 for (size_t i = 0; i < RawSize/2; ++i )
464 j = ((uint16_t *)Raw)[i] * 4;
465 *localRGB++ = ((uint16_t *)LutRGBA)[j];
466 *localRGB++ = ((uint16_t *)LutRGBA)[j+1];
467 *localRGB++ = ((uint16_t *)LutRGBA)[j+2];
474 //-----------------------------------------------------------------------------
477 //-----------------------------------------------------------------------------
480 * \brief Read from file a 12 bits per pixel image and decompress it
481 * into a 16 bits per pixel image.
483 void PixelReadConvert::ReadAndDecompress12BitsTo16Bits( std::ifstream *fp )
484 throw ( FormatError )
486 /// \todo Fix the 3D, 4D pb
487 int nbPixels = XSize * YSize * TSize;
488 uint16_t *localDecompres = (uint16_t*)Raw;
490 for( int p = 0; p < nbPixels; p += 2 )
494 fp->read( (char*)&b0, 1);
495 if ( fp->fail() || fp->eof() )
497 throw FormatError( "PixelReadConvert::ReadAndDecompress12BitsTo16Bits()",
498 "Unfound first block" );
501 fp->read( (char*)&b1, 1 );
502 if ( fp->fail() || fp->eof())
504 throw FormatError( "PixelReadConvert::ReadAndDecompress12BitsTo16Bits()",
505 "Unfound second block" );
508 fp->read( (char*)&b2, 1 );
509 if ( fp->fail() || fp->eof())
511 throw FormatError( "PixelReadConvert::ReadAndDecompress12BitsTo16Bits()",
512 "Unfound second block" );
515 // Two steps are necessary to please VC++
517 // 2 pixels 12bit = [0xABCDEF]
518 // 2 pixels 16bit = [0x0ABD] + [0x0FCE]
520 *localDecompres++ = ((b0 >> 4) << 8) + ((b0 & 0x0f) << 4) + (b1 & 0x0f);
522 *localDecompres++ = ((b2 & 0x0f) << 8) + ((b1 >> 4) << 4) + (b2 >> 4);
524 /// \todo JPR Troubles expected on Big-Endian processors ?
529 * \brief Reads from disk the Pixel Data of JPEG Dicom encapsulated
530 * file and decompress it.
531 * @param fp File Pointer
534 bool PixelReadConvert::ReadAndDecompressJPEGFile( std::ifstream *fp )
538 // make sure this is the right JPEG compression
539 assert( !IsJPEGLossless || !IsJPEGLossy || !IsJPEGLS );
540 // FIXME this is really ugly but it seems I have to load the complete
541 // jpeg2000 stream to use jasper:
542 // I don't think we'll ever be able to deal with multiple fragments properly
546 unsigned long inputlength = 0;
547 JPEGFragment *jpegfrag = JPEGInfo->GetFirstFragment();
550 inputlength += jpegfrag->GetLength();
551 jpegfrag = JPEGInfo->GetNextFragment();
553 gdcmAssertMacro( inputlength != 0);
554 uint8_t *inputdata = new uint8_t[inputlength];
555 char *pinputdata = (char*)inputdata;
556 jpegfrag = JPEGInfo->GetFirstFragment();
559 fp->seekg( jpegfrag->GetOffset(), std::ios::beg);
560 fp->read(pinputdata, jpegfrag->GetLength());
561 pinputdata += jpegfrag->GetLength();
562 jpegfrag = JPEGInfo->GetNextFragment();
564 // Warning the inputdata buffer is deleted in the function
565 if ( gdcm_read_JPEG2000_file( Raw,
566 (char*)inputdata, inputlength ) )
570 // wow what happen, must be an error
571 gdcmWarningMacro( "gdcm_read_JPEG2000_file() failed ");
576 if( (unsigned int)ZSize != JPEGInfo->GetFragmentCount() )
578 gdcmErrorMacro( "Sorry GDCM does not handle this type of fragments" );
581 // Hopefully every dicom fragment is *exactly* the j2k stream
582 JPEGFragment *jpegfrag = JPEGInfo->GetFirstFragment();
583 char *praw = (char*)Raw;
586 unsigned long inputlength = jpegfrag->GetLength();
587 char *inputdata = new char[inputlength];
588 fp->seekg( jpegfrag->GetOffset(), std::ios::beg);
589 fp->read(inputdata, jpegfrag->GetLength());
590 // Warning the inputdata buffer is deleted in the function
591 gdcm_read_JPEG2000_file( praw,
592 inputdata, inputlength) ;
593 praw += XSize*YSize*SamplesPerPixel*(BitsAllocated/8);
594 jpegfrag = JPEGInfo->GetNextFragment();
601 // make sure this is the right JPEG compression
602 assert( !IsJPEGLossless || !IsJPEGLossy || !IsJPEG2000 );
603 // WARNING : JPEG-LS is NOT the 'classical' Jpeg Lossless :
604 // [JPEG-LS is the basis for new lossless/near-lossless compression
605 // standard for continuous-tone images intended for JPEG2000. The standard
606 // is based on the LOCO-I algorithm (LOw COmplexity LOssless COmpression
607 // for Images) developed at Hewlett-Packard Laboratories]
609 // see http://datacompression.info/JPEGLS.shtml
612 std::cerr << "count:" << JPEGInfo->GetFragmentCount() << std::endl;
613 unsigned long inputlength = 0;
614 JPEGFragment *jpegfrag = JPEGInfo->GetFirstFragment();
617 inputlength += jpegfrag->GetLength();
618 jpegfrag = JPEGInfo->GetNextFragment();
620 gdcmAssertMacro( inputlength != 0);
621 uint8_t *inputdata = new uint8_t[inputlength];
622 char *pinputdata = (char*)inputdata;
623 jpegfrag = JPEGInfo->GetFirstFragment();
626 fp->seekg( jpegfrag->GetOffset(), std::ios::beg);
627 fp->read(pinputdata, jpegfrag->GetLength());
628 pinputdata += jpegfrag->GetLength();
629 jpegfrag = JPEGInfo->GetNextFragment();
632 //fp->read((char*)Raw, PixelDataLength);
634 std::ofstream out("/tmp/jpegls.jpg");
635 out.write((char*)inputdata, inputlength);
640 gdcmWarningMacro( "Sorry, JPEG-LS not yet taken into account" );
641 fp->seekg( JPEGInfo->GetFirstFragment()->GetOffset(), std::ios::beg);
642 // if ( ! gdcm_read_JPEGLS_file( fp,Raw ) )
647 // make sure this is the right JPEG compression
648 assert( !IsJPEGLS || !IsJPEG2000 );
649 // Precompute the offset localRaw will be shifted with
650 int length = XSize * YSize * ZSize * SamplesPerPixel;
651 int numberBytes = BitsAllocated / 8;
653 // to avoid major troubles when BitsStored == 8 && BitsAllocated==16 !
655 if (BitsStored == 8 && BitsAllocated==16)
659 JPEGInfo->DecompressFromFile(fp, Raw, dummy, numberBytes, length );
665 * \brief Build Red/Green/Blue/Alpha LUT from File when :
666 * - (0028,0004) : Photometric Interpretation == [PALETTE COLOR ]
668 * - (0028,1101),(0028,1102),(0028,1102)
669 * xxx Palette Color Lookup Table Descriptor are found
671 * - (0028,1201),(0028,1202),(0028,1202)
672 * xxx Palette Color Lookup Table Data - are found
673 * \warning does NOT deal with :
674 * - 0028 1100 Gray Lookup Table Descriptor (Retired)
675 * - 0028 1221 Segmented Red Palette Color Lookup Table Data
676 * - 0028 1222 Segmented Green Palette Color Lookup Table Data
677 * - 0028 1223 Segmented Blue Palette Color Lookup Table Data
678 * no known Dicom reader deals with them :-(
679 * @return a RGBA Lookup Table
681 void PixelReadConvert::BuildLUTRGBA()
684 // Note to code reviewers :
685 // The problem is *much more* complicated, since a lot of manufacturers
686 // Don't follow the norm :
687 // have a look at David Clunie's remark at the end of this .cxx file.
694 // http://www.barre.nom.fr/medical/dicom2/limitations.html#Color%20Lookup%20Tables
696 if ( ! IsPaletteColor )
701 if ( LutRedDescriptor == GDCM_UNFOUND
702 || LutGreenDescriptor == GDCM_UNFOUND
703 || LutBlueDescriptor == GDCM_UNFOUND )
705 gdcmWarningMacro( "(At least) a LUT Descriptor is missing" );
709 ////////////////////////////////////////////
710 // Extract the info from the LUT descriptors
711 int lengthR; // Red LUT length in Bytes
712 int debR; // Subscript of the first Lut Value
713 int nbitsR; // Lut item size (in Bits)
714 int nbRead; // nb of items in LUT descriptor (must be = 3)
716 nbRead = sscanf( LutRedDescriptor.c_str(),
718 &lengthR, &debR, &nbitsR );
721 gdcmWarningMacro( "Wrong Red LUT descriptor" );
723 int lengthG; // Green LUT length in Bytes
724 int debG; // Subscript of the first Lut Value
725 int nbitsG; // Lut item size (in Bits)
727 nbRead = sscanf( LutGreenDescriptor.c_str(),
729 &lengthG, &debG, &nbitsG );
732 gdcmWarningMacro( "Wrong Green LUT descriptor" );
735 int lengthB; // Blue LUT length in Bytes
736 int debB; // Subscript of the first Lut Value
737 int nbitsB; // Lut item size (in Bits)
738 nbRead = sscanf( LutRedDescriptor.c_str(),
740 &lengthB, &debB, &nbitsB );
743 gdcmWarningMacro( "Wrong Blue LUT descriptor" );
746 gdcmDebugMacro(" lengthR " << lengthR << " debR "
747 << debR << " nbitsR " << nbitsR);
748 gdcmDebugMacro(" lengthG " << lengthG << " debG "
749 << debG << " nbitsG " << nbitsG);
750 gdcmDebugMacro(" lengthB " << lengthB << " debB "
751 << debB << " nbitsB " << nbitsB);
753 if ( !lengthR ) // if = 2^16, this shall be 0 see : CP-143
755 if ( !lengthG ) // if = 2^16, this shall be 0
757 if ( !lengthB ) // if = 2^16, this shall be 0
760 ////////////////////////////////////////////////////////
762 if ( ( ! LutRedData ) || ( ! LutGreenData ) || ( ! LutBlueData ) )
764 gdcmWarningMacro( "(At least) a LUT is missing" );
768 // -------------------------------------------------------------
770 if ( BitsAllocated <= 8 )
772 // forge the 4 * 8 Bits Red/Green/Blue/Alpha LUT
773 LutRGBA = new uint8_t[ 1024 ]; // 256 * 4 (R, G, B, Alpha)
778 memset( LutRGBA, 0, 1024 );
781 if ( ( nbitsR == 16 ) && ( BitsAllocated == 8 ) )
783 // when LUT item size is different than pixel size
784 mult = 2; // high byte must be = low byte
788 // See PS 3.3-2003 C.11.1.1.2 p 619
792 // if we get a black image, let's just remove the '+1'
793 // from 'i*mult+1' and check again
794 // if it works, we shall have to check the 3 Palettes
795 // to see which byte is ==0 (first one, or second one)
797 // We give up the checking to avoid some (useless ?) overhead
798 // (optimistic asumption)
802 //take "Subscript of the first Lut Value" (debR,debG,debB) into account!
804 //FIXME : +1 : to get 'low value' byte
805 // Trouble expected on Big Endian Processors ?
806 // 16 BIts Per Pixel Palette Color to be swapped?
808 a = LutRGBA + 0 + debR;
809 for( i=0; i < lengthR; ++i )
811 *a = LutRedData[i*mult+1];
815 a = LutRGBA + 1 + debG;
816 for( i=0; i < lengthG; ++i)
818 *a = LutGreenData[i*mult+1];
822 a = LutRGBA + 2 + debB;
823 for(i=0; i < lengthB; ++i)
825 *a = LutBlueData[i*mult+1];
830 for(i=0; i < 256; ++i)
832 *a = 1; // Alpha component
838 // Probabely the same stuff is to be done for 16 Bits Pixels
839 // with 65536 entries LUT ?!?
840 // Still looking for accurate info on the web :-(
842 gdcmWarningMacro( "Sorry Palette Color Lookup Tables not yet dealt with"
843 << " for 16 Bits Per Pixel images" );
845 // forge the 4 * 16 Bits Red/Green/Blue/Alpha LUT
847 LutRGBA = (uint8_t *)new uint16_t[ 65536*4 ]; // 2^16 * 4 (R, G, B, Alpha)
850 memset( LutRGBA, 0, 65536*4*2 ); // 16 bits = 2 bytes ;-)
852 LutItemNumber = 65536;
858 //take "Subscript of the first Lut Value" (debR,debG,debB) into account!
860 a16 = (uint16_t*)LutRGBA + 0 + debR;
861 for( i=0; i < lengthR; ++i )
863 *a16 = ((uint16_t*)LutRedData)[i];
867 a16 = (uint16_t*)LutRGBA + 1 + debG;
868 for( i=0; i < lengthG; ++i)
870 *a16 = ((uint16_t*)LutGreenData)[i];
874 a16 = (uint16_t*)LutRGBA + 2 + debB;
875 for(i=0; i < lengthB; ++i)
877 *a16 = ((uint16_t*)LutBlueData)[i];
881 a16 = (uint16_t*)LutRGBA + 3 ;
882 for(i=0; i < 65536; ++i)
884 *a16 = 1; // Alpha component
887 /* Just to 'see' the LUT, at debug time
888 // Don't remove this commented out code.
890 a16=(uint16_t*)LutRGBA;
891 for (int j=0;j<65536;j++)
893 std::cout << *a16 << " " << *(a16+1) << " "
894 << *(a16+2) << " " << *(a16+3) << std::endl;
902 * \brief Swap the bytes, according to SwapCode.
904 void PixelReadConvert::ConvertSwapZone()
908 // If this file is 'ImplicitVR BigEndian PrivateGE Transfer Syntax',
909 // then the header is in little endian format and the pixel data is in
910 // big endian format. When reading the header, GDCM has already established
911 // a byte swapping code suitable for this machine to read the
912 // header. In TS::ImplicitVRBigEndianPrivateGE, this code will need
913 // to be switched in order to read the pixel data. This must be
914 // done REGARDLESS of the processor endianess!
916 // Example: Assume we are on a little endian machine. When
917 // GDCM reads the header, the header will match the machine
918 // endianess and the swap code will be established as a no-op.
919 // When GDCM reaches the pixel data, it will need to switch the
920 // swap code to do big endian to little endian conversion.
922 // Now, assume we are on a big endian machine. When GDCM reads the
923 // header, the header will be recognized as a different endianess
924 // than the machine endianess, and a swap code will be established
925 // to convert from little endian to big endian. When GDCM readers
926 // the pixel data, the pixel data endianess will now match the
927 // machine endianess. But we currently have a swap code that
928 // converts from little endian to big endian. In this case, we
929 // need to switch the swap code to a no-op.
931 // Therefore, in either case, if the file is in
932 // 'ImplicitVR BigEndian PrivateGE Transfer Syntax', then GDCM needs to switch
933 // the byte swapping code when entering the pixel data.
935 int tempSwapCode = SwapCode;
936 if ( IsPrivateGETransferSyntax )
938 gdcmWarningMacro(" IsPrivateGETransferSyntax found; turn the SwapCode");
939 // PrivateGETransferSyntax only exists for 'true' Dicom images
940 // we assume there is no 'exotic' 32 bits endianess!
941 if (SwapCode == 1234)
945 else if (SwapCode == 4321)
951 if ( BitsAllocated == 16 )
953 uint16_t *im16 = (uint16_t*)Raw;
954 switch( tempSwapCode )
961 for( i = 0; i < RawSize / 2; i++ )
963 im16[i]= (im16[i] >> 8) | (im16[i] << 8 );
967 gdcmWarningMacro("SwapCode value (16 bits) not allowed."
971 else if ( BitsAllocated == 32 )
976 uint32_t *im32 = (uint32_t*)Raw;
977 switch ( tempSwapCode )
982 for( i = 0; i < RawSize / 4; i++ )
984 low = im32[i] & 0x0000ffff; // 4321
985 high = im32[i] >> 16;
986 high = ( high >> 8 ) | ( high << 8 );
987 low = ( low >> 8 ) | ( low << 8 );
989 im32[i] = ( s32 << 16 ) | high;
993 for( i = 0; i < RawSize / 4; i++ )
995 low = im32[i] & 0x0000ffff; // 2143
996 high = im32[i] >> 16;
997 high = ( high >> 8 ) | ( high << 8 );
998 low = ( low >> 8 ) | ( low << 8 );
1000 im32[i] = ( s32 << 16 ) | low;
1004 for( i = 0; i < RawSize / 4; i++ )
1006 low = im32[i] & 0x0000ffff; // 3412
1007 high = im32[i] >> 16;
1009 im32[i] = ( s32 << 16 ) | high;
1013 gdcmWarningMacro("SwapCode value (32 bits) not allowed." << tempSwapCode );
1019 * \brief Deal with endianness i.e. re-arange bytes inside the integer
1021 void PixelReadConvert::ConvertReorderEndianity()
1023 if ( BitsAllocated != 8 )
1028 // Special kludge in order to deal with xmedcon broken images:
1029 if ( BitsAllocated == 16
1030 && BitsStored < BitsAllocated
1033 int l = (int)( RawSize / ( BitsAllocated / 8 ) );
1034 uint16_t *deb = (uint16_t *)Raw;
1035 for(int i = 0; i<l; i++)
1037 if ( *deb == 0xffff )
1047 * \brief Deal with Grey levels i.e. re-arange them
1048 * to have low values = dark, high values = bright
1050 void PixelReadConvert::ConvertFixGreyLevels()
1055 uint32_t i; // to please M$VC6
1060 if ( BitsAllocated == 8 )
1062 uint8_t *deb = (uint8_t *)Raw;
1063 for (i=0; i<RawSize; i++)
1071 if ( BitsAllocated == 16 )
1074 for (j=0; j<BitsStored-1; j++)
1076 mask = (mask << 1) +1; // will be fff when BitsStored=12
1079 uint16_t *deb = (uint16_t *)Raw;
1080 for (i=0; i<RawSize/2; i++)
1090 if ( BitsAllocated == 8 )
1092 uint8_t smask8 = 255;
1093 uint8_t *deb = (uint8_t *)Raw;
1094 for (i=0; i<RawSize; i++)
1096 *deb = smask8 - *deb;
1101 if ( BitsAllocated == 16 )
1103 uint16_t smask16 = 65535;
1104 uint16_t *deb = (uint16_t *)Raw;
1105 for (i=0; i<RawSize/2; i++)
1107 *deb = smask16 - *deb;
1116 * \brief Re-arrange the bits within the bytes.
1117 * @return Boolean always true
1119 bool PixelReadConvert::ConvertReArrangeBits() throw ( FormatError )
1122 if ( BitsStored != BitsAllocated )
1124 int l = (int)( RawSize / ( BitsAllocated / 8 ) );
1125 if ( BitsAllocated == 16 )
1127 // pmask : to mask the 'unused bits' (may contain overlays)
1128 uint16_t pmask = 0xffff;
1130 // It's up to the user to decide if he wants to ignore overlays (if any),
1131 // not to gdcm, without asking.
1132 // default is NOT TO LOAD, in order not to confuse ITK users (and others!).
1134 if ( !FH->GetKeepOverlays() ) // mask spurious bits ! (overlay are NOT loaded!)
1136 pmask = pmask >> ( BitsAllocated - BitsStored );
1138 // else : it's up to the user to manage the 'pixels + overlays' he just loaded!
1140 uint16_t *deb = (uint16_t*)Raw;
1142 if ( !PixelSign ) // Pixels are unsigned
1144 for(int i = 0; i<l; i++)
1146 *deb = (*deb >> (BitsStored - HighBitPosition - 1)) & pmask;
1150 else // Pixels are signed
1152 // Hope there is never ACR-NEMA-like overlays within signed pixels (?!?)
1154 // smask : to check the 'sign' when BitsStored != BitsAllocated
1155 uint16_t smask = 0x0001;
1156 smask = smask << ( 16 - (BitsAllocated - BitsStored + 1) );
1157 // nmask : to propagate sign bit on negative values
1158 int16_t nmask = (int16_t)0x8000;
1159 nmask = nmask >> ( BitsAllocated - BitsStored - 1 );
1161 for(int i = 0; i<l; i++)
1163 *deb = *deb >> (BitsStored - HighBitPosition - 1);
1166 *deb = *deb | nmask;
1170 *deb = *deb & pmask;
1176 else if ( BitsAllocated == 32 )
1178 // pmask : to mask the 'unused bits' (may contain overlays)
1179 uint32_t pmask = 0xffffffff;
1180 pmask = pmask >> ( BitsAllocated - BitsStored );
1182 uint32_t *deb = (uint32_t*)Raw;
1186 for(int i = 0; i<l; i++)
1188 *deb = (*deb >> (BitsStored - HighBitPosition - 1)) & pmask;
1194 // smask : to check the 'sign' when BitsStored != BitsAllocated
1195 uint32_t smask = 0x00000001;
1196 smask = smask >> ( 32 - (BitsAllocated - BitsStored +1 ));
1197 // nmask : to propagate sign bit on negative values
1198 int32_t nmask = 0x80000000;
1199 nmask = nmask >> ( BitsAllocated - BitsStored -1 );
1201 for(int i = 0; i<l; i++)
1203 *deb = *deb >> (BitsStored - HighBitPosition - 1);
1205 *deb = *deb | nmask;
1207 *deb = *deb & pmask;
1214 gdcmWarningMacro("Weird image (BitsAllocated !=8, 12, 16, 32)");
1215 throw FormatError( "Weird image !?" );
1222 * \brief Convert (Red plane, Green plane, Blue plane) to RGB pixels
1223 * \warning Works on all the frames at a time
1225 void PixelReadConvert::ConvertRGBPlanesToRGBPixels()
1227 gdcmWarningMacro("--> ConvertRGBPlanesToRGBPixels");
1229 uint8_t *localRaw = Raw;
1230 uint8_t *copyRaw = new uint8_t[ RawSize ];
1231 memmove( copyRaw, localRaw, RawSize );
1233 int l = XSize * YSize * ZSize;
1235 uint8_t *a = copyRaw;
1236 uint8_t *b = copyRaw + l;
1237 uint8_t *c = copyRaw + l + l;
1239 for (int j = 0; j < l; j++)
1241 *(localRaw++) = *(a++);
1242 *(localRaw++) = *(b++);
1243 *(localRaw++) = *(c++);
1249 * \brief Convert (cY plane, cB plane, cR plane) to RGB pixels
1250 * \warning Works on all the frames at a time
1252 void PixelReadConvert::ConvertYcBcRPlanesToRGBPixels()
1254 // Remarks for YBR newbees :
1255 // YBR_FULL works very much like RGB, i.e. three samples per pixel,
1256 // just the color space is YCbCr instead of RGB. This is particularly useful
1257 // for doppler ultrasound where most of the image is grayscale
1258 // (i.e. only populates the Y components) and Cb and Cr are mostly zero,
1259 // except for the few patches of color on the image.
1260 // On such images, RLE achieves a compression ratio that is much better
1261 // than the compression ratio on an equivalent RGB image.
1263 gdcmWarningMacro("--> ConvertYcBcRPlanesToRGBPixels");
1265 uint8_t *localRaw = Raw;
1266 uint8_t *copyRaw = new uint8_t[ RawSize ];
1267 memmove( copyRaw, localRaw, RawSize );
1269 // to see the tricks about YBR_FULL, YBR_FULL_422,
1270 // YBR_PARTIAL_422, YBR_ICT, YBR_RCT have a look at :
1271 // ftp://medical.nema.org/medical/dicom/final/sup61_ft.pdf
1272 // and be *very* affraid
1275 /// \todo : find an example to see how 3rd dim and 4th dim work together
1276 int l = XSize * YSize * TSize;
1277 int nbFrames = ZSize;
1279 uint8_t *a = copyRaw + 0;
1280 uint8_t *b = copyRaw + l;
1281 uint8_t *c = copyRaw + l+ l;
1284 /// We replaced easy to understand but time consuming floating point
1285 /// computations by the 'well known' integer computation counterpart
1287 /// http://lestourtereaux.free.fr/papers/data/yuvrgb.pdf
1288 /// for code optimisation.
1290 for ( int i = 0; i < nbFrames; i++ )
1292 for ( int j = 0; j < l; j++ )
1294 R = 38142 *(*a-16) + 52298 *(*c -128);
1295 G = 38142 *(*a-16) - 26640 *(*c -128) - 12845 *(*b -128);
1296 B = 38142 *(*a-16) + 66093 *(*b -128);
1305 if (R > 255) R = 255;
1306 if (G > 255) G = 255;
1307 if (B > 255) B = 255;
1309 *(localRaw++) = (uint8_t)R;
1310 *(localRaw++) = (uint8_t)G;
1311 *(localRaw++) = (uint8_t)B;
1320 /// \brief Deals with the color decoding i.e. handle:
1321 /// - R, G, B planes (as opposed to RGB pixels)
1322 /// - YBR (various) encodings.
1323 /// - LUT[s] (or "PALETTE COLOR").
1325 void PixelReadConvert::ConvertHandleColor()
1327 //////////////////////////////////
1328 // Deal with the color decoding i.e. handle:
1329 // - R, G, B planes (as opposed to RGB pixels)
1330 // - YBR (various) encodings.
1331 // - LUT[s] (or "PALETTE COLOR").
1333 // The classification in the color decoding schema is based on the blending
1334 // of two Dicom tags values:
1335 // * "Photometric Interpretation" for which we have the cases:
1336 // - [Photo A] MONOCHROME[1|2] pictures,
1337 // - [Photo B] RGB or YBR_FULL_422 (which acts as RGB),
1338 // - [Photo C] YBR_* (with the above exception of YBR_FULL_422)
1339 // - [Photo D] "PALETTE COLOR" which indicates the presence of LUT[s].
1340 // * "Planar Configuration" for which we have the cases:
1341 // - [Planar 0] 0 then Pixels are already RGB
1342 // - [Planar 1] 1 then we have 3 planes : R, G, B,
1343 // - [Planar 2] 2 then we have 1 gray Plane and 3 LUTs
1345 // Now in theory, one could expect some coherence when blending the above
1346 // cases. For example we should not encounter files belonging at the
1347 // time to case [Planar 0] and case [Photo D].
1348 // Alas, this was only theory ! Because in practice some odd (read ill
1349 // formated Dicom) files (e.g. gdcmData/US-PAL-8-10x-echo.dcm) we encounter:
1350 // - "Planar Configuration" = 0,
1351 // - "Photometric Interpretation" = "PALETTE COLOR".
1352 // Hence gdcm will use the folowing "heuristic" in order to be tolerant
1353 // towards Dicom-non-conformant files:
1354 // << whatever the "Planar Configuration" value might be, a
1355 // "Photometric Interpretation" set to "PALETTE COLOR" forces
1356 // a LUT intervention >>
1358 // Now we are left with the following handling of the cases:
1359 // - [Planar 0] OR [Photo A] no color decoding (since respectively
1360 // Pixels are already RGB and monochrome pictures have no color :),
1361 // - [Planar 1] AND [Photo B] handled with ConvertRGBPlanesToRGBPixels()
1362 // - [Planar 1] AND [Photo C] handled with ConvertYcBcRPlanesToRGBPixels()
1363 // - [Planar 2] OR [Photo D] requires LUT intervention.
1365 gdcmDebugMacro("--> ConvertHandleColor "
1366 << "Planar Configuration " << PlanarConfiguration );
1370 // [Planar 2] OR [Photo D]: LUT intervention done outside
1371 gdcmDebugMacro("--> RawRGB : LUT intervention done outside");
1375 if ( PlanarConfiguration == 1 )
1379 // [Planar 1] AND [Photo C] (remember YBR_FULL_422 acts as RGB)
1380 gdcmDebugMacro("--> YBRFull");
1381 ConvertYcBcRPlanesToRGBPixels();
1385 // [Planar 1] AND [Photo C]
1386 gdcmDebugMacro("--> YBRFull");
1387 ConvertRGBPlanesToRGBPixels();
1392 // When planarConf is 0, and RLELossless (forbidden by Dicom norm)
1393 // pixels need to be RGB-fyied anyway
1397 gdcmDebugMacro("--> RLE Lossless");
1398 ConvertRGBPlanesToRGBPixels();
1401 // In *normal *case, when planarConf is 0, pixels are already in RGB
1404 /// Computes the Pixels Size
1405 void PixelReadConvert::ComputeRawAndRGBSizes()
1407 int bitsAllocated = BitsAllocated;
1408 // Number of "Bits Allocated" is fixed to 16 when it's 12, since
1409 // in this case we will expand the image to 16 bits (see
1410 // ReadAndDecompress12BitsTo16Bits() )
1411 if ( BitsAllocated == 12 )
1416 RawSize = XSize * YSize * ZSize * TSize
1417 * ( bitsAllocated / 8 )
1421 RGBSize = 3 * RawSize; // works for 8 and 16 bits per Pixel
1427 RawSize += RawSize%2;
1428 RGBSize += RGBSize%2;
1431 /// Allocates room for RGB Pixels
1432 void PixelReadConvert::AllocateRGB()
1436 RGB = new uint8_t[RGBSize];
1439 /// Allocates room for RAW Pixels
1440 void PixelReadConvert::AllocateRaw()
1444 Raw = new uint8_t[RawSize];
1447 //-----------------------------------------------------------------------------
1450 * \brief Print self.
1451 * @param indent Indentation string to be prepended during printing.
1452 * @param os Stream to print to.
1454 void PixelReadConvert::Print( std::ostream &os, std::string const &indent )
1457 << "--- Pixel information -------------------------"
1460 << "Pixel Data: offset " << PixelOffset
1461 << " x(" << std::hex << PixelOffset << std::dec
1462 << ") length " << PixelDataLength
1463 << " x(" << std::hex << PixelDataLength << std::dec
1464 << ")" << std::endl;
1466 if ( IsRLELossless )
1470 RLEInfo->Print( os, indent );
1474 gdcmWarningMacro("Set as RLE file but NO RLEinfo present.");
1478 if ( IsJPEG2000 || IsJPEGLossless || IsJPEGLossy || IsJPEGLS )
1482 JPEGInfo->Print( os, indent );
1486 gdcmWarningMacro("Set as JPEG file but NO JPEGinfo present.");
1492 * \brief CallStartMethod
1494 void PixelReadConvert::CallStartMethod()
1498 CommandManager::ExecuteCommand(FH,CMD_STARTPROGRESS);
1502 * \brief CallProgressMethod
1504 void PixelReadConvert::CallProgressMethod()
1506 CommandManager::ExecuteCommand(FH,CMD_PROGRESS);
1510 * \brief CallEndMethod
1512 void PixelReadConvert::CallEndMethod()
1515 CommandManager::ExecuteCommand(FH,CMD_ENDPROGRESS);
1518 //-----------------------------------------------------------------------------
1519 } // end namespace gdcm
1521 // Note to developpers :
1522 // Here is a very detailled post from David Clunie, on the troubles caused
1523 // 'non standard' LUT and LUT description
1524 // We shall have to take it into accound in our code.
1529 Subject: Problem with VOI LUTs in Agfa and Fuji CR and GE DX images, was Re: VOI LUT issues
1530 Date: Sun, 06 Feb 2005 17:13:40 GMT
1531 From: David Clunie <dclunie@dclunie.com>
1532 Reply-To: dclunie@dclunie.com
1533 Newsgroups: comp.protocols.dicom
1534 References: <1107553502.040221.189550@o13g2000cwo.googlegroups.com>
1536 > THE LUT that comes with [my] image claims to be 16-bit, but none of the
1537 > values goes higher than 4095. That being said, though, none of my
1538 > original pixel values goes higher than that, either. I have read
1539 > elsewhere on this group that when that happens you are supposed to
1540 > adjust the LUT. Can someone be more specific? There was a thread from
1541 > 2002 where Marco and David were mentioning doing precisely that.
1548 You have encountered the well known "we know what the standard says but
1549 we are going to ignore it and do what we have been doing for almost
1550 a decade regardless" CR vendor bug. Agfa started this, but they are not
1551 the only vendor doing this now; GE and Fuji may have joined the club.
1553 Sadly, one needs to look at the LUT Data, figure out what the maximum
1554 value actually encoded is, and find the next highest power of 2 (e.g.
1555 212 in this case), to figure out what the range of the data is
1556 supposed to be. I have assumed that if the maximum value in the LUT
1557 data is less than a power of 2 minus 1 (e.g. 0xebc) then the intent
1558 of the vendor was not to use the maximum available grayscale range
1559 of the display (e.g. the maximum is 0xfff in this case). An alternative
1560 would be to scale to the actual maximum rather than a power of two.
1562 Very irritating, and in theory not totally reliable if one really
1563 intended the full 16 bits and only used, say 15, but that is extremely
1564 unlikely since everything would be too dark, and this heuristic
1567 There has never been anything in the standard that describes having
1568 to go through these convolutions. Since the only value in the
1569 standard that describes the bit depth of the LUT values is LUT
1570 Descriptor value 3 and that is (usually) always required to be
1571 either 8 or 16, it mystifies me how the creators' of these images
1572 imagine that the receiver is going to divine the range that is intended. Further, the standard is quite explicit that this 3rd
1573 value defines the range of LUT values, but as far as I am aware, all
1574 the vendors are ignoring the standard and indeed sending a third value
1577 This problem is not confined to CR, and is also seen with DX products.
1579 Typically I have seen:
1581 - Agfa CR, which usually (always ?) sends LUTs, values up to 0x0fff
1582 - Fuji CR, which occasionally send LUTs, values up to 0x03ff
1583 - GE DX, for presentation, which always have LUTs, up to 0x3fff
1585 Swissray, Siemens, Philips, Canon and Kodak never seem to send VOI LUTs
1586 at this point (which is a whole other problem). Note that the presence
1587 or absence of a VOI LUT as opposed to window values may be configurable
1588 on the modality in some cases, and I have just looked at what I happen
1589 to have received from a myriad of sites over whose configuration I have
1590 no control. This may be why the majority of Fuji images have no VOI LUTs,
1591 but a few do (or it may be the Siemens system that these Fuji images went
1592 through that perhaps added it). I do have some test Hologic DX images that
1593 are not from a clinical site that do actually get this right (a value
1594 of 12 for the third value and a max of 0xfff).
1596 Since almost every vendor that I have encountered that encodes LUTs
1597 makes this mistake, perhaps it is time to amend the standard to warn
1598 implementor's of receivers and/or sanction this bad behavior. We have
1599 talked about this in the past in WG 6 but so far everyone has been
1600 reluctant to write into the standard such a comment. Maybe it is time
1601 to try again, since if one is not aware of this problem, one cannot
1602 effectively implement display using VOI LUTs, and there is a vast
1603 installed base to contend with.
1605 I did not check presentation states, in which VOI LUTs could also be
1606 encountered, for the prevalence of this mistake, nor did I look at the
1607 encoding of Modality LUT's, which are unusual. Nor did I check digital
1608 mammography images. I would be interested to hear from anyone who has.
1612 PS. The following older thread in this newsgroup discusses this:
1614 "http://groups-beta.google.com/group/comp.protocols.dicom/browse_frm/t hread/6a033444802a35fc/0f0a9a1e35c1468e?q=voi+lut&_done=%2Fgroup%2Fcom p.protocols.dicom%2Fsearch%3Fgroup%3Dcomp.protocols.dicom%26q%3Dvoi+lu t%26qt_g%3D1%26searchnow%3DSearch+this+group%26&_doneTitle=Back+to+Sea rch&&d#0f0a9a1e35c1468e"
1616 PPS. From a historical perspective, the following may be of interest.
1618 In the original standard in 1993, all that was said about this was a
1619 reference to the corresponding such where Modality LUTs are described
1622 "The third value specifies the number of bits for each entry in the
1623 LUT Data. It shall take the value 8 or 16. The LUT Data shall be stored
1624 in a format equivalent to 8 or 16 bits allocated and high bit equal
1627 Since the high bit hint was not apparently explicit enough, a very
1628 early CP, CP 15 (submitted by Agfa as it happens), replaced this with:
1630 "The third value conveys the range of LUT entry values. It shall take
1631 the value 8 or 16, corresponding with the LUT entry value range of
1634 Note: The third value is not required for describing the
1635 LUT data and is only included for informational usage
1636 and for maintaining compatibility with ACRNEMA 2.0.
1638 The LUT Data contains the LUT entry values."
1640 That is how it read in the 1996, 1998 and 1999 editions.
1642 By the 2000 edition, Supplement 33 that introduced presentation states
1643 extensively reworked this entire section and tried to explain this in
1646 "The output range is from 0 to 2^n-1 where n is the third value of LUT
1647 Descriptor. This range is always unsigned."
1649 and also added a note to spell out what the output range meant in the
1652 "9. The output of the Window Center/Width or VOI LUT transformation
1653 is either implicitly scaled to the full range of the display device
1654 if there is no succeeding transformation defined, or implicitly scaled
1655 to the full input range of the succeeding transformation step (such as
1656 the Presentation LUT), if present. See C.11.6.1."
1658 It still reads this way in the 2004 edition.
1660 Note that LUTs in other applications than the general VOI LUT allow for
1661 values other than 8 or 16 in the third value of LUT descriptor to permit
1662 ranges other than 0 to 255 or 65535.
1664 In addition, the DX Image Module specializes the VOI LUT
1665 attributes as follows, in PS 3.3 section C.8.11.3.1.5 (added in Sup 32):
1667 "The third value specifies the number of bits for each entry in the LUT
1668 Data (analogous to ìbits storedî). It shall be between 10-16. The LUT
1669 Data shall be stored in a format equivalent to 16 ìbits allocatedî and
1670 ìhigh bitî equal to ìbits storedî - 1. The third value conveys the range
1671 of LUT entry values. These unsigned LUT entry values shall range between
1672 0 and 2^n-1, where n is the third value of the LUT Descriptor."
1674 So in the case of the GE DX for presentation images, the third value of
1675 LUT descriptor is allowed to be and probably should be 14 rather than 16.