mirror of https://github.com/CGAL/cgal
1168 lines
44 KiB
C++
1168 lines
44 KiB
C++
#ifndef CGAL_SURFACE_MESH_SEGMENTATION_H
|
|
#define CGAL_SURFACE_MESH_SEGMENTATION_H
|
|
/* NEED TO BE DONE
|
|
* About implementation:
|
|
* +) I am not using BGL, as far as I checked there is a progress on BGL redesign
|
|
* (https://cgal.geometryfactory.com/CGAL/Members/wiki/Features/BGL) which introduces some features
|
|
* for face-based traversal / manipulation by FaceGraphs
|
|
* +) Deciding on which parameters will be taken from user
|
|
*
|
|
* About paper (and correctness / efficiency etc.):
|
|
* +) Weighting ray distances with inverse of their angles: not sure how to weight exactly
|
|
* +) Anisotropic smoothing: have no idea what it is exactly, should read some material (google search is not enough)
|
|
* +) Deciding how to generate rays in cone: for now using "polar angle" and "accept-reject (square)" and "concentric mapping" techniques
|
|
*/
|
|
|
|
|
|
#include <CGAL/internal/Surface_mesh_segmentation/Expectation_maximization.h>
|
|
#include <CGAL/internal/Surface_mesh_segmentation/K_means_clustering.h>
|
|
#include <CGAL/internal/Surface_mesh_segmentation/Alpha_expansion_graph_cut.h>
|
|
|
|
//AF: This files does not use Simple_cartesian
|
|
//IOY: Yes, not sure where it came from (with update may be), I am going to ask about it.
|
|
#include <CGAL/Simple_cartesian.h>
|
|
#include <CGAL/AABB_tree.h>
|
|
#include <CGAL/AABB_traits.h>
|
|
#include <CGAL/AABB_polyhedron_triangle_primitive.h>
|
|
#include <CGAL/utility.h>
|
|
#include <CGAL/Timer.h>
|
|
#include <CGAL/Mesh_3/dihedral_angle_3.h>
|
|
#include <CGAL/internal/Surface_mesh_segmentation/AABB_traversal_traits.h>
|
|
|
|
#include <boost/optional.hpp>
|
|
|
|
#include <iostream>
|
|
#include <fstream>
|
|
#include <cmath>
|
|
#include <vector>
|
|
#include <algorithm>
|
|
#include <utility>
|
|
|
|
//AF: macros must be prefixed with "CGAL_"
|
|
//IOY: done
|
|
#define CGAL_LOG_5 1.60943791
|
|
#define CGAL_NORMALIZATION_ALPHA 4.0
|
|
#define CGAL_ANGLE_ST_DEV_DIVIDER 3.0
|
|
|
|
//IOY: these are going to be removed at the end (no CGAL_ pref)
|
|
#define ACTIVATE_SEGMENTATION_DEBUG
|
|
#ifdef ACTIVATE_SEGMENTATION_DEBUG
|
|
#define SEG_DEBUG(x) x;
|
|
#else
|
|
#define SEG_DEBUG(x)
|
|
#endif
|
|
// If defined then profile function becomes active, and called from constructor.
|
|
//#define SEGMENTATION_PROFILE
|
|
|
|
namespace CGAL
|
|
{
|
|
|
|
template <class Polyhedron>
|
|
class Surface_mesh_segmentation
|
|
{
|
|
//type definitions
|
|
public:
|
|
typedef typename Polyhedron::Traits Kernel;
|
|
typedef typename Polyhedron::Facet Facet;
|
|
typedef typename Kernel::Vector_3 Vector;
|
|
typedef typename Kernel::Point_3 Point;
|
|
typedef typename Polyhedron::Facet_iterator Facet_iterator;
|
|
typedef typename Polyhedron::Facet_handle Facet_handle;
|
|
typedef typename Polyhedron::Halfedge_handle Halfedge_handle;
|
|
typedef typename Polyhedron::Edge_iterator Edge_iterator;
|
|
typedef typename Polyhedron::Vertex_handle Vertex_handle;
|
|
protected:
|
|
typedef typename Kernel::Ray_3 Ray;
|
|
typedef typename Kernel::Plane_3 Plane;
|
|
typedef typename Kernel::Segment_3 Segment;
|
|
|
|
typedef typename CGAL::AABB_polyhedron_triangle_primitive<Kernel, Polyhedron>
|
|
Primitive;
|
|
typedef typename CGAL::AABB_tree<CGAL::AABB_traits<Kernel, Primitive> >
|
|
Tree;
|
|
typedef typename Tree::Object_and_primitive_id
|
|
Object_and_primitive_id;
|
|
typedef typename Tree::Primitive_id
|
|
Primitive_id;
|
|
|
|
typedef std::map<Facet_handle, double> Face_value_map;
|
|
typedef std::map<Facet_handle, int> Face_center_map;
|
|
typedef std::map<Halfedge_handle, double> Edge_angle_map;
|
|
/*Sampled points from disk, t1 = coordinate-x, t2 = coordinate-y, t3 = angle with cone-normal (weight). */
|
|
typedef CGAL::Triple<double, double, double> Disk_sample;
|
|
typedef std::vector<CGAL::Triple<double, double, double> > Disk_samples_list;
|
|
|
|
template <typename ValueTypeName>
|
|
struct Compare_second_element {
|
|
bool operator()(const ValueTypeName& v1,const ValueTypeName& v2) const {
|
|
return v1.second < v2.second;
|
|
}
|
|
};
|
|
|
|
template <typename ValueTypeName>
|
|
struct Compare_third_element {
|
|
bool operator()(const ValueTypeName& v1,const ValueTypeName& v2) const {
|
|
return v1.third > v2.third; //descending
|
|
}
|
|
};
|
|
|
|
//member variables
|
|
public:
|
|
Polyhedron* mesh;
|
|
|
|
Face_value_map sdf_values;
|
|
Face_center_map centers;
|
|
Edge_angle_map dihedral_angles;
|
|
|
|
double cone_angle;
|
|
int number_of_rays_sqrt;
|
|
int number_of_centers;
|
|
/*Store sampled points from disk, just for once */
|
|
Disk_samples_list disk_samples;
|
|
|
|
//these are going to be removed...
|
|
std::ofstream log_file;
|
|
|
|
bool use_minimum_segment;
|
|
double multiplier_for_segment;
|
|
static long miss_counter;
|
|
//member functions
|
|
public:
|
|
Surface_mesh_segmentation(Polyhedron* mesh,
|
|
int number_of_rays_sqrt = 7, double cone_angle = (2.0 / 3.0) * CGAL_PI,
|
|
int number_of_centers = 2);
|
|
|
|
void calculate_sdf_values();
|
|
|
|
double calculate_sdf_value_of_facet (const Facet_handle& facet,
|
|
const Tree& tree) const;
|
|
template <class Query>
|
|
boost::tuple<bool, bool, double> cast_and_return_minimum(const Query& ray,
|
|
const Tree& tree, const Facet_handle& facet) const;
|
|
template <class Query>
|
|
boost::tuple<bool, bool, double> cast_and_return_minimum_use_closest(
|
|
const Query& ray, const Tree& tree, const Facet_handle& facet) const;
|
|
|
|
double calculate_sdf_value_from_rays (std::vector<double>& ray_distances,
|
|
std::vector<double>& ray_weights) const;
|
|
double calculate_sdf_value_from_rays_with_mean (std::vector<double>&
|
|
ray_distances, std::vector<double>& ray_weights) const;
|
|
double calculate_sdf_value_from_rays_with_trimmed_mean (
|
|
std::vector<double>& ray_distances, std::vector<double>& ray_weights) const;
|
|
|
|
void arrange_center_orientation(const Plane& plane, const Vector& unit_normal,
|
|
Point& center) const;
|
|
void calculate_dihedral_angles();
|
|
double calculate_dihedral_angle_of_edge(const Halfedge_handle& edge) const;
|
|
double calculate_dihedral_angle_of_edge_2(const Halfedge_handle& edge) const;
|
|
|
|
void disk_sampling_rejection();
|
|
void disk_sampling_polar_mapping();
|
|
void disk_sampling_concentric_mapping();
|
|
|
|
void normalize_sdf_values();
|
|
void smooth_sdf_values();
|
|
|
|
void apply_GMM_fitting();
|
|
void apply_K_means_clustering();
|
|
void apply_GMM_fitting_with_K_means_init();
|
|
void apply_graph_cut();
|
|
|
|
void write_sdf_values(const char* file_name);
|
|
void read_sdf_values(const char* file_name);
|
|
void read_center_ids(const char* file_name);
|
|
void profile(const char* file_name);
|
|
|
|
};
|
|
template <class Polyhedron>
|
|
long Surface_mesh_segmentation<Polyhedron>::miss_counter(0);
|
|
|
|
template <class Polyhedron>
|
|
inline Surface_mesh_segmentation<Polyhedron>::Surface_mesh_segmentation(
|
|
Polyhedron* mesh, int number_of_rays_sqrt, double cone_angle,
|
|
int number_of_centers)
|
|
: mesh(mesh), cone_angle(cone_angle), number_of_rays_sqrt(number_of_rays_sqrt),
|
|
number_of_centers(number_of_centers), log_file("log_file.txt"),
|
|
use_minimum_segment(false), multiplier_for_segment(1)
|
|
{
|
|
disk_sampling_concentric_mapping();
|
|
#ifdef SEGMENTATION_PROFILE
|
|
profile("profile.txt");
|
|
#else
|
|
SEG_DEBUG(CGAL::Timer t)
|
|
SEG_DEBUG(t.start())
|
|
calculate_sdf_values();
|
|
SEG_DEBUG(std::cout << t.time() << std::endl)
|
|
#endif
|
|
//
|
|
//write_sdf_values("sdf_values_sample_dino_ws.txt");
|
|
//read_sdf_values("sdf_values_sample_elephant.txt");
|
|
//apply_GMM_fitting();
|
|
apply_graph_cut();
|
|
|
|
}
|
|
|
|
template <class Polyhedron>
|
|
inline void Surface_mesh_segmentation<Polyhedron>::calculate_sdf_values()
|
|
{
|
|
sdf_values.clear();
|
|
Tree tree(mesh->facets_begin(), mesh->facets_end());
|
|
for(Facet_iterator facet_it = mesh->facets_begin();
|
|
facet_it != mesh->facets_end(); ++facet_it) {
|
|
CGAL_precondition(facet_it->is_triangle()); //Mesh should contain triangles.
|
|
|
|
double sdf = calculate_sdf_value_of_facet(facet_it, tree);
|
|
sdf_values.insert(std::pair<Facet_handle, double>(facet_it, sdf));
|
|
}
|
|
|
|
//std::cout << Listing_intersection_traits_ray_or_segment_triangle
|
|
// < typename Tree::AABB_traits, Ray, std::back_insert_iterator< std::list<Object_and_primitive_id> > >::inter_counter << std::endl;
|
|
//std::cout << Listing_intersection_traits_ray_or_segment_triangle
|
|
// < typename Tree::AABB_traits, Ray, std::back_insert_iterator< std::list<Object_and_primitive_id> > >::true_inter_counter << std::endl;
|
|
//std::cout << Listing_intersection_traits_ray_or_segment_triangle
|
|
// < typename Tree::AABB_traits, Ray, std::back_insert_iterator< std::list<Object_and_primitive_id> > >::do_inter_counter << std::endl;
|
|
normalize_sdf_values();
|
|
smooth_sdf_values();
|
|
}
|
|
|
|
template <class Polyhedron>
|
|
inline double
|
|
Surface_mesh_segmentation<Polyhedron>::calculate_sdf_value_of_facet(
|
|
const Facet_handle& facet, const Tree& tree) const
|
|
{
|
|
// AF: Use const Point&
|
|
//IOY: Done, and I am going to change other places (where it is approprite) like this.
|
|
const Point& p1 = facet->halfedge()->vertex()->point();
|
|
const Point& p2 = facet->halfedge()->next()->vertex()->point();
|
|
//AF: Use previous instead of next()->next()
|
|
//IOY: Done
|
|
const Point& p3 = facet->halfedge()->prev()->vertex()->point();
|
|
Point center = CGAL::centroid(p1, p2, p3);
|
|
Vector normal = CGAL::unit_normal(p2, p1,
|
|
p3); //Assuming triangles are CCW oriented.
|
|
|
|
Plane plane(center, normal);
|
|
Vector v1 = plane.base1();
|
|
Vector v2 = plane.base2();
|
|
v1 = v1 / sqrt(v1.squared_length());
|
|
v2 = v2 / sqrt(v2.squared_length());
|
|
|
|
//arrange_center_orientation(plane, normal, center);
|
|
|
|
int ray_count = number_of_rays_sqrt * number_of_rays_sqrt;
|
|
|
|
std::vector<double> ray_distances, ray_weights;
|
|
ray_distances.reserve(ray_count);
|
|
ray_weights.reserve(ray_count);
|
|
|
|
double length_of_normal = 1.0 / tan(cone_angle / 2);
|
|
normal = normal * length_of_normal;
|
|
// stores segment length,
|
|
// making it too large might cause a non-filtered bboxes in traversal,
|
|
// making it too small might cause a miss and consecutive ray casting.
|
|
// for now storing maximum found distance so far.
|
|
|
|
//#define SHOOT_ONLY_RAYS
|
|
#ifndef SHOOT_ONLY_RAYS
|
|
boost::optional<double> segment_distance;
|
|
#endif
|
|
for(Disk_samples_list::const_iterator sample_it = disk_samples.begin();
|
|
sample_it != disk_samples.end(); ++sample_it) {
|
|
bool is_intersected, intersection_is_acute;
|
|
double min_distance;
|
|
Vector disk_vector = v1 * sample_it->first + v2 * sample_it->second;
|
|
Vector ray_direction = normal + disk_vector;
|
|
|
|
#ifdef SHOOT_ONLY_RAYS
|
|
Ray ray(center, ray_direction);
|
|
boost::tie(is_intersected, intersection_is_acute,
|
|
min_distance) = cast_and_return_minimum(ray, tree, facet);
|
|
if(!intersection_is_acute) {
|
|
continue;
|
|
}
|
|
#else
|
|
// at first cast ray
|
|
if(!segment_distance) {
|
|
Ray ray(center, ray_direction);
|
|
boost::tie(is_intersected, intersection_is_acute,
|
|
min_distance) = cast_and_return_minimum(ray, tree, facet);
|
|
if(!intersection_is_acute) {
|
|
continue;
|
|
}
|
|
segment_distance = min_distance; //first assignment of the segment_distance
|
|
} else { // use segment_distance to limit rays as segments
|
|
ray_direction = ray_direction / sqrt(ray_direction.squared_length());
|
|
|
|
ray_direction = ray_direction * (*segment_distance * multiplier_for_segment);
|
|
Segment segment(center, CGAL::operator+(center, ray_direction));
|
|
boost::tie(is_intersected, intersection_is_acute,
|
|
min_distance) = cast_and_return_minimum(segment, tree, facet);
|
|
if(!is_intersected) {
|
|
//continue; // for utopia case - just continue on miss
|
|
++miss_counter;
|
|
//Ray ray(segment.target(), ray_direction);
|
|
Ray ray(segment.target(), ray_direction);
|
|
boost::tie(is_intersected, intersection_is_acute,
|
|
min_distance) = cast_and_return_minimum(ray, tree, facet);
|
|
if(!intersection_is_acute) {
|
|
continue;
|
|
}
|
|
min_distance += CGAL::sqrt(
|
|
segment.squared_length()); // since we our source is segment.target()
|
|
} else if(!intersection_is_acute) {
|
|
continue;
|
|
}
|
|
|
|
if(use_minimum_segment) {
|
|
if(min_distance <
|
|
*segment_distance) { // update segment_distance (minimum / maximum)
|
|
*segment_distance = min_distance;
|
|
}
|
|
} else {
|
|
if(min_distance >
|
|
*segment_distance) { // update segment_distance (minimum / maximum)
|
|
*segment_distance = min_distance;
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
ray_weights.push_back(sample_it->third);
|
|
ray_distances.push_back(min_distance);
|
|
}
|
|
return calculate_sdf_value_from_rays_with_trimmed_mean(ray_distances,
|
|
ray_weights);
|
|
}
|
|
|
|
// just for Ray and Segment
|
|
|
|
template <class Polyhedron>
|
|
template <class Query>
|
|
boost::tuple<bool, bool, double>
|
|
Surface_mesh_segmentation<Polyhedron>::cast_and_return_minimum(
|
|
const Query& query, const Tree& tree, const Facet_handle& facet) const
|
|
{
|
|
boost::tuple<bool, bool, double> min_distance = boost::make_tuple(false, false,
|
|
0);
|
|
std::list<Object_and_primitive_id> intersections;
|
|
#if 1
|
|
//SL: the difference with all_intersections is that in the traversal traits, we do do_intersect before calling intersection.
|
|
typedef std::back_insert_iterator< std::list<Object_and_primitive_id> >
|
|
Output_iterator;
|
|
Listing_intersection_traits_ray_or_segment_triangle<typename Tree::AABB_traits,Query,Output_iterator>
|
|
traversal_traits(std::back_inserter(intersections));
|
|
tree.traversal(query,traversal_traits);
|
|
#else
|
|
tree.all_intersections(query, std::back_inserter(intersections));
|
|
#endif
|
|
Vector min_i_ray;
|
|
Primitive_id min_id;
|
|
for(typename std::list<Object_and_primitive_id>::iterator op_it =
|
|
intersections.begin();
|
|
op_it != intersections.end() ; ++op_it) {
|
|
CGAL::Object object = op_it->first;
|
|
Primitive_id id = op_it->second;
|
|
if(id == facet) {
|
|
continue; //Since center is located on related facet, we should skip it if there is an intersection with it.
|
|
}
|
|
|
|
const Point* i_point;
|
|
//Point i_point;
|
|
//AF: Use object_cast as it is faster than assign
|
|
//IOY: ok, also using pointer-returning version to get rid of copying
|
|
if(!(i_point = CGAL::object_cast<Point>(&object))) {
|
|
continue;
|
|
}
|
|
//if(!CGAL::assign(i_point, object)) { continue; } //What to do here (in case of intersection object is a segment), I am not sure ???
|
|
|
|
Vector i_ray = (query.source() - *i_point);
|
|
double new_distance = i_ray.squared_length();
|
|
if(!min_distance.get<0>() || new_distance < min_distance.get<2>()) {
|
|
min_distance.get<2>() = new_distance;
|
|
min_distance.get<0>() = true;
|
|
min_id = id;
|
|
min_i_ray = i_ray;
|
|
}
|
|
}
|
|
if(!min_distance.get<0>()) {
|
|
return min_distance;
|
|
}
|
|
|
|
const Point& min_v1 = min_id->halfedge()->vertex()->point();
|
|
const Point& min_v2 = min_id->halfedge()->next()->vertex()->point();
|
|
const Point& min_v3 = min_id->halfedge()->prev()->vertex()->point();
|
|
Vector min_normal = CGAL::normal(min_v1, min_v2, min_v3) * -1.0;
|
|
|
|
if(CGAL::angle(CGAL::ORIGIN + min_i_ray, Point(CGAL::ORIGIN),
|
|
CGAL::ORIGIN + min_normal) != CGAL::ACUTE) {
|
|
return min_distance;
|
|
}
|
|
min_distance.get<1>() = true; // founded intersection is acceptable.
|
|
min_distance.get<2>() = sqrt(min_distance.get<2>());
|
|
return min_distance;
|
|
}
|
|
|
|
template <class Polyhedron>
|
|
template <class Query>
|
|
boost::tuple<bool, bool, double>
|
|
Surface_mesh_segmentation<Polyhedron>::cast_and_return_minimum_use_closest (
|
|
const Query& ray, const Tree& tree,
|
|
const Facet_handle& facet) const
|
|
{
|
|
//static double dist = 0.1;
|
|
//boost::optional<double> min_distance_2 = dist++;
|
|
//return min_distance_2;
|
|
|
|
boost::tuple<bool, bool, double> min_distance = boost::make_tuple(false, false,
|
|
0);
|
|
#if 1
|
|
Closest_intersection_traits<typename Tree::AABB_traits, Query> traversal_traits;
|
|
tree.traversal(ray, traversal_traits);
|
|
boost::optional<Object_and_primitive_id> intersection =
|
|
traversal_traits.result();
|
|
#else
|
|
boost::optional<Object_and_primitive_id> intersection = tree.any_intersection(
|
|
ray);
|
|
#endif
|
|
if(!intersection) {
|
|
return min_distance;
|
|
}
|
|
min_distance.get<0>() = true; // intersection is found
|
|
|
|
CGAL::Object object = intersection->first;
|
|
Primitive_id min_id = intersection->second;
|
|
|
|
if(min_id == facet) {
|
|
CGAL_error(); // There should be no such case, after center-facet arrangments.
|
|
}
|
|
|
|
const Point* i_point;
|
|
if(!(i_point = CGAL::object_cast<Point>(&object))) {
|
|
return min_distance;
|
|
}
|
|
|
|
Vector min_i_ray = ray.source() - *i_point;
|
|
const Point& min_v1 = min_id->halfedge()->vertex()->point();
|
|
const Point& min_v2 = min_id->halfedge()->next()->vertex()->point();
|
|
const Point& min_v3 = min_id->halfedge()->prev()->vertex()->point();
|
|
Vector min_normal = CGAL::normal(min_v1, min_v2, min_v3) * -1.0;
|
|
|
|
if(CGAL::angle(CGAL::ORIGIN + min_i_ray, Point(CGAL::ORIGIN),
|
|
CGAL::ORIGIN + min_normal) != CGAL::ACUTE) {
|
|
return min_distance;
|
|
}
|
|
min_distance.get<1>() = true;
|
|
min_distance.get<2>() = sqrt(min_i_ray.squared_length());
|
|
return min_distance;
|
|
}
|
|
|
|
template <class Polyhedron>
|
|
inline double
|
|
Surface_mesh_segmentation<Polyhedron>::calculate_sdf_value_from_rays(
|
|
std::vector<double>& ray_distances,
|
|
std::vector<double>& ray_weights) const
|
|
{
|
|
int accepted_ray_count = ray_distances.size();
|
|
if(accepted_ray_count == 0) {
|
|
return 0.0;
|
|
} else if(accepted_ray_count == 1) {
|
|
return ray_distances[0];
|
|
}
|
|
/* local variables */
|
|
double total_weights = 0.0, total_distance = 0.0;
|
|
double median_sdf = 0.0, mean_sdf = 0.0, st_dev = 0.0;
|
|
/* Calculate mean sdf */
|
|
std::vector<double>::iterator w_it = ray_weights.begin();
|
|
for(std::vector<double>::iterator dist_it = ray_distances.begin();
|
|
dist_it != ray_distances.end();
|
|
++dist_it, ++w_it) {
|
|
total_distance += (*dist_it) * (*w_it);
|
|
total_weights += (*w_it);
|
|
}
|
|
mean_sdf = total_distance / total_weights;
|
|
total_weights = 0.0;
|
|
total_distance = 0.0;
|
|
/* Calculate median sdf */
|
|
int half_ray_count = accepted_ray_count / 2;
|
|
std::nth_element(ray_distances.begin(), ray_distances.begin() + half_ray_count,
|
|
ray_distances.end());
|
|
if( accepted_ray_count % 2 == 0) {
|
|
double median_1 = ray_distances[half_ray_count];
|
|
double median_2 = *std::max_element(ray_distances.begin(),
|
|
ray_distances.begin() + half_ray_count);
|
|
median_sdf = (median_1 + median_2) / 2;
|
|
} else {
|
|
median_sdf = ray_distances[half_ray_count];
|
|
}
|
|
/* Calculate st dev using mean_sdf as mean */
|
|
for(std::vector<double>::iterator dist_it = ray_distances.begin();
|
|
dist_it != ray_distances.end(); ++dist_it) {
|
|
double dif = (*dist_it) - mean_sdf;
|
|
st_dev += dif * dif;
|
|
}
|
|
st_dev = sqrt(st_dev / ray_distances.size());
|
|
/* Calculate sdf, accept rays : ray_dist - median < st dev */
|
|
w_it = ray_weights.begin();
|
|
for(std::vector<double>::iterator dist_it = ray_distances.begin();
|
|
dist_it != ray_distances.end();
|
|
++dist_it, ++w_it) {
|
|
// AF: replace fabs with CGAL::abs
|
|
if(CGAL::abs((*dist_it) - median_sdf) > st_dev) {
|
|
continue;
|
|
}
|
|
total_distance += (*dist_it) * (*w_it);
|
|
total_weights += (*w_it);
|
|
}
|
|
return total_distance / total_weights;
|
|
}
|
|
|
|
template <class Polyhedron>
|
|
inline double
|
|
Surface_mesh_segmentation<Polyhedron>::calculate_sdf_value_from_rays_with_mean(
|
|
std::vector<double>& ray_distances,
|
|
std::vector<double>& ray_weights) const
|
|
{
|
|
double total_weights = 0.0, total_distance = 0.0;
|
|
double mean_sdf = 0.0, st_dev = 0.0;
|
|
std::vector<double>::iterator w_it = ray_weights.begin();
|
|
for(std::vector<double>::iterator dist_it = ray_distances.begin();
|
|
dist_it != ray_distances.end(); ++dist_it, ++w_it) {
|
|
total_distance += (*dist_it) * (*w_it);
|
|
total_weights += (*w_it);
|
|
}
|
|
mean_sdf = total_distance / total_weights;
|
|
total_weights = 0.0;
|
|
total_distance = 0.0;
|
|
for(std::vector<double>::iterator dist_it = ray_distances.begin();
|
|
dist_it != ray_distances.end(); ++dist_it) {
|
|
double dif = (*dist_it) - mean_sdf;
|
|
st_dev += dif * dif;
|
|
}
|
|
st_dev = sqrt(st_dev / ray_distances.size());
|
|
|
|
w_it = ray_weights.begin();
|
|
for(std::vector<double>::iterator dist_it = ray_distances.begin();
|
|
dist_it != ray_distances.end(); ++dist_it, ++w_it) {
|
|
if(CGAL::abs((*dist_it) - mean_sdf) > st_dev) {
|
|
continue;
|
|
}
|
|
total_distance += (*dist_it) * (*w_it);
|
|
total_weights += (*w_it);
|
|
}
|
|
return total_distance / total_weights;
|
|
}
|
|
|
|
template <class Polyhedron>
|
|
inline double
|
|
Surface_mesh_segmentation<Polyhedron>::calculate_sdf_value_from_rays_with_trimmed_mean(
|
|
std::vector<double>& ray_distances,
|
|
std::vector<double>& ray_weights) const
|
|
{
|
|
std::vector<std::pair<double, double> > distances_with_weights;
|
|
distances_with_weights.reserve(ray_distances.size());
|
|
std::vector<double>::iterator w_it = ray_weights.begin();
|
|
for(std::vector<double>::iterator dist_it = ray_distances.begin();
|
|
dist_it != ray_distances.end(); ++dist_it, ++w_it) {
|
|
distances_with_weights.push_back(std::pair<double, double>(*w_it, *dist_it));
|
|
}
|
|
std::sort(distances_with_weights.begin(), distances_with_weights.end(),
|
|
Compare_second_element<std::pair<double, double> >());
|
|
int b = static_cast<int>(distances_with_weights.size() / 20.0 +
|
|
0.5); // Eliminate %5.
|
|
int e = distances_with_weights.size() - b; // Eliminate %5.
|
|
|
|
double total_weights = 0.0, total_distance = 0.0;
|
|
double trimmed_mean = 0.0, st_dev = 0.0;
|
|
|
|
for(int i = b; i < e; ++i) {
|
|
total_distance += distances_with_weights[i].first *
|
|
distances_with_weights[i].second;
|
|
total_weights += distances_with_weights[i].first;
|
|
}
|
|
trimmed_mean = total_distance / total_weights;
|
|
|
|
total_weights = 0.0;
|
|
total_distance = 0.0;
|
|
for(std::vector<double>::iterator dist_it = ray_distances.begin();
|
|
dist_it != ray_distances.end(); ++dist_it) {
|
|
double dif = (*dist_it) - trimmed_mean;
|
|
st_dev += dif * dif;
|
|
}
|
|
st_dev = sqrt(st_dev / ray_distances.size());
|
|
|
|
w_it = ray_weights.begin();
|
|
for(std::vector<double>::iterator dist_it = ray_distances.begin();
|
|
dist_it != ray_distances.end(); ++dist_it, ++w_it) {
|
|
if(CGAL::abs((*dist_it) - trimmed_mean) > st_dev) {
|
|
continue;
|
|
}
|
|
total_distance += (*dist_it) * (*w_it);
|
|
total_weights += (*w_it);
|
|
}
|
|
return total_distance / total_weights;
|
|
}
|
|
|
|
/* Slightly move center towards inverse normal of facet.
|
|
* Parameter plane is constructed by inverse normal.
|
|
*/
|
|
template <class Polyhedron>
|
|
void Surface_mesh_segmentation<Polyhedron>::arrange_center_orientation(
|
|
const Plane& plane, const Vector& unit_normal, Point& center) const
|
|
{
|
|
/*
|
|
* Not sure how to decide on how much I should move center ?
|
|
*/
|
|
double epsilon = 1e-8;
|
|
// Option-1
|
|
//if(plane.has_on_positive_side(center)) { return; }
|
|
//Vector epsilon_normal = unit_normal * epsilon;
|
|
//do {
|
|
// center = CGAL::operator+(center, epsilon_normal);
|
|
//} while(!plane.has_on_positive_side(center));
|
|
|
|
//Option-2
|
|
//if(plane.has_on_positive_side(center)) { return; }
|
|
//double distance = sqrt(CGAL::squared_distance(plane, center));
|
|
//distance = distance > epsilon ? (distance + epsilon) : epsilon;
|
|
//Vector distance_normal = unit_normal * distance;
|
|
//
|
|
//do {
|
|
// center = CGAL::operator+(center, distance_normal);
|
|
//} while(!plane.has_on_positive_side(center));
|
|
|
|
//Option-3
|
|
Ray ray(center, unit_normal);
|
|
typename Kernel::Do_intersect_3 intersector = Kernel().do_intersect_3_object();
|
|
if(!intersector(ray, plane)) {
|
|
return;
|
|
}
|
|
|
|
Vector epsilon_normal = unit_normal * epsilon;
|
|
do {
|
|
center = CGAL::operator+(center, epsilon_normal);
|
|
ray = Ray(center, unit_normal);
|
|
} while(intersector(ray, plane));
|
|
|
|
}
|
|
|
|
template <class Polyhedron>
|
|
void Surface_mesh_segmentation<Polyhedron>::calculate_dihedral_angles()
|
|
{
|
|
for(Edge_iterator edge_it = mesh->edges_begin(); edge_it != mesh->edges_end();
|
|
++edge_it) {
|
|
double angle = calculate_dihedral_angle_of_edge(edge_it);
|
|
dihedral_angles.insert(std::pair<Halfedge_handle, double>(edge_it, angle));
|
|
}
|
|
}
|
|
|
|
// if concave then returns angle value between [epsilon - 1] which corresponds to angle [0 - Pi]
|
|
// if convex then returns epsilon directly.
|
|
template <class Polyhedron>
|
|
double Surface_mesh_segmentation<Polyhedron>::calculate_dihedral_angle_of_edge(
|
|
const Halfedge_handle& edge) const
|
|
{
|
|
double epsilon = 1e-8; // not sure but should not return zero for log(angle)...
|
|
Facet_handle f1 = edge->facet();
|
|
Facet_handle f2 = edge->opposite()->facet();
|
|
|
|
const Point& f2_v1 = f2->halfedge()->vertex()->point();
|
|
const Point& f2_v2 = f2->halfedge()->next()->vertex()->point();
|
|
const Point& f2_v3 = f2->halfedge()->prev()->vertex()->point();
|
|
/**
|
|
* As far as I see from results, segment boundaries are occurred in 'concave valleys'.
|
|
* There is no such thing written (clearly) in the paper but should we just penalize 'concave' edges (not convex edges) ?
|
|
* Actually that is what I understood from 'positive dihedral angle'.
|
|
*/
|
|
const Point& unshared_point_on_f1 = edge->next()->vertex()->point();
|
|
Plane p2(f2_v1, f2_v2, f2_v3);
|
|
bool concave = p2.has_on_positive_side(unshared_point_on_f1);
|
|
if(!concave) {
|
|
return epsilon; // So no penalties for convex dihedral angle ? Not sure...
|
|
}
|
|
|
|
const Point& f1_v1 = f1->halfedge()->vertex()->point();
|
|
const Point& f1_v2 = f1->halfedge()->next()->vertex()->point();
|
|
const Point& f1_v3 = f1->halfedge()->prev()->vertex()->point();
|
|
Vector f1_normal = CGAL::unit_normal(f1_v1, f1_v2, f1_v3);
|
|
Vector f2_normal = CGAL::unit_normal(f2_v1, f2_v2, f2_v3);
|
|
|
|
double dot = f1_normal * f2_normal;
|
|
if(dot > 1.0) {
|
|
dot = 1.0;
|
|
} else if(dot < -1.0) {
|
|
dot = -1.0;
|
|
}
|
|
double angle = acos(dot) / CGAL_PI; // [0-1] normalize
|
|
if(angle < epsilon) {
|
|
angle = epsilon;
|
|
}
|
|
return angle;
|
|
}
|
|
|
|
template <class Polyhedron>
|
|
double Surface_mesh_segmentation<Polyhedron>::calculate_dihedral_angle_of_edge_2(
|
|
const Halfedge_handle& edge) const
|
|
{
|
|
double epsilon = 1e-8; // not sure but should not return zero for log(angle)...
|
|
const Point& a = edge->vertex()->point();
|
|
const Point& b = edge->prev()->vertex()->point();
|
|
const Point& c = edge->next()->vertex()->point();
|
|
const Point& d = edge->opposite()->next()->vertex()->point();
|
|
// As far as I check: if, say, dihedral angle is 5, this returns 175,
|
|
// if dihedral angle is -5, this returns -175.
|
|
double n_angle = CGAL::Mesh_3::dihedral_angle(a, b, c, d) / 180.0;
|
|
bool n_concave = n_angle > 0;
|
|
double folded_angle = 1 + ((n_concave ? -1 : +1) * n_angle);
|
|
folded_angle = (CGAL::max)(folded_angle, epsilon);
|
|
|
|
if(!n_concave) {
|
|
return epsilon; // we may want to also penalize convex angles as well...
|
|
}
|
|
return folded_angle;
|
|
|
|
//Facet_handle f1 = edge->facet();
|
|
//Facet_handle f2 = edge->opposite()->facet();
|
|
//
|
|
//const Point& f2_v1 = f2->halfedge()->vertex()->point();
|
|
//const Point& f2_v2 = f2->halfedge()->next()->vertex()->point();
|
|
//const Point& f2_v3 = f2->halfedge()->prev()->vertex()->point();
|
|
///**
|
|
// * As far as I see from results, segment boundaries are occurred in 'concave valleys'.
|
|
// * There is no such thing written (clearly) in the paper but should we just penalize 'concave' edges (not convex edges) ?
|
|
// * Actually that is what I understood from 'positive dihedral angle'.
|
|
// */
|
|
//const Point& unshared_point_on_f1 = edge->next()->vertex()->point();
|
|
//Plane p2(f2_v1, f2_v2, f2_v3);
|
|
//bool concave = p2.has_on_positive_side(unshared_point_on_f1);
|
|
////std::cout << n_angle << std::endl;
|
|
////if(!concave) { return epsilon; } // So no penalties for convex dihedral angle ? Not sure...
|
|
|
|
//const Point& f1_v1 = f1->halfedge()->vertex()->point();
|
|
//const Point& f1_v2 = f1->halfedge()->next()->vertex()->point();
|
|
//const Point& f1_v3 = f1->halfedge()->prev()->vertex()->point();
|
|
//Vector f1_normal = CGAL::unit_normal(f1_v1, f1_v2, f1_v3);
|
|
//Vector f2_normal = CGAL::unit_normal(f2_v1, f2_v2, f2_v3);
|
|
//
|
|
//double dot = f1_normal * f2_normal;
|
|
//if(dot > 1.0) { dot = 1.0; }
|
|
//else if(dot < -1.0) { dot = -1.0; }
|
|
//double angle = acos(dot) / CGAL_PI; // [0-1] normalize
|
|
//std::cout << angle << " " << n_angle << " " << (concave ? "concave": "convex") << std::endl;
|
|
//if(fabs(angle - folded_angle) > 1e-6)
|
|
//{
|
|
//
|
|
//}
|
|
//if(angle < epsilon) { angle = epsilon; }
|
|
//return angle;
|
|
}
|
|
|
|
template <class Polyhedron>
|
|
inline void Surface_mesh_segmentation<Polyhedron>::disk_sampling_rejection()
|
|
{
|
|
int number_of_points_sqrt = number_of_rays_sqrt;
|
|
double length_of_normal = 1.0 / tan(cone_angle / 2);
|
|
double mid_point = (number_of_points_sqrt-1) / 2.0;
|
|
double angle_st_dev = cone_angle / CGAL_ANGLE_ST_DEV_DIVIDER;
|
|
|
|
for(int i = 0; i < number_of_points_sqrt; ++i)
|
|
for(int j = 0; j < number_of_points_sqrt; ++j) {
|
|
double w1 = (i - mid_point)/(mid_point);
|
|
double w2 = (j - mid_point)/(mid_point);
|
|
double R = sqrt(w1*w1 + w2*w2);
|
|
if(R > 1.0) {
|
|
continue;
|
|
}
|
|
double angle = atan(R / length_of_normal);
|
|
angle = exp(-0.5 * (pow(angle / angle_st_dev, 2))); // weight
|
|
disk_samples.push_back(Disk_sample(w1, w2, angle));
|
|
}
|
|
}
|
|
|
|
template <class Polyhedron>
|
|
inline void Surface_mesh_segmentation<Polyhedron>::disk_sampling_polar_mapping()
|
|
{
|
|
int number_of_points_sqrt = number_of_rays_sqrt;
|
|
double length_of_normal = 1.0 / tan(cone_angle / 2);
|
|
double angle_st_dev = cone_angle / CGAL_ANGLE_ST_DEV_DIVIDER;
|
|
|
|
for(int i = 0; i < number_of_points_sqrt; ++i)
|
|
for(int j = 0; j < number_of_points_sqrt; ++j) {
|
|
double w1 = i / (double) (number_of_points_sqrt-1);
|
|
double w2 = j / (double) (number_of_points_sqrt-1);
|
|
double R = w1;
|
|
double Q = 2 * w1 * CGAL_PI;
|
|
double angle = atan(R / length_of_normal);
|
|
angle = exp(-0.5 * (pow(angle / angle_st_dev, 2))); // weight
|
|
disk_samples.push_back(Disk_sample(R * cos(Q), R * sin(Q), angle));
|
|
}
|
|
}
|
|
|
|
template <class Polyhedron>
|
|
inline void
|
|
Surface_mesh_segmentation<Polyhedron>::disk_sampling_concentric_mapping()
|
|
{
|
|
int number_of_points_sqrt = number_of_rays_sqrt;
|
|
double length_of_normal = 1.0 / tan(cone_angle / 2);
|
|
double fraction = 2.0 / (number_of_points_sqrt -1);
|
|
double angle_st_dev = cone_angle / CGAL_ANGLE_ST_DEV_DIVIDER;
|
|
|
|
for(int i = 0; i < number_of_points_sqrt; ++i)
|
|
for(int j = 0; j < number_of_points_sqrt; ++j) {
|
|
double w1 = -1 + i * fraction;
|
|
double w2 = -1 + j * fraction;
|
|
double R, Q;
|
|
if(w1 == 0 && w2 == 0) {
|
|
R = 0;
|
|
Q = 0;
|
|
} else if(w1 > -w2) {
|
|
if(w1 > w2) {
|
|
R = w1;
|
|
Q = (w2 / w1);
|
|
} else {
|
|
R = w2;
|
|
Q = (2 - w1 / w2);
|
|
}
|
|
} else {
|
|
if(w1 < w2) {
|
|
R = -w1;
|
|
Q = (4 + w2 / w1);
|
|
} else {
|
|
R = -w2;
|
|
Q = (6 - w1 / w2);
|
|
}
|
|
}
|
|
Q *= (0.25 * CGAL_PI);
|
|
double angle = atan(R / length_of_normal);
|
|
angle = exp(-0.5 * (pow(angle / angle_st_dev, 2))); // weight
|
|
disk_samples.push_back(Disk_sample(R * cos(Q), R * sin(Q), angle));
|
|
}
|
|
//Sorting sampling
|
|
//The sampling that are closer to center comes first.
|
|
//More detailed: weights are inverse proportional with distance to center,
|
|
//use weights to sort in descending order.
|
|
std::sort(disk_samples.begin(), disk_samples.end(),
|
|
Compare_third_element<CGAL::Triple<double, double, double> >());
|
|
}
|
|
|
|
|
|
template <class Polyhedron>
|
|
inline void Surface_mesh_segmentation<Polyhedron>::normalize_sdf_values()
|
|
{
|
|
//SL: use CGAL::min_max_element //IOY: done.
|
|
typedef typename Face_value_map::iterator fv_iterator;
|
|
Compare_second_element<typename Face_value_map::value_type> comparator;
|
|
std::pair<fv_iterator, fv_iterator> min_max_pair =
|
|
CGAL::min_max_element(sdf_values.begin(), sdf_values.end(), comparator,
|
|
comparator);
|
|
|
|
double max_value = min_max_pair.second->second,
|
|
min_value = min_max_pair.first->second;
|
|
double max_min_dif = max_value - min_value;
|
|
for(typename Face_value_map::iterator pair_it = sdf_values.begin();
|
|
pair_it != sdf_values.end(); ++pair_it) {
|
|
double linear_normalized = (pair_it->second - min_value) / max_min_dif;
|
|
double log_normalized = log(linear_normalized * CGAL_NORMALIZATION_ALPHA + 1) /
|
|
CGAL_LOG_5;
|
|
pair_it->second = log_normalized;
|
|
}
|
|
}
|
|
|
|
template <class Polyhedron>
|
|
inline void Surface_mesh_segmentation<Polyhedron>::smooth_sdf_values()
|
|
{
|
|
Face_value_map smoothed_sdf_values;
|
|
for(typename Face_value_map::iterator pair_it = sdf_values.begin();
|
|
pair_it != sdf_values.end(); ++pair_it) {
|
|
Facet_handle f = pair_it->first;
|
|
typename Facet::Halfedge_around_facet_circulator facet_circulator =
|
|
f->facet_begin();
|
|
double total_neighbor_sdf = 0.0;
|
|
do {
|
|
total_neighbor_sdf += sdf_values[facet_circulator->opposite()->facet()];
|
|
} while( ++facet_circulator != f->facet_begin());
|
|
total_neighbor_sdf /= 3;
|
|
smoothed_sdf_values[f] = (pair_it->second + total_neighbor_sdf) / 2;
|
|
}
|
|
sdf_values = smoothed_sdf_values;
|
|
}
|
|
|
|
template <class Polyhedron>
|
|
inline void Surface_mesh_segmentation<Polyhedron>::apply_GMM_fitting()
|
|
{
|
|
centers.clear();
|
|
std::vector<double> sdf_vector;
|
|
sdf_vector.reserve(sdf_values.size());
|
|
for(typename Face_value_map::iterator pair_it = sdf_values.begin();
|
|
pair_it != sdf_values.end(); ++pair_it) {
|
|
sdf_vector.push_back(pair_it->second);
|
|
}
|
|
SEG_DEBUG(CGAL::Timer t)
|
|
SEG_DEBUG(t.start())
|
|
// apply em with 5 runs, number of runs might become a parameter.
|
|
internal::Expectation_maximization fitter(number_of_centers, sdf_vector, 10);
|
|
SEG_DEBUG(std::cout << t.time() << std::endl)
|
|
std::vector<int> center_memberships;
|
|
fitter.fill_with_center_ids(center_memberships);
|
|
std::vector<int>::iterator center_it = center_memberships.begin();
|
|
for(typename Face_value_map::iterator pair_it = sdf_values.begin();
|
|
pair_it != sdf_values.end(); ++pair_it, ++center_it) {
|
|
centers.insert(std::pair<Facet_handle, int>(pair_it->first, (*center_it)));
|
|
}
|
|
}
|
|
|
|
template <class Polyhedron>
|
|
inline void Surface_mesh_segmentation<Polyhedron>::apply_K_means_clustering()
|
|
{
|
|
centers.clear();
|
|
std::vector<double> sdf_vector;
|
|
sdf_vector.reserve(sdf_values.size());
|
|
for(typename Face_value_map::iterator pair_it = sdf_values.begin();
|
|
pair_it != sdf_values.end(); ++pair_it) {
|
|
sdf_vector.push_back(pair_it->second);
|
|
}
|
|
internal::K_means_clustering clusterer(number_of_centers, sdf_vector);
|
|
std::vector<int> center_memberships;
|
|
clusterer.fill_with_center_ids(center_memberships);
|
|
std::vector<int>::iterator center_it = center_memberships.begin();
|
|
for(typename Face_value_map::iterator pair_it = sdf_values.begin();
|
|
pair_it != sdf_values.end(); ++pair_it, ++center_it) {
|
|
centers.insert(std::pair<Facet_handle, int>(pair_it->first, (*center_it)));
|
|
}
|
|
//center_memberships_temp = center_memberships; //remove
|
|
}
|
|
template <class Polyhedron>
|
|
inline void
|
|
Surface_mesh_segmentation<Polyhedron>::apply_GMM_fitting_with_K_means_init()
|
|
{
|
|
centers.clear();
|
|
std::vector<double> sdf_vector;
|
|
sdf_vector.reserve(sdf_values.size());
|
|
for(typename Face_value_map::iterator pair_it = sdf_values.begin();
|
|
pair_it != sdf_values.end(); ++pair_it) {
|
|
sdf_vector.push_back(pair_it->second);
|
|
}
|
|
internal::K_means_clustering clusterer(number_of_centers, sdf_vector);
|
|
std::vector<int> center_memberships;
|
|
clusterer.fill_with_center_ids(center_memberships);
|
|
//std::vector<int> center_memberships = center_memberships_temp;
|
|
internal::Expectation_maximization fitter(number_of_centers, sdf_vector,
|
|
center_memberships);
|
|
center_memberships.clear();
|
|
fitter.fill_with_center_ids(center_memberships);
|
|
std::vector<int>::iterator center_it = center_memberships.begin();
|
|
for(typename Face_value_map::iterator pair_it = sdf_values.begin();
|
|
pair_it != sdf_values.end(); ++pair_it, ++center_it) {
|
|
centers.insert(std::pair<Facet_handle, int>(pair_it->first, (*center_it)));
|
|
}
|
|
}
|
|
|
|
template <class Polyhedron>
|
|
void Surface_mesh_segmentation<Polyhedron>::apply_graph_cut()
|
|
{
|
|
//assign an id for every facet (facet-id)
|
|
std::map<Facet_handle, int> facet_indices;
|
|
int index = 0;
|
|
for(Facet_iterator facet_it = mesh->facets_begin();
|
|
facet_it != mesh->facets_end();
|
|
++facet_it, ++index) {
|
|
facet_indices.insert(std::pair<Facet_handle, int>(facet_it, index));
|
|
}
|
|
//edges and their weights. pair<int, int> stores facet-id pairs (see above) (may be using CGAL::Triple can be more suitable)
|
|
std::vector<std::pair<int, int> > edges;
|
|
std::vector<double> edge_weights;
|
|
for(Edge_iterator edge_it = mesh->edges_begin(); edge_it != mesh->edges_end();
|
|
++edge_it) {
|
|
double angle = calculate_dihedral_angle_of_edge(edge_it);
|
|
int index_f1 = facet_indices[edge_it->facet()];
|
|
int index_f2 = facet_indices[edge_it->opposite()->facet()];
|
|
edges.push_back(std::pair<int, int>(index_f1, index_f2));
|
|
angle = -log(angle);
|
|
angle *= 15; //lambda, will be variable.
|
|
// we may also want to consider edge lengths, also penalize convex angles.
|
|
edge_weights.push_back(angle);
|
|
}
|
|
//apply gmm fitting
|
|
std::vector<double> sdf_vector;
|
|
sdf_vector.reserve(sdf_values.size());
|
|
for(typename Face_value_map::iterator pair_it = sdf_values.begin();
|
|
pair_it != sdf_values.end(); ++pair_it) {
|
|
sdf_vector.push_back(pair_it->second);
|
|
}
|
|
internal::Expectation_maximization fitter(number_of_centers, sdf_vector, 50);
|
|
//fill probability matrix.
|
|
std::vector<std::vector<double> > probability_matrix;
|
|
fitter.fill_with_minus_log_probabilities(probability_matrix);
|
|
std::vector<int> labels;
|
|
fitter.fill_with_center_ids(labels);
|
|
|
|
//apply graph cut
|
|
std::vector<int> center_ids;
|
|
internal::Alpha_expansion_graph_cut gc(edges, edge_weights, labels,
|
|
probability_matrix, center_ids);
|
|
|
|
std::vector<int>::iterator center_it = center_ids.begin();
|
|
centers.clear();
|
|
for(Facet_iterator facet_it = mesh->facets_begin();
|
|
facet_it != mesh->facets_end();
|
|
++facet_it, ++center_it) {
|
|
centers.insert(std::pair<Facet_handle, int>(facet_it, (*center_it)));
|
|
}
|
|
}
|
|
|
|
//AF: it is not common in CGAL to have functions with a file name as argument
|
|
//IOY: Yes, I am just using these read-write functions in development phase,
|
|
// in order to not to compute sdf-values everytime program runs.
|
|
// They will be removed at the end, or I will migrate them into main.
|
|
template <class Polyhedron>
|
|
inline void Surface_mesh_segmentation<Polyhedron>::write_sdf_values(
|
|
const char* file_name)
|
|
{
|
|
std::ofstream output(file_name);
|
|
for(Facet_iterator facet_it = mesh->facets_begin();
|
|
facet_it != mesh->facets_end(); ++facet_it) {
|
|
output << sdf_values[facet_it] << std::endl;
|
|
}
|
|
output.close();
|
|
}
|
|
|
|
template <class Polyhedron>
|
|
inline void Surface_mesh_segmentation<Polyhedron>::read_sdf_values(
|
|
const char* file_name)
|
|
{
|
|
std::ifstream input(file_name);
|
|
sdf_values.clear();
|
|
for(Facet_iterator facet_it = mesh->facets_begin();
|
|
facet_it != mesh->facets_end(); ++facet_it) {
|
|
double sdf_value;
|
|
input >> sdf_value;
|
|
sdf_values.insert(std::pair<Facet_handle, double>(facet_it, sdf_value));
|
|
}
|
|
}
|
|
|
|
template <class Polyhedron>
|
|
inline void Surface_mesh_segmentation<Polyhedron>::read_center_ids(
|
|
const char* file_name)
|
|
{
|
|
std::ifstream input(file_name);
|
|
centers.clear();
|
|
int max_center = 0;
|
|
for(Facet_iterator facet_it = mesh->facets_begin();
|
|
facet_it != mesh->facets_end(); ++facet_it) {
|
|
int center_id;
|
|
input >> center_id;
|
|
centers.insert(std::pair<Facet_handle, int>(facet_it, center_id));
|
|
if(center_id > max_center) {
|
|
max_center = center_id;
|
|
}
|
|
}
|
|
number_of_centers = max_center + 1;
|
|
}
|
|
|
|
|
|
template <class Polyhedron>
|
|
inline void Surface_mesh_segmentation<Polyhedron>::profile(
|
|
const char* file_name)
|
|
{
|
|
|
|
#ifdef SEGMENTATION_PROFILE
|
|
typedef Listing_intersection_traits_ray_or_segment_triangle
|
|
< typename Tree::AABB_traits, Ray, std::back_insert_iterator< std::list<Object_and_primitive_id> > >
|
|
Traits_with_ray;
|
|
typedef Listing_intersection_traits_ray_or_segment_triangle
|
|
< typename Tree::AABB_traits, Segment, std::back_insert_iterator< std::list<Object_and_primitive_id> > >
|
|
Traits_with_segment;
|
|
std::ofstream output(file_name);
|
|
|
|
//for minimum
|
|
for(int i = 0; i < 5; ++i) {
|
|
use_minimum_segment = true;
|
|
multiplier_for_segment = 1.0 + i;
|
|
|
|
CGAL::Timer t;
|
|
t.start();
|
|
calculate_sdf_values();
|
|
|
|
double time = t.time();
|
|
long inter_counter = Traits_with_ray::inter_counter +
|
|
Traits_with_segment::inter_counter;
|
|
long true_inter_counter = Traits_with_ray::true_inter_counter +
|
|
Traits_with_segment::true_inter_counter;
|
|
long do_inter_counter = Traits_with_ray::do_inter_counter +
|
|
Traits_with_segment::do_inter_counter;
|
|
long do_inter_in_segment = Traits_with_segment::do_inter_counter;
|
|
long do_inter_in_ray = Traits_with_ray::do_inter_counter;
|
|
|
|
output << "time: " << time << std::endl;
|
|
output << "how many times intersecion(query, primitive) is called: " <<
|
|
inter_counter << std::endl;
|
|
output << "how many times intersecion(query, primitive) returned true: " <<
|
|
true_inter_counter << std::endl;
|
|
output << "how many nodes are visited in segment casting: " <<
|
|
do_inter_in_segment << std::endl;
|
|
output << "how many nodes are visited in ray casting: " << do_inter_in_ray <<
|
|
std::endl;
|
|
output << "how many nodes are visited: " << do_inter_counter << std::endl;
|
|
output << "how many miss occured: " << miss_counter << std::endl;
|
|
|
|
//reset
|
|
miss_counter = 0;
|
|
Traits_with_ray::inter_counter = 0;
|
|
Traits_with_segment::inter_counter = 0;
|
|
Traits_with_ray::true_inter_counter = 0;
|
|
Traits_with_segment::true_inter_counter = 0;
|
|
Traits_with_ray::do_inter_counter = 0;
|
|
Traits_with_segment::do_inter_counter = 0;
|
|
}
|
|
//for maximum
|
|
for(int i = 0; i < 5; ++i) {
|
|
use_minimum_segment = false;
|
|
multiplier_for_segment = 1.0 + i * 0.2;
|
|
|
|
CGAL::Timer t;
|
|
t.start();
|
|
calculate_sdf_values();
|
|
|
|
double time = t.time();
|
|
long inter_counter = Traits_with_ray::inter_counter +
|
|
Traits_with_segment::inter_counter;
|
|
long true_inter_counter = Traits_with_ray::true_inter_counter +
|
|
Traits_with_segment::true_inter_counter;
|
|
long do_inter_counter = Traits_with_ray::do_inter_counter +
|
|
Traits_with_segment::do_inter_counter;
|
|
long do_inter_in_segment = Traits_with_segment::do_inter_counter;
|
|
long do_inter_in_ray = Traits_with_ray::do_inter_counter;
|
|
|
|
output << "time: " << time << std::endl;
|
|
output << "how many times intersecion(query, primitive) is called: " <<
|
|
inter_counter << std::endl;
|
|
output << "how many times intersecion(query, primitive) returned true: " <<
|
|
true_inter_counter << std::endl;
|
|
output << "how many nodes are visited in segment casting: " <<
|
|
do_inter_in_segment << std::endl;
|
|
output << "how many nodes are visited in ray casting: " << do_inter_in_ray <<
|
|
std::endl;
|
|
output << "how many nodes are visited: " << do_inter_counter << std::endl;
|
|
output << "how many miss occured: " << miss_counter << std::endl;
|
|
|
|
//reset
|
|
miss_counter = 0;
|
|
Traits_with_ray::inter_counter = 0;
|
|
Traits_with_segment::inter_counter = 0;
|
|
Traits_with_ray::true_inter_counter = 0;
|
|
Traits_with_segment::true_inter_counter = 0;
|
|
Traits_with_ray::do_inter_counter = 0;
|
|
Traits_with_segment::do_inter_counter = 0;
|
|
}
|
|
output.close();
|
|
#endif
|
|
}
|
|
|
|
} //namespace CGAL
|
|
#undef CGAL_ANGLE_ST_DEV_DIVIDER
|
|
#undef CGAL_LOG_5
|
|
#undef CGAL_NORMALIZATION_ALPHA
|
|
|
|
#ifdef SEG_DEBUG
|
|
#undef SEG_DEBUG
|
|
#endif
|
|
#endif //CGAL_SURFACE_MESH_SEGMENTATION_H
|