]> Creatis software - gdcm.git/blob - src/gdcmPixelReadConvert.cxx
Just one more check for GE Private TS on big endian machines ...
[gdcm.git] / src / gdcmPixelReadConvert.cxx
1 /*=========================================================================
2  
3   Program:   gdcm
4   Module:    $RCSfile: gdcmPixelReadConvert.cxx,v $
5   Language:  C++
6   Date:      $Date: 2005/10/23 17:51:43 $
7   Version:   $Revision: 1.86 $
8                                                                                 
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.
12                                                                                 
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.
16                                                                                 
17 =========================================================================*/
18
19 #include "gdcmPixelReadConvert.h"
20 #include "gdcmDebug.h"
21 #include "gdcmFile.h"
22 #include "gdcmGlobal.h"
23 #include "gdcmTS.h"
24 #include "gdcmDocEntry.h"
25 #include "gdcmRLEFramesInfo.h"
26 #include "gdcmJPEGFragmentsInfo.h"
27
28 #include <fstream>
29 #include <stdio.h> //for sscanf
30
31 namespace gdcm
32 {
33
34 //bool ReadMPEGFile (std::ifstream *fp, void *image_buffer, size_t lenght);
35 bool gdcm_read_JPEG2000_file (void* raw, 
36                               char *inputdata, size_t inputlength);
37 //-----------------------------------------------------------------------------
38 #define str2num(str, typeNum) *((typeNum *)(str))
39
40 //-----------------------------------------------------------------------------
41 // Constructor / Destructor
42 /// Constructor
43 PixelReadConvert::PixelReadConvert() 
44 {
45    RGB          = 0;
46    RGBSize      = 0;
47    Raw          = 0;
48    RawSize      = 0;
49    LutRGBA      = 0;
50    LutRedData   = 0;
51    LutGreenData = 0;
52    LutBlueData  = 0;
53    RLEInfo      = 0;
54    JPEGInfo     = 0;
55    UserFunction = 0;
56    FileInternal = 0;
57 }
58
59 /// Canonical Destructor
60 PixelReadConvert::~PixelReadConvert() 
61 {
62    Squeeze();
63 }
64
65 //-----------------------------------------------------------------------------
66 // Public
67 /**
68  * \brief Predicate to know whether the image[s] (once Raw) is RGB.
69  * \note See comments of \ref ConvertHandleColor
70  */
71 bool PixelReadConvert::IsRawRGB()
72 {
73    if (   IsMonochrome
74        || PlanarConfiguration == 2
75        || IsPaletteColor )
76    {
77       return false;
78    }
79    return true;
80 }
81 /**
82  * \brief Gets various usefull informations from the file header
83  * @param file gdcm::File pointer
84  */
85 void PixelReadConvert::GrabInformationsFromFile( File *file )
86 {
87    // Number of Bits Allocated for storing a Pixel is defaulted to 16
88    // when absent from the file.
89    BitsAllocated = file->GetBitsAllocated();
90    if ( BitsAllocated == 0 )
91    {
92       BitsAllocated = 16;
93    }
94
95    // Number of "Bits Stored", defaulted to number of "Bits Allocated"
96    // when absent from the file.
97    BitsStored = file->GetBitsStored();
98    if ( BitsStored == 0 )
99    {
100       BitsStored = BitsAllocated;
101    }
102
103    // High Bit Position, defaulted to "Bits Allocated" - 1
104    HighBitPosition = file->GetHighBitPosition();
105    if ( HighBitPosition == 0 )
106    {
107       HighBitPosition = BitsAllocated - 1;
108    }
109
110    XSize           = file->GetXSize();
111    YSize           = file->GetYSize();
112    ZSize           = file->GetZSize();
113    SamplesPerPixel = file->GetSamplesPerPixel();
114    //PixelSize       = file->GetPixelSize();  Useless
115    PixelSign       = file->IsSignedPixelData();
116    SwapCode        = file->GetSwapCode();
117    std::string ts  = file->GetTransferSyntax();
118    IsRaw =
119         ( ! file->IsDicomV3() )
120      || Global::GetTS()->GetSpecialTransferSyntax(ts) == TS::ImplicitVRLittleEndian
121      || Global::GetTS()->GetSpecialTransferSyntax(ts) == TS::ImplicitVRBigEndianPrivateGE
122      || Global::GetTS()->GetSpecialTransferSyntax(ts) == TS::ExplicitVRLittleEndian
123      || Global::GetTS()->GetSpecialTransferSyntax(ts) == TS::ExplicitVRBigEndian
124      || Global::GetTS()->GetSpecialTransferSyntax(ts) == TS::DeflatedExplicitVRLittleEndian;
125      
126    IsPrivateGETransferSyntax = 
127                 ( Global::GetTS()->GetSpecialTransferSyntax(ts) == TS::ImplicitVRBigEndianPrivateGE );
128
129    IsMPEG          = Global::GetTS()->IsMPEG(ts);
130    IsJPEG2000      = Global::GetTS()->IsJPEG2000(ts);
131    IsJPEGLS        = Global::GetTS()->IsJPEGLS(ts);
132    IsJPEGLossy     = Global::GetTS()->IsJPEGLossy(ts);
133    IsJPEGLossless  = Global::GetTS()->IsJPEGLossless(ts);
134    IsRLELossless   = Global::GetTS()->IsRLELossless(ts);
135
136    PixelOffset     = file->GetPixelOffset();
137    PixelDataLength = file->GetPixelAreaLength();
138    RLEInfo         = file->GetRLEInfo();
139    JPEGInfo        = file->GetJPEGInfo();
140
141    IsMonochrome    = file->IsMonochrome();
142    IsMonochrome1   = file->IsMonochrome1();
143    IsPaletteColor  = file->IsPaletteColor();
144    IsYBRFull       = file->IsYBRFull();
145
146    PlanarConfiguration = file->GetPlanarConfiguration();
147
148    /////////////////////////////////////////////////////////////////
149    // LUT section:
150    HasLUT = file->HasLUT();
151    if ( HasLUT )
152    {
153       // Just in case some access to a File element requires disk access.
154       LutRedDescriptor   = file->GetEntryString( 0x0028, 0x1101 );
155       LutGreenDescriptor = file->GetEntryString( 0x0028, 0x1102 );
156       LutBlueDescriptor  = file->GetEntryString( 0x0028, 0x1103 );
157    
158       // The following comment is probabely meaningless, since LUT are *always*
159       // loaded at parsing time, whatever their length is.
160          
161       // Depending on the value of Document::MAX_SIZE_LOAD_ELEMENT_VALUE
162       // [ refer to invocation of Document::SetMaxSizeLoadEntry() in
163       // Document::Document() ], the loading of the value (content) of a
164       // [Bin|Val]Entry occurence migth have been hindered (read simply NOT
165       // loaded). Hence, we first try to obtain the LUTs data from the file
166       // and when this fails we read the LUTs data directly from disk.
167       // \TODO Reading a [Bin|Val]Entry directly from disk is a kludge.
168       //       We should NOT bypass the [Bin|Val]Entry class. Instead
169       //       an access to an UNLOADED content of a [Bin|Val]Entry occurence
170       //       (e.g. DataEntry::GetBinArea()) should force disk access from
171       //       within the [Bin|Val]Entry class itself. The only problem
172       //       is that the [Bin|Val]Entry is unaware of the FILE* is was
173       //       parsed from. Fix that. FIXME.
174    
175       // //// Red round
176       file->LoadEntryBinArea(0x0028, 0x1201);
177       LutRedData = (uint8_t*)file->GetEntryBinArea( 0x0028, 0x1201 );
178       if ( ! LutRedData )
179       {
180          gdcmWarningMacro("Unable to read Red Palette Color Lookup Table data");
181       }
182
183       // //// Green round:
184       file->LoadEntryBinArea(0x0028, 0x1202);
185       LutGreenData = (uint8_t*)file->GetEntryBinArea(0x0028, 0x1202 );
186       if ( ! LutGreenData)
187       {
188          gdcmWarningMacro("Unable to read Green Palette Color Lookup Table data");
189       }
190
191       // //// Blue round:
192       file->LoadEntryBinArea(0x0028, 0x1203);
193       LutBlueData = (uint8_t*)file->GetEntryBinArea( 0x0028, 0x1203 );
194       if ( ! LutBlueData )
195       {
196          gdcmWarningMacro("Unable to read Blue Palette Color Lookup Table data");
197       }
198    }
199    FileInternal = file;   
200
201    ComputeRawAndRGBSizes();
202 }
203
204 /// \brief Reads from disk and decompresses Pixels
205 bool PixelReadConvert::ReadAndDecompressPixelData( std::ifstream *fp )
206 {
207    // ComputeRawAndRGBSizes is already made by 
208    // ::GrabInformationsFromfile. So, the structure sizes are
209    // correct
210    Squeeze();
211
212    //////////////////////////////////////////////////
213    //// First stage: get our hands on the Pixel Data.
214    if ( !fp )
215    {
216       gdcmWarningMacro( "Unavailable file pointer." );
217       return false;
218    }
219
220    fp->seekg( PixelOffset, std::ios::beg );
221    if ( fp->fail() || fp->eof() )
222    {
223       gdcmWarningMacro( "Unable to find PixelOffset in file." );
224       return false;
225    }
226
227    AllocateRaw();
228
229    //////////////////////////////////////////////////
230    //// Second stage: read from disk and decompress.
231    if ( BitsAllocated == 12 )
232    {
233       ReadAndDecompress12BitsTo16Bits( fp);
234    }
235    else if ( IsRaw )
236    {
237       // This problem can be found when some obvious informations are found
238       // after the field containing the image data. In this case, these
239       // bad data are added to the size of the image (in the PixelDataLength
240       // variable). But RawSize is the right size of the image !
241       if ( PixelDataLength != RawSize )
242       {
243          gdcmWarningMacro( "Mismatch between PixelReadConvert : "
244                           << PixelDataLength << " and RawSize : " << RawSize );
245       }
246       if ( PixelDataLength > RawSize )
247       {
248          fp->read( (char*)Raw, RawSize);
249       }
250       else
251       {
252          fp->read( (char*)Raw, PixelDataLength);
253       }
254
255       if ( fp->fail() || fp->eof())
256       {
257          gdcmWarningMacro( "Reading of Raw pixel data failed." );
258          return false;
259       }
260    } 
261    else if ( IsRLELossless )
262    {
263       if ( ! RLEInfo->DecompressRLEFile
264                                ( fp, Raw, XSize, YSize, ZSize, BitsAllocated ) )
265       {
266          gdcmWarningMacro( "RLE decompressor failed." );
267          return false;
268       }
269    }
270    else if ( IsMPEG )
271    {
272       //gdcmWarningMacro( "Sorry, MPEG not yet taken into account" );
273       //return false;
274       // fp has already been seek to start of mpeg
275       //ReadMPEGFile(fp, Raw, PixelDataLength); 
276       return true;
277    }
278    else
279    {
280       // Default case concerns JPEG family
281       if ( ! ReadAndDecompressJPEGFile( fp ) )
282       {
283          gdcmWarningMacro( "JPEG decompressor failed." );
284          return false;
285       }
286    }
287
288    ////////////////////////////////////////////
289    //// Third stage: twigle the bytes and bits.
290    ConvertReorderEndianity();
291    ConvertReArrangeBits();
292    ConvertFixGreyLevels();
293    if (UserFunction) // user is allowed to Mirror, TopDown, Rotate,...the image
294       UserFunction( Raw, FileInternal);
295    ConvertHandleColor();
296
297    return true;
298 }
299
300 /// Deletes Pixels Area
301 void PixelReadConvert::Squeeze() 
302 {
303    if ( RGB )
304       delete [] RGB;
305    RGB = 0;
306
307    if ( Raw )
308       delete [] Raw;
309    Raw = 0;
310
311    if ( LutRGBA )
312       delete [] LutRGBA;
313    LutRGBA = 0;
314 }
315
316 /**
317  * \brief Build the RGB image from the Raw image and the LUTs.
318  */
319 bool PixelReadConvert::BuildRGBImage()
320 {
321    if ( RGB )
322    {
323       // The job is already done.
324       return true;
325    }
326
327    if ( ! Raw )
328    {
329       // The job can't be done
330       return false;
331    }
332
333    BuildLUTRGBA();
334    if ( ! LutRGBA )
335    {
336       // The job can't be done
337       return false;
338    }
339
340    gdcmWarningMacro( "--> BuildRGBImage" );
341                                                                                 
342    // Build RGB Pixels
343    AllocateRGB();
344    
345    int j;
346    if ( BitsAllocated <= 8 )
347    {
348       uint8_t *localRGB = RGB;
349       for (size_t i = 0; i < RawSize; ++i )
350       {
351          j  = Raw[i] * 4;
352          *localRGB++ = LutRGBA[j];
353          *localRGB++ = LutRGBA[j+1];
354          *localRGB++ = LutRGBA[j+2];
355       }
356     }
357  
358     else  // deal with 16 bits pixels and 16 bits Palette color
359     {
360       uint16_t *localRGB = (uint16_t *)RGB;
361       for (size_t i = 0; i < RawSize/2; ++i )
362       {
363          j  = ((uint16_t *)Raw)[i] * 4;
364          *localRGB++ = ((uint16_t *)LutRGBA)[j];
365          *localRGB++ = ((uint16_t *)LutRGBA)[j+1];
366          *localRGB++ = ((uint16_t *)LutRGBA)[j+2];
367       } 
368     }
369  
370    return true;
371 }
372
373 //-----------------------------------------------------------------------------
374 // Protected
375
376 //-----------------------------------------------------------------------------
377 // Private
378 /**
379  * \brief Read from file a 12 bits per pixel image and decompress it
380  *        into a 16 bits per pixel image.
381  */
382 void PixelReadConvert::ReadAndDecompress12BitsTo16Bits( std::ifstream *fp )
383                throw ( FormatError )
384 {
385    int nbPixels = XSize * YSize;
386    uint16_t *localDecompres = (uint16_t*)Raw;
387
388    for( int p = 0; p < nbPixels; p += 2 )
389    {
390       uint8_t b0, b1, b2;
391
392       fp->read( (char*)&b0, 1);
393       if ( fp->fail() || fp->eof() )
394       {
395          throw FormatError( "PixelReadConvert::ReadAndDecompress12BitsTo16Bits()",
396                                 "Unfound first block" );
397       }
398
399       fp->read( (char*)&b1, 1 );
400       if ( fp->fail() || fp->eof())
401       {
402          throw FormatError( "PixelReadConvert::ReadAndDecompress12BitsTo16Bits()",
403                                 "Unfound second block" );
404       }
405
406       fp->read( (char*)&b2, 1 );
407       if ( fp->fail() || fp->eof())
408       {
409          throw FormatError( "PixelReadConvert::ReadAndDecompress12BitsTo16Bits()",
410                                 "Unfound second block" );
411       }
412
413       // Two steps are necessary to please VC++
414       //
415       // 2 pixels 12bit =     [0xABCDEF]
416       // 2 pixels 16bit = [0x0ABD] + [0x0FCE]
417       //                        A                     B                 D
418       *localDecompres++ =  ((b0 >> 4) << 8) + ((b0 & 0x0f) << 4) + (b1 & 0x0f);
419       //                        F                     C                 E
420       *localDecompres++ =  ((b2 & 0x0f) << 8) + ((b1 >> 4) << 4) + (b2 >> 4);
421
422       /// \todo JPR Troubles expected on Big-Endian processors ?
423    }
424 }
425
426 /**
427  * \brief     Reads from disk the Pixel Data of JPEG Dicom encapsulated
428  *            file and decompress it.
429  * @param     fp File Pointer
430  * @return    Boolean
431  */
432 bool PixelReadConvert::ReadAndDecompressJPEGFile( std::ifstream *fp )
433 {
434    if ( IsJPEG2000 )
435    {
436      // make sure this is the right JPEG compression
437      assert( !IsJPEGLossless || !IsJPEGLossy || !IsJPEGLS );
438      // FIXME this is really ugly but it seems I have to load the complete
439      // jpeg2000 stream to use jasper:
440      // I don't think we'll ever be able to deal with multiple fragments properly
441
442       unsigned long inputlength = 0;
443       JPEGFragment *jpegfrag = JPEGInfo->GetFirstFragment();
444       while( jpegfrag )
445       {
446          inputlength += jpegfrag->GetLength();
447          jpegfrag = JPEGInfo->GetNextFragment();
448       }
449       gdcmAssertMacro( inputlength != 0);
450       uint8_t *inputdata = new uint8_t[inputlength];
451       char *pinputdata = (char*)inputdata;
452       jpegfrag = JPEGInfo->GetFirstFragment();
453       while( jpegfrag )
454       {
455          fp->seekg( jpegfrag->GetOffset(), std::ios::beg);
456          fp->read(pinputdata, jpegfrag->GetLength());
457          pinputdata += jpegfrag->GetLength();
458          jpegfrag = JPEGInfo->GetNextFragment();
459       }
460       // Warning the inputdata buffer is delete in the function
461       if ( ! gdcm_read_JPEG2000_file( Raw, 
462           (char*)inputdata, inputlength ) )
463       {
464          return true;
465       }
466       // wow what happen, must be an error
467       return false;
468    }
469    else if ( IsJPEGLS )
470    {
471      // make sure this is the right JPEG compression
472      assert( !IsJPEGLossless || !IsJPEGLossy || !IsJPEG2000 );
473    // WARNING : JPEG-LS is NOT the 'classical' Jpeg Lossless : 
474    // [JPEG-LS is the basis for new lossless/near-lossless compression
475    // standard for continuous-tone images intended for JPEG2000. The standard
476    // is based on the LOCO-I algorithm (LOw COmplexity LOssless COmpression
477    // for Images) developed at Hewlett-Packard Laboratories]
478    //
479    // see http://datacompression.info/JPEGLS.shtml
480    //
481 #if 0
482    std::cerr << "count:" << JPEGInfo->GetFragmentCount() << std::endl;
483       unsigned long inputlength = 0;
484       JPEGFragment *jpegfrag = JPEGInfo->GetFirstFragment();
485       while( jpegfrag )
486       {
487          inputlength += jpegfrag->GetLength();
488          jpegfrag = JPEGInfo->GetNextFragment();
489       }
490       gdcmAssertMacro( inputlength != 0);
491       uint8_t *inputdata = new uint8_t[inputlength];
492       char *pinputdata = (char*)inputdata;
493       jpegfrag = JPEGInfo->GetFirstFragment();
494       while( jpegfrag )
495       {
496          fp->seekg( jpegfrag->GetOffset(), std::ios::beg);
497          fp->read(pinputdata, jpegfrag->GetLength());
498          pinputdata += jpegfrag->GetLength();
499          jpegfrag = JPEGInfo->GetNextFragment();
500       }  
501       
502   //fp->read((char*)Raw, PixelDataLength);
503
504   std::ofstream out("/tmp/jpegls.jpg");
505   out.write((char*)inputdata, inputlength);
506   out.close();
507   delete[] inputdata;
508 #endif
509
510       gdcmWarningMacro( "Sorry, JPEG-LS not yet taken into account" );
511       fp->seekg( JPEGInfo->GetFirstFragment()->GetOffset(), std::ios::beg);
512 //    if ( ! gdcm_read_JPEGLS_file( fp,Raw ) )
513          return false;
514    }
515    else
516    {
517      // make sure this is the right JPEG compression
518      assert( !IsJPEGLS || !IsJPEG2000 );
519      // Precompute the offset localRaw will be shifted with
520      int length = XSize * YSize * SamplesPerPixel;
521      int numberBytes = BitsAllocated / 8;
522
523      JPEGInfo->DecompressFromFile(fp, Raw, BitsStored, numberBytes, length );
524      return true;
525    }
526 }
527
528 /**
529  * \brief Build Red/Green/Blue/Alpha LUT from File when :
530  *         - (0028,0004) : Photometric Interpretation == [PALETTE COLOR ]
531  *         and
532  *         - (0028,1101),(0028,1102),(0028,1102)
533  *            xxx Palette Color Lookup Table Descriptor are found
534  *          and 
535  *         - (0028,1201),(0028,1202),(0028,1202)
536  *           xxx Palette Color Lookup Table Data - are found
537  * \warning does NOT deal with :
538  *   - 0028 1100 Gray Lookup Table Descriptor (Retired)
539  *   - 0028 1221 Segmented Red Palette Color Lookup Table Data
540  *   - 0028 1222 Segmented Green Palette Color Lookup Table Data
541  *   - 0028 1223 Segmented Blue Palette Color Lookup Table Data
542  *   no known Dicom reader deals with them :-(
543  * @return a RGBA Lookup Table
544  */
545 void PixelReadConvert::BuildLUTRGBA()
546 {
547
548    // Note to code reviewers :
549    // The problem is *much more* complicated, since a lot of manufacturers
550    // Don't follow the norm :
551    // have a look at David Clunie's remark at the end of this .cxx file.
552    if ( LutRGBA )
553    
554    {
555       return;
556    }
557    // Not so easy : see
558    // http://www.barre.nom.fr/medical/dicom2/limitations.html#Color%20Lookup%20Tables
559                                                                                 
560    if ( ! IsPaletteColor )
561    {
562       return;
563    }
564                                                                                 
565    if (   LutRedDescriptor   == GDCM_UNFOUND
566        || LutGreenDescriptor == GDCM_UNFOUND
567        || LutBlueDescriptor  == GDCM_UNFOUND )
568    {
569       gdcmWarningMacro( "(At least) a LUT Descriptor is missing" );
570       return;
571    }
572
573    ////////////////////////////////////////////
574    // Extract the info from the LUT descriptors
575    int lengthR;   // Red LUT length in Bytes
576    int debR;      // Subscript of the first Lut Value
577    int nbitsR;    // Lut item size (in Bits)
578    int nbRead;    // nb of items in LUT descriptor (must be = 3)
579
580    nbRead = sscanf( LutRedDescriptor.c_str(),
581                         "%d\\%d\\%d",
582                         &lengthR, &debR, &nbitsR );
583    if ( nbRead != 3 )
584    {
585       gdcmWarningMacro( "Wrong Red LUT descriptor" );
586    }                                                                                
587    int lengthG;  // Green LUT length in Bytes
588    int debG;     // Subscript of the first Lut Value
589    int nbitsG;   // Lut item size (in Bits)
590
591    nbRead = sscanf( LutGreenDescriptor.c_str(),
592                     "%d\\%d\\%d",
593                     &lengthG, &debG, &nbitsG );  
594    if ( nbRead != 3 )
595    {
596       gdcmWarningMacro( "Wrong Green LUT descriptor" );
597    }
598                                                                                 
599    int lengthB;  // Blue LUT length in Bytes
600    int debB;     // Subscript of the first Lut Value
601    int nbitsB;   // Lut item size (in Bits)
602    nbRead = sscanf( LutRedDescriptor.c_str(),
603                     "%d\\%d\\%d",
604                     &lengthB, &debB, &nbitsB );
605    if ( nbRead != 3 )
606    {
607       gdcmWarningMacro( "Wrong Blue LUT descriptor" );
608    }
609  
610    gdcmWarningMacro(" lengthR " << lengthR << " debR " 
611                  << debR << " nbitsR " << nbitsR);
612    gdcmWarningMacro(" lengthG " << lengthG << " debG " 
613                  << debG << " nbitsG " << nbitsG);
614    gdcmWarningMacro(" lengthB " << lengthB << " debB " 
615                  << debB << " nbitsB " << nbitsB);
616
617    if ( !lengthR ) // if = 2^16, this shall be 0 see : CP-143
618       lengthR=65536;
619    if ( !lengthG ) // if = 2^16, this shall be 0
620       lengthG=65536;
621    if ( !lengthB ) // if = 2^16, this shall be 0
622       lengthB=65536; 
623                                                                                 
624    ////////////////////////////////////////////////////////
625
626    if ( ( ! LutRedData ) || ( ! LutGreenData ) || ( ! LutBlueData ) )
627    {
628       gdcmWarningMacro( "(At least) a LUT is missing" );
629       return;
630    }
631
632    // -------------------------------------------------------------
633    
634    if ( BitsAllocated <= 8 )
635    {
636       // forge the 4 * 8 Bits Red/Green/Blue/Alpha LUT
637       LutRGBA = new uint8_t[ 1024 ]; // 256 * 4 (R, G, B, Alpha)
638       if ( !LutRGBA )
639          return;
640       LutItemNumber = 256;
641       LutItemSize   = 8;
642       memset( LutRGBA, 0, 1024 );
643                                                                                 
644       int mult;
645       if ( ( nbitsR == 16 ) && ( BitsAllocated == 8 ) )
646       {
647          // when LUT item size is different than pixel size
648          mult = 2; // high byte must be = low byte
649       }
650       else
651       {
652          // See PS 3.3-2003 C.11.1.1.2 p 619
653          mult = 1;
654       }
655                                                                                 
656       // if we get a black image, let's just remove the '+1'
657       // from 'i*mult+1' and check again
658       // if it works, we shall have to check the 3 Palettes
659       // to see which byte is ==0 (first one, or second one)
660       // and fix the code
661       // We give up the checking to avoid some (useless ?) overhead
662       // (optimistic asumption)
663       int i;
664       uint8_t *a;
665
666       //take "Subscript of the first Lut Value" (debR,debG,debB) into account!
667
668       //FIXME :  +1 : to get 'low value' byte
669       //         Trouble expected on Big Endian Processors ?
670       //         16 BIts Per Pixel Palette Color to be swapped?
671
672       a = LutRGBA + 0 + debR;
673       for( i=0; i < lengthR; ++i )
674       {
675          *a = LutRedData[i*mult+1]; 
676          a += 4;
677       }
678                                                                                 
679       a = LutRGBA + 1 + debG;
680       for( i=0; i < lengthG; ++i)
681       {
682          *a = LutGreenData[i*mult+1];
683          a += 4;
684       }
685                                                                                 
686       a = LutRGBA + 2 + debB;
687       for(i=0; i < lengthB; ++i)
688       {
689          *a = LutBlueData[i*mult+1];
690          a += 4;
691       }
692                                     
693       a = LutRGBA + 3 ;
694       for(i=0; i < 256; ++i)
695       {
696          *a = 1; // Alpha component
697          a += 4;
698       }
699    }
700    else
701    {
702       // Probabely the same stuff is to be done for 16 Bits Pixels
703       // with 65536 entries LUT ?!?
704       // Still looking for accurate info on the web :-(
705
706       gdcmWarningMacro( "Sorry Palette Color Lookup Tables not yet dealt with"
707                          << " for 16 Bits Per Pixel images" );
708
709       // forge the 4 * 16 Bits Red/Green/Blue/Alpha LUT
710
711       LutRGBA = (uint8_t *)new uint16_t[ 65536*4 ]; // 2^16 * 4 (R, G, B, Alpha)
712       if ( !LutRGBA )
713          return;
714       memset( LutRGBA, 0, 65536*4*2 );  // 16 bits = 2 bytes ;-)
715
716       LutItemNumber = 65536;
717       LutItemSize   = 16;
718
719       int i;
720       uint16_t *a16;
721
722       //take "Subscript of the first Lut Value" (debR,debG,debB) into account!
723
724       a16 = (uint16_t*)LutRGBA + 0 + debR;
725       for( i=0; i < lengthR; ++i )
726       {
727          *a16 = ((uint16_t*)LutRedData)[i];
728          a16 += 4;
729       }
730                                                                               
731       a16 = (uint16_t*)LutRGBA + 1 + debG;
732       for( i=0; i < lengthG; ++i)
733       {
734          *a16 = ((uint16_t*)LutGreenData)[i];
735          a16 += 4;
736       }
737                                                                                 
738       a16 = (uint16_t*)LutRGBA + 2 + debB;
739       for(i=0; i < lengthB; ++i)
740       {
741          *a16 = ((uint16_t*)LutBlueData)[i];
742          a16 += 4;
743       }
744                                                                              
745       a16 = (uint16_t*)LutRGBA + 3 ;
746       for(i=0; i < 65536; ++i)
747       {
748          *a16 = 1; // Alpha component
749          a16 += 4;
750       }
751 /* Just to 'see' the LUT, at debug time
752 // Don't remove this commented out code.
753
754       a16=(uint16_t*)LutRGBA;
755       for (int j=0;j<65536;j++)
756       {
757          std::cout << *a16     << " " << *(a16+1) << " "
758                    << *(a16+2) << " " << *(a16+3) << std::endl;
759          a16+=4;
760       }
761 */
762    }
763 }
764
765 /**
766  * \brief Swap the bytes, according to \ref SwapCode.
767  */
768 void PixelReadConvert::ConvertSwapZone()
769 {
770    unsigned int i;
771    uint16_t localSwapCode = SwapCode;
772    
773    // If this file is 'ImplicitVR BigEndian PrivateGE Transfer Syntax', 
774    // then the header is in little endian format and the pixel data is in 
775    // big endian format.  When reading the header, GDCM has already established
776    // a byte swapping code suitable for this machine to read the
777    // header. In TS::ImplicitVRBigEndianPrivateGE, this code will need
778    // to be switched in order to read the pixel data.  This must be
779    // done REGARDLESS of the processor endianess!
780    //
781    // Example:  Assume we are on a little endian machine.  When
782    // GDCM reads the header, the header will match the machine
783    // endianess and the swap code will be established as a no-op.
784    // When GDCM reaches the pixel data, it will need to switch the
785    // swap code to do big endian to little endian conversion.
786    //
787    // Now, assume we are on a big endian machine.  When GDCM reads the
788    // header, the header will be recognized as a different endianess
789    // than the machine endianess, and a swap code will be established
790    // to convert from little endian to big endian.  When GDCM readers
791    // the pixel data, the pixel data endianess will now match the
792    // machine endianess.  But we currently have a swap code that
793    // converts from little endian to big endian.  In this case, we
794    // need to switch the swap code to a no-op.
795    //
796    // Therefore, in either case, if the file is in
797    // 'ImplicitVR BigEndian PrivateGE Transfer Syntax', then GDCM needs to switch
798    // the byte swapping code when entering the pixel data.
799
800 /* //Let me check something.
801    //I wait for the Dashboard !   
802    if ( IsPrivateGETransferSyntax )
803    {
804       // PrivateGETransferSyntax only exists for 'true' Dicom images
805       // we assume there is no 'exotic' 32 bits endianess!
806       switch (localSwapCode)
807       {
808          case 1234:
809             localSwapCode = 4321;
810             break;
811          case 4321:
812             localSwapCode = 1234;
813             break;
814       }  
815    }
816 */   
817    if ( BitsAllocated == 16 )
818    {
819       uint16_t *im16 = (uint16_t*)Raw;
820       switch( localSwapCode )
821       {
822          case 1234:
823             break;
824          case 3412:
825          case 2143:
826          case 4321:
827             for( i = 0; i < RawSize / 2; i++ )
828             {
829                im16[i]= (im16[i] >> 8) | (im16[i] << 8 );
830             }
831             break;
832          default:
833             gdcmWarningMacro("SwapCode value (16 bits) not allowed.");
834       }
835    }
836    else if ( BitsAllocated == 32 )
837    {
838       uint32_t s32;
839       uint16_t high;
840       uint16_t low;
841       uint32_t *im32 = (uint32_t*)Raw;
842       switch ( localSwapCode )
843       {
844          case 1234:
845             break;
846          case 4321:
847             for( i = 0; i < RawSize / 4; i++ )
848             {
849                low     = im32[i] & 0x0000ffff;  // 4321
850                high    = im32[i] >> 16;
851                high    = ( high >> 8 ) | ( high << 8 );
852                low     = ( low  >> 8 ) | ( low  << 8 );
853                s32     = low;
854                im32[i] = ( s32 << 16 ) | high;
855             }
856             break;
857          case 2143:
858             for( i = 0; i < RawSize / 4; i++ )
859             {
860                low     = im32[i] & 0x0000ffff;   // 2143
861                high    = im32[i] >> 16;
862                high    = ( high >> 8 ) | ( high << 8 );
863                low     = ( low  >> 8 ) | ( low  << 8 );
864                s32     = high;
865                im32[i] = ( s32 << 16 ) | low;
866             }
867             break;
868          case 3412:
869             for( i = 0; i < RawSize / 4; i++ )
870             {
871                low     = im32[i] & 0x0000ffff; // 3412
872                high    = im32[i] >> 16;
873                s32     = low;
874                im32[i] = ( s32 << 16 ) | high;
875             }
876             break;
877          default:
878             gdcmWarningMacro("SwapCode value (32 bits) not allowed." );
879       }
880    }
881 }
882
883 /**
884  * \brief Deal with endianness i.e. re-arange bytes inside the integer
885  */
886 void PixelReadConvert::ConvertReorderEndianity()
887 {
888    if ( BitsAllocated != 8 )
889    {
890       ConvertSwapZone();
891    }
892
893    // Special kludge in order to deal with xmedcon broken images:
894    if ( BitsAllocated == 16
895      && BitsStored < BitsAllocated
896      && !PixelSign )
897    {
898       int l = (int)( RawSize / ( BitsAllocated / 8 ) );
899       uint16_t *deb = (uint16_t *)Raw;
900       for(int i = 0; i<l; i++)
901       {
902          if ( *deb == 0xffff )
903          {
904            *deb = 0;
905          }
906          deb++;
907       }
908    }
909 }
910
911 /**
912  * \brief Deal with Grey levels i.e. re-arange them
913  *        to have low values = dark, high values = bright
914  */
915 void PixelReadConvert::ConvertFixGreyLevels()
916 {
917    if (!IsMonochrome1)
918       return;
919
920    uint32_t i; // to please M$VC6
921    int16_t j;
922
923    if (!PixelSign)
924    {
925       if ( BitsAllocated == 8 )
926       {
927          uint8_t *deb = (uint8_t *)Raw;
928          for (i=0; i<RawSize; i++)      
929          {
930             *deb = 255 - *deb;
931             deb++;
932          }
933          return;
934       }
935
936       if ( BitsAllocated == 16 )
937       {
938          uint16_t mask =1;
939          for (j=0; j<BitsStored-1; j++)
940          {
941             mask = (mask << 1) +1; // will be fff when BitsStored=12
942          }
943
944          uint16_t *deb = (uint16_t *)Raw;
945          for (i=0; i<RawSize/2; i++)      
946          {
947             *deb = mask - *deb;
948             deb++;
949          }
950          return;
951        }
952    }
953    else
954    {
955       if ( BitsAllocated == 8 )
956       {
957          uint8_t smask8 = 255;
958          uint8_t *deb = (uint8_t *)Raw;
959          for (i=0; i<RawSize; i++)      
960          {
961             *deb = smask8 - *deb;
962             deb++;
963          }
964          return;
965       }
966       if ( BitsAllocated == 16 )
967       {
968          uint16_t smask16 = 65535;
969          uint16_t *deb = (uint16_t *)Raw;
970          for (i=0; i<RawSize/2; i++)      
971          {
972             *deb = smask16 - *deb;
973             deb++;
974          }
975          return;
976       }
977    }
978 }
979
980 /**
981  * \brief  Re-arrange the bits within the bytes.
982  * @return Boolean always true
983  */
984 bool PixelReadConvert::ConvertReArrangeBits() throw ( FormatError )
985 {
986
987    if ( BitsStored != BitsAllocated )
988    {
989       int l = (int)( RawSize / ( BitsAllocated / 8 ) );
990       if ( BitsAllocated == 16 )
991       {
992          // pmask : to mask the 'unused bits' (may contain overlays)
993          uint16_t pmask = 0xffff;
994          pmask = pmask >> ( BitsAllocated - BitsStored );
995
996          uint16_t *deb = (uint16_t*)Raw;
997
998          if ( !PixelSign )  // Pixels are unsigned
999          {
1000             for(int i = 0; i<l; i++)
1001             {   
1002                *deb = (*deb >> (BitsStored - HighBitPosition - 1)) & pmask;
1003                deb++;
1004             }
1005          }
1006          else // Pixels are signed
1007          {
1008             // smask : to check the 'sign' when BitsStored != BitsAllocated
1009             uint16_t smask = 0x0001;
1010             smask = smask << ( 16 - (BitsAllocated - BitsStored + 1) );
1011             // nmask : to propagate sign bit on negative values
1012             int16_t nmask = (int16_t)0x8000;  
1013             nmask = nmask >> ( BitsAllocated - BitsStored - 1 );
1014  
1015             for(int i = 0; i<l; i++)
1016             {
1017                *deb = *deb >> (BitsStored - HighBitPosition - 1);
1018                if ( *deb & smask )
1019                {
1020                   *deb = *deb | nmask;
1021                }
1022                else
1023                {
1024                   *deb = *deb & pmask;
1025                }
1026                deb++;
1027             }
1028          }
1029       }
1030       else if ( BitsAllocated == 32 )
1031       {
1032          // pmask : to mask the 'unused bits' (may contain overlays)
1033          uint32_t pmask = 0xffffffff;
1034          pmask = pmask >> ( BitsAllocated - BitsStored );
1035
1036          uint32_t *deb = (uint32_t*)Raw;
1037
1038          if ( !PixelSign )
1039          {
1040             for(int i = 0; i<l; i++)
1041             {             
1042                *deb = (*deb >> (BitsStored - HighBitPosition - 1)) & pmask;
1043                deb++;
1044             }
1045          }
1046          else
1047          {
1048             // smask : to check the 'sign' when BitsStored != BitsAllocated
1049             uint32_t smask = 0x00000001;
1050             smask = smask >> ( 32 - (BitsAllocated - BitsStored +1 ));
1051             // nmask : to propagate sign bit on negative values
1052             int32_t nmask = 0x80000000;   
1053             nmask = nmask >> ( BitsAllocated - BitsStored -1 );
1054
1055             for(int i = 0; i<l; i++)
1056             {
1057                *deb = *deb >> (BitsStored - HighBitPosition - 1);
1058                if ( *deb & smask )
1059                   *deb = *deb | nmask;
1060                else
1061                   *deb = *deb & pmask;
1062                deb++;
1063             }
1064          }
1065       }
1066       else
1067       {
1068          gdcmWarningMacro("Weird image (BitsAllocated !=8, 12, 16, 32)");
1069          throw FormatError( "Weird image !?" );
1070       }
1071    }
1072    return true;
1073 }
1074
1075 /**
1076  * \brief   Convert (Red plane, Green plane, Blue plane) to RGB pixels
1077  * \warning Works on all the frames at a time
1078  */
1079 void PixelReadConvert::ConvertRGBPlanesToRGBPixels()
1080 {
1081    gdcmWarningMacro("--> ConvertRGBPlanesToRGBPixels");
1082
1083    uint8_t *localRaw = Raw;
1084    uint8_t *copyRaw = new uint8_t[ RawSize ];
1085    memmove( copyRaw, localRaw, RawSize );
1086
1087    int l = XSize * YSize * ZSize;
1088
1089    uint8_t *a = copyRaw;
1090    uint8_t *b = copyRaw + l;
1091    uint8_t *c = copyRaw + l + l;
1092
1093    for (int j = 0; j < l; j++)
1094    {
1095       *(localRaw++) = *(a++);
1096       *(localRaw++) = *(b++);
1097       *(localRaw++) = *(c++);
1098    }
1099    delete[] copyRaw;
1100 }
1101
1102 /**
1103  * \brief   Convert (cY plane, cB plane, cR plane) to RGB pixels
1104  * \warning Works on all the frames at a time
1105  */
1106 void PixelReadConvert::ConvertYcBcRPlanesToRGBPixels()
1107 {
1108   // Remarks for YBR newbees :
1109   // YBR_FULL works very much like RGB, i.e. three samples per pixel, 
1110   // just the color space is YCbCr instead of RGB. This is particularly useful
1111   // for doppler ultrasound where most of the image is grayscale 
1112   // (i.e. only populates the Y components) and Cb and Cr are mostly zero,
1113   // except for the few patches of color on the image.
1114   // On such images, RLE achieves a compression ratio that is much better 
1115   // than the compression ratio on an equivalent RGB image. 
1116  
1117    gdcmWarningMacro("--> ConvertYcBcRPlanesToRGBPixels");
1118    
1119    uint8_t *localRaw = Raw;
1120    uint8_t *copyRaw = new uint8_t[ RawSize ];
1121    memmove( copyRaw, localRaw, RawSize );
1122
1123    // to see the tricks about YBR_FULL, YBR_FULL_422,
1124    // YBR_PARTIAL_422, YBR_ICT, YBR_RCT have a look at :
1125    // ftp://medical.nema.org/medical/dicom/final/sup61_ft.pdf
1126    // and be *very* affraid
1127    //
1128    int l        = XSize * YSize;
1129    int nbFrames = ZSize;
1130
1131    uint8_t *a = copyRaw + 0;
1132    uint8_t *b = copyRaw + l;
1133    uint8_t *c = copyRaw + l+ l;
1134    int32_t R, G, B;
1135
1136    ///  We replaced easy to understand but time consuming floating point
1137    ///  computations by the 'well known' integer computation counterpart
1138    ///  Refer to :
1139    ///            http://lestourtereaux.free.fr/papers/data/yuvrgb.pdf
1140    ///  for code optimisation.
1141
1142    for ( int i = 0; i < nbFrames; i++ )
1143    {
1144       for ( int j = 0; j < l; j++ )
1145       {
1146          R = 38142 *(*a-16) + 52298 *(*c -128);
1147          G = 38142 *(*a-16) - 26640 *(*c -128) - 12845 *(*b -128);
1148          B = 38142 *(*a-16) + 66093 *(*b -128);
1149
1150          R = (R+16384)>>15;
1151          G = (G+16384)>>15;
1152          B = (B+16384)>>15;
1153
1154          if (R < 0)   R = 0;
1155          if (G < 0)   G = 0;
1156          if (B < 0)   B = 0;
1157          if (R > 255) R = 255;
1158          if (G > 255) G = 255;
1159          if (B > 255) B = 255;
1160
1161          *(localRaw++) = (uint8_t)R;
1162          *(localRaw++) = (uint8_t)G;
1163          *(localRaw++) = (uint8_t)B;
1164          a++;
1165          b++;
1166          c++;
1167       }
1168    }
1169    delete[] copyRaw;
1170 }
1171
1172 /// \brief Deals with the color decoding i.e. handle:
1173 ///   - R, G, B planes (as opposed to RGB pixels)
1174 ///   - YBR (various) encodings.
1175 ///   - LUT[s] (or "PALETTE COLOR").
1176
1177 void PixelReadConvert::ConvertHandleColor()
1178 {
1179    //////////////////////////////////
1180    // Deal with the color decoding i.e. handle:
1181    //   - R, G, B planes (as opposed to RGB pixels)
1182    //   - YBR (various) encodings.
1183    //   - LUT[s] (or "PALETTE COLOR").
1184    //
1185    // The classification in the color decoding schema is based on the blending
1186    // of two Dicom tags values:
1187    // * "Photometric Interpretation" for which we have the cases:
1188    //  - [Photo A] MONOCHROME[1|2] pictures,
1189    //  - [Photo B] RGB or YBR_FULL_422 (which acts as RGB),
1190    //  - [Photo C] YBR_* (with the above exception of YBR_FULL_422)
1191    //  - [Photo D] "PALETTE COLOR" which indicates the presence of LUT[s].
1192    // * "Planar Configuration" for which we have the cases:
1193    //  - [Planar 0] 0 then Pixels are already RGB
1194    //  - [Planar 1] 1 then we have 3 planes : R, G, B,
1195    //  - [Planar 2] 2 then we have 1 gray Plane and 3 LUTs
1196    //
1197    // Now in theory, one could expect some coherence when blending the above
1198    // cases. For example we should not encounter files belonging at the
1199    // time to case [Planar 0] and case [Photo D].
1200    // Alas, this was only theory ! Because in practice some odd (read ill
1201    // formated Dicom) files (e.g. gdcmData/US-PAL-8-10x-echo.dcm) we encounter:
1202    //     - "Planar Configuration" = 0,
1203    //     - "Photometric Interpretation" = "PALETTE COLOR".
1204    // Hence gdcm will use the folowing "heuristic" in order to be tolerant
1205    // towards Dicom-non-conformant files:
1206    //   << whatever the "Planar Configuration" value might be, a
1207    //      "Photometric Interpretation" set to "PALETTE COLOR" forces
1208    //      a LUT intervention >>
1209    //
1210    // Now we are left with the following handling of the cases:
1211    // - [Planar 0] OR  [Photo A] no color decoding (since respectively
1212    //       Pixels are already RGB and monochrome pictures have no color :),
1213    // - [Planar 1] AND [Photo B] handled with ConvertRGBPlanesToRGBPixels()
1214    // - [Planar 1] AND [Photo C] handled with ConvertYcBcRPlanesToRGBPixels()
1215    // - [Planar 2] OR  [Photo D] requires LUT intervention.
1216
1217    gdcmWarningMacro("--> ConvertHandleColor"
1218                     << "Planar Configuration " << PlanarConfiguration );
1219
1220    if ( ! IsRawRGB() )
1221    {
1222       // [Planar 2] OR  [Photo D]: LUT intervention done outside
1223       gdcmWarningMacro("--> RawRGB : LUT intervention done outside");
1224       return;
1225    }
1226                                                                                 
1227    if ( PlanarConfiguration == 1 )
1228    {
1229       if ( IsYBRFull )
1230       {
1231          // [Planar 1] AND [Photo C] (remember YBR_FULL_422 acts as RGB)
1232          gdcmWarningMacro("--> YBRFull");
1233          ConvertYcBcRPlanesToRGBPixels();
1234       }
1235       else
1236       {
1237          // [Planar 1] AND [Photo C]
1238          gdcmWarningMacro("--> YBRFull");
1239          ConvertRGBPlanesToRGBPixels();
1240       }
1241       return;
1242    }
1243                                                                                 
1244    // When planarConf is 0, and RLELossless (forbidden by Dicom norm)
1245    // pixels need to be RGB-fyied anyway
1246
1247    if (IsRLELossless)
1248    { 
1249      gdcmWarningMacro("--> RLE Lossless");
1250      ConvertRGBPlanesToRGBPixels();
1251    }
1252
1253    // In *normal *case, when planarConf is 0, pixels are already in RGB
1254 }
1255
1256 /// Computes the Pixels Size
1257 void PixelReadConvert::ComputeRawAndRGBSizes()
1258 {
1259    int bitsAllocated = BitsAllocated;
1260    // Number of "Bits Allocated" is fixed to 16 when it's 12, since
1261    // in this case we will expand the image to 16 bits (see
1262    //    \ref ReadAndDecompress12BitsTo16Bits() )
1263    if (  BitsAllocated == 12 )
1264    {
1265       bitsAllocated = 16;
1266    }
1267                                                                                 
1268    RawSize =  XSize * YSize * ZSize
1269                      * ( bitsAllocated / 8 )
1270                      * SamplesPerPixel;
1271    if ( HasLUT )
1272    {
1273       RGBSize = 3 * RawSize; // works for 8 and 16 bits per Pixel
1274    }
1275    else
1276    {
1277       RGBSize = RawSize;
1278    }
1279 }
1280
1281 /// Allocates room for RGB Pixels
1282 void PixelReadConvert::AllocateRGB()
1283 {
1284   if ( RGB )
1285      delete [] RGB;
1286   RGB = new uint8_t[RGBSize];
1287 }
1288
1289 /// Allocates room for RAW Pixels
1290 void PixelReadConvert::AllocateRaw()
1291 {
1292   if ( Raw )
1293      delete [] Raw;
1294   Raw = new uint8_t[RawSize];
1295 }
1296
1297 //-----------------------------------------------------------------------------
1298 // Print
1299 /**
1300  * \brief        Print self.
1301  * @param indent Indentation string to be prepended during printing.
1302  * @param os     Stream to print to.
1303  */
1304 void PixelReadConvert::Print( std::ostream &os, std::string const &indent )
1305 {
1306    os << indent
1307       << "--- Pixel information -------------------------"
1308       << std::endl;
1309    os << indent
1310       << "Pixel Data: offset " << PixelOffset
1311       << " x(" << std::hex << PixelOffset << std::dec
1312       << ")   length " << PixelDataLength
1313       << " x(" << std::hex << PixelDataLength << std::dec
1314       << ")" << std::endl;
1315
1316    if ( IsRLELossless )
1317    {
1318       if ( RLEInfo )
1319       {
1320          RLEInfo->Print( os, indent );
1321       }
1322       else
1323       {
1324          gdcmWarningMacro("Set as RLE file but NO RLEinfo present.");
1325       }
1326    }
1327
1328    if ( IsJPEG2000 || IsJPEGLossless || IsJPEGLossy || IsJPEGLS )
1329    {
1330       if ( JPEGInfo )
1331       {
1332          JPEGInfo->Print( os, indent );
1333       }
1334       else
1335       {
1336          gdcmWarningMacro("Set as JPEG file but NO JPEGinfo present.");
1337       }
1338    }
1339 }
1340
1341 //-----------------------------------------------------------------------------
1342 } // end namespace gdcm
1343
1344 // Note to developpers :
1345 // Here is a very detailled post from David Clunie, on the troubles caused 
1346 // 'non standard' LUT and LUT description
1347 // We shall have to take it into accound in our code.
1348 // Some day ...
1349
1350
1351 /*
1352 Subject: Problem with VOI LUTs in Agfa and Fuji CR and GE DX images, was Re: VOI LUT issues
1353 Date: Sun, 06 Feb 2005 17:13:40 GMT
1354 From: David Clunie <dclunie@dclunie.com>
1355 Reply-To: dclunie@dclunie.com
1356 Newsgroups: comp.protocols.dicom
1357 References: <1107553502.040221.189550@o13g2000cwo.googlegroups.com>
1358
1359 > THE LUT that comes with [my] image claims to be 16-bit, but none of the
1360 > values goes higher than 4095.  That being said, though, none of my
1361 > original pixel values goes higher than that, either.  I have read
1362 > elsewhere on this group that when that happens you are supposed to
1363 > adjust the LUT.  Can someone be more specific?  There was a thread from
1364 > 2002 where Marco and David were mentioning doing precisely that.
1365 >
1366 > Thanks
1367 >
1368 > -carlos rodriguez
1369
1370
1371 You have encountered the well known "we know what the standard says but
1372 we are going to ignore it and do what we have been doing for almost
1373 a decade regardless" CR vendor bug. Agfa started this, but they are not
1374 the only vendor doing this now; GE and Fuji may have joined the club.
1375
1376 Sadly, one needs to look at the LUT Data, figure out what the maximum
1377 value actually encoded is, and find the next highest power of 2 (e.g.
1378 212 in this case), to figure out what the range of the data is
1379 supposed to be. I have assumed that if the maximum value in the LUT
1380 data is less than a power of 2 minus 1 (e.g. 0xebc) then the intent
1381 of the vendor was not to use the maximum available grayscale range
1382 of the display (e.g. the maximum is 0xfff in this case). An alternative
1383 would be to scale to the actual maximum rather than a power of two.
1384
1385 Very irritating, and in theory not totally reliable if one really
1386 intended the full 16 bits and only used, say 15, but that is extremely
1387 unlikely since everything would be too dark, and this heuristic
1388 seems to work OK.
1389
1390 There has never been anything in the standard that describes having
1391 to go through these convolutions. Since the only value in the
1392 standard that describes the bit depth of the LUT values is LUT
1393 Descriptor value 3 and that is (usually) always required to be
1394 either 8 or 16, it mystifies me how the creators' of these images
1395 imagine that the receiver is going to divine the range that is intended. Further, the standard is quite explicit that this 3rd
1396 value defines the range of LUT values, but as far as I am aware, all
1397 the vendors are ignoring the standard and indeed sending a third value
1398 of 16 in all cases.
1399
1400 This problem is not confined to CR, and is also seen with DX products.
1401
1402 Typically I have seen:
1403
1404 - Agfa CR, which usually (always ?) sends LUTs, values up to 0x0fff
1405 - Fuji CR, which occasionally send LUTs, values up to 0x03ff
1406 - GE DX, for presentation, which always have LUTs, up to 0x3fff
1407
1408 Swissray, Siemens, Philips, Canon and Kodak never seem to send VOI LUTs
1409 at this point (which is a whole other problem). Note that the presence
1410 or absence of a VOI LUT as opposed to window values may be configurable
1411 on the modality in some cases, and I have just looked at what I happen
1412 to have received from a myriad of sites over whose configuration I have
1413 no control. This may be why the majority of Fuji images have no VOI LUTs,
1414 but a few do (or it may be the Siemens system that these Fuji images went
1415 through that perhaps added it). I do have some test Hologic DX images that
1416 are not from a clinical site that do actually get this right (a value
1417 of 12 for the third value and a max of 0xfff).
1418
1419 Since almost every vendor that I have encountered that encodes LUTs
1420 makes this mistake, perhaps it is time to amend the standard to warn
1421 implementor's of receivers and/or sanction this bad behavior. We have
1422 talked about this in the past in WG 6 but so far everyone has been
1423 reluctant to write into the standard such a comment. Maybe it is time
1424 to try again, since if one is not aware of this problem, one cannot
1425 effectively implement display using VOI LUTs, and there is a vast
1426 installed base to contend with.
1427
1428 I did not check presentation states, in which VOI LUTs could also be
1429 encountered, for the prevalence of this mistake, nor did I look at the
1430 encoding of Modality LUT's, which are unusual. Nor did I check digital
1431 mammography images. I would be interested to hear from anyone who has.
1432
1433 David
1434
1435 PS. The following older thread in this newsgroup discusses this:
1436
1437 "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"
1438
1439 PPS. From a historical perspective, the following may be of interest.
1440
1441 In the original standard in 1993, all that was said about this was a
1442 reference to the corresponding such where Modality LUTs are described
1443 that said:
1444
1445 "The third value specifies the number of bits for each entry in the
1446 LUT Data. It shall take the value 8 or 16. The LUT Data shall be stored
1447 in a format equivalent to 8 or 16 bits allocated and high bit equal
1448 1-bits allocated."
1449
1450 Since the high bit hint was not apparently explicit enough, a very
1451 early CP, CP 15 (submitted by Agfa as it happens), replaced this with:
1452
1453 "The third value conveys the range of LUT entry values. It shall take
1454 the value 8 or 16, corresponding with the LUT entry value range of
1455 256 or 65536.
1456
1457 Note:    The third value is not required for describing the
1458     LUT data and is only included for informational usage
1459     and for maintaining compatibility with ACRNEMA 2.0.
1460
1461 The LUT Data contains the LUT entry values."
1462
1463 That is how it read in the 1996, 1998 and 1999 editions.
1464
1465 By the 2000 edition, Supplement 33 that introduced presentation states
1466 extensively reworked this entire section and tried to explain this in
1467 different words:
1468
1469 "The output range is from 0 to 2^n-1 where n is the third value of LUT
1470 Descriptor. This range is always unsigned."
1471
1472 and also added a note to spell out what the output range meant in the
1473 VOI LUT section:
1474
1475 "9. The output of the Window Center/Width or VOI LUT transformation
1476 is either implicitly scaled to the full range of the display device
1477 if there is no succeeding transformation defined, or implicitly scaled
1478 to the full input range of the succeeding transformation step (such as
1479 the Presentation LUT), if present. See C.11.6.1."
1480
1481 It still reads this way in the 2004 edition.
1482
1483 Note that LUTs in other applications than the general VOI LUT allow for
1484 values other than 8 or 16 in the third value of LUT descriptor to permit
1485 ranges other than 0 to 255 or 65535.
1486
1487 In addition, the DX Image Module specializes the VOI LUT
1488 attributes as follows, in PS 3.3 section C.8.11.3.1.5 (added in Sup 32):
1489
1490 "The third value specifies the number of bits for each entry in the LUT
1491 Data (analogous to ìbits storedî). It shall be between 10-16. The LUT
1492 Data shall be stored in a format equivalent to 16 ìbits allocatedî and
1493 ìhigh bitî equal to ìbits storedî - 1. The third value conveys the range
1494 of LUT entry values. These unsigned LUT entry values shall range between
1495 0 and 2^n-1, where n is the third value of the LUT Descriptor."
1496
1497 So in the case of the GE DX for presentation images, the third value of
1498 LUT descriptor is allowed to be and probably should be 14 rather than 16.
1499
1500 */