Medical Imaging Interaction Toolkit  2016.11.0
Medical Imaging Interaction Toolkit
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Modules Pages
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.