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