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

STYLE : 80 characters rule

parent e1b2b1e5
......@@ -45,12 +45,14 @@ public:
typedef UInt32ImageType LabelImageType;
typedef LabelImageType::InternalPixelType LabelImagePixelType;
//typedef otb::StreamingStatisticsImageFilter<LabelImageType> StatisticsImageFilterType;
typedef otb::StreamingStatisticsMapFromLabelImageFilter<ImageType, LabelImageType> StatisticsMapFromLabelImageFilterType;
typedef otb::StreamingStatisticsMapFromLabelImageFilter
<ImageType, LabelImageType> StatisticsMapFromLabelImageFilterType;
typedef otb::LabelImageSmallRegionMergingFilter<LabelImageType> LabelImageSmallRegionMergingFilterType;
typedef otb::LabelImageSmallRegionMergingFilter<LabelImageType>
LabelImageSmallRegionMergingFilterType;
typedef itk::ChangeLabelImageFilter<LabelImageType,LabelImageType> ChangeLabelImageFilterType;
typedef itk::ChangeLabelImageFilter<LabelImageType,LabelImageType>
ChangeLabelImageFilterType;
itkNewMacro(Self);
itkTypeMacro(Merging, otb::Application);
......@@ -61,34 +63,43 @@ private:
void DoInit() override
{
SetName("SmallRegionsMerging");
SetDescription("This application merges small regions of a segmentation result to connected region.");
SetDescription("This application merges small regions of a segmentation "
"result to connected region.");
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.");
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("This application is more efficient if the labels are contiguous, starting from 0.");
SetDocAuthors("OTB-Team");
SetDocSeeAlso( "Segmentation");
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." );
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." );
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.");
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");
......@@ -123,7 +134,8 @@ private:
auto labelStatsFilter = StatisticsMapFromLabelImageFilterType::New();
labelStatsFilter->SetInput(imageIn);
labelStatsFilter->SetInputLabelImage(labelIn);
AddProcess(labelStatsFilter->GetStreamer() , "Computing stats on input image ...");
AddProcess(labelStatsFilter->GetStreamer() , "Computing stats on input"
" image ...");
labelStatsFilter->Update();
// Convert Map to Unordered map
......@@ -135,18 +147,19 @@ private:
labelPopulation[population.first]=population.second;
}
auto meanValueMap = labelStatsFilter->GetMeanValueMap();
std::unordered_map< unsigned int, itk::VariableLengthVector<double> > meanValues;
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.
// 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 ...");
......@@ -168,7 +181,8 @@ private:
SetParameterOutputImage("out", changeLabelFilter->GetOutput());
RegisterPipeline();
clock_t toc = clock();
otbAppLogINFO(<<"Elapsed time: "<<(double)(toc - tic) / CLOCKS_PER_SEC<<" seconds");
otbAppLogINFO(<<"Elapsed time: "<<(double)(toc - tic) / CLOCKS_PER_SEC<<
" seconds");
}
};
......
......@@ -32,15 +32,17 @@ 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.
* This persistent filter should be used as template parameter of a PersistentFilterStreamingDecorator
* 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.
* 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
*
......@@ -52,13 +54,14 @@ class PersistentLabelImageSmallRegionMergingFilter
{
public:
/** Standard class typedef */
typedef PersistentLabelImageSmallRegionMergingFilter Self;
typedef PersistentImageFilter<TInputLabelImage, TInputLabelImage> Superclass;
typedef itk::SmartPointer<Self> Pointer;
typedef itk::SmartPointer<const Self> ConstPointer;
typedef PersistentLabelImageSmallRegionMergingFilter Self;
typedef PersistentImageFilter<TInputLabelImage, TInputLabelImage> Superclass;
typedef itk::SmartPointer<Self> Pointer;
typedef itk::SmartPointer<const Self> ConstPointer;
/** Type macro */
itkTypeMacro(PersistentLabelImageSmallRegionMergingFilter, PersistentImageFilter);
itkTypeMacro(PersistentLabelImageSmallRegionMergingFilter,
PersistentImageFilter);
itkNewMacro(Self);
/** Template parameters typedefs */
......@@ -70,13 +73,16 @@ public:
typedef typename InputImageType::PointType PointType;
typedef typename InputImageType::RegionType RegionType;
typedef itk::VariableLengthVector<double> RealVectorPixelType;
typedef itk::VariableLengthVector<double> RealVectorPixelType;
typedef std::unordered_map<InputLabelType, std::set<InputLabelType> > NeigboursMapType;
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;
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);
......@@ -122,16 +128,18 @@ public:
virtual void Synthetize(void) override;
protected:
/** The input requested region should be padded by a radius of 1 to use the neigbourhood iterator*/
/** 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 */
/** 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
/** Use the LUT recursively to find the label corresponding to the input
* label */
InputLabelType FindCorrespondingLabel( InputLabelType label);
/** Constructor */
......@@ -150,27 +158,30 @@ private:
/** Size of the segments to be merged */
unsigned int m_Size;
/** Vector containing at position i the population of the segment labelled i */
/** Map containing at key i the population of the segment labelled i */
LabelPopulationType m_LabelPopulation;
/** Vector containing at position i the population of mean of element of the segment labelled i*/
/** 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 */
/** 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 sized specified
* by a parameter.
* The merged image can then be computed using a ChangeLabelImageFilterType.
* 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
*
......@@ -182,10 +193,10 @@ class ITK_EXPORT LabelImageSmallRegionMergingFilter
{
public:
/** Standard Self typedef */
typedef LabelImageSmallRegionMergingFilter Self;
typedef itk::ProcessObject Superclass;
typedef itk::SmartPointer<Self> Pointer;
typedef itk::SmartPointer<const Self> ConstPointer;
typedef LabelImageSmallRegionMergingFilter Self;
typedef itk::ProcessObject Superclass;
typedef itk::SmartPointer<Self> Pointer;
typedef itk::SmartPointer<const Self> ConstPointer;
/** Type macro */
itkNewMacro(Self);
......@@ -194,12 +205,20 @@ public:
itkTypeMacro(LabelImageSmallRegionMergingFilter, itk::ProcessObject);
// Small region merging filter typedefs
typedef PersistentLabelImageSmallRegionMergingFilter< TInputLabelImage > PersistentLabelImageSmallRegionMergingFilterType;
typedef PersistentFilterStreamingDecorator < PersistentLabelImageSmallRegionMergingFilterType > LabelImageSmallRegionMergingFilterType;
typedef PersistentLabelImageSmallRegionMergingFilter< TInputLabelImage >
PersistentLabelImageSmallRegionMergingFilterType;
typedef PersistentFilterStreamingDecorator
< PersistentLabelImageSmallRegionMergingFilterType >
LabelImageSmallRegionMergingFilterType;
typedef typename PersistentLabelImageSmallRegionMergingFilterType::LabelPopulationType LabelPopulationType;
typedef typename PersistentLabelImageSmallRegionMergingFilterType::LabelStatisticType LabelStatisticType;
typedef typename PersistentLabelImageSmallRegionMergingFilterType::LUTType LUTType;
typedef typename PersistentLabelImageSmallRegionMergingFilterType
::LabelPopulationType LabelPopulationType;
typedef typename PersistentLabelImageSmallRegionMergingFilterType
::LabelStatisticType LabelStatisticType;
typedef typename PersistentLabelImageSmallRegionMergingFilterType::LUTType
LUTType;
/** Set/Get size of polygon to be merged */
......@@ -215,7 +234,8 @@ public:
/** Set the Label population map */
void SetLabelPopulation( LabelPopulationType const & labelPopulation )
{
m_SmallRegionMergingFilter->GetFilter()->SetLabelPopulation( labelPopulation );
m_SmallRegionMergingFilter->GetFilter()
->SetLabelPopulation(labelPopulation);
}
/** Get the Label population map */
......@@ -227,11 +247,11 @@ public:
/** Set the Label statistic map */
void SetLabelStatistic( LabelStatisticType const & labelStatistic )
{
m_SmallRegionMergingFilter->GetFilter()->SetLabelStatistic( labelStatistic );
m_SmallRegionMergingFilter->GetFilter()->SetLabelStatistic(labelStatistic);
}
/** Get the Label statistic map */
LabelStatisticType const & GetLabelStatistic( ) const
LabelStatisticType const & GetLabelStatistic() const
{
return m_SmallRegionMergingFilter->GetFilter()->GetLabelStatistic();
}
......@@ -250,14 +270,19 @@ protected:
/** Destructor */
~LabelImageSmallRegionMergingFilter() override = default;
/** Generate Data method (Update LabelImageSmallRegionMergingFilterType recursively) */
/** Generate Data method (Update LabelImageSmallRegionMergingFilterType
* recursively) */
void GenerateData() override;
private:
LabelImageSmallRegionMergingFilter(const Self &) = delete;
void operator =(const Self&) = delete;
typename LabelImageSmallRegionMergingFilterType::Pointer m_SmallRegionMergingFilter;
// 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;
};
......
......@@ -49,17 +49,21 @@ PersistentLabelImageSmallRegionMergingFilter< TInputLabelImage >
::Synthetize()
{
NeigboursMapType neighboursMap;
// Merge the neighbours maps from all threads
for( unsigned int threadId = 0; threadId < this->GetNumberOfThreads(); threadId++)
for( unsigned int threadId = 0; threadId < this->GetNumberOfThreads();
threadId++)
{
for (auto const & neighbours : m_NeighboursMapsTmp[threadId])
{
neighboursMap[ neighbours.first ].insert( neighbours.second.begin(), neighbours.second.end() );
neighboursMap[ neighbours.first ].insert
( neighbours.second.begin(), neighbours.second.end() );
}
}
// For each label of the label map, find the "closest" connected label, according
// to the euclidian distance between the corresponding m_labelStatistic elements.
// For each label of the label map, find the "closest" connected label,
// according to the euclidian distance between the corresponding
// m_labelStatistic elements.
for (auto const & neighbours : neighboursMap)
{
double proximity = itk::NumericTraits<double>::max();
......@@ -82,6 +86,8 @@ PersistentLabelImageSmallRegionMergingFilter< TInputLabelImage >
auto curLabelLUT = FindCorrespondingLabel(label);
auto adjLabelLUT = FindCorrespondingLabel(closestNeighbour);
// Keep the smallest label (this prevents infinite loop in the LUT
// (like LUT[i]=j and LUT[j]=i)
if(curLabelLUT < adjLabelLUT)
{
m_LUT[adjLabelLUT] = curLabelLUT;
......@@ -98,24 +104,31 @@ PersistentLabelImageSmallRegionMergingFilter< TInputLabelImage >
label.second = FindCorrespondingLabel( label.first );
}
// Update statistics
// Update statistics : for each newly merged segments, sum the population, and
// recompute the mean.
for (auto label : m_LUT)
{
if((m_LabelPopulation[label.first]!=0) && (label.second != label.first))
{
m_LabelStatistic[ label.second ] = (m_LabelStatistic[label.second]*m_LabelPopulation[label.second] +
m_LabelStatistic[label.first]*m_LabelPopulation[label.first] ) / (m_LabelPopulation[label.first]+m_LabelPopulation[label.second]);
m_LabelStatistic[ label.second ] =
( m_LabelStatistic[label.first]*m_LabelPopulation[label.first]
+ m_LabelStatistic[label.second]*m_LabelPopulation[label.second] )
/ (m_LabelPopulation[label.first]+m_LabelPopulation[label.second]);
m_LabelPopulation[ label.second ] += m_LabelPopulation[ label.first ] ;
// Do not use this label anymore
m_LabelPopulation[ label.first ] = 0;
}
}
}
template <class TInputLabelImage >
typename PersistentLabelImageSmallRegionMergingFilter< TInputLabelImage >::InputLabelType
typename PersistentLabelImageSmallRegionMergingFilter< TInputLabelImage >
::InputLabelType
PersistentLabelImageSmallRegionMergingFilter< TInputLabelImage >
::FindCorrespondingLabel( typename PersistentLabelImageSmallRegionMergingFilter< TInputLabelImage >
::InputLabelType label)
::FindCorrespondingLabel( typename PersistentLabelImageSmallRegionMergingFilter
< TInputLabelImage >::InputLabelType label)
{
auto correspondingLabel = m_LUT[label];
while (label != correspondingLabel)
......@@ -160,7 +173,8 @@ PersistentLabelImageSmallRegionMergingFilter< TInputLabelImage >
// build an exception
itk::InvalidRequestedRegionError e(__FILE__, __LINE__);
e.SetLocation(ITK_LOCATION);
e.SetDescription("Requested region is (at least partially) outside the largest possible region.");
e.SetDescription("Requested region is (at least partially) outside the "
"largest possible region.");
e.SetDataObject(inputPtr);
throw e;
}
......@@ -170,10 +184,12 @@ PersistentLabelImageSmallRegionMergingFilter< TInputLabelImage >
template <class TInputLabelImage >
void
PersistentLabelImageSmallRegionMergingFilter< TInputLabelImage >
::ThreadedGenerateData(const RegionType& outputRegionForThread, itk::ThreadIdType threadId )
::ThreadedGenerateData(const RegionType& outputRegionForThread,
itk::ThreadIdType threadId )
{
using IteratorType = itk::ImageRegionConstIterator< TInputLabelImage >;
using NeighborhoodIteratorType = itk::ConstShapedNeighborhoodIterator< TInputLabelImage >;
using NeighborhoodIteratorType =
itk::ConstShapedNeighborhoodIterator< TInputLabelImage >;
typename NeighborhoodIteratorType::RadiusType radius;
radius.Fill(1);
......@@ -204,7 +220,7 @@ PersistentLabelImageSmallRegionMergingFilter< TInputLabelImage >
{
int neighbourLabel = m_LUT[ ci.Get() ];
if (neighbourLabel != currentLabel)
m_NeighboursMapsTmp[threadId][ currentLabel ].insert( neighbourLabel );
m_NeighboursMapsTmp[threadId][ currentLabel ].insert(neighbourLabel);
}
}
}
......
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