mirror of
https://github.com/truebelief/cc-treeiso-plugin.git
synced 2026-08-31 01:20:29 +08:00
707 lines
34 KiB
C++
707 lines
34 KiB
C++
#pragma once
|
|
|
|
//Local
|
|
#include "Graph.h"
|
|
|
|
//System
|
|
#include <math.h>
|
|
#include <queue>
|
|
#include <iostream>
|
|
#include <fstream>
|
|
|
|
//Boost
|
|
#include <boost/graph/boykov_kolmogorov_max_flow.hpp>
|
|
|
|
namespace CP
|
|
{
|
|
template <typename T>
|
|
struct CPparameter
|
|
{
|
|
T reg_strenth; //regularization strength, multiply the edge weight
|
|
uint32_t cutoff; //minimal component size
|
|
uint32_t flow_steps; //number of steps in the optimal binary cut computation
|
|
uint32_t kmeans_ite; //number of iteration in the kmeans sampling
|
|
uint32_t kmeans_resampling; //number of kmeans re-intilialization
|
|
uint32_t verbose; //verbosity
|
|
uint32_t max_ite_main; //max number of iterations in the main loop
|
|
bool backward_step; //indicates if a backward step should be performed
|
|
double stopping_ratio; //when (E(t-1) - E(t) / (E(0) - E(t)) is too small, the algorithm stops
|
|
fidelityType fidelity; //the fidelity function
|
|
double smoothing; //smoothing term (for Kl divergence only)
|
|
bool parallel; //enable/disable parrallelism
|
|
T weight_decay; //for continued optimization of the flow steps
|
|
};
|
|
|
|
template <typename T>
|
|
struct CutPursuit
|
|
{
|
|
Graph<T> main_graph; //the Graph structure containing the main structure
|
|
Graph<T> reduced_graph; //the reduced graph whose vertices are the connected component
|
|
std::vector<std::vector<VertexDescriptor<T>>> components; //contains the list of the vertices in each component
|
|
std::vector<VertexDescriptor<T>> root_vertex; //the root vertex for each connected components
|
|
std::vector<bool> saturated_components; //is the component saturated (uncuttable)
|
|
std::vector<std::vector<EdgeDescriptor>> borders; //the list of edges forming the borders between the connected components
|
|
VertexDescriptor<T> source; //source vertex for graph cut
|
|
VertexDescriptor<T> sink; //sink vertex
|
|
uint32_t dim; // dimension of the data
|
|
uint32_t nVertex; // number of data point
|
|
uint32_t nEdge; // number of edges between vertices (not counting the edge to source/sink)
|
|
CP::VertexIterator<T> lastIterator; //iterator pointing to the last vertex which is neither sink nor source
|
|
CPparameter<T> parameter;
|
|
|
|
CutPursuit(uint32_t nbVertex = 1)
|
|
{
|
|
this->main_graph = Graph<T>(nbVertex);
|
|
this->reduced_graph = Graph<T>(1);
|
|
this->components = std::vector<std::vector<VertexDescriptor<T>>>(1);
|
|
this->root_vertex = std::vector<VertexDescriptor<T>>(1, 0);
|
|
this->saturated_components = std::vector<bool>(1, false);
|
|
this->source = VertexDescriptor<T>();
|
|
this->sink = VertexDescriptor<T>();
|
|
this->dim = 1;
|
|
this->nVertex = 1;
|
|
this->nEdge = 0;
|
|
this->parameter.flow_steps = 3;
|
|
this->parameter.kmeans_ite = 5;
|
|
this->parameter.kmeans_resampling = 3;
|
|
this->parameter.verbose = 2;
|
|
this->parameter.max_ite_main = 6;
|
|
this->parameter.backward_step = true;
|
|
this->parameter.stopping_ratio = 0.0001;
|
|
this->parameter.fidelity = L2;
|
|
this->parameter.smoothing = 0.1;
|
|
this->parameter.parallel = true;
|
|
this->parameter.weight_decay = static_cast<T>(0.7);
|
|
}
|
|
|
|
//=============================================================================================
|
|
std::pair<std::vector<T>, std::vector<T>> run()
|
|
{
|
|
//first initilialize the structure
|
|
this->initialize();
|
|
if (this->parameter.verbose > 0)
|
|
{
|
|
std::cout << "Graph " << boost::num_vertices(this->main_graph) << " vertices and "
|
|
<< boost::num_edges(this->main_graph) << " edges and observation of dimension "
|
|
<< this->dim << '\n';
|
|
}
|
|
T energy_zero = this->compute_energy().first; //energy with 1 component
|
|
T old_energy = energy_zero; //energy at the previous iteration
|
|
//vector with time and energy, useful for benchmarking
|
|
std::vector<T> energy_out(this->parameter.max_ite_main), time_out(this->parameter.max_ite_main);
|
|
TimeStack ts; ts.tic();
|
|
//the main loop
|
|
for (uint32_t ite_main = 1; ite_main <= this->parameter.max_ite_main; ite_main++)
|
|
{
|
|
//--------those two lines are the whole iteration-------------------------
|
|
size_t saturation = this->split(); //compute optimal binary partition
|
|
this->reduce(); //compute the new reduced graph
|
|
//-------end of the iteration - rest is stopping check and display------
|
|
std::pair<T, T> energy = this->compute_energy();
|
|
energy_out.push_back((energy.first + energy.second));
|
|
time_out.push_back(ts.tocDouble());
|
|
if (this->parameter.verbose > 1)
|
|
{
|
|
printf("Iteration %3i - %4i components - ", ite_main, static_cast<int>(this->components.size()));
|
|
printf("Saturation %5.1f %% - ", (100.0 * saturation) / this->nVertex);
|
|
switch (this->parameter.fidelity)
|
|
{
|
|
case L2:
|
|
{
|
|
printf("Quadratic Energy %4.3f %% - ", 100 * (energy.first + energy.second) / energy_zero);
|
|
break;
|
|
}
|
|
case linear:
|
|
{
|
|
printf("Linear Energy %10.1f - ", energy.first + energy.second);
|
|
break;
|
|
}
|
|
case KL:
|
|
{
|
|
printf("KL Energy %4.3f %% - ", 100 * (energy.first + energy.second) / energy_zero);
|
|
break;
|
|
}
|
|
case SPG:
|
|
{
|
|
printf("Quadratic Energy %4.3f %% - ", 100 * (energy.first + energy.second) / energy_zero);
|
|
break;
|
|
}
|
|
}
|
|
std::cout << "Timer " << ts.toc() << std::endl;
|
|
}
|
|
//----stopping checks-----
|
|
if (saturation == this->nVertex)
|
|
{ //all components are saturated
|
|
if (this->parameter.verbose > 1)
|
|
{
|
|
std::cout << "All components are saturated" << std::endl;
|
|
}
|
|
break;
|
|
}
|
|
if ((old_energy - energy.first - energy.second) / (old_energy)
|
|
< this->parameter.stopping_ratio)
|
|
{ //relative energy progress stopping criterion
|
|
if (this->parameter.verbose > 1)
|
|
{
|
|
std::cout << "Stopping criterion reached" << std::endl;
|
|
}
|
|
break;
|
|
}
|
|
if (ite_main >= this->parameter.max_ite_main)
|
|
{ //max number of iteration
|
|
if (this->parameter.verbose > 1)
|
|
{
|
|
std::cout << "Max number of iteration reached" << std::endl;
|
|
}
|
|
break;
|
|
}
|
|
old_energy = energy.first + energy.second;
|
|
}
|
|
if (this->parameter.cutoff > 0)
|
|
{
|
|
this->cutoff();
|
|
}
|
|
return std::pair<std::vector<T>, std::vector<T>>(energy_out, time_out);
|
|
}
|
|
|
|
//=============================================================================================
|
|
//=========== VIRTUAL METHODS DEPENDING ON THE CHOICE OF FIDELITY FUNCTION =====================
|
|
//=============================================================================================
|
|
//
|
|
//=============================================================================================
|
|
//============================= SPLIT ===========================================
|
|
//=============================================================================================
|
|
virtual size_t split()
|
|
{
|
|
//compute the optimal binary partition
|
|
return 0;
|
|
}
|
|
|
|
//=============================================================================================
|
|
//================================ compute_energy_L2 ====================================
|
|
//=============================================================================================
|
|
virtual std::pair<T, T> compute_energy()
|
|
{
|
|
//compute the current energy
|
|
return std::pair<T, T>(0, 0);
|
|
}
|
|
|
|
//=============================================================================================
|
|
//================================= COMPUTE_VALUE =========================================
|
|
//=============================================================================================
|
|
virtual std::pair<std::vector<T>, T> compute_value(const uint32_t & ind_com)
|
|
{
|
|
//compute the optimal the values associated with the current partition
|
|
return std::pair<std::vector<T>, T>(std::vector<T>(0), 0);
|
|
}
|
|
|
|
//=============================================================================================
|
|
//================================= COMPUTE_MERGE_GAIN =========================================
|
|
//=============================================================================================
|
|
virtual std::pair<std::vector<T>, T> compute_merge_gain(const VertexDescriptor<T> & comp1
|
|
, const VertexDescriptor<T> & comp2)
|
|
{
|
|
//compute the gain of mergeing two connected components
|
|
return std::pair<std::vector<T>, T>(std::vector<T>(0), 0);
|
|
}
|
|
|
|
//=============================================================================================
|
|
//========================== END OF VIRTUAL METHODS ===========================================
|
|
//=============================================================================================
|
|
|
|
//=============================================================================================
|
|
//============================= INITIALIZE ===========================================
|
|
//=============================================================================================
|
|
void initialize()
|
|
{
|
|
//build the reduced graph with one component, fill the first vector of components
|
|
//and add the sink and source nodes
|
|
VertexIterator<T> ite_ver, ite_ver_end;
|
|
EdgeAttributeMap<T> edge_attribute_map = boost::get(boost::edge_bundle, this->main_graph);
|
|
this->components[0] = std::vector<VertexDescriptor<T>>(0);//(this->nVertex);
|
|
this->root_vertex[0] = *boost::vertices(this->main_graph).first;
|
|
this->nVertex = static_cast<uint32_t>(boost::num_vertices(this->main_graph));
|
|
this->nEdge = static_cast<uint32_t>(boost::num_edges(this->main_graph));
|
|
//--------compute the first reduced graph----------------------------------------------------------
|
|
for (boost::tie(ite_ver, ite_ver_end) = boost::vertices(this->main_graph);
|
|
ite_ver != ite_ver_end; ++ite_ver)
|
|
{
|
|
this->components[0].push_back(*ite_ver);
|
|
}
|
|
this->lastIterator = ite_ver;
|
|
this->compute_value(0);
|
|
//--------build the link to source and sink--------------------------------------------------------
|
|
this->source = boost::add_vertex(this->main_graph);
|
|
this->sink = boost::add_vertex(this->main_graph);
|
|
uint32_t eIndex = static_cast<uint32_t>(boost::num_edges(this->main_graph));
|
|
ite_ver = boost::vertices(this->main_graph).first;
|
|
for (uint32_t ind_ver = 0; ind_ver < this->nVertex; ind_ver++)
|
|
{
|
|
// note that source and edge will have many nieghbors, and hence boost::edge should never be called to get
|
|
// the in_edge. use the out_edge and then reverse_Edge
|
|
addDoubledge<T>(this->main_graph, this->source, boost::vertex(ind_ver, this->main_graph), 0.,
|
|
eIndex, edge_attribute_map, false);
|
|
eIndex += 2;
|
|
addDoubledge<T>(this->main_graph, boost::vertex(ind_ver, this->main_graph), this->sink, 0.,
|
|
eIndex, edge_attribute_map, false);
|
|
eIndex += 2;
|
|
++ite_ver;
|
|
}
|
|
|
|
}
|
|
|
|
//=============================================================================================
|
|
//================================ COMPUTE_REDUCE_VALUE ====================================
|
|
//=============================================================================================
|
|
void compute_reduced_value()
|
|
{
|
|
for (uint32_t ind_com = 0; ind_com < this->components.size(); ++ind_com)
|
|
{ //compute the reduced value of each component
|
|
compute_value(ind_com);
|
|
}
|
|
}
|
|
|
|
//=============================================================================================
|
|
//============================= ACTIVATE_EDGES ==========================================
|
|
//=============================================================================================
|
|
size_t activate_edges(bool allows_saturation = true)
|
|
{ //this function analyzes the optimal binary partition to detect:
|
|
//- saturated components (i.e. uncuttable)
|
|
//- new activated edges
|
|
VertexAttributeMap<T> vertex_attribute_map = boost::get(boost::vertex_bundle, this->main_graph);
|
|
EdgeAttributeMap<T> edge_attribute_map = boost::get(boost::edge_bundle, this->main_graph);
|
|
//saturation is the proportion of nodes in saturated components
|
|
size_t saturation = 0;
|
|
uint32_t nb_comp = static_cast<uint32_t>(this->components.size());
|
|
//---- first check if the component are saturated-------------------------
|
|
//#pragma omp parallel for if (this->parameter.parallel) schedule(dynamic)
|
|
for (uint32_t ind_com = 0; ind_com < nb_comp; ind_com++)
|
|
{
|
|
if (this->saturated_components[ind_com])
|
|
{ //ind_com is saturated, we increement saturation by ind_com size
|
|
saturation += this->components[ind_com].size();
|
|
continue;
|
|
}
|
|
std::vector<T> totalWeight(2, 0);
|
|
for (uint32_t ind_ver = 0; ind_ver < this->components[ind_com].size(); ind_ver++)
|
|
{
|
|
bool isSink
|
|
= (vertex_attribute_map(this->components[ind_com][ind_ver]).color
|
|
== vertex_attribute_map(this->sink).color);
|
|
if (isSink)
|
|
{
|
|
totalWeight[0] += vertex_attribute_map(this->components[ind_com][ind_ver]).weight;
|
|
}
|
|
else
|
|
{
|
|
totalWeight[1] += vertex_attribute_map(this->components[ind_com][ind_ver]).weight;
|
|
}
|
|
}
|
|
if (allows_saturation && ((totalWeight[0] == 0) || (totalWeight[1] == 0)))
|
|
{
|
|
//the component is saturated
|
|
this->saturateComponent(ind_com);
|
|
saturation += this->components[ind_com].size();
|
|
}
|
|
}
|
|
//----check which edges have been activated----
|
|
EdgeIterator<T> ite_edg, ite_edg_end;
|
|
uint32_t color_v1, color_v2, color_combination;
|
|
for (boost::tie(ite_edg, ite_edg_end) = boost::edges(this->main_graph);
|
|
ite_edg != ite_edg_end; ++ite_edg)
|
|
{
|
|
if (!edge_attribute_map(*ite_edg).realEdge)
|
|
{
|
|
continue;
|
|
}
|
|
color_v1 = vertex_attribute_map(boost::source(*ite_edg, this->main_graph)).color;
|
|
color_v2 = vertex_attribute_map(boost::target(*ite_edg, this->main_graph)).color;
|
|
//color_source = 0, color_sink = 4, uncolored = 1
|
|
//we want an edge when a an interface source/sink
|
|
//this corresponds to a sum of 4
|
|
//for the case of uncolored nodes we arbitrarily chose source-uncolored
|
|
color_combination = color_v1 + color_v2;
|
|
if ((color_combination == 0) || (color_combination == 2) || (color_combination == 2)
|
|
|| (color_combination == 8))
|
|
{ //edge between two vertices of the same color
|
|
continue;
|
|
}
|
|
//the edge is active!
|
|
edge_attribute_map(*ite_edg).isActive = true;
|
|
edge_attribute_map(*ite_edg).capacity = 0;
|
|
vertex_attribute_map(boost::source(*ite_edg, this->main_graph)).isBorder = true;
|
|
vertex_attribute_map(boost::target(*ite_edg, this->main_graph)).isBorder = true;
|
|
}
|
|
return saturation;
|
|
}
|
|
|
|
//=============================================================================================
|
|
//============================= REDUCE ===========================================
|
|
//=============================================================================================
|
|
void reduce()
|
|
{ //compute the reduced graph, and if need be performed a backward check
|
|
this->compute_connected_components();
|
|
if (this->parameter.backward_step)
|
|
{ //compute the structure of the reduced graph
|
|
this->compute_reduced_graph();
|
|
//check for beneficial merges
|
|
this->merge(false);
|
|
}
|
|
else
|
|
{ //compute only the value associated to each connected components
|
|
this->compute_reduced_value();
|
|
}
|
|
}
|
|
|
|
//=============================================================================================
|
|
//============================== compute_connected_components=========================================
|
|
//=============================================================================================
|
|
void compute_connected_components()
|
|
{ //this function compute the connected components of the graph with active edges removed
|
|
//the boolean vector indicating wether or not the edges and vertices have been seen already
|
|
//the root is the first vertex of a component
|
|
//this function is written such that the new components are appended at the end of components
|
|
//this allows not to recompute saturated component
|
|
VertexAttributeMap<T> vertex_attribute_map = boost::get(boost::vertex_bundle, this->main_graph);
|
|
VertexIndexMap<T> vertex_index_map = get(boost::vertex_index, this->main_graph);
|
|
//indicate which edges and nodes have been seen already by the dpsearch
|
|
std::vector<bool> edges_seen(this->nEdge, false);
|
|
std::vector<bool> vertices_seen(this->nVertex + 2, false);
|
|
vertices_seen[vertex_index_map(this->source)] = true;
|
|
vertices_seen[vertex_index_map(this->sink)] = true;
|
|
//-------- start with the known roots------------------------------------------------------
|
|
//#pragma omp parallel for if (this->parameter.parallel) schedule(dynamic)
|
|
for (uint32_t ind_com = 0; ind_com < this->root_vertex.size(); ind_com++)
|
|
{
|
|
VertexDescriptor<T> root = this->root_vertex[ind_com]; //the first vertex of the component
|
|
if (this->saturated_components[ind_com])
|
|
{ //this component is saturated, we don't need to recompute it
|
|
for (uint32_t ind_ver = 0; ind_ver < this->components[ind_com].size(); ++ind_ver)
|
|
{
|
|
vertices_seen[vertex_index_map(this->components[ind_com][ind_ver])] = true;
|
|
}
|
|
}
|
|
else
|
|
{ //compute the new content of this component
|
|
this->components.at(ind_com) = connected_comp_from_root(root, this->components.at(ind_com).size(), vertices_seen, edges_seen);
|
|
}
|
|
}
|
|
//----now look for components that did not already exists----
|
|
VertexIterator<T> ite_ver;
|
|
for (ite_ver = boost::vertices(this->main_graph).first;
|
|
ite_ver != this->lastIterator; ++ite_ver)
|
|
{
|
|
if (vertices_seen[vertex_index_map(*ite_ver)])
|
|
{
|
|
continue;
|
|
} //this vertex is not currently in a connected component
|
|
VertexDescriptor<T> root = *ite_ver; //we define it as the root of a new component
|
|
size_t current_component_size = this->components[vertex_attribute_map(root).in_component].size();
|
|
this->components.push_back(connected_comp_from_root(root, current_component_size, vertices_seen, edges_seen));
|
|
this->root_vertex.push_back(root);
|
|
this->saturated_components.push_back(false);
|
|
}
|
|
this->components.shrink_to_fit();
|
|
}
|
|
|
|
//=============================================================================================
|
|
//============================== CONNECTED_COMP_FROM_ROOT=========================================
|
|
//=============================================================================================
|
|
inline std::vector<VertexDescriptor<T>> connected_comp_from_root(const VertexDescriptor<T>& root
|
|
, const size_t& size_comp, std::vector<bool>& vertices_seen, std::vector<bool>& edges_seen)
|
|
{
|
|
//this function compute the connected component of the graph with active edges removed
|
|
// associated with the root ROOT by performing a depth search first
|
|
EdgeAttributeMap<T> edge_attribute_map = boost::get(boost::edge_bundle, this->main_graph);
|
|
VertexIndexMap<T> vertex_index_map = get(boost::vertex_index, this->main_graph);
|
|
EdgeIndexMap<T> edge_index_map = get(&EdgeAttribute<T>::index, this->main_graph);
|
|
std::vector<VertexDescriptor<T>> vertices_added; //the vertices in the current connected component
|
|
// vertices_added contains the vertices that have been added to the current coomponent
|
|
vertices_added.reserve(size_comp);
|
|
//heap_explore contains the vertices to be added to the current component
|
|
std::vector<VertexDescriptor<T>> vertices_to_add;
|
|
vertices_to_add.reserve(size_comp);
|
|
VertexDescriptor<T> vertex_current; //the node being consideed
|
|
EdgeDescriptor edge_current, edge_reverse; //the edge being considered
|
|
//fill the heap with the root node
|
|
vertices_to_add.push_back(root);
|
|
while (vertices_to_add.size() > 0)
|
|
{ //as long as there are vertices left to add
|
|
vertex_current = vertices_to_add.back(); //the current node is the last node to add
|
|
vertices_to_add.pop_back(); //remove the current node from the vertices to add
|
|
if (vertices_seen[vertex_index_map(vertex_current)])
|
|
{ //this vertex has already been treated
|
|
continue;
|
|
}
|
|
vertices_added.push_back(vertex_current); //we add the current node
|
|
vertices_seen[vertex_index_map(vertex_current)] = true; //and flag it as seen
|
|
//----we now explore the neighbors of current_node
|
|
typename boost::graph_traits<Graph<T>>::out_edge_iterator ite_edg, ite_edg_end;
|
|
for (boost::tie(ite_edg, ite_edg_end) = boost::out_edges(vertex_current, this->main_graph);
|
|
ite_edg != ite_edg_end; ++ite_edg)
|
|
{ //explore edges leaving current_node
|
|
edge_current = *ite_edg;
|
|
if (edge_attribute_map(*ite_edg).isActive || (edges_seen[edge_index_map(edge_current)]))
|
|
{ //edge is either active or treated, we skip it
|
|
continue;
|
|
}
|
|
//the target of this edge is a node to add
|
|
edge_reverse = edge_attribute_map(edge_current).edge_reverse;
|
|
edges_seen[edge_index_map(edge_current)] = true;
|
|
edges_seen[edge_index_map(edge_reverse)] = true;
|
|
vertices_to_add.push_back(boost::target(edge_current, this->main_graph));
|
|
}
|
|
}
|
|
vertices_added.shrink_to_fit();
|
|
return vertices_added;
|
|
}
|
|
|
|
//=============================================================================================
|
|
//================================ COMPUTE_REDUCE_GRAPH ====================================
|
|
//=============================================================================================
|
|
void compute_reduced_graph()
|
|
{ //compute the adjacency structure between components as well as weight and value of each component
|
|
//this is stored in the reduced graph structure
|
|
EdgeAttributeMap<T> edge_attribute_map = boost::get(boost::edge_bundle, this->main_graph);
|
|
VertexAttributeMap<T> vertex_attribute_map = boost::get(boost::vertex_bundle, this->main_graph);
|
|
this->reduced_graph = Graph<T>(this->components.size());
|
|
VertexAttributeMap<T> component_attribute_map = boost::get(boost::vertex_bundle, this->reduced_graph);
|
|
//----fill the value sof the reduced graph----
|
|
#ifdef OPENMP
|
|
#pragma omp parallel for schedule(dynamic)
|
|
#endif
|
|
for (uint32_t ind_com = 0; ind_com < this->components.size(); ind_com++)
|
|
{
|
|
std::pair<std::vector<T>, T> component_values_and_weight = this->compute_value(ind_com);
|
|
//----fill the value and weight field of the reduced graph-----------------------------
|
|
VertexDescriptor<T> reduced_vertex = boost::vertex(ind_com, this->reduced_graph);
|
|
component_attribute_map[reduced_vertex] = VertexAttribute<T>(this->dim);
|
|
component_attribute_map(reduced_vertex).weight
|
|
= component_values_and_weight.second;
|
|
for (uint32_t i_dim = 0; i_dim < this->dim; i_dim++)
|
|
{
|
|
component_attribute_map(reduced_vertex).value[i_dim] = component_values_and_weight.first[i_dim];
|
|
}
|
|
}
|
|
//------compute the edges of the reduced graph
|
|
EdgeAttributeMap<T> border_edge_attribute_map = boost::get(boost::edge_bundle, this->reduced_graph);
|
|
this->borders.clear();
|
|
EdgeDescriptor edge_current, border_edge_current;
|
|
uint32_t ind_border_edge = 0, comp1, comp2, component_source, component_target;
|
|
VertexDescriptor<T> source_component, target_component;
|
|
bool reducedEdgeExists;
|
|
typename boost::graph_traits<Graph<T>>::edge_iterator ite_edg, ite_edg_end;
|
|
for (boost::tie(ite_edg, ite_edg_end) = boost::edges(this->main_graph); ite_edg != ite_edg_end; ++ite_edg)
|
|
{
|
|
if (!edge_attribute_map(*ite_edg).realEdge)
|
|
{ //edges linking the source or edge node do not take part
|
|
continue;
|
|
}
|
|
edge_current = *ite_edg;
|
|
//compute the connected components of the source and target of current_edge
|
|
comp1 = vertex_attribute_map(boost::source(edge_current, this->main_graph)).in_component;
|
|
comp2 = vertex_attribute_map(boost::target(edge_current, this->main_graph)).in_component;
|
|
if (comp1 == comp2)
|
|
{ //this edge links two nodes in the same connected component
|
|
continue;
|
|
}
|
|
//by convention we note component_source the smallest index and
|
|
//component_target the largest
|
|
component_source = std::min(comp1, comp2);
|
|
component_target = std::max(comp1, comp2);
|
|
//retrieve the corresponding vertex in the reduced graph
|
|
source_component = boost::vertex(component_source, this->reduced_graph);
|
|
target_component = boost::vertex(component_target, this->reduced_graph);
|
|
//try to add the border-edge linking those components in the reduced graph
|
|
boost::tie(border_edge_current, reducedEdgeExists)
|
|
= boost::edge(source_component, target_component, this->reduced_graph);
|
|
if (!reducedEdgeExists)
|
|
{ //this border-edge did not already existed in the reduced graph
|
|
//border_edge_current = boost::add_edge(source_component, target_component, this->reduced_graph).first;
|
|
border_edge_current = boost::add_edge(source_component, target_component, this->reduced_graph).first;
|
|
border_edge_attribute_map(border_edge_current).index = ind_border_edge;
|
|
border_edge_attribute_map(border_edge_current).weight = 0;
|
|
ind_border_edge++;
|
|
//create a new entry for the borders list containing this border
|
|
this->borders.push_back(std::vector<EdgeDescriptor>(0));
|
|
}
|
|
//add the weight of the current edge to the weight of the border-edge
|
|
border_edge_attribute_map(border_edge_current).weight += 0.5*edge_attribute_map(edge_current).weight;
|
|
this->borders[border_edge_attribute_map(border_edge_current).index].push_back(edge_current);
|
|
}
|
|
}
|
|
|
|
//=============================================================================================
|
|
//================================ MERGE ====================================
|
|
//=============================================================================================
|
|
uint32_t merge(bool is_cutoff)
|
|
{
|
|
// TODO: right now we only do one loop through the heap of potential mergeing, and only
|
|
//authorize one mergeing per component. We could update the gain and merge until it is no longer
|
|
//beneficial
|
|
//check wether the energy can be decreased by removing edges from the reduced graph
|
|
//----load graph structure---
|
|
VertexAttributeMap<T> vertex_attribute_map = boost::get(boost::vertex_bundle, this->main_graph);
|
|
VertexAttributeMap<T> component_attribute_map = boost::get(boost::vertex_bundle, this->reduced_graph);
|
|
EdgeAttributeMap<T> border_edge_attribute_map = boost::get(boost::edge_bundle, this->reduced_graph);
|
|
EdgeAttributeMap<T> edge_attribute_map = boost::get(boost::edge_bundle, this->main_graph);
|
|
VertexIndexMap<T> component_index_map = boost::get(boost::vertex_index, this->reduced_graph);
|
|
//-----------------------------------
|
|
EdgeDescriptor border_edge_current;
|
|
typename boost::graph_traits<Graph<T>>::edge_iterator ite_border, ite_border_end;
|
|
typename std::vector<EdgeDescriptor>::iterator ite_border_edge;
|
|
VertexDescriptor<T> source_component, target_component;
|
|
uint32_t ind_source_component, ind_target_component, border_edge_currentIndex;
|
|
//gain_current is the vector of gains associated with each mergeing move
|
|
//std::vector<T> gain_current(boost::num_edges(this->reduced_graph));
|
|
//we store in merge_queue the potential mergeing with a priority on the potential gain
|
|
std::priority_queue<ComponentsFusion<T>, std::vector<ComponentsFusion<T>>, lessComponentsFusion<T>> merge_queue;
|
|
T gain; // the gain obtained by removing the border corresponding to the edge in the reduced graph
|
|
for (boost::tie(ite_border, ite_border_end) = boost::edges(this->reduced_graph); ite_border != ite_border_end; ++ite_border)
|
|
{
|
|
//a first pass go through all the edges in the reduced graph and compute the gain obtained by
|
|
//mergeing the corresponding vertices
|
|
border_edge_current = *ite_border;
|
|
border_edge_currentIndex = border_edge_attribute_map(border_edge_current).index;
|
|
//retrieve the two components corresponding to this border
|
|
source_component = boost::source(border_edge_current, this->reduced_graph);
|
|
target_component = boost::target(border_edge_current, this->reduced_graph);
|
|
if (is_cutoff && component_attribute_map(source_component).weight >= this->parameter.cutoff
|
|
&&component_attribute_map(target_component).weight >= this->parameter.cutoff)
|
|
{
|
|
continue;
|
|
}
|
|
ind_source_component = static_cast<uint32_t>(component_index_map(source_component));
|
|
ind_target_component = static_cast<uint32_t>(component_index_map(target_component));
|
|
//----now compute the gain of mergeing those two components-----
|
|
// compute the fidelity lost by mergeing the two connected components
|
|
std::pair<std::vector<T>, T> merge_gain = compute_merge_gain(source_component, target_component);
|
|
// the second part is due to the removing of the border
|
|
gain = merge_gain.second
|
|
+ border_edge_attribute_map(border_edge_current).weight * this->parameter.reg_strenth;
|
|
//mergeing_information store the indexes of the components as well as the edge index and the gain
|
|
//in a structure ordered by the gain
|
|
ComponentsFusion<T> mergeing_information(ind_source_component, ind_target_component, border_edge_currentIndex, gain);
|
|
mergeing_information.merged_value = merge_gain.first;
|
|
if (is_cutoff || gain > 0)
|
|
{ //it is beneficial to merge those two components
|
|
//we add them to the merge_queue
|
|
merge_queue.push(mergeing_information);
|
|
//gain_current.at(border_edge_currentIndex) = gain;
|
|
}
|
|
}
|
|
uint32_t n_merged = 0;
|
|
//----go through the priority queue of merges and perform them as long as it is beneficial---
|
|
//is_merged indicate which components no longer exists because they have been merged with a neighboring component
|
|
std::vector<bool> is_merged(this->components.size(), false);
|
|
//to_destroy indicates the components that are needed to be removed
|
|
std::vector<bool> to_destroy(this->components.size(), false);
|
|
while (merge_queue.size() > 0)
|
|
{ //loop through the potential mergeing and accept the ones that decrease the energy
|
|
ComponentsFusion<T> mergeing_information = merge_queue.top();
|
|
if (!is_cutoff && mergeing_information.merge_gain <= 0)
|
|
{ //no more mergeing provide a gain in energy
|
|
break;
|
|
}
|
|
merge_queue.pop();
|
|
if (is_merged.at(mergeing_information.comp1) || (is_merged.at(mergeing_information.comp2)))
|
|
{
|
|
//at least one of the components have already been merged
|
|
continue;
|
|
}
|
|
n_merged++;
|
|
//---proceed with the fusion of comp1 and comp2----
|
|
//add the vertices of comp2 to comp1
|
|
this->components[mergeing_information.comp1].insert(this->components[mergeing_information.comp1].end()
|
|
, components[mergeing_information.comp2].begin(), this->components[mergeing_information.comp2].end());
|
|
//if comp1 was saturated it might not be anymore
|
|
this->saturated_components[mergeing_information.comp1] = false;
|
|
//the new weight is the sum of both weights
|
|
component_attribute_map(mergeing_information.comp1).weight
|
|
+= component_attribute_map(mergeing_information.comp2).weight;
|
|
//the new value is already computed in mergeing_information
|
|
component_attribute_map(mergeing_information.comp1).value = mergeing_information.merged_value;
|
|
//we deactivate the border between comp1 and comp2
|
|
for (ite_border_edge = this->borders.at(mergeing_information.border_index).begin();
|
|
ite_border_edge != this->borders.at(mergeing_information.border_index).end(); ++ite_border_edge)
|
|
{
|
|
edge_attribute_map(*ite_border_edge).isActive = false;
|
|
}
|
|
is_merged.at(mergeing_information.comp1) = true;
|
|
is_merged.at(mergeing_information.comp2) = true;
|
|
to_destroy.at(mergeing_information.comp2) = true;
|
|
}
|
|
//we now rebuild the vectors components, rootComponents and saturated_components
|
|
std::vector<std::vector<VertexDescriptor<T>>> new_components;
|
|
std::vector<VertexDescriptor<T>> new_root_vertex;
|
|
std::vector<bool> new_saturated_components;
|
|
uint32_t ind_new_component = 0;
|
|
for (uint32_t ind_com = 0; ind_com < this->components.size(); ind_com++)
|
|
{
|
|
if (to_destroy.at(ind_com))
|
|
{ //this component has been removed
|
|
continue;
|
|
}//this components is kept
|
|
new_components.push_back(this->components.at(ind_com));
|
|
new_root_vertex.push_back(this->root_vertex.at(ind_com));
|
|
new_saturated_components.push_back(saturated_components.at(ind_com));
|
|
//if (is_merged.at(ind_com))
|
|
//{ //we need to update the value of the vertex in this component
|
|
for (uint32_t ind_ver = 0; ind_ver < this->components[ind_com].size(); ++ind_ver)
|
|
{
|
|
vertex_attribute_map(this->components[ind_com][ind_ver]).value
|
|
= component_attribute_map(boost::vertex(ind_com, this->reduced_graph)).value;
|
|
vertex_attribute_map(this->components[ind_com][ind_ver]).in_component
|
|
= ind_new_component;//ind_com;
|
|
}
|
|
//}
|
|
ind_new_component++;
|
|
}
|
|
this->components = new_components;
|
|
this->root_vertex = new_root_vertex;
|
|
this->saturated_components = new_saturated_components;
|
|
return n_merged;
|
|
}
|
|
|
|
//=============================================================================================
|
|
//================================ CUTOFF ====================================
|
|
//=============================================================================================
|
|
void cutoff()
|
|
{
|
|
int i = 0;
|
|
uint32_t n_merged;
|
|
while (true)
|
|
{
|
|
//this->compute_connected_components();
|
|
this->compute_reduced_graph();
|
|
n_merged = merge(true);
|
|
i++;
|
|
if (n_merged == 0 || i > 50)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
//===============================================================================================
|
|
//========================= saturateComponent ===================================================
|
|
//===============================================================================================
|
|
inline void saturateComponent(const uint32_t & ind_com)
|
|
{ //this component is uncuttable and needs to be removed from further graph-cuts
|
|
EdgeAttributeMap<T> edge_attribute_map = boost::get(boost::edge_bundle, this->main_graph);
|
|
this->saturated_components[ind_com] = true;
|
|
for (uint32_t i_ver = 0; i_ver < this->components[ind_com].size(); i_ver++)
|
|
{
|
|
VertexDescriptor<T> desc_v = this->components[ind_com][i_ver];
|
|
// because of the adjacency structure NEVER access edge (source,v) directly!
|
|
EdgeDescriptor edg_ver2source = boost::edge(desc_v, this->source, this->main_graph).first;
|
|
EdgeDescriptor edg_source2ver = edge_attribute_map(edg_ver2source).edge_reverse; //use edge_reverse instead
|
|
EdgeDescriptor edg_sink2ver = boost::edge(desc_v, this->sink, this->main_graph).first;
|
|
// we set the capacities of edges to source and sink to zero
|
|
edge_attribute_map(edg_source2ver).capacity = 0.;
|
|
edge_attribute_map(edg_sink2ver).capacity = 0.;
|
|
}
|
|
}
|
|
};
|
|
}
|