Medical Imaging Interaction Toolkit  2016.11.0
Medical Imaging Interaction Toolkit
old/mitkImageStatisticsCalculator.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 
17 
19 #include "mitkImageAccessByItk.h"
20 #include "mitkImageCast.h"
21 #include "mitkExtractImageFilter.h"
22 #include "mitkImageTimeSelector.h"
23 #include "mitkITKImageImport.h"
24 
27 
28 #include <itkScalarImageToHistogramGenerator.h>
29 
30 #include <itkChangeInformationImageFilter.h>
31 #include <itkExtractImageFilter.h>
32 #include <itkMinimumMaximumImageCalculator.h>
33 #include <itkStatisticsImageFilter.h>
34 #include <itkLabelStatisticsImageFilter.h>
35 #include <itkMaskImageFilter.h>
36 #include <itkImageFileWriter.h>
37 #include <itkRescaleIntensityImageFilter.h>
38 
39 #include <itkMaskImageFilter.h>
40 
41 #include <itkImageRegionConstIterator.h>
42 #include <itkImageRegionIterator.h>
43 
44 #include <itkCastImageFilter.h>
45 #include <itkVTKImageImport.h>
46 #include <itkVTKImageExport.h>
47 #include <itkImageDuplicator.h>
48 
49 #include <vtkPoints.h>
50 #include <vtkImageStencil.h>
51 #include <vtkImageImport.h>
52 #include <vtkImageExport.h>
53 #include <vtkLassoStencilSource.h>
54 
55 #include <itkFFTConvolutionImageFilter.h>
56 #include <itkConstantBoundaryCondition.h>
57 #include <itkImageDuplicator.h>
58 
59 #include <itkContinuousIndex.h>
60 #include <itkNumericTraits.h>
61 #include <list>
62 
63 #include <exception>
64 
65 #include "itkImage.h"
66 
67 
68 //#define DEBUG_HOTSPOTSEARCH
69 
70 #define _USE_MATH_DEFINES
71 #include <math.h>
72 
73 #include "vtkLassoStencilSource.h"
74 
75 
76 namespace mitk
77 {
79  : m_MaskingMode( MASKING_MODE_NONE ),
80  m_MaskingModeChanged( false ),
81  m_IgnorePixelValue(0.0),
82  m_DoIgnorePixelValue(false),
83  m_IgnorePixelValueChanged(false),
84  m_PlanarFigureAxis (0),
85  m_PlanarFigureSlice (0),
86  m_PlanarFigureCoordinate0 (0),
87  m_PlanarFigureCoordinate1 (0),
88  m_HistogramBinSize(1.0),
89  m_UseDefaultBinSize(true),
90  m_UseBinSizeBasedOnVOIRegion(false),
91  m_HotspotRadiusInMM(6.2035049089940), // radius of a 1cm3 sphere in mm
92  m_CalculateHotspot(false),
93  m_HotspotRadiusInMMChanged(false),
94  m_HotspotMustBeCompletelyInsideImage(true)
95  {
96  m_EmptyHistogram = HistogramType::New();
97  m_EmptyHistogram->SetMeasurementVectorSize(1);
98  HistogramType::SizeType histogramSize(1);
99  histogramSize.Fill( 256 );
100  m_EmptyHistogram->Initialize( histogramSize );
101 
102  m_EmptyStatistics.Reset();
103  }
104 
106  {
107  }
108 
109 
111  {
112  m_UseDefaultBinSize = useDefault;
113  }
114 
115 
116 
117 
119  :m_HotspotStatistics(withHotspotStatistics ? new Statistics(false) : nullptr)
120  {
121  Reset();
122  }
123 
125  :m_HotspotStatistics( nullptr)
126  {
127  this->SetLabel( other.GetLabel() );
128  this->SetN( other.GetN() );
129  this->SetMin( other.GetMin() );
130  this->SetMax( other.GetMax() );
131  this->SetMedian( other.GetMedian() );
132  this->SetMean( other.GetMean() );
133  this->SetVariance( other.GetVariance() );
134  this->SetKurtosis( other.GetKurtosis() );
135  this->SetSkewness( other.GetSkewness() );
136  this->SetUniformity( other.GetUniformity() );
137  this->SetEntropy( other.GetEntropy() );
138  this->SetUPP( other.GetUPP() );
139  this->SetMPP( other.GetMPP() );
140  this->SetSigma( other.GetSigma() );
141  this->SetRMS( other.GetRMS() );
142  this->SetMaxIndex( other.GetMaxIndex() );
143  this->SetMinIndex( other.GetMinIndex() );
144  this->SetHotspotIndex( other.GetHotspotIndex() );
145 
146  if (other.m_HotspotStatistics)
147  {
148  this->m_HotspotStatistics = new Statistics(false);
149  *this->m_HotspotStatistics = *other.m_HotspotStatistics;
150  }
151  }
152 
154  {
155  return m_HotspotStatistics != nullptr;
156  }
157 
159  {
160  m_HasHotspotStatistics = hasHotspotStatistics;
161  }
162 
163 
165  {
166  delete m_HotspotStatistics;
167  }
168 
170  {
171  return this->Variance;
172  }
173 
175  {
176  if( this->Variance != value )
177  {
178  if( value < 0.0 )
179  {
180  this->Variance = 0.0; // if given value is negative set variance to 0.0
181  }
182  else
183  {
184  this->Variance = value;
185  }
186  }
187  }
188 
190  {
191  return this->Sigma;
192  }
193 
195  {
196  if( this->Sigma != value )
197  {
198  // for some compiler the value != value works to check for NaN but not for all
199  // but we can always be sure that the standard deviation is a positive value
200  if( value != value || value < 0.0 )
201  {
202  // if standard deviation is NaN we just assume 0.0
203  this->Sigma = 0.0;
204  }
205  else
206  {
207  this->Sigma = value;
208  }
209  }
210  }
211 
212  void ImageStatisticsCalculator::Statistics::Reset(unsigned int dimension)
213  {
214  SetLabel(0);
215  SetN( 0 );
216  SetMin( 0.0 );
217  SetMax( 0.0 );
218  SetMedian( 0.0 );
219  SetVariance( 0.0 );
220  SetMean( 0.0 );
221  SetSigma( 0.0 );
222  SetRMS( 0.0 );
223 
224  SetSkewness( 0.0 );
225  SetKurtosis( 0.0 );
226  SetUniformity( 0.0 );
227  SetEntropy( 0.0 );
228  SetMPP( 0.0 );
229  SetUPP( 0.0 );
230 
231  vnl_vector<int> zero;
232  zero.set_size(dimension);
233  for(unsigned int i = 0; i < dimension; ++i)
234  {
235  zero[i] = 0;
236  }
237 
238  SetMaxIndex(zero);
239  SetMinIndex(zero);
240  SetHotspotIndex(zero);
241 
242  if (m_HotspotStatistics != nullptr)
243  {
244  m_HotspotStatistics->Reset(dimension);
245  }
246  }
247 
250  {
251  if (m_HotspotStatistics)
252  {
253  return *m_HotspotStatistics;
254  }
255  else
256  {
257  throw std::logic_error("Object has no hostspot statistics, see HasHotspotStatistics()");
258  }
259  }
260 
263  {
264  if (m_HotspotStatistics)
265  {
266  return *m_HotspotStatistics;
267  }
268  else
269  {
270  throw std::logic_error("Object has no hostspot statistics, see HasHotspotStatistics()");
271  }
272  }
273 
276  {
277  if (this == &other)
278  return *this;
279 
280  this->SetLabel( other.GetLabel() );
281  this->SetN( other.GetN() );
282  this->SetMin( other.GetMin() );
283  this->SetMax( other.GetMax() );
284  this->SetMean( other.GetMean() );
285  this->SetMedian( other.GetMedian() );
286  this->SetVariance( other.GetVariance() );
287  this->SetSigma( other.GetSigma() );
288  this->SetRMS( other.GetRMS() );
289  this->SetMinIndex( other.GetMinIndex() );
290  this->SetMaxIndex( other.GetMaxIndex() );
291  this->SetHotspotIndex( other.GetHotspotIndex() );
292  this->SetSkewness( other.GetSkewness() );
293  this->SetKurtosis( other.GetKurtosis() );
294  this->SetUniformity( other.GetUniformity() );
295  this->SetEntropy( other.GetEntropy() );
296  this->SetUPP( other.GetUPP() );
297  this->SetMPP( other.GetMPP() );
298 
299 
300  delete this->m_HotspotStatistics;
301  this->m_HotspotStatistics = nullptr;
302 
303  if (other.m_HotspotStatistics)
304  {
305  this->m_HotspotStatistics = new Statistics(false);
306  *this->m_HotspotStatistics = *other.m_HotspotStatistics;
307  }
308 
309  return *this;
310 
311  }
312 
314  {
315  if ( m_Image != image )
316  {
317  m_Image = image;
318  this->Modified();
319  unsigned int numberOfTimeSteps = image->GetTimeSteps();
320 
321  // Initialize vectors to time-size of this image
322  m_ImageHistogramVector.resize( numberOfTimeSteps );
323  m_MaskedImageHistogramVector.resize( numberOfTimeSteps );
324  m_PlanarFigureHistogramVector.resize( numberOfTimeSteps );
325 
326  m_ImageStatisticsVector.resize( numberOfTimeSteps );
327  m_MaskedImageStatisticsVector.resize( numberOfTimeSteps );
328  m_PlanarFigureStatisticsVector.resize( numberOfTimeSteps );
329 
330  m_ImageStatisticsTimeStampVector.resize( numberOfTimeSteps );
331  m_MaskedImageStatisticsTimeStampVector.resize( numberOfTimeSteps );
332  m_PlanarFigureStatisticsTimeStampVector.resize( numberOfTimeSteps );
333 
334  m_ImageStatisticsCalculationTriggerVector.resize( numberOfTimeSteps );
335  m_MaskedImageStatisticsCalculationTriggerVector.resize( numberOfTimeSteps );
336  m_PlanarFigureStatisticsCalculationTriggerVector.resize( numberOfTimeSteps );
337 
338  for ( unsigned int t = 0; t < image->GetTimeSteps(); ++t )
339  {
340  m_ImageStatisticsTimeStampVector[t].Modified();
342  }
343  }
344  }
345 
346 
348  {
349  if ( m_Image.IsNull() )
350  {
351  itkExceptionMacro( << "Image needs to be set first!" );
352  }
353 
354  if ( m_ImageMask != imageMask )
355  {
356  m_ImageMask = imageMask;
357  this->Modified();
358 
359  for ( unsigned int t = 0; t < m_Image->GetTimeSteps(); ++t )
360  {
363  }
364  }
365  }
366 
367 
369  {
370  if ( m_Image.IsNull() )
371  {
372  itkExceptionMacro( << "Image needs to be set first!" );
373  }
374 
375  if ( m_PlanarFigure != planarFigure )
376  {
377  m_PlanarFigure = planarFigure;
378  this->Modified();
379 
380  for ( unsigned int t = 0; t < m_Image->GetTimeSteps(); ++t )
381  {
384  }
385  }
386  }
387 
388 
390  {
391  if ( m_MaskingMode != mode )
392  {
393  m_MaskingMode = mode;
394  m_MaskingModeChanged = true;
395  this->Modified();
396  }
397  }
398 
399 
401  {
403  {
405  m_MaskingModeChanged = true;
406  this->Modified();
407  }
408  }
409 
410 
412  {
414  {
416  m_MaskingModeChanged = true;
417  this->Modified();
418  }
419  }
420 
421 
423  {
425  {
427  m_MaskingModeChanged = true;
428  this->Modified();
429  }
430  }
431 
433  {
434  if ( m_IgnorePixelValue != value )
435  {
436  m_IgnorePixelValue = value;
438  {
440  }
441  this->Modified();
442  }
443  }
444 
446  {
447  return m_IgnorePixelValue;
448  }
449 
451  {
452  if ( m_DoIgnorePixelValue != value )
453  {
454  m_DoIgnorePixelValue = value;
456  this->Modified();
457  }
458  }
459 
461  {
462  return m_DoIgnorePixelValue;
463  }
464 
466  {
467  this->m_HistogramBinSize = size;
468  }
470  {
471  return this->m_HistogramBinSize;
472  }
473 
475  {
476  if ( m_HotspotRadiusInMM != value )
477  {
478  m_HotspotRadiusInMM = value;
480  {
482  //MITK_INFO <<"Hotspot radius changed, new convolution required";
483  }
484  this->Modified();
485  }
486  }
487 
489  {
490  return m_HotspotRadiusInMM;
491  }
492 
494  {
495  if ( m_CalculateHotspot != on )
496  {
497  m_CalculateHotspot = on;
499  //MITK_INFO <<"Hotspot calculation changed, new convolution required";
500  this->Modified();
501  }
502  }
503 
505  {
506  return m_CalculateHotspot;
507  }
508 
509  void ImageStatisticsCalculator::SetHotspotMustBeCompletlyInsideImage(bool hotspotMustBeCompletelyInsideImage, bool warn)
510  {
511  m_HotspotMustBeCompletelyInsideImage = hotspotMustBeCompletelyInsideImage;
513  {
514  MITK_WARN << "Hotspot calculation will extrapolate pixels at image borders. Be aware of the consequences for the hotspot location.";
515  }
516  }
517 
519  {
521  }
522 
523 
524  /* Implementation of the min max values for setting the range of the histogram */
525  template < typename TPixel, unsigned int VImageDimension >
527  double &max, int &counter, double &sigma,
528  const itk::Image< TPixel, VImageDimension > *InputImage,
529  itk::Image< unsigned short, VImageDimension > *MaskedImage )
530  {
531  typedef itk::Image< unsigned short, VImageDimension > MaskImageType;
532  typedef itk::Image< TPixel, VImageDimension > ImageType;
533 
534  typedef itk::ImageRegionConstIteratorWithIndex<ImageType> Imageie;
535  typedef itk::ImageRegionConstIteratorWithIndex<MaskImageType> Imageie2;
536 
537  Imageie2 labelIterator2( MaskedImage, MaskedImage->GetRequestedRegion() );
538  Imageie labelIterator3( InputImage, InputImage->GetRequestedRegion() );
539 
540  max = 0;
541  min = 0;
542  counter = 0;
543  sigma = 0;
544  double SumOfSquares = 0;
545  double sumSquared = 0;
546 
547  double actualPielValue = 0;
548  int counterOfPixelsInROI = 0;
549 
550 
551  for( labelIterator2.GoToBegin(); !labelIterator2.IsAtEnd(); ++labelIterator2, ++labelIterator3)
552  {
553  if( labelIterator2.Value()== 1.0)
554  {
555  counter++;
556 
557  counterOfPixelsInROI++;
558  actualPielValue = labelIterator3.Value();
559 
560  sumSquared = sumSquared + actualPielValue;
561  SumOfSquares = SumOfSquares + std::pow(actualPielValue,2);
562 
563  if(counterOfPixelsInROI == 1)
564  {
565  max = actualPielValue;
566  min = actualPielValue;
567  }
568 
569  if(actualPielValue >= max)
570  {
571  max = actualPielValue;
572  }
573  else if(actualPielValue <= min)
574  {
575  min = actualPielValue;
576  }
577 
578  }
579  }
580 
581  if (counter > 1)
582  {
583  sigma = ( SumOfSquares - std::pow( sumSquared, 2) / counter ) / ( counter-1 );
584  }
585  else
586  {
587  sigma = 0;
588  }
589 
590  }
591 
592 
593  bool ImageStatisticsCalculator::ComputeStatistics( unsigned int timeStep )
594  {
595 
596  if (m_Image.IsNull() )
597  {
598  mitkThrow() << "Image not set!";
599  }
600 
601  if (!m_Image->IsInitialized())
602  {
603  mitkThrow() << "Image not initialized!";
604  }
605 
606  if ( m_Image->GetReferenceCount() == 1 )
607  {
608  // Image no longer valid; we are the only ones to still hold a reference on it
609  return false;
610  }
611 
612  if ( timeStep >= m_Image->GetTimeSteps() )
613  {
614  throw std::runtime_error( "Error: invalid time step!" );
615  }
616 
617  // If a mask was set but we are the only ones to still hold a reference on
618  // it, delete it.
619  if ( m_ImageMask.IsNotNull() && (m_ImageMask->GetReferenceCount() == 1) )
620  {
621  m_ImageMask = nullptr;
622  }
623 
624  // Check if statistics is already up-to-date
625  unsigned long imageMTime = m_ImageStatisticsTimeStampVector[timeStep].GetMTime();
626  unsigned long maskedImageMTime = m_MaskedImageStatisticsTimeStampVector[timeStep].GetMTime();
627  unsigned long planarFigureMTime = m_PlanarFigureStatisticsTimeStampVector[timeStep].GetMTime();
628 
629  bool imageStatisticsCalculationTrigger = m_ImageStatisticsCalculationTriggerVector[timeStep];
630  bool maskedImageStatisticsCalculationTrigger = m_MaskedImageStatisticsCalculationTriggerVector[timeStep];
631  bool planarFigureStatisticsCalculationTrigger = m_PlanarFigureStatisticsCalculationTriggerVector[timeStep];
632 
635  && ((m_MaskingMode != MASKING_MODE_NONE) || (imageMTime > m_Image->GetMTime() && !imageStatisticsCalculationTrigger))
636  && ((m_MaskingMode != MASKING_MODE_IMAGE) || (maskedImageMTime > m_ImageMask->GetMTime() && !maskedImageStatisticsCalculationTrigger))
637  && ((m_MaskingMode != MASKING_MODE_PLANARFIGURE) || (planarFigureMTime > m_PlanarFigure->GetMTime() && !planarFigureStatisticsCalculationTrigger)) )
638  {
639  // Statistics is up to date!
640  if ( m_MaskingModeChanged )
641  {
642  m_MaskingModeChanged = false;
643  }
644  else
645  {
646  return false;
647  }
648  }
649 
650  // Reset state changed flag
651  m_MaskingModeChanged = false;
653 
654  // Depending on masking mode, extract and/or generate the required image
655  // and mask data from the user input
656  this->ExtractImageAndMask( timeStep );
657 
658 
659  StatisticsContainer *statisticsContainer;
660  HistogramContainer *histogramContainer;
661  switch ( m_MaskingMode )
662  {
663  case MASKING_MODE_NONE:
664  default:
666  {
667  statisticsContainer = &m_ImageStatisticsVector[timeStep];
668  histogramContainer = &m_ImageHistogramVector[timeStep];
669 
670  m_ImageStatisticsTimeStampVector[timeStep].Modified();
672  }
673  else
674  {
675  statisticsContainer = &m_MaskedImageStatisticsVector[timeStep];
676  histogramContainer = &m_MaskedImageHistogramVector[timeStep];
677 
678  m_MaskedImageStatisticsTimeStampVector[timeStep].Modified();
680  }
681  break;
682 
683  case MASKING_MODE_IMAGE:
684  statisticsContainer = &m_MaskedImageStatisticsVector[timeStep];
685  histogramContainer = &m_MaskedImageHistogramVector[timeStep];
686 
687  m_MaskedImageStatisticsTimeStampVector[timeStep].Modified();
689  break;
690 
692  statisticsContainer = &m_PlanarFigureStatisticsVector[timeStep];
693  histogramContainer = &m_PlanarFigureHistogramVector[timeStep];
694 
695  m_PlanarFigureStatisticsTimeStampVector[timeStep].Modified();
697  break;
698  }
699 
700  // Calculate statistics and histogram(s)
701  if ( m_InternalImage->GetDimension() == 3 )
702  {
704  {
707  InternalCalculateStatisticsUnmasked,
708  3,
709  statisticsContainer,
710  histogramContainer );
711  }
712  else
713  {
716  InternalCalculateStatisticsMasked,
717  3,
718  m_InternalImageMask3D.GetPointer(),
719  statisticsContainer,
720  histogramContainer );
721  }
722  }
723  else if ( m_InternalImage->GetDimension() == 2 )
724  {
726  {
729  InternalCalculateStatisticsUnmasked,
730  2,
731  statisticsContainer,
732  histogramContainer );
733  }
734  else
735  {
738  InternalCalculateStatisticsMasked,
739  2,
740  m_InternalImageMask2D.GetPointer(),
741  statisticsContainer,
742  histogramContainer );
743  }
744  }
745  else
746  {
747  MITK_ERROR << "ImageStatistics: Image dimension not supported!";
748  }
749 
750 
751  // Release unused image smart pointers to free memory
755 
756 
757 
758  return true;
759  }
760 
761 
763  ImageStatisticsCalculator::GetBinsAndFreuqencyForHistograms( unsigned int timeStep , unsigned int label ) const
764  {
765  const HistogramType *binsAndFrequencyToCalculate = this->GetHistogram(0);
766 
767  // ToDo: map should be created on stack not on heap
768  std::map<int, double> returnedHistogramMap;
769 
770  unsigned int size = binsAndFrequencyToCalculate->Size();
771  for( unsigned int bin=0; bin < size; ++bin )
772  {
773  double frequency = binsAndFrequencyToCalculate->GetFrequency( bin, 0 );
774  //if( frequency > mitk::eps )
775  {
776  returnedHistogramMap.insert( std::pair<int, double>(binsAndFrequencyToCalculate->GetMeasurement( bin, 0 ), binsAndFrequencyToCalculate->GetFrequency( bin, 0 ) ) );
777  }
778  }
779 
780  return returnedHistogramMap;
781  }
782 
784  ImageStatisticsCalculator::GetHistogram( unsigned int timeStep, unsigned int label ) const
785  {
786  if ( m_Image.IsNull() || (timeStep >= m_Image->GetTimeSteps()) )
787  {
788  return nullptr;
789  }
790 
791  switch ( m_MaskingMode )
792  {
793  case MASKING_MODE_NONE:
794  default:
795  {
797  return m_MaskedImageHistogramVector[timeStep][label];
798 
799  return m_ImageHistogramVector[timeStep][label];
800  }
801 
802  case MASKING_MODE_IMAGE:
803  return m_MaskedImageHistogramVector[timeStep][label];
804 
806  return m_PlanarFigureHistogramVector[timeStep][label];
807  }
808  }
809 
811  ImageStatisticsCalculator::GetHistogramVector( unsigned int timeStep ) const
812  {
813  if ( m_Image.IsNull() || (timeStep >= m_Image->GetTimeSteps()) )
814  {
816  }
817 
818  switch ( m_MaskingMode )
819  {
820  case MASKING_MODE_NONE:
821  default:
822  {
824  return m_MaskedImageHistogramVector[timeStep];
825 
826  return m_ImageHistogramVector[timeStep];
827  }
828 
829  case MASKING_MODE_IMAGE:
830  return m_MaskedImageHistogramVector[timeStep];
831 
833  return m_PlanarFigureHistogramVector[timeStep];
834  }
835  }
836 
837 
839  ImageStatisticsCalculator::GetStatistics( unsigned int timeStep, unsigned int label ) const
840  {
841  if ( m_Image.IsNull() || (timeStep >= m_Image->GetTimeSteps()) )
842  {
843  return m_EmptyStatistics;
844  }
845 
846  switch ( m_MaskingMode )
847  {
848  case MASKING_MODE_NONE:
849  default:
850  {
852  return m_MaskedImageStatisticsVector[timeStep][label];
853 
854  return m_ImageStatisticsVector[timeStep][label];
855  }
856  case MASKING_MODE_IMAGE:
857  return m_MaskedImageStatisticsVector[timeStep][label];
858 
860  return m_PlanarFigureStatisticsVector[timeStep][label];
861  }
862  }
863 
865  ImageStatisticsCalculator::GetStatisticsVector( unsigned int timeStep ) const
866  {
867  if ( m_Image.IsNull() || (timeStep >= m_Image->GetTimeSteps()) )
868  {
870  }
871 
872  switch ( m_MaskingMode )
873  {
874  case MASKING_MODE_NONE:
875  default:
876  {
878  return m_MaskedImageStatisticsVector[timeStep];
879 
880  return m_ImageStatisticsVector[timeStep];
881  }
882  case MASKING_MODE_IMAGE:
883  return m_MaskedImageStatisticsVector[timeStep];
884 
886  return m_PlanarFigureStatisticsVector[timeStep];
887  }
888  }
889 
890 
891 
893  {
894  if ( m_Image.IsNull() )
895  {
896  throw std::runtime_error( "Error: image empty!" );
897  }
898 
899  if ( timeStep >= m_Image->GetTimeSteps() )
900  {
901  throw std::runtime_error( "Error: invalid time step!" );
902  }
903 
905  imageTimeSelector->SetInput( m_Image );
906  imageTimeSelector->SetTimeNr( timeStep );
907  imageTimeSelector->UpdateLargestPossibleRegion();
908  mitk::Image *timeSliceImage = imageTimeSelector->GetOutput();
909 
910 
911  switch ( m_MaskingMode )
912  {
913  case MASKING_MODE_NONE:
914  {
915  m_InternalImage = timeSliceImage;
916  m_InternalImageMask2D = nullptr;
917  m_InternalImageMask3D = nullptr;
919  {
920  if( m_InternalImage->GetDimension() == 3 )
921  {
922  if(itk::ImageIOBase::USHORT != timeSliceImage->GetPixelType().GetComponentType())
923  CastToItkImage( timeSliceImage, m_InternalImageMask3D );
924  else
925  CastToItkImage( timeSliceImage->Clone(), m_InternalImageMask3D );
926  m_InternalImageMask3D->FillBuffer(1);
927  }
928  if( m_InternalImage->GetDimension() == 2 )
929  {
930  if(itk::ImageIOBase::USHORT != timeSliceImage->GetPixelType().GetComponentType())
931  CastToItkImage( timeSliceImage, m_InternalImageMask2D );
932  else
933  CastToItkImage( timeSliceImage->Clone(), m_InternalImageMask2D );
934  m_InternalImageMask2D->FillBuffer(1);
935  }
936  }
937  break;
938  }
939 
940  case MASKING_MODE_IMAGE:
941  {
942  if ( m_ImageMask.IsNotNull() && (m_ImageMask->GetReferenceCount() > 1) )
943  {
944  if ( timeStep >= m_ImageMask->GetTimeSteps() )
945  {
946  // Use the last mask time step in case the current time step is bigger than the total
947  // number of mask time steps.
948  // It makes more sense setting this to the last mask time step than to 0.
949  // For instance if you have a mask with 2 time steps and an image with 5:
950  // If time step 0 is selected, the mask will use time step 0.
951  // If time step 1 is selected, the mask will use time step 1.
952  // If time step 2+ is selected, the mask will use time step 1.
953  // If you have a mask with only one time step instead, this will always default to 0.
954  timeStep = m_ImageMask->GetTimeSteps() - 1;
955  }
956 
957  ImageTimeSelector::Pointer maskedImageTimeSelector = ImageTimeSelector::New();
958  maskedImageTimeSelector->SetInput( m_ImageMask );
959  maskedImageTimeSelector->SetTimeNr( timeStep );
960  maskedImageTimeSelector->UpdateLargestPossibleRegion();
961  mitk::Image *timeSliceMaskedImage = maskedImageTimeSelector->GetOutput();
962 
963  m_InternalImage = timeSliceImage;
964  CastToItkImage( timeSliceMaskedImage, m_InternalImageMask3D );
965  }
966  else
967  {
968  throw std::runtime_error( "Error: image mask empty!" );
969  }
970  break;
971  }
973  {
974  m_InternalImageMask2D = nullptr;
975 
976  if ( m_PlanarFigure.IsNull() )
977  {
978  throw std::runtime_error( "Error: planar figure empty!" );
979  }
980  if ( !m_PlanarFigure->IsClosed() )
981  {
982  throw std::runtime_error( "Masking not possible for non-closed figures" );
983  }
984 
985  const BaseGeometry *imageGeometry = timeSliceImage->GetGeometry();
986  if ( imageGeometry == nullptr )
987  {
988  throw std::runtime_error( "Image geometry invalid!" );
989  }
990 
991  const PlaneGeometry *planarFigurePlaneGeometry = m_PlanarFigure->GetPlaneGeometry();
992  if ( planarFigurePlaneGeometry == nullptr )
993  {
994  throw std::runtime_error( "Planar-Figure not yet initialized!" );
995  }
996 
997  const PlaneGeometry *planarFigureGeometry =
998  dynamic_cast< const PlaneGeometry * >( planarFigurePlaneGeometry );
999  if ( planarFigureGeometry == nullptr )
1000  {
1001  throw std::runtime_error( "Non-planar planar figures not supported!" );
1002  }
1003 
1004  // Find principal direction of PlanarFigure in input image
1005  unsigned int axis;
1006  if ( !this->GetPrincipalAxis( imageGeometry,
1007  planarFigureGeometry->GetNormal(), axis ) )
1008  {
1009  throw std::runtime_error( "Non-aligned planar figures not supported!" );
1010  }
1011  m_PlanarFigureAxis = axis;
1012 
1013  // Find slice number corresponding to PlanarFigure in input image
1014  MaskImage3DType::IndexType index;
1015  imageGeometry->WorldToIndex( planarFigureGeometry->GetOrigin(), index );
1016 
1017  unsigned int slice = index[axis];
1018  m_PlanarFigureSlice = slice;
1019 
1020 
1021  // Extract slice with given position and direction from image
1022  unsigned int dimension = timeSliceImage->GetDimension();
1023 
1024  if (dimension != 2)
1025  {
1027  imageExtractor->SetInput( timeSliceImage );
1028  imageExtractor->SetSliceDimension( axis );
1029  imageExtractor->SetSliceIndex( slice );
1030  imageExtractor->Update();
1031  m_InternalImage = imageExtractor->GetOutput();
1032  }
1033  else
1034  {
1035  m_InternalImage = timeSliceImage;
1036  }
1037 
1038  // Compute mask from PlanarFigure
1042  2, axis );
1043  }
1044  }
1045 
1047  {
1048  if ( m_InternalImage->GetDimension() == 3 )
1049  {
1053  3,
1054  m_InternalImageMask3D.GetPointer() );
1055  }
1056  else if ( m_InternalImage->GetDimension() == 2 )
1057  {
1061  2,
1062  m_InternalImageMask2D.GetPointer() );
1063  }
1064  }
1065  }
1066 
1067 
1069  const BaseGeometry *geometry, Vector3D vector,
1070  unsigned int &axis )
1071  {
1072  vector.Normalize();
1073  for ( unsigned int i = 0; i < 3; ++i )
1074  {
1075  Vector3D axisVector = geometry->GetAxisVector( i );
1076  axisVector.Normalize();
1077 
1078  if ( fabs( fabs( axisVector * vector ) - 1.0) < mitk::eps )
1079  {
1080  axis = i;
1081  return true;
1082  }
1083  }
1084 
1085  return false;
1086  }
1087 
1088 
1089  unsigned int ImageStatisticsCalculator::calcNumberOfBins(mitk::ScalarType min, mitk::ScalarType max)
1090  {
1091  return std::ceil( ( (max - min ) / m_HistogramBinSize) );
1092  }
1093 
1094 
1095  template < typename TPixel, unsigned int VImageDimension >
1096  void ImageStatisticsCalculator::InternalCalculateStatisticsUnmasked(
1097  const itk::Image< TPixel, VImageDimension > *image,
1098  StatisticsContainer *statisticsContainer,
1099  HistogramContainer* histogramContainer )
1100  {
1101  typedef itk::Image< TPixel, VImageDimension > ImageType;
1102  typedef typename ImageType::IndexType IndexType;
1103 
1104  typedef itk::Statistics::ScalarImageToHistogramGenerator< ImageType >
1105  HistogramGeneratorType;
1106 
1107  statisticsContainer->clear();
1108  histogramContainer->clear();
1109 
1110  // Progress listening...
1111  typedef itk::SimpleMemberCommand< ImageStatisticsCalculator > ITKCommandType;
1112  ITKCommandType::Pointer progressListener;
1113  progressListener = ITKCommandType::New();
1114  progressListener->SetCallbackFunction( this,
1116 
1117 
1118  // Issue 100 artificial progress events since ScalarIMageToHistogramGenerator
1119  // does not (yet?) support progress reporting
1120  this->InvokeEvent( itk::StartEvent() );
1121  for ( unsigned int i = 0; i < 100; ++i )
1122  {
1124  }
1125 
1126  // Calculate statistics (separate filter)
1127  typedef itk::ExtendedStatisticsImageFilter< ImageType > StatisticsFilterType;
1128  typename StatisticsFilterType::Pointer statisticsFilter = StatisticsFilterType::New();
1129  statisticsFilter->SetInput( image );
1130  statisticsFilter->SetBinSize( 100 );
1131  statisticsFilter->SetCoordinateTolerance( 0.001 );
1132  statisticsFilter->SetDirectionTolerance( 0.001 );
1133 
1134  unsigned long observerTag = statisticsFilter->AddObserver( itk::ProgressEvent(), progressListener );
1135  try
1136  {
1137  statisticsFilter->Update();
1138  }
1139  catch (const itk::ExceptionObject& e)
1140  {
1141  mitkThrow() << "Image statistics calculation failed due to following ITK Exception: \n " << e.what();
1142  }
1143  catch( const std::exception& e )
1144  {
1145  //mitkThrow() << "Image statistics calculation failed due to following ITK Exception: \n " << e.what();
1146  }
1147 
1148  statisticsFilter->RemoveObserver( observerTag );
1149  this->InvokeEvent( itk::EndEvent() );
1150 
1151  // Calculate minimum and maximum
1152  typedef itk::MinimumMaximumImageCalculator< ImageType > MinMaxFilterType;
1153  typename MinMaxFilterType::Pointer minMaxFilter = MinMaxFilterType::New();
1154  minMaxFilter->SetImage( image );
1155  unsigned long observerTag2 = minMaxFilter->AddObserver( itk::ProgressEvent(), progressListener );
1156  minMaxFilter->Compute();
1157  minMaxFilter->RemoveObserver( observerTag2 );
1158  this->InvokeEvent( itk::EndEvent() );
1159 
1160  Statistics statistics;
1161  statistics.Reset();
1162  statistics.SetLabel(1);
1163  statistics.SetN(image->GetBufferedRegion().GetNumberOfPixels());
1164  statistics.SetMin(statisticsFilter->GetMinimum());
1165  statistics.SetMax(statisticsFilter->GetMaximum());
1166  statistics.SetMean(statisticsFilter->GetMean());
1167  statistics.SetMedian(statisticsFilter->GetMedian());
1168  statistics.SetVariance(statisticsFilter->GetVariance());
1169  statistics.SetSkewness(statisticsFilter->GetSkewness());
1170  statistics.SetKurtosis(statisticsFilter->GetKurtosis());
1171  statistics.SetUniformity( statisticsFilter->GetUniformity());
1172  statistics.SetEntropy( statisticsFilter->GetEntropy());
1173  statistics.SetUPP( statisticsFilter->GetUPP());
1174  statistics.SetMPP( statisticsFilter->GetMPP());
1175  statistics.SetSigma(statisticsFilter->GetSigma());
1176  statistics.SetRMS(sqrt( statistics.GetMean() * statistics.GetMean() + statistics.GetSigma() * statistics.GetSigma() ));
1177 
1178  statistics.GetMinIndex().set_size(image->GetImageDimension());
1179  statistics.GetMaxIndex().set_size(image->GetImageDimension());
1180 
1181  vnl_vector<int> tmpMaxIndex;
1182  vnl_vector<int> tmpMinIndex;
1183 
1184  tmpMaxIndex.set_size(image->GetImageDimension() );
1185  tmpMinIndex.set_size(image->GetImageDimension() );
1186 
1187  for (unsigned int i=0; i<statistics.GetMaxIndex().size(); i++)
1188  {
1189  tmpMaxIndex[i] = minMaxFilter->GetIndexOfMaximum()[i];
1190  tmpMinIndex[i] = minMaxFilter->GetIndexOfMinimum()[i];
1191  }
1192 
1193  statistics.SetMinIndex(tmpMaxIndex);
1194  statistics.SetMinIndex(tmpMinIndex);
1195 
1196  if( IsHotspotCalculated() && VImageDimension == 3 )
1197  {
1198  typedef itk::Image< unsigned short, VImageDimension > MaskImageType;
1199  typename MaskImageType::Pointer nullMask;
1200  bool isHotspotDefined(false);
1201  Statistics hotspotStatistics = this->CalculateHotspotStatistics(image, nullMask.GetPointer(), m_HotspotRadiusInMM, isHotspotDefined, 0 );
1202  if (isHotspotDefined)
1203  {
1204  statistics.SetHasHotspotStatistics(true);
1205  statistics.GetHotspotStatistics() = hotspotStatistics;
1206  }
1207  else
1208  {
1209  statistics.SetHasHotspotStatistics(false);
1210  }
1211 
1212  if(statistics.GetHotspotStatistics().HasHotspotStatistics() )
1213  {
1214  MITK_DEBUG << "Hotspot statistics available";
1215  statistics.SetHotspotIndex(hotspotStatistics.GetHotspotIndex());
1216  }
1217  else
1218  {
1219  MITK_ERROR << "No hotspot statistics available!";
1220  }
1221  }
1222 
1223  statisticsContainer->push_back( statistics );
1224 
1225  // Calculate histogram
1226  // calculate bin size or number of bins
1227  unsigned int numberOfBins = 200; // default number of bins
1228  if (m_UseDefaultBinSize)
1229  {
1230  m_HistogramBinSize = std::ceil( (statistics.GetMax() - statistics.GetMin() + 1)/numberOfBins );
1231  }
1232  else
1233  {
1234  numberOfBins = calcNumberOfBins(statistics.GetMin(), statistics.GetMax());
1235  }
1236  typename HistogramGeneratorType::Pointer histogramGenerator = HistogramGeneratorType::New();
1237  histogramGenerator->SetInput( image );
1238  histogramGenerator->SetMarginalScale( 100 );
1239  histogramGenerator->SetNumberOfBins( numberOfBins );
1240  histogramGenerator->SetHistogramMin( statistics.GetMin() );
1241  histogramGenerator->SetHistogramMax( statistics.GetMax() );
1242  histogramGenerator->Compute();
1243  histogramContainer->push_back( histogramGenerator->GetOutput() );
1244  }
1245 
1246  template < typename TPixel, unsigned int VImageDimension >
1248  const itk::Image< TPixel, VImageDimension > *image,
1249  itk::Image< unsigned short, VImageDimension > *maskImage )
1250  {
1251  typedef itk::Image< TPixel, VImageDimension > ImageType;
1252  typedef itk::Image< unsigned short, VImageDimension > MaskImageType;
1253 
1254  itk::ImageRegionIterator<MaskImageType>
1255  itmask(maskImage, maskImage->GetLargestPossibleRegion());
1256  itk::ImageRegionConstIterator<ImageType>
1257  itimage(image, image->GetLargestPossibleRegion());
1258 
1259  itmask.GoToBegin();
1260  itimage.GoToBegin();
1261 
1262  while( !itmask.IsAtEnd() )
1263  {
1264  if(m_IgnorePixelValue == itimage.Get())
1265  {
1266  itmask.Set(0);
1267  }
1268 
1269  ++itmask;
1270  ++itimage;
1271  }
1272  }
1273 
1274  template < typename TPixel, unsigned int VImageDimension >
1275  void ImageStatisticsCalculator::InternalCalculateStatisticsMasked(
1276  const itk::Image< TPixel, VImageDimension > *image,
1277  itk::Image< unsigned short, VImageDimension > *maskImage,
1278  StatisticsContainer* statisticsContainer,
1279  HistogramContainer* histogramContainer )
1280  {
1281  typedef itk::Image< TPixel, VImageDimension > ImageType;
1282  typedef itk::Image< unsigned short, VImageDimension > MaskImageType;
1283  typedef typename ImageType::IndexType IndexType;
1284  typedef typename ImageType::PointType PointType;
1285  typedef typename ImageType::SpacingType SpacingType;
1286  typedef typename ImageType::Pointer ImagePointer;
1288  typedef itk::ChangeInformationImageFilter< MaskImageType > ChangeInformationFilterType;
1289  typedef itk::ExtractImageFilter< ImageType, ImageType > ExtractImageFilterType;
1290 
1291  statisticsContainer->clear();
1292  histogramContainer->clear();
1293 
1294  // Make sure that mask is set
1295  if ( maskImage == nullptr )
1296  {
1297  itkExceptionMacro( << "Mask image needs to be set!" );
1298  }
1299 
1300  // Make sure that spacing of mask and image are the same
1301  //SpacingType imageSpacing = image->GetSpacing();
1302  //SpacingType maskSpacing = maskImage->GetSpacing();
1303  //PointType zeroPoint; zeroPoint.Fill( 0.0 );
1304  //if ( (zeroPoint + imageSpacing).SquaredEuclideanDistanceTo( (zeroPoint + maskSpacing) ) > mitk::eps )
1305  //{
1306  // itkExceptionMacro( << "Mask needs to have same spacing as image! (Image spacing: " << imageSpacing << "; Mask spacing: " << maskSpacing << ")" );
1307  //}
1308  // Make sure that orientation of mask and image are the same
1309  typedef typename ImageType::DirectionType DirectionType;
1310  DirectionType imageDirection = image->GetDirection();
1311  DirectionType maskDirection = maskImage->GetDirection();
1312  for( int i = 0; i < imageDirection.ColumnDimensions; ++i )
1313  {
1314  for( int j = 0; j < imageDirection.ColumnDimensions; ++j )
1315  {
1316  double differenceDirection = imageDirection[i][j] - maskDirection[i][j];
1317  if ( fabs( differenceDirection ) > mitk::eps )
1318  {
1319  double differenceDirection = imageDirection[i][j] - maskDirection[i][j];
1320  if ( fabs( differenceDirection ) > 0.001 /*mitk::eps*/ ) // TODO: temp fix (bug 17121)
1321  {
1322  itkExceptionMacro( << "Mask needs to have same direction as image! (Image direction: " << imageDirection << "; Mask direction: " << maskDirection << ")" );
1323  }
1324  }
1325  }
1326  }
1327  // Make sure that the voxels of mask and image are correctly "aligned", i.e., voxel boundaries are the same in both images
1328  PointType imageOrigin = image->GetOrigin();
1329  PointType maskOrigin = maskImage->GetOrigin();
1330  long offset[ImageType::ImageDimension];
1331 
1332  typedef itk::ContinuousIndex<double, VImageDimension> ContinousIndexType;
1333  ContinousIndexType maskOriginContinousIndex, imageOriginContinousIndex;
1334 
1335  image->TransformPhysicalPointToContinuousIndex(maskOrigin, maskOriginContinousIndex);
1336  image->TransformPhysicalPointToContinuousIndex(imageOrigin, imageOriginContinousIndex);
1337 
1338  for ( unsigned int i = 0; i < ImageType::ImageDimension; ++i )
1339  {
1340  double misalignment = maskOriginContinousIndex[i] - floor( maskOriginContinousIndex[i] + 0.5 );
1341  if ( fabs( misalignment ) > mitk::eps )
1342  {
1343  itkWarningMacro( << "Pixels/voxels of mask and image are not sufficiently aligned! (Misalignment: " << misalignment << ")" );
1344  }
1345 
1346  double indexCoordDistance = maskOriginContinousIndex[i] - imageOriginContinousIndex[i];
1347  offset[i] = int( indexCoordDistance + image->GetBufferedRegion().GetIndex()[i] + 0.5 );
1348  }
1349 
1350  // Adapt the origin and region (index/size) of the mask so that the origin of both are the same
1351  typename ChangeInformationFilterType::Pointer adaptMaskFilter;
1352  adaptMaskFilter = ChangeInformationFilterType::New();
1353  adaptMaskFilter->ChangeOriginOn();
1354  adaptMaskFilter->ChangeRegionOn();
1355  adaptMaskFilter->SetInput( maskImage );
1356  adaptMaskFilter->SetOutputOrigin( image->GetOrigin() );
1357  adaptMaskFilter->SetOutputOffset( offset );
1358  adaptMaskFilter->SetCoordinateTolerance( 0.001 );
1359  adaptMaskFilter->SetDirectionTolerance( 0.001 );
1360 
1361 
1362  typename MaskImageType::Pointer adaptedMaskImage;
1363  try
1364  {
1365  adaptMaskFilter->Update();
1366  adaptedMaskImage = adaptMaskFilter->GetOutput();
1367  }
1368  catch( const itk::ExceptionObject &e)
1369  {
1370  mitkThrow() << "Attempt to adapt shifted origin of the mask image failed due to ITK Exception: \n" << e.what();
1371  }
1372  catch( const std::exception& e )
1373  {
1374  //mitkThrow() << "Image statistics calculation failed due to following ITK Exception: \n " << e.what();
1375  }
1376 
1377 
1378  // Make sure that mask region is contained within image region
1379  if ( adaptedMaskImage.IsNotNull() &&
1380  !image->GetLargestPossibleRegion().IsInside( adaptedMaskImage->GetLargestPossibleRegion() ) )
1381  {
1382  itkWarningMacro( << "Mask region needs to be inside of image region! (Image region: "
1383  << image->GetLargestPossibleRegion() << "; Mask region: " << adaptedMaskImage->GetLargestPossibleRegion() << ")" );
1384  }
1385 
1386 
1387  // If mask region is smaller than image region, extract the sub-sampled region from the original image
1388  typename ImageType::SizeType imageSize = image->GetBufferedRegion().GetSize();
1389  typename ImageType::SizeType maskSize = maskImage->GetBufferedRegion().GetSize();
1390  bool maskSmallerImage = false;
1391  for ( unsigned int i = 0; i < ImageType::ImageDimension; ++i )
1392  {
1393  if ( maskSize[i] < imageSize[i] )
1394  {
1395  maskSmallerImage = true;
1396  }
1397  }
1398 
1399  typename ImageType::ConstPointer adaptedImage;
1400  if ( maskSmallerImage )
1401  {
1402  typename ExtractImageFilterType::Pointer extractImageFilter = ExtractImageFilterType::New();
1403  extractImageFilter->SetInput( image );
1404  extractImageFilter->SetExtractionRegion( adaptedMaskImage->GetBufferedRegion() );
1405  extractImageFilter->SetCoordinateTolerance( 0.001 );
1406  extractImageFilter->SetDirectionTolerance( 0.001 );
1407  extractImageFilter->Update();
1408  adaptedImage = extractImageFilter->GetOutput();
1409  }
1410  else
1411  {
1412  adaptedImage = image;
1413  }
1414 
1415  // Initialize Filter
1416  typedef itk::StatisticsImageFilter< ImageType > StatisticsFilterType;
1417  typename StatisticsFilterType::Pointer statisticsFilter = StatisticsFilterType::New();
1418  statisticsFilter->SetInput( adaptedImage );
1419 
1420  try
1421  {
1422  statisticsFilter->Update();
1423  }
1424  catch( const itk::ExceptionObject& e)
1425  {
1426  mitkThrow() << "Image statistics initialization computation failed with ITK Exception: \n " << e.what();
1427  }
1428  catch( const std::exception& e )
1429  {
1430  //mitkThrow() << "Image statistics calculation failed due to following ITK Exception: \n " << e.what();
1431  }
1432 
1433  // Calculate bin size or number of bins
1434  unsigned int numberOfBins = m_HistogramBinSize; // default number of bins
1435  double maximum = 0.0;
1436  double minimum = 0.0;
1437 
1439  {
1440  maximum = statisticsFilter->GetMaximum();
1441  minimum = statisticsFilter->GetMinimum();
1442 
1443  if (m_UseDefaultBinSize)
1444  {
1445  m_HistogramBinSize = std::ceil( static_cast<double>((statisticsFilter->GetMaximum() - statisticsFilter->GetMinimum() + 1)/numberOfBins) );
1446  }
1447  else
1448  {
1449  numberOfBins = calcNumberOfBins(statisticsFilter->GetMinimum(), statisticsFilter->GetMaximum());
1450  }
1451  }
1452  else
1453  {
1454  double sig = 0.0;
1455  int counter = 0;
1456 
1457  //Find the min and max values for the Roi to set the range for the histogram
1458  GetMinAndMaxValue( minimum, maximum, counter, sig, image, maskImage);
1459 // numberOfBins = maximum - minimum;
1460 // if(maximum - minimum <= 10)
1461 // {
1462 // numberOfBins = 100;
1463 // }
1464  }
1465 
1466 
1467  typename LabelStatisticsFilterType::Pointer labelStatisticsFilter = LabelStatisticsFilterType::New();
1468  labelStatisticsFilter->SetInput( adaptedImage );
1469  labelStatisticsFilter->SetLabelInput( adaptedMaskImage );
1470  labelStatisticsFilter->SetCoordinateTolerance( 0.001 );
1471  labelStatisticsFilter->SetDirectionTolerance( 0.001 );
1472  labelStatisticsFilter->UseHistogramsOn();
1473  labelStatisticsFilter->SetHistogramParameters( numberOfBins, floor(minimum), ceil(maximum) ); //statisticsFilter->GetMinimum() statisticsFilter->GetMaximum()
1474 
1475  // Add progress listening
1476  typedef itk::SimpleMemberCommand< ImageStatisticsCalculator > ITKCommandType;
1477  ITKCommandType::Pointer progressListener;
1478  progressListener = ITKCommandType::New();
1479  progressListener->SetCallbackFunction( this,
1481  unsigned long observerTag = labelStatisticsFilter->AddObserver(
1482  itk::ProgressEvent(), progressListener );
1483 
1484  // Execute filter
1485  this->InvokeEvent( itk::StartEvent() );
1486 
1487  // Make sure that only the mask region is considered (otherwise, if the mask region is smaller
1488  // than the image region, the Update() would result in an exception).
1489  labelStatisticsFilter->GetOutput()->SetRequestedRegion( adaptedMaskImage->GetLargestPossibleRegion() );
1490 
1491  // Execute the filter
1492  try
1493  {
1494  labelStatisticsFilter->Update();
1495  }
1496  catch( const itk::ExceptionObject& e)
1497  {
1498  mitkThrow() << "Image statistics calculation failed due to following ITK Exception: \n " << e.what();
1499  }
1500  catch( const std::exception& e )
1501  {
1502  //mitkThrow() << "Image statistics calculation failed due to following ITK Exception: \n " << e.what();
1503  }
1504 
1505  this->InvokeEvent( itk::EndEvent() );
1506 
1507  if( observerTag )
1508  labelStatisticsFilter->RemoveObserver( observerTag );
1509 
1510  // Find all relevant labels of mask (other than 0)
1511  std::list< int > relevantLabels = labelStatisticsFilter->GetRelevantLabels();
1512  unsigned int i;
1513 
1514  if ( labelStatisticsFilter->GetMaskingNonEmpty() )
1515  {
1516  std::list< int >::iterator it;
1517  for ( it = relevantLabels.begin(), i = 0;
1518  it != relevantLabels.end();
1519  ++it, ++i )
1520  {
1521  Statistics statistics; // restore previous code
1522  labelStatisticsFilter->GetHistogram(*it) ;
1523  histogramContainer->push_back( HistogramType::ConstPointer( labelStatisticsFilter->GetHistogram( (*it) ) ) );
1524 
1525  statistics.SetLabel (*it);
1526  statistics.SetN(labelStatisticsFilter->GetCount( *it ));
1527  statistics.SetMin(labelStatisticsFilter->GetMinimum( *it ));
1528  statistics.SetMax(labelStatisticsFilter->GetMaximum( *it ));
1529  statistics.SetMean(labelStatisticsFilter->GetMean( *it ));
1530  statistics.SetMedian(labelStatisticsFilter->GetMedian( *it));
1531  statistics.SetMedian(labelStatisticsFilter->GetMedian( *it ));
1532  statistics.SetVariance(labelStatisticsFilter->GetVariance( *it ));
1533  statistics.SetSigma(labelStatisticsFilter->GetSigma( *it ));
1534  statistics.SetSkewness(labelStatisticsFilter->GetSkewness( *it ));
1535  statistics.SetKurtosis(labelStatisticsFilter->GetKurtosis( *it ));
1536  statistics.SetUniformity( labelStatisticsFilter->GetUniformity( *it ));
1537  statistics.SetEntropy( labelStatisticsFilter->GetEntropy( *it ));
1538  statistics.SetUPP( labelStatisticsFilter->GetUPP( *it));
1539  statistics.SetMPP( labelStatisticsFilter->GetMPP( *it));
1540  statistics.SetRMS(sqrt( statistics.GetMean() * statistics.GetMean()
1541  + statistics.GetSigma() * statistics.GetSigma() ));
1542 
1543  // restrict image to mask area for min/max index calculation
1544  typedef itk::MaskImageFilter< ImageType, MaskImageType, ImageType > MaskImageFilterType;
1546  bool isMinAndMaxSameValue = (statistics.GetMin() == statistics.GetMax());
1547  // bug 17962: following is a workaround for the case when min and max are the same, we can probably find a nicer way here
1548  double outsideValue = (isMinAndMaxSameValue ? (statistics.GetMax()/2) : (statistics.GetMin()+statistics.GetMax())/2);
1549  masker->SetOutsideValue( outsideValue );
1550  masker->SetInput1(adaptedImage);
1551  masker->SetInput2(adaptedMaskImage);
1552  masker->SetCoordinateTolerance( 0.001 );
1553  masker->SetDirectionTolerance( 0.001 );
1554  masker->Update();
1555  // get index of minimum and maximum
1556  typedef itk::MinimumMaximumImageCalculator< ImageType > MinMaxFilterType;
1557  typename MinMaxFilterType::Pointer minMaxFilter = MinMaxFilterType::New();
1558  minMaxFilter->SetImage( masker->GetOutput() );
1559  unsigned long observerTag2 = minMaxFilter->AddObserver( itk::ProgressEvent(), progressListener );
1560  minMaxFilter->Compute();
1561  minMaxFilter->RemoveObserver( observerTag2 );
1562  this->InvokeEvent( itk::EndEvent() );
1563 
1564  typename MinMaxFilterType::IndexType tempMaxIndex = minMaxFilter->GetIndexOfMaximum();
1565  // bug 17962: following is a workaround for the case when min and max are the same, we can probably find a nicer way here
1566  typename MinMaxFilterType::IndexType tempMinIndex =
1567  (isMinAndMaxSameValue ? minMaxFilter->GetIndexOfMaximum() : minMaxFilter->GetIndexOfMinimum());
1568 
1569  // FIX BUG 14644
1570  //If a PlanarFigure is used for segmentation the
1571  //adaptedImage is a single slice (2D). Adding the
1572  // 3. dimension.
1573 
1574  vnl_vector<int> maxIndex;
1575  vnl_vector<int> minIndex;
1576  maxIndex.set_size(m_Image->GetDimension());
1577  minIndex.set_size(m_Image->GetDimension());
1578 
1579  if (m_MaskingMode == MASKING_MODE_PLANARFIGURE && m_Image->GetDimension()==3)
1580  {
1581  maxIndex[m_PlanarFigureCoordinate0] = tempMaxIndex[0];
1582  maxIndex[m_PlanarFigureCoordinate1] = tempMaxIndex[1];
1584 
1585  minIndex[m_PlanarFigureCoordinate0] = tempMinIndex[0] ;
1586  minIndex[m_PlanarFigureCoordinate1] = tempMinIndex[1];
1588  } else
1589  {
1590  for (unsigned int i = 0; i<maxIndex.size(); i++)
1591  {
1592  maxIndex[i] = tempMaxIndex[i];
1593  minIndex[i] = tempMinIndex[i];
1594  }
1595  }
1596  // FIX END
1597  statistics.SetMaxIndex(maxIndex);
1598  statistics.SetMinIndex(minIndex);
1599  /*****************************************************Calculate Hotspot Statistics**********************************************/
1600 
1601  if(IsHotspotCalculated() && VImageDimension == 3)
1602  {
1603  bool isDefined(false);
1604  Statistics hotspotStatistics = CalculateHotspotStatistics(adaptedImage.GetPointer(), adaptedMaskImage.GetPointer(),GetHotspotRadiusInMM(), isDefined, *it);
1605  statistics.GetHotspotStatistics() = hotspotStatistics;
1606  if(statistics.GetHotspotStatistics().HasHotspotStatistics())
1607  {
1608  MITK_DEBUG << "Hotspot statistics available";
1609  statistics.SetHotspotIndex( hotspotStatistics.GetHotspotIndex() );
1610  }
1611  else
1612  {
1613  MITK_ERROR << "No hotspot statistics available!";
1614  }
1615  }
1616  statisticsContainer->push_back( statistics );
1617  }
1618  }
1619  else
1620  {
1621  histogramContainer->push_back( HistogramType::ConstPointer( m_EmptyHistogram ) );
1622  statisticsContainer->push_back( Statistics() );
1623  }
1624  }
1625 
1626 
1627  template <typename TPixel, unsigned int VImageDimension >
1630  const itk::Image<TPixel, VImageDimension> *inputImage,
1631  itk::Image<unsigned short, VImageDimension> *maskImage,
1632  double neccessaryDistanceToImageBorderInMM,
1633  unsigned int label)
1634  {
1635  typedef itk::Image< TPixel, VImageDimension > ImageType;
1636  typedef itk::Image< unsigned short, VImageDimension > MaskImageType;
1637 
1638  typedef itk::ImageRegionConstIteratorWithIndex<MaskImageType> MaskImageIteratorType;
1639  typedef itk::ImageRegionConstIteratorWithIndex<ImageType> InputImageIndexIteratorType;
1640 
1641  typename ImageType::SpacingType spacing = inputImage->GetSpacing();
1642 
1643  ImageExtrema minMax;
1644  minMax.Defined = false;
1645  minMax.MaxIndex.set_size(VImageDimension);
1646  minMax.MaxIndex.set_size(VImageDimension);
1647 
1648  typename ImageType::RegionType allowedExtremaRegion = inputImage->GetLargestPossibleRegion();
1649 
1650  bool keepDistanceToImageBorders( neccessaryDistanceToImageBorderInMM > 0 );
1651  if (keepDistanceToImageBorders)
1652  {
1653  long distanceInPixels[VImageDimension];
1654  for(unsigned short dimension = 0; dimension < VImageDimension; ++dimension)
1655  {
1656  // To confirm that the whole hotspot is inside the image we have to keep a specific distance to the image-borders, which is as long as
1657  // the radius. To get the amount of indices we divide the radius by spacing and add 0.5 because voxels are center based:
1658  // For example with a radius of 2.2 and a spacing of 1 two indices are enough because 2.2 / 1 + 0.5 = 2.7 => 2.
1659  // But with a radius of 2.7 we need 3 indices because 2.7 / 1 + 0.5 = 3.2 => 3
1660  distanceInPixels[dimension] = int( neccessaryDistanceToImageBorderInMM / spacing[dimension] + 0.5);
1661  }
1662 
1663  allowedExtremaRegion.ShrinkByRadius(distanceInPixels);
1664  }
1665 
1666  InputImageIndexIteratorType imageIndexIt(inputImage, allowedExtremaRegion);
1667 
1668  float maxValue = itk::NumericTraits<float>::min();
1669  float minValue = itk::NumericTraits<float>::max();
1670 
1671  typename ImageType::IndexType maxIndex;
1672  typename ImageType::IndexType minIndex;
1673 
1674  for(unsigned short i = 0; i < VImageDimension; ++i)
1675  {
1676  maxIndex[i] = 0;
1677  minIndex[i] = 0;
1678  }
1679 
1680  if (maskImage != nullptr)
1681  {
1682  MaskImageIteratorType maskIt(maskImage, maskImage->GetLargestPossibleRegion());
1683  typename ImageType::IndexType imageIndex;
1684  typename ImageType::PointType worldPosition;
1685  typename ImageType::IndexType maskIndex;
1686 
1687  for(maskIt.GoToBegin(); !maskIt.IsAtEnd(); ++maskIt)
1688  {
1689  imageIndex = maskIndex = maskIt.GetIndex();
1690 
1691  if(maskIt.Get() == label)
1692  {
1693  if( allowedExtremaRegion.IsInside(imageIndex) )
1694  {
1695  imageIndexIt.SetIndex( imageIndex );
1696  double value = imageIndexIt.Get();
1697  minMax.Defined = true;
1698 
1699  //Calculate minimum, maximum and corresponding index-values
1700  if( value > maxValue )
1701  {
1702  maxIndex = imageIndexIt.GetIndex();
1703  maxValue = value;
1704  }
1705 
1706  if(value < minValue )
1707  {
1708  minIndex = imageIndexIt.GetIndex();
1709  minValue = value;
1710  }
1711  }
1712  }
1713  }
1714  }
1715  else
1716  {
1717  for(imageIndexIt.GoToBegin(); !imageIndexIt.IsAtEnd(); ++imageIndexIt)
1718  {
1719  double value = imageIndexIt.Get();
1720  minMax.Defined = true;
1721 
1722  //Calculate minimum, maximum and corresponding index-values
1723  if( value > maxValue )
1724  {
1725  maxIndex = imageIndexIt.GetIndex();
1726  maxValue = value;
1727  }
1728 
1729  if(value < minValue )
1730  {
1731  minIndex = imageIndexIt.GetIndex();
1732  minValue = value;
1733  }
1734  }
1735  }
1736 
1737  minMax.MaxIndex.set_size(VImageDimension);
1738  minMax.MinIndex.set_size(VImageDimension);
1739 
1740  for(unsigned int i = 0; i < minMax.MaxIndex.size(); ++i)
1741  {
1742  minMax.MaxIndex[i] = maxIndex[i];
1743  }
1744 
1745  for(unsigned int i = 0; i < minMax.MinIndex.size(); ++i)
1746  {
1747  minMax.MinIndex[i] = minIndex[i];
1748  }
1749 
1750  minMax.Max = maxValue;
1751  minMax.Min = minValue;
1752 
1753  return minMax;
1754  }
1755 
1756  template <unsigned int VImageDimension>
1757  itk::Size<VImageDimension>
1759  ::CalculateConvolutionKernelSize(double spacing[VImageDimension], double radiusInMM)
1760  {
1761  typedef itk::Image< float, VImageDimension > KernelImageType;
1762  typedef typename KernelImageType::SizeType SizeType;
1763  SizeType maskSize;
1764 
1765  for(unsigned int i = 0; i < VImageDimension; ++i)
1766  {
1767  maskSize[i] = static_cast<int>( 2 * radiusInMM / spacing[i]);
1768 
1769  // We always want an uneven size to have a clear center point in the convolution mask
1770  if(maskSize[i] % 2 == 0 )
1771  {
1772  ++maskSize[i];
1773  }
1774  }
1775  return maskSize;
1776  }
1777 
1778  template <unsigned int VImageDimension>
1781  ::GenerateHotspotSearchConvolutionKernel(double mmPerPixel[VImageDimension], double radiusInMM)
1782  {
1783  std::stringstream ss;
1784  for (unsigned int i = 0; i < VImageDimension; ++i)
1785  {
1786  ss << mmPerPixel[i];
1787  if (i < VImageDimension -1)
1788  ss << ",";
1789  }
1790  MITK_DEBUG << "Update convolution kernel for spacing (" << ss.str() << ") and radius " << radiusInMM << "mm";
1791 
1792 
1793  double radiusInMMSquared = radiusInMM * radiusInMM;
1794  typedef itk::Image< float, VImageDimension > KernelImageType;
1795  typename KernelImageType::Pointer convolutionKernel = KernelImageType::New();
1796 
1797  // Calculate size and allocate mask image
1798  typedef typename KernelImageType::SizeType SizeType;
1799  SizeType maskSize = this->CalculateConvolutionKernelSize<VImageDimension>(mmPerPixel, radiusInMM);
1800 
1801  Point3D convolutionMaskCenterIndex; convolutionMaskCenterIndex.Fill(0.0);
1802  for(unsigned int i = 0; i < VImageDimension; ++i)
1803  {
1804  convolutionMaskCenterIndex[i] = 0.5 * (double)(maskSize[i]-1);
1805  }
1806 
1807  typedef typename KernelImageType::IndexType IndexType;
1808  IndexType maskIndex;
1809  maskIndex.Fill(0);
1810 
1811  typedef typename KernelImageType::RegionType RegionType;
1812  RegionType maskRegion;
1813  maskRegion.SetSize(maskSize);
1814  maskRegion.SetIndex(maskIndex);
1815 
1816  convolutionKernel->SetRegions(maskRegion);
1817  convolutionKernel->SetSpacing(mmPerPixel);
1818  convolutionKernel->Allocate();
1819 
1820  // Fill mask image values by subsampling the image grid
1821  typedef itk::ImageRegionIteratorWithIndex<KernelImageType> MaskIteratorType;
1822  MaskIteratorType maskIt(convolutionKernel,maskRegion);
1823 
1824  int numberOfSubVoxelsPerDimension = 2; // per dimension!
1825  int numberOfSubVoxels = ::pow( static_cast<float>(numberOfSubVoxelsPerDimension), static_cast<float>(VImageDimension) );
1826  double subVoxelSizeInPixels = 1.0 / (double)numberOfSubVoxelsPerDimension;
1827  double valueOfOneSubVoxel = 1.0 / (double)numberOfSubVoxels;
1828  double maskValue = 0.0;
1829  Point3D subVoxelIndexPosition;
1830  double distanceSquared = 0.0;
1831 
1832  typedef itk::ContinuousIndex<double, VImageDimension> ContinuousIndexType;
1833  for(maskIt.GoToBegin(); !maskIt.IsAtEnd(); ++maskIt)
1834  {
1835  ContinuousIndexType indexPoint(maskIt.GetIndex());
1836  Point3D voxelPosition;
1837  for (unsigned int dimension = 0; dimension < VImageDimension; ++dimension)
1838  {
1839  voxelPosition[dimension] = indexPoint[dimension];
1840  }
1841 
1842  maskValue = 0.0;
1843  Vector3D subVoxelOffset; subVoxelOffset.Fill(0.0);
1844  // iterate sub-voxels by iterating all possible offsets
1845  for (subVoxelOffset[0] = -0.5 + subVoxelSizeInPixels / 2.0;
1846  subVoxelOffset[0] < +0.5;
1847  subVoxelOffset[0] += subVoxelSizeInPixels)
1848  {
1849  for (subVoxelOffset[1] = -0.5 + subVoxelSizeInPixels / 2.0;
1850  subVoxelOffset[1] < +0.5;
1851  subVoxelOffset[1] += subVoxelSizeInPixels)
1852  {
1853  for (subVoxelOffset[2] = -0.5 + subVoxelSizeInPixels / 2.0;
1854  subVoxelOffset[2] < +0.5;
1855  subVoxelOffset[2] += subVoxelSizeInPixels)
1856  {
1857  subVoxelIndexPosition = voxelPosition + subVoxelOffset; // this COULD be integrated into the for-loops if neccessary (add voxelPosition to initializer and end condition)
1858  distanceSquared =
1859  (subVoxelIndexPosition[0]-convolutionMaskCenterIndex[0]) * mmPerPixel[0] * (subVoxelIndexPosition[0]-convolutionMaskCenterIndex[0]) * mmPerPixel[0]
1860  + (subVoxelIndexPosition[1]-convolutionMaskCenterIndex[1]) * mmPerPixel[1] * (subVoxelIndexPosition[1]-convolutionMaskCenterIndex[1]) * mmPerPixel[1]
1861  + (subVoxelIndexPosition[2]-convolutionMaskCenterIndex[2]) * mmPerPixel[2] * (subVoxelIndexPosition[2]-convolutionMaskCenterIndex[2]) * mmPerPixel[2];
1862 
1863  if (distanceSquared <= radiusInMMSquared)
1864  {
1865  maskValue += valueOfOneSubVoxel;
1866  }
1867  }
1868  }
1869  }
1870  maskIt.Set( maskValue );
1871  }
1872 
1873  return convolutionKernel;
1874  }
1875 
1876  template <typename TPixel, unsigned int VImageDimension>
1878  ImageStatisticsCalculator::GenerateConvolutionImage( const itk::Image<TPixel, VImageDimension>* inputImage )
1879  {
1880  double mmPerPixel[VImageDimension];
1881  for (unsigned int dimension = 0; dimension < VImageDimension; ++dimension)
1882  {
1883  mmPerPixel[dimension] = inputImage->GetSpacing()[dimension];
1884  }
1885 
1886  // update convolution kernel
1887  typedef itk::Image< float, VImageDimension > KernelImageType;
1888  typename KernelImageType::Pointer convolutionKernel = this->GenerateHotspotSearchConvolutionKernel<VImageDimension>(mmPerPixel, m_HotspotRadiusInMM);
1889 
1890  // update convolution image
1891  typedef itk::Image< TPixel, VImageDimension > InputImageType;
1892  typedef itk::Image< TPixel, VImageDimension > ConvolutionImageType;
1893  typedef itk::FFTConvolutionImageFilter<InputImageType,
1894  KernelImageType,
1895  ConvolutionImageType> ConvolutionFilterType;
1896 
1897  typename ConvolutionFilterType::Pointer convolutionFilter = ConvolutionFilterType::New();
1898  typedef itk::ConstantBoundaryCondition<InputImageType, InputImageType> BoundaryConditionType;
1899  BoundaryConditionType boundaryCondition;
1900  boundaryCondition.SetConstant(0.0);
1901 
1903  {
1904  // overwrite default boundary condition
1905  convolutionFilter->SetBoundaryCondition(&boundaryCondition);
1906  }
1907 
1908  convolutionFilter->SetInput(inputImage);
1909  convolutionFilter->SetKernelImage(convolutionKernel);
1910  convolutionFilter->SetNormalize(true);
1911  MITK_DEBUG << "Update Convolution image for hotspot search";
1912  convolutionFilter->UpdateLargestPossibleRegion();
1913 
1914  typename ConvolutionImageType::Pointer convolutionImage = convolutionFilter->GetOutput();
1915  convolutionImage->SetSpacing( inputImage->GetSpacing() ); // only workaround because convolution filter seems to ignore spacing of input image
1916 
1918  return convolutionImage;
1919  }
1920 
1921  template < typename TPixel, unsigned int VImageDimension>
1922  void
1924  ::FillHotspotMaskPixels( itk::Image<TPixel, VImageDimension>* maskImage,
1925  itk::Point<double, VImageDimension> sphereCenter,
1926  double sphereRadiusInMM)
1927  {
1928  typedef itk::Image< TPixel, VImageDimension > MaskImageType;
1929  typedef itk::ImageRegionIteratorWithIndex<MaskImageType> MaskImageIteratorType;
1930 
1931  MaskImageIteratorType maskIt(maskImage, maskImage->GetLargestPossibleRegion());
1932 
1933  typename MaskImageType::IndexType maskIndex;
1934  typename MaskImageType::PointType worldPosition;
1935 
1936  for(maskIt.GoToBegin(); !maskIt.IsAtEnd(); ++maskIt)
1937  {
1938  maskIndex = maskIt.GetIndex();
1939  maskImage->TransformIndexToPhysicalPoint(maskIndex, worldPosition);
1940  maskIt.Set( worldPosition.EuclideanDistanceTo(sphereCenter) <= sphereRadiusInMM ? 1 : 0 );
1941  }
1942  }
1943 
1944  template < typename TPixel, unsigned int VImageDimension>
1947  const itk::Image<TPixel, VImageDimension>* inputImage,
1948  itk::Image<unsigned short, VImageDimension>* maskImage,
1949  double radiusInMM,
1950  bool& isHotspotDefined,
1951  unsigned int label)
1952  {
1953  // get convolution image (updated in GenerateConvolutionImage())
1954  typedef itk::Image< TPixel, VImageDimension > InputImageType;
1955  typedef itk::Image< TPixel, VImageDimension > ConvolutionImageType;
1956  typedef itk::Image< float, VImageDimension > KernelImageType;
1957  typedef itk::Image< unsigned short, VImageDimension > MaskImageType;
1958 
1959  //typename ConvolutionImageType::Pointer convolutionImage = dynamic_cast<ConvolutionImageType*>(this->GenerateConvolutionImage(inputImage));
1960  typename ConvolutionImageType::Pointer convolutionImage = this->GenerateConvolutionImage(inputImage);
1961 
1962  if (convolutionImage.IsNull())
1963  {
1964  MITK_ERROR << "Empty convolution image in CalculateHotspotStatistics(). We should never reach this state (logic error).";
1965  throw std::logic_error("Empty convolution image in CalculateHotspotStatistics()");
1966  }
1967 
1968  // find maximum in convolution image, given the current mask
1969  double requiredDistanceToBorder = m_HotspotMustBeCompletelyInsideImage ? m_HotspotRadiusInMM : -1.0;
1970  ImageExtrema convolutionImageInformation = CalculateExtremaWorld(convolutionImage.GetPointer(), maskImage, requiredDistanceToBorder, label);
1971 
1972  isHotspotDefined = convolutionImageInformation.Defined;
1973 
1974  if (!isHotspotDefined)
1975  {
1976  m_EmptyStatistics.Reset(VImageDimension);
1977  MITK_ERROR << "No origin of hotspot-sphere was calculated! Returning empty statistics";
1978  return m_EmptyStatistics;
1979  }
1980  else
1981  {
1982  // create a binary mask around the "hotspot" region, fill the shape of a sphere around our hotspot center
1983  typedef itk::ImageDuplicator< InputImageType > DuplicatorType;
1984  typename DuplicatorType::Pointer copyMachine = DuplicatorType::New();
1985  copyMachine->SetInputImage(inputImage);
1986  copyMachine->Update();
1987 
1988  typedef itk::CastImageFilter< InputImageType, MaskImageType > CastFilterType;
1989  typename CastFilterType::Pointer caster = CastFilterType::New();
1990  caster->SetInput( copyMachine->GetOutput() );
1991  caster->Update();
1992  typename MaskImageType::Pointer hotspotMaskITK = caster->GetOutput();
1993 
1994  typedef typename InputImageType::IndexType IndexType;
1995  IndexType maskCenterIndex;
1996  for (unsigned int d =0; d< VImageDimension;++d) maskCenterIndex[d]=convolutionImageInformation.MaxIndex[d];
1997  typename ConvolutionImageType::PointType maskCenter;
1998  inputImage->TransformIndexToPhysicalPoint(maskCenterIndex,maskCenter);
1999 
2000  this->FillHotspotMaskPixels(hotspotMaskITK.GetPointer(), maskCenter, radiusInMM);
2001 
2002  // calculate statistics within the binary mask
2004  typename LabelStatisticsFilterType::Pointer labelStatisticsFilter;
2005  labelStatisticsFilter = LabelStatisticsFilterType::New();
2006  labelStatisticsFilter->SetInput( inputImage );
2007  labelStatisticsFilter->SetLabelInput( hotspotMaskITK );
2008  labelStatisticsFilter->SetCoordinateTolerance( 0.001 );
2009  labelStatisticsFilter->SetDirectionTolerance( 0.001 );
2010 
2011  labelStatisticsFilter->Update();
2012 
2013  Statistics hotspotStatistics;
2014  hotspotStatistics.SetHotspotIndex(convolutionImageInformation.MaxIndex);
2015  hotspotStatistics.SetMean(convolutionImageInformation.Max);
2016 
2017  if ( labelStatisticsFilter->HasLabel( 1 ) )
2018  {
2019  hotspotStatistics.SetLabel (1);
2020  hotspotStatistics.SetN(labelStatisticsFilter->GetCount(1));
2021  hotspotStatistics.SetMin(labelStatisticsFilter->GetMinimum(1));
2022  hotspotStatistics.SetMax(labelStatisticsFilter->GetMaximum(1));
2023  hotspotStatistics.SetMedian(labelStatisticsFilter->GetMedian(1));
2024  hotspotStatistics.SetVariance(labelStatisticsFilter->GetVariance(1));
2025  hotspotStatistics.SetSigma(labelStatisticsFilter->GetSigma(1));
2026  hotspotStatistics.SetRMS(sqrt( hotspotStatistics.GetMean() * hotspotStatistics.GetMean()
2027  + hotspotStatistics.GetSigma() * hotspotStatistics.GetSigma() ));
2028 
2029  MITK_DEBUG << "Statistics for inside hotspot: Mean " << hotspotStatistics.GetMean()
2030  << ", SD " << hotspotStatistics.GetSigma()
2031  << ", Max " << hotspotStatistics.GetMax()
2032  << ", Min " << hotspotStatistics.GetMin();
2033  }
2034  else
2035  {
2036  MITK_ERROR << "Uh oh! Unable to calculate statistics for hotspot region...";
2037  return m_EmptyStatistics;
2038  }
2039 
2040  return hotspotStatistics;
2041  }
2042  }
2043 
2044  template < typename TPixel, unsigned int VImageDimension >
2046  const itk::Image< TPixel, VImageDimension > *image, unsigned int axis )
2047  {
2048  typedef itk::Image< TPixel, VImageDimension > ImageType;
2049 
2050  typedef itk::CastImageFilter< ImageType, MaskImage2DType > CastFilterType;
2051 
2052  // Generate mask image as new image with same header as input image and
2053  // initialize with 1.
2054  typename CastFilterType::Pointer castFilter = CastFilterType::New();
2055  castFilter->SetInput( image );
2056  castFilter->Update();
2057  castFilter->GetOutput()->FillBuffer( 1 );
2058 
2059  // all PolylinePoints of the PlanarFigure are stored in a vtkPoints object.
2060  // These points are used by the vtkLassoStencilSource to create
2061  // a vtkImageStencil.
2062  const mitk::PlaneGeometry *planarFigurePlaneGeometry = m_PlanarFigure->GetPlaneGeometry();
2063  const typename PlanarFigure::PolyLineType planarFigurePolyline = m_PlanarFigure->GetPolyLine( 0 );
2064  const mitk::BaseGeometry *imageGeometry3D = m_Image->GetGeometry( 0 );
2065  // If there is a second poly line in a closed planar figure, treat it as a hole.
2066  PlanarFigure::PolyLineType planarFigureHolePolyline;
2067 
2068  if (m_PlanarFigure->GetPolyLinesSize() == 2)
2069  planarFigureHolePolyline = m_PlanarFigure->GetPolyLine(1);
2070 
2071 
2072  // Determine x- and y-dimensions depending on principal axis
2073  // TODO use plane geometry normal to determine that automatically, then check whether the PF is aligned with one of the three principal axis
2074  int i0, i1;
2075  switch ( axis )
2076  {
2077  case 0:
2078  i0 = 1;
2079  i1 = 2;
2080  break;
2081 
2082  case 1:
2083  i0 = 0;
2084  i1 = 2;
2085  break;
2086 
2087  case 2:
2088  default:
2089  i0 = 0;
2090  i1 = 1;
2091  break;
2092  }
2095 
2096  // store the polyline contour as vtkPoints object
2097  bool outOfBounds = false;
2098  vtkSmartPointer<vtkPoints> points = vtkSmartPointer<vtkPoints>::New();
2099  typename PlanarFigure::PolyLineType::const_iterator it;
2100  for ( it = planarFigurePolyline.begin();
2101  it != planarFigurePolyline.end();
2102  ++it )
2103  {
2104  Point3D point3D;
2105 
2106  // Convert 2D point back to the local index coordinates of the selected
2107  // image
2108  // Fabian: From PlaneGeometry documentation:
2109  // Converts a 2D point given in mm (pt2d_mm) relative to the upper-left corner of the geometry into the corresponding world-coordinate (a 3D point in mm, pt3d_mm).
2110  // To convert a 2D point given in units (e.g., pixels in case of an image) into a 2D point given in mm (as required by this method), use IndexToWorld.
2111  planarFigurePlaneGeometry->Map( *it, point3D );
2112 
2113  // Polygons (partially) outside of the image bounds can not be processed
2114  // further due to a bug in vtkPolyDataToImageStencil
2115  if ( !imageGeometry3D->IsInside( point3D ) )
2116  {
2117  outOfBounds = true;
2118  }
2119 
2120  // Fabian: Why convert to index coordinates?
2121  imageGeometry3D->WorldToIndex( point3D, point3D );
2122 
2123  points->InsertNextPoint( point3D[i0], point3D[i1], 0 );
2124  }
2125 
2126  vtkSmartPointer<vtkPoints> holePoints = nullptr;
2127 
2128  if (!planarFigureHolePolyline.empty())
2129  {
2130  holePoints = vtkSmartPointer<vtkPoints>::New();
2131 
2132  Point3D point3D;
2133  PlanarFigure::PolyLineType::const_iterator end = planarFigureHolePolyline.end();
2134 
2135  for (it = planarFigureHolePolyline.begin(); it != end; ++it)
2136  {
2137  // Fabian: same as above
2138  planarFigurePlaneGeometry->Map(*it, point3D);
2139  imageGeometry3D->WorldToIndex(point3D, point3D);
2140  holePoints->InsertNextPoint(point3D[i0], point3D[i1], 0);
2141  }
2142  }
2143 
2144  // mark a malformed 2D planar figure ( i.e. area = 0 ) as out of bounds
2145  // this can happen when all control points of a rectangle lie on the same line = two of the three extents are zero
2146  double bounds[6] = {0, 0, 0, 0, 0, 0};
2147  points->GetBounds( bounds );
2148  bool extent_x = (fabs(bounds[0] - bounds[1])) < mitk::eps;
2149  bool extent_y = (fabs(bounds[2] - bounds[3])) < mitk::eps;
2150  bool extent_z = (fabs(bounds[4] - bounds[5])) < mitk::eps;
2151 
2152  // throw an exception if a closed planar figure is deformed, i.e. has only one non-zero extent
2153  if ( m_PlanarFigure->IsClosed() &&
2154  ((extent_x && extent_y) || (extent_x && extent_z) || (extent_y && extent_z)))
2155  {
2156  mitkThrow() << "Figure has a zero area and cannot be used for masking.";
2157  }
2158 
2159  if ( outOfBounds )
2160  {
2161  throw std::runtime_error( "Figure at least partially outside of image bounds!" );
2162  }
2163 
2164  // create a vtkLassoStencilSource and set the points of the Polygon
2165  vtkSmartPointer<vtkLassoStencilSource> lassoStencil = vtkSmartPointer<vtkLassoStencilSource>::New();
2166  lassoStencil->SetShapeToPolygon();
2167  lassoStencil->SetPoints( points );
2168 
2169  vtkSmartPointer<vtkLassoStencilSource> holeLassoStencil = nullptr;
2170 
2171  if (holePoints.GetPointer() != nullptr)
2172  {
2173  holeLassoStencil = vtkSmartPointer<vtkLassoStencilSource>::New();
2174  holeLassoStencil->SetShapeToPolygon();
2175  holeLassoStencil->SetPoints(holePoints);
2176  }
2177 
2178  // Export from ITK to VTK (to use a VTK filter)
2179  typedef itk::VTKImageImport< MaskImage2DType > ImageImportType;
2180  typedef itk::VTKImageExport< MaskImage2DType > ImageExportType;
2181 
2182  typename ImageExportType::Pointer itkExporter = ImageExportType::New();
2183  itkExporter->SetInput( castFilter->GetOutput() );
2184 
2185  vtkSmartPointer<vtkImageImport> vtkImporter = vtkSmartPointer<vtkImageImport>::New();
2186  this->ConnectPipelines( itkExporter, vtkImporter );
2187 
2188  // Apply the generated image stencil to the input image
2189  vtkSmartPointer<vtkImageStencil> imageStencilFilter = vtkSmartPointer<vtkImageStencil>::New();
2190  imageStencilFilter->SetInputConnection( vtkImporter->GetOutputPort() );
2191  imageStencilFilter->SetStencilConnection(lassoStencil->GetOutputPort());
2192  imageStencilFilter->ReverseStencilOff();
2193  imageStencilFilter->SetBackgroundValue( 0 );
2194  imageStencilFilter->Update();
2195 
2196  vtkSmartPointer<vtkImageStencil> holeStencilFilter = nullptr;
2197 
2198  if (holeLassoStencil.GetPointer() != nullptr)
2199  {
2200  holeStencilFilter = vtkSmartPointer<vtkImageStencil>::New();
2201  holeStencilFilter->SetInputConnection(imageStencilFilter->GetOutputPort());
2202  holeStencilFilter->SetStencilConnection(holeLassoStencil->GetOutputPort());
2203  holeStencilFilter->ReverseStencilOn();
2204  holeStencilFilter->SetBackgroundValue(0);
2205  holeStencilFilter->Update();
2206  }
2207 
2208  // Export from VTK back to ITK
2209  vtkSmartPointer<vtkImageExport> vtkExporter = vtkSmartPointer<vtkImageExport>::New();
2210  vtkExporter->SetInputConnection( holeStencilFilter.GetPointer() == nullptr
2211  ? imageStencilFilter->GetOutputPort()
2212  : holeStencilFilter->GetOutputPort());
2213  vtkExporter->Update();
2214 
2215  typename ImageImportType::Pointer itkImporter = ImageImportType::New();
2216  this->ConnectPipelines( vtkExporter, itkImporter );
2217  itkImporter->Update();
2218 
2219  typedef itk::ImageDuplicator< ImageImportType::OutputImageType > DuplicatorType;
2221  duplicator->SetInputImage( itkImporter->GetOutput() );
2222  duplicator->Update();
2223 
2224  // Store mask
2225  m_InternalImageMask2D = duplicator->GetOutput();
2226  }
2227 
2228 
2230  {
2231  // Need to throw away every second progress event to reach a final count of
2232  // 100 since two consecutive filters are used in this case
2233  static int updateCounter = 0;
2234  if ( updateCounter++ % 2 == 0 )
2235  {
2236  this->InvokeEvent( itk::ProgressEvent() );
2237  }
2238  }
2239 
2240 
2242  {
2243  this->InvokeEvent( itk::ProgressEvent() );
2244  }
2245 
2246 }
const Point3D GetOrigin() const
Get the origin, e.g. the upper-left corner of the plane.
mitk::Point3D PointType
itk::SmartPointer< Self > Pointer
void SetMaskingModeToNone()
Set/Get operation mode for masking.
void SetHistogramBinSize(double size)
Set bin size for histogram resolution.
Statistics CalculateHotspotStatistics(const itk::Image< TPixel, VImageDimension > *inputImage, itk::Image< unsigned short, VImageDimension > *maskImage, double radiusInMM, bool &isHotspotDefined, unsigned int label)
Calculates the hotspot statistics depending on masking mode. Hotspot statistics are calculated for a ...
void SetImage(const mitk::Image *image)
Set image from which to compute statistics.
bool GetHotspotMustBeCompletlyInsideImage() const
Returns true if hotspot has to be completly inside the image.
void SetDoIgnorePixelValue(bool doit)
Set whether a pixel value should be ignored in the statistics.
virtual bool Map(const mitk::Point3D &pt3d_mm, mitk::Point2D &pt2d_mm) const
Project a 3D point given in mm (pt3d_mm) onto the 2D geometry. The result is a 2D point in mm (pt2d_m...
#define MITK_ERROR
Definition: mitkLogMacros.h:24
Extension of the itkLabelStatisticsImageFilter that also calculates the Skewness,Kurtosis,Entropy,Uniformity.
double ScalarType
bool IsHotspotCalculated()
Returns true whether the hotspot should be calculated, otherwise false.
void InternalCalculateMaskFromPlanarFigure(const itk::Image< TPixel, VImageDimension > *image, unsigned int axis)
void SetUseDefaultBinSize(bool useDefault)
Automatically calculate bin size to obtain 200 bins.
void SetMaskingMode(unsigned int mode)
Set/Get operation mode for masking.
#define MITK_DEBUG
Definition: mitkLogMacros.h:26
DataCollection - Class to facilitate loading/accessing structured data.
const StatisticsContainer & GetStatisticsVector(unsigned int timeStep=0) const
Retrieve statistics depending on the current masking mode (for all image labels). ...
void SetPlanarFigure(mitk::PlanarFigure *planarFigure)
Set planar figure for masking.
bool m_UseDefaultBinSize
Bin size for histogram resoluion.
itk::SmartPointer< itk::Image< float, VImageDimension > > GenerateHotspotSearchConvolutionKernel(double spacing[VImageDimension], double radiusInMM)
Generates image of kernel which is needed for convolution.
Class for common statistics, includig hotspot properties.
StatisticsContainer::Pointer GetStatistics(unsigned int timeStep=0, unsigned int label=1)
Returns the statistics for label label and timeStep timeStep. If these requested statistics are not c...
void SetCalculateHotspot(bool calculateHotspot)
Sets whether the hotspot should be calculated.
unsigned int GetTimeSteps() const
Get the number of time steps from the TimeGeometry As the base data has not a data vector given by it...
Definition: mitkBaseData.h:346
itk::Size< VImageDimension > CalculateConvolutionKernelSize(double spacing[VImageDimension], double radiusInMM)
Returns size of convolution kernel depending on spacing and radius.
itk::Statistics::Histogram< double > HistogramType
BinFrequencyType GetBinsAndFreuqencyForHistograms(unsigned int timeStep=0, unsigned int label=0) const
static Pointer New()
#define AccessFixedDimensionByItk_3(mitkImage, itkImageTypeFunction, dimension, arg1, arg2, arg3)
void FillHotspotMaskPixels(itk::Image< TPixel, VImageDimension > *maskImage, itk::Point< double, VImageDimension > sphereCenter, double sphereRadiusInMM)
Fills pixels of the spherical hotspot mask.
Vector3D GetNormal() const
Normal of the plane.
itk::SmartPointer< const Self > ConstPointer
void SetSigma(const double)
Set standard deviation (sigma)
static Vector3D offset
map::core::discrete::Elements< 3 >::InternalImageType ImageType
std::vector< HistogramType::ConstPointer > HistogramContainer
bool GetPrincipalAxis(const BaseGeometry *geometry, Vector3D vector, unsigned int &axis)
If the passed vector matches any of the three principal axes of the passed geometry, the ínteger value corresponding to the axis is set and true is returned.
Vector3D GetAxisVector(unsigned int direction) const
Get vector along bounding-box in the specified direction in mm.
#define MITK_WARN
Definition: mitkLogMacros.h:23
itk::Image< double, 3 > InputImageType
#define AccessFixedDimensionByItk_2(mitkImage, itkImageTypeFunction, dimension, arg1, arg2)
Container class for storing the computed image statistics.
void SetHotspotRadiusInMM(double hotspotRadiusInMM)
Sets the radius for the hotspot.
#define mitkThrow()
virtual void SetHotspotIndex(const vnl_vector< int > _arg)
Image class for storing images.
Definition: mitkImage.h:76
void SetMaskingModeToPlanarFigure()
Set/Get operation mode for masking.
void SetMaskingModeToImage()
Set/Get operation mode for masking.
itk::Image< unsigned char, 3 > MaskImageType
Definition: CLBrainMask.cpp:36
int GetComponentType() const
Get the component type (the scalar (!) type). Each element may contain m_NumberOfComponents (more tha...
const mitk::PixelType GetPixelType(int n=0) const
Returns the PixelType of channel n.
Definition: mitkImage.cpp:105
void GetMinAndMaxValue(double &minimum, double &maximum, int &counter, double &sigma, const itk::Image< TPixel, VImageDimension > *InputImage, itk::Image< unsigned short, VImageDimension > *MaskImageType)
static T max(T x, T y)
Definition: svm.cpp:70
itk::SmartPointer< itk::Image< TPixel, VImageDimension > > GenerateConvolutionImage(const itk::Image< TPixel, VImageDimension > *inputImage)
Convolves image with spherical kernel image. Used for hotspot calculation.
const HistogramType * GetHistogram(unsigned int timeStep=0, unsigned int label=0) const
Retrieve the histogram depending on the current masking mode.
void ConnectPipelines(ITK_Exporter exporter, vtkSmartPointer< VTK_Importer > importer)
void SetHotspotMustBeCompletlyInsideImage(bool hotspotIsCompletlyInsideImage, bool warn=true)
Sets flag whether hotspot is completly inside the image. Please note that if set to false it can be p...
double GetHotspotRadiusInMM()
Returns the radius of the hotspot.
static T min(T x, T y)
Definition: svm.cpp:67
#define AccessFixedDimensionByItk_1(mitkImage, itkImageTypeFunction, dimension, arg1)
Base-class for geometric planar (2D) figures, such as lines, circles, rectangles, polygons...
void MITKCORE_EXPORT CastToItkImage(const mitk::Image *mitkImage, itk::SmartPointer< ItkOutputImageType > &itkOutputImage)
Cast an mitk::Image to an itk::Image with a specific type.
Extension of the itkStatisticsImageFilter that also calculates the Skewness and Kurtosis.
virtual bool ComputeStatistics(unsigned int timeStep=0)
Compute statistics (together with histogram) for the current masking mode.
virtual void SetMaxIndex(const vnl_vector< int > _arg)
MITKCORE_EXPORT const ScalarType eps
std::vector< PolyLineElement > PolyLineType
void SetIgnorePixelValue(double value)
Set a pixel value for pixels that will be ignored in the statistics.
Describes a two-dimensional, rectangular plane.
bool IsInside(const mitk::Point3D &p) const
Test whether the point p (world coordinates in mm) is inside the bounding box.
void SetImageMask(const mitk::Image *imageMask)
Set image for masking.
ImageExtrema CalculateExtremaWorld(const itk::Image< TPixel, VImageDimension > *inputImage, itk::Image< unsigned short, VImageDimension > *maskImage, double neccessaryDistanceToImageBorderInMM, unsigned int label)
Calculates minimum, maximum, mean value and their corresponding indices in a given ROI...
unsigned int GetDimension() const
Get dimension of the image.
Definition: mitkImage.cpp:110
double GetHistogramBinSize()
Get bin size for histogram resolution.
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
bool GetDoIgnorePixelValue()
Get whether a pixel value will be ignored in the statistics.
virtual void SetMinIndex(const vnl_vector< int > _arg)
const HistogramContainer & GetHistogramVector(unsigned int timeStep=0) const
Retrieve the histogram depending on the current masking mode (for all image labels.
double GetIgnorePixelValue()
Get the pixel value for pixels that will be ignored in the statistics.
void ExtractImageAndMask(unsigned int timeStep=0)
Depending on the masking mode, the image and mask from which to calculate statistics is extracted fro...
void InternalMaskIgnoredPixels(const itk::Image< TPixel, VImageDimension > *image, itk::Image< unsigned short, VImageDimension > *maskImage)
itk::SmartPointer< const Self > ConstPointer
Definition: mitkImage.h:88
BaseGeometry Describes the geometry of a data object.
static Pointer New()
Pointer Clone() const
void WorldToIndex(const mitk::Point3D &pt_mm, mitk::Point3D &pt_units) const
Convert world coordinates (in mm) of a point to (continuous!) index coordinates.
static itkEventMacro(BoundingShapeInteractionEvent, itk::AnyEvent) class MITKBOUNDINGSHAPE_EXPORT BoundingShapeInteractor Pointer New()
Basic interaction methods for mitk::GeometryData.