Surface Mesh Segmentation
 All Classes Namespaces Files Functions Typedefs Pages
SDF_calculation.h
1 #ifndef CGAL_SURFACE_MESH_SEGMENTATION_SDF_CALCULATION_H
2 #define CGAL_SURFACE_MESH_SEGMENTATION_SDF_CALCULATION_H
3 
4 #include <CGAL/AABB_tree.h>
5 #include <CGAL/AABB_traits.h>
6 #include <CGAL/AABB_polyhedron_triangle_primitive.h>
7 #include <CGAL/internal/Surface_mesh_segmentation/AABB_traversal_traits.h>
9 #include <vector>
10 #include <algorithm>
11 
12 #include <boost/tuple/tuple.hpp>
13 #include <boost/optional.hpp>
14 
15 #define CGAL_ST_DEV_MULTIPLIER 1 //0.75
16 
17 namespace CGAL {
19 namespace internal {
20 
28 template <
29  class Polyhedron,
30  class GeomTraits,
31  class DiskSampling = Vogel_disk_sampling<boost::tuple<double, double, double> >
32  >
33 class SDF_calculation
34 {
35 //type definitions
36 private:
37 
38  typedef typename GeomTraits::Vector_3 Vector;
39  typedef typename GeomTraits::Point_3 Point;
40  typedef typename GeomTraits::Ray_3 Ray;
41  typedef typename GeomTraits::Plane_3 Plane;
42  typedef typename GeomTraits::Segment_3 Segment;
43 
44  typedef typename Polyhedron::Traits Kernel;
45  typedef typename Polyhedron::Facet Facet;
46  typedef typename Polyhedron::Facet Vertex;
47 
48  typedef typename Polyhedron::Facet_const_iterator Facet_const_iterator;
49  typedef typename Polyhedron::Facet_const_handle Facet_const_handle;
50 
51  typedef AABB_const_polyhedron_triangle_primitive<GeomTraits, Polyhedron> Primitive;
52  typedef typename CGAL::AABB_tree<AABB_traits<GeomTraits, Primitive> > Tree;
53  typedef typename Tree::Object_and_primitive_id Object_and_primitive_id;
54  typedef typename Tree::Primitive_id Primitive_id;
55 
56  // Sampled points from disk, t1 = coordinate-x, t2 = coordinate-y, t3 = weight.
57  typedef boost::tuple<double, double, double> Disk_sample;
58  typedef std::vector<Disk_sample> Disk_samples_list;
59 
60 // member variables
61 private:
62  double cone_angle;
63  int number_of_rays;
64 
65  Disk_samples_list disk_samples;
66 
67  bool use_minimum_segment;
68  double multiplier_for_segment;
69  GeomTraits traits;
70 
71  typename GeomTraits::Angle_3 angle_functor;
72  typename GeomTraits::Construct_scaled_vector_3 scale_functor;
73  typename GeomTraits::Construct_sum_of_vectors_3 sum_functor;
74  typename GeomTraits::Construct_normal_3 normal_functor;
75  typename GeomTraits::Construct_unit_normal_3 unit_normal_functor;
76  typename GeomTraits::Construct_translated_point_3 translated_point_functor;
77  typename GeomTraits::Construct_centroid_3 centroid_functor;
78 public:
82  SDF_calculation(GeomTraits traits)
83  : traits(traits), use_minimum_segment(false), multiplier_for_segment(1),
84  angle_functor(traits.angle_3_object()),
85  scale_functor(traits.construct_scaled_vector_3_object()),
86  sum_functor(traits.construct_sum_of_vectors_3_object()),
87  normal_functor(traits.construct_normal_3_object()),
88  unit_normal_functor(traits.construct_unit_normal_3_object()),
89  translated_point_functor(traits.construct_translated_point_3_object()),
90  centroid_functor(traits.construct_centroid_3_object())
91  { }
92 
100  template <class FacetValueMap>
101  void calculate_sdf_values(const Polyhedron& mesh, double cone_angle, int number_of_rays, FacetValueMap sdf_values)
102  {
103  this->cone_angle = cone_angle;
104  this->number_of_rays = number_of_rays;
105 
106  DiskSampling()(number_of_rays, cone_angle, std::back_inserter(disk_samples));
107 
108  Tree tree(mesh.facets_begin(), mesh.facets_end());
109  for(Facet_const_iterator facet_it = mesh.facets_begin(); facet_it != mesh.facets_end(); ++facet_it)
110  {
111  sdf_values[facet_it] = calculate_sdf_value_of_facet(facet_it, tree);
112  }
113  }
114 
115 private:
123  double calculate_sdf_value_of_facet(Facet_const_handle facet, const Tree& tree) const
124  {
125  const Point& p1 = facet->halfedge()->vertex()->point();
126  const Point& p2 = facet->halfedge()->next()->vertex()->point();
127  const Point& p3 = facet->halfedge()->prev()->vertex()->point();
128  Point center = centroid_functor(p1, p2, p3);
129  Vector normal = unit_normal_functor(p2, p1, p3);
130 
131  Plane plane(center, normal);
132  Vector v1 = plane.base1(), v2 = plane.base2();
133  v1 = scale_functor(v1, 1.0 / CGAL::sqrt(v1.squared_length()));
134  v2 = scale_functor(v2, 1.0 / CGAL::sqrt(v2.squared_length()));
135 
136  std::vector<std::pair<double, double> > ray_distances;
137  ray_distances.reserve(disk_samples.size());
138 
139  const double length_of_normal = 1.0 / tan(cone_angle / 2.0);
140  normal = scale_functor(normal, length_of_normal);
141 
142  for(Disk_samples_list::const_iterator sample_it = disk_samples.begin();
143  sample_it != disk_samples.end(); ++sample_it)
144  {
145  bool is_intersected, intersection_is_acute;
146  double min_distance;
147  Vector disk_vector = sum_functor(scale_functor(v1, sample_it->get<0>()), scale_functor(v2, sample_it->get<1>()));
148  Vector ray_direction = sum_functor(normal, disk_vector);
149 
150  Ray ray(center, ray_direction);
151  boost::tie(is_intersected, intersection_is_acute, min_distance) = cast_and_return_minimum(ray, tree, facet);
152  if(!intersection_is_acute) { continue; }
153 
154  ray_distances.push_back(std::make_pair(min_distance, sample_it->get<2>()));
155  }
156  return remove_outliers_and_calculate_sdf_value(ray_distances);
157  }
158 
170  template <class Query> // Query can be templated for just Ray and Segment types.
171  boost::tuple<bool, bool, double> cast_and_return_minimum(
172  const Query& query, const Tree& tree, Facet_const_handle facet) const
173  {
174  boost::tuple<bool, bool, double> min_distance(false, false, 0.0);
175  std::list<Object_and_primitive_id> intersections;
176 
177  //SL: the difference with all_intersections is that in the traversal traits, we do do_intersect before calling intersection.
178  typedef std::back_insert_iterator< std::list<Object_and_primitive_id> > Output_iterator;
179  Listing_intersection_traits_ray_or_segment_triangle<typename Tree::AABB_traits,Query,Output_iterator>
180  traversal_traits(std::back_inserter(intersections));
181  tree.traversal(query,traversal_traits);
182 
183  Vector min_i_ray;
184  Primitive_id min_id;
185  for(typename std::list<Object_and_primitive_id>::iterator op_it = intersections.begin();
186  op_it != intersections.end() ; ++op_it)
187  {
188  Object object = op_it->first;
189  Primitive_id id = op_it->second;
190  if(id == facet) { continue; } // since center is located on related facet, we should skip it if there is an intersection with it.
191 
192  const Point* i_point;
193  if(!(i_point = object_cast<Point>(&object))) { continue; } // continue in case of segment.
194 
195  Vector i_ray(*i_point, query.source());
196  double new_distance = i_ray.squared_length();
197  if(!min_distance.get<0>() || new_distance < min_distance.get<2>())
198  {
199  min_distance.get<2>() = new_distance;
200  min_distance.get<0>() = true;
201  min_id = id;
202  min_i_ray = i_ray;
203  }
204  }
205  if(!min_distance.get<0>()) { return min_distance; }
206 
207  // check whether the ray makes acute angle with intersected facet
208  const Point& min_v1 = min_id->halfedge()->vertex()->point();
209  const Point& min_v2 = min_id->halfedge()->next()->vertex()->point();
210  const Point& min_v3 = min_id->halfedge()->prev()->vertex()->point();
211  Vector min_normal = scale_functor(normal_functor(min_v1, min_v2, min_v3), -1.0);
212 
213  if(angle_functor(translated_point_functor(Point(ORIGIN), min_i_ray),
214  Point(ORIGIN),
215  translated_point_functor(Point(ORIGIN), min_normal)) != ACUTE)
216  {
217  return min_distance;
218  }
219  min_distance.get<1>() = true; // founded intersection is acceptable.
220  min_distance.get<2>() = std::sqrt(min_distance.get<2>());
221  return min_distance;
222  }
223 
230  double remove_outliers_and_calculate_sdf_value(std::vector<std::pair<double, double> >& ray_distances) const
231  {
232  // pair first -> distance, second -> weight
233 
234  const int accepted_ray_count = ray_distances.size();
235  if(accepted_ray_count == 0) { return 0.0; }
236  else if(accepted_ray_count == 1) { return ray_distances[0].first * ray_distances[0].second; }
237 
238  /* Calculate median sdf */
239  const int half_ray_count = accepted_ray_count / 2;
240  std::nth_element(ray_distances.begin(), ray_distances.begin() + half_ray_count, ray_distances.end());
241  double median_sdf = ray_distances[half_ray_count].first;
242  if(accepted_ray_count % 2 == 0)
243  {
244  median_sdf += std::max_element(ray_distances.begin(), ray_distances.begin() + half_ray_count)->first;
245  median_sdf /= 2.0;
246  }
247 
248  /* Calculate median absolute deviation */
249  std::vector<double> absolute_deviation;
250  absolute_deviation.reserve(accepted_ray_count);
251  for(std::vector<std::pair<double, double> >::iterator it = ray_distances.begin(); it != ray_distances.end(); ++it)
252  {
253  absolute_deviation.push_back(std::abs(it->first - median_sdf));
254  }
255 
256  std::nth_element(absolute_deviation.begin(), absolute_deviation.begin() + half_ray_count, absolute_deviation.end());
257  double median_deviation = absolute_deviation[half_ray_count];
258  if(accepted_ray_count % 2 == 0)
259  {
260  median_deviation += *std::max_element(absolute_deviation.begin(), absolute_deviation.begin() + half_ray_count);
261  median_deviation /= 2.0;
262  }
263 
264  /* Calculate sdf, accept rays if ray_dist - median < (median_deviation * Factor) */
265  double total_weights = 0.0, total_distance = 0.0;
266  for(std::vector<std::pair<double, double> >::iterator it = ray_distances.begin(); it != ray_distances.end(); ++it)
267  {
268  if(std::abs(it->first - median_sdf) > (median_deviation * CGAL_ST_DEV_MULTIPLIER)) { continue; }
269  total_distance += it->first * it->second;
270  total_weights += it->second;
271  }
272 
273  if(total_distance == 0.0) { return median_sdf; } // no ray is accepted, return median.
274  else { return total_distance / total_weights; }
275  }
276 };
277 }//namespace internal
279 }//namespace CGAL
280 #undef CGAL_ST_DEV_MULTIPLIER
281 #undef CGAL_ACCEPTANCE_RATE_THRESHOLD
282 
283 #endif //CGAL_SURFACE_MESH_SEGMENTATION_SDF_CALCULATION_H