/*========================================================================= Program: Insight Segmentation & Registration Toolkit Module: $RCSfile: itkOtsuThresholdImageCalculator.txx,v $ Language: C++ Date: $Date: 2004/07/31 12:19:51 $ Version: $Revision: 1.6 $ 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 _itkOtsuThresholdImageCalculator_txx #define _itkOtsuThresholdImageCalculator_txx #include "itkOtsuThresholdImageCalculator.h" #include "itkImageRegionConstIteratorWithIndex.h" #include "itkMinimumMaximumImageCalculator.h" #include "vnl/vnl_math.h" namespace itk { /* * Constructor */ template OtsuThresholdImageCalculator ::OtsuThresholdImageCalculator() { m_Image = NULL; m_Threshold = NumericTraits::Zero; m_NumberOfHistogramBins = 128; } /* * Compute the Otsu's threshold */ template void OtsuThresholdImageCalculator ::Compute(double w) { unsigned int j; if ( !m_Image ) { return; } double totalPixels = (double) m_Image->GetBufferedRegion().GetNumberOfPixels(); if ( totalPixels == 0 ) { return; } // compute image max and min typedef MinimumMaximumImageCalculator RangeCalculator; typename RangeCalculator::Pointer rangeCalculator = RangeCalculator::New(); rangeCalculator->SetImage( m_Image ); rangeCalculator->Compute(); PixelType imageMin = rangeCalculator->GetMinimum(); PixelType imageMax = rangeCalculator->GetMaximum(); if ( imageMin >= imageMax ) { m_Threshold = imageMin; return; } // create a histogram std::vector relativeFrequency; relativeFrequency.resize( m_NumberOfHistogramBins ); for ( j = 0; j < m_NumberOfHistogramBins; j++ ) { relativeFrequency[j] = 0.0; } double binMultiplier = (double) m_NumberOfHistogramBins / (double) ( imageMax - imageMin ); typedef ImageRegionConstIteratorWithIndex Iterator; Iterator iter( m_Image, m_Image->GetBufferedRegion() ); while ( !iter.IsAtEnd() ) { unsigned int binNumber; PixelType value = iter.Get(); if ( value == imageMin ) { binNumber = 0; } else { binNumber = (unsigned int) ceil( (value - imageMin) * binMultiplier ) - 1; if ( binNumber == m_NumberOfHistogramBins ) // in case of rounding errors { binNumber -= 1; } } relativeFrequency[binNumber] += 1.0; ++iter; } // normalize the frequencies double totalMean = 0.0; for ( j = 0; j < m_NumberOfHistogramBins; j++ ) { relativeFrequency[j] /= totalPixels; totalMean += (j+1) * relativeFrequency[j]; } // compute Otsu's threshold by maximizing the between-class // variance double freqLeft = relativeFrequency[0]; double meanLeft = 1.0; double meanRight = ( totalMean - freqLeft ) / ( 1.0 - freqLeft ); double maxVarBetween = freqLeft * ( 1.0 - freqLeft ) * pow(fabs(meanLeft - meanRight), w); int maxBinNumber = 0; double freqLeftOld = freqLeft; double meanLeftOld = meanLeft; for ( j = 1; j < m_NumberOfHistogramBins; j++ ) { freqLeft += relativeFrequency[j]; meanLeft = ( meanLeftOld * freqLeftOld + (j+1) * relativeFrequency[j] ) / freqLeft; if (freqLeft == 1.0) { meanRight = 0.0; } else { meanRight = ( totalMean - meanLeft * freqLeft ) / ( 1.0 - freqLeft ); } double varBetween = freqLeft * ( 1.0 - freqLeft ) * pow(fabs(meanLeft - meanRight), w); if ( varBetween > maxVarBetween ) { maxVarBetween = varBetween; maxBinNumber = j; } // cache old values freqLeftOld = freqLeft; meanLeftOld = meanLeft; } m_Threshold = static_cast( imageMin + ( maxBinNumber + 1 ) / binMultiplier ); } template void OtsuThresholdImageCalculator ::PrintSelf( std::ostream& os, Indent indent ) const { Superclass::PrintSelf(os,indent); os << indent << "Threshold: " << m_Threshold << std::endl; os << indent << "NumberOfHistogramBins: " << m_NumberOfHistogramBins << std::endl; os << indent << "Image: " << m_Image.GetPointer() << std::endl; } } // end namespace itk #endif