Surface Mesh Segmentation
 All Classes Namespaces Files Functions Typedefs Pages
Alpha_expansion_graph_cut.h
Go to the documentation of this file.
1 #ifndef CGAL_SURFACE_MESH_SEGMENTATION_ALPHA_EXPANSION_GRAPH_CUT_H
2 #define CGAL_SURFACE_MESH_SEGMENTATION_ALPHA_EXPANSION_GRAPH_CUT_H
3 
14 #include <CGAL/assertions.h>
15 
16 #include <boost/version.hpp>
17 #include <boost/graph/adjacency_list.hpp>
18 #if BOOST_VERSION >= 104400 // at this version kolmogorov_max_flow become depricated.
19  #include <boost/graph/boykov_kolmogorov_max_flow.hpp>
20 #else
21  #include <boost/graph/kolmogorov_max_flow.hpp>
22 #endif
23 
24 #include <vector>
25 
26 //#define CGAL_USE_BOYKOV_KOLMOGOROV_MAXFLOW_SOFTWARE
27 
28 #ifdef CGAL_USE_BOYKOV_KOLMOGOROV_MAXFLOW_SOFTWARE
29 #include <CGAL/internal/auxiliary/graph.h>
30 #endif
31 
32 namespace CGAL {
34 namespace internal {
35 
37 // Comments about performance:
38 //
39 // 1) With BGL:
40 // * Using adjacency_list:
41 // ** Without pre-allocating vertex-list
42 // | OutEdgeList | VertexList | Performance |
43 // | listS | listS | 25.2 |
44 // | vecS | listS | 22.7 |
45 // | listS | vecS | 30.7 |
46 // | vecS | vecS | 26.1 |
47 //
48 // ** With pre-allocating vertex-list with max-node size
49 // (Note: exact number of vertices are not certain at the beginning)
50 // | OutEdgeList | VertexList | Performance |
51 // | listS | vecS | 25.2 |
52 // | vecS | vecS | 23.4 |
53 //
54 // * Didn't try adjacency_matrix since our graph is sparse
55 // ( Also one can check BGL book, performance section )
56 //
57 // Decision: Two implementation is provided:
58 // * Alpha_expansion_graph_cut_boost: use adjacency_list<vecS, listS> without
59 // pre-allocating vertex-list, it should be DEFAULT since it is both efficient and clear.
60 // * Alpha_expansion_graph_cut_boost_with_preallocate: use adjacency_list<vecS, vecS>
61 // with pre-allocating vertex-list using max-node size.
62 //
63 // 2) With Boykov-Kolmogorov MAXFLOW software:
64 // (http://pub.ist.ac.at/~vnk/software/maxflow-v2.21.src.tar.gz)
65 // | Performance |
66 // | 3.1 |
67 // * Alpha_expansion_graph_cut_boykov_kolmogorov provides an implementation.
68 // MAXFLOW does not provide any option for pre-allocation (It is possible with v_3.02 though).
69 //
71 
78 class Alpha_expansion_graph_cut_boost
79 {
80 private:
81  typedef boost::adjacency_list_traits<boost::vecS, boost::listS, boost::directedS> Adjacency_list_traits;
82 
83  typedef boost::adjacency_list<boost::vecS, boost::listS, boost::directedS,
84  // 4 vertex properties
85  boost::property<boost::vertex_index_t, std::size_t,
86  boost::property<boost::vertex_color_t, boost::default_color_type,
87  boost::property<boost::vertex_distance_t, double,
88  boost::property<boost::vertex_predecessor_t, Adjacency_list_traits::edge_descriptor > > > >,
89  // 3 edge properties
90  boost::property<boost::edge_capacity_t, double,
91  boost::property<boost::edge_residual_capacity_t, double,
92  boost::property<boost::edge_reverse_t, Adjacency_list_traits::edge_descriptor> > > > Graph;
93 
94  typedef boost::graph_traits<Graph> Traits;
95  typedef boost::color_traits<boost::default_color_type> ColorTraits;
96 
97  typedef Traits::vertex_descriptor Vertex_descriptor;
98  typedef Traits::vertex_iterator Vertex_iterator;
99  typedef Traits::edge_descriptor Edge_descriptor;
100  typedef Traits::edge_iterator Edge_iterator;
101 
111  boost::tuple<Edge_descriptor, Edge_descriptor>
112  add_edge_and_reverse(Vertex_descriptor& v1, Vertex_descriptor& v2, double w1, double w2, Graph& graph) const
113  {
114  Edge_descriptor v1_v2, v2_v1;
115  bool v1_v2_added, v2_v1_added;
116 
117  tie(v1_v2, v1_v2_added) = boost::add_edge(v1, v2, graph);
118  tie(v2_v1, v2_v1_added) = boost::add_edge(v2, v1, graph);
119 
120  CGAL_assertion(v1_v2_added && v2_v1_added);
121  //put edge capacities
122  boost::put(boost::edge_reverse, graph, v1_v2, v2_v1);
123  boost::put(boost::edge_reverse, graph, v2_v1, v1_v2);
124 
125  //map reverse edges
126  boost::put(boost::edge_capacity, graph, v1_v2, w1);
127  boost::put(boost::edge_capacity, graph, v2_v1, w2);
128 
129  return boost::make_tuple(v1_v2, v2_v1);
130  }
131 
132 public:
142 double operator()(const std::vector<std::pair<int, int> >& edges,
143  const std::vector<double>& edge_weights,
144  const std::vector<std::vector<double> >& probability_matrix,
145  std::vector<int>& labels) const
146 {
147  double min_cut = (std::numeric_limits<double>::max)();
148  bool success;
149  do {
150  success = false;
151  int alpha = 0;
152  for(std::vector<std::vector<double> >::const_iterator it = probability_matrix.begin();
153  it != probability_matrix.end(); ++it, ++alpha)
154  {
155  Graph graph;
156  Vertex_descriptor cluster_source = boost::add_vertex(graph);
157  Vertex_descriptor cluster_sink = boost::add_vertex(graph);
158  std::vector<Vertex_descriptor> inserted_vertices;
159  inserted_vertices.reserve(labels.size());
160 
161  // For E-Data
162  // add every facet as a vertex to the graph, put edges to source & sink vertices
163  for(std::size_t vertex_i = 0; vertex_i < labels.size(); ++vertex_i)
164  {
165  Vertex_descriptor new_vertex = boost::add_vertex(graph);
166  inserted_vertices.push_back(new_vertex);
167  double source_weight = probability_matrix[alpha][vertex_i];
168  // since it is expansion move, current alpha labeled vertices will be assigned to alpha again,
169  // making sink_weight 'infinity' guarantee this.
170  double sink_weight = (labels[vertex_i] == alpha) ? (std::numeric_limits<double>::max)()
171  : probability_matrix[labels[vertex_i]][vertex_i];
172 
173  add_edge_and_reverse(cluster_source, new_vertex, source_weight, 0.0, graph);
174  add_edge_and_reverse(new_vertex, cluster_sink, sink_weight, 0.0, graph);
175  }
176 
177  // For E-Smooth
178  // add edge between every vertex,
179  std::vector<double>::const_iterator weight_it = edge_weights.begin();
180  for(std::vector<std::pair<int, int> >::const_iterator edge_it = edges.begin(); edge_it != edges.end();
181  ++edge_it, ++weight_it)
182  {
183  Vertex_descriptor v1 = inserted_vertices[edge_it->first], v2 = inserted_vertices[edge_it->second];
184  int label_1 = labels[edge_it->first], label_2 = labels[edge_it->second];
185  if(label_1 == label_2)
186  {
187  if(label_1 != alpha)
188  { add_edge_and_reverse(v1, v2, *weight_it, *weight_it, graph); }
189  }
190  else
191  {
192  Vertex_descriptor inbetween = boost::add_vertex(graph);
193 
194  double w1 = (label_1 == alpha) ? 0 : *weight_it;
195  double w2 = (label_2 == alpha) ? 0 : *weight_it;
196  add_edge_and_reverse(inbetween, v1, w1, w1, graph);
197  add_edge_and_reverse(inbetween, v2, w2, w2, graph);
198  add_edge_and_reverse(inbetween, cluster_sink, *weight_it, 0.0, graph);
199  }
200  }
201  // initialize vertex indices, it is neccessary since we are using VertexList = listS
202  Vertex_iterator v_begin, v_end;
203  Traits::vertices_size_type index = 0;
204  for(boost::tie(v_begin, v_end) = vertices(graph); v_begin != v_end; ++v_begin)
205  {
206  boost::put(boost::vertex_index, graph, *v_begin, index++);
207  }
208 
209  #if BOOST_VERSION >= 104400
210  double flow = boost::boykov_kolmogorov_max_flow(graph, cluster_source, cluster_sink);
211  #else
212  double flow = boost::kolmogorov_max_flow(graph, cluster_source, cluster_sink);
213  #endif
214 
215  if(min_cut - flow < flow * 1e-10) { continue; }
216  min_cut = flow;
217  success = true;
218  //update labeling
219  for(std::size_t vertex_i = 0; vertex_i < inserted_vertices.size(); ++vertex_i)
220  {
221  boost::default_color_type color = boost::get(boost::vertex_color, graph, inserted_vertices[vertex_i]);
222  if(labels[vertex_i] != alpha && color == ColorTraits::white()) //new comers (expansion occurs)
223  {
224  labels[vertex_i] = alpha;
225  }
226  }
227 
228  }
229  } while(success);
230  return min_cut;
231 }
232 };
233 
234 #ifdef CGAL_USE_BOYKOV_KOLMOGOROV_MAXFLOW_SOFTWARE
235 
241 class Alpha_expansion_graph_cut_boykov_kolmogorov
242 {
243 public:
253 double operator()(const std::vector<std::pair<int, int> >& edges,
254  const std::vector<double>& edge_weights,
255  const std::vector<std::vector<double> >& probability_matrix,
256  std::vector<int>& labels) const
257 {
258  double min_cut = (std::numeric_limits<double>::max)();
259  bool success;
260  do {
261  success = false;
262  int alpha = 0;
263  for(std::vector<std::vector<double> >::const_iterator it = probability_matrix.begin();
264  it != probability_matrix.end(); ++it, ++alpha)
265  {
266  Graph graph;
267  std::vector<Graph::node_id> inserted_vertices;
268  inserted_vertices.reserve(labels.size());
269  // For E-Data
270  // add every facet as a vertex to graph, put edges to source-sink vertices
271  for(std::size_t vertex_i = 0; vertex_i < probability_matrix[0].size(); ++vertex_i)
272  {
273  Graph::node_id new_vertex = graph.add_node();
274  inserted_vertices.push_back(new_vertex);
275 
276  double source_weight = probability_matrix[alpha][vertex_i];
277  // since it is expansion move, current alpha labeled vertices will be assigned to alpha again,
278  // making sink_weight 'infinity' guarantee this.
279  double sink_weight = (labels[vertex_i] == alpha) ? (std::numeric_limits<double>::max)()
280  : probability_matrix[labels[vertex_i]][vertex_i];
281  graph.add_tweights(new_vertex, source_weight, sink_weight);
282  }
283  // For E-Smooth
284  // add edge between every vertex,
285  std::vector<double>::const_iterator weight_it = edge_weights.begin();
286  for(std::vector<std::pair<int, int> >::const_iterator edge_it = edges.begin(); edge_it != edges.end();
287  ++edge_it, ++weight_it)
288  {
289  Graph::node_id v1 = inserted_vertices[edge_it->first];
290  Graph::node_id v2 = inserted_vertices[edge_it->second];
291  int label_1 = labels[edge_it->first], label_2 = labels[edge_it->second];
292  if(label_1 == label_2)
293  {
294  if(label_1 != alpha)
295  { graph.add_edge(v1, v2, *weight_it, *weight_it); }
296  }
297  else
298  {
299  Graph::node_id inbetween = graph.add_node();
300 
301  double w1 = (label_1 == alpha) ? 0 : *weight_it;
302  double w2 = (label_2 == alpha) ? 0 : *weight_it;
303  graph.add_edge(inbetween, v1, w1, w1);
304  graph.add_edge(inbetween, v2, w2, w2);
305 
306  graph.add_tweights(inbetween, 0.0, *weight_it);
307  }
308  }
309  double flow = graph.maxflow();
310  if(min_cut - flow < flow * 1e-10) { continue; }
311 
312  min_cut = flow;
313  success = true;
314  //update labeling
315  for(std::size_t vertex_i = 0; vertex_i < labels.size(); ++vertex_i)
316  {
317  if(labels[vertex_i] != alpha && graph.what_segment(inserted_vertices[vertex_i]) == Graph::SINK)
318  {
319  labels[vertex_i] = alpha;
320  }
321  }
322  }
323  } while(success);
324  return min_cut;
325 }
326 };
327 #endif //CGAL_USE_BOYKOV_KOLMOGOROV_MAXFLOW_SOFTWARE
328 }//namespace internal
330 }//namespace CGAL
331 #endif //CGAL_SURFACE_MESH_SEGMENTATION_ALPHA_EXPANSION_GRAPH_CUT_H