Skip to content
Snippets Groups Projects
Commit 486141a2 authored by Charles Peyrega's avatar Charles Peyrega
Browse files

ENH: Add a Majority Voting Image Filter with the corresponding tests

parent 82fe1fda
Branches
Tags
No related merge requests found
/*=========================================================================
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 __otbNeighborhoodMajorityVotingImageFilter_h
#define __otbNeighborhoodMajorityVotingImageFilter_h
// First make sure that the configuration is available.
// This line can be removed once the optimized versions
// gets integrated into the main directories.
#include "itkConfigure.h"
#include "itkMorphologyImageFilter.h"
#include "itkBinaryBallStructuringElement.h"
namespace otb {
/**
* \class NeighborhoodMajorityVotingImageFilter
* \Majority Voting of an image
*
* Filters a labeled image using Majority Voting in a specified neighbordhood. Majority Voting takes the
* more representative value of all the pixels identified by the structuring element and then sets the
* center pixel to this label value.
*
* The structuring element is assumed to be composed of binary
* values (zero or one). Only elements of the structuring element
* having values > 0 are candidates for affecting the center pixel.
*
* For the each input image pixel,
* - NeighborhoodIterator gives neighbors of the pixel.
* - Evaluate() member function returns the more representative label value among
* the image neighbors where the kernel has elements > 0.
* - Replace the original label value with the more representative label value
*
* \sa MorphologyImageFilter, GrayscaleFunctionDilateImageFilter, BinaryDilateImageFilter
* \ingroup ImageEnhancement MathematicalMorphologyImageFilters
*/
/*
template<class TInputImage, class TOutputImage, class TKernel>
class ITK_EXPORT NeighborhoodMajorityVotingImageFilter :
public itk::MorphologyImageFilter<TInputImage, TOutputImage, TKernel>
{*/
template<class TInputImage>
class ITK_EXPORT NeighborhoodMajorityVotingImageFilter :
public itk::MorphologyImageFilter<
TInputImage,
TInputImage,
itk::BinaryBallStructuringElement< typename TInputImage::PixelType, TInputImage::ImageDimension >
>
{
public:
/** Standard class typedefs. */
typedef NeighborhoodMajorityVotingImageFilter Self;
//typedef itk::MorphologyImageFilter<TInputImage, TOutputImage, TKernel> Superclass;
typedef itk::MorphologyImageFilter<
TInputImage,
TInputImage,
itk::BinaryBallStructuringElement< typename TInputImage::PixelType, TInputImage::ImageDimension >
> Superclass;
typedef itk::SmartPointer<Self> Pointer;
typedef itk::SmartPointer<const Self> ConstPointer;
/** Standard New method. */
itkNewMacro(Self);
/** Runtime information support. */
itkTypeMacro(NeighborhoodMajorityVotingImageFilter,
itk::MorphologyImageFilter);
/** Declaration of pixel type. */
typedef typename Superclass::PixelType PixelType;
/** Kernel (structuring element) definition. */
typedef typename itk::BinaryBallStructuringElement< typename TInputImage::PixelType, TInputImage::ImageDimension > BallStructuringType;
/** Kernel (structuring element) iterator. */
typedef typename Superclass::KernelIteratorType KernelIteratorType;
/** Neighborhood iterator type. */
typedef typename Superclass::NeighborhoodIteratorType NeighborhoodIteratorType;
/** Kernel typedef. */
typedef typename Superclass::KernelType KernelType;
/** Radius typedef. */
typedef typename Superclass::RadiusType RadiusType;
/** Default boundary condition type */
typedef typename Superclass::DefaultBoundaryConditionType DefaultBoundaryConditionType;
/** ImageDimension constant */
itkStaticConstMacro(InputImageDimension, unsigned int,
TInputImage::ImageDimension);
/** NeighborhoodDimension constant */
itkStaticConstMacro(KernelDimension, unsigned int,
BallStructuringType::NeighborhoodDimension);
/** Type of the pixels in the Kernel. */
typedef typename BallStructuringType::PixelType KernelPixelType;
#ifdef ITK_USE_CONCEPT_CHECKING
/** Begin concept checking */
itkConceptMacro(SameDimensionCheck,
(itk::Concept::SameDimension<InputImageDimension, KernelDimension>));
itkConceptMacro(InputGreaterThanComparableCheck,
(itk::Concept::GreaterThanComparable<PixelType>));
itkConceptMacro(KernelGreaterThanComparableCheck,
(itk::Concept::GreaterThanComparable<KernelPixelType>));
/** End concept checking */
#endif
//Creates a SetNoDataValue method
virtual void SetNoDataValue(const PixelType _arg)
{
itkDebugMacro("setting NoDataValue to " << _arg);
if (this->m_NoDataValue != _arg)
{
this->m_NoDataValue = _arg;
m_MajorityVotingBoundaryCondition.SetConstant(m_NoDataValue);
this->OverrideBoundaryCondition(&m_MajorityVotingBoundaryCondition);
this->Modified();
}
}
//Creates a SetUndefinedValue method
itkSetMacro(UndefinedValue, PixelType);
//Creates a SetRadiusNeighborhood method
/** Set kernel (structuring element). */
virtual void SetRadiusNeighborhood(const RadiusType _arg)
{
itkDebugMacro("setting RadiusNeighborhood to " << _arg);
if (this->m_RadiusNeighborhood != _arg)
{
this->m_RadiusNeighborhood = _arg;
//Kernel Setting
BallStructuringType seBall;
seBall.SetRadius(m_RadiusNeighborhood);
seBall.CreateStructuringElement();
this->SetKernel(seBall);
this->Modified();
}
}
protected:
NeighborhoodMajorityVotingImageFilter();
~NeighborhoodMajorityVotingImageFilter() {};
/** Evaluate image neighborhood with kernel to find the new value
* for the center pixel value
*
* It will return the more representative label value of the labeled image pixels whose corresponding
* element in the structuring element is positive. This version of
* Evaluate is used for non-boundary pixels. */
PixelType Evaluate(const NeighborhoodIteratorType &nit,
const KernelIteratorType kernelBegin,
const KernelIteratorType kernelEnd);
private:
NeighborhoodMajorityVotingImageFilter(const Self&); //purposely not implemented
void operator=(const Self&); //purposely not implemented
// Default boundary condition for majority voting filter, defaults to
DefaultBoundaryConditionType m_MajorityVotingBoundaryCondition;
PixelType m_NoDataValue;
PixelType m_UndefinedValue;
RadiusType m_RadiusNeighborhood;
}; // end of class
} // end namespace otb
#ifndef ITK_MANUAL_INSTANTIATION
#include "otbNeighborhoodMajorityVotingImageFilter.txx"
#endif
#endif
/*=========================================================================
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 __otbNeighborhoodMajorityVotingImageFilter_txx
#define __otbNeighborhoodMajorityVotingImageFilter_txx
// First make sure that the configuration is available.
// This line can be removed once the optimized versions
// gets integrated into the main directories.
#include "itkConfigure.h"
#include "otbNeighborhoodMajorityVotingImageFilter.h"
namespace otb {
template<class TInputImage>
NeighborhoodMajorityVotingImageFilter<TInputImage>::NeighborhoodMajorityVotingImageFilter()
{
this->SetNoDataValue(itk::NumericTraits<PixelType>::NonpositiveMin());
this->SetUndefinedValue(itk::NumericTraits<PixelType>::NonpositiveMin());
RadiusType radInit;
radInit[0] = 1;
radInit[1] = 1;
this->SetRadiusNeighborhood(radInit);
}
template<class TInputImage>
typename NeighborhoodMajorityVotingImageFilter<TInputImage>::PixelType
NeighborhoodMajorityVotingImageFilter<TInputImage>::Evaluate(const NeighborhoodIteratorType &nit,
const KernelIteratorType kernelBegin,
const KernelIteratorType kernelEnd) {
/*
KernelIteratorType kernelBegin_ = this->GetKernel().Begin();
KernelIteratorType kernelEnd_ = this->GetKernel().End();
*/
PixelType majorityLabel = 0; //Value of the more representative pixels in the neighborhood
unsigned int majorityFreq = 0; //Number of pixels with the more representative value (majorityLabel) in the neighborhood
unsigned int i;
KernelIteratorType kernel_it;
std::map<PixelType, unsigned int> histoNeigh;
PixelType centerPixel = nit.GetCenterPixel();
if (centerPixel != m_NoDataValue){
for (i = 0, kernel_it = kernelBegin; kernel_it < kernelEnd; ++kernel_it, ++i) {
// if structuring element is positive, use the pixel under that element
// in the image
PixelType label = nit.GetPixel(i);
if ((*kernel_it > itk::NumericTraits<KernelPixelType>::Zero) && (label != m_NoDataValue)) {
// note we use GetPixel() on the SmartNeighborhoodIterator to
// respect boundary conditions
//If the current label has already been added to the histogram histoNeigh
if(histoNeigh.count(label) > 0){
histoNeigh[label]++;
}
else{
histoNeigh[label] = 1;
}
}
}
//Extraction of the more representative Label in the neighborhood (majorityLabel) and its Frequency (majorityFreq)
typename std::map<PixelType, unsigned int>::iterator histoIt;
for (histoIt = histoNeigh.begin(); histoIt != histoNeigh.end(); ++histoIt) {
if (histoIt->second > majorityFreq) {
majorityFreq = histoIt->second; //Frequency
majorityLabel = histoIt->first; //Label
}
}
//If the majorityLabel is NOT unique in the neighborhood
for (histoIt = histoNeigh.begin(); histoIt != histoNeigh.end(); ++histoIt) {
if ( (histoIt->second == majorityFreq) && (histoIt->first != majorityLabel) ) {
majorityLabel = m_UndefinedValue;
break;
}
}
}
//If centerPixel == m_NoDataValue
else{
majorityLabel = m_UndefinedValue;
}
return majorityLabel;
}
} // end namespace otb
#endif
...@@ -311,6 +311,21 @@ ADD_TEST(leTvSVMImageClassificationFilter ${LEARNING_TESTS3} ...@@ -311,6 +311,21 @@ ADD_TEST(leTvSVMImageClassificationFilter ${LEARNING_TESTS3}
${INPUTDATA}/ROI_QB_MUL_4.tif ${INPUTDATA}/ROI_QB_MUL_4.tif
${INPUTDATA}/svm_model_image ${INPUTDATA}/svm_model_image
${TEMP}/leSVMImageClassificationFilterOutput.tif) ${TEMP}/leSVMImageClassificationFilterOutput.tif)
# ------- otb::SVMImageClassificationFilterWithNeighborhoodMajorityVoting ---------------------------
ADD_TEST(leTvSVMImageClassificationFilterWithNeighborhoodMajorityVoting ${LEARNING_TESTS5}
--compare-image ${NOTOL}
${BASELINE}/leSVMImageClassificationWithNMVFilterOutput.tif
${TEMP}/leSVMImageClassificationWithNMVFilterOutput.tif
otbNeighborhoodMajorityVotingImageFilterTest
${TEMP}/leSVMImageClassificationFilterOutput.tif
${TEMP}/leSVMImageClassificationWithNMVFilterOutput.tif
)
SET_TESTS_PROPERTIES(leTvSVMImageClassificationFilterWithNeighborhoodMajorityVoting PROPERTIES DEPENDS leTvSVMImageClassificationFilter)
# ------- otb::SVMImageClassificationWithRuleFilter ---------------------- # ------- otb::SVMImageClassificationWithRuleFilter ----------------------
...@@ -628,6 +643,23 @@ ADD_TEST(leTvDecisionTreeWithRealValues ${LEARNING_TESTS5} ...@@ -628,6 +643,23 @@ ADD_TEST(leTvDecisionTreeWithRealValues ${LEARNING_TESTS5}
otbDecisionTreeWithRealValues) otbDecisionTreeWithRealValues)
ADD_TEST(leTuNeighborhoodMajorityVotingImageFilterNew ${LEARNING_TESTS5}
otbNeighborhoodMajorityVotingImageFilterNew)
ADD_TEST(leTvNeighborhoodMajorityVotingImageFilterTest ${LEARNING_TESTS5}
--compare-image ${NOTOL}
${BASELINE}/QB_1_ortho_4Cls_N_Classified_OTB_NMV_x2_y5.tif
${TEMP}/QB_1_ortho_4Cls_N_Classified_OTB_NMV_x2_y5.tif
otbNeighborhoodMajorityVotingImageFilterTest
${INPUTDATA}/Classification/QB_1_ortho_4Cls_N_Classified_OTB.tif
${TEMP}/QB_1_ortho_4Cls_N_Classified_OTB_NMV_x2_y5.tif
2 #xRadius
5 #yRadius
128 #NoDataValue
10 #UndefinedValue
)
# Testing srcs # Testing srcs
SET(BasicLearning_SRCS1 SET(BasicLearning_SRCS1
otbLearningTests1.cxx otbLearningTests1.cxx
...@@ -704,6 +736,8 @@ otbLearningTests5.cxx ...@@ -704,6 +736,8 @@ otbLearningTests5.cxx
otbDecisionTreeNew.cxx otbDecisionTreeNew.cxx
otbDecisionTreeBuild.cxx otbDecisionTreeBuild.cxx
otbDecisionTreeWithRealValues.cxx otbDecisionTreeWithRealValues.cxx
otbNeighborhoodMajorityVotingImageFilterNew.cxx
otbNeighborhoodMajorityVotingImageFilterTest.cxx
) )
OTB_ADD_EXECUTABLE(otbLearningTests1 "${BasicLearning_SRCS1}" "OTBLearning;OTBIO;OTBTesting") OTB_ADD_EXECUTABLE(otbLearningTests1 "${BasicLearning_SRCS1}" "OTBLearning;OTBIO;OTBTesting")
......
...@@ -27,4 +27,6 @@ void RegisterTests() ...@@ -27,4 +27,6 @@ void RegisterTests()
REGISTER_TEST(otbDecisionTreeNew); REGISTER_TEST(otbDecisionTreeNew);
REGISTER_TEST(otbDecisionTreeBuild); REGISTER_TEST(otbDecisionTreeBuild);
REGISTER_TEST(otbDecisionTreeWithRealValues); REGISTER_TEST(otbDecisionTreeWithRealValues);
REGISTER_TEST(otbNeighborhoodMajorityVotingImageFilterNew);
REGISTER_TEST(otbNeighborhoodMajorityVotingImageFilterTest);
} }
/*=========================================================================
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 "itkMacro.h"
#include "otbImage.h"
#include <iostream>
#include "otbNeighborhoodMajorityVotingImageFilter.h"
int otbNeighborhoodMajorityVotingImageFilterNew(int argc, char* argv[])
{
typedef unsigned char LabelPixelType;
const unsigned int Dimension = 2;
typedef otb::Image<LabelPixelType, Dimension> LabelImageType;
typedef otb::NeighborhoodMajorityVotingImageFilter<LabelImageType> NeighborhoodMajorityVotingFilterType;
NeighborhoodMajorityVotingFilterType::Pointer NeighMajVotingFilter = NeighborhoodMajorityVotingFilterType::New();
std::cout << NeighMajVotingFilter << std::endl;
return EXIT_SUCCESS;
}
/*=========================================================================
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 "itkMacro.h"
#include "otbImage.h"
#include <iostream>
#include <otbImageFileReader.h>
#include "otbStreamingImageFileWriter.h"
#include "otbNeighborhoodMajorityVotingImageFilter.h"
#include "itkTimeProbe.h"
int otbNeighborhoodMajorityVotingImageFilterTest(int argc, char* argv[]) {
typedef unsigned char LabelPixelType;
const unsigned int Dimension = 2;
typedef otb::Image<LabelPixelType, Dimension> LabelImageType;
typedef otb::ImageFileReader<LabelImageType> ReaderType;
typedef otb::StreamingImageFileWriter<LabelImageType> WriterType;
typedef otb::NeighborhoodMajorityVotingImageFilter<LabelImageType> NeighborhoodMajorityVotingFilterType;
typedef NeighborhoodMajorityVotingFilterType::RadiusType RadiusType;
const char * inputFileName = argv[1];
const char * outputFileName = argv[2];
ReaderType::Pointer reader = ReaderType::New();
reader->SetFileName(inputFileName);
//NEIGHBORHOOD MAJORITY FILTER
NeighborhoodMajorityVotingFilterType::Pointer NeighMajVotingFilter;
NeighMajVotingFilter = NeighborhoodMajorityVotingFilterType::New();
NeighMajVotingFilter->SetInput(reader->GetOutput());
if(argc >= 4)
{
RadiusType rad;
rad[0] = atoi(argv[3]);
rad[1] = atoi(argv[4]);
NeighMajVotingFilter->SetRadiusNeighborhood(rad);
if(argc >= 5)
{
NeighMajVotingFilter->SetNoDataValue(atoi(argv[5]));
if(argc >= 6)
{
NeighMajVotingFilter->SetUndefinedValue(atoi(argv[6]));
}
}
}
WriterType::Pointer writer = WriterType::New();
writer->SetFileName(outputFileName);
writer->SetInput(NeighMajVotingFilter->GetOutput());
writer->Update();
return EXIT_SUCCESS;
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment