cgal/Point_set_processing_3/include/CGAL/vcm_estimate_edges.h

352 lines
14 KiB
C++

// Copyright (c) 2014 INRIA Sophia-Antipolis (France).
// All rights reserved.
//
// This file is part of CGAL (www.cgal.org).
// You can redistribute it and/or modify it under the terms of the GNU
// General Public License as published by the Free Software Foundation,
// either version 3 of the License, or (at your option) any later version.
//
// Licensees holding a valid commercial license may use this file in
// accordance with the commercial license agreement provided with the software.
//
// This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
// WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
//
// $URL$
// $Id$
//
// Author(s) : Jocelyn Meyron and Quentin Mérigot
//
#ifndef CGAL_VCM_ESTIMATE_EDGES_H
#define CGAL_VCM_ESTIMATE_EDGES_H
#include <Eigen/Dense>
#include <CGAL/vcm_estimate_normals.h>
#include <CGAL/Kd_tree.h>
#include <CGAL/Search_traits_3.h>
#include <CGAL/Fuzzy_sphere.h>
#include <CGAL/Orthogonal_k_neighbor_search.h>
#include <CGAL/Delaunay_triangulation_3.h>
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/kruskal_min_spanning_tree.hpp>
#include <boost/graph/dijkstra_shortest_paths.hpp>
#include <fstream>
#include <cmath>
namespace CGAL {
// ----------------------------------------------------------------------------
// Private section
// ----------------------------------------------------------------------------
namespace internal {
/// @cond SKIP_IN_MANUAL
// Determine if a point is on an edge using the VCM of the point.
// A point will be considered as an edge point iff it satisfies a criteria
// relating the eigenvalues of its VCM.
template <class Covariance>
bool
is_on_edge (Covariance &cov,
double threshold,
Eigen::Vector3f &dir) {
// Construct covariance matrix
Eigen::Matrix3f m = construct_covariance_matrix(cov);
// Diagonalizing the matrix
Eigen::Vector3f eigenvalues;
Eigen::Matrix3f eigenvectors;
if (! diagonalize_selfadjoint_matrix(m, eigenvectors, eigenvalues)) {
return false;
}
// Compute the ratio
float r = eigenvalues(1) / (eigenvalues(0) + eigenvalues(1) + eigenvalues(2));
if (r >= threshold) {
dir = eigenvectors.col(1);
return true;
}
return false;
}
/// @endcond
/// @cond SKIP_IN_MANUAL
// Computes the Rips graph of a set of points.
// The Rips graph is a graph where the vertices are the points and
// there is an edge between each vertex contained in a ball of a given radius.
// There is also a cost at each edge.
// The cost between the points pi and pj is given by: c(pi, pj) = norm(p, pi, pj) ^ p
// where p is a given exponent (whose default value is 2 so the cost is the euclidean
// squared distance).
template <typename Undirected_Graph,
typename K>
void
compute_rips_graph (Undirected_Graph& g, ///< constructed graph.
std::vector<typename K::Point_3> points_on_edges, ///< given points.
std::map<typename K::Point_3, size_t> indices, ///< map between each point and its corresponding index in the array.
double rips_radius, ///< radius of the sphere used to add edges.
float exponent, ///< exponent used in the computation of the cost.
const K & /* kernel */) ///< geometric traits
{
typedef typename K::Point_3 Point;
typedef typename K::Vector_3 Vector;
typedef typename K::FT FT;
// KD-Tree
typedef typename CGAL::Search_traits_3<K> Traits;
typedef typename CGAL::Kd_tree<Traits> Tree;
typedef typename CGAL::Fuzzy_sphere<Traits> Fuzzy_sphere;
Tree tree(points_on_edges.begin(), points_on_edges.end());
// Vertices
for (unsigned int ind = 0; ind < points_on_edges.size(); ++ind)
boost::add_vertex(g);
// Edges
// We put edges between two vertices which are in a given ball
for (unsigned int ind = 0; ind < points_on_edges.size(); ++ind) {
std::vector<Point> nn;
Point p = points_on_edges[ind];
tree.search(std::back_inserter(nn),
Fuzzy_sphere(p, rips_radius));
for (unsigned int k = 0; k < nn.size(); k++) {
Vector v = nn[k] - p;
FT cost = pow(fabs(v.x()), exponent) +
pow(fabs(v.y()), exponent) +
pow(fabs(v.z()), exponent);
boost::add_edge(ind, indices[nn[k]], cost, g);
}
}
}
/// @endcond
/// @cond SKIP_IN_MANUAL
// Computes the nearest neighbors graph of a set of points.
// The nearest neighbors graph is a graph where the vertices are the points and
// there is an edge between each vertex and its k nearest neighbors where k is a given parameter.
// There is also a cost at each edge.
// The cost between the points pi and pj is given by: c(pi, pj) = norm(p, pi, pj) ^ p
// where p is a given exponent (whose default value is 2 so the cost is the euclidean
// squared distance).
template <typename Undirected_Graph,
typename K>
void
compute_nearest_neighbors_graph (Undirected_Graph& g, ///< constructed graph.
std::vector<typename K::Point_3> points_on_edges, ///< given points.
std::map<typename K::Point_3, size_t> indices, ///< map between each point and its corresponding index in the array.
int nb_neighbors, ///< number of neighbors to consider when constructing the graph.
float exponent, ///< exponent used in the computation of the cost.
const K & /* kernel */) ///< geometric traits
{
typedef typename K::Point_3 Point;
typedef typename K::Vector_3 Vector;
typedef typename K::FT FT;
typedef typename CGAL::Search_traits_3<K> Traits;
typedef typename CGAL::Orthogonal_k_neighbor_search<Traits> Neighbor_search;
typedef typename Neighbor_search::Tree Tree;
Tree tree(points_on_edges.begin(), points_on_edges.end());
// Vertices
for (unsigned int ind = 0; ind < points_on_edges.size(); ++ind)
boost::add_vertex(g);
// Edges
// We put edges between each vertex and its k nearest neighbors
for (unsigned int ind = 0; ind < points_on_edges.size(); ++ind) {
std::vector<Point> nn;
Point p = points_on_edges[ind];
Neighbor_search search(tree, p, nb_neighbors);
for (typename Neighbor_search::iterator nit = search.begin();
nit != search.end();
++nit) {
nn.push_back(nit->first);
}
for (unsigned int k = 0; k < nn.size(); k++) {
Vector v = nn[k] - p;
FT cost = pow(fabs(v.x()), exponent) +
pow(fabs(v.y()), exponent) +
pow(fabs(v.z()), exponent);
boost::add_edge(ind, indices[nn[k]], cost, g);
}
}
}
/// @endcond
/// @cond SKIP_IN_MANUAL
// Computes the Delaunay graph of a set of points.
// The Delaunay graph is a graph where the vertices are the points and
// there the edges are the Delaunay edges.
// There is also a cost at each edge.
// The cost between the points pi and pj is given by: c(pi, pj) = norm(p, pi, pj) ^ p
// where p is a given exponent (whose default value is 2 so the cost is the euclidean
// squared distance).
template <typename Undirected_Graph,
typename K>
void
compute_delaunay_graph (Undirected_Graph& g, ///< constructed graph.
std::vector<typename K::Point_3> points_on_edges, ///< given points.
std::map<typename K::Point_3, size_t> indices, ///< map between each point and its corresponding index in the array.
float exponent, ///< exponent used in the computation of the cost.
const K & /* kernel */) ///< geometric traits
{
typedef typename K::Point_3 Point;
typedef typename K::Vector_3 Vector;
typedef typename K::FT FT;
typedef CGAL::Delaunay_triangulation_3<K> DT;
typedef typename DT::Finite_edges_iterator Finite_edges_iterator;
DT dt(points_on_edges.begin(), points_on_edges.end());
// Vertices
for (unsigned int ind = 0; ind < points_on_edges.size(); ++ind)
boost::add_vertex(g);
// Edges
// The edges are the Delaunay edges
for (Finite_edges_iterator eit = dt.finite_edges_begin();
eit != dt.finite_edges_end();
++eit) {
typename DT::Segment seg = dt.segment(*eit);
Point s = seg.source(),
t = seg.target();
Vector v = s - t;
FT cost = pow(fabs(v.x()), exponent) +
pow(fabs(v.y()), exponent) +
pow(fabs(v.z()), exponent);
boost::add_edge(indices[s], indices[t], cost, g);
}
}
/// @endcond
} // namespace internal
// ----------------------------------------------------------------------------
// Public section
// ----------------------------------------------------------------------------
/// \ingroup PkgPointSetProcessing
/// Estimates the feature edges of the `[first, beyond)` range of points
/// using the Voronoi Covariance Measure.
/// It returns a vector of all the points that have been estimated as edge points.
/// It mainly consists in computing the VCM using `vcm_compute` and then
/// determining which point must be considered as an edge one or not (using a criterion
/// based on the eigenvalues of the covariance matrices).
///
/// See `vcm_compute()` for more details on the VCM.
///
/// @tparam ForwardIterator iterator over input points.
/// @tparam PointPMap is a model of `ReadablePropertyMap` with a value_type = `Kernel::Point_3`.
/// @tparam Kernel Geometric traits class.
/// @tparam Covariance Covariance matrix type..
template < typename ForwardIterator,
typename PointPMap,
typename Kernel
>
std::vector<typename Kernel::Point_3>
vcm_estimate_edges (ForwardIterator first, ///< iterator over the first input point.
ForwardIterator beyond, ///< past-the-end iterator over the input points.
PointPMap point_pmap, ///< property map: value_type of ForwardIterator -> Point_3.
double R, ///< offset radius: radius of the sphere to intersect the Voronoi cell with.
double r, ///< convolution radius: all points in a sphere with this radius will be convolved.
double threshold, ///< threshold used to determine if a point is an edge point or not.
const Kernel& k ///< geometric traits.
)
{
typedef typename Kernel::FT FT;
typedef CGAL::Voronoi_covariance_3::Voronoi_covariance_3<FT> Covariance;
typedef typename Kernel::Point_3 Point;
typedef typename Kernel::FT FT;
// Compute the VCM and convolve it
std::vector<Covariance> cov;
vcm_compute(first, beyond,
point_pmap,
cov,
R,
r,
k);
// Find the potential points on the edges
std::vector<Point> points_on_edges;
int i = 0;
for (ForwardIterator it = first; it != beyond; ++it) {
Eigen::Vector3f dir;
if (internal::is_on_edge(cov[i], threshold, dir)) {
points_on_edges.push_back(get(point_pmap, *it));
}
i++;
}
return points_on_edges;
}
/// \ingroup PkgPointSetProcessing
/// Constructs a minimum spanning tree where the vertices are the points
/// given in argument.
/// First, it creates a graph where the vertices are the points and there is an edge
/// between each vertices contained in a sphere of a given radius (`rips_radius`).
/// The cost on the edges is the norm on the Lp space to the power p (where p = `exponent`).
/// Secondly, it constructs the MST using BGL and the Kruskal algorithm.
/// It returns a vector of segments where each segment represent a polyline which belongs to the estimated feature.
/// @tparam Kernel Geometric traits class.
template < typename Kernel >
std::vector<typename Kernel::Segment_3>
construct_mst (std::vector<typename Kernel::Point_3> points_on_edges, ///< estimated edges points (see `vcm_estimate_edges()`).
const Kernel& k, ///< geometric traits.
double rips_radius = 0.1, ///< radius used for the construction of the Rips graph.
float exponent = 2 ///< exponent of the cost between edges.
)
{
typedef typename Kernel::Point_3 Point;
typedef typename Kernel::Segment_3 Segment;
// Map between points and their corresponding indices
std::map<Point, size_t> indices;
for (size_t s = 0; s < points_on_edges.size(); ++s)
indices[points_on_edges[s]] = s;
// Compute the graph
typedef boost::property<boost::edge_weight_t, double> EdgeWeightProperty;
typedef boost::adjacency_list<boost::vecS,
boost::vecS,
boost::undirectedS,
boost::no_property,
EdgeWeightProperty > Undirected_Graph;
typedef boost::graph_traits<Undirected_Graph>::edge_descriptor Edge_descriptor;
Undirected_Graph g;
compute_rips_graph(g, points_on_edges, indices, rips_radius, exponent, k);
// Compute the MST
boost::property_map<Undirected_Graph, boost::edge_weight_t>::type weight = get(boost::edge_weight, g);
std::vector<Edge_descriptor> spanning_tree;
boost::kruskal_minimum_spanning_tree(g, std::back_inserter(spanning_tree));
// Construct the polylines
std::vector<Segment> polylines;
for (std::vector<Edge_descriptor>::iterator ei = spanning_tree.begin();
ei != spanning_tree.end();
++ei) {
unsigned int si = boost::source(*ei, g);
unsigned int ti = boost::target(*ei, g);
Segment s(points_on_edges[si], points_on_edges[ti]);
polylines.push_back(s);
}
return polylines;
}
} // namespace CGAL
#endif // CGAL_VCM_ESTIMATE_EDGES_H