Commit c12f8611 authored by Cédric Traizet's avatar Cédric Traizet
Browse files

ENH : refactoring of small region merging app

parent fa93eaec
......@@ -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})
/*
* 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 "otbMultiChannelExtractROI.h"
#include "otbExtractROI.h"
//#include "otbStreamingStatisticsImageFilter.h"
#include "otbSystem.h"
#include "itkUnaryFunctorImageFilter.h"
#include "itkChangeLabelImageFilter.h"
#include "otbTileImageFilter.h"
#include <time.h>
#include <algorithm>
#include <climits>
#include "otbWrapperApplication.h"
#include "otbWrapperApplicationFactory.h"
#include "otbStandardWriterWatcher.h"
// New includes
#include "otbStreamingStatisticsMapFromLabelImageFilter.h"
#include "otbLabelImageSmallRegionMergingFilter.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::MultiChannelExtractROI <ImagePixelType,ImagePixelType > MultiChannelExtractROIFilterType;
typedef otb::ExtractROI<LabelImagePixelType,LabelImagePixelType> ExtractROIFilterType;
//typedef otb::StreamingStatisticsImageFilter<LabelImageType> StatisticsImageFilterType;
typedef otb::StreamingStatisticsMapFromLabelImageFilter<ImageType, LabelImageType> StatisticsMapFromLabelImageFilterType;
typedef otb::LabelImageSmallRegionMergingFilter<LabelImageType, ImageType> LabelImageSmallRegionMergingFilterType;
typedef itk::ImageRegionConstIterator<LabelImageType> LabelImageIterator;
typedef itk::ImageRegionConstIterator<ImageType> ImageIterator;
typedef itk::ChangeLabelImageFilter<LabelImageType,LabelImageType> ChangeLabelImageFilterType;
typedef otb::TileImageFilter<LabelImageType> TileImageFilterType;
itkNewMacro(Self);
itkTypeMacro(Merging, otb::Application);
private:
ChangeLabelImageFilterType::Pointer m_ChangeLabelFilter;
void DoInit() override
{
SetName("SmallRegionsMerging");
SetDescription("This application performs the third (optional) step of the exact Large-Scale Mean-Shift segmentation workflow [1].");
SetDocName("Exact Large-Scale Mean-Shift segmentation, step 3 (optional)");
SetDocLongDescription("Given a segmentation result (can be the out output parameter of the"
" LSMSSegmentation application [2]) 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. For large images one can use the"
" tilesizex and tilesizey parameters for tile-wise processing, with the"
" guarantees of identical results.\n\n"
"The output of this application can be passed to the"
" LSMSVectorization application [3] to complete the LSMS workflow.");
SetDocLimitations("This application is part of the Large-Scale Mean-Shift segmentation"
" workflow (LSMS) and may not be suited for any other purpose. This"
" application is not compatible with in-memory connection since it does"
" its own internal streaming.");
SetDocAuthors("David Youssefi");
SetDocSeeAlso( "[1] Michel, J., Youssefi, D., & Grizonnet, M. (2015). Stable"
" mean-shift algorithm and its application to the segmentation of"
" arbitrarily large remote sensing images. IEEE Transactions on"
" Geoscience and Remote Sensing, 53(2), 952-964.\n"
"[2] LSMSegmentation\n"
"[3] LSMSVectorization");
AddDocTag(Tags::Segmentation);
AddDocTag("LSMS");
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. An ecoding of uint32 is advised." );
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 lower than this criterion, the segment is merged with the segment that has the closest sepctral signature.");
SetDefaultParameterInt("minsize", 50);
SetMinimumParameterIntValue("minsize", 0);
MandatoryOff("minsize");
AddParameter(ParameterType_Int, "tilesizex", "Size of tiles in pixel (X-axis)");
SetParameterDescription("tilesizex", "Size of tiles along the X-axis for tile-wise processing.");
SetDefaultParameterInt("tilesizex", 500);
SetMinimumParameterIntValue("tilesizex", 1);
AddParameter(ParameterType_Int, "tilesizey", "Size of tiles in pixel (Y-axis)");
SetParameterDescription("tilesizey", "Size of tiles along the Y-axis for tile-wise processing.");
SetDefaultParameterInt("tilesizey", 500);
SetMinimumParameterIntValue("tilesizey", 1);
AddRAMParameter();
// Doc example parameter settings
SetDocExampleParameterValue("in","smooth.tif");
SetDocExampleParameterValue("inseg","segmentation.tif");
SetDocExampleParameterValue("out","merged.tif");
SetDocExampleParameterValue("minsize","20");
SetDocExampleParameterValue("tilesizex","256");
SetDocExampleParameterValue("tilesizey","256");
SetOfficialDocLink();
}
void DoUpdateParameters() override
{
}
void DoExecute() override
{
clock_t tic = clock();
unsigned int minSize = GetParameterInt("minsize");
unsigned long sizeTilesX = GetParameterInt("tilesizex");
unsigned long sizeTilesY = GetParameterInt("tilesizey");
//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();
auto meanMap = labelStatsFilter->GetMeanValueMap();
auto nbPixelsMap = labelStatsFilter->GetLabelPopulationMap();
// Merge small segments
auto regionMergingFilter = LabelImageSmallRegionMergingFilterType::New();
regionMergingFilter->SetInputLabelImage( labelIn );
regionMergingFilter->SetInputSpectralImage( imageIn );
regionMergingFilter->SetLabelPopulation( nbPixelsMap );
regionMergingFilter->SetLabelStatistic( labelStatsFilter->GetMeanValueMap() );
for (unsigned int size = 1 ; size < minSize ; size++)
{
regionMergingFilter->SetSize( size );
regionMergingFilter->Update();
}
/*
//Relabelling
m_ChangeLabelFilter = ChangeLabelImageFilterType::New();
m_ChangeLabelFilter->SetInput(labelIn);
for(LabelImagePixelType label = 1; label<regionCount+1; ++label)
{
if(label!=LUT[label])
{
m_ChangeLabelFilter->SetChange(label,LUT[label]);
}
}
SetParameterOutputImage("out", m_ChangeLabelFilter->GetOutput());
*/
clock_t toc = clock();
otbAppLogINFO(<<"Elapsed time: "<<(double)(toc - tic) / CLOCKS_PER_SEC<<" seconds");
}
};
}
}
OTB_APPLICATION_EXPORT(otb::Wrapper::SmallRegionsMerging)
/*
* 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 "otbImage.h"
#include "otbVectorImage.h"
#include "itkImageToImageFilter.h"
#include "otbPersistentImageFilter.h"
#include "otbPersistentFilterStreamingDecorator.h"
#include <set>
namespace otb
{
/** \class PersistentLabelImageSmallRegionMergingFilter
*
*
* This class merges regions in the input label image according to the input
* image of spectral values and the RangeBandwidth parameter.
*
*
* \ingroup ImageSegmentation
*
* \ingroup OTBConversion
*/
template <class TInputLabelImage, class TInputSpectralImage >
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 InputPixelType;
typedef typename InputImageType::IndexType InputIndexType;
typedef typename InputImageType::SizeType InputSizeType;
typedef typename InputImageType::IndexValueType InputIndexValueType;
typedef typename InputImageType::PointType PointType;
typedef typename InputImageType::RegionType RegionType;
typedef typename InputImageType::SizeType SizeType;
typedef TInputLabelImage InputLabelImageType;
typedef typename InputLabelImageType::PixelType InputLabelType;
typedef TInputSpectralImage InputSpectralImageType;
typedef typename TInputSpectralImage::PixelType SpectralPixelType;
typedef itk::VariableLengthVector<double> RealVectorPixelType;
typedef std::map<InputLabelType, double> LabelPopulationMapType;
typedef std::map<InputLabelType, std::set<InputLabelType> > NeigboursMapType;
typedef std::map<InputLabelType, RealVectorPixelType > LabelStatisticMapType;
/** Sets the input image where the value of a pixel is the region id */
void SetInputLabelImage( const InputLabelImageType * labelImage);
/** Sets the input image representing spectral values */
void SetInputSpectralImage( const InputSpectralImageType * spectralImage);
/** Returns input label image */
InputLabelImageType * GetInputLabelImage();
/** Returns input spectral image */
InputSpectralImageType * GetInputSpectralImage();
/** Set/Get size of polygon to be merged */
itkGetMacro(Size , unsigned int);
itkSetMacro(Size , unsigned int);
/** Set/Get the Label population map */
void SetLabelPopulation( LabelPopulationMapType const & labelPopulation )
{
m_LabelPopulation = labelPopulation;
}
LabelPopulationMapType const & GetLabelPopulation() const
{
return m_LabelPopulation;
}
void SetLabelStatistic( LabelStatisticMapType const & labelStatistic )
{
m_labelStatistic = labelStatistic;
}
LabelStatisticMapType const & GetLabelStatistic() const
{
return m_labelStatistic;
}
virtual void Reset(void);
virtual void Synthetize(void);
protected:
//void EnlargeOutputRequestedRegion( itk::DataObject *output ) override;
void GenerateOutputInformation(void) override;
void ThreadedGenerateData(const RegionType&
outputRegionForThread, itk::ThreadIdType threadId) override;
/** Constructor */
PersistentLabelImageSmallRegionMergingFilter();
/** Destructor */
~PersistentLabelImageSmallRegionMergingFilter() override;
/** PrintSelf method */
void PrintSelf(std::ostream& os, itk::Indent indent) const override;
private:
PersistentLabelImageSmallRegionMergingFilter(const Self &) = delete;
void operator =(const Self&) = delete;
unsigned int m_Size;
LabelPopulationMapType m_LabelPopulation;
LabelStatisticMapType m_labelStatistic;
// Neigbours maps for each thread
std::vector <NeigboursMapType > m_NeighboursMapsTmp;
NeigboursMapType m_NeighboursMap;
};
/** \class LabelImageSmallRegionMergingFilter
*
*
* This class merges regions in the input label image according to the input
* image of spectral values and the RangeBandwidth parameter.
*
*
* \ingroup ImageSegmentation
*
* \ingroup OTBConversion
*/
template <class TInputLabelImage, class TInputSpectralImage>
class ITK_EXPORT LabelImageSmallRegionMergingFilter :
public PersistentFilterStreamingDecorator<PersistentLabelImageSmallRegionMergingFilter<TInputLabelImage, TInputSpectralImage> >
{
public:
/** Standard Self typedef */
typedef LabelImageSmallRegionMergingFilter Self;
typedef PersistentFilterStreamingDecorator
<PersistentLabelImageSmallRegionMergingFilter<TInputLabelImage, TInputSpectralImage> > Superclass;
typedef itk::SmartPointer<Self> Pointer;
typedef itk::SmartPointer<const Self> ConstPointer;
/** Type macro */
itkNewMacro(Self);
/** Creation through object factory macro */
itkTypeMacro(LabelImageSmallRegionMergingFilter, PersistentFilterStreamingDecorator);
typedef PersistentLabelImageSmallRegionMergingFilter<TInputLabelImage, TInputSpectralImage> PersistentFilterType;
typedef typename PersistentFilterType::InputLabelImageType InputLabelImageType;
typedef typename PersistentFilterType::InputSpectralImageType InputSpectralImageType;
typedef typename PersistentFilterType::LabelPopulationMapType LabelPopulationMapType;
typedef typename PersistentFilterType::LabelStatisticMapType LabelStatisticMapType;
/** Sets the input image where the value of a pixel is the region id */
void SetInputLabelImage( const InputLabelImageType * labelImage)
{
this->GetFilter()->SetInputLabelImage( labelImage );
}
/** Sets the input image representing spectral values */
void SetInputSpectralImage( const InputSpectralImageType * spectralImage)
{
this->GetFilter()->SetInputSpectralImage( spectralImage );
}
/** Returns input label image */
InputLabelImageType * GetInputLabelImage()
{
return this->GetFilter()->GetInputLabelImage();
}
/** Returns input spectral image */
InputSpectralImageType * GetInputSpectralImage()
{
return this->GetFilter()->GetInputSpectralImage();
}
/** Set size of polygon to be merged */
void SetSize(unsigned int size)
{
this->GetFilter()->SetSize( size );
}
/** Get size of polygon to be merged */
unsigned int GetSize()
{
return this->GetFilter()->GetSize();
}
/** Set the Label population map */
void SetLabelPopulation( LabelPopulationMapType const & labelPopulation )
{
this->GetFilter()->SetLabelPopulation( labelPopulation );
}
/** Get the Label population map */
LabelPopulationMapType const & GetLabelPopulation( ) const
{
return this->GetFilter()->GetLabelPopulation();
}
/** Set the Label statistic map */
void SetLabelStatistic( LabelStatisticMapType const & labelStatistic )
{
this->GetFilter()->SetLabelStatistic( labelStatistic );
}
/** Get the Label statistic map */
LabelStatisticMapType const & GetLabelStatistic( ) const
{
return this->GetFilter()->GetLabelStatistic();
}
protected:
/** Constructor */
LabelImageSmallRegionMergingFilter() {}
/** Destructor */
~LabelImageSmallRegionMergingFilter() override {}
private:
LabelImageSmallRegionMergingFilter(const Self &) = delete;
void operator =(const Self&) = delete;
};
} // end namespace otb
#ifndef OTB_MANUAL_INSTANTIATION
#include "otbLabelImageSmallRegionMergingFilter.hxx"
#endif
#endif
/*
* 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_hxx
#define otbLabelImageSmallRegionMergingFilter_hxx
#include "otbLabelImageSmallRegionMergingFilter.h"
#include "itkImageConstIterator.h"
#include "itkConstShapedNeighborhoodIterator.h"
#include "itkProgressReporter.h"
namespace otb
{
template <class TInputLabelImage, class TInputSpectralImage>
PersistentLabelImageSmallRegionMergingFilter<TInputLabelImage, TInputSpectralImage>
::PersistentLabelImageSmallRegionMergingFilter() : m_Size(1)
{
}
template <class TInputLabelImage, class TInputSpectralImage>
void
PersistentLabelImageSmallRegionMergingFilter<TInputLabelImage, TInputSpectralImage>
::SetInputLabelImage( const TInputLabelImage * labelImage)
{
// Process object is not const-correct so the const casting is required.
this->SetNthInput(0, const_cast<TInputLabelImage *>( labelImage ));
}
template <class TInputLabelImage, class TInputSpectralImage>
void
PersistentLabelImageSmallRegionMergingFilter<TInputLabelImage, TInputSpectralImage>
::SetInputSpectralImage( const TInputSpectralImage * spectralImage)
{
// Process object is not const-correct so the const casting is required.
this->SetNthInput(1, const_cast<TInputSpectralImage *>( spectralImage ));
}
template <class TInputLabelImage, class TInputSpectralImage>
TInputLabelImage *
PersistentLabelImageSmallRegionMergingFilter<TInputLabelImage, TInputSpectralImage>
::GetInputLabelImage()
{
return dynamic_cast<TInputLabelImage*>(itk::ProcessObject::GetInput(0));
}
template <class TInputLabelImage, class TInputSpectralImage>
TInputSpectralImage *
PersistentLabelImageSmallRegionMergingFilter<TInputLabelImage, TInputSpectralImage>
::GetInputSpectralImage()
{
return dynamic_cast<TInputSpectralImage*>(itk::ProcessObject::GetInput(1));
}
template <class TInputLabelImage, class TInputSpectralImage>
PersistentLabelImageSmallRegionMergingFilter<TInputLabelImage, TInputSpectralImage>
::~PersistentLabelImageSmallRegionMergingFilter()
{
}
template <class TInputLabelImage, class TInputSpectralImage>
void
PersistentLabelImageSmallRegionMergingFilter<TInputLabelImage, TInputSpectralImage>
::Reset()
{
std::cout << "Reset" << std::endl;