ACloudViewer  3.9.4
A Modern Library for 3D Data Processing
CutPursuit.h
Go to the documentation of this file.
1 // ----------------------------------------------------------------------------
2 // - CloudViewer: www.cloudViewer.org -
3 // ----------------------------------------------------------------------------
4 // Copyright (c) 2018-2024 www.cloudViewer.org
5 // SPDX-License-Identifier: MIT
6 // ----------------------------------------------------------------------------
7 
8 #pragma once
9 
10 // Local
11 #include "Graph.h"
12 
13 // System
14 #include <math.h>
15 
16 #include <fstream>
17 #include <iostream>
18 #include <queue>
19 
20 // Boost
21 #include <boost/graph/boykov_kolmogorov_max_flow.hpp>
22 
23 namespace CP {
24 template <typename T>
25 struct CPparameter {
26  T reg_strenth; // regularization strength, multiply the edge weight
27  uint32_t cutoff; // minimal component size
28  uint32_t flow_steps; // number of steps in the optimal binary cut
29  // computation
30  uint32_t kmeans_ite; // number of iteration in the kmeans sampling
31  uint32_t kmeans_resampling; // number of kmeans re-intilialization
32  uint32_t verbose; // verbosity
33  uint32_t max_ite_main; // max number of iterations in the main loop
34  bool backward_step; // indicates if a backward step should be performed
35  double stopping_ratio; // when (E(t-1) - E(t) / (E(0) - E(t)) is too small,
36  // the algorithm stops
37  fidelityType fidelity; // the fidelity function
38  double smoothing; // smoothing term (for Kl divergence only)
39  bool parallel; // enable/disable parrallelism
40  T weight_decay; // for continued optimization of the flow steps
41 };
42 
43 template <typename T>
44 struct CutPursuit {
45  Graph<T> main_graph; // the Graph structure containing the main structure
46  Graph<T> reduced_graph; // the reduced graph whose vertices are the
47  // connected component
48  std::vector<std::vector<VertexDescriptor<T>>>
49  components; // contains the list of the vertices in each component
50  std::vector<VertexDescriptor<T>>
51  root_vertex; // the root vertex for each connected components
52  std::vector<bool>
53  saturated_components; // is the component saturated (uncuttable)
54  std::vector<std::vector<EdgeDescriptor>>
55  borders; // the list of edges forming the borders between the
56  // connected components
57  VertexDescriptor<T> source; // source vertex for graph cut
58  VertexDescriptor<T> sink; // sink vertex
59  uint32_t dim; // dimension of the data
60  uint32_t nVertex; // number of data point
61  uint32_t nEdge; // number of edges between vertices (not counting the edge
62  // to source/sink)
63  CP::VertexIterator<T> lastIterator; // iterator pointing to the last vertex
64  // which is neither sink nor source
66 
67  CutPursuit(uint32_t nbVertex = 1) {
68  this->main_graph = Graph<T>(nbVertex);
69  this->reduced_graph = Graph<T>(1);
70  this->components = std::vector<std::vector<VertexDescriptor<T>>>(1);
71  this->root_vertex = std::vector<VertexDescriptor<T>>(1, 0);
72  this->saturated_components = std::vector<bool>(1, false);
73  this->source = VertexDescriptor<T>();
74  this->sink = VertexDescriptor<T>();
75  this->dim = 1;
76  this->nVertex = 1;
77  this->nEdge = 0;
78  this->parameter.reg_strenth = 0;
79  this->parameter.cutoff = 0;
80  this->parameter.flow_steps = 3;
81  this->parameter.kmeans_ite = 5;
82  this->parameter.kmeans_resampling = 3;
83  this->parameter.verbose = 2;
84  this->parameter.max_ite_main = 6;
85  this->parameter.backward_step = true;
86  this->parameter.stopping_ratio = 0.0001;
87  this->parameter.fidelity = L2;
88  this->parameter.smoothing = 0.1;
89  this->parameter.parallel = true;
90  this->parameter.weight_decay = static_cast<T>(0.7);
91  }
92 
93  //=============================================================================================
94  std::pair<std::vector<T>, std::vector<T>> run() {
95  // first initilialize the structure
96  this->initialize();
97  if (this->parameter.verbose > 0) {
98  std::cout << "Graph " << boost::num_vertices(this->main_graph)
99  << " vertices and " << boost::num_edges(this->main_graph)
100  << " edges and observation of dimension " << this->dim
101  << '\n';
102  }
103  T energy_zero = this->compute_energy().first; // energy with 1
104  // component
105  T old_energy = energy_zero; // energy at the previous iteration
106  // vector with time and energy, useful for benchmarking
107  std::vector<T> energy_out(this->parameter.max_ite_main),
108  time_out(this->parameter.max_ite_main);
109  TimeStack ts;
110  ts.tic();
111  // the main loop
112  for (uint32_t ite_main = 1; ite_main <= this->parameter.max_ite_main;
113  ite_main++) {
114  //--------those two lines are the whole
115  // iteration-------------------------
116  size_t saturation =
117  this->split(); // compute optimal binary partition
118  this->reduce(); // compute the new reduced graph
119  //-------end of the iteration - rest is stopping check and
120  // display------
121  std::pair<T, T> energy = this->compute_energy();
122  energy_out.push_back((energy.first + energy.second));
123  time_out.push_back(ts.tocDouble());
124  if (this->parameter.verbose > 1) {
125  printf("Iteration %3i - %4i components - ", ite_main,
126  static_cast<int>(this->components.size()));
127  printf("Saturation %5.1f %% - ",
128  (100.0 * saturation) / this->nVertex);
129  switch (this->parameter.fidelity) {
130  case L2: {
131  printf("Quadratic Energy %4.3f %% - ",
132  100 * (energy.first + energy.second) /
133  energy_zero);
134  break;
135  }
136  case linear: {
137  printf("Linear Energy %10.1f - ",
138  energy.first + energy.second);
139  break;
140  }
141  case KL: {
142  printf("KL Energy %4.3f %% - ",
143  100 * (energy.first + energy.second) /
144  energy_zero);
145  break;
146  }
147  case SPG: {
148  printf("Quadratic Energy %4.3f %% - ",
149  100 * (energy.first + energy.second) /
150  energy_zero);
151  break;
152  }
153  }
154  std::cout << "Timer " << ts.toc() << std::endl;
155  }
156  //----stopping checks-----
157  if (saturation == this->nVertex) { // all components are saturated
158  if (this->parameter.verbose > 1) {
159  std::cout << "All components are saturated" << std::endl;
160  }
161  break;
162  }
163  if ((old_energy - energy.first - energy.second) / (old_energy) <
164  this->parameter.stopping_ratio) { // relative energy progress
165  // stopping criterion
166  if (this->parameter.verbose > 1) {
167  std::cout << "Stopping criterion reached" << std::endl;
168  }
169  break;
170  }
171  if (ite_main >=
172  this->parameter.max_ite_main) { // max number of iteration
173  if (this->parameter.verbose > 1) {
174  std::cout << "Max number of iteration reached" << std::endl;
175  }
176  break;
177  }
178  old_energy = energy.first + energy.second;
179  }
180  if (this->parameter.cutoff > 0) {
181  this->cutoff();
182  }
183  return std::pair<std::vector<T>, std::vector<T>>(energy_out, time_out);
184  }
185 
186  //=============================================================================================
187  //=========== VIRTUAL METHODS DEPENDING ON THE CHOICE OF FIDELITY FUNCTION
188  //=====================
189  //=============================================================================================
190  //
191  //=============================================================================================
192  //============================= SPLIT
193  //===========================================
194  //=============================================================================================
195  virtual size_t split() {
196  // compute the optimal binary partition
197  return 0;
198  }
199 
200  //=============================================================================================
201  //================================ compute_energy_L2
202  //====================================
203  //=============================================================================================
204  virtual std::pair<T, T> compute_energy() {
205  // compute the current energy
206  return std::pair<T, T>(0, 0);
207  }
208 
209  //=============================================================================================
210  //================================= COMPUTE_VALUE
211  //=========================================
212  //=============================================================================================
213  virtual std::pair<std::vector<T>, T> compute_value(
214  const uint32_t& ind_com) {
215  // compute the optimal the values associated with the current partition
216  return std::pair<std::vector<T>, T>(std::vector<T>(0), 0);
217  }
218 
219  //=============================================================================================
220  //================================= COMPUTE_MERGE_GAIN
221  //=========================================
222  //=============================================================================================
223  virtual std::pair<std::vector<T>, T> compute_merge_gain(
224  const VertexDescriptor<T>& comp1,
225  const VertexDescriptor<T>& comp2) {
226  // compute the gain of mergeing two connected components
227  return std::pair<std::vector<T>, T>(std::vector<T>(0), 0);
228  }
229 
230  //=============================================================================================
231  //========================== END OF VIRTUAL METHODS
232  //===========================================
233  //=============================================================================================
234 
235  //=============================================================================================
236  //============================= INITIALIZE
237  //===========================================
238  //=============================================================================================
239  void initialize() {
240  // build the reduced graph with one component, fill the first vector of
241  // components and add the sink and source nodes
242  VertexIterator<T> ite_ver, ite_ver_end;
243  EdgeAttributeMap<T> edge_attribute_map =
244  boost::get(boost::edge_bundle, this->main_graph);
245  this->components[0] =
246  std::vector<VertexDescriptor<T>>(0); //(this->nVertex);
247  this->root_vertex[0] = *boost::vertices(this->main_graph).first;
248  this->nVertex =
249  static_cast<uint32_t>(boost::num_vertices(this->main_graph));
250  this->nEdge = static_cast<uint32_t>(boost::num_edges(this->main_graph));
251  //--------compute the first reduced
252  // graph----------------------------------------------------------
253  for (boost::tie(ite_ver, ite_ver_end) =
254  boost::vertices(this->main_graph);
255  ite_ver != ite_ver_end; ++ite_ver) {
256  this->components[0].push_back(*ite_ver);
257  }
258  this->lastIterator = ite_ver;
259  this->compute_value(0);
260  //--------build the link to source and
261  // sink--------------------------------------------------------
262  this->source = boost::add_vertex(this->main_graph);
263  this->sink = boost::add_vertex(this->main_graph);
264  uint32_t eIndex =
265  static_cast<uint32_t>(boost::num_edges(this->main_graph));
266  ite_ver = boost::vertices(this->main_graph).first;
267  for (uint32_t ind_ver = 0; ind_ver < this->nVertex; ind_ver++) {
268  // note that source and edge will have many nieghbors, and hence
269  // boost::edge should never be called to get the in_edge. use the
270  // out_edge and then reverse_Edge
271  addDoubledge<T>(this->main_graph, this->source,
272  boost::vertex(ind_ver, this->main_graph), 0.,
273  eIndex, edge_attribute_map, false);
274  eIndex += 2;
275  addDoubledge<T>(this->main_graph,
276  boost::vertex(ind_ver, this->main_graph),
277  this->sink, 0., eIndex, edge_attribute_map, false);
278  eIndex += 2;
279  ++ite_ver;
280  }
281  }
282 
283  //=============================================================================================
284  //================================ COMPUTE_REDUCE_VALUE
285  //====================================
286  //=============================================================================================
288  for (uint32_t ind_com = 0; ind_com < this->components.size();
289  ++ind_com) { // compute the reduced value of each component
290  compute_value(ind_com);
291  }
292  }
293 
294  //=============================================================================================
295  //============================= ACTIVATE_EDGES
296  //==========================================
297  //=============================================================================================
298  size_t activate_edges(bool allows_saturation =
299  true) { // this function analyzes the optimal
300  // binary partition to detect:
301  //- saturated components (i.e. uncuttable)
302  //- new activated edges
303  VertexAttributeMap<T> vertex_attribute_map =
304  boost::get(boost::vertex_bundle, this->main_graph);
305  EdgeAttributeMap<T> edge_attribute_map =
306  boost::get(boost::edge_bundle, this->main_graph);
307  // saturation is the proportion of nodes in saturated components
308  size_t saturation = 0;
309  uint32_t nb_comp = static_cast<uint32_t>(this->components.size());
310  //---- first check if the component are
311  // saturated------------------------- #pragma omp parallel for if
312  //(this->parameter.parallel) schedule(dynamic)
313  for (uint32_t ind_com = 0; ind_com < nb_comp; ind_com++) {
314  if (this->saturated_components
315  [ind_com]) { // ind_com is saturated, we increement
316  // saturation by ind_com size
317  saturation += this->components[ind_com].size();
318  continue;
319  }
320  std::vector<T> totalWeight(2, 0);
321  for (uint32_t ind_ver = 0;
322  ind_ver < this->components[ind_com].size(); ind_ver++) {
323  bool isSink = (vertex_attribute_map(
324  this->components[ind_com][ind_ver])
325  .color ==
326  vertex_attribute_map(this->sink).color);
327  if (isSink) {
328  totalWeight[0] +=
329  vertex_attribute_map(
330  this->components[ind_com][ind_ver])
331  .weight;
332  } else {
333  totalWeight[1] +=
334  vertex_attribute_map(
335  this->components[ind_com][ind_ver])
336  .weight;
337  }
338  }
339  if (allows_saturation &&
340  ((totalWeight[0] == 0) || (totalWeight[1] == 0))) {
341  // the component is saturated
342  this->saturateComponent(ind_com);
343  saturation += this->components[ind_com].size();
344  }
345  }
346  //----check which edges have been activated----
347  EdgeIterator<T> ite_edg, ite_edg_end;
348  uint32_t color_v1, color_v2, color_combination;
349  for (boost::tie(ite_edg, ite_edg_end) = boost::edges(this->main_graph);
350  ite_edg != ite_edg_end; ++ite_edg) {
351  if (!edge_attribute_map(*ite_edg).realEdge) {
352  continue;
353  }
354  color_v1 = vertex_attribute_map(
355  boost::source(*ite_edg, this->main_graph))
356  .color;
357  color_v2 = vertex_attribute_map(
358  boost::target(*ite_edg, this->main_graph))
359  .color;
360  // color_source = 0, color_sink = 4, uncolored = 1
361  // we want an edge when a an interface source/sink
362  // this corresponds to a sum of 4
363  // for the case of uncolored nodes we arbitrarily chose
364  // source-uncolored
365  color_combination = color_v1 + color_v2;
366  if ((color_combination == 0) || (color_combination == 2) ||
367  (color_combination == 2) ||
368  (color_combination ==
369  8)) { // edge between two vertices of the same color
370  continue;
371  }
372  // the edge is active!
373  edge_attribute_map(*ite_edg).isActive = true;
374  edge_attribute_map(*ite_edg).capacity = 0;
375  vertex_attribute_map(boost::source(*ite_edg, this->main_graph))
376  .isBorder = true;
377  vertex_attribute_map(boost::target(*ite_edg, this->main_graph))
378  .isBorder = true;
379  }
380  return saturation;
381  }
382 
383  //=============================================================================================
384  //============================= REDUCE
385  //===========================================
386  //=============================================================================================
387  void reduce() { // compute the reduced graph, and if need be performed a
388  // backward check
390  if (this->parameter.backward_step) { // compute the structure of the
391  // reduced graph
392  this->compute_reduced_graph();
393  // check for beneficial merges
394  this->merge(false);
395  } else { // compute only the value associated to each connected
396  // components
397  this->compute_reduced_value();
398  }
399  }
400 
401  //=============================================================================================
402  //==============================
403  // compute_connected_components=========================================
404  //=============================================================================================
405  void compute_connected_components() { // this function compute the
406  // connected components of the graph
407  // with active edges removed
408  // the boolean vector indicating wether or not the edges and vertices
409  // have been seen already the root is the first vertex of a component
410  // this function is written such that the new components are appended at
411  // the end of components this allows not to recompute saturated
412  // component
413  VertexAttributeMap<T> vertex_attribute_map =
414  boost::get(boost::vertex_bundle, this->main_graph);
415  VertexIndexMap<T> vertex_index_map =
416  get(boost::vertex_index, this->main_graph);
417  // indicate which edges and nodes have been seen already by the dpsearch
418  std::vector<bool> edges_seen(this->nEdge, false);
419  std::vector<bool> vertices_seen(this->nVertex + 2, false);
420  vertices_seen[vertex_index_map(this->source)] = true;
421  vertices_seen[vertex_index_map(this->sink)] = true;
422  //-------- start with the known
423  // roots------------------------------------------------------ #pragma
424  // omp parallel for if (this->parameter.parallel) schedule(dynamic)
425  for (uint32_t ind_com = 0; ind_com < this->root_vertex.size();
426  ind_com++) {
427  VertexDescriptor<T> root =
428  this->root_vertex[ind_com]; // the first vertex of the
429  // component
430  if (this->saturated_components[ind_com]) { // this component is
431  // saturated, we don't
432  // need to recompute it
433  for (uint32_t ind_ver = 0;
434  ind_ver < this->components[ind_com].size(); ++ind_ver) {
435  vertices_seen[vertex_index_map(
436  this->components[ind_com][ind_ver])] = true;
437  }
438  } else { // compute the new content of this component
439  this->components.at(ind_com) = connected_comp_from_root(
440  root, this->components.at(ind_com).size(),
441  vertices_seen, edges_seen);
442  }
443  }
444  //----now look for components that did not already exists----
445  VertexIterator<T> ite_ver;
446  for (ite_ver = boost::vertices(this->main_graph).first;
447  ite_ver != this->lastIterator; ++ite_ver) {
448  if (vertices_seen[vertex_index_map(*ite_ver)]) {
449  continue;
450  } // this vertex is not currently in a connected component
451  VertexDescriptor<T> root =
452  *ite_ver; // we define it as the root of a new component
453  size_t current_component_size =
454  this->components[vertex_attribute_map(root).in_component]
455  .size();
456  this->components.push_back(connected_comp_from_root(
457  root, current_component_size, vertices_seen, edges_seen));
458  this->root_vertex.push_back(root);
459  this->saturated_components.push_back(false);
460  }
461  this->components.shrink_to_fit();
462  }
463 
464  //=============================================================================================
465  //==============================
466  // CONNECTED_COMP_FROM_ROOT=========================================
467  //=============================================================================================
468  inline std::vector<VertexDescriptor<T>> connected_comp_from_root(
469  const VertexDescriptor<T>& root,
470  const size_t& size_comp,
471  std::vector<bool>& vertices_seen,
472  std::vector<bool>& edges_seen) {
473  // this function compute the connected component of the graph with
474  // active edges removed
475  // associated with the root ROOT by performing a depth search first
476  EdgeAttributeMap<T> edge_attribute_map =
477  boost::get(boost::edge_bundle, this->main_graph);
478  VertexIndexMap<T> vertex_index_map =
479  get(boost::vertex_index, this->main_graph);
480  EdgeIndexMap<T> edge_index_map =
481  get(&EdgeAttribute<T>::index, this->main_graph);
482  std::vector<VertexDescriptor<T>>
483  vertices_added; // the vertices in the current connected
484  // component
485  // vertices_added contains the vertices that have been added to the
486  // current coomponent
487  vertices_added.reserve(size_comp);
488  // heap_explore contains the vertices to be added to the current
489  // component
490  std::vector<VertexDescriptor<T>> vertices_to_add;
491  vertices_to_add.reserve(size_comp);
492  VertexDescriptor<T> vertex_current; // the node being consideed
493  EdgeDescriptor edge_current, edge_reverse; // the edge being considered
494  // fill the heap with the root node
495  vertices_to_add.push_back(root);
496  while (vertices_to_add.size() >
497  0) { // as long as there are vertices left to add
498  vertex_current = vertices_to_add.back(); // the current node is the
499  // last node to add
500  vertices_to_add.pop_back(); // remove the current node from the
501  // vertices to add
502  if (vertices_seen[vertex_index_map(
503  vertex_current)]) { // this vertex has already been
504  // treated
505  continue;
506  }
507  vertices_added.push_back(vertex_current); // we add the current
508  // node
509  vertices_seen[vertex_index_map(vertex_current)] =
510  true; // and flag it as seen
511  //----we now explore the neighbors of current_node
512  typename boost::graph_traits<Graph<T>>::out_edge_iterator ite_edg,
513  ite_edg_end;
514  for (boost::tie(ite_edg, ite_edg_end) =
515  boost::out_edges(vertex_current, this->main_graph);
516  ite_edg != ite_edg_end;
517  ++ite_edg) { // explore edges leaving current_node
518  edge_current = *ite_edg;
519  if (edge_attribute_map(*ite_edg).isActive ||
520  (edges_seen[edge_index_map(
521  edge_current)])) { // edge is either active or
522  // treated, we skip it
523  continue;
524  }
525  // the target of this edge is a node to add
526  edge_reverse = edge_attribute_map(edge_current).edge_reverse;
527  edges_seen[edge_index_map(edge_current)] = true;
528  edges_seen[edge_index_map(edge_reverse)] = true;
529  vertices_to_add.push_back(
530  boost::target(edge_current, this->main_graph));
531  }
532  }
533  vertices_added.shrink_to_fit();
534  return vertices_added;
535  }
536 
537  //=============================================================================================
538  //================================ COMPUTE_REDUCE_GRAPH
539  //====================================
540  //=============================================================================================
541  void compute_reduced_graph() { // compute the adjacency structure between
542  // components as well as weight and value of
543  // each component
544  // this is stored in the reduced graph structure
545  EdgeAttributeMap<T> edge_attribute_map =
546  boost::get(boost::edge_bundle, this->main_graph);
547  VertexAttributeMap<T> vertex_attribute_map =
548  boost::get(boost::vertex_bundle, this->main_graph);
549  this->reduced_graph = Graph<T>(this->components.size());
550  VertexAttributeMap<T> component_attribute_map =
551  boost::get(boost::vertex_bundle, this->reduced_graph);
552  //----fill the value sof the reduced graph----
553 #ifdef OPENMP
554 #pragma omp parallel for schedule(dynamic)
555 #endif
556  for (uint32_t ind_com = 0; ind_com < this->components.size();
557  ind_com++) {
558  std::pair<std::vector<T>, T> component_values_and_weight =
559  this->compute_value(ind_com);
560  //----fill the value and weight field of the reduced
561  // graph-----------------------------
562  VertexDescriptor<T> reduced_vertex =
563  boost::vertex(ind_com, this->reduced_graph);
564  component_attribute_map[reduced_vertex] =
565  VertexAttribute<T>(this->dim);
566  component_attribute_map(reduced_vertex).weight =
567  component_values_and_weight.second;
568  for (uint32_t i_dim = 0; i_dim < this->dim; i_dim++) {
569  component_attribute_map(reduced_vertex).value[i_dim] =
570  component_values_and_weight.first[i_dim];
571  }
572  }
573  //------compute the edges of the reduced graph
574  EdgeAttributeMap<T> border_edge_attribute_map =
575  boost::get(boost::edge_bundle, this->reduced_graph);
576  this->borders.clear();
577  EdgeDescriptor edge_current, border_edge_current;
578  uint32_t ind_border_edge = 0, comp1, comp2, component_source,
579  component_target;
580  VertexDescriptor<T> source_component, target_component;
581  bool reducedEdgeExists;
582  typename boost::graph_traits<Graph<T>>::edge_iterator ite_edg,
583  ite_edg_end;
584  for (boost::tie(ite_edg, ite_edg_end) = boost::edges(this->main_graph);
585  ite_edg != ite_edg_end; ++ite_edg) {
586  if (!edge_attribute_map(*ite_edg)
587  .realEdge) { // edges linking the source or edge node
588  // do not take part
589  continue;
590  }
591  edge_current = *ite_edg;
592  // compute the connected components of the source and target of
593  // current_edge
594  comp1 = vertex_attribute_map(
595  boost::source(edge_current, this->main_graph))
596  .in_component;
597  comp2 = vertex_attribute_map(
598  boost::target(edge_current, this->main_graph))
599  .in_component;
600  if (comp1 == comp2) { // this edge links two nodes in the same
601  // connected component
602  continue;
603  }
604  // by convention we note component_source the smallest index and
605  // component_target the largest
606  component_source = std::min(comp1, comp2);
607  component_target = std::max(comp1, comp2);
608  // retrieve the corresponding vertex in the reduced graph
609  source_component =
610  boost::vertex(component_source, this->reduced_graph);
611  target_component =
612  boost::vertex(component_target, this->reduced_graph);
613  // try to add the border-edge linking those components in the
614  // reduced graph
615  boost::tie(border_edge_current, reducedEdgeExists) = boost::edge(
616  source_component, target_component, this->reduced_graph);
617  if (!reducedEdgeExists) { // this border-edge did not already
618  // existed in the reduced graph
619  // border_edge_current = boost::add_edge(source_component,
620  // target_component, this->reduced_graph).first;
621  border_edge_current =
622  boost::add_edge(source_component, target_component,
623  this->reduced_graph)
624  .first;
625  border_edge_attribute_map(border_edge_current).index =
626  ind_border_edge;
627  border_edge_attribute_map(border_edge_current).weight = 0;
628  ind_border_edge++;
629  // create a new entry for the borders list containing this
630  // border
631  this->borders.push_back(std::vector<EdgeDescriptor>(0));
632  }
633  // add the weight of the current edge to the weight of the
634  // border-edge
635  border_edge_attribute_map(border_edge_current).weight +=
636  0.5 * edge_attribute_map(edge_current).weight;
637  this->borders[border_edge_attribute_map(border_edge_current).index]
638  .push_back(edge_current);
639  }
640  }
641 
642  //=============================================================================================
643  //================================ MERGE
644  //====================================
645  //=============================================================================================
646  uint32_t merge(bool is_cutoff) {
647  // TODO: right now we only do one loop through the heap of potential
648  // mergeing, and only
649  // authorize one mergeing per component. We could update the gain and
650  // merge until it is no longer beneficial check wether the energy can be
651  // decreased by removing edges from the reduced graph
652  //----load graph structure---
653  VertexAttributeMap<T> vertex_attribute_map =
654  boost::get(boost::vertex_bundle, this->main_graph);
655  VertexAttributeMap<T> component_attribute_map =
656  boost::get(boost::vertex_bundle, this->reduced_graph);
657  EdgeAttributeMap<T> border_edge_attribute_map =
658  boost::get(boost::edge_bundle, this->reduced_graph);
659  EdgeAttributeMap<T> edge_attribute_map =
660  boost::get(boost::edge_bundle, this->main_graph);
661  VertexIndexMap<T> component_index_map =
662  boost::get(boost::vertex_index, this->reduced_graph);
663  //-----------------------------------
664  EdgeDescriptor border_edge_current;
665  typename boost::graph_traits<Graph<T>>::edge_iterator ite_border,
666  ite_border_end;
667  typename std::vector<EdgeDescriptor>::iterator ite_border_edge;
668  VertexDescriptor<T> source_component, target_component;
669  uint32_t ind_source_component, ind_target_component,
670  border_edge_currentIndex;
671  // gain_current is the vector of gains associated with each mergeing
672  // move std::vector<T>
673  // gain_current(boost::num_edges(this->reduced_graph)); we store in
674  // merge_queue the potential mergeing with a priority on the potential
675  // gain
676  std::priority_queue<ComponentsFusion<T>,
677  std::vector<ComponentsFusion<T>>,
679  merge_queue;
680  T gain; // the gain obtained by removing the border corresponding to
681  // the edge in the reduced graph
682  for (boost::tie(ite_border, ite_border_end) =
683  boost::edges(this->reduced_graph);
684  ite_border != ite_border_end; ++ite_border) {
685  // a first pass go through all the edges in the reduced graph and
686  // compute the gain obtained by mergeing the corresponding vertices
687  border_edge_current = *ite_border;
688  border_edge_currentIndex =
689  border_edge_attribute_map(border_edge_current).index;
690  // retrieve the two components corresponding to this border
691  source_component =
692  boost::source(border_edge_current, this->reduced_graph);
693  target_component =
694  boost::target(border_edge_current, this->reduced_graph);
695  if (is_cutoff &&
696  component_attribute_map(source_component).weight >=
697  this->parameter.cutoff &&
698  component_attribute_map(target_component).weight >=
699  this->parameter.cutoff) {
700  continue;
701  }
702  ind_source_component = static_cast<uint32_t>(
703  component_index_map(source_component));
704  ind_target_component = static_cast<uint32_t>(
705  component_index_map(target_component));
706  //----now compute the gain of mergeing those two components-----
707  // compute the fidelity lost by mergeing the two connected
708  // components
709  std::pair<std::vector<T>, T> merge_gain =
710  compute_merge_gain(source_component, target_component);
711  // the second part is due to the removing of the border
712  gain = merge_gain.second +
713  border_edge_attribute_map(border_edge_current).weight *
714  this->parameter.reg_strenth;
715  // mergeing_information store the indexes of the components as well
716  // as the edge index and the gain in a structure ordered by the gain
717  ComponentsFusion<T> mergeing_information(
718  ind_source_component, ind_target_component,
719  border_edge_currentIndex, gain);
720  mergeing_information.merged_value = merge_gain.first;
721  if (is_cutoff ||
722  gain > 0) { // it is beneficial to merge those two components
723  // we add them to the merge_queue
724  merge_queue.push(mergeing_information);
725  // gain_current.at(border_edge_currentIndex) = gain;
726  }
727  }
728  uint32_t n_merged = 0;
729  //----go through the priority queue of merges and perform them as long
730  // as it is beneficial--- is_merged indicate which components no longer
731  // exists because they have been merged with a neighboring component
732  std::vector<bool> is_merged(this->components.size(), false);
733  // to_destroy indicates the components that are needed to be removed
734  std::vector<bool> to_destroy(this->components.size(), false);
735  while (merge_queue.size() >
736  0) { // loop through the potential mergeing and accept the ones
737  // that decrease the energy
738  ComponentsFusion<T> mergeing_information = merge_queue.top();
739  if (!is_cutoff &&
740  mergeing_information.merge_gain <=
741  0) { // no more mergeing provide a gain in energy
742  break;
743  }
744  merge_queue.pop();
745  if (is_merged.at(mergeing_information.comp1) ||
746  (is_merged.at(mergeing_information.comp2))) {
747  // at least one of the components have already been merged
748  continue;
749  }
750  n_merged++;
751  //---proceed with the fusion of comp1 and comp2----
752  // add the vertices of comp2 to comp1
753  this->components[mergeing_information.comp1].insert(
754  this->components[mergeing_information.comp1].end(),
755  components[mergeing_information.comp2].begin(),
756  this->components[mergeing_information.comp2].end());
757  // if comp1 was saturated it might not be anymore
758  this->saturated_components[mergeing_information.comp1] = false;
759  // the new weight is the sum of both weights
760  component_attribute_map(mergeing_information.comp1).weight +=
761  component_attribute_map(mergeing_information.comp2).weight;
762  // the new value is already computed in mergeing_information
763  component_attribute_map(mergeing_information.comp1).value =
764  mergeing_information.merged_value;
765  // we deactivate the border between comp1 and comp2
766  for (ite_border_edge =
767  this->borders.at(mergeing_information.border_index)
768  .begin();
769  ite_border_edge !=
770  this->borders.at(mergeing_information.border_index).end();
771  ++ite_border_edge) {
772  edge_attribute_map(*ite_border_edge).isActive = false;
773  }
774  is_merged.at(mergeing_information.comp1) = true;
775  is_merged.at(mergeing_information.comp2) = true;
776  to_destroy.at(mergeing_information.comp2) = true;
777  }
778  // we now rebuild the vectors components, rootComponents and
779  // saturated_components
780  std::vector<std::vector<VertexDescriptor<T>>> new_components;
781  std::vector<VertexDescriptor<T>> new_root_vertex;
782  std::vector<bool> new_saturated_components;
783  uint32_t ind_new_component = 0;
784  for (uint32_t ind_com = 0; ind_com < this->components.size();
785  ind_com++) {
786  if (to_destroy.at(ind_com)) { // this component has been removed
787  continue;
788  } // this components is kept
789  new_components.push_back(this->components.at(ind_com));
790  new_root_vertex.push_back(this->root_vertex.at(ind_com));
791  new_saturated_components.push_back(
792  saturated_components.at(ind_com));
793  // if (is_merged.at(ind_com))
794  //{ //we need to update the value of the vertex in this component
795  for (uint32_t ind_ver = 0;
796  ind_ver < this->components[ind_com].size(); ++ind_ver) {
797  vertex_attribute_map(this->components[ind_com][ind_ver]).value =
798  component_attribute_map(
799  boost::vertex(ind_com, this->reduced_graph))
800  .value;
801  vertex_attribute_map(this->components[ind_com][ind_ver])
802  .in_component = ind_new_component; // ind_com;
803  }
804  //}
805  ind_new_component++;
806  }
807  this->components = new_components;
808  this->root_vertex = new_root_vertex;
809  this->saturated_components = new_saturated_components;
810  return n_merged;
811  }
812 
813  //=============================================================================================
814  //================================ CUTOFF
815  //====================================
816  //=============================================================================================
817  void cutoff() {
818  int i = 0;
819  uint32_t n_merged;
820  while (true) {
821  // this->compute_connected_components();
822  this->compute_reduced_graph();
823  n_merged = merge(true);
824  i++;
825  if (n_merged == 0 || i > 50) {
826  break;
827  }
828  }
829  }
830 
831  //===============================================================================================
832  //========================= saturateComponent
833  //===================================================
834  //===============================================================================================
835  inline void saturateComponent(
836  const uint32_t&
837  ind_com) { // this component is uncuttable and needs to be
838  // removed from further graph-cuts
839  EdgeAttributeMap<T> edge_attribute_map =
840  boost::get(boost::edge_bundle, this->main_graph);
841  this->saturated_components[ind_com] = true;
842  for (uint32_t i_ver = 0; i_ver < this->components[ind_com].size();
843  i_ver++) {
844  VertexDescriptor<T> desc_v = this->components[ind_com][i_ver];
845  // because of the adjacency structure NEVER access edge (source,v)
846  // directly!
847  EdgeDescriptor edg_ver2source =
848  boost::edge(desc_v, this->source, this->main_graph).first;
849  EdgeDescriptor edg_source2ver =
850  edge_attribute_map(edg_ver2source)
851  .edge_reverse; // use edge_reverse instead
852  EdgeDescriptor edg_sink2ver =
853  boost::edge(desc_v, this->sink, this->main_graph).first;
854  // we set the capacities of edges to source and sink to zero
855  edge_attribute_map(edg_source2ver).capacity = 0.;
856  edge_attribute_map(edg_sink2ver).capacity = 0.;
857  }
858  }
859 };
860 } // namespace CP
long vertex_index
fidelityType
Definition: Common.h:27
@ L2
Definition: Common.h:27
@ linear
Definition: Common.h:27
@ KL
Definition: Common.h:27
@ SPG
Definition: Common.h:27
std::string toc() const
Definition: Common.h:59
double tocDouble() const
Definition: Common.h:65
void tic()
Definition: Common.h:57
int min(int a, int b)
Definition: cutil_math.h:53
int max(int a, int b)
Definition: cutil_math.h:48
Definition: API.h:123
boost::vec_adj_list_vertex_property_map< Graph< T >, Graph< T > *, VertexAttribute< T >, VertexAttribute< T > &, boost::vertex_bundle_t > VertexAttributeMap
Definition: Graph.h:92
typename boost::graph_traits< Graph< T > >::edge_iterator EdgeIterator
Definition: Graph.h:84
typename boost::graph_traits< Graph< T > >::vertex_iterator VertexIterator
Definition: Graph.h:82
boost::graph_traits< boost::adjacency_list< boost::vecS, boost::vecS, boost::directedS > >::edge_descriptor EdgeDescriptor
Definition: Graph.h:22
typename boost::property_map< Graph< T >, uint32_t EdgeAttribute< T >::* >::type EdgeIndexMap
Definition: Graph.h:107
typename boost::graph_traits< CP::Graph< T > >::vertex_descriptor VertexDescriptor
Definition: Graph.h:75
typename boost::adjacency_list< boost::vecS, boost::vecS, boost::directedS, VertexAttribute< T >, EdgeAttribute< T > > Graph
Definition: Graph.h:71
typename boost::property_map< Graph< T >, boost::vertex_index_t >::type VertexIndexMap
Definition: Graph.h:103
boost::adj_list_edge_property_map< boost::directed_tag, EdgeAttribute< T >, EdgeAttribute< T > &, uint64_t, CP::EdgeAttribute< T >, boost::edge_bundle_t > EdgeAttributeMap
Definition: Graph.h:100
QTextStream & endl(QTextStream &stream)
Definition: QtCompat.h:718
double stopping_ratio
Definition: CutPursuit.h:35
uint32_t max_ite_main
Definition: CutPursuit.h:33
uint32_t kmeans_ite
Definition: CutPursuit.h:30
uint32_t flow_steps
Definition: CutPursuit.h:28
double smoothing
Definition: CutPursuit.h:38
uint32_t cutoff
Definition: CutPursuit.h:27
bool backward_step
Definition: CutPursuit.h:34
uint32_t kmeans_resampling
Definition: CutPursuit.h:31
uint32_t verbose
Definition: CutPursuit.h:32
fidelityType fidelity
Definition: CutPursuit.h:37
void initialize()
Definition: CutPursuit.h:239
void compute_connected_components()
Definition: CutPursuit.h:405
CPparameter< T > parameter
Definition: CutPursuit.h:65
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)
Definition: CutPursuit.h:468
CutPursuit(uint32_t nbVertex=1)
Definition: CutPursuit.h:67
Graph< T > main_graph
Definition: CutPursuit.h:45
CP::VertexIterator< T > lastIterator
Definition: CutPursuit.h:63
std::vector< std::vector< VertexDescriptor< T > > > components
Definition: CutPursuit.h:49
VertexDescriptor< T > sink
Definition: CutPursuit.h:58
VertexDescriptor< T > source
Definition: CutPursuit.h:57
size_t activate_edges(bool allows_saturation=true)
Definition: CutPursuit.h:298
std::vector< std::vector< EdgeDescriptor > > borders
Definition: CutPursuit.h:55
void compute_reduced_graph()
Definition: CutPursuit.h:541
virtual std::pair< std::vector< T >, T > compute_value(const uint32_t &ind_com)
Definition: CutPursuit.h:213
virtual size_t split()
Definition: CutPursuit.h:195
virtual std::pair< std::vector< T >, T > compute_merge_gain(const VertexDescriptor< T > &comp1, const VertexDescriptor< T > &comp2)
Definition: CutPursuit.h:223
uint32_t dim
Definition: CutPursuit.h:59
uint32_t nVertex
Definition: CutPursuit.h:60
std::vector< bool > saturated_components
Definition: CutPursuit.h:53
void compute_reduced_value()
Definition: CutPursuit.h:287
std::pair< std::vector< T >, std::vector< T > > run()
Definition: CutPursuit.h:94
Graph< T > reduced_graph
Definition: CutPursuit.h:46
std::vector< VertexDescriptor< T > > root_vertex
Definition: CutPursuit.h:51
void saturateComponent(const uint32_t &ind_com)
Definition: CutPursuit.h:835
uint32_t nEdge
Definition: CutPursuit.h:61
uint32_t merge(bool is_cutoff)
Definition: CutPursuit.h:646
virtual std::pair< T, T > compute_energy()
Definition: CutPursuit.h:204
std::size_t comp1
Definition: Common.h:88
std::size_t border_index
Definition: Common.h:89
std::size_t comp2
Definition: Common.h:88
std::vector< T > merged_value
Definition: Common.h:92