Surface Mesh Segmentation
 All Classes Namespaces Files Functions Typedefs Pages
Surface_mesh_segmentation.h
1 #ifndef CGAL_SURFACE_MESH_SEGMENTATION_H
2 #define CGAL_SURFACE_MESH_SEGMENTATION_H
3 
4 #include <CGAL/internal/Surface_mesh_segmentation/Expectation_maximization.h>
7 #include <CGAL/internal/Surface_mesh_segmentation/SDF_calculation.h>
8 
9 #include <CGAL/Mesh_3/dihedral_angle_3.h>
10 
11 #include <boost/property_map/property_map.hpp>
12 
13 #include <cmath>
14 #include <vector>
15 #include <algorithm>
16 #include <utility>
17 #include <map>
18 
19 #define CGAL_NORMALIZATION_ALPHA 5.0
20 #define CGAL_CONVEX_FACTOR 0.08
21 #define CGAL_SMOOTHING_LAMBDA_MULTIPLIER 100.0
22 
23 namespace CGAL {
25 namespace internal{
45 template <
46  class Polyhedron,
47  class GeomTraits,
48 #ifdef CGAL_USE_BOYKOV_KOLMOGOROV_MAXFLOW_SOFTWARE
49  class GraphCut = Alpha_expansion_graph_cut_boykov_kolmogorov,
50 #else
51  class GraphCut = Alpha_expansion_graph_cut_boost,
52 #endif
53  class Filter = Bilateral_filtering<Polyhedron>
54  >
55 class Surface_mesh_segmentation
56 {
57 //type definitions
58 public:
59  typedef typename Polyhedron::Facet_const_handle Facet_const_handle;
60 
61 private:
62  //typedef typename Polyhedron::Traits Kernel;
63  typedef typename GeomTraits::Point_3 Point;
64 
65  typedef typename Polyhedron::Facet Facet;
66 
67  typedef typename Polyhedron::Edge_const_iterator Edge_const_iterator;
68  typedef typename Polyhedron::Halfedge_const_handle Halfedge_const_handle;
69  typedef typename Polyhedron::Halfedge_const_iterator Halfedge_const_iterator;
70  typedef typename Polyhedron::Facet_const_iterator Facet_const_iterator;
71  typedef typename Polyhedron::Vertex_const_iterator Vertex_const_iterator;
72 
73  typedef SDF_calculation<Polyhedron, GeomTraits> SDF_calculation_class;
74 
75 // member variables
76 private:
77  const Polyhedron& mesh;
78  GeomTraits traits;
79 // member functions
80 public:
85 Surface_mesh_segmentation(const Polyhedron& mesh, GeomTraits traits)
86  : mesh(mesh), traits(traits)
87 { CGAL_precondition(mesh.is_pure_triangle()); }
88 
89 // Use these two functions together
90 template <class SDFPropertyMap>
91 std::pair<double, double>
92 calculate_sdf_values(double cone_angle, int number_of_rays, SDFPropertyMap sdf_pmap)
93 {
94  // calculate sdf values
95  SDF_calculation_class(traits).calculate_sdf_values(mesh, cone_angle, number_of_rays, sdf_pmap);
96  // apply post-processing steps
97  check_zero_sdf_values(sdf_pmap);
98  Filter()(mesh, get_window_size(), sdf_pmap);
99  std::pair<double, double> min_max_sdf_values = linear_normalize_sdf_values(sdf_pmap);
100 
101  // return minimum and maximum sdf values before normalization
102  return min_max_sdf_values;
103 }
104 
105 template <class FacetSegmentMap, class SDFPropertyMap>
106 int partition(int number_of_centers, double smoothing_lambda, SDFPropertyMap sdf_pmap, FacetSegmentMap segment_pmap)
107 {
108  smoothing_lambda = (std::max)(0.0, (std::min)(1.0, smoothing_lambda)); // clip into [0-1]
109  smoothing_lambda *= CGAL_SMOOTHING_LAMBDA_MULTIPLIER; // scale it into meaningful range for graph-cut
110 
111  // log normalize sdf values
112  std::vector<double> sdf_values;
113  log_normalize_sdf_values(sdf_pmap, sdf_values);
114 
115  // soft clustering using GMM-fitting initialized with k-means
116  Expectation_maximization fitter(number_of_centers, sdf_values,
117  Expectation_maximization::K_MEANS_INITIALIZATION, 1);
118 
119  std::vector<int> labels;
120  fitter.fill_with_center_ids(labels);
121 
122  std::vector<std::vector<double> > probability_matrix;
123  fitter.fill_with_probabilities(probability_matrix);
124  log_normalize_probability_matrix(probability_matrix);
125 
126  // calculating edge weights
127  std::vector<std::pair<int, int> > edges;
128  std::vector<double> edge_weights;
129  calculate_and_log_normalize_dihedral_angles(smoothing_lambda, edges, edge_weights);
130 
131  // apply graph cut
132  GraphCut()(edges, edge_weights, probability_matrix, labels);
133  std::vector<int>::iterator label_it = labels.begin();
134  for(Facet_const_iterator facet_it = mesh.facets_begin(); facet_it != mesh.facets_end();
135  ++facet_it, ++label_it)
136  {
137  segment_pmap[facet_it] = *label_it; // fill with cluster-ids
138  }
139  // assign a segment id for each facet
140  int number_of_segments = assign_segments(number_of_centers, sdf_pmap, segment_pmap);
141  return number_of_segments;
142 }
143 
144 private:
145 
153 double calculate_dihedral_angle_of_edge(Halfedge_const_handle edge) const
154 {
155  CGAL_precondition(!edge->is_border_edge());
156  const Point& a = edge->vertex()->point();
157  const Point& b = edge->prev()->vertex()->point();
158  const Point& c = edge->next()->vertex()->point();
159  const Point& d = edge->opposite()->next()->vertex()->point();
160  // As far as I check: if, say, dihedral angle is 5, this returns 175,
161  // if dihedral angle is -5, this returns -175.
162  // Another words this function returns angle between planes.
163  double n_angle = Mesh_3::dihedral_angle(a, b, c, d);
164  n_angle /= 180.0;
165  bool concave = n_angle > 0;
166  double angle = 1 + ((concave ? -1 : +1) * n_angle);
167 
168  if(!concave) { angle *= CGAL_CONVEX_FACTOR; }
169  return angle;
170 }
171 
179 template<class SDFPropertyMap>
180 void log_normalize_sdf_values(SDFPropertyMap sdf_values, std::vector<double>& normalized_sdf_values)
181 {
182  normalized_sdf_values.reserve(mesh.size_of_facets());
183  for(Facet_const_iterator facet_it = mesh.facets_begin(); facet_it != mesh.facets_end(); ++facet_it)
184  {
185  double log_normalized = log(sdf_values[facet_it] * CGAL_NORMALIZATION_ALPHA + 1) / log(CGAL_NORMALIZATION_ALPHA + 1);
186  normalized_sdf_values.push_back(log_normalized);
187  }
188 }
189 
195 template<class SDFPropertyMap>
196 std::pair<double, double> linear_normalize_sdf_values(SDFPropertyMap sdf_values)
197 {
198  double max_sdf = -(std::numeric_limits<double>::max)();
199  double min_sdf = (std::numeric_limits<double>::max)();
200  for(Facet_const_iterator facet_it = mesh.facets_begin(); facet_it != mesh.facets_end(); ++facet_it)
201  {
202  double sdf_value = sdf_values[facet_it];
203  max_sdf = (std::max)(sdf_value, max_sdf);
204  min_sdf = (std::min)(sdf_value, min_sdf);
205  }
206  const double max_min_dif = max_sdf - min_sdf;
207  for(Facet_const_iterator facet_it = mesh.facets_begin(); facet_it != mesh.facets_end(); ++facet_it)
208  {
209  sdf_values[facet_it] = (sdf_values[facet_it] - min_sdf) / max_min_dif;
210  }
211  return std::make_pair(min_sdf, max_sdf);
212 }
213 
214 
224 int get_window_size()
225 {
226  double facet_sqrt = std::sqrt(mesh.size_of_facets() / 2000.0);
227  return static_cast<int>(facet_sqrt) + 1;
228 }
229 
237 template<class SDFPropertyMap>
238 void check_zero_sdf_values(SDFPropertyMap sdf_values)
239 {
240  std::vector<Facet_const_handle> still_zero_facets;
241  double min_sdf = (std::numeric_limits<double>::max)();
242  // If there is any facet which has no sdf value, assign average sdf value of its neighbors
243  for(Facet_const_iterator facet_it = mesh.facets_begin(); facet_it != mesh.facets_end(); ++facet_it)
244  {
245  double sdf_value = sdf_values[facet_it];
246  if(sdf_value == 0.0)
247  {
248  typename Facet::Halfedge_around_facet_const_circulator facet_circulator = facet_it->facet_begin();
249  double total_neighbor_sdf = 0.0;
250  do {
251  if(!facet_circulator->opposite()->is_border())
252  {
253  total_neighbor_sdf += sdf_values[facet_circulator->opposite()->facet()];
254  }
255  } while( ++facet_circulator != facet_it->facet_begin());
256 
257  sdf_value = total_neighbor_sdf / 3.0;
258  sdf_values[facet_it] = sdf_value;
259 
260  if(sdf_value == 0.0) { still_zero_facets.push_back(facet_it); } // if sdf_value is still zero, put facet to zero-list
261  else { min_sdf = (std::min)(sdf_value, min_sdf); } // find min_sdf other than zero
262  }
263  else { min_sdf = (std::min)(sdf_value, min_sdf); } // find min_sdf other than zero
264  }
265  // If still there is any facet which has no sdf value, assign minimum sdf value.
266  // This is meaningful since (being an outlier) 0 sdf values might effect normalization & log extremely.
267  for(typename std::vector<Facet_const_handle>::iterator it = still_zero_facets.begin();
268  it != still_zero_facets.end(); ++it)
269  {
270  sdf_values[*it] = min_sdf;
271  }
272 }
273 
279 void log_normalize_probability_matrix(std::vector<std::vector<double> >& probabilities) const
280 {
281  const double epsilon = 5e-6;
282  for(std::vector<std::vector<double> >::iterator it_i = probabilities.begin(); it_i != probabilities.end(); ++it_i)
283  {
284  for(std::vector<double>::iterator it = it_i->begin(); it != it_i->end(); ++it)
285  {
286  double probability = (std::max)(*it, epsilon); // give every facet a little probability to be in any cluster
287  probability = -log(probability);
288  *it = (std::max)(probability, std::numeric_limits<double>::epsilon());
289  // zero values are not accepted in max-flow as weights for edges which connects some vertex with Source or Sink (in boost::boykov..)
290  }
291  }
292 }
293 
300 void calculate_and_log_normalize_dihedral_angles(double smoothing_lambda,
301  std::vector<std::pair<int, int> >& edges, std::vector<double>& edge_weights) const
302 {
303  // associate each facet with an id
304  // important note: ids should be compatible with iteration order of facets:
305  // [0 <- facet_begin(),...., size_of_facets() -1 <- facet_end()]
306  // Why ? it is send to graph cut algorithm where other data associated with facets are also sorted according to iteration order.
307  std::map<Facet_const_handle, int> facet_index_map;
308  int facet_index = 0;
309  for(Facet_const_iterator facet_it = mesh.facets_begin(); facet_it != mesh.facets_end();
310  ++facet_it, ++facet_index)
311  {
312  facet_index_map[facet_it] = facet_index;
313  }
314 
315  const double epsilon = 5e-6;
316  // edges and their weights. pair<int, int> stores facet-id pairs (see above) (may be using boost::tuple can be more suitable)
317  for(Edge_const_iterator edge_it = mesh.edges_begin(); edge_it != mesh.edges_end(); ++edge_it)
318  {
319  if(edge_it->is_border_edge()) { continue; } // if edge does not contain two neighbor facets then do not include it in graph-cut
320  const int index_f1 = facet_index_map[edge_it->facet()];
321  const int index_f2 = facet_index_map[edge_it->opposite()->facet()];
322  edges.push_back(std::make_pair(index_f1, index_f2));
323 
324  double angle = calculate_dihedral_angle_of_edge(edge_it);
325 
326  if(angle < epsilon) { angle = epsilon; }
327  angle = -log(angle);
328  angle = (std::max)(angle, std::numeric_limits<double>::epsilon());
329  angle *= smoothing_lambda;
330  edge_weights.push_back(angle);
331  }
332 }
333 
334 template<class Pair>
335 struct Sort_pairs_with_second {
336  bool operator() (const Pair& pair_1, const Pair& pair_2) const
337  {
338  return pair_1.second < pair_2.second;
339  }
340 };
341 
343 // 0 0 1 1 0 0 0 0 2 2 4 4 //
344 // 0 0 1 1 0 0 0 0 2 2 4 4 //
345 // 1 0 2 2 1 1 -> 1 0 3 3 5 5 //
346 // 1 0 2 2 1 1 1 0 3 3 5 5 //
347 // cluster-ids -> segment-ids //
360 template<class SegmentPropertyMap, class SDFProperyMap>
361 int assign_segments(int number_of_clusters, SDFProperyMap sdf_values, SegmentPropertyMap segments)
362 {
363  // assign a segment-id to each facet
364  int segment_id = number_of_clusters;
365  std::vector<std::pair<int, double> > segments_with_average_sdf_values;
366 
367  for(Facet_const_iterator facet_it = mesh.facets_begin(); facet_it != mesh.facets_end(); ++facet_it)
368  {
369  if(segments[facet_it] < number_of_clusters) // not visited by depth_first_traversal
370  {
371  double average_sdf_value = breadth_first_traversal(facet_it, segment_id, sdf_values, segments);
372 
373  segments_with_average_sdf_values.push_back(std::make_pair(segment_id, average_sdf_value));
374  ++segment_id;
375  }
376  }
377  // sort segments according to their average sdf value
378  sort(segments_with_average_sdf_values.begin(), segments_with_average_sdf_values.end(),
379  Sort_pairs_with_second<std::pair<int, double> >());
380  // map each segment-id to its new sorted index
381  std::vector<int> segment_id_to_sorted_id_map(segments_with_average_sdf_values.size());
382  for(std::size_t index = 0; index < segments_with_average_sdf_values.size(); ++index)
383  {
384  int segment_id = segments_with_average_sdf_values[index].first - number_of_clusters;
385  segment_id_to_sorted_id_map[segment_id] = index;
386  }
387  // make one-pass on facets. First make segment-id zero based by subtracting number_of_clusters
388  // . Then place its sorted index to pmap
389  for(Facet_const_iterator facet_it = mesh.facets_begin(); facet_it != mesh.facets_end(); ++facet_it)
390  {
391  int segment_id = segments[facet_it] - number_of_clusters;
392  segments[facet_it] = segment_id_to_sorted_id_map[segment_id];
393  }
394  return segment_id - number_of_clusters;
395 }
396 
406 template<class SegmentPropertyMap, class SDFProperyMap>
407 double
408 breadth_first_traversal(Facet_const_handle root, int segment_id, SDFProperyMap sdf_values, SegmentPropertyMap segments)
409 {
410  std::queue<Facet_const_handle> facet_queue;
411  facet_queue.push(root);
412 
413  int prev_segment_id = segments[root];
414  segments[root] = segment_id;
415 
416  double total_sdf_value = sdf_values[root];
417  int visited_facet_count = 1;
418 
419  while(!facet_queue.empty())
420  {
421  Facet_const_handle facet = facet_queue.front();
422 
423  typename Facet::Halfedge_around_facet_const_circulator facet_circulator = facet->facet_begin();
424  do {
425  if(facet_circulator->opposite()->is_border()) { continue; } // no facet to traversal
426  Facet_const_handle neighbor = facet_circulator->opposite()->facet();
427  if(prev_segment_id == segments[neighbor])
428  {
429  segments[neighbor] = segment_id;
430  facet_queue.push(neighbor);
431 
432  total_sdf_value += sdf_values[neighbor];
433  ++visited_facet_count;
434  }
435  } while( ++facet_circulator != facet->facet_begin());
436 
437  facet_queue.pop();
438  }
439 
440  return total_sdf_value / visited_facet_count;
441 }
442 
443 };
444 }//namespace internal
446 } //namespace CGAL
447 
448 #undef CGAL_NORMALIZATION_ALPHA
449 #undef CGAL_CONVEX_FACTOR
450 #undef CGAL_SMOOTHING_LAMBDA_MULTIPLIER
451 #endif //CGAL_SURFACE_MESH_SEGMENTATION_H