1 /*=========================================================================
4 Module: $RCSfile: gdcmPixelReadConvert.cxx,v $
6 Date: $Date: 2008/05/20 09:22:03 $
7 Version: $Revision: 1.128 $
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'
175 // (we don't know yet how to process !)
176 gdcmWarningMacro("Unexpected Transfer Syntax :[" << ts << "]");
182 PixelOffset = file->GetPixelOffset();
183 PixelDataLength = file->GetPixelAreaLength();
184 RLEInfo = file->GetRLEInfo();
185 JPEGInfo = file->GetJPEGInfo();
187 IsMonochrome = file->IsMonochrome();
188 IsMonochrome1 = file->IsMonochrome1();
189 IsPaletteColor = file->IsPaletteColor();
190 IsYBRFull = file->IsYBRFull();
192 PlanarConfiguration = file->GetPlanarConfiguration();
194 /////////////////////////////////////////////////////////////////
196 HasLUT = file->HasLUT();
201 The three values of Palette Color Lookup Table Descriptor (0028,1101-1103)
202 describe the format of the Lookup Table Data in the corresponding
203 Data Element (0028,1201-1203) or (0028,1221-1223).
205 The first value is the number of entries in the lookup table.
206 When the number of table entries is equal to 2**16 then this value shall be 0.
208 The second value is the first stored pixel value mapped.
209 This pixel value is mapped to the first entry in the Lookup Table Data.
210 All image pixel values less than the first entry value mapped are also
211 mapped to the first entry in the Lookup Table Data.
212 An image pixel value one greater than the first entry value mapped is
213 mapped to the second entry in the Lookup Table Data.
214 Subsequent image pixel values are mapped to the subsequent entries in
215 the Lookup Table Data up to an image pixel value equal to number of
216 entries + first entry value mapped - 1 which is mapped to the last entry
217 in the Lookup Table Data.
218 Image pixel values greater than or equal to number of entries + first entry
219 value mapped are also mapped to the last entry in the Lookup Table Data.
221 The third value specifies the number of bits for each entry in the Lookup
222 Table Data. It shall take the value of 8 or 16.
223 The LUT Data shall be stored in a format equivalent to 8 or 16 bits
224 allocated where the high bit is equal to bits allocated-1.
226 When the Palette Color Lookup Table Descriptor (0028,1101-1103) are used as
227 part of the Palette Color Lookup Table Module, the third value shall be
230 Note: A value of 16 indicates the Lookup Table Data will range from (0,0,0)
231 minimum intensity to (65535,65535,65535) maximum intensity.
235 // Just in case some access to a File element requires disk access.
236 LutRedDescriptor = file->GetEntryString( 0x0028, 0x1101 );
237 LutGreenDescriptor = file->GetEntryString( 0x0028, 0x1102 );
238 LutBlueDescriptor = file->GetEntryString( 0x0028, 0x1103 );
239 // Is it a Segmented Palette ? Check if we find the red one:
240 if( file->GetDocEntry(0x0028,0x1221) ) // no need to check for blue & green
242 GDCM_NAME_SPACE::TagKey DCM_RedPaletteColorLookupTableDescriptor (0x0028, 0x1101);
243 GDCM_NAME_SPACE::TagKey DCM_GreenPaletteColorLookupTableDescriptor (0x0028, 0x1102);
244 GDCM_NAME_SPACE::TagKey DCM_BluePaletteColorLookupTableDescriptor (0x0028, 0x1103);
246 GDCM_NAME_SPACE::TagKey DCM_SegmentedRedPaletteColorLookupTableData (0x0028, 0x1221);
247 GDCM_NAME_SPACE::TagKey DCM_SegmentedGreenPaletteColorLookupTableData (0x0028, 0x1222);
248 GDCM_NAME_SPACE::TagKey DCM_SegmentedBluePaletteColorLookupTableData (0x0028, 0x1223);
251 LutRedData = new uint8_t[65535*2]; // FIXME: leak
252 LutGreenData = new uint8_t[65535*2];
253 LutBlueData = new uint8_t[65535*2];
254 // TODO need to check file is indeed PALETTE COLOR:
255 ReadPaletteInto(file, DCM_RedPaletteColorLookupTableDescriptor,
256 DCM_SegmentedRedPaletteColorLookupTableData,LutRedData);
257 ReadPaletteInto(file, DCM_GreenPaletteColorLookupTableDescriptor,
258 DCM_SegmentedGreenPaletteColorLookupTableData,LutGreenData);
259 ReadPaletteInto(file, DCM_BluePaletteColorLookupTableDescriptor,
260 DCM_SegmentedBluePaletteColorLookupTableData,LutBlueData);
266 // FIXME : The following comment is probabely meaningless, since LUT are *always*
267 // loaded at parsing time, whatever their length is.
269 // Depending on the value of Document::MAX_SIZE_LOAD_ELEMENT_VALUE
270 // [ refer to invocation of Document::SetMaxSizeLoadEntry() in
271 // Document::Document() ], the loading of the value (content) of a
272 // [Bin|Val]Entry occurence migth have been hindered (read simply NOT
273 // loaded). Hence, we first try to obtain the LUTs data from the file
274 // and when this fails we read the LUTs data directly from disk.
275 // \TODO Reading a [Bin|Val]Entry directly from disk is a kludge.
276 // We should NOT bypass the [Bin|Val]Entry class. Instead
277 // an access to an UNLOADED content of a [Bin|Val]Entry occurence
278 // (e.g. DataEntry::GetBinArea()) should force disk access from
279 // within the [Bin|Val]Entry class itself. The only problem
280 // is that the [Bin|Val]Entry is unaware of the FILE* is was
281 // parsed from. Fix that. FIXME.
284 file->LoadEntryBinArea(0x0028, 0x1201);
285 LutRedData = (uint8_t*)file->GetEntryBinArea( 0x0028, 0x1201 );
288 gdcmWarningMacro("Unable to read Red Palette Color Lookup Table data");
292 file->LoadEntryBinArea(0x0028, 0x1202);
293 LutGreenData = (uint8_t*)file->GetEntryBinArea(0x0028, 0x1202 );
296 gdcmWarningMacro("Unable to read Green Palette Color Lookup Table data");
300 file->LoadEntryBinArea(0x0028, 0x1203);
301 LutBlueData = (uint8_t*)file->GetEntryBinArea( 0x0028, 0x1203 );
304 gdcmWarningMacro("Unable to read Blue Palette Color Lookup Table data");
310 ComputeRawAndRGBSizes();
313 /// \brief Reads from disk and decompresses Pixels
314 bool PixelReadConvert::ReadAndDecompressPixelData( std::ifstream *fp )
316 // ComputeRawAndRGBSizes is already made by
317 // ::GrabInformationsFromfile. So, the structure sizes are
321 //////////////////////////////////////////////////
322 //// First stage: get our hands on the Pixel Data.
325 gdcmWarningMacro( "Unavailable file pointer." );
329 fp->seekg( PixelOffset, std::ios::beg );
330 if ( fp->fail() || fp->eof() )
332 gdcmWarningMacro( "Unable to find PixelOffset in file." );
338 //////////////////////////////////////////////////
340 CallStartMethod(); // for progress bar
341 unsigned int count = 0;
342 unsigned int frameSize;
343 unsigned int bitsAllocated = BitsAllocated;
344 //if(bitsAllocated == 12)
345 if(bitsAllocated > 8 && bitsAllocated < 16)
347 frameSize = XSize*YSize*SamplesPerPixel*bitsAllocated/8;
349 //// Second stage: read from disk and decompress.
351 if ( BitsAllocated == 12 ) // We suppose 'BitsAllocated' = 12 only exist for uncompressed files
353 ReadAndDecompress12BitsTo16Bits( fp);
357 // This problem can be found when some obvious informations are found
358 // after the field containing the image data. In this case, these
359 // bad data are added to the size of the image (in the PixelDataLength
360 // variable). But RawSize is the right size of the image !
361 if ( PixelDataLength != RawSize )
363 gdcmWarningMacro( "Mismatch between PixelReadConvert : "
364 << PixelDataLength << " and RawSize : " << RawSize );
367 //todo : is it the right patch?
368 char *raw = (char*)Raw;
369 uint32_t remainingLength;
371 unsigned int lengthToRead;
373 if ( PixelDataLength > RawSize )
374 lengthToRead = RawSize;
376 lengthToRead = PixelDataLength;
378 // perform a frame by frame reading
379 remainingLength = lengthToRead;
380 unsigned int nbFrames = lengthToRead / frameSize;
381 for (i=0;i<nbFrames; i++)
383 Progress = (float)(count+1)/(float)nbFrames;
384 fp->read( raw, frameSize);
386 remainingLength -= frameSize;
389 if (remainingLength !=0 )
390 fp->read( raw, remainingLength);
392 if ( fp->fail() || fp->eof())
394 gdcmWarningMacro( "Reading of Raw pixel data failed." );
398 else if ( IsRLELossless )
400 if ( ! RLEInfo->DecompressRLEFile
401 ( fp, Raw, XSize, YSize, ZSize, TSize, BitsAllocated ) )
403 gdcmWarningMacro( "RLE decompressor failed." );
409 //gdcmWarningMacro( "Sorry, MPEG not yet taken into account" );
411 // fp has already been seek to start of mpeg
412 //ReadMPEGFile(fp, (char*)Raw, PixelDataLength);
417 // Default case concerns JPEG family
418 if ( ! ReadAndDecompressJPEGFile( fp ) )
420 gdcmWarningMacro( "JPEG decompressor ( ReadAndDecompressJPEGFile()"
421 << " method ) failed." );
426 ////////////////////////////////////////////
427 //// Third stage: twigle the bytes and bits.
428 ConvertReorderEndianity();
429 ConvertReArrangeBits();
430 ConvertFixGreyLevels();
431 if (UserFunction) // user is allowed to Mirror, TopDown, Rotate,...the image
432 UserFunction( Raw, FileInternal);
433 ConvertHandleColor();
438 /// Deletes Pixels Area
439 void PixelReadConvert::Squeeze()
450 // delete [] LutRGBA;
455 * \brief Build the RGB image from the Raw image and the LUTs.
457 bool PixelReadConvert::BuildRGBImage()
461 // The job is already done.
467 // The job can't be done
474 // The job can't be done
478 gdcmDebugMacro( "--> BuildRGBImage" );
484 if ( BitsAllocated <= 8 )
486 uint8_t *localRGB = RGB;
487 for (size_t i = 0; i < RawSize; ++i )
490 *localRGB++ = LutRGBA[j];
491 *localRGB++ = LutRGBA[j+1];
492 *localRGB++ = LutRGBA[j+2];
496 else // deal with 16 bits pixels and 16 bits Palette color
498 uint16_t *localRGB = (uint16_t *)RGB;
499 for (size_t i = 0; i < RawSize/2; ++i )
501 j = ((uint16_t *)Raw)[i] * 4;
502 *localRGB++ = ((uint16_t *)LutRGBA)[j];
503 *localRGB++ = ((uint16_t *)LutRGBA)[j+1];
504 *localRGB++ = ((uint16_t *)LutRGBA)[j+2];
511 //-----------------------------------------------------------------------------
514 //-----------------------------------------------------------------------------
517 * \brief Read from file a 12 bits per pixel image and decompress it
518 * into a 16 bits per pixel image.
520 void PixelReadConvert::ReadAndDecompress12BitsTo16Bits( std::ifstream *fp )
521 throw ( FormatError )
523 /// \todo Fix the 3D, 4D pb
524 int nbPixels = XSize * YSize * TSize;
525 uint16_t *localDecompres = (uint16_t*)Raw;
527 for( int p = 0; p < nbPixels; p += 2 )
531 fp->read( (char*)&b0, 1);
532 if ( fp->fail() || fp->eof() )
534 throw FormatError( "PixelReadConvert::ReadAndDecompress12BitsTo16Bits()",
535 "Unfound first block" );
538 fp->read( (char*)&b1, 1 );
539 if ( fp->fail() || fp->eof())
541 throw FormatError( "PixelReadConvert::ReadAndDecompress12BitsTo16Bits()",
542 "Unfound second block" );
545 fp->read( (char*)&b2, 1 );
546 if ( fp->fail() || fp->eof())
548 throw FormatError( "PixelReadConvert::ReadAndDecompress12BitsTo16Bits()",
549 "Unfound second block" );
552 // Two steps are necessary to please VC++
554 // 2 pixels 12bit = [0xABCDEF]
555 // 2 pixels 16bit = [0x0ABD] + [0x0FCE]
557 *localDecompres++ = ((b0 >> 4) << 8) + ((b0 & 0x0f) << 4) + (b1 & 0x0f);
559 *localDecompres++ = ((b2 & 0x0f) << 8) + ((b1 >> 4) << 4) + (b2 >> 4);
561 /// \todo JPR Troubles expected on Big-Endian processors ?
566 * \brief Reads from disk the Pixel Data of JPEG Dicom encapsulated
567 * file and decompress it.
568 * @param fp File Pointer
571 bool PixelReadConvert::ReadAndDecompressJPEGFile( std::ifstream *fp )
575 // make sure this is the right JPEG compression
576 assert( !IsJPEGLossless || !IsJPEGLossy || !IsJPEGLS );
577 // FIXME this is really ugly but it seems I have to load the complete
578 // jpeg2000 stream to use jasper:
579 // I don't think we'll ever be able to deal with multiple fragments properly
583 unsigned long inputlength = 0;
584 JPEGFragment *jpegfrag = JPEGInfo->GetFirstFragment();
587 inputlength += jpegfrag->GetLength();
588 jpegfrag = JPEGInfo->GetNextFragment();
590 gdcmAssertMacro( inputlength != 0);
591 uint8_t *inputdata = new uint8_t[inputlength];
592 char *pinputdata = (char*)inputdata;
593 jpegfrag = JPEGInfo->GetFirstFragment();
596 fp->seekg( jpegfrag->GetOffset(), std::ios::beg);
597 fp->read(pinputdata, jpegfrag->GetLength());
598 pinputdata += jpegfrag->GetLength();
599 jpegfrag = JPEGInfo->GetNextFragment();
601 // Warning the inputdata buffer is deleted in the function
602 if ( gdcm_read_JPEG2000_file( Raw,
603 (char*)inputdata, inputlength ) )
607 // wow what happen, must be an error
608 gdcmWarningMacro( "gdcm_read_JPEG2000_file() failed ");
613 if( (unsigned int)ZSize != JPEGInfo->GetFragmentCount() )
615 gdcmErrorMacro( "Sorry GDCM does not handle this type of fragments" );
618 // Hopefully every dicom fragment is *exactly* the j2k stream
619 JPEGFragment *jpegfrag = JPEGInfo->GetFirstFragment();
620 char *praw = (char*)Raw;
623 unsigned long inputlength = jpegfrag->GetLength();
624 char *inputdata = new char[inputlength];
625 fp->seekg( jpegfrag->GetOffset(), std::ios::beg);
626 fp->read(inputdata, jpegfrag->GetLength());
627 // Warning the inputdata buffer is deleted in the function
628 gdcm_read_JPEG2000_file( praw,
629 inputdata, inputlength) ;
630 praw += XSize*YSize*SamplesPerPixel*(BitsAllocated/8);
631 jpegfrag = JPEGInfo->GetNextFragment();
638 // make sure this is the right JPEG compression
639 assert( !IsJPEGLossless || !IsJPEGLossy || !IsJPEG2000 );
640 // WARNING : JPEG-LS is NOT the 'classical' Jpeg Lossless :
641 // [JPEG-LS is the basis for new lossless/near-lossless compression
642 // standard for continuous-tone images intended for JPEG2000. The standard
643 // is based on the LOCO-I algorithm (LOw COmplexity LOssless COmpression
644 // for Images) developed at Hewlett-Packard Laboratories]
646 // see http://datacompression.info/JPEGLS.shtml
649 std::cerr << "count:" << JPEGInfo->GetFragmentCount() << std::endl;
650 unsigned long inputlength = 0;
651 JPEGFragment *jpegfrag = JPEGInfo->GetFirstFragment();
654 inputlength += jpegfrag->GetLength();
655 jpegfrag = JPEGInfo->GetNextFragment();
657 gdcmAssertMacro( inputlength != 0);
658 uint8_t *inputdata = new uint8_t[inputlength];
659 char *pinputdata = (char*)inputdata;
660 jpegfrag = JPEGInfo->GetFirstFragment();
663 fp->seekg( jpegfrag->GetOffset(), std::ios::beg);
664 fp->read(pinputdata, jpegfrag->GetLength());
665 pinputdata += jpegfrag->GetLength();
666 jpegfrag = JPEGInfo->GetNextFragment();
669 //fp->read((char*)Raw, PixelDataLength);
671 std::ofstream out("/tmp/jpegls.jpg");
672 out.write((char*)inputdata, inputlength);
677 gdcmWarningMacro( "Sorry, JPEG-LS not yet taken into account" );
678 fp->seekg( JPEGInfo->GetFirstFragment()->GetOffset(), std::ios::beg);
679 // if ( ! gdcm_read_JPEGLS_file( fp,Raw ) )
684 // make sure this is the right JPEG compression
685 assert( !IsJPEGLS || !IsJPEG2000 );
686 // Precompute the offset localRaw will be shifted with
687 int length = XSize * YSize * ZSize * SamplesPerPixel;
688 int numberBytes = BitsAllocated / 8;
690 // to avoid major troubles when BitsStored == 8 && BitsAllocated==16 !
692 if (BitsStored == 8 && BitsAllocated==16)
696 JPEGInfo->DecompressFromFile(fp, Raw, dummy, numberBytes, length );
699 //else (not sure how get there...), must be one of those crazy DICOM file
704 * \brief Build Red/Green/Blue/Alpha LUT from File when :
705 * - (0028,0004) : Photometric Interpretation == [PALETTE COLOR ]
707 * - (0028,1101),(0028,1102),(0028,1102)
708 * xxx Palette Color Lookup Table Descriptor are found
710 * - (0028,1201),(0028,1202),(0028,1202)
711 * xxx Palette Color Lookup Table Data - are found
712 * \warning does NOT deal with :
713 * - 0028 1100 Gray Lookup Table Descriptor (Retired)
714 * - 0028 1221 Segmented Red Palette Color Lookup Table Data
715 * - 0028 1222 Segmented Green Palette Color Lookup Table Data
716 * - 0028 1223 Segmented Blue Palette Color Lookup Table Data
717 * no known Dicom reader deals with them :-(
718 * @return a RGBA Lookup Table
720 void PixelReadConvert::BuildLUTRGBA()
723 // Note to code reviewers :
724 // The problem is *much more* complicated, since a lot of manufacturers
725 // Don't follow the norm :
726 // have a look at David Clunie's remark at the end of this .cxx file.
733 // http://www.barre.nom.fr/medical/dicom2/limitations.html#Color%20Lookup%20Tables
735 if ( ! IsPaletteColor )
740 if ( LutRedDescriptor == GDCM_UNFOUND
741 || LutGreenDescriptor == GDCM_UNFOUND
742 || LutBlueDescriptor == GDCM_UNFOUND )
744 gdcmWarningMacro( "(At least) a LUT Descriptor is missing" );
748 ////////////////////////////////////////////
749 // Extract the info from the LUT descriptors
750 int lengthR; // Red LUT length in Bytes
751 int debR; // Subscript of the first Lut Value
752 int nbitsR; // Lut item size (in Bits)
753 int nbRead; // nb of items in LUT descriptor (must be = 3)
755 nbRead = sscanf( LutRedDescriptor.c_str(),
757 &lengthR, &debR, &nbitsR );
760 gdcmWarningMacro( "Wrong Red LUT descriptor" );
762 int lengthG; // Green LUT length in Bytes
763 int debG; // Subscript of the first Lut Value
764 int nbitsG; // Lut item size (in Bits)
766 nbRead = sscanf( LutGreenDescriptor.c_str(),
768 &lengthG, &debG, &nbitsG );
771 gdcmWarningMacro( "Wrong Green LUT descriptor" );
774 int lengthB; // Blue LUT length in Bytes
775 int debB; // Subscript of the first Lut Value
776 int nbitsB; // Lut item size (in Bits)
777 nbRead = sscanf( LutRedDescriptor.c_str(),
779 &lengthB, &debB, &nbitsB );
782 gdcmWarningMacro( "Wrong Blue LUT descriptor" );
785 gdcmDebugMacro(" lengthR " << lengthR << " debR "
786 << debR << " nbitsR " << nbitsR);
787 gdcmDebugMacro(" lengthG " << lengthG << " debG "
788 << debG << " nbitsG " << nbitsG);
789 gdcmDebugMacro(" lengthB " << lengthB << " debB "
790 << debB << " nbitsB " << nbitsB);
792 if ( !lengthR ) // if = 2^16, this shall be 0 see : CP-143
794 if ( !lengthG ) // if = 2^16, this shall be 0
796 if ( !lengthB ) // if = 2^16, this shall be 0
799 ////////////////////////////////////////////////////////
801 if ( ( ! LutRedData ) || ( ! LutGreenData ) || ( ! LutBlueData ) )
803 gdcmWarningMacro( "(At least) a LUT is missing" );
807 // -------------------------------------------------------------
809 if ( BitsAllocated <= 8 )
811 // forge the 4 * 8 Bits Red/Green/Blue/Alpha LUT
812 LutRGBA = new uint8_t[ 1024 ]; // 256 * 4 (R, G, B, Alpha)
817 memset( LutRGBA, 0, 1024 );
820 if ( ( nbitsR == 16 ) && ( BitsAllocated == 8 ) )
822 // when LUT item size is different than pixel size
823 mult = 2; // high byte must be = low byte
827 // See PS 3.3-2003 C.11.1.1.2 p 619
831 // if we get a black image, let's just remove the '+1'
832 // from 'i*mult+1' and check again
833 // if it works, we shall have to check the 3 Palettes
834 // to see which byte is ==0 (first one, or second one)
836 // We give up the checking to avoid some (useless ?) overhead
837 // (optimistic asumption)
841 //take "Subscript of the first Lut Value" (debR,debG,debB) into account!
843 //FIXME : +1 : to get 'low value' byte
844 // Trouble expected on Big Endian Processors ?
845 // 16 BIts Per Pixel Palette Color to be swapped?
847 a = LutRGBA + 0 + debR;
848 for( i=0; i < lengthR; ++i )
850 *a = LutRedData[i*mult+1];
854 a = LutRGBA + 1 + debG;
855 for( i=0; i < lengthG; ++i)
857 *a = LutGreenData[i*mult+1];
861 a = LutRGBA + 2 + debB;
862 for(i=0; i < lengthB; ++i)
864 *a = LutBlueData[i*mult+1];
869 for(i=0; i < 256; ++i)
871 *a = 1; // Alpha component
877 // Probabely the same stuff is to be done for 16 Bits Pixels
878 // with 65536 entries LUT ?!?
879 // Still looking for accurate info on the web :-(
881 gdcmWarningMacro( "Sorry Palette Color Lookup Tables not yet dealt with"
882 << " for 16 Bits Per Pixel images" );
884 // forge the 4 * 16 Bits Red/Green/Blue/Alpha LUT
886 LutRGBA = (uint8_t *)new uint16_t[ 65536*4 ]; // 2^16 * 4 (R, G, B, Alpha)
889 memset( LutRGBA, 0, 65536*4*2 ); // 16 bits = 2 bytes ;-)
891 LutItemNumber = 65536;
897 //take "Subscript of the first Lut Value" (debR,debG,debB) into account!
899 a16 = (uint16_t*)LutRGBA + 0 + debR;
900 for( i=0; i < lengthR; ++i )
902 *a16 = ((uint16_t*)LutRedData)[i];
906 a16 = (uint16_t*)LutRGBA + 1 + debG;
907 for( i=0; i < lengthG; ++i)
909 *a16 = ((uint16_t*)LutGreenData)[i];
913 a16 = (uint16_t*)LutRGBA + 2 + debB;
914 for(i=0; i < lengthB; ++i)
916 *a16 = ((uint16_t*)LutBlueData)[i];
920 a16 = (uint16_t*)LutRGBA + 3 ;
921 for(i=0; i < 65536; ++i)
923 *a16 = 1; // Alpha component
926 // Just to 'see' the LUT, at debug time
927 // Don't remove this commented out code.
929 a16=(uint16_t*)LutRGBA;
930 for (int j=0;j<65536;j++)
932 std::cout << *a16 << " " << *(a16+1) << " "
933 << *(a16+2) << " " << *(a16+3) << std::endl;
941 * \brief Swap the bytes, according to SwapCode.
943 void PixelReadConvert::ConvertSwapZone()
947 // If this file is 'ImplicitVR BigEndian PrivateGE Transfer Syntax',
948 // then the header is in little endian format and the pixel data is in
949 // big endian format. When reading the header, GDCM has already established
950 // a byte swapping code suitable for this machine to read the
951 // header. In TS::ImplicitVRBigEndianPrivateGE, this code will need
952 // to be switched in order to read the pixel data. This must be
953 // done REGARDLESS of the processor endianess!
955 // Example: Assume we are on a little endian machine. When
956 // GDCM reads the header, the header will match the machine
957 // endianess and the swap code will be established as a no-op.
958 // When GDCM reaches the pixel data, it will need to switch the
959 // swap code to do big endian to little endian conversion.
961 // Now, assume we are on a big endian machine. When GDCM reads the
962 // header, the header will be recognized as a different endianess
963 // than the machine endianess, and a swap code will be established
964 // to convert from little endian to big endian. When GDCM readers
965 // the pixel data, the pixel data endianess will now match the
966 // machine endianess. But we currently have a swap code that
967 // converts from little endian to big endian. In this case, we
968 // need to switch the swap code to a no-op.
970 // Therefore, in either case, if the file is in
971 // 'ImplicitVR BigEndian PrivateGE Transfer Syntax', then GDCM needs to switch
972 // the byte swapping code when entering the pixel data.
974 int tempSwapCode = SwapCode;
975 if ( IsPrivateGETransferSyntax )
977 gdcmWarningMacro(" IsPrivateGETransferSyntax found; turn the SwapCode");
978 // PrivateGETransferSyntax only exists for 'true' Dicom images
979 // we assume there is no 'exotic' 32 bits endianess!
980 if (SwapCode == 1234)
984 else if (SwapCode == 4321)
990 if ( BitsAllocated == 16 )
992 uint16_t *im16 = (uint16_t*)Raw;
993 switch( tempSwapCode )
1000 for( i = 0; i < RawSize / 2; i++ )
1002 im16[i]= (im16[i] >> 8) | (im16[i] << 8 );
1006 gdcmWarningMacro("SwapCode value (16 bits) not allowed."
1010 else if ( BitsAllocated == 32 )
1015 uint32_t *im32 = (uint32_t*)Raw;
1016 switch ( tempSwapCode )
1021 for( i = 0; i < RawSize / 4; i++ )
1023 low = im32[i] & 0x0000ffff; // 4321
1024 high = im32[i] >> 16;
1025 high = ( high >> 8 ) | ( high << 8 );
1026 low = ( low >> 8 ) | ( low << 8 );
1028 im32[i] = ( s32 << 16 ) | high;
1032 for( i = 0; i < RawSize / 4; i++ )
1034 low = im32[i] & 0x0000ffff; // 2143
1035 high = im32[i] >> 16;
1036 high = ( high >> 8 ) | ( high << 8 );
1037 low = ( low >> 8 ) | ( low << 8 );
1039 im32[i] = ( s32 << 16 ) | low;
1043 for( i = 0; i < RawSize / 4; i++ )
1045 low = im32[i] & 0x0000ffff; // 3412
1046 high = im32[i] >> 16;
1048 im32[i] = ( s32 << 16 ) | high;
1052 gdcmWarningMacro("SwapCode value (32 bits) not allowed." << tempSwapCode );
1058 * \brief Deal with endianness i.e. re-arange bytes inside the integer
1060 void PixelReadConvert::ConvertReorderEndianity()
1062 if ( BitsAllocated != 8 )
1067 // Special kludge in order to deal with xmedcon broken images:
1068 if ( BitsAllocated == 16
1069 && BitsStored < BitsAllocated
1072 int l = (int)( RawSize / ( BitsAllocated / 8 ) );
1073 uint16_t *deb = (uint16_t *)Raw;
1074 for(int i = 0; i<l; i++)
1076 if ( *deb == 0xffff )
1086 * \brief Deal with Grey levels i.e. re-arange them
1087 * to have low values = dark, high values = bright
1089 void PixelReadConvert::ConvertFixGreyLevels()
1094 uint32_t i; // to please M$VC6
1099 if ( BitsAllocated == 8 )
1101 uint8_t *deb = (uint8_t *)Raw;
1102 for (i=0; i<RawSize; i++)
1110 if ( BitsAllocated == 16 )
1113 for (j=0; j<BitsStored-1; j++)
1115 mask = (mask << 1) +1; // will be fff when BitsStored=12
1118 uint16_t *deb = (uint16_t *)Raw;
1119 for (i=0; i<RawSize/2; i++)
1129 if ( BitsAllocated == 8 )
1131 uint8_t smask8 = 255;
1132 uint8_t *deb = (uint8_t *)Raw;
1133 for (i=0; i<RawSize; i++)
1135 *deb = smask8 - *deb;
1140 if ( BitsAllocated == 16 )
1142 uint16_t smask16 = 65535;
1143 uint16_t *deb = (uint16_t *)Raw;
1144 for (i=0; i<RawSize/2; i++)
1146 *deb = smask16 - *deb;
1155 * \brief Re-arrange the bits within the bytes.
1156 * @return Boolean always true
1158 bool PixelReadConvert::ConvertReArrangeBits() throw ( FormatError )
1161 if ( BitsStored != BitsAllocated )
1163 int l = (int)( RawSize / ( BitsAllocated / 8 ) );
1164 if ( BitsAllocated == 16 )
1166 // pmask : to mask the 'unused bits' (may contain overlays)
1167 uint16_t pmask = 0xffff;
1169 // It's up to the user to decide if he wants to ignore overlays (if any),
1170 // not to gdcm, without asking.
1171 // default is NOT TO LOAD, in order not to confuse ITK users (and others!).
1173 if ( !FH->GetKeepOverlays() ) // mask spurious bits ! (overlay are NOT loaded!)
1175 pmask = pmask >> ( BitsAllocated - BitsStored );
1177 // else : it's up to the user to manage the 'pixels + overlays' he just loaded!
1179 uint16_t *deb = (uint16_t*)Raw;
1181 if ( !PixelSign ) // Pixels are unsigned
1183 for(int i = 0; i<l; i++)
1185 *deb = (*deb >> (BitsStored - HighBitPosition - 1)) & pmask;
1189 else // Pixels are signed
1191 // Hope there is never ACR-NEMA-like overlays within signed pixels (?!?)
1193 // smask : to check the 'sign' when BitsStored != BitsAllocated
1194 uint16_t smask = 0x0001;
1195 smask = smask << ( 16 - (BitsAllocated - BitsStored + 1) );
1196 // nmask : to propagate sign bit on negative values
1197 int16_t nmask = (int16_t)0x8000;
1198 nmask = nmask >> ( BitsAllocated - BitsStored - 1 );
1200 for(int i = 0; i<l; i++)
1202 *deb = *deb >> (BitsStored - HighBitPosition - 1);
1205 *deb = *deb | nmask;
1209 *deb = *deb & pmask;
1215 else if ( BitsAllocated == 32 )
1217 // pmask : to mask the 'unused bits' (may contain overlays)
1218 uint32_t pmask = 0xffffffff;
1219 pmask = pmask >> ( BitsAllocated - BitsStored );
1221 uint32_t *deb = (uint32_t*)Raw;
1225 for(int i = 0; i<l; i++)
1227 *deb = (*deb >> (BitsStored - HighBitPosition - 1)) & pmask;
1233 // smask : to check the 'sign' when BitsStored != BitsAllocated
1234 uint32_t smask = 0x00000001;
1235 smask = smask >> ( 32 - (BitsAllocated - BitsStored +1 ));
1236 // nmask : to propagate sign bit on negative values
1237 int32_t nmask = 0x80000000;
1238 nmask = nmask >> ( BitsAllocated - BitsStored -1 );
1240 for(int i = 0; i<l; i++)
1242 *deb = *deb >> (BitsStored - HighBitPosition - 1);
1244 *deb = *deb | nmask;
1246 *deb = *deb & pmask;
1253 gdcmWarningMacro("Weird image (BitsAllocated !=8, 12, 16, 32)");
1254 throw FormatError( "Weird image !?" );
1261 * \brief Convert (Red plane, Green plane, Blue plane) to RGB pixels
1262 * \warning Works on all the frames at a time
1264 void PixelReadConvert::ConvertRGBPlanesToRGBPixels()
1266 gdcmWarningMacro("--> ConvertRGBPlanesToRGBPixels");
1268 uint8_t *localRaw = Raw;
1269 uint8_t *copyRaw = new uint8_t[ RawSize ];
1270 memmove( copyRaw, localRaw, RawSize );
1272 int l = XSize * YSize * ZSize;
1274 uint8_t *a = copyRaw;
1275 uint8_t *b = copyRaw + l;
1276 uint8_t *c = copyRaw + l + l;
1278 for (int j = 0; j < l; j++)
1280 *(localRaw++) = *(a++);
1281 *(localRaw++) = *(b++);
1282 *(localRaw++) = *(c++);
1288 * \brief Convert (cY plane, cB plane, cR plane) to RGB pixels
1289 * \warning Works on all the frames at a time
1291 void PixelReadConvert::ConvertYcBcRPlanesToRGBPixels()
1293 // Remarks for YBR newbees :
1294 // YBR_FULL works very much like RGB, i.e. three samples per pixel,
1295 // just the color space is YCbCr instead of RGB. This is particularly useful
1296 // for doppler ultrasound where most of the image is grayscale
1297 // (i.e. only populates the Y components) and Cb and Cr are mostly zero,
1298 // except for the few patches of color on the image.
1299 // On such images, RLE achieves a compression ratio that is much better
1300 // than the compression ratio on an equivalent RGB image.
1302 gdcmWarningMacro("--> ConvertYcBcRPlanesToRGBPixels");
1304 uint8_t *localRaw = Raw;
1305 uint8_t *copyRaw = new uint8_t[ RawSize ];
1306 memmove( copyRaw, localRaw, RawSize );
1308 // to see the tricks about YBR_FULL, YBR_FULL_422,
1309 // YBR_PARTIAL_422, YBR_ICT, YBR_RCT have a look at :
1310 // ftp://medical.nema.org/medical/dicom/final/sup61_ft.pdf
1311 // and be *very* affraid
1314 /// \todo : find an example to see how 3rd dim and 4th dim work together
1315 int l = XSize * YSize * TSize;
1316 int nbFrames = ZSize;
1318 uint8_t *a = copyRaw + 0;
1319 uint8_t *b = copyRaw + l;
1320 uint8_t *c = copyRaw + l+ l;
1323 /// We replaced easy to understand but time consuming floating point
1324 /// computations by the 'well known' integer computation counterpart
1326 /// http://lestourtereaux.free.fr/papers/data/yuvrgb.pdf
1327 /// for code optimisation.
1329 for ( int i = 0; i < nbFrames; i++ )
1331 for ( int j = 0; j < l; j++ )
1333 R = 38142 *(*a-16) + 52298 *(*c -128);
1334 G = 38142 *(*a-16) - 26640 *(*c -128) - 12845 *(*b -128);
1335 B = 38142 *(*a-16) + 66093 *(*b -128);
1344 if (R > 255) R = 255;
1345 if (G > 255) G = 255;
1346 if (B > 255) B = 255;
1348 *(localRaw++) = (uint8_t)R;
1349 *(localRaw++) = (uint8_t)G;
1350 *(localRaw++) = (uint8_t)B;
1359 /// \brief Deals with the color decoding i.e. handle:
1360 /// - R, G, B planes (as opposed to RGB pixels)
1361 /// - YBR (various) encodings.
1362 /// - LUT[s] (or "PALETTE COLOR").
1364 void PixelReadConvert::ConvertHandleColor()
1366 //////////////////////////////////
1367 // Deal with the color decoding i.e. handle:
1368 // - R, G, B planes (as opposed to RGB pixels)
1369 // - YBR (various) encodings.
1370 // - LUT[s] (or "PALETTE COLOR").
1372 // The classification in the color decoding schema is based on the blending
1373 // of two Dicom tags values:
1374 // * "Photometric Interpretation" for which we have the cases:
1375 // - [Photo A] MONOCHROME[1|2] pictures,
1376 // - [Photo B] RGB or YBR_FULL_422 (which acts as RGB),
1377 // - [Photo C] YBR_* (with the above exception of YBR_FULL_422)
1378 // - [Photo D] "PALETTE COLOR" which indicates the presence of LUT[s].
1379 // * "Planar Configuration" for which we have the cases:
1380 // - [Planar 0] 0 then Pixels are already RGB
1381 // - [Planar 1] 1 then we have 3 planes : R, G, B,
1382 // - [Planar 2] 2 then we have 1 gray Plane and 3 LUTs
1384 // Now in theory, one could expect some coherence when blending the above
1385 // cases. For example we should not encounter files belonging at the
1386 // time to case [Planar 0] and case [Photo D].
1387 // Alas, this was only theory ! Because in practice some odd (read ill
1388 // formated Dicom) files (e.g. gdcmData/US-PAL-8-10x-echo.dcm) we encounter:
1389 // - "Planar Configuration" = 0,
1390 // - "Photometric Interpretation" = "PALETTE COLOR".
1391 // Hence gdcm will use the folowing "heuristic" in order to be tolerant
1392 // towards Dicom-non-conformant files:
1393 // << whatever the "Planar Configuration" value might be, a
1394 // "Photometric Interpretation" set to "PALETTE COLOR" forces
1395 // a LUT intervention >>
1397 // Now we are left with the following handling of the cases:
1398 // - [Planar 0] OR [Photo A] no color decoding (since respectively
1399 // Pixels are already RGB and monochrome pictures have no color :),
1400 // - [Planar 1] AND [Photo B] handled with ConvertRGBPlanesToRGBPixels()
1401 // - [Planar 1] AND [Photo C] handled with ConvertYcBcRPlanesToRGBPixels()
1402 // - [Planar 2] OR [Photo D] requires LUT intervention.
1404 gdcmDebugMacro("--> ConvertHandleColor "
1405 << "Planar Configuration " << PlanarConfiguration );
1409 // [Planar 2] OR [Photo D]: LUT intervention done outside
1410 gdcmDebugMacro("--> RawRGB : LUT intervention done outside");
1414 if ( PlanarConfiguration == 1 )
1418 // [Planar 1] AND [Photo C] (remember YBR_FULL_422 acts as RGB)
1419 gdcmDebugMacro("--> YBRFull");
1420 ConvertYcBcRPlanesToRGBPixels();
1424 // [Planar 1] AND [Photo C]
1425 gdcmDebugMacro("--> YBRFull");
1426 ConvertRGBPlanesToRGBPixels();
1431 // When planarConf is 0, and RLELossless (forbidden by Dicom norm)
1432 // pixels need to be RGB-fyied anyway
1436 gdcmDebugMacro("--> RLE Lossless");
1437 ConvertRGBPlanesToRGBPixels();
1440 // In *normal *case, when planarConf is 0, pixels are already in RGB
1443 /// Computes the Pixels Size
1444 void PixelReadConvert::ComputeRawAndRGBSizes()
1446 int bitsAllocated = BitsAllocated;
1447 // Number of "Bits Allocated" is fixed to 16 when it's 12, since
1448 // in this case we will expand the image to 16 bits (see
1449 // ReadAndDecompress12BitsTo16Bits() )
1450 if ( BitsAllocated == 12 )
1455 RawSize = XSize * YSize * ZSize * TSize
1456 * ( bitsAllocated / 8 )
1460 RGBSize = 3 * RawSize; // works for 8 and 16 bits per Pixel
1466 RawSize += RawSize%2;
1467 RGBSize += RGBSize%2;
1470 /// Allocates room for RGB Pixels
1471 void PixelReadConvert::AllocateRGB()
1475 RGB = new uint8_t[RGBSize];
1478 /// Allocates room for RAW Pixels
1479 void PixelReadConvert::AllocateRaw()
1483 Raw = new uint8_t[RawSize];
1486 //-----------------------------------------------------------------------------
1489 * \brief Print self.
1490 * @param indent Indentation string to be prepended during printing.
1491 * @param os Stream to print to.
1493 void PixelReadConvert::Print( std::ostream &os, std::string const &indent )
1496 << "--- Pixel information -------------------------"
1499 << "Pixel Data: offset " << PixelOffset
1500 << " x(" << std::hex << PixelOffset << std::dec
1501 << ") length " << PixelDataLength
1502 << " x(" << std::hex << PixelDataLength << std::dec
1503 << ")" << std::endl;
1505 if ( IsRLELossless )
1509 RLEInfo->Print( os, indent );
1513 gdcmWarningMacro("Set as RLE file but NO RLEinfo present.");
1517 if ( IsJPEG2000 || IsJPEGLossless || IsJPEGLossy || IsJPEGLS )
1521 JPEGInfo->Print( os, indent );
1525 gdcmWarningMacro("Set as JPEG file but NO JPEGinfo present.");
1531 * \brief CallStartMethod
1533 void PixelReadConvert::CallStartMethod()
1537 CommandManager::ExecuteCommand(FH,CMD_STARTPROGRESS);
1541 * \brief CallProgressMethod
1543 void PixelReadConvert::CallProgressMethod()
1545 CommandManager::ExecuteCommand(FH,CMD_PROGRESS);
1549 * \brief CallEndMethod
1551 void PixelReadConvert::CallEndMethod()
1554 CommandManager::ExecuteCommand(FH,CMD_ENDPROGRESS);
1557 //-----------------------------------------------------------------------------
1558 } // end namespace gdcm
1560 // Note to developpers :
1561 // Here is a very detailled post from David Clunie, on the troubles caused
1562 // 'non standard' LUT and LUT description
1563 // We shall have to take it into accound in our code.
1568 Subject: Problem with VOI LUTs in Agfa and Fuji CR and GE DX images, was Re: VOI LUT issues
1569 Date: Sun, 06 Feb 2005 17:13:40 GMT
1570 From: David Clunie <dclunie@dclunie.com>
1571 Reply-To: dclunie@dclunie.com
1572 Newsgroups: comp.protocols.dicom
1573 References: <1107553502.040221.189550@o13g2000cwo.googlegroups.com>
1575 > THE LUT that comes with [my] image claims to be 16-bit, but none of the
1576 > values goes higher than 4095. That being said, though, none of my
1577 > original pixel values goes higher than that, either. I have read
1578 > elsewhere on this group that when that happens you are supposed to
1579 > adjust the LUT. Can someone be more specific? There was a thread from
1580 > 2002 where Marco and David were mentioning doing precisely that.
1587 You have encountered the well known "we know what the standard says but
1588 we are going to ignore it and do what we have been doing for almost
1589 a decade regardless" CR vendor bug. Agfa started this, but they are not
1590 the only vendor doing this now; GE and Fuji may have joined the club.
1592 Sadly, one needs to look at the LUT Data, figure out what the maximum
1593 value actually encoded is, and find the next highest power of 2 (e.g.
1594 212 in this case), to figure out what the range of the data is
1595 supposed to be. I have assumed that if the maximum value in the LUT
1596 data is less than a power of 2 minus 1 (e.g. 0xebc) then the intent
1597 of the vendor was not to use the maximum available grayscale range
1598 of the display (e.g. the maximum is 0xfff in this case). An alternative
1599 would be to scale to the actual maximum rather than a power of two.
1601 Very irritating, and in theory not totally reliable if one really
1602 intended the full 16 bits and only used, say 15, but that is extremely
1603 unlikely since everything would be too dark, and this heuristic
1606 There has never been anything in the standard that describes having
1607 to go through these convolutions. Since the only value in the
1608 standard that describes the bit depth of the LUT values is LUT
1609 Descriptor value 3 and that is (usually) always required to be
1610 either 8 or 16, it mystifies me how the creators' of these images
1611 imagine that the receiver is going to divine the range that is intended. Further, the standard is quite explicit that this 3rd
1612 value defines the range of LUT values, but as far as I am aware, all
1613 the vendors are ignoring the standard and indeed sending a third value
1616 This problem is not confined to CR, and is also seen with DX products.
1618 Typically I have seen:
1620 - Agfa CR, which usually (always ?) sends LUTs, values up to 0x0fff
1621 - Fuji CR, which occasionally send LUTs, values up to 0x03ff
1622 - GE DX, for presentation, which always have LUTs, up to 0x3fff
1624 Swissray, Siemens, Philips, Canon and Kodak never seem to send VOI LUTs
1625 at this point (which is a whole other problem). Note that the presence
1626 or absence of a VOI LUT as opposed to window values may be configurable
1627 on the modality in some cases, and I have just looked at what I happen
1628 to have received from a myriad of sites over whose configuration I have
1629 no control. This may be why the majority of Fuji images have no VOI LUTs,
1630 but a few do (or it may be the Siemens system that these Fuji images went
1631 through that perhaps added it). I do have some test Hologic DX images that
1632 are not from a clinical site that do actually get this right (a value
1633 of 12 for the third value and a max of 0xfff).
1635 Since almost every vendor that I have encountered that encodes LUTs
1636 makes this mistake, perhaps it is time to amend the standard to warn
1637 implementor's of receivers and/or sanction this bad behavior. We have
1638 talked about this in the past in WG 6 but so far everyone has been
1639 reluctant to write into the standard such a comment. Maybe it is time
1640 to try again, since if one is not aware of this problem, one cannot
1641 effectively implement display using VOI LUTs, and there is a vast
1642 installed base to contend with.
1644 I did not check presentation states, in which VOI LUTs could also be
1645 encountered, for the prevalence of this mistake, nor did I look at the
1646 encoding of Modality LUT's, which are unusual. Nor did I check digital
1647 mammography images. I would be interested to hear from anyone who has.
1651 PS. The following older thread in this newsgroup discusses this:
1653 "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"
1655 PPS. From a historical perspective, the following may be of interest.
1657 In the original standard in 1993, all that was said about this was a
1658 reference to the corresponding such where Modality LUTs are described
1661 "The third value specifies the number of bits for each entry in the
1662 LUT Data. It shall take the value 8 or 16. The LUT Data shall be stored
1663 in a format equivalent to 8 or 16 bits allocated and high bit equal
1666 Since the high bit hint was not apparently explicit enough, a very
1667 early CP, CP 15 (submitted by Agfa as it happens), replaced this with:
1669 "The third value conveys the range of LUT entry values. It shall take
1670 the value 8 or 16, corresponding with the LUT entry value range of
1673 Note: The third value is not required for describing the
1674 LUT data and is only included for informational usage
1675 and for maintaining compatibility with ACRNEMA 2.0.
1677 The LUT Data contains the LUT entry values."
1679 That is how it read in the 1996, 1998 and 1999 editions.
1681 By the 2000 edition, Supplement 33 that introduced presentation states
1682 extensively reworked this entire section and tried to explain this in
1685 "The output range is from 0 to 2^n-1 where n is the third value of LUT
1686 Descriptor. This range is always unsigned."
1688 and also added a note to spell out what the output range meant in the
1691 "9. The output of the Window Center/Width or VOI LUT transformation
1692 is either implicitly scaled to the full range of the display device
1693 if there is no succeeding transformation defined, or implicitly scaled
1694 to the full input range of the succeeding transformation step (such as
1695 the Presentation LUT), if present. See C.11.6.1."
1697 It still reads this way in the 2004 edition.
1699 Note that LUTs in other applications than the general VOI LUT allow for
1700 values other than 8 or 16 in the third value of LUT descriptor to permit
1701 ranges other than 0 to 255 or 65535.
1703 In addition, the DX Image Module specializes the VOI LUT
1704 attributes as follows, in PS 3.3 section C.8.11.3.1.5 (added in Sup 32):
1706 "The third value specifies the number of bits for each entry in the LUT
1707 Data (analogous to ìbits storedî). It shall be between 10-16. The LUT
1708 Data shall be stored in a format equivalent to 16 ìbits allocatedî and
1709 ìhigh bitî equal to ìbits storedî - 1. The third value conveys the range
1710 of LUT entry values. These unsigned LUT entry values shall range between
1711 0 and 2^n-1, where n is the third value of the LUT Descriptor."
1713 So in the case of the GE DX for presentation images, the third value of
1714 LUT descriptor is allowed to be and probably should be 14 rather than 16.