Commit 49366a84 authored by Pierre Lassalle's avatar Pierre Lassalle

Add the GRM Remote Module

parent 84788f22
OTB_CREATE_APPLICATION(NAME GRMSegmentation
SOURCES otbGRM.cxx
LINK_LIBRARIES ${${otb-module}_LIBRARIES}
)
/*=======================================================================
Program: ORFEO Toolbox
Language: C++
Date: $Date$
Version: $Revision$
Author: Lassalle Pierre
Contact: lassallepierre34@gmail.com
Website: http://pierre33.github.io/
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 "otbImage.h"
#include "otbVectorImage.h"
#include "otbImageFileReader.h"
#include "otbImageFileWriter.h"
#include "grmSpringSegmenter.h"
#include "grmFullLambdaScheduleSegmenter.h"
#include "grmBaatzSegmenter.h"
#include "otbWrapperApplication.h"
namespace otb
{
namespace Wrapper
{
class otbGRM : public Application
{
public:
typedef otbGRM Self;
typedef itk::SmartPointer<Self> Pointer;
typedef FloatVectorImageType ImageType;
typedef UInt32ImageType LabelImageType;
itkNewMacro(Self);
itkTypeMacro(otbGRM, otb::Application);
private:
void DoInit()
{
SetName("otbGRM");
SetDescription("This application allows to use the Generic Region Merging library (GRM) and provides currently 3 homogeneity criteria: Euclidean Distance, Full Lambda Schedule and Baatz & Schape criterion.");
AddParameter(ParameterType_InputImage, "in", "Input Image");
AddParameter(ParameterType_OutputFilename, "out", "Ouput Label Image");
AddParameter(ParameterType_Choice, "criterion", "Homogeneity criterion to use");
AddChoice("bs", "Baatz & Schape");
AddChoice("ed", "Euclidean Distance");
AddChoice("fls", "Full Lambda Schedule");
AddParameter(ParameterType_Float, "threshold", "Threshold for the criterion");
AddParameter(ParameterType_Int, "niter", "Number of iterations");
SetDefaultParameterFloat("niter", 0);
MandatoryOff("niter");
AddParameter(ParameterType_Int, "speed", "Activate it to boost the segmentation speed");
SetDefaultParameterFloat("speed", 0);
MandatoryOff("speed");
// For Baatz & Schape
AddParameter(ParameterType_Float, "cw", "Weight for the spectral homogeneity");
SetDefaultParameterFloat("cw", 0.5);
MandatoryOff("cw");
AddParameter(ParameterType_Float, "sw", "Weight for the spatial homogeneity");
SetDefaultParameterFloat("sw", 0.5);
MandatoryOff("sw");
}
void DoUpdateParameters()
{
}
void DoExecute()
{
// Mandatory parameters
// Input Image
ImageType::Pointer image = GetParameterImage("in");
image->Update();
// Output label file name.
const std::string labelImageFileName = GetParameterString("out");
// Criterion selected
const std::string selectedCriterion = GetParameterString("criterion");
// Threshold
float threshold = GetParameterFloat("threshold");
const unsigned int niter = GetParameterInt("niter");
const int speed = GetParameterInt("speed");
typedef otb::ImageFileWriter<LabelImageType> LabelImageWriter;
if(selectedCriterion == "bs")
{
const float cw = GetParameterFloat("cw");
const float sw = GetParameterFloat("sw");
grm::BaatzParam params;
params.m_SpectralWeight = cw;
params.m_ShapeWeight = sw;
grm::BaatzSegmenter<ImageType> segmenter;
segmenter.SetParam(params);
segmenter.SetThreshold(threshold*threshold);
segmenter.SetInput(image);
if(niter > 0)
segmenter.SetNumberOfIterations(niter);
if(speed > 0)
segmenter.SetDoFastSegmentation(true);
segmenter.Update();
LabelImageWriter::Pointer labelWriter = LabelImageWriter::New();
labelWriter->SetFileName(labelImageFileName);
labelWriter->SetInput(segmenter.GetLabeledClusteredOutput());
labelWriter->Update();
}
else if(selectedCriterion == "ed")
{
grm::SpringSegmenter<ImageType> segmenter;
segmenter.SetThreshold(threshold);
segmenter.SetInput(image);
if(niter > 0)
segmenter.SetNumberOfIterations(niter);
if(speed > 0)
segmenter.SetDoFastSegmentation(true);
segmenter.Update();
LabelImageWriter::Pointer labelWriter = LabelImageWriter::New();
labelWriter->SetFileName(labelImageFileName);
labelWriter->SetInput(segmenter.GetLabeledClusteredOutput());
labelWriter->Update();
}
else if(selectedCriterion == "fls")
{
grm::FullLambdaScheduleSegmenter<ImageType> segmenter;
segmenter.SetThreshold(threshold);
segmenter.SetInput(image);
if(niter > 0)
segmenter.SetNumberOfIterations(niter);
if(speed > 0)
segmenter.SetDoFastSegmentation(true);
segmenter.Update();
LabelImageWriter::Pointer labelWriter = LabelImageWriter::New();
labelWriter->SetFileName(labelImageFileName);
labelWriter->SetInput(segmenter.GetLabeledClusteredOutput());
labelWriter->Update();
}
}
};
} // end of namespace Wrapper
} // end of namespace otb
/*=========================================================================
Program: Generic Region Merging Library
Language: C++
author: Lassalle Pierre
contact: lassallepierre34@gmail.com
Copyright (c) Centre National d'Etudes Spatiales. All rights reserved
This software is distributed WITHOUT ANY WARRANTY; without even
the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
PURPOSE. See the above copyright notices for more information.
=========================================================================*/
#ifndef __GRM_GRAPH_OPERATIONS_H
#define __GRM_GRAPH_OPERATIONS_H
#include "grmGraph.h"
#include "grmNeighborhood.h"
#include <iostream>
#include <cassert>
#include <limits>
#include <map>
#include <utility>
#include <set>
#include <random>
#include <numeric>
namespace grm
{
template<class TSegmenter>
class GraphOperations
{
public:
/* Some convenient typedefs */
typedef TSegmenter SegmenterType;
typedef typename SegmenterType::ImageType ImageType;
typedef typename SegmenterType::GraphType GraphType;
typedef typename GraphType::NodeType NodeType;
typedef typename GraphType::EdgeType EdgeType;
typedef typename GraphType::NodePointerType NodePointerType;
typedef typename GraphType::NodeListType NodeList;
typedef typename GraphType::NodeIteratorType NodeIterator;
typedef typename GraphType::NodeConstIteratorType NodeConstIterator;
typedef typename GraphType::EdgeListType EdgeList;
typedef typename GraphType::EdgeIteratorType EdgeIterator;
typedef typename GraphType::EdgeConstIteratorType EdgeConstIterator;
using ContourOperator = lp::ContourOperations;
/*
* Given the size of the input image and the mask of the
* neighborhood, we initialize a new graph of nodes
*
* @params:
* GraphType& graph: reference to a graph of nodes
* const unsigned int width: width of the input image
* const unsigned int height: height of the input image
* CONNECTIVITY mask : mask of the neighborhood (4X4 or 8X8)
*/
static void InitNodes(ImageType * inputImg,
SegmenterType& seg,
CONNECTIVITY mask);
/*
* Given a graph of nodes, we explore all the nodes
* and for each node we compute his merging costs
* with all its neighboring nodes given a function
* to compute the merging cost between two nodes.
*
* @params:
* GraphType& graph: reference to the graph of nodes
* float(*fptr)(NodeType*, NodeType*): pointer to the function
* to compute the merging cost between two adjacent nodes.
*/
static void UpdateMergingCosts(SegmenterType& seg);
/*
* Given a node A, we analyse its best node B.
* If the node A is also node B's best node
* then it returns a pointer to node B if node A 's id
* is smaller or a pointer to node A if node B's id is
* smaller
* else it returns a null pointer.
* (Local Mutual Best Fitting Heuristic)
*
* @params:
* NodeType * a : Pointer to the node A
* float t : threshold of the criterion
*/
static NodePointerType CheckLMBF(NodePointerType, float t);
/*
* Given a node A, we analyse its best node B.
* If the criterion is checked and the node B
* is valid then it returns a pointer to the
* node A if node's A id is smaller or a pointer
* to node B if node B's id is smaller
* else it returns a null pointer.
*
* @params:
* NodeType * a : pointer to node A
* float t : threshold of the criterion
*/
static NodePointerType CheckBF(NodePointerType a, float t);
/*
* Given the current node and the target node, it returns
* the edge from the current node targeting to the target
* node.
*
* @params
* const NodeType * n : pointer to the current node.
* const NodeType * target : pointer to the target node.
* @return an iterator pointing to the candidate edge.
*/
static EdgeIterator FindEdge(NodePointerType n, NodePointerType target);
/*
* Given a node a and the node b to be merged into node a,
* it updates the neighbors of node a with respect to the
* neighbors of node b.
*
* @params
* NodeType * a : pointer to node a.
* NodeType * b : pointer to node b.
*/
static void UpdateNeighbors(NodePointerType a, NodePointerType b);
/*
* Given 2 nodes A and B (node B being merged into node A)
* we update the internal attributes of node A with respect
* to node B.
*
* @params:
* NodeType * a: pointer to node A.
* NodeType * b: pointer to node B.
*/
static void UpdateInternalAttributes(NodePointerType a,
NodePointerType b,
const unsigned int width);
/*
* Given a graph, it removes all the expired nodes.
*
* @params
* GraphType& graph : reference to the graph.
*/
static void RemoveExpiredNodes(GraphType& graph);
/*
* Given a graph, a region merging algorithm, a threshold
* and the dimension of the image, it performs one iteration
* of the merging process using the local mutual best fitting
* heuristic.
*
* @params
* GraphType& graph : reference to the graph
* SegmenterType& seg : reference to the region merging algorithm.
* const float threshold : threshold for this iteration.
* const unsigned int width : width of the image.
* const unsigned int height : height of the image.
*
* @return a boolean pointing out if there was at least a fusion
* of nodes.
*/
static bool PerfomOneIterationWithLMBF(SegmenterType& seg);
/*
* Given a graph, a region merging algorithm, a threshold,
* the number of iterations to apply and the dimension of the image,
* it performs all the iterations of the merging process using the
* local mutual best fitting heuristic.
* This method can be used when the threshold is constant during
* the region merging process.
*
* @params
* GraphType& graph : reference to the graph
* SegmenterType& seg : reference to the region merging algorithm.
* const float threshold : threshold for this iteration.
* const unsigned int numberOfIterations: number of iteration to perform.
* const unsigned int width : width of the image.
* const unsigned int height : height of the image.
*
* @return a boolean pointing out if there was at least a fusion
* of nodes.
*/
static bool PerfomAllIterationsWithLMBFAndConstThreshold(SegmenterType& seg);
static bool PerfomAllDitheredIterationsWithBF(SegmenterType& seg);
static bool PerfomOneDitheredIterationWithBF(SegmenterType& seg);
static void ComputeMergingCostsUsingDither(NodePointerType r, SegmenterType& seg);
};
} // end of namespace lsrm
#include "grmGraphOperations.txx"
#endif
/*=========================================================================
Program: Generic Region Merging Library
Language: C++
author: Lassalle Pierre
contact: lassallepierre34@gmail.com
Copyright (c) Centre National d'Etudes Spatiales. All rights reserved
This software is distributed WITHOUT ANY WARRANTY; without even
the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
PURPOSE. See the above copyright notices for more information.
=========================================================================*/
#ifndef __GRM_GRAPH_TO_OTBIMAGE_H
#define __GRM_GRAPH_TO_OTBIMAGE_H
#include <itkRGBPixel.h>
#include <itkImageRegion.h>
#include <otbImage.h>
#include <otbVectorImage.h>
#include <otbImageFileReader.h>
#include <otbImageFileWriter.h>
#include "grmGraph.h"
#include <string>
#include <stdlib.h>
#include <time.h>
#include "lpContour.h"
namespace grm
{
template<class TGraph>
class GraphToOtbImage
{
public:
/* Some convenient typedefs */
typedef TGraph GraphType;
typedef typename GraphType::NodeType NodeType;
typedef std::vector< std::shared_ptr<NodeType> > NodeList;
typedef typename NodeList::const_iterator NodeConstIterator;
typedef unsigned int LabelPixelType;
typedef otb::Image<LabelPixelType, 2> LabelImageType;
typedef unsigned char ClusterPixelType;
typedef otb::VectorImage<ClusterPixelType, 2> ClusteredImageType;
using ContourOperator = lp::ContourOperations;
LabelImageType::Pointer GetLabelImage(const GraphType& graph,
const unsigned int width,
const unsigned int height);
ClusteredImageType::Pointer GetClusteredOutput(const GraphType& graph,
const unsigned int width,
const unsigned int height);
};
} // end of namespace grm
#include "grmGraphToOtbImage.txx"
#endif
/*=========================================================================
Program: Generic Region Merging Library
Language: C++
author: Lassalle Pierre
contact: lassallepierre34@gmail.com
Copyright (c) Centre National d'Etudes Spatiales. All rights reserved
This software is distributed WITHOUT ANY WARRANTY; without even
the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
PURPOSE. See the above copyright notices for more information.
=========================================================================*/
#ifndef __GRM_GRAPH_TO_OTBIMAGE_TXX
#define __GRM_GRAPH_TO_OTBIMAGE_TXX
#include "grmGraphToOtbImage.h"
#include "itkImageRegionIterator.h"
namespace grm
{
template<class TGraph>
typename GraphToOtbImage<TGraph>::LabelImageType::Pointer
GraphToOtbImage<TGraph>::GetLabelImage(const GraphType& graph,
const unsigned int width,
const unsigned int height)
{
LabelImageType::IndexType index;
LabelImageType::SizeType size;
LabelImageType::RegionType region;
index[0] = 0; index[1] = 0;
size[0] = width; size[1] = height;
region.SetIndex(index);
region.SetSize(size);
LabelImageType::Pointer label_img = LabelImageType::New();
label_img->SetRegions(region);
label_img->Allocate();
using LabelImageIterator = itk::ImageRegionIterator<LabelImageType>;
LabelImageIterator it(label_img, label_img->GetLargestPossibleRegion());
for(it.GoToBegin();!it.IsAtEnd(); ++it)
it.Set(0);
// Start at 1 (value 0 can be used for invalid pixels)
unsigned int label = 1;
for(auto& node : graph.m_Nodes)
{
lp::CellLists borderPixels;
ContourOperator::GenerateBorderCells(borderPixels, node->m_Contour, node->m_Id, width);
for (auto& pix: borderPixels)
{
index[0] = pix % width;
index[1] = pix / width;
label_img->SetPixel(index, label);
}
++label;
}
long unsigned int pixelValue = 0;
for(it.GoToBegin(); !it.IsAtEnd(); ++it)
{
auto pixel = it.Get();
if(pixel == 0)
it.Set(pixelValue);
else
pixelValue = pixel;
}
return label_img;
}
template<class TGraph>
typename GraphToOtbImage<TGraph>::ClusteredImageType::Pointer
GraphToOtbImage<TGraph>::GetClusteredOutput(const GraphType& graph,
const unsigned int width,
const unsigned int height)
{
ClusteredImageType::IndexType index;
ClusteredImageType::SizeType size;
ClusteredImageType::RegionType region;
index[0] = 0; index[1] = 0;
size[0] = width; size[1] = height;
region.SetIndex(index);
region.SetSize(size);
ClusteredImageType::Pointer clusterImg = ClusteredImageType::New();
clusterImg->SetRegions(region);
clusterImg->SetNumberOfComponentsPerPixel(3);
clusterImg->Allocate();
ClusteredImageType::PixelType pixelValue;
pixelValue.Reserve(3);
pixelValue[0] = 255;
pixelValue[1] = 255;
pixelValue[2] = 255;
using ClusterImageIterator = itk::ImageRegionIterator<ClusteredImageType>;
ClusterImageIterator it(clusterImg, clusterImg->GetLargestPossibleRegion());
for(it.GoToBegin();!it.IsAtEnd(); ++it)
it.Set(pixelValue);
srand(time(NULL));
unsigned char c1, c2, c3;
for(auto& node : graph.m_Nodes)
{
auto rv = rand() % 255;
c1 = rv;//rand() % 256;
c2 = rv;//rand() % 256;
c3 = rv;//rand() % 256;
lp::CellLists borderPixels;
ContourOperator::GenerateBorderCells(borderPixels, node->m_Contour, node->m_Id, width);
for (auto& pix : borderPixels)
{
index[0] = pix % width;
index[1] = pix / width;
pixelValue[0] = c1;
pixelValue[1] = c2;
pixelValue[2] = c3;
clusterImg->SetPixel(index, pixelValue);
}
}
for(it.GoToBegin(); !it.IsAtEnd(); ++it)
{
auto pixel = it.Get();
if(pixel[0] == 255 && pixel[1] == 255 && pixel[2] == 255)
it.Set(pixelValue);
else
pixelValue = pixel;
}
return clusterImg;
}
} // end of namespace grm
#endif
otb_module_test()
otb_test_application(NAME apOTBGRM
APP otbGRM
OPTIONS -in ${INPUTDATA}/QB_Toulouse_Ortho_XS.tif
-out ${TEMP}/apGRMLabeledImage.tif int16
-criterion bs
-threshold 60
-cw 0.7
-sw 0.3
)
otb_test_application(NAME apOTBGRM
APP otbGRM
OPTIONS -in ${INPUTDATA}/QB_Toulouse_Ortho_XS.tif
-out ${TEMP}/apGRMLabeledImage.tif int16
-criterion ed
-threshold 30
)
otb_test_application(NAME apOTBGRM
APP otbGRM
OPTIONS -in ${INPUTDATA}/QB_Toulouse_Ortho_XS.tif
-out ${TEMP}/apGRMLabeledImage.tif int16
-criterion fls
-threshold 500
)
otb_test_application(NAME apOTBGRM
APP otbGRM
OPTIONS -in ${INPUTDATA}/QB_Toulouse_Ortho_XS.tif
-out ${TEMP}/apGRMLabeledImage.tif int16
-speed 1
-criterion bs
-threshold 60
-cw 0.7
-sw 0.3
)
otb_test_application(NAME apOTBGRM
APP otbGRM
OPTIONS -in ${INPUTDATA}/QB_Toulouse_Ortho_XS.tif
-out ${TEMP}/apGRMLabeledImage.tif int16
-speed 1
-criterion ed
-threshold 30
)
otb_test_application(NAME apOTBGRM
APP otbGRM
OPTIONS -in ${INPUTDATA}/QB_Toulouse_Ortho_XS.tif
-out ${TEMP}/apGRMLabeledImage.tif int16
-speed 1
-criterion fls
-threshold 500
)
otb_test_application(NAME apOTBGRM
APP otbGRM
OPTIONS -in ${INPUTDATA}/QB_Toulouse_Ortho_XS.tif
-out ${TEMP}/apGRMLabeledImage.tif int16
-niter 75
-criterion bs
-threshold 60
-cw 0.7
-sw 0.3
)
otb_test_application(NAME apOTBGRM
APP otbGRM
OPTIONS -in ${INPUTDATA}/QB_Toulouse_Ortho_XS.tif
-out ${TEMP}/apGRMLabeledImage.tif int16
-niter 75
-criterion ed
-threshold 30
)
otb_test_application(NAME apOTBGRM
APP otbGRM
OPTIONS -in ${INPUTDATA}/QB_Toulouse_Ortho_XS.tif
-out ${TEMP}/apGRMLabeledImage.tif int16
-niter 75
-criterion fls
-threshold 500
)
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