Skip to content
Snippets Groups Projects
Commit 15fba6ef authored by Ludovic Hussonnois's avatar Ludovic Hussonnois
Browse files

Merge remote-tracking branch 'remotes/origin/app_morphological' into develop

parents 6686547a 246c30c3
No related branches found
No related tags found
No related merge requests found
Showing
with 928 additions and 2 deletions
......@@ -2,6 +2,7 @@ set(OTBAppMorphology_LINK_LIBS
${OTBImageBase_LIBRARIES}
${OTBApplicationEngine_LIBRARIES}
${OTBObjectList_LIBRARIES}
${OTBMorphologicalProfiles_LIBRARIES}
)
otb_create_application(
......@@ -13,3 +14,19 @@ otb_create_application(
NAME GrayScaleMorphologicalOperation
SOURCES otbGrayScaleMorphologicalOperation.cxx
LINK_LIBRARIES ${${otb-module}_LIBRARIES})
otb_create_application(
NAME MorphologicalClassification
SOURCES otbMorphologicalClassification.cxx
LINK_LIBRARIES ${${otb-module}_LIBRARIES})
otb_create_application(
NAME MorphologicalProfilesAnalysis
SOURCES otbMorphologicalProfilesAnalysis.cxx
LINK_LIBRARIES ${${otb-module}_LIBRARIES})
otb_create_application(
NAME MorphologicalMultiScaleDecomposition
SOURCES otbMorphologicalMultiScaleDecomposition.cxx
LINK_LIBRARIES ${${otb-module}_LIBRARIES})
/*=========================================================================
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 "otbGeodesicMorphologyDecompositionImageFilter.h"
#include "otbWrapperApplication.h"
#include "otbWrapperApplicationFactory.h"
#include "itkBinaryBallStructuringElement.h"
#include "itkBinaryCrossStructuringElement.h"
#include "itkBinaryDilateImageFilter.h"
#include "itkBinaryErodeImageFilter.h"
#include "itkBinaryMorphologicalOpeningImageFilter.h"
#include "itkBinaryMorphologicalClosingImageFilter.h"
#include "otbMultiToMonoChannelExtractROI.h"
#include "otbImageList.h"
#include "otbImageListToVectorImageFilter.h"
#include "itkTimeProbe.h"
#include "otbConvexOrConcaveClassificationFilter.h"
#include "otbMorphologicalProfilesSegmentationFilter.h"
namespace otb {
namespace Wrapper {
class MorphologicalClassification : public Application {
public:
/** Standard class typedefs. */
typedef MorphologicalClassification Self;
typedef Application Superclass;
typedef itk::SmartPointer<Self> Pointer;
typedef itk::SmartPointer<const Self> ConstPointer;
typedef FloatVectorImageType::InternalPixelType InputPixelType;
typedef otb::Image<InputPixelType, 2> FloatImageType;
typedef unsigned short LabeledPixelType;
typedef otb::Image<LabeledPixelType, 2> LabeledImageType;
typedef otb::MultiToMonoChannelExtractROI<FloatVectorImageType::InternalPixelType, InputPixelType>
ExtractorFilterType;
typedef otb::ConvexOrConcaveClassificationFilter<FloatImageType, LabeledImageType> ClassificationFilterType;
typedef itk::BinaryBallStructuringElement<InputPixelType, 2> BallStructuringElementType;
typedef itk::BinaryCrossStructuringElement<InputPixelType, 2> CrossStructuringElementType;
/** Standard macro */
itkNewMacro( Self );
itkTypeMacro( MorphologicalClassification, otb::Application );
private:
void DoInit() ITK_OVERRIDE
{
SetName( "MorphologicalClassification" );
SetDescription( "Performs morphological convex, concave and flat classification on an input image channel" );
// Documentation
SetDocName( "Morphological Classification" );
SetDocLongDescription( "This algorithm is based on the following publication:\n"
"\n"
"Martino Pesaresi and Jon Alti Benediktsson, Member, IEEE: A new approach\n"
"for the morphological segmentation of high resolution satellite imagery.\n"
"IEEE Transactions on geoscience and remote sensing, vol. 39, NO. 2,\n"
"February 2001, p. 309-320.\n"
"\n"
"This application perform the following decision rule to classify a pixel\n"
"between the three classes Convex, Concave and Flat. Let :math:`f` denote\n"
"the input image and :math:`\\psi_{N}(f)` the geodesic leveling of\n"
":math:`f` with a structuring element of size :math:`N`. One can derive\n"
"the following decision rule to classify :math:`f` into Convex (label\n"
":math:`\\stackrel{\\smile}{k}`), Concave (label\n"
":math:`\\stackrel{rown}{k}`) and Flat (label :math:`\\bar{k}`): \n"
"\n"
":math:`f(n) = \\begin{cases} \\stackrel{\\smile}{k} &:& f-\\psi_{N}(f)>\\sigma \\\\ \\stackrel{\\frown}{k} &:& \\psi_{N}(f)-f>\\sigma\\\\ \\bar{k}&:&\\mid f - \\psi_{N}(f) \\mid \\leq \\sigma \\end{cases}`"
"\n\n"
"This output is a labeled image (0 : Flat, 1 : Convex, 2 : Concave)" );
SetDocLimitations( "Generation of the morphological classification is not streamable, pay attention to this fact when setting the radius size of the structuring element." );
SetDocAuthors( "OTB-Team" );
SetDocSeeAlso( "otbConvexOrConcaveClassificationFilter class" );
AddDocTag( "MorphologicalClassification" );
AddParameter( ParameterType_InputImage, "in", "Input Image" );
SetParameterDescription( "in", "The input image to be classified." );
AddParameter( ParameterType_OutputImage, "out", "Output Image" );
SetParameterDescription( "out",
"The output classified image with 3 different values (0 : Flat, 1 : Convex, 2 : Concave)" );
AddParameter( ParameterType_Int, "channel", "Selected Channel" );
SetParameterDescription( "channel", "The selected channel index for input image" );
SetDefaultParameterInt( "channel", 1 );
SetMinimumParameterIntValue( "channel", 1 );
AddRAMParameter();
// Structuring Element (Ball | Cross)
AddParameter( ParameterType_Choice, "structype", "Structuring Element Type" );
SetParameterDescription( "structype", "Choice of the structuring element type" );
AddChoice( "structype.ball", "Ball" );
AddChoice( "structype.cross", "Cross" );
AddParameter( ParameterType_Int, "radius", "Radius" );
SetParameterDescription( "radius", "Radius of the structuring element (in pixels)" );
SetDefaultParameterInt( "radius", 5 );
SetMinimumParameterIntValue( "radius", 1 );
AddParameter( ParameterType_Float, "sigma", "Sigma value for leveling tolerance" );
SetParameterDescription( "sigma", "Sigma value for leveling tolerance" );
SetDefaultParameterFloat( "sigma", 0.5 );
SetMinimumParameterFloatValue( "sigma", 0 );
SetDocExampleParameterValue( "in", "ROI_IKO_PAN_LesHalles.tif" );
SetDocExampleParameterValue( "channel", "1" );
SetDocExampleParameterValue( "structype", "ball" );
SetDocExampleParameterValue( "radius", "5" );
SetDocExampleParameterValue( "sigma", "0.5" );
SetDocExampleParameterValue( "out", "output.tif" );
}
void DoUpdateParameters() ITK_OVERRIDE
{
// Nothing to do here : all parameters are independent
}
void DoExecute() ITK_OVERRIDE
{
FloatVectorImageType::Pointer inImage = GetParameterImage( "in" );
int nBComp = inImage->GetNumberOfComponentsPerPixel();
int selectedChannel = GetParameterInt( "channel" );
if ( selectedChannel > nBComp )
{
itkExceptionMacro( << "The specified channel index for input image is invalid." );
}
m_ExtractorFilter = ExtractorFilterType::New();
m_ExtractorFilter->SetInput( inImage );
m_ExtractorFilter->SetStartX( static_cast<unsigned int>(inImage->GetLargestPossibleRegion().GetIndex( 0 )) );
m_ExtractorFilter->SetStartY( static_cast<unsigned int>(inImage->GetLargestPossibleRegion().GetIndex( 1 )) );
m_ExtractorFilter->SetSizeX( inImage->GetLargestPossibleRegion().GetSize( 0 ) );
m_ExtractorFilter->SetSizeY( inImage->GetLargestPossibleRegion().GetSize( 1 ) );
m_ExtractorFilter->SetChannel( static_cast<unsigned int>(selectedChannel) );
unsigned int sigma = static_cast<unsigned int>(GetParameterInt( "sigma" ));
unsigned int radius = static_cast<unsigned int>(GetParameterInt( "radius" ));
m_ClassificationFilter = ClassificationFilterType::New();
m_ClassificationFilter->SetInput( m_ExtractorFilter->GetOutput() );
m_ClassificationFilter->SetSigma( sigma );
m_ClassificationFilter->SetFlatLabel( 0 );
m_ClassificationFilter->SetConvexLabel( 1 );
m_ClassificationFilter->SetConcaveLabel( 2 );
if ( GetParameterString( "structype" ) == "ball" )
{
performClassification<BallStructuringElementType>( radius );
} else // Cross
{
performClassification<CrossStructuringElementType>( radius );
}
SetParameterOutputImage( "out", m_ClassificationFilter->GetOutput() );
}
template<typename TStructuringElement>
void performClassification(unsigned int radius_size) {
typedef otb::GeodesicMorphologyDecompositionImageFilter<FloatImageType, FloatImageType, TStructuringElement> TDecompositionImageFilter;
typename TDecompositionImageFilter::Pointer decompositionImageFilter;
decompositionImageFilter = TDecompositionImageFilter::New();
decompositionImageFilter->SetInput( m_ExtractorFilter->GetOutput() );
typename TStructuringElement::RadiusType radius;
radius.Fill( radius_size );
decompositionImageFilter->SetRadius( radius );
AddProcess(decompositionImageFilter, "Image Decomposition");
decompositionImageFilter->Update();
m_ClassificationFilter->SetInputLeveling( decompositionImageFilter->GetOutput() );
}
ExtractorFilterType::Pointer m_ExtractorFilter;
ClassificationFilterType::Pointer m_ClassificationFilter;
};
}
}
OTB_APPLICATION_EXPORT( otb::Wrapper::MorphologicalClassification )
/*=========================================================================
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 "itkComposeImageFilter.h"
#include "otbWrapperApplication.h"
#include "otbWrapperApplicationFactory.h"
#include "itkBinaryBallStructuringElement.h"
#include "itkBinaryCrossStructuringElement.h"
#include "itkBinaryDilateImageFilter.h"
#include "itkBinaryErodeImageFilter.h"
#include "itkBinaryMorphologicalOpeningImageFilter.h"
#include "itkBinaryMorphologicalClosingImageFilter.h"
#include "otbMultiToMonoChannelExtractROI.h"
#include "otbImageList.h"
#include "otbImageListToVectorImageFilter.h"
#include "itkTimeProbe.h"
#include "otbConvexOrConcaveClassificationFilter.h"
#include "otbMorphologicalProfilesSegmentationFilter.h"
#include "otbGeodesicMorphologyIterativeDecompositionImageFilter.h"
namespace otb {
namespace Wrapper {
class MorphologicalMultiScaleDecomposition : public Application {
public:
/** Standard class typedefs. */
typedef MorphologicalMultiScaleDecomposition Self;
typedef Application Superclass;
typedef itk::SmartPointer<Self> Pointer;
typedef itk::SmartPointer<const Self> ConstPointer;
typedef FloatVectorImageType::InternalPixelType InputVectorPixelType;
typedef otb::MultiToMonoChannelExtractROI<FloatVectorImageType::InternalPixelType, InputVectorPixelType>
ExtractorFilterType;
typedef itk::BinaryBallStructuringElement<InputVectorPixelType, 2> BallStructuringElementType;
typedef itk::BinaryCrossStructuringElement<InputVectorPixelType, 2> CrossStructuringElementType;
/** Standard macro */
itkNewMacro( Self );
itkTypeMacro( MorphologicalMultiScaleDecomposition, otb::Application );
private:
void DoInit() ITK_OVERRIDE
{
SetName( "MorphologicalMultiScaleDecomposition" );
SetDescription( "Perform a geodesic morphology based image analysis on an input image channel" );
// Documentation
SetDocName( "Morphological Multi Scale Decomposition" );
SetDocLongDescription(
"This application recursively apply geodesic decomposition. \n"
"\n"
"This algorithm is derived from the following publication:\n"
"\n"
"Martino Pesaresi and Jon Alti Benediktsson, Member, IEEE: A new approach "
"for the morphological segmentation of high resolution satellite imagery.\n"
"IEEE Transactions on geoscience and remote sensing, vol. 39, NO. 2, "
"February 2001, p. 309-320.\n"
"\n"
"It provides a geodesic decomposition of the input image, with the "
"following scheme. Let :math:`f_0` denote the input image, :math:`\\stackrel{\\smile}{\\mu}_{N}(f)` denote the convex membership function, :math:`\\stackrel{\\frown}{\\mu}_{N}(f)` denote the concave membership function and :math:`\\psi_{N}(f)` denote the leveling function, for a given radius :math:`N` as defined in the documentation\n"
"of the GeodesicMorphologyDecompositionImageFilter. Let :math:`[N_{1},\\ldots, N_{n}]` denote a range of increasing radius (or scales). The iterative decomposition is defined as follows:\n"
"\n"
"[ f\\_n = `\\psi`\\_N\\_n(f\\_n-1) ]\n"
"\n"
"[ :math:`\\stackrel{\\frown}{f}_n` = :math:`\\stackrel{\\frown}{\\mu}_N_N(f_n)` ]\n"
"\n"
"[ :math:`\\stackrel{\\smile}{f}_n` = :math:`\\stackrel{\\smile}{\\mu}_N_N(f_n)` ]\n"
"\n"
"The :math:`\\stackrel{\\frown}{f}_{n}` and :math:`\\stackrel{\\frown}{f}_{n}` are membership function for the convex\n"
"(resp. concave) objects whose size is comprised between :math:`N_{n-1}` and :math:`N_n`\n"
"\n"
"Output convex, concave and leveling images with N bands, where N is the number of levels." );
SetDocLimitations( "Generation of the multi scale decomposition is not streamable, pay attention to this fact when setting the number of iterating levels." );
SetDocAuthors( "OTB-Team" );
SetDocSeeAlso( "otbGeodesicMorphologyDecompositionImageFilter class" );
AddDocTag( "MorphologicalMultiScaleDecomposition" );
AddParameter( ParameterType_InputImage, "in", "Input Image" );
SetParameterDescription( "in", "The input image to be classified." );
AddParameter( ParameterType_OutputImage, "outconvex", "Output Convex Image" );
SetParameterDescription( "outconvex", "The output convex image with N bands" );
AddParameter( ParameterType_OutputImage, "outconcave", "Output Concave Image" );
SetParameterDescription( "outconcave", "The output concave concave with N bands" );
AddParameter( ParameterType_OutputImage, "outleveling", "Output Image" );
SetParameterDescription( "outleveling", "The output leveling image with N bands" );
AddParameter( ParameterType_Int, "channel", "Selected Channel" );
SetParameterDescription( "channel", "The selected channel index for input image" );
SetDefaultParameterInt( "channel", 1 );
SetMinimumParameterIntValue( "channel", 1 );
AddRAMParameter();
// Strucring Element (Ball | Cross)
AddParameter( ParameterType_Choice, "structype", "Structuring Element Type" );
SetParameterDescription( "structype", "Choice of the structuring element type" );
AddChoice( "structype.ball", "Ball" );
AddChoice( "structype.cross", "Cross" );
AddParameter( ParameterType_Int, "radius", "Initial radius" );
SetParameterDescription( "radius", "Initial radius of the structuring element (in pixels)" );
SetDefaultParameterInt( "radius", 5 );
SetMinimumParameterIntValue( "radius", 1 );
AddParameter( ParameterType_Int, "step", "Radius step." );
SetParameterDescription( "step", "Radius step along the profile (in pixels)" );
SetDefaultParameterInt( "step", 1 );
SetMinimumParameterIntValue( "step", 1 );
AddParameter( ParameterType_Int, "levels", "Number of levels use for multi scale" );
SetParameterDescription( "levels", "Number of levels use for multi scale" );
SetDefaultParameterInt( "levels", 1 );
SetMinimumParameterIntValue( "levels", 1 );
SetDocExampleParameterValue("in", "ROI_IKO_PAN_LesHalles.tif");
SetDocExampleParameterValue("structype", "ball");
SetDocExampleParameterValue("channel", "1");
SetDocExampleParameterValue("radius", "2");
SetDocExampleParameterValue("levels", "2");
SetDocExampleParameterValue("step", "3");
SetDocExampleParameterValue("outconvex", "convex.tif");
SetDocExampleParameterValue("outconcave", "concave.tif");
SetDocExampleParameterValue("outleveling", "leveling.tif");
}
void DoUpdateParameters() ITK_OVERRIDE
{
// Nothing to do here : all parameters are independent
}
void DoExecute() ITK_OVERRIDE
{
FloatVectorImageType::Pointer inImage = GetParameterImage( "in" );
int nBComp = inImage->GetNumberOfComponentsPerPixel();
int selectedChannel = GetParameterInt( "channel" );
if ( selectedChannel > nBComp )
{
itkExceptionMacro( << "The specified channel index for input image is invalid." );
}
m_ExtractorFilter = ExtractorFilterType::New();
m_ExtractorFilter->SetInput( inImage );
m_ExtractorFilter->SetStartX( static_cast<unsigned int>(inImage->GetLargestPossibleRegion().GetIndex( 0 )) );
m_ExtractorFilter->SetStartY( static_cast<unsigned int>(inImage->GetLargestPossibleRegion().GetIndex( 1 )) );
m_ExtractorFilter->SetSizeX( inImage->GetLargestPossibleRegion().GetSize( 0 ) );
m_ExtractorFilter->SetSizeY( inImage->GetLargestPossibleRegion().GetSize( 1 ) );
m_ExtractorFilter->SetChannel( static_cast<unsigned int>(GetParameterInt( "channel" )) );
unsigned int numberOfLevels = static_cast<unsigned int>(GetParameterInt( "levels" ));
unsigned int initValue = static_cast<unsigned int>(GetParameterInt( "radius" ));
unsigned int step = static_cast<unsigned int>(GetParameterInt( "step" ));
if ( GetParameterString( "structype" ) == "ball" )
{
performDecomposition<BallStructuringElementType>( numberOfLevels, step, initValue );
} else // Cross
{
performDecomposition<CrossStructuringElementType>( numberOfLevels, step, initValue );
}
}
template<typename StructuringElement>
void performDecomposition(unsigned int numberOfLevels, unsigned int step, unsigned int initValue) {
typedef otb::GeodesicMorphologyIterativeDecompositionImageFilter<FloatImageType, StructuringElement> TDecompositionImageFilter;
typedef typename TDecompositionImageFilter::OutputImageListType TImageList;
typedef otb::ImageListToVectorImageFilter<TImageList, FloatVectorImageType> TListToVectorImageFilter;
typename TDecompositionImageFilter::Pointer decompositionImageFilter;
decompositionImageFilter = TDecompositionImageFilter::New();
decompositionImageFilter->SetInput( m_ExtractorFilter->GetOutput() );
decompositionImageFilter->SetNumberOfIterations( numberOfLevels );
decompositionImageFilter->SetInitialValue( initValue );
decompositionImageFilter->SetStep( step );
AddProcess(decompositionImageFilter, "Image Decomposition");
decompositionImageFilter->Update();
typename TListToVectorImageFilter::Pointer levelingListToVectorImageFilter = TListToVectorImageFilter::New();
typename TListToVectorImageFilter::Pointer concaveListToVectorImageFilter = TListToVectorImageFilter::New();
typename TListToVectorImageFilter::Pointer convexListToVectorImageFilter = TListToVectorImageFilter::New();
levelingListToVectorImageFilter->SetInput( decompositionImageFilter->GetOutput() );
levelingListToVectorImageFilter->Update();
SetParameterOutputImage( "outleveling", levelingListToVectorImageFilter->GetOutput() );
concaveListToVectorImageFilter->SetInput( decompositionImageFilter->GetConcaveOutput() );
concaveListToVectorImageFilter->Update();
SetParameterOutputImage( "outconcave", concaveListToVectorImageFilter->GetOutput() );
convexListToVectorImageFilter->SetInput( decompositionImageFilter->GetConvexOutput() );
convexListToVectorImageFilter->Update();
SetParameterOutputImage( "outconvex", convexListToVectorImageFilter->GetOutput() );
}
ExtractorFilterType::Pointer m_ExtractorFilter;
};
}
}
OTB_APPLICATION_EXPORT( otb::Wrapper::MorphologicalMultiScaleDecomposition )
/*=========================================================================
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 <itkComposeImageFilter.h>
#include "otbWrapperApplication.h"
#include "otbWrapperApplicationFactory.h"
#include "itkBinaryBallStructuringElement.h"
#include "itkBinaryCrossStructuringElement.h"
#include "itkBinaryDilateImageFilter.h"
#include "itkBinaryErodeImageFilter.h"
#include "itkBinaryMorphologicalOpeningImageFilter.h"
#include "itkBinaryMorphologicalClosingImageFilter.h"
#include "otbMultiToMonoChannelExtractROI.h"
#include "otbImageList.h"
#include "otbImageListToVectorImageFilter.h"
#include "itkTimeProbe.h"
#include "otbConvexOrConcaveClassificationFilter.h"
#include "otbMorphologicalProfilesSegmentationFilter.h"
#include "otbGeodesicMorphologyIterativeDecompositionImageFilter.h"
namespace otb
{
namespace Wrapper
{
class MorphologicalProfilesAnalysis : public Application
{
public:
/** Standard class typedefs. */
typedef MorphologicalProfilesAnalysis Self;
typedef Application Superclass;
typedef itk::SmartPointer<Self> Pointer;
typedef itk::SmartPointer<const Self> ConstPointer;
typedef FloatVectorImageType::InternalPixelType InputPixelType;
typedef unsigned short LabeledPixelType;
typedef otb::Image<LabeledPixelType, 2> LabeledImageType;
typedef otb::MultiToMonoChannelExtractROI<InputPixelType, InputPixelType> ExtractorFilterType;
typedef itk::BinaryBallStructuringElement<InputPixelType, 2> BallStructuringElementType;
typedef itk::BinaryCrossStructuringElement<InputPixelType, 2> CrossStructuringElementType;
/** Standard macro */
itkNewMacro( Self );
itkTypeMacro( MorphologicalProfilesAnalysis, otb::Application );
private:
void DoInit() ITK_OVERRIDE
{
SetName( "MorphologicalProfilesAnalysis" );
SetDescription( "Performs morphological profiles analysis on an input image channel." );
// Documentation
SetDocName( "Morphological Profiles Analysis" );
SetDocLongDescription( "This algorithm is derived from the following publication:\n"
"\n"
"Martino Pesaresi and Jon Alti Benediktsson, Member, IEEE: A new approach\n"
"for the morphological segmentation of high resolution satellite imagery.\n"
"IEEE Transactions on geoscience and remote sensing, vol. 39, NO. 2,\n"
"February 2001, p. 309-320.\n"
"\n"
"Depending of the profile selection, the application provides::\n\n"
"- The multi scale geodesic morphological opening or closing profile of the input image.\n"
"- The multi scale derivative of the opening or closing profile.\n"
"- The parameter (called characteristic) of the maximum derivative value of the multi scale closing or opening profile for which this maxima occurs.\n"
"- The labeled classification of the input image.\n"
"\n"
"The behavior of the classification is :\n"
"\n"
"Given :math:`x_1` and :math:`x_2` two membership values,\n"
":math:`L_1, L_2` two labels associated, and :math:`\\sigma` a tolerance\n"
"value, the following decision rule is applied:\n"
"\n"
":math:`L = \\begin{cases} L_{1} &:& x_{1}>x_{2} \\quad and \\quad x_{1}>\\sigma\\\\ L_{2} &:& x_{2}>x_{1} \\quad and \\quad x_{2}>\\sigma\\\\ 0 &:& otherwise. \\end{cases}`"
"\n"
"The output image can be :"
"- A :math:`N` multi band image for the opening/closing normal or derivative profiles.\n"
"- A mono band image for the opening/closing characteristics.\n"
"- A labeled image for the classification\n" );
SetDocLimitations( "Generation of the morphological profile is not streamable, pay attention to this fact when setting the radius initial size and step of the structuring element." );
SetDocAuthors( "OTB-Team" );
SetDocSeeAlso( "otbMorphologicalOpeningProfileFilter, otbMorphologicalClosingProfileFilter, otbProfileToProfileDerivativeFilter, otbProfileDerivativeToMultiScaleCharacteristicsFilter, otbMultiScaleConvexOrConcaveClassificationFilter, classes" );
AddDocTag( "MorphologicalProfilesAnalysis" );
AddParameter( ParameterType_InputImage, "in", "Input Image" );
SetParameterDescription( "in", "The input image." );
AddParameter( ParameterType_OutputImage, "out", "Output Image" );
SetParameterDescription( "out", "The output image." );
AddParameter( ParameterType_Int, "channel", "Selected Channel" );
SetParameterDescription( "channel", "The selected channel index for input image" );
SetDefaultParameterInt( "channel", 1 );
SetMinimumParameterIntValue( "channel", 1 );
AddRAMParameter();
// Structuring Element (Ball | Cross)
AddParameter( ParameterType_Choice, "structype", "Structuring Element Type" );
SetParameterDescription( "structype", "Choice of the structuring element type" );
AddChoice( "structype.ball", "Ball" );
AddChoice( "structype.cross", "Cross" );
AddParameter( ParameterType_Int, "size", "Profile Size" );
SetParameterDescription( "size", "Size of the profiles" );
SetDefaultParameterInt( "size", 5 );
SetMinimumParameterIntValue( "size", 2 );
AddParameter( ParameterType_Int, "radius", "Initial radius" );
SetParameterDescription( "radius", "Initial radius of the structuring element (in pixels)" );
SetDefaultParameterInt( "radius", 5 );
SetMinimumParameterIntValue( "radius", 1 );
AddParameter( ParameterType_Int, "step", "Radius step." );
SetParameterDescription( "step", "Radius step along the profile (in pixels)" );
SetDefaultParameterInt( "step", 1 );
SetMinimumParameterIntValue( "step", 1 );
AddParameter( ParameterType_Choice, "profile", "Profile" );
SetParameterDescription( "profile", "" );
AddChoice( "profile.opening", "opening" );
AddChoice( "profile.closing", "closing" );
AddChoice( "profile.derivativeopening", "derivativeopening" );
AddChoice( "profile.derivativeclosing", "derivativeclosing" );
AddChoice( "profile.openingcharacteristics", "openingcharacteristics" );
AddChoice( "profile.closingcharacteristics", "closingcharacteristics" );
AddChoice( "profile.classification", "classification" );
AddParameter( ParameterType_Float, "profile.classification.sigma", "Sigma value for leveling tolerance" );
SetParameterDescription( "profile.classification.sigma", "Sigma value for leveling tolerance" );
SetDefaultParameterFloat( "profile.classification.sigma", 1 );
SetMinimumParameterFloatValue( "profile.classification.sigma", 0 );
SetDocExampleParameterValue( "in", "ROI_IKO_PAN_LesHalles.tif" );
SetDocExampleParameterValue( "channel", "1" );
SetDocExampleParameterValue( "structype", "ball" );
SetDocExampleParameterValue( "profile", "classification" );
SetDocExampleParameterValue( "size", "5" );
SetDocExampleParameterValue( "radius", "1" );
SetDocExampleParameterValue( "step", "1" );
SetDocExampleParameterValue( "profile.classification.sigma", "1" );
SetDocExampleParameterValue( "out", "output.tif" );
}
void DoUpdateParameters() ITK_OVERRIDE
{
// Nothing to do here : all parameters are independent
}
void DoExecute() ITK_OVERRIDE
{
FloatVectorImageType::Pointer inImage = GetParameterImage( "in" );
int nBComp = inImage->GetNumberOfComponentsPerPixel();
int selectedChannel = GetParameterInt( "channel" );
if( selectedChannel > nBComp )
{
itkExceptionMacro( << "The specified channel index for input image is invalid." );
}
m_ExtractorFilter = ExtractorFilterType::New();
m_ExtractorFilter->SetInput( inImage );
m_ExtractorFilter->SetStartX( static_cast<unsigned int>(inImage->GetLargestPossibleRegion().GetIndex( 0 )) );
m_ExtractorFilter->SetStartY( static_cast<unsigned int>(inImage->GetLargestPossibleRegion().GetIndex( 1 )) );
m_ExtractorFilter->SetSizeX( inImage->GetLargestPossibleRegion().GetSize( 0 ) );
m_ExtractorFilter->SetSizeY( inImage->GetLargestPossibleRegion().GetSize( 1 ) );
m_ExtractorFilter->SetChannel( static_cast<unsigned int>(GetParameterInt( "channel" )) );
unsigned int profileSize = static_cast<unsigned int>(GetParameterInt( "size" ));
unsigned short initValue = static_cast<unsigned short>(GetParameterInt( "radius" ));
unsigned short step = static_cast<unsigned short>(GetParameterInt( "step" ));
float sigma = GetParameterFloat( "profile.classification.sigma" );
std::string profile = GetParameterString( "profile" );
if ( GetParameterString( "structype" ) == "ball" )
{
performProfileAnalysis<BallStructuringElementType>( profile, profileSize, initValue, step, sigma );
}
else // Cross
{
performProfileAnalysis<CrossStructuringElementType>( profile, profileSize, initValue, step, sigma );
}
}
template<typename StructuringElementType>
void
performProfileAnalysis(std::string profile, unsigned int profileSize, unsigned short initValue,
unsigned short step, float sigma) {
typedef otb::MorphologicalOpeningProfileFilter<FloatImageType, FloatImageType, StructuringElementType> OpeningProfileFilterType;
typedef otb::MorphologicalClosingProfileFilter<FloatImageType, FloatImageType, StructuringElementType> ClosingProfileFilterType;
typedef otb::ProfileToProfileDerivativeFilter<FloatImageType, FloatImageType> DerivativeFilterType;
typedef otb::MultiScaleConvexOrConcaveClassificationFilter<FloatImageType, LabeledImageType> MultiScaleClassificationFilterType;
typedef otb::ProfileDerivativeToMultiScaleCharacteristicsFilter<FloatImageType, FloatImageType, LabeledImageType> MultiScaleCharacteristicsFilterType;
// Instantiation
typename OpeningProfileFilterType::Pointer oprofileFilter;
typename ClosingProfileFilterType::Pointer cprofileFilter;
typename DerivativeFilterType::Pointer oderivativeFilter;
typename DerivativeFilterType::Pointer cderivativeFilter;
typename MultiScaleCharacteristicsFilterType::Pointer omsCharFilter;
typename MultiScaleCharacteristicsFilterType::Pointer cmsCharFilter;
typename MultiScaleClassificationFilterType::Pointer classificationFilter;
bool classify = profile == "classification";
bool opening = profile == "opening";
bool closing = profile == "closing";
bool characOpening = profile == "openingcharacteristics";
bool characClosing = profile == "closingcharacteristics";
bool derivativeOpening = profile == "derivativeopening";
bool derivativeClosing = profile == "derivativeclosing";
bool doOpening = classify || opening || derivativeOpening || characOpening;
bool doClosing = classify || closing || derivativeClosing || characClosing;
if ( doOpening )
{
performOperations<OpeningProfileFilterType, DerivativeFilterType, MultiScaleCharacteristicsFilterType>(
oprofileFilter, oderivativeFilter, omsCharFilter,
opening, derivativeOpening, characOpening,
profileSize, step, initValue );
if ( !classify )
return;
}
if ( doClosing )
{
performOperations<ClosingProfileFilterType, DerivativeFilterType, MultiScaleCharacteristicsFilterType>(
cprofileFilter, cderivativeFilter, cmsCharFilter,
closing, derivativeClosing, characClosing,
profileSize, step, initValue );
if ( !classify )
return;
}
classificationFilter = MultiScaleClassificationFilterType::New();
classificationFilter->SetOpeningProfileDerivativeMaxima( omsCharFilter->GetOutput() );
classificationFilter->SetOpeningProfileCharacteristics( omsCharFilter->GetOutputCharacteristics() );
classificationFilter->SetClosingProfileDerivativeMaxima( cmsCharFilter->GetOutput() );
classificationFilter->SetClosingProfileCharacteristics( cmsCharFilter->GetOutputCharacteristics() );
classificationFilter->SetSigma( sigma );
classificationFilter->SetLabelSeparator( static_cast<unsigned short>(initValue + profileSize * step) );
AddProcess(classificationFilter, "Classification");
classificationFilter->Update();
SetParameterOutputImage( "out", classificationFilter->GetOutput() );
}
template<typename TProfileFilter, typename TDerivativeFilter, typename TCharacteristicsFilter>
void
performOperations(typename TProfileFilter::Pointer &profileFilter,
typename TDerivativeFilter::Pointer &derivativeFilter,
typename TCharacteristicsFilter::Pointer &msCharFilter,
bool profile, bool derivative, bool characteristics,
unsigned int profileSize, unsigned short initValue, unsigned short step) {
typedef ImageList<FloatImageType> TImageList;
typedef otb::ImageListToVectorImageFilter<TImageList, FloatVectorImageType> TListToVectorImageFilter;
profileFilter = TProfileFilter::New();
profileFilter->SetInput( m_ExtractorFilter->GetOutput() );
profileFilter->SetProfileSize( profileSize );
profileFilter->SetInitialValue( initValue );
profileFilter->SetStep( step );
if ( profile )
{
TListToVectorImageFilter::Pointer listToVectorImageFilter = TListToVectorImageFilter::New();
listToVectorImageFilter->SetInput( profileFilter->GetOutput() );
AddProcess(listToVectorImageFilter, "Profile");
listToVectorImageFilter->Update();
SetParameterOutputImage( "out", listToVectorImageFilter->GetOutput() );
return;
}
derivativeFilter = TDerivativeFilter::New();
derivativeFilter->SetInput( profileFilter->GetOutput() );
if ( derivative )
{
TListToVectorImageFilter::Pointer listToVectorImageFilter = TListToVectorImageFilter::New();
listToVectorImageFilter->SetInput( derivativeFilter->GetOutput() );
AddProcess(listToVectorImageFilter, "Derivative");
listToVectorImageFilter->Update();
SetParameterOutputImage( "out", listToVectorImageFilter->GetOutput() );
return;
}
msCharFilter = TCharacteristicsFilter::New();
msCharFilter->SetInput( derivativeFilter->GetOutput() );
msCharFilter->SetInitialValue( initValue );
msCharFilter->SetStep( step );
if ( characteristics )
{
AddProcess(msCharFilter, "Characteristics");
msCharFilter->Update();
SetParameterOutputImage( "out", msCharFilter->GetOutputCharacteristics() );
}
}
ExtractorFilterType::Pointer m_ExtractorFilter;
};
}
}
OTB_APPLICATION_EXPORT( otb::Wrapper::MorphologicalProfilesAnalysis )
......@@ -6,6 +6,7 @@ otb_module(OTBAppMorphology
OTBObjectList
OTBITK
OTBApplicationEngine
OTBMorphologicalProfiles
TEST_DEPENDS
OTBTestKernel
......
......@@ -28,3 +28,101 @@ otb_test_application(NAME apTvFEGrayScaleMorphologicalOperation
${BASELINE}/apTvFEGrayScaleMorphologicalOperation.tif
${TEMP}/apTvFEGrayScaleMorphologicalOperation.tif)
#----------- MorphologicalMultiScaleDecomposition TESTS ----------------
otb_test_application(NAME apTvFEMorphologicalMultiScaleDecomposition
APP MorphologicalMultiScaleDecomposition
OPTIONS -in ${INPUTDATA}/ROI_IKO_PAN_LesHalles.tif
-channel 1
-radius 4
-structype ball
-outleveling ${TEMP}/apTvFEMorphologicalMultiScaleDecomposition_levelingIteration_1.tif
-outconcave ${TEMP}/apTvFEMorphologicalMultiScaleDecomposition_concaveIteration_1.tif
-outconvex ${TEMP}/apTvFEMorphologicalMultiScaleDecomposition_convexIteration_1.tif
VALID --compare-n-images ${NOTOL} 3
${BASELINE}/apTvFEMorphologicalMultiScaleDecomposition_levelingIteration_1.tif
${TEMP}/apTvFEMorphologicalMultiScaleDecomposition_levelingIteration_1.tif
${BASELINE}/apTvFEMorphologicalMultiScaleDecomposition_concaveIteration_1.tif
${TEMP}/apTvFEMorphologicalMultiScaleDecomposition_concaveIteration_1.tif
${BASELINE}/apTvFEMorphologicalMultiScaleDecomposition_convexIteration_1.tif
${TEMP}/apTvFEMorphologicalMultiScaleDecomposition_convexIteration_1.tif)
#----------- Iterative MorphologicalMultiScaleDecomposition TESTS ----------------
otb_test_application(NAME apTvFEMorphologicalMultiScaleDecompositionIteratif
APP MorphologicalMultiScaleDecomposition
OPTIONS -in ${INPUTDATA}/ROI_IKO_PAN_LesHalles.tif
-channel 1
-levels 2
-structype ball
-step 3
-radius 2
-outleveling ${TEMP}/apTvFEMorphologicalMultiScaleDecomposition_levelingIteration_2.tif
-outconcave ${TEMP}/apTvFEMorphologicalMultiScaleDecomposition_concaveIteration_2.tif
-outconvex ${TEMP}/apTvFEMorphologicalMultiScaleDecomposition_convexIteration_2.tif
VALID --compare-n-images ${NOTOL} 3
${BASELINE}/apTvFEMorphologicalMultiScaleDecomposition_levelingIteration_2.tif
${TEMP}/apTvFEMorphologicalMultiScaleDecomposition_levelingIteration_2.tif
${BASELINE}/apTvFEMorphologicalMultiScaleDecomposition_concaveIteration_2.tif
${TEMP}/apTvFEMorphologicalMultiScaleDecomposition_concaveIteration_2.tif
${BASELINE}/apTvFEMorphologicalMultiScaleDecomposition_convexIteration_2.tif
${TEMP}/apTvFEMorphologicalMultiScaleDecomposition_convexIteration_2.tif)
#----------- MorphologicalClassification TESTS ----------------
otb_test_application(NAME apTvFEMorphologicalClassification
APP MorphologicalClassification
OPTIONS -in ${INPUTDATA}/ROI_IKO_PAN_LesHalles.tif
-channel 1
-structype ball
-radius 5
-sigma 0.5
-out ${TEMP}/apTvFEMorphologicalProfilesClassification.tif
VALID --compare-image ${NOTOL}
${BASELINE}/apTvFEMorphologicalProfilesClassification.tif
${TEMP}/apTvFEMorphologicalProfilesClassification.tif)
#----------- Opening MorphologicalProfilesAnalysis TESTS ----------------
otb_test_application(NAME apTvFEMorphologicalProfilesAnalysisOpening
APP MorphologicalProfilesAnalysis
OPTIONS -in ${INPUTDATA}/ROI_IKO_PAN_LesHalles.tif
-channel 1
-structype ball
-profile closing
-size 4
-radius 1
-step 1
-out ${TEMP}/apTvFEMorphologicalProfilesOpeningAnalysis.tif)
#----------- Closing MorphologicalProfilesAnalysis TESTS ----------------
otb_test_application(NAME apTvFEMorphologicalProfilesAnalysisClosing
APP MorphologicalProfilesAnalysis
OPTIONS -in ${INPUTDATA}/ROI_IKO_PAN_LesHalles.tif
-channel 1
-structype ball
-profile closing
-size 4
-radius 1
-step 1
-out ${TEMP}/apTvFEMorphologicalProfilesClosingAnalysis.tif
VALID --compare-image ${NOTOL}
${BASELINE}/apTvFEMorphologicalProfilesClosingAnalysis.tif
${TEMP}/apTvFEMorphologicalProfilesClosingAnalysis.tif)
#----------- Classfication MorphologicalProfilesAnalysis TESTS ----------------
otb_test_application(NAME apTvFEMorphologicalProfilesAnalysisClassification
APP MorphologicalProfilesAnalysis
OPTIONS -in ${INPUTDATA}/ROI_IKO_PAN_LesHalles.tif
-channel 1
-structype ball
-profile classification
-size 5
-radius 1
-step 1
-profile.classification.sigma 1
-out ${TEMP}/apTvFEMorphologicalProfilesClosingAnalysis.tif
VALID --compare-image ${NOTOL}
${BASELINE}/msMultiScaleConvexOrConcaveClassificationFilterOutput.tif
${TEMP}/apTvFEMorphologicalProfilesClosingAnalysis.tif)
......@@ -174,6 +174,8 @@ private:
bool m_PreserveIntensities;
/** Use fully connected morphological operators */
bool m_FullyConnected;
/** Progress accumulator to report internal filter progress */
itk::ProgressAccumulator::Pointer m_Progress;
};
} // End namespace otb
......
......@@ -36,12 +36,24 @@ GeodesicMorphologyDecompositionImageFilter<TInputImage, TOutputImage, TStructuri
m_Radius.Fill(1);
// Create a process accumulator for tracking the progress of minipipeline
m_Progress = itk::ProgressAccumulator::New();
m_Progress->SetMiniPipelineFilter(this);
m_OpeningFilter = OpeningFilterType::New();
m_ClosingFilter = ClosingFilterType::New();
m_LevelingFilter = LevelingFilterType::New();
m_ConvexFilter = ConvexFilterType::New();
m_ConcaveFilter = ConcaveFilterType::New();
// Register Internal Filter for progress
m_Progress->RegisterInternalFilter(m_OpeningFilter, 0.2);
m_Progress->RegisterInternalFilter(m_ClosingFilter, 0.2);
m_Progress->RegisterInternalFilter(m_LevelingFilter, 0.2);
m_Progress->RegisterInternalFilter(m_ConvexFilter, 0.2);
m_Progress->RegisterInternalFilter(m_ConcaveFilter, 0.2);
m_FullyConnected = true;
m_PreserveIntensities = true;
}
......
......@@ -146,6 +146,8 @@ private:
unsigned int m_InitialValue;
/** The number of iterations */
unsigned int m_NumberOfIterations;
/** Progress accumulator to report internal filter progress */
itk::ProgressAccumulator::Pointer m_Progress;
};
} // End namespace otb
......
......@@ -32,6 +32,10 @@ template <class TImage, class TStructuringElement>
GeodesicMorphologyIterativeDecompositionImageFilter<TImage, TStructuringElement>
::GeodesicMorphologyIterativeDecompositionImageFilter()
{
// Create a process accumulator for tracking the progress of minipipeline
m_Progress = itk::ProgressAccumulator::New();
m_Progress->SetMiniPipelineFilter(this);
this->SetNumberOfRequiredInputs(1);
this->SetNumberOfRequiredOutputs(3);
m_NumberOfIterations = 2;
......@@ -185,6 +189,10 @@ GeodesicMorphologyIterativeDecompositionImageFilter<TImage, TStructuringElement>
while (i < m_NumberOfIterations)
{
filter = DecompositionFilterType::New();
// Register Internal Filter for progress
m_Progress->RegisterInternalFilter(filter, 1./m_NumberOfIterations);
typename StructuringElementType::RadiusType radius;
radius.Fill(m_InitialValue + i * m_Step);
filter->SetRadius(radius);
......@@ -195,8 +203,8 @@ GeodesicMorphologyIterativeDecompositionImageFilter<TImage, TStructuringElement>
filter->GetOutput()->UpdateOutputData();
outputPtr->SetNthElement(i, filter->GetOutput());
concOutputPtr->SetNthElement(i, filter->GetConvexMap());
convOutputPtr->SetNthElement(i, filter->GetConcaveMap());
concOutputPtr->SetNthElement(i, filter->GetConcaveMap());
convOutputPtr->SetNthElement(i, filter->GetConvexMap());
current = filter->GetOutput();
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment