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 "gdcmIconImageFilter.h"
15 #include "gdcmIconImage.h"
16 #include "gdcmAttribute.h"
17 #include "gdcmPrivateTag.h"
18 #include "gdcmJPEGCodec.h"
19 
20 namespace gdcm
21 {
22 
23 class IconImageFilterInternals
24 {
25 public:
26   std::vector < SmartPointer< IconImage > > icons;
27 };
28 
IconImageFilter()29 IconImageFilter::IconImageFilter():F(new File),Internals(new IconImageFilterInternals)
30 {
31 }
32 
~IconImageFilter()33 IconImageFilter::~IconImageFilter()
34 {
35   delete Internals;
36 }
37 
ExtractIconImages()38 void IconImageFilter::ExtractIconImages()
39 {
40   const DataSet &rootds = F->GetDataSet();
41   const FileMetaInformation &header = F->GetHeader();
42   const TransferSyntax &ts = header.GetDataSetTransferSyntax();
43 
44   // PICKER-16-MONO2-Nested_icon.dcm
45   const Tag ticonimage(0x0088,0x0200);
46 
47   // Public Icon
48   if( rootds.FindDataElement( ticonimage ) )
49     {
50     const DataElement &iconimagesq = rootds.GetDataElement( ticonimage );
51     SmartPointer<SequenceOfItems> sq = iconimagesq.GetValueAsSQ();
52     // Is SQ empty ?
53     if( sq && sq->GetNumberOfItems() == 1 )
54       {
55       gdcmAssertAlwaysMacro( sq->GetNumberOfItems() == 1 );
56 
57       SmartPointer< IconImage > si1 = new IconImage;
58       IconImage &pixeldata = *si1;
59       SequenceOfItems::ConstIterator it = sq->Begin();
60       const DataSet &ds = it->GetNestedDataSet();
61 
62         {
63         Attribute<0x0028,0x0011> at = { 0 };
64         at.SetFromDataSet( ds );
65         pixeldata.SetDimension(0, at.GetValue() );
66         }
67 
68         {
69         Attribute<0x0028,0x0010> at = { 0 };
70         at.SetFromDataSet( ds );
71         pixeldata.SetDimension(1, at.GetValue() );
72         }
73 
74       PixelFormat pf;
75         {
76         Attribute<0x0028,0x0100> at = { 0 };
77         at.SetFromDataSet( ds );
78         pf.SetBitsAllocated( at.GetValue() );
79         }
80         {
81         Attribute<0x0028,0x0101> at = { 0 };
82         at.SetFromDataSet( ds );
83         pf.SetBitsStored( at.GetValue() );
84         }
85         {
86         Attribute<0x0028,0x0102> at = { 0 };
87         at.SetFromDataSet( ds );
88         pf.SetHighBit( at.GetValue() );
89         }
90         {
91         Attribute<0x0028,0x0103> at = { 0 };
92         at.SetFromDataSet( ds );
93         pf.SetPixelRepresentation( at.GetValue() );
94         }
95         {
96         Attribute<0x0028,0x0002> at = { 1 };
97         at.SetFromDataSet( ds );
98         pf.SetSamplesPerPixel( at.GetValue() );
99         }
100       pixeldata.SetPixelFormat( pf );
101       // D 0028|0004 [CS] [Photometric Interpretation] [MONOCHROME2 ]
102       const Tag tphotometricinterpretation(0x0028, 0x0004);
103       assert( ds.FindDataElement( tphotometricinterpretation ) );
104       const ByteValue *photometricinterpretation =
105         ds.GetDataElement( tphotometricinterpretation ).GetByteValue();
106       std::string photometricinterpretation_str(
107         photometricinterpretation->GetPointer(),
108         photometricinterpretation->GetLength() );
109       PhotometricInterpretation pi(
110         PhotometricInterpretation::GetPIType(
111           photometricinterpretation_str.c_str()));
112       assert( pi != PhotometricInterpretation::UNKNOWN);
113       pixeldata.SetPhotometricInterpretation( pi );
114 
115       //
116       if ( pi == PhotometricInterpretation::PALETTE_COLOR )
117         {
118         SmartPointer<LookupTable> lut = new LookupTable;
119         const Tag testseglut(0x0028, (0x1221 + 0));
120         if( ds.FindDataElement( testseglut ) )
121           {
122           gdcmAssertAlwaysMacro(0 && "Please report this image");
123           }
124         lut->Allocate( pf.GetBitsAllocated() );
125 
126         // for each red, green, blue:
127         for(int i=0; i<3; ++i)
128           {
129           // (0028,1101) US 0\0\16
130           // (0028,1102) US 0\0\16
131           // (0028,1103) US 0\0\16
132           const Tag tdescriptor(0x0028, (uint16_t)(0x1101 + i));
133           //const Tag tdescriptor(0x0028, 0x3002);
134           Element<VR::US,VM::VM3> el_us3;
135           // Now pass the byte array to a DICOMizer:
136           el_us3.SetFromDataElement( ds[tdescriptor] ); //.GetValue() );
137           lut->InitializeLUT( LookupTable::LookupTableType(i),
138             el_us3[0], el_us3[1], el_us3[2] );
139 
140           // (0028,1201) OW
141           // (0028,1202) OW
142           // (0028,1203) OW
143           const Tag tlut(0x0028, (uint16_t)(0x1201 + i));
144           //const Tag tlut(0x0028, 0x3006);
145 
146           // Segmented LUT
147           // (0028,1221) OW
148           // (0028,1222) OW
149           // (0028,1223) OW
150           const Tag seglut(0x0028, (uint16_t)(0x1221 + i));
151           if( ds.FindDataElement( tlut ) )
152             {
153             const ByteValue *lut_raw = ds.GetDataElement( tlut ).GetByteValue();
154             assert( lut_raw );
155             // LookupTableType::RED == 0
156             lut->SetLUT( LookupTable::LookupTableType(i),
157               (const unsigned char*)lut_raw->GetPointer(), lut_raw->GetLength() );
158             //assert( pf.GetBitsAllocated() == el_us3.GetValue(2) );
159 
160             //unsigned long check =
161             //  (el_us3.GetValue(0) ? el_us3.GetValue(0) : 65536)
162             //  * el_us3.GetValue(2) / 8;
163             //assert( check == lut_raw->GetLength() ); (void)check;
164             }
165           else if( ds.FindDataElement( seglut ) )
166             {
167             const ByteValue *lut_raw = ds.GetDataElement( seglut ).GetByteValue();
168             assert( lut_raw );
169             lut->SetLUT( LookupTable::LookupTableType(i),
170               (const unsigned char*)lut_raw->GetPointer(), lut_raw->GetLength() );
171             //assert( pf.GetBitsAllocated() == el_us3.GetValue(2) );
172 
173             //unsigned long check =
174             //  (el_us3.GetValue(0) ? el_us3.GetValue(0) : 65536)
175             //  * el_us3.GetValue(2) / 8;
176             //assert( check == lut_raw->GetLength() ); (void)check;
177             }
178           else
179             {
180             gdcmWarningMacro( "Icon Sequence is incomplete. Giving up" );
181             pixeldata.Clear();
182             return;
183             }
184           }
185         pixeldata.SetLUT(*lut);
186         }
187 
188       const Tag tpixeldata = Tag(0x7fe0, 0x0010);
189       if( !ds.FindDataElement( tpixeldata ) )
190         {
191         gdcmWarningMacro( "Icon Sequence is incomplete. Giving up" );
192         pixeldata.Clear();
193         return;
194         }
195       const DataElement& de = ds.GetDataElement( tpixeldata );
196       pixeldata.SetDataElement( de );
197 
198       // Pass TransferSyntax:
199       pixeldata.SetTransferSyntax( ts );
200       Internals->icons.emplace_back(pixeldata );
201       }
202     }
203 
204   // AMIInvalidPrivateDefinedLengthSQasUN.dcm
205   // GE_CT_With_Private_compressed-icon.dcm
206   // MR_GE_with_Private_Compressed_Icon_0009_1110.dcm
207   // FIXME:
208   // Not all tags from the private sequence can be handled
209   // For instance an icon has a window center/width ...
210   const PrivateTag tgeiconimage(0x0009,0x0010,"GEIIS");
211 
212   // Private Icon
213   if( rootds.FindDataElement( tgeiconimage ) )
214     {
215     const DataElement &iconimagesq = rootds.GetDataElement( tgeiconimage );
216     //const SequenceOfItems* sq = iconimagesq.GetSequenceOfItems();
217     SmartPointer<SequenceOfItems> sq = iconimagesq.GetValueAsSQ();
218     // Is SQ empty ?
219     assert( sq );
220     if( !sq ) return;
221     SmartPointer< IconImage > si1 = new IconImage;
222     IconImage &pixeldata = *si1;
223 
224     SequenceOfItems::ConstIterator it = sq->Begin();
225     const DataSet &ds = it->GetNestedDataSet();
226 
227     // D 0028|0011 [US] [Columns] [512]
228       {
229       //const DataElement& de = ds.GetDataElement( Tag(0x0028, 0x0011) );
230       Attribute<0x0028,0x0011> at = { 0 };
231       at.SetFromDataSet( ds );
232       pixeldata.SetDimension(0, at.GetValue() );
233       }
234 
235     // D 0028|0010 [US] [Rows] [512]
236       {
237       //const DataElement& de = ds.GetDataElement( Tag(0x0028, 0x0010) );
238       Attribute<0x0028,0x0010> at = { 0 };
239       at.SetFromDataSet( ds );
240       pixeldata.SetDimension(1, at.GetValue() );
241       }
242 
243     PixelFormat pf1;
244     // D 0028|0100 [US] [Bits Allocated] [16]
245       {
246       //const DataElement& de = ds.GetDataElement( Tag(0x0028, 0x0100) );
247       Attribute<0x0028,0x0100> at = { 0 };
248       at.SetFromDataSet( ds );
249       pf1.SetBitsAllocated( at.GetValue() );
250       }
251     // D 0028|0101 [US] [Bits Stored] [12]
252       {
253       //const DataElement& de = ds.GetDataElement( Tag(0x0028, 0x0101) );
254       Attribute<0x0028,0x0101> at = { 0 };
255       at.SetFromDataSet( ds );
256       pf1.SetBitsStored( at.GetValue() );
257       }
258     // D 0028|0102 [US] [High Bit] [11]
259       {
260       //const DataElement& de = ds.GetDataElement( Tag(0x0028, 0x0102) );
261       Attribute<0x0028,0x0102> at = { 0 };
262       at.SetFromDataSet( ds );
263       pf1.SetHighBit( at.GetValue() );
264       }
265     // D 0028|0103 [US] [Pixel Representation] [0]
266       {
267       //const DataElement& de = ds.GetDataElement( Tag(0x0028, 0x0103) );
268       Attribute<0x0028,0x0103> at = { 0 };
269       at.SetFromDataSet( ds );
270       pf1.SetPixelRepresentation( at.GetValue() );
271       }
272     // (0028,0002) US 1                                        #   2, 1 SamplesPerPixel
273       {
274       //const DataElement& de = ds.GetDataElement( Tag(0x0028, 0x0002) );
275       Attribute<0x0028,0x0002> at = { 1 };
276       at.SetFromDataSet( ds );
277       pf1.SetSamplesPerPixel( at.GetValue() );
278       }
279     pixeldata.SetPixelFormat( pf1 );
280     // D 0028|0004 [CS] [Photometric Interpretation] [MONOCHROME2 ]
281     const Tag tphotometricinterpretation(0x0028, 0x0004);
282     assert( ds.FindDataElement( tphotometricinterpretation ) );
283     const ByteValue *photometricinterpretation = ds.GetDataElement( tphotometricinterpretation ).GetByteValue();
284     std::string photometricinterpretation_str(
285       photometricinterpretation->GetPointer(),
286       photometricinterpretation->GetLength() );
287     PhotometricInterpretation pi(
288       PhotometricInterpretation::GetPIType(
289         photometricinterpretation_str.c_str()));
290     assert( pi != PhotometricInterpretation::UNKNOWN);
291     pixeldata.SetPhotometricInterpretation( pi );
292     const Tag tpixeldata = Tag(0x7fe0, 0x0010);
293     assert( ds.FindDataElement( tpixeldata ) );
294       {
295       const DataElement& de = ds.GetDataElement( tpixeldata );
296 #if 0
297       JPEGCodec jpeg;
298       jpeg.SetPhotometricInterpretation( pixeldata.GetPhotometricInterpretation() );
299       jpeg.SetPlanarConfiguration( 0 );
300       PixelFormat pf = pixeldata.GetPixelFormat();
301       // Apparently bits stored can only be 8 or 12:
302       if( pf.GetBitsStored() == 16 )
303         {
304         pf.SetBitsStored( 12 );
305         }
306       jpeg.SetPixelFormat( pf );
307       DataElement de2;
308       jpeg.Decode( de, de2);
309       pixeldata.SetDataElement( de2 );
310 #endif
311 #if 0
312       JPEGCodec jpeg;
313       jpeg.SetPhotometricInterpretation( pixeldata.GetPhotometricInterpretation() );
314       jpeg.SetPixelFormat( pixeldata.GetPixelFormat() );
315       DataElement de2;
316       jpeg.Decode( de, de2);
317       PixelFormat &pf2 = jpeg.GetPixelFormat();
318 #endif
319 #if 1
320       std::istringstream is;
321       const ByteValue *bv = de.GetByteValue();
322       assert( bv );
323       is.str( std::string( bv->GetPointer(), bv->GetLength() ) );
324       TransferSyntax jpegts;
325       JPEGCodec jpeg;
326       jpeg.SetPixelFormat( pf1 ); // important to initialize
327       bool b = jpeg.GetHeaderInfo( is, jpegts );
328       if( !b )
329         {
330         assert( 0 );
331         }
332       //jpeg.GetPixelFormat().Print (std::cout);
333       pixeldata.SetPixelFormat( jpeg.GetPixelFormat() );
334       // Set appropriate transfer Syntax
335       pixeldata.SetTransferSyntax( jpegts );
336 #endif
337       pixeldata.SetDataElement( de );
338       }
339     Internals->icons.emplace_back(pixeldata );
340     }
341 
342   // AFAIK this icon SQ is undocumented , but I found it in:
343   // gdcmDataExtra/gdcmBreakers/2929J888_8b_YBR_RLE_PlanConf0_breaker.dcm
344   // aka 'SmallPreview'
345   // The SQ contains a DataElement:
346   // (0002,0010) UI [1.2.840.10008.1.2.1]                          # 20,1 Transfer Syntax UID
347   // sigh...
348   const PrivateTag tgeiconimage2(0x6003,0x0010,"GEMS_Ultrasound_ImageGroup_001");
349 
350   if( rootds.FindDataElement( tgeiconimage2 ) )
351     {
352     const DataElement &iconimagesq = rootds.GetDataElement( tgeiconimage2 );
353     //const SequenceOfItems* sq = iconimagesq.GetSequenceOfItems();
354     SmartPointer<SequenceOfItems> sq = iconimagesq.GetValueAsSQ();
355     // Is SQ empty ?
356     assert( sq );
357     if( !sq ) return;
358 
359     SmartPointer< IconImage > si1 = new IconImage;
360     IconImage &pixeldata = *si1;
361 
362     SequenceOfItems::ConstIterator it = sq->Begin();
363     const DataSet &ds = it->GetNestedDataSet();
364 
365     // D 0028|0011 [US] [Columns] [512]
366       {
367       const DataElement& de = ds.GetDataElement( Tag(0x0028, 0x0011) );
368       Attribute<0x0028,0x0011> at;
369       at.SetFromDataElement( de );
370       pixeldata.SetDimension(0, at.GetValue() );
371       }
372 
373     // D 0028|0010 [US] [Rows] [512]
374       {
375       const DataElement& de = ds.GetDataElement( Tag(0x0028, 0x0010) );
376       Attribute<0x0028,0x0010> at;
377       at.SetFromDataElement( de );
378       pixeldata.SetDimension(1, at.GetValue() );
379       }
380 
381     PixelFormat pf;
382     // D 0028|0100 [US] [Bits Allocated] [16]
383       {
384       const DataElement& de = ds.GetDataElement( Tag(0x0028, 0x0100) );
385       Attribute<0x0028,0x0100> at;
386       at.SetFromDataElement( de );
387       pf.SetBitsAllocated( at.GetValue() );
388       }
389     // D 0028|0101 [US] [Bits Stored] [12]
390       {
391       const DataElement& de = ds.GetDataElement( Tag(0x0028, 0x0101) );
392       Attribute<0x0028,0x0101> at;
393       at.SetFromDataElement( de );
394       pf.SetBitsStored( at.GetValue() );
395       }
396     // D 0028|0102 [US] [High Bit] [11]
397       {
398       const DataElement& de = ds.GetDataElement( Tag(0x0028, 0x0102) );
399       Attribute<0x0028,0x0102> at;
400       at.SetFromDataElement( de );
401       pf.SetHighBit( at.GetValue() );
402       }
403     // D 0028|0103 [US] [Pixel Representation] [0]
404       {
405       const DataElement& de = ds.GetDataElement( Tag(0x0028, 0x0103) );
406       Attribute<0x0028,0x0103> at;
407       at.SetFromDataElement( de );
408       pf.SetPixelRepresentation( at.GetValue() );
409       }
410     // (0028,0002) US 1                                        #   2, 1 SamplesPerPixel
411       {
412       const DataElement& de = ds.GetDataElement( Tag(0x0028, 0x0002) );
413       Attribute<0x0028,0x0002> at;
414       at.SetFromDataElement( de );
415       pf.SetSamplesPerPixel( at.GetValue() );
416       }
417     pixeldata.SetPixelFormat( pf );
418     // D 0028|0004 [CS] [Photometric Interpretation] [MONOCHROME2 ]
419     const Tag tphotometricinterpretation(0x0028, 0x0004);
420     assert( ds.FindDataElement( tphotometricinterpretation ) );
421     const ByteValue *photometricinterpretation = ds.GetDataElement( tphotometricinterpretation ).GetByteValue();
422     std::string photometricinterpretation_str(
423       photometricinterpretation->GetPointer(),
424       photometricinterpretation->GetLength() );
425     PhotometricInterpretation pi(
426       PhotometricInterpretation::GetPIType(
427         photometricinterpretation_str.c_str()));
428     assert( pi != PhotometricInterpretation::UNKNOWN);
429     pixeldata.SetPhotometricInterpretation( pi );
430     //const Tag tpixeldata = Tag(0x7fe0, 0x0010);
431     const PrivateTag tpixeldata(0x6003,0x0011,"GEMS_Ultrasound_ImageGroup_001");
432     assert( ds.FindDataElement( tpixeldata ) );
433       {
434       const DataElement& de = ds.GetDataElement( tpixeldata );
435       pixeldata.SetDataElement( de );
436       /*
437       JPEGCodec jpeg;
438       jpeg.SetPhotometricInterpretation( pixeldata.GetPhotometricInterpretation() );
439       jpeg.SetPlanarConfiguration( 0 );
440       PixelFormat pf = pixeldata.GetPixelFormat();
441       // Apparently bits stored can only be 8 or 12:
442       if( pf.GetBitsStored() == 16 )
443       {
444       pf.SetBitsStored( 12 );
445       }
446       jpeg.SetPixelFormat( pf );
447       DataElement de2;
448       jpeg.Decode( de, de2);
449       pixeldata.SetDataElement( de2 );
450        */
451       }
452       {
453       Attribute<0x002,0x0010> at;
454       at.SetFromDataSet( ds );
455       TransferSyntax tstype = TransferSyntax::GetTSType( at.GetValue() );
456       pixeldata.SetTransferSyntax( tstype );
457       }
458     Internals->icons.emplace_back(pixeldata );
459     }
460 }
461 
462 /*
463 [ICONDATA2]
464 PrivateCreator = VEPRO VIM 5.0 DATA
465 Group = 0x0055
466 Element = 0x0030
467 Data.ID     = C|0|3
468 Data.Type    = C|3|1
469 Data.Width    = I|4|2
470 Data.Height    = I|6|2
471 */
472 namespace {
473 struct VeproData
474 {
475   char ID[3];
476   char Type;
477   uint16_t Width;
478   uint16_t Height;
479 };
480 }
481 
482 // documentation was found in : VIM/VIMSYS/dcmviewpriv.dat
ExtractVeproIconImages()483 void IconImageFilter::ExtractVeproIconImages()
484 {
485   const DataSet &rootds = F->GetDataSet();
486 
487   const PrivateTag ticon1(0x55,0x0030,"VEPRO VIF 3.0 DATA");
488   const PrivateTag ticon2(0x55,0x0030,"VEPRO VIM 5.0 DATA");
489 
490   const ByteValue * bv = nullptr;
491   // Prefer VIF over VIM ?
492   if( rootds.FindDataElement( ticon1 ) )
493     {
494     const DataElement &de = rootds.GetDataElement( ticon1 );
495     bv = de.GetByteValue();
496     }
497   else if( rootds.FindDataElement( ticon2 ) )
498     {
499     const DataElement &de = rootds.GetDataElement( ticon2 );
500     bv = de.GetByteValue();
501     }
502 
503   if( bv )
504     {
505     const char *buf = bv->GetPointer();
506     size_t len = bv->GetLength();
507     VeproData data;
508     memcpy(&data, buf, sizeof(data));
509 
510     const char *raw = buf + sizeof(data);
511 
512     size_t offset = 4;
513     // All header starts with the letter 'RAW\0', it looks like we need
514     // to skip them (all 4 of them)
515     int magic = memcmp( raw, "RAW\0", 4 );
516     gdcmAssertAlwaysMacro( magic == 0 );
517 
518     unsigned int dims[3] = {};
519     dims[0] = data.Width;
520     dims[1] = data.Height;
521 
522     assert( dims[0] * dims[1] == len - sizeof(data) - offset );
523 
524     DataElement pd;
525     pd.SetByteValue( raw + offset, (uint32_t)(len - sizeof(data) - offset) );
526 
527     SmartPointer< IconImage > si1 = new IconImage;
528     IconImage &pixeldata = *si1;
529     pixeldata.SetDataElement( pd );
530 
531     pixeldata.SetDimension(0, dims[0] );
532     pixeldata.SetDimension(1, dims[1] );
533 
534     PixelFormat pf = PixelFormat::UINT8;
535     pixeldata.SetPixelFormat( pf );
536     pixeldata.SetPhotometricInterpretation( PhotometricInterpretation::MONOCHROME2 );
537 
538     Internals->icons.emplace_back(pixeldata );
539     }
540 }
541 
Extract()542 bool IconImageFilter::Extract()
543 {
544   Internals->icons.clear();
545   ExtractIconImages();
546   ExtractVeproIconImages();
547   return GetNumberOfIconImages() != 0;
548 }
549 
GetNumberOfIconImages() const550 unsigned int IconImageFilter::GetNumberOfIconImages() const
551 {
552   // what is icons are in undefined length sequence ?
553   return (unsigned int)Internals->icons.size();
554 }
555 
GetIconImage(unsigned int i) const556 IconImage& IconImageFilter::GetIconImage( unsigned int i ) const
557 {
558   assert( i < Internals->icons.size() );
559   return *Internals->icons[i];
560 }
561 
562 } // end namespace gdcm
563