1 /*=========================================================================
2 
3   Program: GDCM (Grassroots DICOM). A DICOM library
4 
5   Copyright (c) 2006-2011 Mathieu Malaterre
6   All rights reserved.
7   See Copyright.txt or http://gdcm.sourceforge.net/Copyright.html for details.
8 
9      This software is distributed WITHOUT ANY WARRANTY; without even
10      the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
11      PURPOSE.  See the above copyright notice for more information.
12 
13 =========================================================================*/
14 #include "gdcmImageChangeTransferSyntax.h"
15 #include "gdcmSequenceOfFragments.h"
16 #include "gdcmSequenceOfItems.h"
17 #include "gdcmFragment.h"
18 #include "gdcmPixmap.h"
19 #include "gdcmBitmap.h"
20 #include "gdcmRAWCodec.h"
21 #include "gdcmJPEGCodec.h"
22 #include "gdcmJPEGLSCodec.h"
23 #include "gdcmJPEG2000Codec.h"
24 #include "gdcmRLECodec.h"
25 
26 namespace gdcm
27 {
28 
29 /*
30 bool ImageChangeTransferSyntax::TryRAWCodecIcon(const DataElement &pixelde)
31 {
32   unsigned long len = Input->GetIconImage().GetBufferLength();
33   //assert( len == pixelde.GetByteValue()->GetLength() );
34   const TransferSyntax &ts = GetTransferSyntax();
35 
36   RAWCodec codec;
37   if( codec.CanCode( ts ) )
38     {
39     codec.SetDimensions( Input->GetIconImage().GetDimensions() );
40     codec.SetPlanarConfiguration( Input->GetIconImage().GetPlanarConfiguration() );
41     codec.SetPhotometricInterpretation( Input->GetIconImage().GetPhotometricInterpretation() );
42     codec.SetPixelFormat( Input->GetIconImage().GetPixelFormat() );
43     codec.SetNeedOverlayCleanup( Input->GetIconImage().AreOverlaysInPixelData() );
44     DataElement out;
45     //bool r = codec.Code(Input->GetDataElement(), out);
46     bool r = codec.Code(pixelde, out);
47 
48     DataElement &de = Output->GetIconImage().GetDataElement();
49     de.SetValue( out.GetValue() );
50     if( !r )
51       {
52       return false;
53       }
54     return true;
55     }
56   return false;
57 }
58 */
59 
UpdatePhotometricInterpretation(Bitmap const & input,Bitmap & output)60 void UpdatePhotometricInterpretation( Bitmap const &input, Bitmap &output )
61 {
62   // when decompressing J2K, need to revert to proper photo inter in uncompressed TS:
63   if( input.GetPhotometricInterpretation() == PhotometricInterpretation::YBR_RCT
64     || input.GetPhotometricInterpretation() == PhotometricInterpretation::YBR_ICT )
65     {
66     output.SetPhotometricInterpretation( PhotometricInterpretation::RGB );
67     }
68   // when decompressing loss jpeg, need to revert to proper photo inter in uncompressed TS:
69   if( input.GetPhotometricInterpretation() == PhotometricInterpretation::YBR_FULL_422 )
70     {
71     output.SetPhotometricInterpretation( PhotometricInterpretation::YBR_FULL );
72     }
73   assert( output.GetPhotometricInterpretation() == PhotometricInterpretation::RGB
74     || output.GetPhotometricInterpretation() == PhotometricInterpretation::YBR_FULL
75     || output.GetPhotometricInterpretation() == PhotometricInterpretation::MONOCHROME1
76     || output.GetPhotometricInterpretation() == PhotometricInterpretation::MONOCHROME2
77     || output.GetPhotometricInterpretation() == PhotometricInterpretation::ARGB
78     || output.GetPhotometricInterpretation() == PhotometricInterpretation::PALETTE_COLOR ); // programmer error
79 }
80 
TryRAWCodec(const DataElement & pixelde,Bitmap const & input,Bitmap & output)81 bool ImageChangeTransferSyntax::TryRAWCodec(const DataElement &pixelde, Bitmap const &input, Bitmap &output)
82 {
83   unsigned long len = input.GetBufferLength(); (void)len;
84   //assert( len == pixelde.GetByteValue()->GetLength() );
85   const TransferSyntax &ts = GetTransferSyntax();
86 
87   RAWCodec codec;
88   if( codec.CanCode( ts ) )
89     {
90     codec.SetDimensions( input.GetDimensions() );
91     codec.SetPlanarConfiguration( input.GetPlanarConfiguration() );
92     codec.SetPhotometricInterpretation( input.GetPhotometricInterpretation() );
93     codec.SetPixelFormat( input.GetPixelFormat() );
94     codec.SetNeedOverlayCleanup( input.AreOverlaysInPixelData() || input.UnusedBitsPresentInPixelData() );
95     DataElement out;
96     //bool r = codec.Code(input.GetDataElement(), out);
97     bool r = codec.Code(pixelde, out);
98 
99     if( !r )
100       {
101       return false;
102       }
103     DataElement &de = output.GetDataElement();
104     de.SetValue( out.GetValue() );
105     UpdatePhotometricInterpretation( input, output );
106     return true;
107     }
108   return false;
109 }
110 
TryRLECodec(const DataElement & pixelde,Bitmap const & input,Bitmap & output)111 bool ImageChangeTransferSyntax::TryRLECodec(const DataElement &pixelde, Bitmap const &input, Bitmap &output)
112 {
113   unsigned long len = input.GetBufferLength(); (void)len;
114   //assert( len == pixelde.GetByteValue()->GetLength() );
115   const TransferSyntax &ts = GetTransferSyntax();
116 
117   RLECodec codec;
118   if( codec.CanCode( ts ) )
119     {
120     codec.SetDimensions( input.GetDimensions() );
121     codec.SetPlanarConfiguration( input.GetPlanarConfiguration() );
122     codec.SetPhotometricInterpretation( input.GetPhotometricInterpretation() );
123     codec.SetPixelFormat( input.GetPixelFormat() );
124     codec.SetNeedOverlayCleanup( input.AreOverlaysInPixelData() || input.UnusedBitsPresentInPixelData() );
125     DataElement out;
126     //bool r = codec.Code(input.GetDataElement(), out);
127     bool r = codec.Code(pixelde, out);
128 
129     if( !r )
130       {
131       return false;
132       }
133     DataElement &de = output.GetDataElement();
134     de.SetValue( out.GetValue() );
135     UpdatePhotometricInterpretation( input, output );
136     if( input.GetPixelFormat().GetSamplesPerPixel() == 3 )
137     {
138       if( input.GetPlanarConfiguration() == 0 )
139       {
140         // http://dicom.nema.org/medical/dicom/current/output/chtml/part05/sect_G.2.html
141         // The use of separate segments implies that the Planar Configuration (0028,0006) will always be 1 for RLE compressed images.
142         output.SetPlanarConfiguration(1);
143       }
144     }
145     return true;
146     }
147   return false;
148 }
149 
TryJPEGCodec(const DataElement & pixelde,Bitmap const & input,Bitmap & output)150 bool ImageChangeTransferSyntax::TryJPEGCodec(const DataElement &pixelde, Bitmap const &input, Bitmap &output)
151 {
152   unsigned long len = input.GetBufferLength(); (void)len;
153   //assert( len == pixelde.GetByteValue()->GetLength() );
154   const TransferSyntax &ts = GetTransferSyntax();
155 
156   JPEGCodec jpgcodec;
157   // pass lossy/lossless flag:
158   // JPEGCodec are easier to deal with since there is no dual transfer syntax
159   // that can be both lossy and lossless:
160   if( ts.IsLossy() )
161     {
162     //assert( !ts.IsLossless() ); // I cannot do since since Try* functions are called with all TS, I could be receiving a JPEGLS TS...
163     jpgcodec.SetLossless( false );
164     }
165 
166   ImageCodec *codec = &jpgcodec;
167   JPEGCodec *usercodec = dynamic_cast<JPEGCodec*>(UserCodec);
168   if( usercodec && usercodec->CanCode( ts ) )
169     {
170     codec = usercodec;
171     }
172 
173   if( codec->CanCode( ts ) )
174     {
175     codec->SetDimensions( input.GetDimensions() );
176     // FIXME: GDCM always apply the planar configuration to 0...
177     //if( input.GetPlanarConfiguration() )
178     //  {
179     //  output.SetPlanarConfiguration( 0 );
180     //  }
181     codec->SetPlanarConfiguration( input.GetPlanarConfiguration() );
182     codec->SetPhotometricInterpretation( input.GetPhotometricInterpretation() );
183     codec->SetPixelFormat( input.GetPixelFormat() );
184     codec->SetNeedOverlayCleanup( input.AreOverlaysInPixelData() || input.UnusedBitsPresentInPixelData() );
185     // let's check we are not trying to compress 16bits with JPEG/Lossy/8bits
186     if( !input.GetPixelFormat().IsCompatible( ts ) )
187       {
188       gdcmErrorMacro("Pixel Format incompatible with TS" );
189       return false;
190       }
191     DataElement out;
192     //bool r = codec.Code(input.GetDataElement(), out);
193     bool r = codec->Code(pixelde, out);
194     // FIXME: this is not the best place to change the Output image internal type,
195     // but since I know IJG is always applying the Planar Configuration, it does make
196     // any sense to EVER produce a JPEG image where the Planar Configuration would be one
197     // so let's be nice and actually sync JPEG configuration with DICOM Planar Conf.
198     output.SetPlanarConfiguration( 0 );
199     //output.SetPhotometricInterpretation( PhotometricInterpretation::RGB );
200 
201     // Indeed one cannot produce a true lossless RGB image according to DICOM standard
202     // when doing lossless jpeg:
203     if( output.GetPhotometricInterpretation() == PhotometricInterpretation::RGB )
204       {
205       gdcmWarningMacro( "Technically this is not defined in the standard. \n"
206         "Some validator may complains this image is invalid, but would be wrong.");
207       }
208 
209     // PHILIPS_Gyroscan-12-MONO2-Jpeg_Lossless.dcm
210     if( !r )
211       {
212       return false;
213       }
214     DataElement &de = output.GetDataElement();
215     de.SetValue( out.GetValue() );
216     UpdatePhotometricInterpretation( input, output );
217     // When compressing with JPEG I think planar should always be:
218     //output.SetPlanarConfiguration(0);
219     // FIXME ! This should be done all the time for all codec:
220     // Did PI change or not ?
221     if ( !output.GetPhotometricInterpretation().IsSameColorSpace( codec->GetPhotometricInterpretation() ) )
222       {
223       // HACK
224       //Image *i = (Image*)this;
225       //i->SetPhotometricInterpretation( codec.GetPhotometricInterpretation() );
226       assert(0);
227       }
228     return true;
229     }
230   return false;
231 }
232 
TryJPEGLSCodec(const DataElement & pixelde,Bitmap const & input,Bitmap & output)233 bool ImageChangeTransferSyntax::TryJPEGLSCodec(const DataElement &pixelde, Bitmap const &input, Bitmap &output)
234 {
235   unsigned long len = input.GetBufferLength(); (void)len;
236   //assert( len == pixelde.GetByteValue()->GetLength() );
237   const TransferSyntax &ts = GetTransferSyntax();
238 
239   JPEGLSCodec jlscodec;
240   ImageCodec *codec = &jlscodec;
241   JPEGLSCodec *usercodec = dynamic_cast<JPEGLSCodec*>(UserCodec);
242   if( usercodec && usercodec->CanCode( ts ) )
243     {
244     codec = usercodec;
245     }
246 
247   if( codec->CanCode( ts ) )
248     {
249     codec->SetDimensions( input.GetDimensions() );
250     codec->SetPixelFormat( input.GetPixelFormat() );
251     //codec.SetNumberOfDimensions( input.GetNumberOfDimensions() );
252     codec->SetPlanarConfiguration( input.GetPlanarConfiguration() );
253     codec->SetPhotometricInterpretation( input.GetPhotometricInterpretation() );
254     codec->SetNeedOverlayCleanup( input.AreOverlaysInPixelData() || input.UnusedBitsPresentInPixelData() );
255     DataElement out;
256     //bool r = codec.Code(input.GetDataElement(), out);
257     bool r;
258     if( input.AreOverlaysInPixelData() || input.UnusedBitsPresentInPixelData() )
259       {
260       ByteValue *bv = const_cast<ByteValue*>(pixelde.GetByteValue());
261       assert( bv );
262       gdcm::DataElement tmp;
263       tmp.SetByteValue( bv->GetPointer(), bv->GetLength());
264       bv = const_cast<ByteValue*>(tmp.GetByteValue());
265       r = codec->CleanupUnusedBits((char*)bv->GetVoidPointer(), bv->GetLength());
266       if(!r) return false;
267       r = codec->Code(tmp, out);
268       }
269     else
270       {
271       r = codec->Code(pixelde, out);
272       }
273     if(!r) return false;
274 
275     DataElement &de = output.GetDataElement();
276     de.SetValue( out.GetValue() );
277     UpdatePhotometricInterpretation( input, output );
278     if( input.GetPixelFormat().GetSamplesPerPixel() == 3 )
279     {
280       if( input.GetPlanarConfiguration() == 0 )
281       {
282         // http://dicom.nema.org/medical/dicom/current/output/chtml/part05/sect_8.2.3.html#table_8.2.3-1
283         output.SetPlanarConfiguration(1);
284       }
285     }
286 
287     return r;
288     }
289   return false;
290 }
291 
TryJPEG2000Codec(const DataElement & pixelde,Bitmap const & input,Bitmap & output)292 bool ImageChangeTransferSyntax::TryJPEG2000Codec(const DataElement &pixelde, Bitmap const &input, Bitmap &output)
293 {
294   unsigned long len = input.GetBufferLength(); (void)len;
295   //assert( len == pixelde.GetByteValue()->GetLength() );
296   const TransferSyntax &ts = GetTransferSyntax();
297 
298   JPEG2000Codec j2kcodec;
299   ImageCodec *codec = &j2kcodec;
300   JPEG2000Codec *usercodec = dynamic_cast<JPEG2000Codec*>(UserCodec);
301   if( usercodec && usercodec->CanCode( ts ) )
302     {
303     codec = usercodec;
304     }
305 
306   if( codec->CanCode( ts ) )
307     {
308     codec->SetDimensions( input.GetDimensions() );
309     codec->SetPixelFormat( input.GetPixelFormat() );
310     codec->SetNumberOfDimensions( input.GetNumberOfDimensions() );
311     codec->SetPlanarConfiguration( input.GetPlanarConfiguration() );
312     codec->SetPhotometricInterpretation( input.GetPhotometricInterpretation() );
313     codec->SetNeedOverlayCleanup( input.AreOverlaysInPixelData() || input.UnusedBitsPresentInPixelData() );
314     DataElement out;
315     //bool r = codec.Code(input.GetDataElement(), out);
316     bool r = codec->Code(pixelde, out);
317 
318     // The value of Planar Configuration (0028,0006) is irrelevant since the
319     // manner of encoding components is specified in the JPEG 2000 standard,
320     // hence it shall be set to 0.
321     output.SetPlanarConfiguration( 0 );
322 
323     if( input.GetPixelFormat().GetSamplesPerPixel() == 3 )
324       {
325       if( input.GetPhotometricInterpretation().IsSameColorSpace( PhotometricInterpretation::RGB ) )
326         {
327         if( ts == TransferSyntax::JPEG2000Lossless )
328           {
329           output.SetPhotometricInterpretation( PhotometricInterpretation::YBR_RCT );
330           }
331         else
332           {
333           assert( ts == TransferSyntax::JPEG2000 );
334           output.SetPhotometricInterpretation( PhotometricInterpretation::YBR_ICT );
335           }
336         }
337       else
338         {
339         assert( input.GetPhotometricInterpretation().IsSameColorSpace( PhotometricInterpretation::YBR_FULL ) );
340         if( ts == TransferSyntax::JPEG2000Lossless )
341           {
342           output.SetPhotometricInterpretation( PhotometricInterpretation::YBR_FULL );
343           // Indeed one cannot produce a true lossless RGB image according to DICOM standard
344           gdcmWarningMacro( "Technically this is not defined in the standard. \n"
345             "Some validator may complains this image is invalid, but would be wrong.");
346           }
347         else
348           {
349           assert( ts == TransferSyntax::JPEG2000 );
350           //output.SetPhotometricInterpretation( PhotometricInterpretation::YBR_ICT );
351           // FIXME: technically when doing lossy we could be standard compliant and first convert to
352           // RGB THEN compress to YBR_ICT. For now produce improper j2k image
353           output.SetPhotometricInterpretation( PhotometricInterpretation::YBR_FULL );
354           }
355         }
356       }
357     else
358       {
359       assert( input.GetPixelFormat().GetSamplesPerPixel() == 1 );
360       }
361 
362     if( !r ) return false;
363     DataElement &de = output.GetDataElement();
364     de.SetValue( out.GetValue() );
365     UpdatePhotometricInterpretation( input, output );
366     return r;
367     }
368   return false;
369 }
370 
Change()371 bool ImageChangeTransferSyntax::Change()
372 {
373   if( TS == TransferSyntax::TS_END )
374     {
375     if( !Force ) return false;
376     // When force option is set but no specific TransferSyntax has been set, only inspect the
377     // encapsulated stream...
378     // See ImageReader::Read
379     if( Input->GetTransferSyntax().IsEncapsulated() && Input->GetTransferSyntax() != TransferSyntax::RLELossless )
380       {
381       Output = Input;
382       return true;
383       }
384     return false;
385     }
386   // let's get rid of some easy case:
387   if( Input->GetPhotometricInterpretation() == PhotometricInterpretation::PALETTE_COLOR &&
388     TS.IsLossy() )
389     {
390     gdcmErrorMacro( "PALETTE_COLOR and Lossy compression are impossible. Convert to RGB first." );
391     return false;
392     }
393 
394   Output = Input;
395 //  if( TS.IsLossy() && !TS.IsLossless() )
396 //    Output->SetLossyFlag( true );
397 
398   // Fast path
399   if( Input->GetTransferSyntax() == TS && !Force ) return true;
400 
401   // FIXME
402   // For now only support raw input, otherwise we would need to first decompress them
403   if( (Input->GetTransferSyntax() != TransferSyntax::ImplicitVRLittleEndian
404     && Input->GetTransferSyntax() != TransferSyntax::ExplicitVRLittleEndian
405     && Input->GetTransferSyntax() != TransferSyntax::ExplicitVRBigEndian)
406     // YBR_FULL_422 / raw needs to be decompressed:
407     || ( (Input->GetTransferSyntax() == TransferSyntax::ImplicitVRLittleEndian
408        || Input->GetTransferSyntax() == TransferSyntax::ExplicitVRLittleEndian
409        || Input->GetTransferSyntax() == TransferSyntax::ExplicitVRBigEndian)
410        && Input->GetPhotometricInterpretation() == PhotometricInterpretation::YBR_FULL_422 )
411     || Force )
412     {
413     // In memory decompression:
414     DataElement pixeldata( Tag(0x7fe0,0x0010) );
415     ByteValue *bv0 = new ByteValue();
416     uint32_t len0 = (uint32_t)Input->GetBufferLength();
417     bv0->SetLength( len0 );
418     bool b = Input->GetBuffer( (char*)bv0->GetVoidPointer() );
419     if( !b )
420       {
421       gdcmErrorMacro( "Error in getting buffer from input image." );
422       return false;
423       }
424     pixeldata.SetValue( *bv0 );
425 
426     bool success = false;
427     if( !success ) success = TryRAWCodec(pixeldata, *Input, *Output);
428     if( !success ) success = TryJPEGCodec(pixeldata, *Input, *Output);
429     if( !success ) success = TryJPEGLSCodec(pixeldata, *Input, *Output);
430     if( !success ) success = TryJPEG2000Codec(pixeldata, *Input, *Output);
431     if( !success ) success = TryRLECodec(pixeldata, *Input, *Output);
432     Output->SetTransferSyntax( TS );
433     if( !success )
434       {
435       //assert(0);
436       return false;
437       }
438 
439     // same goes for icon
440     DataElement iconpixeldata( Tag(0x7fe0,0x0010) );
441     Bitmap &bitmap = *Input;
442     if( Pixmap *pixmap = dynamic_cast<Pixmap*>( &bitmap ) )
443       {
444       Bitmap &outbitmap = *Output;
445       Pixmap *outpixmap = dynamic_cast<Pixmap*>( &outbitmap );
446       assert( outpixmap != nullptr );
447       if( !pixmap->GetIconImage().IsEmpty() )
448         {
449         // same goes for icon
450         ByteValue *bv = new ByteValue();
451         uint32_t len = (uint32_t)pixmap->GetIconImage().GetBufferLength();
452         bv->SetLength( len );
453         bool bb = pixmap->GetIconImage().GetBuffer( (char*)bv->GetVoidPointer() );
454         if( !bb )
455           {
456           return false;
457           }
458         iconpixeldata.SetValue( *bv );
459 
460         success = false;
461         if( !success ) success = TryRAWCodec(iconpixeldata, pixmap->GetIconImage(), outpixmap->GetIconImage());
462         if( !success ) success = TryJPEGCodec(iconpixeldata, pixmap->GetIconImage(), outpixmap->GetIconImage());
463         if( !success ) success = TryJPEGLSCodec(iconpixeldata, pixmap->GetIconImage(), outpixmap->GetIconImage());
464         if( !success ) success = TryJPEG2000Codec(iconpixeldata, pixmap->GetIconImage(), outpixmap->GetIconImage());
465         if( !success ) success = TryRLECodec(iconpixeldata, pixmap->GetIconImage(), outpixmap->GetIconImage());
466         outpixmap->GetIconImage().SetTransferSyntax( TS );
467         if( !success )
468           {
469           //assert(0);
470           return false;
471           }
472         assert( outpixmap->GetIconImage().GetTransferSyntax() == TS );
473         }
474       }
475 
476     //Output->ComputeLossyFlag();
477     assert( Output->GetTransferSyntax() == TS );
478     //if( TS.IsLossy() ) assert( Output->IsLossy() );
479     return success;
480     }
481 
482   // too bad we actually have to do some work...
483   bool success = false;
484   if( !success ) success = TryRAWCodec(Input->GetDataElement(), *Input, *Output);
485   if( !success ) success = TryJPEGCodec(Input->GetDataElement(), *Input, *Output);
486   if( !success ) success = TryJPEG2000Codec(Input->GetDataElement(), *Input, *Output);
487   if( !success ) success = TryJPEGLSCodec(Input->GetDataElement(), *Input, *Output);
488   if( !success ) success = TryRLECodec(Input->GetDataElement(), *Input, *Output);
489   Output->SetTransferSyntax( TS );
490   if( !success )
491     {
492     //assert(0);
493     return false;
494     }
495 
496   Bitmap &bitmap = *Input;
497   if( Pixmap *pixmap = dynamic_cast<Pixmap*>( &bitmap ) )
498     {
499     if( !pixmap->GetIconImage().IsEmpty() && CompressIconImage )
500       {
501       Bitmap &outbitmap = *Output;
502       Pixmap *outpixmap = dynamic_cast<Pixmap*>( &outbitmap );
503 
504       // same goes for icon
505       success = false;
506       if( !success ) success = TryRAWCodec(pixmap->GetIconImage().GetDataElement(), pixmap->GetIconImage(), outpixmap->GetIconImage());
507       if( !success ) success = TryJPEGCodec(pixmap->GetIconImage().GetDataElement(), pixmap->GetIconImage(), outpixmap->GetIconImage());
508       if( !success ) success = TryJPEGLSCodec(pixmap->GetIconImage().GetDataElement(), pixmap->GetIconImage(), outpixmap->GetIconImage());
509       if( !success ) success = TryJPEG2000Codec(pixmap->GetIconImage().GetDataElement(), pixmap->GetIconImage(), outpixmap->GetIconImage());
510       if( !success ) success = TryRLECodec(pixmap->GetIconImage().GetDataElement(), pixmap->GetIconImage(), outpixmap->GetIconImage());
511       outpixmap->GetIconImage().SetTransferSyntax( TS );
512       if( !success )
513         {
514         //assert(0);
515         return false;
516         }
517       assert( outpixmap->GetIconImage().GetTransferSyntax() == TS );
518       }
519     }
520 
521   //Output->ComputeLossyFlag();
522 
523   assert( Output->GetTransferSyntax() == TS );
524   return success;
525 }
526 
527 
528 } // end namespace gdcm
529