diff --git a/Code/ChangeDetection/otbKullbackLeiblerDistanceImageFilter.h b/Code/ChangeDetection/otbKullbackLeiblerDistanceImageFilter.h new file mode 100644 index 0000000000000000000000000000000000000000..486691e62315ba239ba4278a2bf4e9b3398c4fbd --- /dev/null +++ b/Code/ChangeDetection/otbKullbackLeiblerDistanceImageFilter.h @@ -0,0 +1,148 @@ +/*========================================================================= + + Program: ORFEO Toolbox + Language: C++ + Date: $Date$ + Version: $Revision$ + + + Copyright (c) Centre National d'Etudes Spatiales. All rights reserved. + See OTBCopyright.txt 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 __otbKullbackLeiblerDistanceImageFilter_h +#define __otbKullbackLeiblerDistanceImageFilter_h + +#include "itkVariableLengthVector.h" +#include "otbBinaryFunctorNeighborhoodImageFilter.h" + +namespace otb { + +template <class TInput> +class CumulantsForEdgeworth +{ + public : + CumulantsForEdgeworth ( const TInput & input ); + virtual ~CumulantsForEdgeworth () { }; + + // Divergence de KL + template <class TInput2> + double KL_divergence ( const CumulantsForEdgeworth<TInput2> & cumulants ); + + // access to attributes + inline double GetMean () const { return this->fMean; } + inline double GetVariance () const { return this->fVariance; } + inline double GetSkewness () const { return this->fSkewness; } + inline double GetKurtosis () const { return this->fKurtosis; } + + protected : + + // estimation des moments à partir du voisinage initial + int MakeSumAndMoments ( const TInput & input ); + // transformation moment -> cumulants (pour Edgeworth) + int MakeCumulants(); + + // Attributs internes à la classe + double fSum0, fSum1, fSum2, fSum3, fSum4; + double fMu1, fMu2, fMu3, fMu4; + double fMean, fVariance, fSkewness, fKurtosis; +}; + + + +namespace Functor { + + template < class TInput1, class TInput2, class TOutput > + class KullbackLeiblerDistance + { + public : + KullbackLeiblerDistance () { } + virtual ~KullbackLeiblerDistance () { } + TOutput operator () ( const TInput1 & it1, const TInput2 & it2 ) { + CumulantsForEdgeworth<TInput1> cum1 ( it1 ); + CumulantsForEdgeworth<TInput2> cum2 ( it2 ); + return static_cast<TOutput> ( cum1.KL_divergence( cum2 ) + + cum2.KL_divergence( cum1 ) ); + } + }; + +} // Functor + +/** \class KullbackLeiblerDistanceImageFilter + * \brief Implements neighborhood-wise the computation of KullbackLeibler distance over Edgeworth approximation. + * + * This filter is parametrized over the types of the two + * input images and the type of the output image. + * + * Numeric conversions (castings) are done by the C++ defaults. + * + * The filter will walk over all the pixels in the two input images, and for + * each one of them it will do the following: + * + * - cast the input 1 pixel value to \c double + * - cast the input 2 pixel value to \c double + * - compute the first four cumulants of the two pixel values + * - compute the value of the Edgeorth approximation of the KL distance + * - cast the \c double value resulting to the pixel type of the output image + * - store the casted value into the output image. + * + * The filter expect all images to have the same dimension + * (e.g. all 2D, or all 3D, or all ND) + * + * See article of Lin Saito et Levine + * "Edgeworth Approximation of the Kullback-Leibler Distance Towards Problems in Image Analysis" + * and + * "Edgeworth Expansions of the Kullback-Leibler Information" (submitted to JASA, nov 25, 1999) + * http://www.math.ucdavis.edu/~saito/publications + * + * \ingroup IntensityImageFilters Multithreaded + */ +template <class TInputImage1, class TInputImage2, class TOutputImage> +class ITK_EXPORT KullbackLeiblerDistanceImageFilter : + public otb::BinaryFunctorNeighborhoodImageFilter< + TInputImage1,TInputImage2,TOutputImage, + Functor::KullbackLeiblerDistance< + typename itk::ConstNeighborhoodIterator<TInputImage1>, + typename itk::ConstNeighborhoodIterator<TInputImage2>, + typename TOutputImage::PixelType> > +{ + public: + /** Standard class typedefs. */ + typedef KullbackLeiblerDistanceImageFilter Self; + typedef typename otb::BinaryFunctorNeighborhoodImageFilter< + TInputImage1,TInputImage2,TOutputImage, + Functor::KullbackLeiblerDistance< + typename itk::ConstNeighborhoodIterator<TInputImage1>, + typename itk::ConstNeighborhoodIterator<TInputImage2>, + typename TOutputImage::PixelType> + > Superclass; + typedef itk::SmartPointer<Self> Pointer; + typedef itk::SmartPointer<const Self> ConstPointer; + + /** Method for creation through the object factory. */ + itkNewMacro(Self); + + protected: + KullbackLeiblerDistanceImageFilter() {} + virtual ~KullbackLeiblerDistanceImageFilter() {} + + private: + KullbackLeiblerDistanceImageFilter(const Self&); //purposely not implemented + void operator=(const Self&); //purposely not implemented + +}; + +#ifndef ITK_MANUAL_INSTANTIATION +#include "otbKullbackLeiblerDistanceImageFilter.txx" +#endif + +} // namespace otb + +#endif + + diff --git a/Code/ChangeDetection/otbKullbackLeiblerDistanceImageFilter.txx b/Code/ChangeDetection/otbKullbackLeiblerDistanceImageFilter.txx new file mode 100644 index 0000000000000000000000000000000000000000..7ec38194c7aec9c6cb8ad15ac482c3d5593635d0 --- /dev/null +++ b/Code/ChangeDetection/otbKullbackLeiblerDistanceImageFilter.txx @@ -0,0 +1,168 @@ +/*========================================================================= + + Program: ORFEO Toolbox + Language: C++ + Date: $Date$ + Version: $Revision$ + + + Copyright (c) Centre National d'Etudes Spatiales. All rights reserved. + See OTBCopyright.txt 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 __otbKullbackLeiblerDistanceImageFilter_txx +#define __otbKullbackLeiblerDistanceImageFilter_txx + +#include "otbKullbackLeiblerDistanceImageFilter.h" + + +/* ******************************************************************* + * Classe CumulantsForEdgeworth + ********************************************************************* + */ +template <class TInput> +CumulantsForEdgeworth<TInput> +::CumulantsForEdgeworth ( const TInput & input ) +{ + MakeSumAndMoments( input ); + MakeCumulants(); +} + +/* ========================== Divergence de KL ======================= */ + +template <class TInput> +template <class TInput2> +double +CumulantsForEdgeworth<TInput> +::KL_divergence ( const CumulantsForEdgeworth<TInput2> & cumulants ) +{ + double cum1 = GetMean(); + double cum2 = GetVariance(); + double cum3 = GetSkewness(); + + double sqrt_cum2 = sqrt( cum2 ); + double cum2_3 = cum2 * cum2 * cum2; + double cum3_2 = cum3 * cum3; + + double tilde_cum1 = cumulants.GetMean(); + double tilde_cum2 = cumulants.GetVariance(); + double tilde_cum3 = cumulants.GetSkewness(); + double tilde_cum4 = cumulants.GetKurtosis(); + + double tilde_cum2_2 = cum2 * cum2; + double tilde_cum2_3 = cum2 * tilde_cum2_2; + double tilde_cum2_6 = tilde_cum2_3 * tilde_cum2_3; + double tilde_cum3_2 = tilde_cum3 * tilde_cum3; + + double beta = sqrt_cum2 / tilde_cum2; + double alpha = ( cum1 - tilde_cum1 ) / tilde_cum2; + + double alpha_2 = alpha * alpha; + double alpha_4 = alpha_2 * alpha_2; + double alpha_6 = alpha_2 * alpha_4; + + double beta_2 = beta * beta; + double beta_4 = beta_2 * beta_2; + double beta_6 = beta_2 * beta_4; + + double c2 = alpha_2 + beta_2; + double c3 = alpha * ( alpha_2 + 3.0 * beta_2 ); + double c4 = alpha_4 + 6.0 * alpha_2 * beta_2 + 3.0 * beta_4; + double c6 = alpha_6 + 15.0 * alpha_4 * beta_2 + 45.0 * alpha_2 * beta_4 + 15.0 * beta_6; + + double a1 = c3 - 3.0 * alpha / tilde_cum2; + double a2 = c4 - 6.0 * c2 / tilde_cum2 + 3.0 / tilde_cum2_2; // Watch for tilde_cum2_2 mistake in the article! + double a3 = c6 - 15.0 * c4 / tilde_cum2 + 45.0 * c2 / tilde_cum2_2 - 15.0 / tilde_cum2_3; + + double tmp = cum1 - tilde_cum1 + sqrt_cum2; + double resu = cum3_2 / ( 12.0 * cum2_3 ) + + ( log( tilde_cum2 / cum2 ) + - 1.0 + tmp * tmp / tilde_cum2 ) / 2.0 + - ( tilde_cum3 * a1 / 6.0 + tilde_cum4 * a2 / 24.0 + tilde_cum3_2 * a3 / 72.0 ) + - tilde_cum3_2 * ( c6 - 6.0 * c4 / cum2 + 9.0 * c2 / tilde_cum2_2 ) / 72.0 + - 10.0 * cum3 * tilde_cum3 * ( cum1 - tilde_cum1 ) * ( cum2 - tilde_cum2 ) / tilde_cum2_6; + + return resu < 0.0 ? 0.0 : resu; +} + +/* ====== Estimation des moments à partir du voisinage initial ======= */ + +template <class TInput> +int +CumulantsForEdgeworth<TInput> +::MakeSumAndMoments ( const TInput & input ) +{ + + fSum0 = fSum1 = fSum2 = fSum3 = fSum4 = 0.0; + double pixel,pixel_2; + + for ( unsigned long i = 0; i < input.Size(); i++ ) + { + pixel = static_cast<double> ( input.GetPixel(i) ); + pixel_2 = pixel * pixel; + + fSum0 += 1.0; + fSum1 += pixel; + fSum2 += pixel_2; + fSum3 += pixel_2 * pixel; + fSum4 += pixel_2 * pixel_2; + } + + fMu1 = fSum1 / fSum0; + fMu2 = fSum2 / fSum0 - fMu1 * fMu1; + + if ( fMu2 <= 0.0 ) + { + std::cerr << "Potential NAN detected in " << __PRETTY_FUNCTION__ << "\n"; + fMu3 = 0.0; + fMu4 = 4.0; + return 1; + } + + double sigma = sqrt( fMu2 ); + + itk::VariableLengthVector<double> tab ( input.Size() ); + double * x = const_cast<double *> ( tab.GetDataPointer() ); + for ( unsigned long i = 0; i < input.Size(); i++ ) + *x++ = ( static_cast<double> ( input.GetPixel(i) ) - fMu1 ) / sigma; + + fMu3 = fMu4 = 0.0; + x = const_cast<double *> ( tab.GetDataPointer() ); + for ( unsigned long i = 0; i < input.Size(); i++ ) + { + pixel = *x++; + pixel_2 = pixel * pixel; + + fMu3 += pixel * pixel_2; + fMu4 += pixel_2 * pixel_2; + } + + fMu3 /= fSum0; + fMu4 /= fSum0; + + return 0; +} + +/* =========== transformation moment -> cumulants ==================== */ + +template <class TInput> +int +CumulantsForEdgeworth<TInput> +::MakeCumulants () +{ + fMean = fMu1; + fVariance = fMu2; + fSkewness = fMu3; + fKurtosis = fMu4 - 3.0; + return 0; +} + + +#endif + + diff --git a/Code/Common/otbBinaryFunctorNeighborhoodImageFilter.txx b/Code/Common/otbBinaryFunctorNeighborhoodImageFilter.txx index 59842a04b02b4492dadaaa4f757959f5def1e54b..176d3475fcc7558c2bbd959dfe0d7b8657dce85e 100755 --- a/Code/Common/otbBinaryFunctorNeighborhoodImageFilter.txx +++ b/Code/Common/otbBinaryFunctorNeighborhoodImageFilter.txx @@ -125,7 +125,7 @@ BinaryFunctorNeighborhoodImageFilter<TInputImage1, TInputImage2, TOutputImage, T for (fit1=faceList1.begin(), fit2=faceList2.begin(); fit1 != faceList1.end(), fit2 != faceList2.end(); ++fit1, ++fit2) { neighInputIt1 = itk::ConstNeighborhoodIterator<TInputImage1>(r1, inputPtr1, *fit1); - neighInputIt2 = itk::ConstNeighborhoodIterator<TInputImage2>(r1, inputPtr2, *fit2); + neighInputIt2 = itk::ConstNeighborhoodIterator<TInputImage2>(r2, inputPtr2, *fit2); // outputIt = itk::ImageRegionIterator<TOutputImage>(outputPtr, outputRegionForThread); outputIt = itk::ImageRegionIterator<TOutputImage>(outputPtr, *fit1); diff --git a/Testing/Code/ChangeDetection/CMakeLists.txt b/Testing/Code/ChangeDetection/CMakeLists.txt index cdb4b9e3c348d6961a25cb78ee90ee1a11561014..2ffdae80376af237dd34e53f059e2ada0e2ed0bb 100755 --- a/Testing/Code/ChangeDetection/CMakeLists.txt +++ b/Testing/Code/ChangeDetection/CMakeLists.txt @@ -69,8 +69,9 @@ ADD_TEST(cdTvLHMI ${CHANGEDETECTION_TESTS} ) ADD_TEST(cdTvJHMI ${CHANGEDETECTION_TESTS} - --compare-image ${TOL} ${BASELINE}/cdJHMIImage.png - ${TEMP}/cdJHMIImage.png + --compare-image ${TOL} + ${BASELINE}/cdJHMIImage.png + ${TEMP}/cdJHMIImage.png otbJHMIChangeDetectionTest ${INPUTDATA}/GomaAvant.png ${INPUTDATA}/GomaApres.png @@ -78,6 +79,18 @@ ADD_TEST(cdTvJHMI ${CHANGEDETECTION_TESTS} ${TEMP}/cdJHMIImage.png ) +ADD_TEST(cdTuKullbackLeiblerDistanceImageFilterNew ${CHANGEDETECTION_TESTS} + otbKullbackLeiblerDistanceImageFilterNew) + +ADD_TEST(cdTvKullbackLeiblerDistanceImageFilter ${CHANGEDETECTION_TESTS} + --compare-image ${EPSILON} + ${BASELINE}/cdTVKullbackLeiblerDistanceImageFilterOutput.hdr + ${TEMP}/cdTVKullbackLeiblerDistanceImageFilterOutput.hdr + otbKullbackLeiblerDistanceImageFilter + ${INPUTDATA}/GomaAvant.png + ${INPUTDATA}/GomaApres.png + ${TEMP}/cdTVKullbackLeiblerDistanceImageFilterOutput.hdr + 35) # ------- Fichiers sources CXX ----------------------------------- @@ -88,6 +101,8 @@ otbMeanDiffChangeDetectionTest.cxx otbMeanRatioChangeDetectionTest.cxx otbLHMIChangeDetectionTest.cxx otbJHMIChangeDetectionTest.cxx +otbKullbackLeiblerDistanceImageFilterNew.cxx +otbKullbackLeiblerDistanceImageFilter.cxx ) diff --git a/Testing/Code/ChangeDetection/otbChangeDetectionTests.cxx b/Testing/Code/ChangeDetection/otbChangeDetectionTests.cxx index 1fc6c9e7a1a47f57fc42c3023c2b05305f7c134f..ec33ff32046f2bd5f7c76fd148883fde6af1b9cc 100755 --- a/Testing/Code/ChangeDetection/otbChangeDetectionTests.cxx +++ b/Testing/Code/ChangeDetection/otbChangeDetectionTests.cxx @@ -32,4 +32,6 @@ REGISTER_TEST(otbMeanDiffChangeDetectionTest ); REGISTER_TEST(otbMeanRatioChangeDetectionTest ); REGISTER_TEST(otbLHMIChangeDetectionTest ); REGISTER_TEST(otbJHMIChangeDetectionTest ); +REGISTER_TEST(otbKullbackLeiblerDistanceImageFilterNew); +REGISTER_TEST(otbKullbackLeiblerDistanceImageFilter); } diff --git a/Testing/Code/ChangeDetection/otbKullbackLeiblerDistanceImageFilter.cxx b/Testing/Code/ChangeDetection/otbKullbackLeiblerDistanceImageFilter.cxx new file mode 100644 index 0000000000000000000000000000000000000000..cb1b7e68c89b6b96f66f200a6c06c5add38a7104 --- /dev/null +++ b/Testing/Code/ChangeDetection/otbKullbackLeiblerDistanceImageFilter.cxx @@ -0,0 +1,81 @@ +/*========================================================================= + + Program: ORFEO Toolbox + Language: C++ + Date: $Date$ + Version: $Revision$ + + + Copyright (c) Centre National d'Etudes Spatiales. All rights reserved. + See OTBCopyright.txt 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. + +=========================================================================*/ +#include "itkExceptionObject.h" +#include "otbImage.h" +#include "otbImageFileReader.h" +#include "otbImageFileWriter.h" +#include "otbKullbackLeiblerDistanceImageFilter.h" + + +int otbKullbackLeiblerDistanceImageFilter(int argc, char * argv[]) +{ + try + { + if(argc != 5) + { + std::cerr<<"Detection de changements par mesure de Kullback-Leibler, optimisee par un developpement de Edgeworth\n"; + std::cerr << argv[0] << " imgAv imgAp imgResu winSize\n"; + return 1; + } + + char * fileName1 = argv[1]; + char * fileName2 = argv[2]; + char * fileNameOut = argv[3]; + int winSize = atoi(argv[4]); + + + const unsigned int Dimension = 2; + typedef double PixelType; + + typedef otb::Image<PixelType,Dimension> ImageType; + typedef otb::KullbackLeiblerDistanceImageFilter<ImageType,ImageType,ImageType> FilterType; + typedef otb::ImageFileReader<ImageType> ReaderType; + typedef otb::ImageFileWriter<ImageType> WriterType; + + ReaderType::Pointer reader1 = ReaderType::New(); + reader1->SetFileName( fileName1 ); + + ReaderType::Pointer reader2 = ReaderType::New(); + reader2->SetFileName( fileName2 ); + + FilterType::Pointer filter = FilterType::New(); + filter->SetRadius( (winSize-1)/2 ); + filter->SetInput1( reader1->GetOutput() ); + filter->SetInput2( reader2->GetOutput() ); + + WriterType::Pointer writer = WriterType::New(); + writer->SetFileName(fileNameOut); + writer->SetInput(filter->GetOutput()); + writer->Update(); + + } + + catch( itk::ExceptionObject & err ) + { + std::cout << "Exception itk::ExceptionObject thrown !" << std::endl; + std::cout << err << std::endl; + return EXIT_FAILURE; + } + + catch( ... ) + { + std::cout << "Unknown exception thrown !" << std::endl; + return EXIT_FAILURE; + } + return EXIT_SUCCESS; +} diff --git a/Testing/Code/ChangeDetection/otbKullbackLeiblerDistanceImageFilterNew.cxx b/Testing/Code/ChangeDetection/otbKullbackLeiblerDistanceImageFilterNew.cxx new file mode 100644 index 0000000000000000000000000000000000000000..7f40408688888d9723f1dc55335a1bc48942f4b9 --- /dev/null +++ b/Testing/Code/ChangeDetection/otbKullbackLeiblerDistanceImageFilterNew.cxx @@ -0,0 +1,50 @@ +/*========================================================================= + + Program: ORFEO Toolbox + Language: C++ + Date: $Date$ + Version: $Revision$ + + + Copyright (c) Centre National d'Etudes Spatiales. All rights reserved. + See OTBCopyright.txt 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. + +=========================================================================*/ +#include "itkExceptionObject.h" +#include "otbImage.h" +#include "otbKullbackLeiblerDistanceImageFilter.h" + + +int otbKullbackLeiblerDistanceImageFilterNew(int argc, char * argv[]) +{ + try + { + const unsigned int Dimension = 2; + typedef double PixelType; + + typedef otb::Image<PixelType,Dimension> ImageType; + typedef otb::KullbackLeiblerDistanceImageFilter<ImageType,ImageType,ImageType> FilterType; + + FilterType::Pointer filter = FilterType::New(); + + } + + catch( itk::ExceptionObject & err ) + { + std::cout << "Exception itk::ExceptionObject thrown !" << std::endl; + std::cout << err << std::endl; + return EXIT_FAILURE; + } + + catch( ... ) + { + std::cout << "Unknown exception thrown !" << std::endl; + return EXIT_FAILURE; + } + return EXIT_SUCCESS; +}