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