An error occurred while loading the file. Please try again.
-
Mathias Chouet authored
refactored PAB for late model binding
a5e70168
/*=========================================================================
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_TXX
#define GRM_GRAPH_OPERATIONS_TXX
#include <otbImageFileReader.h>
#include "grmGraphOperations.h"
namespace grm
{
template<class TSegmenter>
void GraphOperations<TSegmenter>::InitNodes(ImageType * inputImg,
SegmenterType& seg,
CONNECTIVITY mask)
{
unsigned int width, height;
{
width = inputImg->GetLargestPossibleRegion().GetSize()[0];
height = inputImg->GetLargestPossibleRegion().GetSize()[1];
}
const long unsigned int num_nodes = width * height;
seg.m_Graph.m_Nodes.reserve(num_nodes);
for(long unsigned int i = 0;
i < num_nodes;
++i)
{
NodePointerType n(new NodeType);
n->m_Id = i;
n->m_Valid = true;
n->m_Expired = false;
n->m_IsMerged = true; // force to compute costs for the first iteration
n->m_Perimeter = 4;
n->m_Area = 1;
n->m_Bbox.m_UX = i % width;
n->m_Bbox.m_UY = i / width;
n->m_Bbox.m_W = 1;
n->m_Bbox.m_H = 1;
// An initial contour is the one aroun a pixel
ContourOperator::Push1(n->m_Contour);
ContourOperator::Push2(n->m_Contour);
ContourOperator::Push3(n->m_Contour);
ContourOperator::Push0(n->m_Contour);
seg.m_Graph.m_Nodes.push_back(n);
}
seg.InitFromImage();
if(mask == FOUR)
{
for(auto& r : seg.m_Graph.m_Nodes)
7172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140
{
long int neighborhood[4];
FOURNeighborhood(neighborhood, r->m_Id, width, height);
for(short j = 0; j < 4; ++j)
{
if(neighborhood[j] > -1 && !seg.m_Graph.m_Nodes[neighborhood[j]]->m_Expired)
r->m_Edges.push_back(EdgeType( seg.m_Graph.m_Nodes[neighborhood[j]], 0, 1));
}
}
}
else
{
for(auto& r : seg.m_Graph.m_Nodes)
{
long int neighborhood[8];
EIGHTNeighborhood(neighborhood, r->m_Id, width, height);
for(short j = 0; j < 8; ++j)
{
if(neighborhood[j] > -1 && !seg.m_Graph.m_Nodes[neighborhood[j]]->m_Expired)
{
if(j % 2 > 0)
r->m_Edges.push_back(EdgeType( seg.m_Graph.m_Nodes[neighborhood[j]], 0, 0));
else
r->m_Edges.push_back(EdgeType( seg.m_Graph.m_Nodes[neighborhood[j]], 0, 1));
}
}
}
}
RemoveExpiredNodes(seg.m_Graph);
}
template<class TSegmenter>
void GraphOperations<TSegmenter>::UpdateMergingCosts(SegmenterType& seg)
{
float min_cost;
long unsigned int min_id = 0;
std::size_t idx, min_idx;
for(auto& r : seg.m_Graph.m_Nodes)
{
for(auto& edge : r->m_Edges)
edge.m_CostUpdated = false;
}
for(auto& r : seg.m_Graph.m_Nodes)
{
min_cost = std::numeric_limits<float>::max();
idx = 0;
min_idx = 0;
r->m_Expired = false;
r->m_Valid = true;
for(auto& edge : r->m_Edges)
{
auto neighborR = edge.GetRegion();
// Compute the cost if necessary
if(!edge.m_CostUpdated && (neighborR->m_IsMerged || r->m_IsMerged))
{
auto edgeFromNeighborToR = FindEdge(neighborR, r);
edge.m_Cost = seg.ComputeMergingCost(r, neighborR);
edgeFromNeighborToR->m_Cost = edge.m_Cost;
edge.m_CostUpdated = true;
edgeFromNeighborToR->m_CostUpdated = true;
}
// Check if the cost of the edge is the minimum
141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210
if(min_cost > edge.m_Cost)
{
min_cost = edge.m_Cost;
min_id = neighborR->m_Id;
min_idx = idx;
}
else if(min_cost == edge.m_Cost)
{
if(min_id > neighborR->m_Id)
{
min_id = neighborR->m_Id;
min_idx = idx;
}
}
++idx;
}
assert(min_idx < r->m_Edges.size());
std::swap(r->m_Edges[0], r->m_Edges[min_idx]);
}
// Reset the merge flag for all the regions.
for(auto& r : seg.m_Graph.m_Nodes)
r->m_IsMerged = false;
}
template<class TSegmenter>
typename GraphOperations<TSegmenter>::NodePointerType
GraphOperations<TSegmenter>::CheckLMBF(NodePointerType a, float t)
{
if(a->m_Valid)
{
float cost = a->m_Edges.front().m_Cost;
if(cost < t)
{
NodePointerType b = a->m_Edges.front().GetRegion();
if( b->m_Valid)
{
NodePointerType best_b = b->m_Edges.front().GetRegion();
if(a == best_b)
{
if(a->m_Id < b->m_Id)
return a;
else
return b;
}
else
return NodePointerType();
}
else
return NodePointerType();
}
else
return NodePointerType();
}
else
return NodePointerType();
}
template<class TSegmenter>
typename GraphOperations<TSegmenter>::NodePointerType
GraphOperations<TSegmenter>::CheckBF(NodePointerType a, float t)
{
if(a->m_Valid)
{
float cost = a->m_Edges.front().m_Cost;
211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280
if( cost < t )
{
NodePointerType b = a->m_Edges.front().GetRegion();
if(b->m_Valid)
{
if( a->m_Id < b->m_Id )
return a;
else
return b;
}
else
return NodePointerType();
}
else
return NodePointerType();
}
else
return NodePointerType();
}
template<class TSegmenter>
typename GraphOperations<TSegmenter>::EdgeIterator
GraphOperations<TSegmenter>::FindEdge(NodePointerType n, NodePointerType target)
{
return std::find_if(n->m_Edges.begin(), n->m_Edges.end(),[&](EdgeType& e)->bool{
return e.GetRegion() == target;
});
}
template<class TSegmenter>
void
GraphOperations<TSegmenter>::UpdateNeighbors(NodePointerType a, NodePointerType b)
{
unsigned int boundary;
/* Explore the neighbors of b */
for (auto& edge : b->m_Edges)
{
// Retrieve the edge targeting node b.
auto neigh_b = edge.GetRegion();
auto toB = FindEdge(neigh_b, b);
/* If the edge tageting to node b is the first then
the corresponding node is not valid anymore. */
if(toB == neigh_b->m_Edges.begin())
neigh_b->m_Valid = false;
/* Keep in memory the boundary between node b
and node neigh_b */
boundary = edge.m_Boundary;
/*
We can remove safely the edge from node neigh_b
targeting to node b
*/
neigh_b->m_Edges.erase(toB);
if(neigh_b != a)
{
/* Retrieve the edge targeting to node a. */
auto toA = FindEdge(neigh_b, a);
if( toA == neigh_b->m_Edges.end() )
{
/* No edge exists between node a and node neigh_b. */
/* Add an edge from node neigh_b targeting node a. */
neigh_b->m_Edges.push_back(EdgeType(a, 0, boundary));
281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350
/* Add an edge from node a targeting node neigh_b. */
a->m_Edges.push_back(EdgeType(neigh_b, 0, boundary));
}
else
{
/* An edge exists between node a and node neigh_b. */
/* Increment the boundary of the edge from node neigh_b
targeting to node a. */
toA->m_Boundary += boundary;
/* Increment the boundary of the edge from node a
targeting to node neigh_b. */
auto toNeighB = FindEdge(a, neigh_b);
toNeighB->m_Boundary += boundary;
}
}
}
}
template<class TSegmenter>
void
GraphOperations<TSegmenter>::UpdateInternalAttributes(NodePointerType a,
NodePointerType b,
const unsigned int width)
{
lp::BoundingBox mergedBBox;
lp::Contour mergedContour;
ContourOperator::MergeContour(mergedContour, mergedBBox, a->m_Contour,
b->m_Contour, a->m_Bbox, b->m_Bbox,
a->m_Id, b->m_Id, width);
/* Step 1: update the bounding box */
a->m_Bbox = mergedBBox;//MergeBoundingBoxes(a->m_Bbox, b->m_Bbox);
/* Step 2: update the contour */
a->m_Contour = mergedContour;
/* Step 2 : update perimeter and area attributes */
EdgeIterator toB = FindEdge(a, b);
a->m_Perimeter += (b->m_Perimeter - 2 * toB->m_Boundary);
a->m_Area += b->m_Area;
/* Step 2: update the neighborhood */
UpdateNeighbors(a,b);
/* Step 3: update the node' states */
a->m_Valid = false;
b->m_Valid = false;
b->m_Expired = true;
a->m_IsMerged = true;
}
template<class TSegmenter>
void
GraphOperations<TSegmenter>::RemoveExpiredNodes(GraphType& graph)
{
NodeIterator nit = std::remove_if(graph.m_Nodes.begin(), graph.m_Nodes.end(), [](NodePointerType r)->bool{
return r->m_Expired;
});
graph.m_Nodes.erase(nit, graph.m_Nodes.end());
}
template<class TSegmenter>
bool
GraphOperations<TSegmenter>::PerfomOneIterationWithLMBF(SegmenterType& seg)
351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420
{
bool merged = false;
/* Update the costs of merging between adjacent nodes */
UpdateMergingCosts(seg);
for(auto& region : seg.m_Graph.m_Nodes)
{
auto res_node = CheckLMBF(region, seg.GetThreshold());
if(res_node)
{
seg.UpdateSpecificAttributes(res_node, res_node->m_Edges.front().GetRegion());
UpdateInternalAttributes(res_node, res_node->m_Edges.front().GetRegion(),
seg.GetImageWidth());
merged = true;
}
}
RemoveExpiredNodes(seg.m_Graph);
if(seg.m_Graph.m_Nodes.size() < 2)
return false;
return merged;
}
template<class TSegmenter>
bool
GraphOperations<TSegmenter>::PerfomAllIterationsWithLMBFAndConstThreshold(SegmenterType& seg)
{
bool merged = true;
unsigned int maxNumberOfIterations;
unsigned int iterations = 0;
unsigned int numberOfIterations = seg.GetNumberOfIterations();
if(numberOfIterations < 1)
maxNumberOfIterations = 200;
else
maxNumberOfIterations = numberOfIterations;
while(merged &&
iterations < maxNumberOfIterations &&
seg.m_Graph.m_Nodes.size() > 1)
{
std::cout << "." << std::flush;
++iterations;
merged = PerfomOneIterationWithLMBF(seg);
}
std::cout << std::endl;
if(seg.m_Graph.m_Nodes.size() < 2)
return false;
return merged;
}
/* New !!! utilisation of a dither matrix */
template<class TSegmenter>
bool
GraphOperations<TSegmenter>::PerfomAllDitheredIterationsWithBF(SegmenterType& seg)
{
bool merged = true;
unsigned int maxNumberOfIterations;
unsigned int numberOfIterations = seg.GetNumberOfIterations();
if(numberOfIterations < 1)
421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490
maxNumberOfIterations = 200;
else
maxNumberOfIterations = numberOfIterations;
unsigned int iterations = 0;
while(merged &&
iterations < maxNumberOfIterations &&
seg.m_Graph.m_Nodes.size() > 1)
{
std::cout << "." << std::flush;
++iterations;
merged = PerfomOneDitheredIterationWithBF(seg);
}
std::cout << std::endl;
if(seg.m_Graph.m_Nodes.size() < 2)
return false;
return merged;
}
template<class TSegmenter>
bool
GraphOperations<TSegmenter>::PerfomOneDitheredIterationWithBF(SegmenterType& seg)
{
bool merged = false;
std::vector<long unsigned int> randomIndices(seg.m_Graph.m_Nodes.size());
std::iota(randomIndices.begin(), randomIndices.end(), 0);
std::shuffle(randomIndices.begin(), randomIndices.end(), std::mt19937{std::random_device{}()});
for(const auto& i : randomIndices)
{
if(seg.m_Graph.m_Nodes[i]->m_Valid == true)
{
auto currSeg = seg.m_Graph.m_Nodes[i];
// This segment is marked as used.
currSeg->m_Valid = false;
// Compute cost with all its neighbors
ComputeMergingCostsUsingDither(currSeg, seg);
// Get the most similar segment
auto bestSeg = currSeg->m_Edges.front().GetRegion();
if(currSeg->m_Edges.front().m_Cost < seg.GetThreshold() && !bestSeg->m_Expired)
{
merged = true;
if(currSeg->m_Id < bestSeg->m_Id)
{
seg.UpdateSpecificAttributes(currSeg, bestSeg);
UpdateInternalAttributes(currSeg, bestSeg, seg.GetImageWidth());
for(auto& edge : currSeg->m_Edges)
{
edge.m_CostUpdated = false;
auto toNeigh = FindEdge(edge.GetRegion(), currSeg);
toNeigh->m_CostUpdated = false;
}
}
else
{
seg.UpdateSpecificAttributes(bestSeg, currSeg);
UpdateInternalAttributes(bestSeg, currSeg, seg.GetImageWidth());
for(auto& edge : bestSeg->m_Edges)
491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554
{
edge.m_CostUpdated = false;
auto toNeigh = FindEdge(edge.GetRegion(), bestSeg);
toNeigh->m_CostUpdated = false;
}
}
}
}
}
RemoveExpiredNodes(seg.m_Graph);
// Mark all the segments to be valid
if(seg.m_Graph.m_Nodes.size() < 2)
return false;
for(auto& r : seg.m_Graph.m_Nodes)
r->m_Valid = true;
return merged;
}
template<class TSegmenter>
void GraphOperations<TSegmenter>::ComputeMergingCostsUsingDither(NodePointerType r, SegmenterType& seg)
{
float min_cost = std::numeric_limits<float>::max();
std::size_t idx = 0, min_idx = 0;
for(auto& edge : r->m_Edges)
{
// Compute the cost if the neighbor is not expired and the cost has to be updated
if(!edge.GetRegion()->m_Expired)
{
auto neighborR = edge.GetRegion();
// Compute the cost if necessary
if(!edge.m_CostUpdated)
{
auto edgeFromNeighborToR = FindEdge(neighborR, r);
edge.m_Cost = seg.ComputeMergingCost(r, neighborR);
edgeFromNeighborToR->m_Cost = edge.m_Cost;
edge.m_CostUpdated = true;
edgeFromNeighborToR->m_CostUpdated = true;
}
// Check if the cost of the edge is the minimum
if(min_cost > edge.m_Cost)
{
min_cost = edge.m_Cost;
min_idx = idx;
}
++idx;
}
}
assert(min_idx < r->m_Edges.size());
std::swap(r->m_Edges[0], r->m_Edges[min_idx]);
}
} // end of namespace grm
#endif