--- /dev/null
+/*=========================================================================
+
+ Program: Insight Segmentation & Registration Toolkit
+ Module: $RCSfile: itkLabelOverlapMeasuresImageFilter.h,v $
+ Language: C++
+ Date: $Date: $
+ Version: $Revision: $
+
+ Copyright (c) Insight Software Consortium. All rights reserved.
+ See ITKCopyright.txt or http://www.itk.org/HTML/Copyright.htm for details.
+
+ This software is distributed WITHOUT ANY WARRANTY; without even
+ the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ PURPOSE. See the above copyright notices for more information.
+
+=========================================================================*/
+#ifndef __itkLabelOverlapMeasuresImageFilter_h
+#define __itkLabelOverlapMeasuresImageFilter_h
+
+#include "itkImageToImageFilter.h"
+#include "itkFastMutexLock.h"
+#include "itkNumericTraits.h"
+
+//#include "itk_hash_map.h"
+#include "itksys/hash_map.hxx"
+
+namespace itk {
+
+/** \class LabelOverlapMeasuresImageFilter
+ * \brief Computes overlap measures between the set same set of labels of
+ * pixels of two images. Background is assumed to be 0.
+ *
+ * \sa LabelOverlapMeasuresImageFilter
+ *
+ * \ingroup MultiThreaded
+ */
+template<class TLabelImage>
+class ITK_EXPORT LabelOverlapMeasuresImageFilter :
+ public ImageToImageFilter<TLabelImage, TLabelImage>
+{
+public:
+ /** Standard Self typedef */
+ typedef LabelOverlapMeasuresImageFilter Self;
+ typedef ImageToImageFilter<TLabelImage,TLabelImage> Superclass;
+ typedef SmartPointer<Self> Pointer;
+ typedef SmartPointer<const Self> ConstPointer;
+
+ /** Method for creation through the object factory. */
+ itkNewMacro( Self );
+
+ /** Runtime information support. */
+ itkTypeMacro( LabelOverlapMeasuresImageFilter, ImageToImageFilter );
+
+ virtual void VerifyInputInformation() { }
+
+
+ /** Image related typedefs. */
+ typedef TLabelImage LabelImageType;
+ typedef typename TLabelImage::Pointer LabelImagePointer;
+ typedef typename TLabelImage::ConstPointer LabelImageConstPointer;
+
+ typedef typename TLabelImage::RegionType RegionType;
+ typedef typename TLabelImage::SizeType SizeType;
+ typedef typename TLabelImage::IndexType IndexType;
+
+ typedef typename TLabelImage::PixelType LabelType;
+
+ /** Type to use form computations. */
+ typedef typename NumericTraits<LabelType>::RealType RealType;
+
+ /** \class LabelLabelOverlapMeasuress
+ * \brief Metrics stored per label */
+ class LabelSetMeasures
+ {
+ public:
+ // default constructor
+ LabelSetMeasures()
+ {
+ m_Source = 0;
+ m_Target = 0;
+ m_Union = 0;
+ m_Intersection = 0;
+ m_SourceComplement = 0;
+ m_TargetComplement = 0;
+ }
+
+ // added for completeness
+ LabelSetMeasures& operator=( const LabelSetMeasures& l )
+ {
+ m_Source = l.m_Source;
+ m_Target = l.m_Target;
+ m_Union = l.m_Union;
+ m_Intersection = l.m_Intersection;
+ m_SourceComplement = l.m_SourceComplement;
+ m_TargetComplement = l.m_TargetComplement;
+ }
+
+ unsigned long m_Source;
+ unsigned long m_Target;
+ unsigned long m_Union;
+ unsigned long m_Intersection;
+ unsigned long m_SourceComplement;
+ unsigned long m_TargetComplement;
+ };
+
+ /** Type of the map used to store data per label */
+ typedef itksys::hash_map<LabelType, LabelSetMeasures> MapType;
+ typedef typename MapType::iterator MapIterator;
+ typedef typename MapType::const_iterator MapConstIterator;
+
+ /** Image related typedefs. */
+ itkStaticConstMacro( ImageDimension, unsigned int,
+ TLabelImage::ImageDimension );
+
+ /** Set the source image. */
+ void SetSourceImage( const LabelImageType * image )
+ { this->SetNthInput( 0, const_cast<LabelImageType *>( image ) ); }
+
+ /** Set the target image. */
+ void SetTargetImage( const LabelImageType * image )
+ { this->SetNthInput( 1, const_cast<LabelImageType *>( image ) ); }
+
+ /** Get the source image. */
+ const LabelImageType * GetSourceImage( void )
+ { return this->GetInput( 0 ); }
+
+ /** Get the target image. */
+ const LabelImageType * GetTargetImage( void )
+ { return this->GetInput( 1 ); }
+
+ /** Get the label set measures */
+ MapType GetLabelSetMeasures()
+ { return this->m_LabelSetMeasures; }
+
+ /**
+ * tric overlap measures
+ */
+ /** measures over all labels */
+ RealType GetTotalOverlap();
+ RealType GetUnionOverlap();
+ RealType GetMeanOverlap();
+ RealType GetVolumeSimilarity();
+ RealType GetFalseNegativeError();
+ RealType GetFalsePositiveError();
+ /** measures over individual labels */
+ RealType GetTargetOverlap( LabelType );
+ RealType GetUnionOverlap( LabelType );
+ RealType GetMeanOverlap( LabelType );
+ RealType GetVolumeSimilarity( LabelType );
+ RealType GetFalseNegativeError( LabelType );
+ RealType GetFalsePositiveError( LabelType );
+ /** alternative names */
+ RealType GetJaccardCoefficient()
+ { return this->GetUnionOverlap(); }
+ RealType GetJaccardCoefficient( LabelType label )
+ { return this->GetUnionOverlap( label ); }
+ RealType GetDiceCoefficient()
+ { return this->GetMeanOverlap(); }
+ RealType GetDiceCoefficient( LabelType label )
+ { return this->GetMeanOverlap( label ); }
+
+
+#ifdef ITK_USE_CONCEPT_CHECKING
+ /** Begin concept checking */
+ itkConceptMacro( Input1HasNumericTraitsCheck,
+ ( Concept::HasNumericTraits<LabelType> ) );
+ /** End concept checking */
+#endif
+
+protected:
+ LabelOverlapMeasuresImageFilter();
+ ~LabelOverlapMeasuresImageFilter(){};
+ void PrintSelf( std::ostream& os, Indent indent ) const;
+
+ /**
+ * Pass the input through unmodified. Do this by setting the output to the
+ * source this by setting the output to the source image in the
+ * AllocateOutputs() method.
+ */
+ void AllocateOutputs();
+
+ void BeforeThreadedGenerateData();
+
+ void AfterThreadedGenerateData();
+
+ /** Multi-thread version GenerateData. */
+ void ThreadedGenerateData( const RegionType&, int );
+
+ // Override since the filter needs all the data for the algorithm
+ void GenerateInputRequestedRegion();
+
+ // Override since the filter produces all of its output
+ void EnlargeOutputRequestedRegion( DataObject *data );
+
+private:
+ LabelOverlapMeasuresImageFilter( const Self& ); //purposely not implemented
+ void operator=( const Self& ); //purposely not implemented
+
+ std::vector<MapType> m_LabelSetMeasuresPerThread;
+ MapType m_LabelSetMeasures;
+
+ SimpleFastMutexLock m_Mutex;
+
+}; // end of class
+
+} // end namespace itk
+
+#ifndef ITK_MANUAL_INSTANTIATION
+#include "itkLabelOverlapMeasuresImageFilter.txx"
+#endif
+
+#endif
--- /dev/null
+/*=========================================================================
+
+ Program: Insight Segmentation & Registration Toolkit
+ Module: $RCSfile: itkLabelOverlapMeasuresImageFilter.txx,v $
+ Language: C++
+ Date: $Date: $
+ Version: $Revision: $
+
+ Copyright (c) Insight Software Consortium. All rights reserved.
+ See ITKCopyright.txt or http://www.itk.org/HTML/Copyright.htm for details.
+
+ This software is distributed WITHOUT ANY WARRANTY; without even
+ the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ PURPOSE. See the above copyright notices for more information.
+
+=========================================================================*/
+#ifndef _itkLabelOverlapMeasuresImageFilter_txx
+#define _itkLabelOverlapMeasuresImageFilter_txx
+
+#include "itkLabelOverlapMeasuresImageFilter.h"
+
+#include "itkImageRegionConstIterator.h"
+#include "itkProgressReporter.h"
+
+namespace itk {
+
+#if defined(__GNUC__) && (__GNUC__ <= 2) //NOTE: This class needs a mutex for gnu 2.95
+/** Used for mutex locking */
+#define LOCK_HASHMAP this->m_Mutex.Lock()
+#define UNLOCK_HASHMAP this->m_Mutex.Unlock()
+#else
+#define LOCK_HASHMAP
+#define UNLOCK_HASHMAP
+#endif
+
+template<class TLabelImage>
+LabelOverlapMeasuresImageFilter<TLabelImage>
+::LabelOverlapMeasuresImageFilter()
+{
+ // this filter requires two input images
+ this->SetNumberOfRequiredInputs( 2 );
+}
+
+template<class TLabelImage>
+void
+LabelOverlapMeasuresImageFilter<TLabelImage>
+::GenerateInputRequestedRegion()
+{
+ Superclass::GenerateInputRequestedRegion();
+ if( this->GetSourceImage() )
+ {
+ LabelImagePointer source = const_cast
+ <LabelImageType *>( this->GetSourceImage() );
+ source->SetRequestedRegionToLargestPossibleRegion();
+ }
+ if( this->GetTargetImage() )
+ {
+ LabelImagePointer target = const_cast
+ <LabelImageType *>( this->GetTargetImage() );
+ target->SetRequestedRegionToLargestPossibleRegion();
+ }
+}
+
+template<class TLabelImage>
+void
+LabelOverlapMeasuresImageFilter<TLabelImage>
+::EnlargeOutputRequestedRegion( DataObject *data )
+{
+ Superclass::EnlargeOutputRequestedRegion( data );
+ data->SetRequestedRegionToLargestPossibleRegion();
+}
+
+
+template<class TLabelImage>
+void
+LabelOverlapMeasuresImageFilter<TLabelImage>
+::AllocateOutputs()
+{
+ // Pass the source through as the output
+ LabelImagePointer image =
+ const_cast<TLabelImage *>( this->GetSourceImage() );
+ this->SetNthOutput( 0, image );
+
+ // Nothing that needs to be allocated for the remaining outputs
+}
+
+template<class TLabelImage>
+void
+LabelOverlapMeasuresImageFilter<TLabelImage>
+::BeforeThreadedGenerateData()
+{
+ int numberOfThreads = this->GetNumberOfThreads();
+
+ // Resize the thread temporaries
+ this->m_LabelSetMeasuresPerThread.resize( numberOfThreads );
+
+ // Initialize the temporaries
+ for( int n = 0; n < numberOfThreads; n++ )
+ {
+ this->m_LabelSetMeasuresPerThread[n].clear();
+ }
+
+ // Initialize the final map
+ this->m_LabelSetMeasures.clear();
+}
+
+template<class TLabelImage>
+void
+LabelOverlapMeasuresImageFilter<TLabelImage>
+::AfterThreadedGenerateData()
+{
+ // Run through the map for each thread and accumulate the set measures.
+ for( int n = 0; n < this->GetNumberOfThreads(); n++ )
+ {
+ // iterate over the map for this thread
+ for( MapConstIterator threadIt = this->m_LabelSetMeasuresPerThread[n].begin();
+ threadIt != this->m_LabelSetMeasuresPerThread[n].end();
+ ++threadIt )
+ {
+ // does this label exist in the cumulative stucture yet?
+ MapIterator mapIt = this->m_LabelSetMeasures.find( ( *threadIt ).first );
+ if( mapIt == this->m_LabelSetMeasures.end() )
+ {
+ // create a new entry
+ typedef typename MapType::value_type MapValueType;
+ mapIt = this->m_LabelSetMeasures.insert( MapValueType(
+ (*threadIt).first, LabelSetMeasures() ) ).first;
+ }
+
+ // accumulate the information from this thread
+ (*mapIt).second.m_Source += (*threadIt).second.m_Source;
+ (*mapIt).second.m_Target += (*threadIt).second.m_Target;
+ (*mapIt).second.m_Union += (*threadIt).second.m_Union;
+ (*mapIt).second.m_Intersection +=
+ (*threadIt).second.m_Intersection;
+ (*mapIt).second.m_SourceComplement +=
+ (*threadIt).second.m_SourceComplement;
+ (*mapIt).second.m_TargetComplement +=
+ (*threadIt).second.m_TargetComplement;
+ } // end of thread map iterator loop
+ } // end of thread loop
+}
+
+template<class TLabelImage>
+void
+LabelOverlapMeasuresImageFilter<TLabelImage>
+::ThreadedGenerateData( const RegionType& outputRegionForThread,
+ int threadId )
+{
+ ImageRegionConstIterator<LabelImageType> ItS( this->GetSourceImage(),
+ outputRegionForThread );
+ ImageRegionConstIterator<LabelImageType> ItT( this->GetTargetImage(),
+ outputRegionForThread );
+
+ // support progress methods/callbacks
+ ProgressReporter progress( this, threadId,
+ 2*outputRegionForThread.GetNumberOfPixels() );
+
+ for( ItS.GoToBegin(), ItT.GoToBegin(); !ItS.IsAtEnd(); ++ItS, ++ItT )
+ {
+ LabelType sourceLabel = ItS.Get();
+ LabelType targetLabel = ItT.Get();
+
+ // is the label already in this thread?
+ MapIterator mapItS =
+ this->m_LabelSetMeasuresPerThread[threadId].find( sourceLabel );
+ MapIterator mapItT =
+ this->m_LabelSetMeasuresPerThread[threadId].find( targetLabel );
+
+ if( mapItS == this->m_LabelSetMeasuresPerThread[threadId].end() )
+ {
+ // create a new label set measures object
+ typedef typename MapType::value_type MapValueType;
+ LOCK_HASHMAP;
+ mapItS = this->m_LabelSetMeasuresPerThread[threadId].insert(
+ MapValueType( sourceLabel, LabelSetMeasures() ) ).first;
+ UNLOCK_HASHMAP;
+ }
+
+ if( mapItT == this->m_LabelSetMeasuresPerThread[threadId].end() )
+ {
+ // create a new label set measures object
+ typedef typename MapType::value_type MapValueType;
+ LOCK_HASHMAP;
+ mapItT = this->m_LabelSetMeasuresPerThread[threadId].insert(
+ MapValueType( targetLabel, LabelSetMeasures() ) ).first;
+ UNLOCK_HASHMAP;
+ }
+
+ (*mapItS).second.m_Source++;
+ (*mapItT).second.m_Target++;
+
+ if( sourceLabel == targetLabel )
+ {
+ (*mapItS).second.m_Intersection++;
+ (*mapItS).second.m_Union++;
+ }
+ else
+ {
+ (*mapItS).second.m_Union++;
+ (*mapItT).second.m_Union++;
+
+ (*mapItS).second.m_SourceComplement++;
+ (*mapItT).second.m_TargetComplement++;
+ }
+
+ progress.CompletedPixel();
+ }
+}
+
+/**
+ * measures
+ */
+template<class TLabelImage>
+typename LabelOverlapMeasuresImageFilter<TLabelImage>::RealType
+LabelOverlapMeasuresImageFilter<TLabelImage>
+::GetTotalOverlap()
+{
+ RealType numerator = 0.0;
+ RealType denominator = 0.0;
+ for( MapIterator mapIt = this->m_LabelSetMeasures.begin();
+ mapIt != this->m_LabelSetMeasures.end(); ++mapIt )
+ {
+ // Do not include the background in the final value.
+ if( (*mapIt).first == NumericTraits<LabelType>::Zero )
+ {
+ continue;
+ }
+ numerator += static_cast<RealType>( (*mapIt).second.m_Intersection );
+ denominator += static_cast<RealType>( (*mapIt).second.m_Target );
+ }
+ return ( numerator / denominator );
+}
+
+template<class TLabelImage>
+typename LabelOverlapMeasuresImageFilter<TLabelImage>::RealType
+LabelOverlapMeasuresImageFilter<TLabelImage>
+::GetTargetOverlap( LabelType label )
+{
+ MapIterator mapIt = this->m_LabelSetMeasures.find( label );
+ if( mapIt == this->m_LabelSetMeasures.end() )
+ {
+ itkWarningMacro( "Label " << label << " not found." );
+ return 0.0;
+ }
+ RealType value =
+ static_cast<RealType>( (*mapIt).second.m_Intersection ) /
+ static_cast<RealType>( (*mapIt).second.m_Target );
+ return value;
+}
+
+template<class TLabelImage>
+typename LabelOverlapMeasuresImageFilter<TLabelImage>::RealType
+LabelOverlapMeasuresImageFilter<TLabelImage>
+::GetUnionOverlap()
+{
+ RealType numerator = 0.0;
+ RealType denominator = 0.0;
+ for( MapIterator mapIt = this->m_LabelSetMeasures.begin();
+ mapIt != this->m_LabelSetMeasures.end(); ++mapIt )
+ {
+ // Do not include the background in the final value.
+ if( (*mapIt).first == NumericTraits<LabelType>::Zero )
+ {
+ continue;
+ }
+ numerator += static_cast<RealType>( (*mapIt).second.m_Intersection );
+ denominator += static_cast<RealType>( (*mapIt).second.m_Union );
+ }
+ return ( numerator / denominator );
+}
+
+template<class TLabelImage>
+typename LabelOverlapMeasuresImageFilter<TLabelImage>::RealType
+LabelOverlapMeasuresImageFilter<TLabelImage>
+::GetUnionOverlap( LabelType label )
+{
+ MapIterator mapIt = this->m_LabelSetMeasures.find( label );
+ if( mapIt == this->m_LabelSetMeasures.end() )
+ {
+ itkWarningMacro( "Label " << label << " not found." );
+ return 0.0;
+ }
+ RealType value =
+ static_cast<RealType>( (*mapIt).second.m_Intersection ) /
+ static_cast<RealType>( (*mapIt).second.m_Union );
+ return value;
+}
+
+template<class TLabelImage>
+typename LabelOverlapMeasuresImageFilter<TLabelImage>::RealType
+LabelOverlapMeasuresImageFilter<TLabelImage>
+::GetMeanOverlap()
+{
+ RealType uo = this->GetUnionOverlap();
+ return ( 2.0 * uo / ( 1.0 + uo ) );
+}
+
+template<class TLabelImage>
+typename LabelOverlapMeasuresImageFilter<TLabelImage>::RealType
+LabelOverlapMeasuresImageFilter<TLabelImage>
+::GetMeanOverlap( LabelType label )
+{
+ RealType uo = this->GetUnionOverlap( label );
+ return ( 2.0 * uo / ( 1.0 + uo ) );
+}
+
+template<class TLabelImage>
+typename LabelOverlapMeasuresImageFilter<TLabelImage>::RealType
+LabelOverlapMeasuresImageFilter<TLabelImage>
+::GetVolumeSimilarity()
+{
+ RealType numerator = 0.0;
+ RealType denominator = 0.0;
+ for( MapIterator mapIt = this->m_LabelSetMeasures.begin();
+ mapIt != this->m_LabelSetMeasures.end(); ++mapIt )
+ {
+ // Do not include the background in the final value.
+ if( (*mapIt).first == NumericTraits<LabelType>::Zero )
+ {
+ continue;
+ }
+ numerator += ( ( static_cast<RealType>( (*mapIt).second.m_Source ) -
+ static_cast<RealType>( (*mapIt).second.m_Target ) ) );
+ denominator += ( ( static_cast<RealType>( (*mapIt).second.m_Source ) +
+ static_cast<RealType>( (*mapIt).second.m_Target ) ) );
+ }
+ return ( 2.0 * numerator / denominator );
+}
+
+template<class TLabelImage>
+typename LabelOverlapMeasuresImageFilter<TLabelImage>::RealType
+LabelOverlapMeasuresImageFilter<TLabelImage>
+::GetVolumeSimilarity( LabelType label )
+{
+ MapIterator mapIt = this->m_LabelSetMeasures.find( label );
+ if( mapIt == this->m_LabelSetMeasures.end() )
+ {
+ itkWarningMacro( "Label " << label << " not found." );
+ return 0.0;
+ }
+ RealType value = 2.0 *
+ ( static_cast<RealType>( (*mapIt).second.m_Source ) -
+ static_cast<RealType>( (*mapIt).second.m_Target ) ) /
+ ( static_cast<RealType>( (*mapIt).second.m_Source ) +
+ static_cast<RealType>( (*mapIt).second.m_Target ) );
+ return value;
+}
+
+template<class TLabelImage>
+typename LabelOverlapMeasuresImageFilter<TLabelImage>::RealType
+LabelOverlapMeasuresImageFilter<TLabelImage>
+::GetFalseNegativeError()
+{
+ RealType numerator = 0.0;
+ RealType denominator = 0.0;
+ for( MapIterator mapIt = this->m_LabelSetMeasures.begin();
+ mapIt != this->m_LabelSetMeasures.end(); ++mapIt )
+ {
+ // Do not include the background in the final value.
+ if( (*mapIt).first == NumericTraits<LabelType>::Zero )
+ {
+ continue;
+ }
+ numerator += static_cast<RealType>( (*mapIt).second.m_TargetComplement );
+ denominator += static_cast<RealType>( (*mapIt).second.m_Target );
+ }
+ return ( numerator / denominator );
+}
+
+template<class TLabelImage>
+typename LabelOverlapMeasuresImageFilter<TLabelImage>::RealType
+LabelOverlapMeasuresImageFilter<TLabelImage>
+::GetFalseNegativeError( LabelType label )
+{
+ MapIterator mapIt = this->m_LabelSetMeasures.find( label );
+ if( mapIt == this->m_LabelSetMeasures.end() )
+ {
+ itkWarningMacro( "Label " << label << " not found." );
+ return 0.0;
+ }
+ RealType value =
+ static_cast<RealType>( (*mapIt).second.m_TargetComplement ) /
+ static_cast<RealType>( (*mapIt).second.m_Target );
+ return value;
+}
+
+template<class TLabelImage>
+typename LabelOverlapMeasuresImageFilter<TLabelImage>::RealType
+LabelOverlapMeasuresImageFilter<TLabelImage>
+::GetFalsePositiveError()
+{
+ RealType numerator = 0.0;
+ RealType denominator = 0.0;
+ for( MapIterator mapIt = this->m_LabelSetMeasures.begin();
+ mapIt != this->m_LabelSetMeasures.end(); ++mapIt )
+ {
+ // Do not include the background in the final value.
+ if( (*mapIt).first == NumericTraits<LabelType>::Zero )
+ {
+ continue;
+ }
+ numerator += static_cast<RealType>( (*mapIt).second.m_SourceComplement );
+ denominator += static_cast<RealType>( (*mapIt).second.m_Source );
+ }
+ return ( numerator / denominator );
+}
+
+template<class TLabelImage>
+typename LabelOverlapMeasuresImageFilter<TLabelImage>::RealType
+LabelOverlapMeasuresImageFilter<TLabelImage>
+::GetFalsePositiveError( LabelType label )
+{
+ MapIterator mapIt = this->m_LabelSetMeasures.find( label );
+ if( mapIt == this->m_LabelSetMeasures.end() )
+ {
+ itkWarningMacro( "Label " << label << " not found." );
+ return 0.0;
+ }
+ RealType value =
+ static_cast<RealType>( (*mapIt).second.m_SourceComplement ) /
+ static_cast<RealType>( (*mapIt).second.m_Source );
+ return value;
+}
+
+template<class TLabelImage>
+void
+LabelOverlapMeasuresImageFilter<TLabelImage>
+::PrintSelf( std::ostream& os, Indent indent ) const
+{
+ Superclass::PrintSelf( os, indent );
+
+}
+
+
+}// end namespace itk
+#endif