mirror of https://github.com/CGAL/cgal
848 lines
34 KiB
C++
848 lines
34 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>
|
|
#include <CGAL/internal/Surface_mesh_segmentation/SDF_calculation.h>
|
|
|
|
#include <CGAL/utility.h>
|
|
#include <CGAL/Timer.h>
|
|
#include <CGAL/Mesh_3/dihedral_angle_3.h>
|
|
|
|
#include <boost/optional.hpp>
|
|
#include <boost/property_map/property_map.hpp>
|
|
|
|
#include <iostream>
|
|
#include <fstream>
|
|
#include <cmath>
|
|
#include <vector>
|
|
#include <algorithm>
|
|
#include <utility>
|
|
#include <queue>
|
|
#include <map>
|
|
//AF: macros must be prefixed with "CGAL_"
|
|
//IOY: done
|
|
#define CGAL_NORMALIZATION_ALPHA 5.0
|
|
#define CGAL_CONVEX_FACTOR 0.08
|
|
#define CGAL_DEFAULT_NUMBER_OF_CLUSTERS 5
|
|
#define CGAL_DEFAULT_SMOOTHING_LAMBDA 23.0
|
|
#define CGAL_DEFAULT_CONE_ANGLE (2.0 / 3.0) * CGAL_PI
|
|
#define CGAL_DEFAULT_NUMBER_OF_RAYS 25
|
|
//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
|
|
{
|
|
|
|
/**
|
|
* It is a connector class which uses soft clustering and graph cut in order to segment meshes.
|
|
* All preprocessing and postprocessing issues are handled here.
|
|
*/
|
|
template <
|
|
class Polyhedron,
|
|
class FacetIndexMap =
|
|
boost::associative_property_map<std::map<typename Polyhedron::Facet_const_iterator, int> >
|
|
>
|
|
class Surface_mesh_segmentation
|
|
{
|
|
//type definitions
|
|
public:
|
|
typedef typename Polyhedron::Traits Kernel;
|
|
typedef typename Polyhedron::Facet Facet;
|
|
typedef typename Polyhedron::Facet Vertex;
|
|
typedef typename Kernel::Vector_3 Vector;
|
|
typedef typename Kernel::Point_3 Point;
|
|
|
|
typedef typename Polyhedron::Edge_const_iterator Edge_const_iterator;
|
|
typedef typename Polyhedron::Halfedge_const_iterator Halfedge_const_iterator;
|
|
typedef typename Polyhedron::Facet_const_iterator Facet_const_iterator;
|
|
typedef typename Polyhedron::Vertex_const_iterator Vertex_const_iterator;
|
|
|
|
typedef internal::SDF_calculation<Polyhedron> SDF_calculation;
|
|
|
|
protected:
|
|
typedef typename Kernel::Plane_3 Plane;
|
|
|
|
// member variables
|
|
public: // going to be protected !
|
|
const Polyhedron& mesh;
|
|
|
|
FacetIndexMap facet_index_map;
|
|
std::map<Facet_const_iterator, int> facet_index_map_internal;
|
|
|
|
std::vector<double> sdf_values;
|
|
std::vector<int> centers;
|
|
std::vector<int> segments;
|
|
// member functions
|
|
bool is_pmmap_custom;
|
|
public:
|
|
Surface_mesh_segmentation(const Polyhedron& mesh)
|
|
: mesh(mesh), facet_index_map(facet_index_map_internal),
|
|
is_pmmap_custom(false) {
|
|
int facet_index = 0;
|
|
for(Facet_const_iterator facet_it = mesh.facets_begin();
|
|
facet_it != mesh.facets_end();
|
|
++facet_it, ++facet_index) {
|
|
boost::put(facet_index_map, facet_it, facet_index);
|
|
}
|
|
}
|
|
|
|
Surface_mesh_segmentation(const Polyhedron& mesh, FacetIndexMap facet_index_map)
|
|
: mesh(mesh), facet_index_map(facet_index_map), is_pmmap_custom(true) {
|
|
}
|
|
|
|
// Copy-constructor
|
|
// Default, we store facet-to-id in property-map adaptor 'FacetIndexMap'.
|
|
// It stores a pointer to std::map instance which is a member variable.
|
|
// So default copy constructor is not working since copied property-map adaptor still
|
|
// points to std::map instance which exists in copied object (rhs).
|
|
Surface_mesh_segmentation(const
|
|
Surface_mesh_segmentation<Polyhedron, FacetIndexMap>& rhs):
|
|
mesh(rhs.mesh), sdf_values(rhs.sdf_values), centers(rhs.centers),
|
|
segments(rhs.segments),
|
|
is_pmmap_custom(rhs.is_pmmap_custom),
|
|
facet_index_map_internal(rhs.facet_index_map_internal) {
|
|
if(!is_pmmap_custom) {
|
|
facet_index_map = FacetIndexMap(facet_index_map_internal);
|
|
}
|
|
}
|
|
|
|
// Use another copy of polyhedron but also copy the state of rhs.
|
|
// Main difference between copy constructor is rhs.mesh is NOT equal to this->mesh.
|
|
// So we cannot copy facet_index_map_internal since facet_iterators of 'this' is different than rhs.
|
|
Surface_mesh_segmentation(const Polyhedron& mesh,
|
|
const Surface_mesh_segmentation<Polyhedron, FacetIndexMap>& rhs)
|
|
: mesh(mesh), sdf_values(rhs.sdf_values), centers(rhs.centers),
|
|
segments(rhs.segments),
|
|
is_pmmap_custom(rhs.is_pmmap_custom) {
|
|
if(!is_pmmap_custom) {
|
|
facet_index_map = FacetIndexMap(facet_index_map_internal);
|
|
|
|
int facet_index = 0;
|
|
for(Facet_const_iterator facet_it = mesh.facets_begin();
|
|
facet_it != mesh.facets_end();
|
|
++facet_it, ++facet_index) {
|
|
boost::put(facet_index_map, facet_it, facet_index);
|
|
}
|
|
}
|
|
}
|
|
// Use these two functions together
|
|
void calculate_sdf_values(double cone_angle = CGAL_DEFAULT_CONE_ANGLE,
|
|
int number_of_ray = CGAL_DEFAULT_NUMBER_OF_RAYS) {
|
|
|
|
typedef std::map<Facet_const_iterator, double> internal_map;
|
|
internal_map facet_value_map_internal;
|
|
boost::associative_property_map<internal_map> sdf_pmap(
|
|
facet_value_map_internal);
|
|
|
|
SDF_calculation(cone_angle, number_of_ray).calculate_sdf_values(mesh, sdf_pmap);
|
|
|
|
double dum = 1.0;
|
|
sdf_values = std::vector<double>(mesh.size_of_facets());
|
|
for(Facet_const_iterator facet_it = mesh.facets_begin();
|
|
facet_it != mesh.facets_end(); ++facet_it) {
|
|
get(sdf_values, facet_it) = boost::get(sdf_pmap, facet_it);
|
|
}
|
|
|
|
check_zero_sdf_values();
|
|
smooth_sdf_values_with_bilateral();
|
|
normalize_sdf_values();
|
|
}
|
|
|
|
void partition(int number_of_centers = CGAL_DEFAULT_NUMBER_OF_CLUSTERS,
|
|
double smoothing_lambda = CGAL_DEFAULT_SMOOTHING_LAMBDA) {
|
|
// soft clustering using GMM-fitting initialized with k-means
|
|
internal::Expectation_maximization fitter(number_of_centers, sdf_values,
|
|
internal::Expectation_maximization::K_MEANS_INITIALIZATION, 1);
|
|
|
|
std::vector<int> labels;
|
|
fitter.fill_with_center_ids(labels);
|
|
|
|
std::vector<std::vector<double> > probability_matrix;
|
|
fitter.fill_with_probabilities(probability_matrix);
|
|
log_normalize_probability_matrix(probability_matrix);
|
|
|
|
// calculating edge weights
|
|
std::vector<std::pair<int, int> > edges;
|
|
std::vector<double> edge_weights;
|
|
calculate_and_log_normalize_dihedral_angles(smoothing_lambda, edges,
|
|
edge_weights);
|
|
|
|
// apply graph cut
|
|
internal::Alpha_expansion_graph_cut gc(edges, edge_weights, probability_matrix,
|
|
labels);
|
|
centers = labels;
|
|
// assign a segment id for each facet
|
|
assign_segments();
|
|
|
|
}
|
|
///////////////////////////////////////////////////////////////////
|
|
// Use these two functions together
|
|
template <class SDFPropertyMap>
|
|
void calculate_sdf_values(SDFPropertyMap sdf_pmap,
|
|
double cone_angle = CGAL_DEFAULT_CONE_ANGLE,
|
|
int number_of_rays = CGAL_DEFAULT_NUMBER_OF_RAYS) {
|
|
SEG_DEBUG(Timer t)
|
|
SEG_DEBUG(t.start())
|
|
|
|
SDF_calculation(cone_angle, number_of_rays).calculate_sdf_values(mesh,
|
|
sdf_pmap);
|
|
|
|
sdf_values = std::vector<double>(mesh.size_of_facets());
|
|
for(Facet_const_iterator facet_it = mesh.facets_begin();
|
|
facet_it != mesh.facets_end(); ++facet_it) {
|
|
get(sdf_values, facet_it) = boost::get(sdf_pmap, facet_it);
|
|
}
|
|
|
|
SEG_DEBUG(std::cout << t.time() << std::endl)
|
|
|
|
check_zero_sdf_values();
|
|
smooth_sdf_values_with_bilateral();
|
|
linear_normalize_sdf_values();
|
|
|
|
for(Facet_const_iterator facet_it = mesh.facets_begin();
|
|
facet_it != mesh.facets_end(); ++facet_it) {
|
|
boost::put(sdf_pmap, facet_it, get(sdf_values, facet_it));
|
|
}
|
|
|
|
SEG_DEBUG(std::cout << t.time() << std::endl)
|
|
}
|
|
|
|
template <class FacetSegmentMap, class SDFPropertyMap>
|
|
void partition(SDFPropertyMap sdf_pmap, FacetSegmentMap segment_pmap,
|
|
int number_of_centers = CGAL_DEFAULT_NUMBER_OF_CLUSTERS,
|
|
double smoothing_lambda = CGAL_DEFAULT_SMOOTHING_LAMBDA) {
|
|
sdf_values = std::vector<double>(mesh.size_of_facets());
|
|
for(Facet_const_iterator facet_it = mesh.facets_begin();
|
|
facet_it != mesh.facets_end(); ++facet_it) {
|
|
get(sdf_values, facet_it) = boost::get(sdf_pmap, facet_it);
|
|
}
|
|
|
|
// log normalize sdf values
|
|
normalize_sdf_values();
|
|
// soft clustering using GMM-fitting initialized with k-means
|
|
internal::Expectation_maximization fitter(number_of_centers, sdf_values,
|
|
internal::Expectation_maximization::K_MEANS_INITIALIZATION, 1);
|
|
|
|
std::vector<int> labels;
|
|
fitter.fill_with_center_ids(labels);
|
|
|
|
std::vector<std::vector<double> > probability_matrix;
|
|
fitter.fill_with_probabilities(probability_matrix);
|
|
log_normalize_probability_matrix(probability_matrix);
|
|
|
|
// calculating edge weights
|
|
std::vector<std::pair<int, int> > edges;
|
|
std::vector<double> edge_weights;
|
|
calculate_and_log_normalize_dihedral_angles(smoothing_lambda, edges,
|
|
edge_weights);
|
|
|
|
// apply graph cut
|
|
internal::Alpha_expansion_graph_cut gc(edges, edge_weights, probability_matrix,
|
|
labels);
|
|
centers = labels;
|
|
// assign a segment id for each facet
|
|
assign_segments();
|
|
for(Facet_const_iterator facet_it = mesh.facets_begin();
|
|
facet_it != mesh.facets_end(); ++facet_it) {
|
|
boost::put(segment_pmap, facet_it, get(segments, facet_it));
|
|
}
|
|
}
|
|
///////////////////////////////////////////////////////////////////
|
|
double get_sdf_value_of_facet(Facet_const_iterator facet) const {
|
|
return sdf_values[boost::get(facet_index_map, facet)];
|
|
}
|
|
|
|
int get_segment_id_of_facet(Facet_const_iterator facet) {
|
|
return segments[boost::get(facet_index_map, facet)];
|
|
}
|
|
|
|
protected:
|
|
double calculate_dihedral_angle_of_edge(Edge_const_iterator& edge) const {
|
|
Facet_const_iterator f1 = edge->facet();
|
|
Facet_const_iterator 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);
|
|
|
|
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 = unit_normal(f1_v1, f1_v2, f1_v3);
|
|
Vector f2_normal = 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(!concave) {
|
|
angle *= CGAL_CONVEX_FACTOR;
|
|
}
|
|
return angle;
|
|
}
|
|
|
|
double calculate_dihedral_angle_of_edge_2(Edge_const_iterator& edge) const {
|
|
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 = Mesh_3::dihedral_angle(a, b, c, d) / 180.0;
|
|
bool concave = n_angle > 0;
|
|
double angle = 1 + ((concave ? -1 : +1) * n_angle);
|
|
|
|
if(!concave) {
|
|
angle *= CGAL_CONVEX_FACTOR;
|
|
}
|
|
return angle;
|
|
|
|
//Facet_const_iterator f1 = edge->facet();
|
|
//Facet_const_iterator 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 = unit_normal(f1_v1, f1_v2, f1_v3);
|
|
//Vector f2_normal = 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;
|
|
}
|
|
|
|
void normalize_sdf_values() {
|
|
typedef std::vector<double>::iterator fv_iterator;
|
|
std::pair<fv_iterator, fv_iterator> min_max_pair =
|
|
min_max_element(sdf_values.begin(), sdf_values.end());
|
|
|
|
double max_value = *min_max_pair.second, min_value = *min_max_pair.first;
|
|
double max_min_dif = max_value - min_value;
|
|
for(fv_iterator it = sdf_values.begin(); it != sdf_values.end(); ++it) {
|
|
double linear_normalized = (*it - min_value) / max_min_dif;
|
|
double log_normalized = log(linear_normalized * CGAL_NORMALIZATION_ALPHA + 1) /
|
|
log(CGAL_NORMALIZATION_ALPHA + 1);
|
|
*it = log_normalized;
|
|
}
|
|
}
|
|
|
|
void linear_normalize_sdf_values() {
|
|
typedef std::vector<double>::iterator fv_iterator;
|
|
std::pair<fv_iterator, fv_iterator> min_max_pair =
|
|
min_max_element(sdf_values.begin(), sdf_values.end());
|
|
|
|
double max_value = *min_max_pair.second, min_value = *min_max_pair.first;
|
|
double max_min_dif = max_value - min_value;
|
|
for(fv_iterator it = sdf_values.begin(); it != sdf_values.end(); ++it) {
|
|
*it = (*it - min_value) / max_min_dif;
|
|
}
|
|
}
|
|
|
|
void smooth_sdf_values() {
|
|
std::vector<double> smoothed_sdf_values(mesh.size_of_facets());
|
|
for(Facet_const_iterator facet_it = mesh.facets_begin();
|
|
facet_it != mesh.facets_end(); ++facet_it) {
|
|
typename Facet::Halfedge_around_facet_const_circulator facet_circulator =
|
|
facet_it->facet_begin();
|
|
double total_neighbor_sdf = 0.0;
|
|
do {
|
|
total_neighbor_sdf += get(sdf_values, facet_circulator->opposite()->facet());
|
|
} while( ++facet_circulator != facet_it->facet_begin());
|
|
|
|
total_neighbor_sdf /= 3.0;
|
|
get(smoothed_sdf_values, facet_it) = (get(sdf_values,
|
|
facet_it) + total_neighbor_sdf) / 2.0;
|
|
}
|
|
sdf_values = smoothed_sdf_values;
|
|
}
|
|
|
|
void smooth_sdf_values_with_gaussian() {
|
|
// take neighbors, use weighted average of neighbors as filtered result. (for weights use gaussian kernel with sigma = window_size/2)
|
|
const int window_size = 2;
|
|
const int iteration = 1;
|
|
|
|
for(int i = 0; i < iteration; ++i) {
|
|
std::vector<double> smoothed_sdf_values(mesh.size_of_facets());
|
|
for(Facet_const_iterator facet_it = mesh.facets_begin();
|
|
facet_it != mesh.facets_end(); ++facet_it) {
|
|
std::map<Facet_const_iterator, int> neighbors;
|
|
get_neighbors_by_vertex(facet_it, neighbors, window_size);
|
|
|
|
double total_sdf_value = 0.0;
|
|
double total_weight = 0.0;
|
|
for(typename std::map<Facet_const_iterator, int>::iterator it =
|
|
neighbors.begin(); it != neighbors.end(); ++it) {
|
|
double weight = exp(-0.5 * (std::pow(it->second / (window_size/2.0),
|
|
2))); // window_size => 2*sigma
|
|
total_sdf_value += get(sdf_values, it->first) * weight;
|
|
total_weight += weight;
|
|
}
|
|
get(smoothed_sdf_values, facet_it) = total_sdf_value / total_weight;
|
|
}
|
|
sdf_values = smoothed_sdf_values;
|
|
}
|
|
}
|
|
|
|
void smooth_sdf_values_with_median() {
|
|
// take neighbors, use median sdf_value as filtered one.
|
|
const int window_size = 2;
|
|
const int iteration = 1;
|
|
|
|
for(int i = 0; i < iteration; ++i) {
|
|
std::vector<double> smoothed_sdf_values(mesh.size_of_facets());;
|
|
for(Facet_const_iterator facet_it = mesh.facets_begin();
|
|
facet_it != mesh.facets_end(); ++facet_it) {
|
|
//Find neighbors and put their sdf values into a list
|
|
std::map<Facet_const_iterator, int> neighbors;
|
|
get_neighbors_by_vertex(facet_it, neighbors, window_size);
|
|
std::vector<double> sdf_of_neighbors;
|
|
for(typename std::map<Facet_const_iterator, int>::iterator it =
|
|
neighbors.begin(); it != neighbors.end(); ++it) {
|
|
sdf_of_neighbors.push_back(get(sdf_values, it->first));
|
|
}
|
|
// Find median.
|
|
double median_sdf = 0.0;
|
|
int half_neighbor_count = sdf_of_neighbors.size() / 2;
|
|
std::nth_element(sdf_of_neighbors.begin(),
|
|
sdf_of_neighbors.begin() + half_neighbor_count, sdf_of_neighbors.end());
|
|
if( half_neighbor_count % 2 == 0) {
|
|
double median_1 = sdf_of_neighbors[half_neighbor_count];
|
|
double median_2 = *std::max_element(sdf_of_neighbors.begin(),
|
|
sdf_of_neighbors.begin() + half_neighbor_count);
|
|
median_sdf = (median_1 + median_2) / 2;
|
|
} else {
|
|
median_sdf = sdf_of_neighbors[half_neighbor_count];
|
|
}
|
|
get(smoothed_sdf_values, facet_it) = median_sdf;
|
|
}
|
|
sdf_values = smoothed_sdf_values;
|
|
}
|
|
}
|
|
|
|
void smooth_sdf_values_with_bilateral() {
|
|
// take neighbors, use weighted average of neighbors as filtered result.
|
|
// two weights are multiplied:
|
|
// spatial: over geodesic distances
|
|
// domain : over sdf_value distances
|
|
const int window_size = 2;
|
|
const int iteration = 1;
|
|
|
|
for(int i = 0; i < iteration; ++i) {
|
|
std::vector<double> smoothed_sdf_values(mesh.size_of_facets());
|
|
for(Facet_const_iterator facet_it = mesh.facets_begin();
|
|
facet_it != mesh.facets_end(); ++facet_it) {
|
|
//Facet_handle facet = facet_it;
|
|
std::map<Facet_const_iterator, int> neighbors;
|
|
get_neighbors_by_vertex(facet_it, neighbors, window_size);
|
|
|
|
double total_sdf_value = 0.0, total_weight = 0.0;
|
|
double current_sdf_value = get(sdf_values, facet_it);
|
|
// calculate deviation for range weighting.
|
|
double deviation = 0.0;
|
|
for(typename std::map<Facet_const_iterator, int>::iterator it =
|
|
neighbors.begin(); it != neighbors.end(); ++it) {
|
|
deviation += std::pow(get(sdf_values, it->first) - current_sdf_value, 2);
|
|
}
|
|
deviation = std::sqrt(deviation / neighbors.size());
|
|
if(deviation == 0.0) {
|
|
deviation = std::numeric_limits<double>::epsilon(); //this might happen
|
|
}
|
|
for(typename std::map<Facet_const_iterator, int>::iterator it =
|
|
neighbors.begin(); it != neighbors.end(); ++it) {
|
|
double spatial_weight = exp(-0.5 * (std::pow(it->second / (window_size/2.0),
|
|
2))); // window_size => 2*sigma
|
|
double domain_weight = exp(-0.5 * (std::pow( (get(sdf_values,
|
|
it->first) - current_sdf_value) / (std::sqrt(2.0)*deviation), 2)));
|
|
double weight = spatial_weight * domain_weight;
|
|
total_sdf_value += get(sdf_values, it->first) * weight;
|
|
total_weight += weight;
|
|
}
|
|
get(smoothed_sdf_values, facet_it) = total_sdf_value / total_weight;
|
|
}
|
|
sdf_values = smoothed_sdf_values;
|
|
}
|
|
}
|
|
|
|
void get_neighbors_by_edge(Facet_const_iterator& facet,
|
|
std::map<Facet_const_iterator, int>& neighbors, int max_level) {
|
|
typedef std::pair<Facet_const_iterator, int> facet_level_pair;
|
|
std::queue<facet_level_pair> facet_queue;
|
|
facet_queue.push(facet_level_pair(facet, 0));
|
|
while(!facet_queue.empty()) {
|
|
const facet_level_pair& pair = facet_queue.front();
|
|
bool inserted = neighbors.insert(pair).second;
|
|
if(inserted && pair.second < max_level) {
|
|
typename Facet::Halfedge_around_facet_const_circulator facet_circulator =
|
|
pair.first->facet_begin();
|
|
do {
|
|
facet_queue.push(facet_level_pair(facet_circulator->opposite()->facet(),
|
|
pair.second + 1));
|
|
} while(++facet_circulator != pair.first->facet_begin());
|
|
}
|
|
facet_queue.pop();
|
|
}
|
|
}
|
|
|
|
void get_neighbors_by_vertex(Facet_const_iterator& facet,
|
|
std::map<Facet_const_iterator, int>& neighbors, int max_level) {
|
|
typedef std::pair<Facet_const_iterator, int> facet_level_pair;
|
|
std::queue<facet_level_pair> facet_queue;
|
|
facet_queue.push(facet_level_pair(facet, 0));
|
|
while(!facet_queue.empty()) {
|
|
const facet_level_pair& pair = facet_queue.front();
|
|
bool inserted = neighbors.insert(pair).second;
|
|
if(inserted && pair.second < max_level) {
|
|
Facet_const_iterator facet = pair.first;
|
|
Halfedge_const_iterator edge = facet->halfedge();
|
|
do { // loop on three vertices of the facet
|
|
Vertex_const_iterator vertex = edge->vertex();
|
|
typename Facet::Halfedge_around_vertex_const_circulator vertex_circulator =
|
|
vertex->vertex_begin();
|
|
do { // for each vertex loop on neighbor vertices
|
|
facet_queue.push(facet_level_pair(vertex_circulator->facet(), pair.second + 1));
|
|
} while(++vertex_circulator != vertex->vertex_begin());
|
|
} while((edge = edge->next()) != facet->halfedge());
|
|
}
|
|
facet_queue.pop();
|
|
}
|
|
}
|
|
|
|
void check_zero_sdf_values() {
|
|
// If there is any facet which has no sdf value, assign average sdf value of its neighbors
|
|
for(Facet_const_iterator facet_it = mesh.facets_begin();
|
|
facet_it != mesh.facets_end(); ++facet_it) {
|
|
if(get(sdf_values, facet_it) == 0.0) {
|
|
typename Facet::Halfedge_around_facet_const_circulator facet_circulator =
|
|
facet_it->facet_begin();
|
|
double total_neighbor_sdf = 0.0;
|
|
do {
|
|
total_neighbor_sdf += get(sdf_values, facet_circulator->opposite()->facet());
|
|
} while( ++facet_circulator != facet_it->facet_begin());
|
|
get(sdf_values, facet_it) = total_neighbor_sdf / 3.0;
|
|
}
|
|
}
|
|
// Find minimum sdf value other than 0
|
|
double min_sdf = (std::numeric_limits<double>::max)();
|
|
for(std::vector<double>::iterator it = sdf_values.begin();
|
|
it != sdf_values.end(); ++it) {
|
|
if(*it < min_sdf && *it != 0.0) {
|
|
min_sdf = *it;
|
|
}
|
|
}
|
|
// If still there is any facet which has no sdf value, assign minimum sdf value.
|
|
// This is meaningful since (being an outlier) 0 sdf values might effect normalization & log extremely.
|
|
for(std::vector<double>::iterator it = sdf_values.begin();
|
|
it != sdf_values.end(); ++it) {
|
|
if(*it == 0.0) {
|
|
*it = min_sdf;
|
|
}
|
|
}
|
|
}
|
|
|
|
void log_normalize_probability_matrix(std::vector<std::vector<double> >&
|
|
probabilities) const {
|
|
const double epsilon = 1e-5;
|
|
for(std::vector<std::vector<double> >::iterator it_i = probabilities.begin();
|
|
it_i != probabilities.end(); ++it_i) {
|
|
for(std::vector<double>::iterator it = it_i->begin(); it != it_i->end(); ++it) {
|
|
double probability = (std::max)(*it, epsilon);
|
|
//probability += epsilon;
|
|
//probability = (std::min)(probability, 1.0);
|
|
probability = -log(probability);
|
|
*it = (std::max)(probability, std::numeric_limits<double>::epsilon());
|
|
}
|
|
}
|
|
}
|
|
|
|
void calculate_and_log_normalize_dihedral_angles(double smoothing_lambda,
|
|
std::vector<std::pair<int, int> >& edges,
|
|
std::vector<double>& edge_weights) const {
|
|
const double epsilon = 1e-5;
|
|
//edges and their weights. pair<int, int> stores facet-id pairs (see above) (may be using boost::tuple can be more suitable)
|
|
for(Edge_const_iterator edge_it = mesh.edges_begin();
|
|
edge_it != mesh.edges_end(); ++edge_it) {
|
|
int index_f1 = boost::get(facet_index_map, edge_it->facet());
|
|
int index_f2 = boost::get(facet_index_map, edge_it->opposite()->facet());
|
|
edges.push_back(std::pair<int, int>(index_f1, index_f2));
|
|
|
|
double angle = calculate_dihedral_angle_of_edge(edge_it);
|
|
if(angle < epsilon) {
|
|
angle = epsilon;
|
|
}
|
|
angle = -log(angle);
|
|
angle = (std::max)(angle, std::numeric_limits<double>::epsilon());
|
|
angle *= smoothing_lambda;
|
|
edge_weights.push_back(angle);
|
|
}
|
|
}
|
|
|
|
void assign_segments() {
|
|
segments = std::vector<int>(mesh.size_of_facets(), -1);
|
|
int segment_id = 0;
|
|
for(Facet_const_iterator facet_it = mesh.facets_begin();
|
|
facet_it != mesh.facets_end(); ++facet_it) {
|
|
if(get(segments, facet_it) == -1) {
|
|
depth_first_traversal(facet_it, segment_id);
|
|
segment_id++;
|
|
}
|
|
}
|
|
}
|
|
|
|
void depth_first_traversal(Facet_const_iterator& facet, int segment_id) {
|
|
get(segments, facet) = segment_id;
|
|
typename Facet::Halfedge_around_facet_const_circulator facet_circulator =
|
|
facet->facet_begin();
|
|
double total_neighbor_sdf = 0.0;
|
|
do {
|
|
Facet_const_iterator neighbor = facet_circulator->opposite()->facet();
|
|
if(get(segments, neighbor) == -1
|
|
&& get(centers, facet) == get(centers, neighbor)) {
|
|
depth_first_traversal(neighbor, segment_id);
|
|
}
|
|
} while( ++facet_circulator != facet->facet_begin());
|
|
}
|
|
|
|
|
|
|
|
template<class T>
|
|
T& get(std::vector<T>& data, const Facet_const_iterator& facet) {
|
|
return data[ boost::get(facet_index_map, facet) ];
|
|
}
|
|
|
|
public:
|
|
/**
|
|
* Going to be removed
|
|
*/
|
|
void write_sdf_values(const char* file_name) {
|
|
std::ofstream output(file_name);
|
|
for(Facet_const_iterator facet_it = mesh.facets_begin();
|
|
facet_it != mesh.facets_end(); ++facet_it) {
|
|
output << get(sdf_values, facet_it) << std::endl;
|
|
}
|
|
output.close();
|
|
}
|
|
/**
|
|
* Going to be removed
|
|
*/
|
|
void read_sdf_values(const char* file_name) {
|
|
std::ifstream input(file_name);
|
|
sdf_values = std::vector<double>(mesh.size_of_facets());
|
|
for(Facet_const_iterator facet_it = mesh.facets_begin();
|
|
facet_it != mesh.facets_end(); ++facet_it) {
|
|
double sdf_value;
|
|
input >> sdf_value;
|
|
get(sdf_values, facet_it) = sdf_value;
|
|
}
|
|
}
|
|
/**
|
|
* Going to be removed
|
|
*/
|
|
void read_center_ids(const char* file_name) {
|
|
//std::ifstream input(file_name);
|
|
//centers.clear();
|
|
//int max_center = 0;
|
|
//for(Facet_const_iterator facet_it = mesh.facets_begin(); facet_it != mesh.facets_end(); ++facet_it)
|
|
//{
|
|
// int center_id;
|
|
// input >> center_id;
|
|
// centers.insert(std::pair<Facet_const_iterator, int>(facet_it, center_id));
|
|
// if(center_id > max_center) { max_center = center_id; }
|
|
//}
|
|
//number_of_centers = max_center + 1;
|
|
}
|
|
|
|
/**
|
|
* Going to be removed
|
|
*/
|
|
void read_probabilities(const char* file_name,
|
|
std::vector<std::vector<double> > & probability_matrix) {
|
|
std::ifstream input(file_name);
|
|
for(std::vector<std::vector<double> >::iterator vec_it =
|
|
probability_matrix.begin(); vec_it != probability_matrix.end(); ++vec_it) {
|
|
for(std::vector<double>::iterator it = vec_it->begin(); it != vec_it->end();
|
|
++it) {
|
|
input >> (*it);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Going to be removed
|
|
*/
|
|
void write_segment_ids(const char* file_name) {
|
|
assign_segments();
|
|
std::ofstream output(file_name);
|
|
for(Facet_const_iterator facet_it = mesh.facets_begin();
|
|
facet_it != mesh.facets_end(); ++facet_it) {
|
|
output << get(segments, facet_it) << std::endl;
|
|
}
|
|
output.close();
|
|
}
|
|
/**
|
|
* Going to be removed
|
|
*/
|
|
void 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;
|
|
|
|
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;
|
|
|
|
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_NORMALIZATION_ALPHA
|
|
#undef CGAL_CONVEX_FACTOR
|
|
#undef CGAL_DEFAULT_NUMBER_OF_CLUSTERS
|
|
#undef CGAL_DEFAULT_SMOOTHING_LAMBDA
|
|
#undef CGAL_DEFAULT_CONE_ANGLE
|
|
#undef CGAL_DEFAULT_NUMBER_OF_RAYS
|
|
|
|
#ifdef SEG_DEBUG
|
|
#undef SEG_DEBUG
|
|
#endif
|
|
#endif //CGAL_SURFACE_MESH_SEGMENTATION_H
|