Commit 100f3818 authored by Cédric Traizet's avatar Cédric Traizet

Merge branch 'small_region_merging' into 'develop'

Refactor small region merging

See merge request orfeotoolbox/otb!233
parents 7ec1f79b bfa19860
......@@ -57,3 +57,8 @@ otb_create_application(
NAME LargeScaleMeanShift
SOURCES otbLargeScaleMeanShift.cxx
LINK_LIBRARIES ${${otb-module}_LIBRARIES})
otb_create_application(
NAME SmallRegionsMerging
SOURCES otbSmallRegionsMerging.cxx
LINK_LIBRARIES ${${otb-module}_LIBRARIES})
......@@ -104,6 +104,7 @@ private:
"[2] LSMSegmentation\n"
"[3] LSMSVectorization");
AddDocTag(Tags::Segmentation);
AddDocTag(Tags::Deprecated);
AddDocTag("LSMS");
AddParameter(ParameterType_InputImage, "in", "Input image");
......
......@@ -86,7 +86,7 @@ private:
ClearApplications();
AddApplication("MeanShiftSmoothing", "smoothing", "Smoothing step");
AddApplication("LSMSSegmentation", "segmentation", "Segmentation step");
AddApplication("LSMSSmallRegionsMerging", "merging", "Small region merging step");
AddApplication("SmallRegionsMerging", "merging", "Small region merging step");
AddApplication("LSMSVectorization", "vectorization", "Vectorization step");
ShareParameter("in","smoothing.in");
......@@ -130,8 +130,6 @@ private:
Connect("merging.ram","smoothing.ram");
Connect("vectorization.ram","smoothing.ram");
Connect("merging.tilesizex","segmentation.tilesizex");
Connect("merging.tilesizey","segmentation.tilesizey");
Connect("vectorization.tilesizex","segmentation.tilesizex");
Connect("vectorization.tilesizey","segmentation.tilesizey");
......
/*
* Copyright (C) 2005-2017 Centre National d'Etudes Spatiales (CNES)
*
* This file is part of Orfeo Toolbox
*
* https://www.orfeo-toolbox.org/
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "otbWrapperApplication.h"
#include "otbWrapperApplicationFactory.h"
#include "otbStreamingStatisticsMapFromLabelImageFilter.h"
#include "otbLabelImageSmallRegionMergingFilter.h"
#include "itkChangeLabelImageFilter.h"
#include "otbStopwatch.h"
namespace otb
{
namespace Wrapper
{
class SmallRegionsMerging : public Application
{
public:
typedef SmallRegionsMerging Self;
typedef Application Superclass;
typedef itk::SmartPointer<Self> Pointer;
typedef itk::SmartPointer<const Self> ConstPointer;
typedef FloatVectorImageType ImageType;
typedef ImageType::InternalPixelType ImagePixelType;
typedef UInt32ImageType LabelImageType;
typedef LabelImageType::InternalPixelType LabelImagePixelType;
typedef otb::StreamingStatisticsMapFromLabelImageFilter
<ImageType, LabelImageType> StatisticsMapFromLabelImageFilterType;
typedef otb::LabelImageSmallRegionMergingFilter<LabelImageType>
LabelImageSmallRegionMergingFilterType;
typedef itk::ChangeLabelImageFilter<LabelImageType,LabelImageType>
ChangeLabelImageFilterType;
itkNewMacro(Self);
itkTypeMacro(Merging, otb::Application);
private:
ChangeLabelImageFilterType::Pointer m_ChangeLabelFilter;
void DoInit() override
{
SetName("SmallRegionsMerging");
SetDescription("This application merges small regions of a segmentation "
"result.");
SetDocName("Small Region Merging");
SetDocLongDescription("Given a segmentation result and the original image,"
" it will merge segments whose size in pixels is"
" lower than minsize parameter with the adjacent"
" segments with the adjacent segment with closest"
" radiometry and acceptable size. \n\n"
"Small segments will be processed by increasing size:"
" first all segments for which area is equal to 1"
" pixel will be merged with adjacent segments, then"
" all segments of area equal to 2 pixels will be"
" processed, until segments of area minsize.");
SetDocLimitations( "None") ;
SetDocAuthors("OTB-Team");
SetDocSeeAlso("Segmentation");
AddDocTag(Tags::Segmentation);
AddParameter(ParameterType_InputImage, "in", "Input image");
SetParameterDescription( "in", "The input image, containing initial"
" spectral signatures corresponding to the segmented image (inseg)." );
AddParameter(ParameterType_InputImage, "inseg", "Segmented image");
SetParameterDescription( "inseg", "Segmented image where each pixel value"
" is the unique integer label of the segment it belongs to." );
AddParameter(ParameterType_OutputImage, "out", "Output Image");
SetParameterDescription( "out", "The output image. The output image is the"
" segmented image where the minimal segments have been merged." );
SetDefaultOutputPixelType("out",ImagePixelType_uint32);
AddParameter(ParameterType_Int, "minsize", "Minimum Segment Size");
SetParameterDescription("minsize", "Minimum Segment Size. If, after the "
" segmentation, a segment is of size strictly lower than this criterion,"
" the segment is merged with the segment that has the closest sepctral"
" signature.");
SetDefaultParameterInt("minsize", 50);
SetMinimumParameterIntValue("minsize", 1);
MandatoryOff("minsize");
AddRAMParameter();
// Doc example parameter settings
SetDocExampleParameterValue("in","smooth.tif");
SetDocExampleParameterValue("inseg","segmentation.tif");
SetDocExampleParameterValue("out","merged.tif");
SetDocExampleParameterValue("minsize","50");
SetOfficialDocLink();
}
void DoUpdateParameters() override
{
}
void DoExecute() override
{
// Start Timer for the application
auto Timer = Stopwatch::StartNew();
unsigned int minSize = GetParameterInt("minsize");
//Acquisition of the input image dimensions
ImageType::Pointer imageIn = GetParameterImage("in");
LabelImageType::Pointer labelIn = GetParameterUInt32Image("inseg");
// Compute statistics for each segment
auto labelStatsFilter = StatisticsMapFromLabelImageFilterType::New();
labelStatsFilter->SetInput(imageIn);
labelStatsFilter->SetInputLabelImage(labelIn);
AddProcess(labelStatsFilter->GetStreamer() , "Computing stats on input"
" image ...");
labelStatsFilter->Update();
// Convert Map to Unordered map
auto labelPopulationMap = labelStatsFilter->GetLabelPopulationMap();
std::unordered_map< unsigned int,double> labelPopulation;
for (auto population : labelPopulationMap)
{
labelPopulation[population.first]=population.second;
}
auto meanValueMap = labelStatsFilter->GetMeanValueMap();
std::unordered_map< unsigned int, itk::VariableLengthVector<double> >
meanValues;
for (const auto & mean : meanValueMap)
{
meanValues[mean.first] = mean.second;
}
// Compute the LUT from the original label image to the merged output
// label image.
auto regionMergingFilter = LabelImageSmallRegionMergingFilterType::New();
regionMergingFilter->SetInputLabelImage( labelIn );
regionMergingFilter->SetLabelPopulation( labelPopulation );
regionMergingFilter->SetLabelStatistic( meanValues );
regionMergingFilter->SetMinSize( minSize);
AddProcess(regionMergingFilter, "Computing LUT ...");
regionMergingFilter->Update();
// Relabelling using the LUT
auto changeLabelFilter = ChangeLabelImageFilterType::New();
changeLabelFilter->SetInput(labelIn);
const auto & LUT = regionMergingFilter->GetLUT();
for (auto const & label : LUT)
{
if (label.first != label.second)
{
changeLabelFilter->SetChange(label.first, label.second);
}
}
SetParameterOutputImage("out", changeLabelFilter->GetOutput());
RegisterPipeline();
Timer.Stop();
otbAppLogINFO( "Total elapsed time: "<<
float(Timer.GetElapsedMilliseconds())/1000 <<" seconds.");
}
};
}
}
OTB_APPLICATION_EXPORT(otb::Wrapper::SmallRegionsMerging)
......@@ -250,6 +250,20 @@ otb_test_application(NAME apTvLSMS3SmallRegionsMerging
set_property(TEST apTvLSMS3SmallRegionsMerging PROPERTY DEPENDS apTvLSMS2Segmentation)
#----------- SmallRegionsMerging TESTS ----------------
otb_test_application(NAME apTvSmallRegionsMerging
APP SmallRegionsMerging
OPTIONS -in ${TEMP}/apTvLSMS1_filtered_range.tif
-inseg ${TEMP}/apTvLSMS2_Segmentation.tif
-out ${TEMP}/apTvSmallMerged.tif uint32
-minsize 10
VALID --compare-image ${NOTOL}
${BASELINE}/apTvSmallMerged.tif
${TEMP}/apTvSmallMerged.tif
)
set_property(TEST apTvSmallRegionsMerging PROPERTY DEPENDS apTvLSMS2Segmentation)
#----------- LSMSVectorization TESTS ----------------
otb_test_application(NAME apTvLSMS4Vectorization_SmallMerged
APP LSMSVectorization
......@@ -308,3 +322,4 @@ otb_test_application(NAME apTvSeLargeScaleMeanShiftTest
${BASELINE_FILES}/apTvSeLargeScaleMeanShiftTestOut.shp
${TEMP}/apTvSeLargeScaleMeanShiftTestOut.shp
)
/*
* Copyright (C) 2005-2017 Centre National d'Etudes Spatiales (CNES)
*
* This file is part of Orfeo Toolbox
*
* https://www.orfeo-toolbox.org/
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef otbLabelImageSmallRegionMergingFilter_h
#define otbLabelImageSmallRegionMergingFilter_h
#include "otbPersistentImageFilter.h"
#include "otbPersistentFilterStreamingDecorator.h"
#include <unordered_map>
namespace otb
{
/** \class PersistentLabelImageSmallRegionMergingFilter
*
* This class can be used to merge each segments of a given size in a label
* image to the connected segment with the closest radiometry (in the sense of
* the euclidian squared distance).
* This persistent filter should be used as template parameter of a
* PersistentFilterStreamingDecorator.
* It computes from an input label image an equivalence table
* that gives for each pixel, the corresponding label in the merged image.
* The merged image can then be computed using a ChangeLabelImageFilter.
*
* This filter can be updated several times for different values of size,
* the output equivalence table will be the results of all computations.
*
* \ingroup ImageSegmentation
*
* \ingroup OTBConversion
*/
template <class TInputLabelImage>
class ITK_EXPORT PersistentLabelImageSmallRegionMergingFilter
: public PersistentImageFilter<TInputLabelImage, TInputLabelImage>
{
public:
/** Standard class typedef */
typedef PersistentLabelImageSmallRegionMergingFilter Self;
typedef PersistentImageFilter<TInputLabelImage, TInputLabelImage> Superclass;
typedef itk::SmartPointer<Self> Pointer;
typedef itk::SmartPointer<const Self> ConstPointer;
/** Type macro */
itkTypeMacro(PersistentLabelImageSmallRegionMergingFilter,
PersistentImageFilter);
itkNewMacro(Self);
/** Template parameters typedefs */
typedef TInputLabelImage InputImageType;
typedef typename InputImageType::Pointer InputImagePointerType;
typedef typename InputImageType::PixelType InputLabelType;
typedef typename InputImageType::SizeType InputSizeType;
typedef typename InputImageType::PointType PointType;
typedef typename InputImageType::RegionType RegionType;
typedef itk::VariableLengthVector<double> RealVectorPixelType;
typedef std::unordered_map<InputLabelType, std::set<InputLabelType> >
NeigboursMapType;
typedef std::unordered_map<InputLabelType , RealVectorPixelType >
LabelStatisticType;
typedef std::unordered_map<InputLabelType , double>
LabelPopulationType;
typedef std::unordered_map<InputLabelType , InputLabelType> LUTType;
/** Set/Get size of segments to be merged */
itkGetMacro(Size , unsigned int);
itkSetMacro(Size , unsigned int);
/** Set the Label population and initialize the LUT */
void SetLabelPopulation( LabelPopulationType const & labelPopulation );
/** Get the Label population */
LabelPopulationType const & GetLabelPopulation() const;
/** Set the label statistic */
void SetLabelStatistic( LabelStatisticType const & labelStatistic );
/** Get the label statistic */
LabelStatisticType const & GetLabelStatistic() const;
/** Get the LUT */
LUTType const & GetLUT() const;
virtual void Reset(void) override;
virtual void Synthetize(void) override;
protected:
/** The input requested region should be padded by a radius of 1 to use the
* neigbourhood iterator */
void GenerateInputRequestedRegion() override;
/** Threaded Generate Data : find the neighbours of each segments of size
* m_Size for each tile and store them in an accumulator */
void ThreadedGenerateData(const RegionType&
outputRegionForThread, itk::ThreadIdType threadId) override;
/** Use the LUT recursively to find the label corresponding to the input
* label */
InputLabelType FindCorrespondingLabel( InputLabelType label);
/** Constructor */
PersistentLabelImageSmallRegionMergingFilter();
/** Destructor */
~PersistentLabelImageSmallRegionMergingFilter() override = default;
/** PrintSelf method */
void PrintSelf(std::ostream& os, itk::Indent indent) const override;
private:
PersistentLabelImageSmallRegionMergingFilter(const Self &) = delete;
void operator =(const Self&) = delete;
/** Size of the segments to be merged */
unsigned int m_Size;
/** Map containing at key i the population of the segment labelled i */
LabelPopulationType m_LabelPopulation;
/** Map containing at key i the mean of element of the segment labelled i */
LabelStatisticType m_LabelStatistic;
/** Neigbours maps for each thread */
std::vector <NeigboursMapType > m_NeighboursMapsTmp;
/** LUT giving correspondance between labels in the original segmentation
* and the merged labels */
LUTType m_LUT;
};
/** \class LabelImageSmallRegionMergingFilter
*
* This filter computes from a label image an equivalence table that gives for
* each pixel, the corresponding label in the merged image. It uses a
* PersistentFilterStreamingDecorator templated over a
* PersistentLabelImageSmallRegionMergingFilter
* to merge the segments recursively from segment of size 1 to segment of a
* size specified by the attribute MinSize.
* The equivalence table can be accessed with the method GetLut and used to
* compute the merged image with a ChangeLabelImageFilterType.
*
* \ingroup ImageSegmentation
*
* \ingroup OTBConversion
*/
template <class TInputLabelImage>
class ITK_EXPORT LabelImageSmallRegionMergingFilter
: public itk::ProcessObject
{
public:
/** Standard Self typedef */
typedef LabelImageSmallRegionMergingFilter Self;
typedef itk::ProcessObject Superclass;
typedef itk::SmartPointer<Self> Pointer;
typedef itk::SmartPointer<const Self> ConstPointer;
/** Type macro */
itkNewMacro(Self);
/** Creation through object factory macro */
itkTypeMacro(LabelImageSmallRegionMergingFilter, itk::ProcessObject);
// Small region merging filter typedefs
typedef PersistentLabelImageSmallRegionMergingFilter< TInputLabelImage >
PersistentLabelImageSmallRegionMergingFilterType;
typedef PersistentFilterStreamingDecorator
< PersistentLabelImageSmallRegionMergingFilterType >
LabelImageSmallRegionMergingFilterType;
typedef typename PersistentLabelImageSmallRegionMergingFilterType
::LabelPopulationType LabelPopulationType;
typedef typename PersistentLabelImageSmallRegionMergingFilterType
::LabelStatisticType LabelStatisticType;
typedef typename PersistentLabelImageSmallRegionMergingFilterType::LUTType
LUTType;
/** Set/Get size of polygon to be merged */
itkGetMacro(MinSize , unsigned int);
itkSetMacro(MinSize , unsigned int);
/** Set the Label population map */
void SetInputLabelImage( const TInputLabelImage * labelImage );
/** Set the Label population map */
void SetLabelPopulation( LabelPopulationType const & labelPopulation );
/** Get the Label population map */
LabelPopulationType const & GetLabelPopulation() const;
/** Set the Label statistic map */
void SetLabelStatistic( LabelStatisticType const & labelStatistic );
/** Get the Label statistic map */
LabelStatisticType const & GetLabelStatistic() const;
/** Get the Label statistic map */
LUTType const & GetLUT() const;
/** Call GenerateData() */
void Update() override;
protected:
/** Constructor */
LabelImageSmallRegionMergingFilter();
/** Destructor */
~LabelImageSmallRegionMergingFilter() override = default;
/** Generate Data method (Update LabelImageSmallRegionMergingFilterType
* recursively) */
void GenerateData() override;
private:
LabelImageSmallRegionMergingFilter(const Self &) = delete;
void operator =(const Self&) = delete;
// Filter used recursively to build the equivalence table
typename
LabelImageSmallRegionMergingFilterType::Pointer m_SmallRegionMergingFilter;
// All segments with size < m_MinSize will be merged to bigger segments.
unsigned int m_MinSize;
};
} // end namespace otb
#ifndef OTB_MANUAL_INSTANTIATION
#include "otbLabelImageSmallRegionMergingFilter.hxx"
#endif
#endif
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment