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