Medical Imaging Interaction Toolkit  2018.4.99-12ad79a3
Medical Imaging Interaction Toolkit
mitkDICOMImageBlockDescriptor.cpp
Go to the documentation of this file.
1 /*============================================================================
2 
3 The Medical Imaging Interaction Toolkit (MITK)
4 
5 Copyright (c) German Cancer Research Center (DKFZ)
6 All rights reserved.
7 
8 Use of this source code is governed by a 3-clause BSD license that can be
9 found in the LICENSE file.
10 
11 ============================================================================*/
12 
14 #include "mitkStringProperty.h"
16 #include <gdcmUIDs.h>
17 #include <vector>
18 
20 : m_ReaderImplementationLevel( SOPClassUnknown )
21 , m_PropertyList( PropertyList::New() )
22 , m_TagCache( nullptr )
23 , m_PropertiesOutOfDate( true )
24 {
25  m_PropertyFunctor = &mitk::DICOMImageBlockDescriptor::GetPropertyForDICOMValues;
26 }
27 
29 {
30 }
31 
33 : m_ImageFrameList( other.m_ImageFrameList )
34 , m_MitkImage( other.m_MitkImage )
35 , m_SliceIsLoaded( other.m_SliceIsLoaded )
36 , m_ReaderImplementationLevel( other.m_ReaderImplementationLevel )
37 , m_TiltInformation( other.m_TiltInformation )
38 , m_PropertyList( other.m_PropertyList->Clone() )
39 , m_TagCache( other.m_TagCache )
40 , m_PropertiesOutOfDate( other.m_PropertiesOutOfDate )
41 , m_AdditionalTagMap(other.m_AdditionalTagMap)
42 , m_FoundAdditionalTags(other.m_FoundAdditionalTags)
43 , m_PropertyFunctor(other.m_PropertyFunctor)
44 {
45  if ( m_MitkImage )
46  {
47  m_MitkImage = m_MitkImage->Clone();
48  }
49 
50  m_PropertyFunctor = &mitk::DICOMImageBlockDescriptor::GetPropertyForDICOMValues;
51 }
52 
55 {
56  if ( this != &other )
57  {
58  m_ImageFrameList = other.m_ImageFrameList;
59  m_MitkImage = other.m_MitkImage;
60  m_SliceIsLoaded = other.m_SliceIsLoaded;
61  m_ReaderImplementationLevel = other.m_ReaderImplementationLevel;
62  m_TiltInformation = other.m_TiltInformation;
63 
64  m_AdditionalTagMap = other.m_AdditionalTagMap;
65  m_FoundAdditionalTags = other.m_FoundAdditionalTags;
66  m_PropertyFunctor = other.m_PropertyFunctor;
67 
68  if ( other.m_PropertyList )
69  {
70  m_PropertyList = other.m_PropertyList->Clone();
71  }
72 
73  if ( other.m_MitkImage )
74  {
75  m_MitkImage = other.m_MitkImage->Clone();
76  }
77 
78  m_TagCache = other.m_TagCache;
79  m_PropertiesOutOfDate = other.m_PropertiesOutOfDate;
80  }
81  return *this;
82 }
83 
85 {
86  DICOMTagList completeList;
87 
88  completeList.push_back( DICOMTag( 0x0018, 0x1164 ) ); // pixel spacing
89  completeList.push_back( DICOMTag( 0x0028, 0x0030 ) ); // imager pixel spacing
90 
91  completeList.push_back( DICOMTag( 0x0008, 0x0018 ) ); // sop instance UID
92  completeList.push_back( DICOMTag( 0x0008, 0x0016 ) ); // sop class UID
93 
94  completeList.push_back( DICOMTag( 0x0020, 0x0011 ) ); // series number
95  completeList.push_back( DICOMTag( 0x0008, 0x1030 ) ); // study description
96  completeList.push_back( DICOMTag( 0x0008, 0x103e ) ); // series description
97  completeList.push_back( DICOMTag( 0x0008, 0x0060 ) ); // modality
98  completeList.push_back( DICOMTag( 0x0018, 0x0024 ) ); // sequence name
99  completeList.push_back( DICOMTag( 0x0020, 0x0037 ) ); // image orientation
100 
101  completeList.push_back( DICOMTag( 0x0020, 0x1041 ) ); // slice location
102  completeList.push_back( DICOMTag( 0x0020, 0x0012 ) ); // acquisition number
103  completeList.push_back( DICOMTag( 0x0020, 0x0013 ) ); // instance number
104  completeList.push_back( DICOMTag( 0x0020, 0x0032 ) ); // image position patient
105 
106  completeList.push_back( DICOMTag( 0x0028, 0x1050 ) ); // window center
107  completeList.push_back( DICOMTag( 0x0028, 0x1051 ) ); // window width
108  completeList.push_back( DICOMTag( 0x0008, 0x0008 ) ); // image type
109  completeList.push_back( DICOMTag( 0x0028, 0x0004 ) ); // photometric interpretation
110 
111  return completeList;
112 }
113 
114 
116  const AdditionalTagsMapType& tagMap)
117 {
118  m_AdditionalTagMap = tagMap;
119 }
120 
121 
123 {
124  m_TiltInformation = info;
125 }
126 
128 {
129  return m_TiltInformation;
130 }
131 
133 {
134  m_ImageFrameList = framelist;
135  m_SliceIsLoaded.resize( framelist.size() );
136  m_SliceIsLoaded.assign( framelist.size(), false );
137 
138  m_PropertiesOutOfDate = true;
139 }
140 
142 {
143  return m_ImageFrameList;
144 }
145 
147 {
148  if ( m_MitkImage != image )
149  {
150  if ( m_TagCache.IsExpired() )
151  {
152  MITK_ERROR << "Unable to describe MITK image with properties without a tag-cache object!";
153  m_MitkImage = nullptr;
154  return;
155  }
156 
157  if ( m_ImageFrameList.empty() )
158  {
159  MITK_ERROR << "Unable to describe MITK image with properties without a frame list!";
160  m_MitkImage = nullptr;
161  return;
162  }
163 
164  // Should verify that the image matches m_ImageFrameList and m_TagCache
165  // however, this is hard to do without re-analyzing all
166  // TODO we should at least make sure that the number of frames is identical (plus rows/columns,
167  // orientation)
168  // without gantry tilt correction, we can also check image origin
169 
170  m_MitkImage = this->DescribeImageWithProperties( this->FixupSpacing( image ) );
171  }
172 }
173 
175 {
176  return m_MitkImage;
177 }
178 
179 mitk::Image::Pointer mitk::DICOMImageBlockDescriptor::FixupSpacing( Image* mitkImage )
180 {
181  if ( mitkImage )
182  {
183  Vector3D imageSpacing = mitkImage->GetGeometry()->GetSpacing();
184 
185  ScalarType desiredSpacingX = imageSpacing[0];
186  ScalarType desiredSpacingY = imageSpacing[1];
188  desiredSpacingX, desiredSpacingY ); // prefer pixel spacing over imager pixel spacing
189 
190  if ( desiredSpacingX <= 0 || desiredSpacingY <= 0 )
191  {
192  return mitkImage;
193  }
194 
195  MITK_DEBUG << "Loaded image with spacing " << imageSpacing[0] << ", " << imageSpacing[1];
196  MITK_DEBUG << "Found correct spacing info " << desiredSpacingX << ", " << desiredSpacingY;
197 
198  imageSpacing[0] = desiredSpacingX;
199  imageSpacing[1] = desiredSpacingY;
200  mitkImage->GetGeometry()->SetSpacing( imageSpacing );
201  }
202 
203  return mitkImage;
204 }
205 void mitk::DICOMImageBlockDescriptor::SetSliceIsLoaded( unsigned int index, bool isLoaded )
206 {
207  if ( index < m_SliceIsLoaded.size() )
208  {
209  m_SliceIsLoaded[index] = isLoaded;
210  }
211  else
212  {
213  std::stringstream ss;
214  ss << "Index " << index << " out of range (" << m_SliceIsLoaded.size() << " indices reserved)";
215  throw std::invalid_argument( ss.str() );
216  }
217 }
218 
219 bool mitk::DICOMImageBlockDescriptor::IsSliceLoaded( unsigned int index ) const
220 {
221  if ( index < m_SliceIsLoaded.size() )
222  {
223  return m_SliceIsLoaded[index];
224  }
225  else
226  {
227  std::stringstream ss;
228  ss << "Index " << index << " out of range (" << m_SliceIsLoaded.size() << " indices reserved)";
229  throw std::invalid_argument( ss.str() );
230  }
231 }
232 
233 bool mitk::DICOMImageBlockDescriptor::AllSlicesAreLoaded() const
234 {
235  bool allLoaded = true;
236  for ( auto iter = m_SliceIsLoaded.cbegin(); iter != m_SliceIsLoaded.cend(); ++iter )
237  {
238  allLoaded &= *iter;
239  }
240 
241  return allLoaded;
242 }
243 
244 /*
245  PS defined IPS defined PS==IPS
246  0 0 --> UNKNOWN spacing, loader will invent
247  0 1 --> spacing as at detector surface
248  1 0 --> spacing as in patient
249  1 1 0 --> detector surface spacing CORRECTED for geometrical
250  magnifications: spacing as in patient
251  1 1 1 --> detector surface spacing NOT corrected for geometrical
252  magnifications: spacing as at detector
253 */
255 {
256  if ( m_ImageFrameList.empty() || m_TagCache.IsExpired() )
257  {
258  MITK_ERROR << "Invalid call to GetPixelSpacingInterpretation. Need to have initialized tag-cache!";
259  return SpacingUnknown;
260  }
261 
262  const std::string pixelSpacing = this->GetPixelSpacing();
263  const std::string imagerPixelSpacing = this->GetImagerPixelSpacing();
264 
265  if ( pixelSpacing.empty() )
266  {
267  if ( imagerPixelSpacing.empty() )
268  {
269  return SpacingUnknown;
270  }
271  else
272  {
273  return SpacingAtDetector;
274  }
275  }
276  else // Pixel Spacing defined
277  {
278  if ( imagerPixelSpacing.empty() )
279  {
280  return SpacingInPatient;
281  }
282  else if ( pixelSpacing != imagerPixelSpacing )
283  {
284  return SpacingInPatient;
285  }
286  else
287  {
288  return SpacingAtDetector;
289  }
290  }
291 }
292 
293 std::string mitk::DICOMImageBlockDescriptor::GetPixelSpacing() const
294 {
295  if ( m_ImageFrameList.empty() || m_TagCache.IsExpired() )
296  {
297  MITK_ERROR << "Invalid call to GetPixelSpacing. Need to have initialized tag-cache!";
298  return std::string( "" );
299  }
300 
301  static const DICOMTag tagPixelSpacing( 0x0028, 0x0030 );
302  return m_TagCache.Lock()->GetTagValue( m_ImageFrameList.front(), tagPixelSpacing ).value;
303 }
304 
305 std::string mitk::DICOMImageBlockDescriptor::GetImagerPixelSpacing() const
306 {
307  if ( m_ImageFrameList.empty() || m_TagCache.IsExpired() )
308  {
309  MITK_ERROR << "Invalid call to GetImagerPixelSpacing. Need to have initialized tag-cache!";
310  return std::string( "" );
311  }
312 
313  static const DICOMTag tagImagerPixelSpacing( 0x0018, 0x1164 );
314  return m_TagCache.Lock()->GetTagValue( m_ImageFrameList.front(), tagImagerPixelSpacing ).value;
315 }
316 
318  ScalarType& spacingY ) const
319 {
320  const std::string pixelSpacing = this->GetPixelSpacing();
321  // preference for "in patient" pixel spacing
322  if ( !DICOMStringToSpacing( pixelSpacing, spacingX, spacingY ) )
323  {
324  const std::string imagerPixelSpacing = this->GetImagerPixelSpacing();
325  // fallback to "on detector" spacing
326  if ( !DICOMStringToSpacing( imagerPixelSpacing, spacingX, spacingY ) )
327  {
328  // at this point we have no hints whether the spacing is correct
329  // do a quick sanity check and either trust in the input or set both to 1
330  // We assume neither spacing to be negative, zero or unexpectedly large for
331  // medical images
332  if (spacingX < mitk::eps || spacingX > 1000 || spacingY < mitk::eps || spacingY > 1000)
333  {
334  spacingX = spacingY = 1.0;
335  }
336  }
337  }
338 }
339 
340 void mitk::DICOMImageBlockDescriptor::SetProperty( const std::string& key, BaseProperty* value )
341 {
342  m_PropertyList->SetProperty( key, value );
343 }
344 
346 {
347  this->UpdateImageDescribingProperties();
348  return m_PropertyList->GetProperty( key );
349 }
350 
351 std::string mitk::DICOMImageBlockDescriptor::GetPropertyAsString( const std::string& key ) const
352 {
353  this->UpdateImageDescribingProperties();
354  const mitk::BaseProperty::Pointer property = m_PropertyList->GetProperty( key );
355  if ( property.IsNotNull() )
356  {
357  return property->GetValueAsString();
358  }
359  else
360  {
361  return std::string( "" );
362  }
363 }
364 
365 void mitk::DICOMImageBlockDescriptor::SetFlag( const std::string& key, bool value )
366 {
367  m_PropertyList->ReplaceProperty( key, BoolProperty::New( value ) );
368 }
369 
370 bool mitk::DICOMImageBlockDescriptor::GetFlag( const std::string& key, bool defaultValue ) const
371 {
372  this->UpdateImageDescribingProperties();
373  BoolProperty::ConstPointer boolProp = dynamic_cast<BoolProperty*>( this->GetProperty( key ) );
374  if ( boolProp.IsNotNull() )
375  {
376  return boolProp->GetValue();
377  }
378  else
379  {
380  return defaultValue;
381  }
382 }
383 
384 void mitk::DICOMImageBlockDescriptor::SetIntProperty( const std::string& key, int value )
385 {
386  m_PropertyList->ReplaceProperty( key, IntProperty::New( value ) );
387 }
388 
389 int mitk::DICOMImageBlockDescriptor::GetIntProperty( const std::string& key, int defaultValue ) const
390 {
391  this->UpdateImageDescribingProperties();
392  IntProperty::ConstPointer intProp = dynamic_cast<IntProperty*>( this->GetProperty( key ) );
393  if ( intProp.IsNotNull() )
394  {
395  return intProp->GetValue();
396  }
397  else
398  {
399  return defaultValue;
400  }
401 }
402 
403 double mitk::DICOMImageBlockDescriptor::stringtodouble( const std::string& str ) const
404 {
405  double d;
406  std::string trimmedstring( str );
407  try
408  {
409  trimmedstring = trimmedstring.erase( trimmedstring.find_last_not_of( " \n\r\t" ) + 1 );
410  }
411  catch ( ... )
412  {
413  // no last not of
414  }
415 
416  std::string firstcomponent( trimmedstring );
417  try
418  {
419  firstcomponent = trimmedstring.erase( trimmedstring.find_first_of( "\\" ) );
420  }
421  catch ( ... )
422  {
423  // no last not of
424  }
425 
426  std::istringstream converter( firstcomponent );
427  if ( !firstcomponent.empty() && ( converter >> d ) && converter.eof() )
428  {
429  return d;
430  }
431  else
432  {
433  throw std::invalid_argument( "Argument is not a convertable number" );
434  }
435 }
436 
437 mitk::Image::Pointer mitk::DICOMImageBlockDescriptor::DescribeImageWithProperties( Image* mitkImage )
438 {
439  // TODO: this is a collection of properties that have been provided by the
440  // legacy DicomSeriesReader.
441  // We should at some point clean up this collection and name them in a more
442  // consistent way!
443 
444  if ( !mitkImage )
445  return mitkImage;
446 
447  // first part: add some tags that describe individual slices
448  // these propeties are defined at analysis time (see UpdateImageDescribingProperties())
449 
450  const char* propertyKeySliceLocation = "dicom.image.0020.1041";
451  const char* propertyKeyInstanceNumber = "dicom.image.0020.0013";
452  const char* propertyKeySOPInstanceUID = "dicom.image.0008.0018";
453 
454  mitkImage->SetProperty( propertyKeySliceLocation, this->GetProperty( "sliceLocationForSlices" ) );
455  mitkImage->SetProperty( propertyKeyInstanceNumber, this->GetProperty( "instanceNumberForSlices" ) );
456  mitkImage->SetProperty( propertyKeySOPInstanceUID, this->GetProperty( "SOPInstanceUIDForSlices" ) );
457  mitkImage->SetProperty( "files", this->GetProperty( "filenamesForSlices" ) );
458 
459  // second part: add properties that describe the whole image block
460  mitkImage->SetProperty( "dicomseriesreader.SOPClassUID", StringProperty::New( this->GetSOPClassUID() ) );
461 
462  mitkImage->SetProperty( "dicomseriesreader.SOPClass", StringProperty::New( this->GetSOPClassUIDAsName() ) );
463 
464  mitkImage->SetProperty(
465  "dicomseriesreader.PixelSpacingInterpretationString",
467  mitkImage->SetProperty(
468  "dicomseriesreader.PixelSpacingInterpretation",
470 
471  mitkImage->SetProperty(
472  "dicomseriesreader.ReaderImplementationLevelString",
473  StringProperty::New( ReaderImplementationLevelToString( m_ReaderImplementationLevel ) ) );
474 
475  mitkImage->SetProperty( "dicomseriesreader.ReaderImplementationLevel",
476  GenericProperty<ReaderImplementationLevel>::New( m_ReaderImplementationLevel ) );
477 
478  mitkImage->SetProperty( "dicomseriesreader.GantyTiltCorrected",
479  BoolProperty::New( this->GetTiltInformation().IsRegularGantryTilt() ) );
480 
481  mitkImage->SetProperty( "dicomseriesreader.3D+t", BoolProperty::New( this->GetFlag( "3D+t", false ) ) );
482 
483  // level window
484  const std::string windowCenter = this->GetPropertyAsString( "windowCenter" );
485  const std::string windowWidth = this->GetPropertyAsString( "windowWidth" );
486  try
487  {
488  const double level = stringtodouble( windowCenter );
489  const double window = stringtodouble( windowWidth );
490  mitkImage->SetProperty( "levelwindow", LevelWindowProperty::New( LevelWindow( level, window ) ) );
491  }
492  catch ( ... )
493  {
494  // nothing, no levelwindow to be predicted...
495  }
496 
497  const std::string modality = this->GetPropertyAsString( "modality" );
498  mitkImage->SetProperty( "modality", StringProperty::New( modality ) );
499 
500  mitkImage->SetProperty( "dicom.pixel.PhotometricInterpretation",
501  this->GetProperty( "photometricInterpretation" ) );
502  mitkImage->SetProperty( "dicom.image.imagetype", this->GetProperty( "imagetype" ) );
503 
504  mitkImage->SetProperty( "dicom.study.StudyDescription", this->GetProperty( "studyDescription" ) );
505  mitkImage->SetProperty( "dicom.series.SeriesDescription", this->GetProperty( "seriesDescription" ) );
506 
507  mitkImage->SetProperty( "dicom.pixel.Rows", this->GetProperty( "rows" ) );
508  mitkImage->SetProperty( "dicom.pixel.Columns", this->GetProperty( "columns" ) );
509 
510  // third part: get all found additional tags of interest
511 
512  for (auto tag : m_FoundAdditionalTags)
513  {
514  BaseProperty* prop = this->GetProperty(tag);
515  if (prop)
516  {
517  mitkImage->SetProperty(tag.c_str(), prop);
518  }
519  }
520 
521  // fourth part: get something from ImageIO. BUT this needs to be created elsewhere. or not at all!
522 
523  return mitkImage;
524 }
525 
527 {
528  m_ReaderImplementationLevel = level;
529 }
530 
532 {
533  return m_ReaderImplementationLevel;
534 }
535 
537 {
538  if ( !m_ImageFrameList.empty() && !m_TagCache.IsExpired() )
539  {
540  static const DICOMTag tagSOPClassUID( 0x0008, 0x0016 );
541  return m_TagCache.Lock()->GetTagValue( m_ImageFrameList.front(), tagSOPClassUID ).value;
542  }
543  else
544  {
545  MITK_ERROR
546  << "Invalid call to DICOMImageBlockDescriptor::GetSOPClassUID(). Need to have initialized tag-cache!";
547  return std::string( "" );
548  }
549 }
550 
552 {
553  if ( !m_ImageFrameList.empty() && !m_TagCache.IsExpired() )
554  {
555  gdcm::UIDs uidKnowledge;
556  uidKnowledge.SetFromUID( this->GetSOPClassUID().c_str() );
557 
558  const char* name = uidKnowledge.GetName();
559  if ( name )
560  {
561  return std::string( name );
562  }
563  else
564  {
565  return std::string( "" );
566  }
567  }
568  else
569  {
570  MITK_ERROR << "Invalid call to DICOMImageBlockDescriptor::GetSOPClassUIDAsName(). Need to have "
571  "initialized tag-cache!";
572  return std::string( "" );
573  }
574 }
575 
577 {
578  int result = 1;
579  this->m_PropertyList->GetIntProperty("timesteps", result);
580 
581  return result;
582 };
583 
585 {
586  const int numberOfTimesteps = this->GetNumberOfTimeSteps();
587  int numberOfFramesPerTimestep = this->m_ImageFrameList.size() / numberOfTimesteps;
588  assert(int(double((double)this->m_ImageFrameList.size() / (double)numberOfTimesteps))
589  == numberOfFramesPerTimestep); // this should hold
590 
591  return numberOfFramesPerTimestep;
592 };
593 
594 
596 {
597  // this must only be used during loading and never afterwards
598  m_TagCache = privateCache;
599 }
600 
601 #define printPropertyRange( label, property_name ) \
602  \
603 { \
604  const std::string first = this->GetPropertyAsString( #property_name "First" ); \
605  const std::string last = this->GetPropertyAsString( #property_name "Last" ); \
606  if ( !first.empty() || !last.empty() ) \
607  { \
608  if ( first == last ) \
609  { \
610  os << " " label ": '" << first << "'" << std::endl; \
611  } \
612  else \
613  { \
614  os << " " label ": '" << first << "' - '" << last << "'" << std::endl; \
615  } \
616  } \
617  \
618 }
619 
620 #define printProperty( label, property_name ) \
621  \
622 { \
623  const std::string first = this->GetPropertyAsString( #property_name ); \
624  if ( !first.empty() ) \
625  { \
626  os << " " label ": '" << first << "'" << std::endl; \
627  } \
628  \
629 }
630 
631 #define printBool( label, commands ) \
632  \
633 { \
634  os << " " label ": '" << ( commands ? "yes" : "no" ) << "'" << std::endl; \
635  \
636 }
637 
638 
639 
640 
641 void mitk::DICOMImageBlockDescriptor::Print(std::ostream& os, bool filenameDetails) const
642 {
643  os << " Number of Frames: '" << m_ImageFrameList.size() << "'" << std::endl;
644  os << " SOP class: '" << this->GetSOPClassUIDAsName() << "'" << std::endl;
645 
646  printProperty( "Series Number", seriesNumber );
647  printProperty( "Study Description", studyDescription );
648  printProperty( "Series Description", seriesDescription );
649  printProperty( "Modality", modality );
650  printProperty( "Sequence Name", sequenceName );
651 
652  printPropertyRange( "Slice Location", sliceLocation );
653  printPropertyRange( "Acquisition Number", acquisitionNumber );
654  printPropertyRange( "Instance Number", instanceNumber );
655  printPropertyRange( "Image Position", imagePositionPatient );
656  printProperty( "Image Orientation", orientation );
657 
658  os << " Pixel spacing interpretation: '"
659  << PixelSpacingInterpretationToString( this->GetPixelSpacingInterpretation() ) << "'" << std::endl;
660  printBool( "Gantry Tilt", this->GetTiltInformation().IsRegularGantryTilt() )
661  // printBool("3D+t", this->GetFlag("3D+t",false))
662 
663  // os << " MITK image loaded: '" << (this->GetMitkImage().IsNotNull() ? "yes" : "no") << "'" <<
664  // std::endl;
665  if ( filenameDetails )
666  {
667  os << " Files in this image block:" << std::endl;
668  for ( auto frameIter = m_ImageFrameList.begin(); frameIter != m_ImageFrameList.end(); ++frameIter )
669  {
670  os << " " << ( *frameIter )->Filename;
671  if ( ( *frameIter )->FrameNo > 0 )
672  {
673  os << ", " << ( *frameIter )->FrameNo;
674  }
675  os << std::endl;
676  }
677  }
678 }
679 
680 #define storeTagValueToProperty( tag_name, tag_g, tag_e ) \
681  \
682 { \
683  const DICOMTag t( tag_g, tag_e ); \
684  const std::string tagValue = tagCache->GetTagValue( firstFrame, t ).value; \
685  const_cast<DICOMImageBlockDescriptor*>( this ) \
686  ->SetProperty( #tag_name, StringProperty::New( tagValue ) ); \
687  \
688 }
689 
690 #define storeTagValueRangeToProperty( tag_name, tag_g, tag_e ) \
691  \
692 { \
693  const DICOMTag t( tag_g, tag_e ); \
694  const std::string tagValueFirst = tagCache->GetTagValue( firstFrame, t ).value; \
695  const std::string tagValueLast = tagCache->GetTagValue( lastFrame, t ).value; \
696  const_cast<DICOMImageBlockDescriptor*>( this ) \
697  ->SetProperty( #tag_name "First", StringProperty::New( tagValueFirst ) ); \
698  const_cast<DICOMImageBlockDescriptor*>( this ) \
699  ->SetProperty( #tag_name "Last", StringProperty::New( tagValueLast ) ); \
700  \
701 }
702 
703 
704 void mitk::DICOMImageBlockDescriptor::UpdateImageDescribingProperties() const
705 {
706  if ( !m_PropertiesOutOfDate )
707  return;
708 
709  if ( !m_ImageFrameList.empty() )
710  {
711  if ( m_TagCache.IsExpired() )
712  {
713  MITK_ERROR << "Invalid call to DICOMImageBlockDescriptor::UpdateImageDescribingProperties(). Need to "
714  "have initialized tag-cache!";
715  return;
716  }
717 
718  auto tagCache = m_TagCache.Lock();
719 
720  const DICOMImageFrameInfo::Pointer firstFrame = m_ImageFrameList.front();
721  const DICOMImageFrameInfo::Pointer lastFrame = m_ImageFrameList.back();
722 
723  // see macros above
724  storeTagValueToProperty( seriesNumber, 0x0020, 0x0011 );
725  storeTagValueToProperty( studyDescription, 0x0008, 0x1030 );
726  storeTagValueToProperty( seriesDescription, 0x0008, 0x103e );
727  storeTagValueToProperty( modality, 0x0008, 0x0060 );
728  storeTagValueToProperty( sequenceName, 0x0018, 0x0024 );
729  storeTagValueToProperty( orientation, 0x0020, 0x0037 );
730  storeTagValueToProperty( rows, 0x0028, 0x0010 );
731  storeTagValueToProperty( columns, 0x0028, 0x0011 );
732 
733  storeTagValueRangeToProperty( sliceLocation, 0x0020, 0x1041 );
734  storeTagValueRangeToProperty( acquisitionNumber, 0x0020, 0x0012 );
735  storeTagValueRangeToProperty( instanceNumber, 0x0020, 0x0013 );
736  storeTagValueRangeToProperty( imagePositionPatient, 0x0020, 0x0032 );
737 
738  storeTagValueToProperty( windowCenter, 0x0028, 0x1050 );
739  storeTagValueToProperty( windowWidth, 0x0028, 0x1051 );
740  storeTagValueToProperty( imageType, 0x0008, 0x0008 );
741  storeTagValueToProperty( photometricInterpretation, 0x0028, 0x0004 );
742 
743  // some per-image attributes
744  // frames are just numbered starting from 0. timestep 1 (the second time-step) has frames starting at
745  // (number-of-frames-per-timestep)
746  // std::string propertyKeySliceLocation = "dicom.image.0020.1041";
747  // std::string propertyKeyInstanceNumber = "dicom.image.0020.0013";
748  // std::string propertyKeySOPInstanceNumber = "dicom.image.0008.0018";
749 
750  StringLookupTable sliceLocationForSlices;
751  StringLookupTable instanceNumberForSlices;
752  StringLookupTable SOPInstanceUIDForSlices;
753  StringLookupTable filenamesForSlices;
754 
755  const DICOMTag tagSliceLocation( 0x0020, 0x1041 );
756  const DICOMTag tagInstanceNumber( 0x0020, 0x0013 );
757  const DICOMTag tagSOPInstanceNumber( 0x0008, 0x0018 );
758 
759  std::unordered_map<std::string, DICOMCachedValueLookupTable> additionalTagResultList;
760 
761  unsigned int slice(0);
762  int timePoint(-1);
763  const int framesPerTimeStep = this->GetNumberOfFramesPerTimeStep();
764  for ( auto frameIter = m_ImageFrameList.begin(); frameIter != m_ImageFrameList.end();
765  ++slice, ++frameIter )
766  {
767  unsigned int zSlice = slice%framesPerTimeStep;
768  if ( zSlice == 0)
769  {
770  timePoint++;
771  }
772 
773  const std::string sliceLocation = tagCache->GetTagValue( *frameIter, tagSliceLocation ).value;
774  sliceLocationForSlices.SetTableValue( slice, sliceLocation );
775 
776  const std::string instanceNumber = tagCache->GetTagValue( *frameIter, tagInstanceNumber ).value;
777  instanceNumberForSlices.SetTableValue( slice, instanceNumber );
778 
779  const std::string sopInstanceUID = tagCache->GetTagValue( *frameIter, tagSOPInstanceNumber ).value;
780  SOPInstanceUIDForSlices.SetTableValue( slice, sopInstanceUID );
781 
782  const std::string filename = ( *frameIter )->Filename;
783  filenamesForSlices.SetTableValue( slice, filename );
784 
785  MITK_DEBUG << "Tag info for slice " << slice << ": SL '" << sliceLocation << "' IN '" << instanceNumber
786  << "' SOP instance UID '" << sopInstanceUID << "'";
787 
788  for (const auto& tag : m_AdditionalTagMap)
789  {
790  const DICOMTagCache::FindingsListType findings = tagCache->GetTagValue( *frameIter, tag.first );
791  for (const auto& finding : findings)
792  {
793  if (finding.isValid)
794  {
795  std::string propKey = (tag.second.empty()) ? DICOMTagPathToPropertyName(finding.path) : tag.second;
796  DICOMCachedValueInfo info{ static_cast<unsigned int>(timePoint), zSlice, finding.value };
797  additionalTagResultList[propKey].SetTableValue(slice, info);
798  }
799  }
800  }
801  }
802 
803  // add property or properties with proper names
804  auto* thisInstance = const_cast<DICOMImageBlockDescriptor*>( this );
805  thisInstance->SetProperty( "sliceLocationForSlices",
806  StringLookupTableProperty::New( sliceLocationForSlices ) );
807 
808  thisInstance->SetProperty( "instanceNumberForSlices",
809  StringLookupTableProperty::New( instanceNumberForSlices ) );
810 
811  thisInstance->SetProperty( "SOPInstanceUIDForSlices",
812  StringLookupTableProperty::New( SOPInstanceUIDForSlices ) );
813 
814  thisInstance->SetProperty( "filenamesForSlices", StringLookupTableProperty::New( filenamesForSlices ) );
815 
816 
817  //add properties for additional tags of interest
818 
819  for ( auto iter = additionalTagResultList.cbegin(); iter != additionalTagResultList.cend(); ++iter )
820  {
821  thisInstance->SetProperty( iter->first, m_PropertyFunctor( iter->second ) );
822  thisInstance->m_FoundAdditionalTags.insert(m_FoundAdditionalTags.cend(),iter->first);
823  }
824 
825  m_PropertiesOutOfDate = false;
826  }
827 }
828 
829 
831 mitk::DICOMImageBlockDescriptor::GetPropertyForDICOMValues(const DICOMCachedValueLookupTable& cacheLookupTable)
832 {
833  const auto& lookupTable = cacheLookupTable.GetLookupTable();
834  typedef std::pair<int, DICOMCachedValueInfo> PairType;
835  if ( std::adjacent_find(
836  lookupTable.cbegin(),
837  lookupTable.cend(),
838  []( const PairType& lhs, const PairType& rhs ) { return lhs.second.Value != rhs.second.Value; } )
839  == lookupTable.cend() )
840  {
841  return static_cast<mitk::BaseProperty::Pointer>(
842  mitk::StringProperty::New(cacheLookupTable.GetTableValue(0).Value).GetPointer());
843  }
844 
845  StringLookupTable stringTable;
846  for (auto element : lookupTable)
847  {
848  stringTable.SetTableValue(element.first, element.second.Value);
849  }
850 
851  return static_cast<mitk::BaseProperty::Pointer>(
852  mitk::StringLookupTableProperty::New(stringTable).GetPointer());
853 }
854 
855 
857 {
858  if ( functor != nullptr )
859  {
860  m_PropertyFunctor = functor;
861  }
862 }
863 
865  const std::string &/*contextName*/, bool /*fallBackOnDefaultContext*/) const
866 {
867  this->UpdateImageDescribingProperties();
868  return m_PropertyList->GetConstProperty(propertyKey);
869 };
870 
871 std::vector<std::string> mitk::DICOMImageBlockDescriptor::GetPropertyKeys(const std::string &/*contextName*/, bool /*includeDefaultContext*/) const
872 {
873  this->UpdateImageDescribingProperties();
874  return m_PropertyList->GetPropertyKeys();
875 };
876 
878 {
879  return std::vector<std::string>();
880 };
void GetDesiredMITKImagePixelSpacing(ScalarType &spacingXinMM, ScalarType &spacingYinMM) const
Describe the correct x/y pixel spacing of the mitk::Image (which some readers might need to adjust af...
const GantryTiltInformation GetTiltInformation() const
Describe the gantry tilt of the acquisition.
BaseProperty::ConstPointer GetConstProperty(const std::string &propertyKey, const std::string &contextName="", bool fallBackOnDefaultContext=true) const override
Get property by its key.
void Print(std::ostream &os, bool filenameDetails) const
Print information about this image block to given stream.
loader did not yet inspect any images, unknown fitness
void SetSpacing(const mitk::Vector3D &aSpacing, bool enforceSetSpacing=false)
Set the spacing (m_Spacing).
BaseProperty * GetProperty(const std::string &key) const
Key-value store describing aspects of the image to be loaded.
void SetIntProperty(const std::string &key, int value)
Convenience function around SetProperty()
void SetTiltInformation(const GantryTiltInformation &info)
Describe the gantry tilt of the acquisition.
static Pointer New()
#define MITK_ERROR
Definition: mitkLogMacros.h:20
double ScalarType
bool DICOMStringToSpacing(const std::string &s, ScalarType &spacingX, ScalarType &spacingY)
std::vector< DICOMTag > DICOMTagList
Definition: mitkDICOMTag.h:59
void SetAdditionalTagsOfInterest(const AdditionalTagsMapType &tagMap)
Set a list of DICOMTagPaths that specifiy all DICOM-Tags that will be copied into the property of the...
Representation of a DICOM tag.
Definition: mitkDICOMTag.h:32
NO spacing information is present, we use (1,1) as default.
DICOMImageBlockDescriptor & operator=(const DICOMImageBlockDescriptor &other)
#define MITK_DEBUG
Definition: mitkLogMacros.h:22
std::string GetSOPClassUIDAsName() const
SOP Class as human readable name (e.g. "CT Image Storage")
Image::Pointer GetMitkImage() const
the 3D mitk::Image that is loaded from the DICOM files of a DICOMImageFrameList
std::string ReaderImplementationLevelToString(const ReaderImplementationLevel &enumValue)
Convert mitk::ReaderImplementationLevel to a human readable string.
#define printProperty(label, property_name)
ReaderImplementationLevel GetReaderImplementationLevel() const
Reader&#39;s capability to appropriately load this set of frames.
int GetIntProperty(const std::string &key, int defaultValue) const
Convenience function around GetProperty()
Key-value list holding instances of BaseProperty.
#define printPropertyRange(label, property_name)
const DICOMImageFrameList & GetImageFrameList() const
List of frames that constitute the mitk::Image (DICOMImageFrames)
std::map< DICOMTagPath, std::string > AdditionalTagsMapType
#define storeTagValueRangeToProperty(tag_name, tag_g, tag_e)
static void info(const char *fmt,...)
Definition: svm.cpp:86
void SetProperty(const std::string &propertyKey, BaseProperty *property, const std::string &contextName="", bool fallBackOnDefaultContext=false) override
Add new or change existent property.
std::function< mitk::BaseProperty::Pointer(const DICOMCachedValueLookupTable &) > TagLookupTableToPropertyFunctor
void SetImageFrameList(const DICOMImageFrameList &framelist)
List of frames that constitute the mitk::Image (DICOMImageFrames)
std::string GetSOPClassUID() const
SOP Class UID of this set of frames.
void SetMitkImage(Image::Pointer image)
The 3D mitk::Image that is loaded from the DICOM files of a DICOMImageFrameList.
The LevelWindow class Class to store level/window values.
static Pointer New()
MITKDICOMREADER_EXPORT std::string DICOMTagPathToPropertyName(const DICOMTagPath &tagPath)
std::vector< std::string > GetPropertyContextNames() const override
Query names of existing contexts.
void SetTagLookupTableToPropertyFunctor(TagLookupTableToPropertyFunctor)
Set a functor that defines how the slice-specific tag-values are stored in a Property.
ReaderImplementationLevel
Describes how well the reader is tested for a certain file type (see mitk::DICOMFileReader).
PixelSpacingInterpretation
How the mitk::Image spacing should be interpreted (see mitk::DICOMFileReader).
Abstract base class for properties.
void SetReaderImplementationLevel(const ReaderImplementationLevel &level)
Reader&#39;s capability to appropriately load this set of frames.
std::vector< DICOMImageFrameInfo::Pointer > DICOMImageFrameList
std::string PixelSpacingInterpretationToString(const PixelSpacingInterpretation &value)
Convert mitk::PixelSpacingInterpretation to a human readable string.
Image class for storing images.
Definition: mitkImage.h:72
Output descriptor for DICOMFileReader.
void SetTagCache(DICOMTagCache *privateCache)
std::vector< std::string > GetPropertyKeys(const std::string &contextName="", bool includeDefaultContext=false) const override
Query keys of existing properties.
mitk::Image::Pointer image
std::string GetPropertyAsString(const std::string &) const
Convenience function around GetProperty()
static Pointer New()
const mitk::Vector3D GetSpacing() const
Get the spacing (size of a pixel).
Gantry tilt analysis result.
void SetProperty(const std::string &key, BaseProperty *value)
Key-value store describing aspects of the image to be loaded.
void SetFlag(const std::string &key, bool value)
Convenience function around SetProperty()
std::list< DICOMDatasetFinding > FindingsListType
PixelSpacingInterpretation GetPixelSpacingInterpretation() const
Describe how the mitk::Image&#39;s pixel spacing should be interpreted.
#define storeTagValueToProperty(tag_name, tag_g, tag_e)
static mitk::PlanarFigure::Pointer Clone(mitk::PlanarFigure::Pointer original)
mitk::BaseGeometry * GetGeometry(int t=0) const
Return the geometry, which is a TimeGeometry, of the data as non-const pointer.
Definition: mitkBaseData.h:138
distances are mm at detector surface
static Pointer New()
bool GetFlag(const std::string &key, bool defaultValue) const
Convenience function around GetProperty()
#define printBool(label, commands)
distances are mm within a patient