Merge branch 'old/gsoc2014-VCM_3-jmeyron' into gsoc2014-VCM_3-jmeyron

Conflicts:
	Installation/changes.html
	Point_set_processing_3/doc/Point_set_processing_3/PackageDescription.txt
	Point_set_processing_3/doc/Point_set_processing_3/Point_set_processing_3.txt
	Point_set_processing_3/doc/Point_set_processing_3/examples.txt
	Point_set_processing_3/examples/Point_set_processing_3/CMakeLists.txt
	Polyhedron/demo/Polyhedron/CMakeLists.txt
This commit is contained in:
Sébastien Loriot 2015-06-04 11:27:56 +02:00
commit c2cd87865e
27 changed files with 99240 additions and 12 deletions

View File

@ -0,0 +1,243 @@
#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
#include <CGAL/Convex_hull_3/dual/halfspace_intersection_3.h>
#include <CGAL/Delaunay_triangulation_3.h>
#include <CGAL/convex_hull_3.h>
#include <CGAL/Timer.h>
#include <CGAL/point_generators_3.h>
#include <vector>
#include <fstream>
typedef CGAL::Exact_predicates_inexact_constructions_kernel K;
typedef K::Point_3 Point;
typedef K::Plane_3 Plane;
typedef K::Vector_3 Vector;
typedef CGAL::Convex_hull_traits_3<K> Traits;
typedef Traits::Polyhedron_3 Polyhedron;
typedef CGAL::Delaunay_triangulation_3<K> DT;
typedef DT::Vertex_handle Vertex_handle;
// Function object that computes the volume and the centroid of a polyhedron.
template <class K>
class Centroid_volume_accumulator {
public:
typedef typename K::Point_3 Point;
typedef typename K::Vector_3 Vector;
Centroid_volume_accumulator() : vol(0),
cx(0), cy(0), cz(0) {}
void operator () (const Point &a,
const Point &b,
const Point &c) {
Vector ex(1, 0, 0),
ey(0, 1, 0),
ez(0, 0, 1);
Vector va = CGAL::ORIGIN - a;
Vector vb = CGAL::ORIGIN - b;
Vector vc = CGAL::ORIGIN - c;
// Updating the volume...
Vector nhat = CGAL::cross_product(b - a, c - a);
vol += nhat * va;
// ... and the centroid
// X
cx += (nhat * ex) * (
CGAL::square(ex * (va + vb)) +
CGAL::square(ex * (vb + vc)) +
CGAL::square(ex * (vc + va)) );
// Y
cy += (nhat * ey) * (
CGAL::square(ey * (va + vb)) +
CGAL::square(ey * (vb + vc)) +
CGAL::square(ey * (vc + va)) );
// Z
cz += (nhat * ez) * (
CGAL::square(ez * (va + vb)) +
CGAL::square(ez * (vb + vc)) +
CGAL::square(ez * (vc + va)) );
}
void end () {
vol /= 6;
cx /= (48 * vol);
cy /= (48 * vol);
cz /= (48 * vol);
}
Point centroid () const {
return Point(cx, cy, cz);
}
float volume () const {
return vol;
}
void reset () {
vol = 0;
cx = 0;
cy = 0;
cz = 0;
}
private:
// Volume
float vol;
// Centroid
float cx, cy, cz;
};
// Apply a function object to all the triangles composing the faces of a polyhedron.
template <typename Polyhedron, class F>
F& apply_function_object_polyhedron (Polyhedron &P,
F &f) {
typedef typename Polyhedron::Halfedge_around_facet_circulator Hafc;
typedef typename Polyhedron::Facet_iterator Facet_iterator;
f.reset();
for (Facet_iterator fit = P.facets_begin();
fit != P.facets_end();
fit++) {
Hafc h0 = fit->facet_begin(), hf = h0--, hs = hf;
hs ++;
while (1) {
// Apply 'f' on each triangle of the polyhedron's facet
f ( h0->vertex()->point(),
hf->vertex()->point(),
hs->vertex()->point() );
if (hs == h0)
break;
hs++; hf++;
}
}
f.end();
return f;
}
// Lloyd algorithm
// Generate points uniformly sampled inside a polyhedron.
// An initial set of points needs to be given.
template <class PolyIterator>
void lloyd_algorithm (PolyIterator poly_begin,
PolyIterator poly_end,
std::vector<Point>& points) {
std::list<Plane> planes;
std::list<Point> centroids;
Centroid_volume_accumulator<K> centroid_acc;
// Compute Delaunay triangulation
DT dt(points.begin(), points.end());
for (DT::Finite_vertices_iterator vit = dt.finite_vertices_begin();
vit != dt.finite_vertices_end();
++vit) {
planes.clear();
// Voronoi cells
std::list<Vertex_handle> vertices;
dt.incident_vertices(vit, std::back_inserter(vertices));
for (std::list<Vertex_handle>::iterator it = vertices.begin();
it != vertices.end();
it++) {
Vector p = ((*it)->point() - vit->point()) / 2;
planes.push_back (Plane(CGAL::midpoint((*it)->point(), vit->point()), p));
}
// Add planes of the polyhedron faces
for (PolyIterator it = poly_begin;
it != poly_end;
it++) {
planes.push_back(*it);
}
// Intersection
Polyhedron P;
CGAL::halfspace_intersection_3(planes.begin(),
planes.end(),
P,
vit->point());
// Centroid
apply_function_object_polyhedron(P, centroid_acc);
centroids.push_back(centroid_acc.centroid());
}
// Replace the initial points by the computed centroids
points.clear();
for (std::list<Point>::iterator it = centroids.begin();
it != centroids.end();
it++) {
points.push_back(*it);
}
}
int main (int argc, char *argv[]) {
// Cube
std::list<Plane> planes;
planes.push_back(Plane(1, 0, 0, -1));
planes.push_back(Plane(-1, 0, 0, -1));
planes.push_back(Plane(0, 1, 0, -1));
planes.push_back(Plane(0, -1, 0, -1));
planes.push_back(Plane(0, 0, 1, -1));
planes.push_back(Plane(0, 0, -1, -1));
std::vector<Point> points;
int N, steps;
// Number of points
if (argc > 1) {
N = atoi(argv[1]);
} else {
N = 50;
}
// Number of steps
if (argc > 2) {
steps = atoi(argv[2]);
} else {
steps = 10;
}
CGAL::Random_points_in_sphere_3<Point> g;
for (int i = 0; i < N; i++) {
Point p = *g++;
points.push_back(p);
}
std::ofstream bos("before_lloyd.xyz");
std::copy(points.begin(), points.end(),
std::ostream_iterator<Point>(bos, "\n"));
// Apply Lloyd algorithm: will generate points
// uniformly sampled inside a cube.
for (int i = 0; i < steps; i++) {
std::cout << "iteration " << i + 1 << std::endl;
CGAL::Timer timer;
timer.start();
lloyd_algorithm(planes.begin(),
planes.end(),
points);
timer.stop();
std::cout << "Execution time : " << timer.time() << "s\n";
}
std::ofstream aos("after_lloyd.xyz");
std::copy(points.begin(), points.end(),
std::ostream_iterator<Point>(aos, "\n"));
return 0;
}

View File

@ -1378,6 +1378,17 @@ ABSTRACT = {We present the first complete, exact and efficient C++ implementatio
,update = "99.05 schirra, 00.09 hert"
}
@article{ cgal:mog-vbcfe-11
,author = {Merigot, Quentin and Ovsjanikov, Maks and Guibas, Leonidas}
,title = {Voronoi-based curvature and feature estimation from point clouds}
,journal = {Visualization and Computer Graphics, IEEE Transactions on}
,volume = {17}
,number = {6}
,pages = {743--756}
,year = {2011}
,publisher = {IEEE}
}
@inproceedings{cgal:mp-fcafg-05
, author = "Guillaume Melquiond and Sylvain Pion"
, title = "Formal certification of arithmetic filters for geometric predicates"

View File

@ -151,6 +151,18 @@ and <code>src/</code> directories).
<!-- Voronoi Diagrams -->
<!-- Mesh Generation -->
<!-- Geometry Processing -->
<h3>Point Set Processing and Surface Reconstruction from Point Sets</h3>
<ul>
<li>
Add the function <code>CGAL::compute_vcm()</code> for computing the
Voronoi Covariance Measure (VCM) of a point set. The output of this function
can be used with the function <code>CGAL::vcm_is_on_feature_edge()</code>
to determine whether a point is on or close to a feature edge. The former
function is also internally used by <code>CGAL::vcm_estimate_normals()</code>
to estimate the normals of a point set and it is particularly suited to point
set with noise.
</li>
</ul>
<!-- Spatial Searching and Sorting -->
<h3>Spatial Sorting</h3>
<ul>

View File

@ -0,0 +1,34 @@
/*!
\ingroup PkgPointSetProcessingConcepts
\cgalConcept
Concept providing functions to extract eigenvectors and eigenvalue from
covariance matrices represented by an array `a` as follows:
<center>
\f$ \begin{bmatrix}
a[0] & a[1] & a[2] \\
a[1] & a[3] & a[4] \\
a[2] & a[4] & a[5] \\
\end{bmatrix}\f$
</center>
\cgalHasModel `CGAL::Eigen_vcm_traits`
*/
class VCMTraits
{
public:
/// fill `eigenvalues` with the eigenvalues of the covariance matrix represented by `cov`.
/// Eigenvalues are sorted by increasing order.
/// \return `true` if the operation was successful and `false` otherwise.
static bool
diagonalize_selfadjoint_covariance_matrix(
const cpp11::array<double,6>& cov,
cpp11::array<double, 3>& eigenvalues);
/// Extract the eigenvector associated to the largest eigenvalue
/// of the covariance matrix represented by `cov`.
/// \return `true` if the operation was successful and `false` otherwise.
static bool
extract_largest_eigenvector_of_covariance_matrix (
const cpp11::array<double,6>& cov,
cpp11::array<double,3> &normal);
};

View File

@ -1,4 +1,6 @@
/// \defgroup PkgPointSetProcessing Point Set Processing Reference
/// \defgroup PkgPointSetProcessing Point Set Processing Reference
/// \defgroup PkgPointSetProcessingConcepts Concepts
/// \ingroup PkgPointSetProcessing
/*!
\addtogroup PkgPointSetProcessing
@ -6,7 +8,7 @@
\cgalPkgPicture{point_set_processing_detail.png}
\cgalPkgSummaryBegin
\cgalPkgAuthors{Pierre Alliez, Clément Jamin, Laurent Saboret, Nader Salman, Shihao Wu}
\cgalPkgDesc{This \cgal component implements methods to analyze and process point sets. The input is an unorganized point set, possibly with normal attributes (unoriented or oriented). The point set can be analyzed to measure its average spacing, and processed through functions devised for simplification, regularization, upsampling, outlier removal, smoothing, normal estimation and normal orientation.}
\cgalPkgDesc{This \cgal component implements methods to analyze and process unorganized point sets. The input is an unorganized point set, possibly with normal attributes (unoriented or oriented). The point set can be analyzed to measure its average spacing, and processed through functions devoted to the simplification, outlier removal, smoothing, normal estimation, normal orientation and feature edges estimation.}
\cgalPkgManuals{Chapter_Point_Set_Processing,PkgPointSetProcessing}
\cgalPkgSummaryEnd
\cgalPkgShortInfoBegin
@ -35,6 +37,9 @@
- `CGAL::pca_estimate_normals()`
- `CGAL::mst_orient_normals()`
- `CGAL::edge_aware_upsample_point_set()`
- `CGAL::compute_vcm()`
- `CGAL::vcm_estimate_normals()`
- `CGAL::vcm_is_on_feature_edge()`
- `CGAL::write_off_points()`
- `CGAL::write_xyz_points()`
*/

View File

@ -255,6 +255,12 @@ at each point from the set by linear least squares fitting of a plane
over its `k` nearest neighbors. This algorithm is simpler and
faster than `jet_estimate_normals()`.
Function `vcm_estimate_normals()` estimates the normal direction
at each point from the set by using the Voronoi Covariance Measure
of the point set. This algorithm is more complex and slower than
the previous algorithms. It is based on the
article \cgalCite{cgal:mog-vbcfe-11}.
\section Point_set_processing_3NormalOrientation Normal Orientation
Function `mst_orient_normals()` orients the normals of a set of
@ -315,10 +321,24 @@ Usually, the neighborhood of sample points should include at least one ring of n
Comparison between different sizes of neighbor radius.
\cgalFigureEnd
\section Point_set_processing_3FeaturesEstimation Feature Edges Estimation
Function `vcm_is_on_feature_edge()` indicates if a points belong to a feature edges of the
point set using its Voronoi Covariance Measure.
It is based on the article \cgalCite{cgal:mog-vbcfe-11}.
It first computes the VCM of the points set using `compute_vcm()`. Then, it estimates
which points belong to a sharp edge by testing if a ratio of eigenvalues
is greater than a given threshold.
\subsection Point_set_processing_3Example_6 Example
The following example reads a point set from a file, estimates the
points that are on sharp edges:
\cgalExample{Point_set_processing_3/edges_example.cpp}
\subsection Point_set_processing_3ImplementationHistory Implementation History
\section Point_set_processing_3ImplementationHistory Implementation History
Pierre Alliez and Laurent Saboret contributed the initial component. Nader Salman contributed the grid simplification. Started from GSoC'2013, three new algorithms were implemented by Shihao Wu and Clément Jamin: WLOP, bilateral smoothing and upsampling.

View File

@ -9,4 +9,5 @@
\example Point_set_processing_3/wlop_simplify_and_regularize_point_set_example.cpp
\example Point_set_processing_3/bilateral_smooth_point_set_example.cpp
\example Point_set_processing_3/edge_aware_upsample_point_set_example.cpp
\example Point_set_processing_3/edges_example.cpp
*/

View File

@ -84,6 +84,7 @@ if ( CGAL_FOUND )
# Executables that require Eigen or BLAS and LAPACK
create_single_source_cgal_program( "jet_smoothing_example.cpp" )
create_single_source_cgal_program( "normal_estimation.cpp" )
create_single_source_cgal_program( "edges_example.cpp" )
else(EIGEN3_FOUND OR LAPACK_FOUND)
message(STATUS "NOTICE: Some of the executables in this directory need either Eigen 3.1 (or greater) or LAPACK, and will not be compiled.")

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,58 @@
#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
#include <CGAL/vcm_estimate_edges.h>
#include <CGAL/property_map.h>
#include <CGAL/IO/read_off_points.h>
#include <utility> // defines std::pair
#include <vector>
#include <fstream>
#include <boost/foreach.hpp>
// Types
typedef CGAL::Exact_predicates_inexact_constructions_kernel Kernel;
typedef Kernel::Point_3 Point;
typedef Kernel::Vector_3 Vector;
// Point with normal vector stored in a std::pair.
typedef std::pair<Point, Vector> PointVectorPair;
typedef std::vector<PointVectorPair> PointList;
typedef CGAL::cpp11::array<double,6> Covariance;
int main (int , char**) {
// Reads a .xyz point set file in points[].
std::list<PointVectorPair> points;
std::ifstream stream("data/fandisk.off");
if (!stream ||
!CGAL::read_off_points(stream,
std::back_inserter(points),
CGAL::First_of_pair_property_map<PointVectorPair>()))
{
std::cerr << "Error: cannot read file data/fandisk.off" << std::endl;
return EXIT_FAILURE;
}
// Estimates covariance matrices per points.
double R = 0.2,
r = 0.1;
std::vector<Covariance> cov;
CGAL::First_of_pair_property_map<PointVectorPair> point_pmap;
CGAL::compute_vcm(points.begin(), points.end(), point_pmap, cov, R, r, Kernel());
// Find the points on the edges.
// Note that this step is not expensive and can be done several time to get better results
double threshold = 0.16;
std::ofstream output("points_on_edges.xyz");
int i = 0;
BOOST_FOREACH(const PointVectorPair& p, points)
{
if (CGAL::vcm_is_on_feature_edge(cov[i], threshold))
output << p.first << "\n";
++i;
}
return 0;
}

View File

@ -17,6 +17,7 @@
// This package
#include <CGAL/pca_estimate_normals.h>
#include <CGAL/jet_estimate_normals.h>
#include <CGAL/vcm_estimate_normals.h>
#include <CGAL/mst_orient_normals.h>
#include <CGAL/property_map.h>
#include <CGAL/IO/read_off_points.h>
@ -94,6 +95,30 @@ void run_jet_estimate_normals(PointList& points, // input points + output normal
<< std::endl;
}
// Compute normals direction using the VCM
void run_vcm_estimate_normals(PointList &points, // input points + output normals
double R, // radius of the offset
double r) { // radius used during the convolution
CGAL::Timer task_timer; task_timer.start();
std::cerr << "Estimates Normals Direction using VCM (R="
<< R << " and r=" << r << ")...\n";
// Estimates normals direction.
// Note: vcm_estimate_normals() requires an iterator over points
// + property maps to access each point's position and normal.
CGAL::vcm_estimate_normals(points.begin(), points.end(),
CGAL::First_of_pair_property_map<PointVectorPair>(),
CGAL::Second_of_pair_property_map<PointVectorPair>(),
R,
r);
long memory = CGAL::Memory_sizer().virtual_size();
std::cerr << "done: " << task_timer.time() << " seconds, "
<< (memory>>20) << " Mb allocated"
<< std::endl;
}
// Hoppe92 normal orientation using a Minimum Spanning Tree.
void run_mst_orient_normals(PointList& points, // input points + input/output normals
unsigned int nb_neighbors_mst) // number of neighbors
@ -144,15 +169,19 @@ int main(int argc, char * argv[])
std::cerr << "Input file formats are .off, .xyz and .pwn.\n";
std::cerr << "Output file formats are .xyz and .pwn.\n";
std::cerr << "Options:\n";
std::cerr << " -estimate plane|quadric Estimates normals direction\n";
std::cerr << " using a tangent plane or quadric (default=quadric)\n";
std::cerr << " -nb_neighbors_pca <int> Number of neighbors\n";
std::cerr << " -estimate plane|quadric|vcm Estimates normals direction\n";
std::cerr << " using a tangent plane or quadric or vcm (default=quadric)\n";
std::cerr << " -nb_neighbors_pca <int> Number of neighbors\n";
std::cerr << " to compute tangent plane (default=18)\n";
std::cerr << " -nb_neighbors_jet_fitting <int> Number of neighbors\n";
std::cerr << " -nb_neighbors_jet_fitting <int> Number of neighbors\n";
std::cerr << " to compute quadric (default=18)\n";
std::cerr << " -orient MST Orient normals\n";
std::cerr << " -offset_radius_vcm <double> Offset radius\n";
std::cerr << " to compute VCM (default=0.1)\n";
std::cerr << " -convolve_radius_vcm <double> Convolve radius\n";
std::cerr << " to compute VCM (default=0)\n";
std::cerr << " -orient MST Orient normals\n";
std::cerr << " using a Minimum Spanning Tree (default=MST)\n";
std::cerr << " -nb_neighbors_mst <int> Number of neighbors\n";
std::cerr << " -nb_neighbors_mst <int> Number of neighbors\n";
std::cerr << " to compute the MST (default=18)\n";
return EXIT_FAILURE;
}
@ -161,6 +190,8 @@ int main(int argc, char * argv[])
unsigned int nb_neighbors_pca_normals = 18; // K-nearest neighbors = 3 rings (estimate normals by PCA)
unsigned int nb_neighbors_jet_fitting_normals = 18; // K-nearest neighbors (estimate normals by Jet Fitting)
unsigned int nb_neighbors_mst = 18; // K-nearest neighbors (orient normals by MST)
double offset_radius_vcm = 0.1; // Offset radius (estimate normals by VCM)
double convolve_radius_vcm = 0; // Convolve radius (estimate normals by VCM)
std::string estimate = "quadric"; // estimate normals by jet fitting
std::string orient = "MST"; // orient normals using a Minimum Spanning Tree
@ -171,7 +202,7 @@ int main(int argc, char * argv[])
{
if (std::string(argv[i])=="-estimate") {
estimate = argv[++i];
if (estimate != "plane" && estimate != "quadric")
if (estimate != "plane" && estimate != "quadric" && estimate != "vcm")
std::cerr << "invalid option " << argv[i] << "\n";
}
else if (std::string(argv[i])=="-nb_neighbors_pca") {
@ -180,6 +211,12 @@ int main(int argc, char * argv[])
else if (std::string(argv[i])=="-nb_neighbors_jet_fitting") {
nb_neighbors_jet_fitting_normals = atoi(argv[++i]);
}
else if (std::string(argv[i])=="-offset_radius_vcm") {
offset_radius_vcm = atof(argv[++i]);
}
else if (std::string(argv[i])=="-convolve_radius_vcm") {
convolve_radius_vcm = atof(argv[++i]);
}
else if (std::string(argv[i])=="-orient") {
orient = argv[++i];
if (orient != "MST")
@ -259,6 +296,8 @@ int main(int argc, char * argv[])
run_pca_estimate_normals(points, nb_neighbors_pca_normals);
else if (estimate == "quadric")
run_jet_estimate_normals(points, nb_neighbors_jet_fitting_normals);
else if (estimate == "vcm")
run_vcm_estimate_normals(points, offset_radius_vcm, convolve_radius_vcm);
// Orient normals.
if (orient == "MST")

View File

@ -0,0 +1,115 @@
// 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_EIGEN_VCM_TRAITS_H
#define CGAL_EIGEN_VCM_TRAITS_H
#include <Eigen/Dense>
#include <Eigen/Eigenvalues>
#include <CGAL/array.h>
namespace CGAL {
/// A model of the concept `VCMTraits` using \ref thirdpartyEigen.
/// \cgalModels `VCMTraits`
class Eigen_vcm_traits{
// Construct the covariance matrix
static Eigen::Matrix3f
construct_covariance_matrix (const cpp11::array<double,6>& cov) {
Eigen::Matrix3f m;
m(0,0) = cov[0]; m(0,1) = cov[1]; m(0,2) = cov[2];
m(1,1) = cov[3]; m(1,2) = cov[4];
m(2,2) = cov[5];
m(1, 0) = m(0,1); m(2, 0) = m(0, 2); m(2, 1) = m(1, 2);
return m;
}
// Diagonalize a selfadjoint matrix
static bool
diagonalize_selfadjoint_matrix (Eigen::Matrix3f &m,
Eigen::Matrix3f &eigenvectors,
Eigen::Vector3f &eigenvalues) {
Eigen::SelfAdjointEigenSolver<Eigen::Matrix3f> eigensolver(m);
if (eigensolver.info() != Eigen::Success) {
return false;
}
eigenvalues = eigensolver.eigenvalues();
eigenvectors = eigensolver.eigenvectors();
return true;
}
public:
static bool
diagonalize_selfadjoint_covariance_matrix(
const cpp11::array<double,6>& cov,
cpp11::array<double, 3>& eigenvalues)
{
Eigen::Matrix3f m = construct_covariance_matrix(cov);
// Diagonalizing the matrix
Eigen::Vector3f eigenvalues_;
Eigen::Matrix3f eigenvectors_;
bool res = diagonalize_selfadjoint_matrix(m, eigenvectors_, eigenvalues_);
if (res)
{
eigenvalues[0]=eigenvalues_[0];
eigenvalues[1]=eigenvalues_[1];
eigenvalues[2]=eigenvalues_[2];
}
return res;
}
// Extract the eigenvector associated to the largest eigenvalue
static bool
extract_largest_eigenvector_of_covariance_matrix (
const cpp11::array<double,6>& cov,
cpp11::array<double,3> &normal)
{
// 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;
}
// Eigenvalues are already sorted by increasing order
normal[0]=eigenvectors(0,0);
normal[1]=eigenvectors(1,0);
normal[2]=eigenvectors(2,0);
return true;
}
};
} // namespace CGAL
#endif // CGAL_EIGEN_VCM_TRAITS_H

View File

@ -0,0 +1,225 @@
// 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_INTERNAL_VCM_VORONOI_COVARIANCE_3_HPP
#define CGAL_INTERNAL_VCM_VORONOI_COVARIANCE_3_HPP
#include <list>
#include <CGAL/array.h>
#include <CGAL/internal/Voronoi_covariance_3/voronoi_covariance_sphere_3.h>
#ifdef CGAL_VORONOI_COVARIANCE_USE_CONSTRUCTIONS
#include <CGAL/Convex_hull_3/dual/halfspace_intersection_with_constructions_3.h>
#else
#include <CGAL/Convex_hull_3/dual/halfspace_intersection_3.h>
#endif
namespace CGAL {
namespace Voronoi_covariance_3 {
namespace internal {
template <class FT>
inline void
covariance_matrix_tetrahedron (FT ax, FT ay, FT az,
FT bx, FT by, FT bz,
FT cx, FT cy, FT cz,
cpp11::array<FT,6> &R)
{
const FT det = (ax*cz*by - ax*bz*cy - ay*bx*cz +
ay*cx*bz + az*bx*cy - az*cx*by) / 60.0;
R[0] += (ax*ax + ax*bx + ax*cx +
bx*bx + bx*cx + cx*cx) * det;
R[1] += (ax*ay + ax*by/2.0 + ax*cy/2.0 +
bx*ay/2.0 + bx*by + bx*cy/2.0 +
cx*ay/2.0 + cx*by/2.0 + cx*cy) * det;
R[2] += (ax*az + ax*bz/2.0 + ax*cz/2.0 +
bx*az/2.0 + bx*bz + bx*cz/2.0 +
cx*az/2.0 + cx*bz/2.0 + cx*cz) * det;
R[3] += (ay*ay + ay*by + ay*cy +
by*by + by*cy + cy*cy) * det;
R[4] += (az*ay + az*by/2.0 + az*cy/2.0 +
bz*ay/2.0 + bz*by + bz*cy/2.0 +
cz*ay/2.0 + cz*by/2.0 + cz*cy) * det;
R[5] += (az*az + az*bz + az*cz +
bz*bz + bz*cz + cz*cz) * det;
}
template <class FT>
class Covariance_accumulator_3
{
public:
typedef cpp11::array<FT, 6> Result_type;
private:
Result_type _result;
public:
Covariance_accumulator_3()
{
std::fill (_result.begin(), _result.end(), FT(0));
}
template <class Point>
inline void operator () (const Point &a,
const Point &b,
const Point &c)
{
internal::covariance_matrix_tetrahedron (a[0], a[1], a[2],
b[0], b[1], b[2],
c[0], c[1], c[2],
_result);
}
const Result_type &result() const
{
return _result;
}
};
template <class FT>
class Volume_accumulator_3
{
public:
typedef FT Result_type;
private:
Result_type _result;
public:
Volume_accumulator_3() : _result(0.0)
{}
template <class Point>
inline void operator () (const Point &a,
const Point &b,
const Point &c)
{
const double vol = CGAL::volume(a, b, c, Point(CGAL::ORIGIN));
//std::cerr << "vol = " << vol << "\n";
_result += vol;
}
const Result_type &result() const
{
return _result;
}
};
template <class DT, class Sphere, class F>
F& tessellate_and_intersect(const DT &dt,
typename DT::Vertex_handle v,
const Sphere &sphere,
F &f)
{
typedef typename DT::Vertex_handle Vertex_handle;
typedef typename DT::Geom_traits::Kernel K;
typedef typename K::Plane_3 Plane;
typedef typename K::Point_3 Point;
typedef typename K::Vector_3 Vector;
typedef typename CGAL::Convex_hull_traits_3<K> Traits;
typedef typename Traits::Polyhedron_3 Polyhedron;
std::list<Vertex_handle> vertices;
dt.incident_vertices(v,std::back_inserter(vertices));
// construct intersection of half-planes using the convex hull function
std::list<Plane> planes;
for(typename std::list<Vertex_handle>::iterator it = vertices.begin();
it != vertices.end(); ++it)
{
Vector p = ((*it)->point() - v->point())/2;
planes.push_back (Plane(CGAL::ORIGIN+p, p));
}
// add half-planes defining the sphere discretization
sphere(std::back_inserter(planes));
Polyhedron P;
#ifdef CGAL_VORONOI_COVARIANCE_USE_CONSTRUCTIONS
halfspace_intersection_with_constructions_3
#else
halfspace_intersection_3
#endif
(planes.begin(), planes.end(), P, Point(CGAL::ORIGIN));
// apply f to the triangles on the boundary of P
for (typename Polyhedron::Facet_iterator it = P.facets_begin();
it != P.facets_end(); ++it)
{
typename Polyhedron::Halfedge_around_facet_circulator
h0 = it->facet_begin(), hf = h0--, hs = hf;
hs ++;
while(1)
{
f (h0->vertex()->point(), hf->vertex()->point(),
hs->vertex()->point());
if (hs == h0)
break;
++hs; ++hf;
}
}
return f;
}
} // namespace internal
template <class DT, class Sphere, class FT>
void
voronoi_covariance_3 (const DT &dt,
typename DT::Vertex_handle v,
const Sphere &sphere,
FT covariance[6])
{
typename internal::Covariance_accumulator_3<FT> ca;
internal::tessellate_and_intersect(dt, v, sphere, ca);
std::copy (ca.result().begin(), ca.result().end(), covariance);
}
template <class DT, class Sphere>
array<typename DT::Geom_traits::FT, 6>
voronoi_covariance_3 (const DT &dt,
typename DT::Vertex_handle v,
const Sphere &sphere)
{
typedef typename DT::Geom_traits::FT FT;
typename internal::Covariance_accumulator_3<FT> ca;
return internal::tessellate_and_intersect(dt, v, sphere, ca).result();
}
template <class DT, class Sphere>
typename DT::Geom_traits::FT
voronoi_volume_3 (const DT &dt,
typename DT::Vertex_handle v,
const Sphere &sphere)
{
typedef typename DT::Geom_traits::FT FT;
typename internal::Volume_accumulator_3<FT> va;
return internal::tessellate_and_intersect(dt, v, sphere, va).result();
}
}; // namespace Voronoi_covariance_3
}; // namespace CGAL
#endif // CGAL_INTERNAL_VCM_VORONOI_COVARIANCE_3_HPP

View File

@ -0,0 +1,106 @@
// 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_INTERNAL_VCM_VORONOI_COVARIANCE_SPHERE_3_HPP
#define CGAL_INTERNAL_VCM_VORONOI_COVARIANCE_SPHERE_3_HPP
#include <CGAL/point_generators_3.h>
namespace CGAL {
namespace Voronoi_covariance_3 {
template <class K>
class Sphere_discretization
{
typedef typename K::FT FT;
FT _R;
size_t _N;
public:
Sphere_discretization (FT R, size_t N = 20) :
_R(R), _N(N)
{
if (_N != 8 && _N != 20)
_N = 20;
}
template <class OutputIterator>
void
operator ()(OutputIterator out) const
{
typedef typename K::Plane_3 Plane;
if (_N == 8)
{
static const FT phi = (FT(1) + ::sqrt(5))/FT(2);
static const FT s = FT(1) / ::sqrt(phi + FT(2));
*out ++ = Plane(0, +s, +s*phi, -_R);
*out ++ = Plane(0, -s, +s*phi, -_R);
*out ++ = Plane(0, +s, -s*phi, -_R);
*out ++ = Plane(0, -s, -s*phi, -_R);
*out ++ = Plane(+s, +s*phi, 0, -_R);
*out ++ = Plane(+s, -s*phi, 0, -_R);
*out ++ = Plane(-s, +s*phi, 0, -_R);
*out ++ = Plane(-s, -s*phi, 0, -_R);
*out ++ = Plane(+s*phi, 0, +s, -_R);
*out ++ = Plane(-s*phi, 0, +s, -_R);
*out ++ = Plane(+s*phi, 0, -s, -_R);
*out ++ = Plane(-s*phi, 0, -s, -_R);
}
else if (_N == 20)
{
const FT phi = (FT(1) + ::sqrt(5))/FT(2);
const FT one_phi = FT(1)/phi;
const FT s = FT(1) / ::sqrt(3.0);
*out ++ = Plane(+s, +s, +s, -_R);
*out ++ = Plane(-s, +s, +s, -_R);
*out ++ = Plane(+s, -s, +s, -_R);
*out ++ = Plane(-s, -s, +s, -_R);
*out ++ = Plane(+s, +s, -s, -_R);
*out ++ = Plane(-s, +s, -s, -_R);
*out ++ = Plane(+s, -s, -s, -_R);
*out ++ = Plane(-s, -s, -s, -_R);
*out ++ = Plane(0, +s*one_phi, +s*phi, -_R);
*out ++ = Plane(0, -s*one_phi, +s*phi, -_R);
*out ++ = Plane(0, +s*one_phi, -s*phi, -_R);
*out ++ = Plane(0, -s*one_phi, -s*phi, -_R);
*out ++ = Plane(+s*one_phi, +s*phi, 0, -_R);
*out ++ = Plane(-s*one_phi, +s*phi, 0, -_R);
*out ++ = Plane(+s*one_phi, -s*phi, 0, -_R);
*out ++ = Plane(-s*one_phi, -s*phi, 0, -_R);
*out ++ = Plane(+s*phi, 0, +s*one_phi, -_R);
*out ++ = Plane(-s*phi, 0, +s*one_phi, -_R);
*out ++ = Plane(+s*phi, 0, -s*one_phi, -_R);
*out ++ = Plane(-s*phi, 0, -s*one_phi, -_R);
}
}
};
}; // namespace Voronoi_covariance_3
}; // namespace CGAL
#endif // CGAL_INTERNAL_VCM_VORONOI_COVARIANCE_SPHERE_3_HPP

View File

@ -0,0 +1,81 @@
// 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 <CGAL/vcm_estimate_normals.h>
namespace CGAL {
/// \ingroup PkgPointSetProcessing
/// determines if a point is on a sharp feature edge from a point set
/// for which the Voronoi covariance Measures have been computed.
///
/// The sharpness of the edge, specified by parameter `threshold`,
/// is used to filtered points according to the external angle around a sharp feature.
///
/// A point is considered to be on a sharp feature if the external angle `alpha` at the edge is such that
/// `alpha >> 2 / sqrt(3) * sqrt(threshold)`.
/// In particular this means that if the input contains sharp features
/// with different external angles, the one with the smallest external angle should be considered,
/// which however would result in selecting more points on sharper regions.
/// More details are provided in \cgalCite{cgal:mog-vbcfe-11}.
///
/// \tparam VCM_traits is a model of `VCMTraits`. If Eigen 3 (or greater) is available and `CGAL_EIGEN3_ENABLED` is defined
/// then an overload using `Eigen_vcm_traits` is provided and this template parameter can be omitted.
/// \sa CGAL::compute_vcm()`
///
template <class FT, class VCM_traits>
bool
vcm_is_on_feature_edge (cpp11::array<FT,6> &cov,
double threshold,
VCM_traits)
{
cpp11::array<double,3> eigenvalues;
if (!VCM_traits::
diagonalize_selfadjoint_covariance_matrix(cov, eigenvalues) )
{
return false;
}
// Compute the ratio
double r = eigenvalues[1] / (eigenvalues[0] + eigenvalues[1] + eigenvalues[2]);
if (r >= threshold)
return true;
return false;
}
#ifdef CGAL_EIGEN3_ENABLED
template <class FT>
bool
vcm_is_on_feature_edge (cpp11::array<FT,6> &cov,
double threshold)
{
return vcm_is_on_feature_edge(cov, threshold, Eigen_vcm_traits());
}
#endif
} // namespace CGAL
#endif // CGAL_VCM_ESTIMATE_EDGES_H

View File

@ -0,0 +1,515 @@
// 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_NORMALS_H
#define CGAL_VCM_ESTIMATE_NORMALS_H
#include <CGAL/internal/Voronoi_covariance_3/voronoi_covariance_3.h>
#include <CGAL/property_map.h>
#include <CGAL/Delaunay_triangulation_3.h>
#include <CGAL/Kd_tree.h>
#include <CGAL/Search_traits_3.h>
#include <CGAL/Search_traits_adapter.h>
#include <CGAL/property_map.h>
#include <CGAL/Orthogonal_k_neighbor_search.h>
#include <CGAL/Fuzzy_sphere.h>
#ifdef CGAL_EIGEN3_ENABLED
#include <CGAL/Eigen_vcm_traits.h>
#endif
#include <iterator>
#include <vector>
namespace CGAL {
// ----------------------------------------------------------------------------
// Private section
// ----------------------------------------------------------------------------
namespace internal {
/// @cond SKIP_IN_MANUAL
/// Computes the VCM for each point in the property map.
/// The matrix is computed by intersecting the Voronoi cell
/// of a point and a sphere whose radius is `offset_radius` and discretized
/// by `N` planes.
///
/// @tparam ForwardIterator iterator over input points.
/// @tparam PointPMap is a model of `ReadablePropertyMap` with a value_type = `Kernel::Point_3`.
/// @tparam K Geometric traits class.
/// @tparam Covariance Covariance matrix type. It is similar to an array with a length of 6.
template < typename ForwardIterator,
typename PointPMap,
class K,
class Covariance
>
void
vcm_offset (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.
std::vector<Covariance> &cov, ///< vector of covariance matrices.
double offset_radius, ///< radius of the sphere.
std::size_t N, ///< number of planes used to discretize the sphere.
const K & /*kernel*/) ///< geometric traits.
{
// Sphere discretization
typename CGAL::Voronoi_covariance_3::Sphere_discretization<K> sphere(offset_radius, N);
// Compute the Delaunay Triangulation
std::vector<typename K::Point_3> points;
points.reserve(std::distance(first, beyond));
for (ForwardIterator it = first; it != beyond; ++it)
points.push_back(get(point_pmap, *it));
typedef Delaunay_triangulation_3<K> DT;
DT dt(points.begin(), points.end());
cov.clear();
cov.reserve(points.size());
// Compute the VCM
for (typename std::vector<typename K::Point_3>::iterator
it = points.begin(); it != points.end(); ++it)
{
typename DT::Vertex_handle vh = dt.nearest_vertex(*it);
cov.push_back(
Voronoi_covariance_3::voronoi_covariance_3(dt, vh, sphere)
);
}
}
/// @endcond
/// @cond SKIP_IN_MANUAL
// Convolve using a radius.
template < class ForwardIterator,
class PointPMap,
class K,
class Covariance
>
void
vcm_convolve (ForwardIterator first,
ForwardIterator beyond,
PointPMap point_pmap,
const std::vector<Covariance> &cov,
std::vector<Covariance> &ncov,
double convolution_radius,
const K &)
{
typedef std::pair<typename K::Point_3, std::size_t> Tree_point;
typedef First_of_pair_property_map< Tree_point > Tree_pmap;
typedef Search_traits_3<K> Traits_base;
typedef Search_traits_adapter<Tree_point, Tree_pmap, Traits_base> Traits;
typedef Kd_tree<Traits> Tree;
typedef Fuzzy_sphere<Traits> Fuzzy_sphere;
// Kd tree
Tree tree;
tree.reserve(cov.size());
std::size_t i=0;
for (ForwardIterator it = first; it != beyond; ++it, ++i)
tree.insert( Tree_point(get(point_pmap, *it), i) );
// Convolving
ncov.clear();
ncov.reserve(cov.size());
for (ForwardIterator it = first; it != beyond; ++it) {
std::vector<Tree_point> nn;
tree.search(std::back_inserter(nn),
Fuzzy_sphere (get(point_pmap, *it), convolution_radius));
Covariance m;
std::fill(m.begin(), m.end(), typename K::FT(0));
for (std::size_t k = 0; k < nn.size(); ++k)
{
std::size_t index = nn[k].second;
for (int i=0; i<6; ++i)
m[i] += cov[index][i];
}
ncov.push_back(m);
}
}
/// @endcond
/// @cond SKIP_IN_MANUAL
// Convolve using neighbors.
template < class ForwardIterator,
class PointPMap,
class K,
class Covariance
>
void
vcm_convolve (ForwardIterator first,
ForwardIterator beyond,
PointPMap point_pmap,
const std::vector<Covariance> &cov,
std::vector<Covariance> &ncov,
unsigned int nb_neighbors_convolve,
const K &)
{
typedef std::pair<typename K::Point_3, std::size_t> Tree_point;
typedef First_of_pair_property_map< Tree_point > Tree_pmap;
typedef Search_traits_3<K> Traits_base;
typedef Search_traits_adapter<Tree_point, Tree_pmap, Traits_base> Traits;
typedef Orthogonal_k_neighbor_search<Traits> Neighbor_search;
typedef typename Neighbor_search::Tree Tree;
// Search tree
Tree tree;
tree.reserve(cov.size());
std::size_t i=0;
for (ForwardIterator it = first; it != beyond; ++it, ++i)
tree.insert( Tree_point(get(point_pmap, *it), i) );
// Convolving
ncov.clear();
ncov.reserve(cov.size());
for (ForwardIterator it = first; it != beyond; ++it) {
Neighbor_search search(tree, get(point_pmap, *it), nb_neighbors_convolve);
Covariance m;
for (typename Neighbor_search::iterator nit = search.begin();
nit != search.end();
++nit)
{
std::size_t index = nit->first.second;
for (int i=0; i<6; ++i)
m[i] += cov[index][i];
}
ncov.push_back(m);
}
}
/// @endcond
} // namespace internal
// ----------------------------------------------------------------------------
// Public section
// ----------------------------------------------------------------------------
/// \ingroup PkgPointSetProcessing
/// computes the Voronoi Covariance Measure (VCM) of a point cloud,
/// a construction that can be used for normal estimation and sharp feature detection.
///
/// The VCM associates to each point the covariance matrix of its Voronoi
/// cell intersected with the ball of radius `offset_radius`.
/// In addition, if the second radius `convolution_radius` is positive, the covariance matrices are smoothed
/// via a convolution process. More specifically, each covariance matrix is replaced by
/// the average of the matrices of the points located at a distance at most `convolution_radius`.
/// The choice for parameter `offset_radius` should refer to the geometry of the underlying surface while
/// the choice for parameter `convolution_radius` should refer to the noise level in the point cloud.
/// For example, if the point cloud is a uniform and noise-free sampling of a smooth surface,
/// `offset_radius` should be set to the minimum local feature size of the surface, while `convolution_radius` can be set to zero.
///
/// The Voronoi covariance matrix of each vertex is stored in an array `a` of length 6 and is as follow:
/*!
<center>
\f$ \begin{bmatrix}
a[0] & a[1] & a[2] \\
a[1] & a[3] & a[4] \\
a[2] & a[4] & a[5] \\
\end{bmatrix}\f$
</center>
*/
///
/// @tparam ForwardIterator iterator over input points.
/// @tparam PointPMap is a model of `ReadablePropertyMap` with a value_type = `Kernel::Point_3`.
/// @tparam CovariancePMap is a model of `ReadWritePropertyMap` with a value_type = `cpp11::array<Kernel::FT,6>`.
/// @tparam Kernel Geometric traits class.
///
/// \sa `CGAL::vcm_is_on_feature_edge()`
/// \sa `CGAL::vcm_estimate_normals()`
///
/// \todo replace ccov by a property map.
template < class ForwardIterator,
class PointPMap,
class Kernel
>
void
compute_vcm (ForwardIterator first,
ForwardIterator beyond,
PointPMap point_pmap,
std::vector< cpp11::array<typename Kernel::FT, 6> > &ccov,
double offset_radius,
double convolution_radius,
const Kernel & kernel)
{
// First, compute the VCM for each point
std::vector< cpp11::array<typename Kernel::FT, 6> > cov;
std::size_t N = 20;
internal::vcm_offset (first, beyond,
point_pmap,
cov,
offset_radius,
N,
kernel);
// Then, convolve it (only when convolution_radius != 0)
if (convolution_radius == 0) {
ccov.reserve(cov.size());
std::copy(cov.begin(), cov.end(), std::back_inserter(ccov));
} else {
internal::vcm_convolve(first, beyond,
point_pmap,
cov,
ccov,
convolution_radius,
kernel);
}
}
/// @cond SKIP_IN_MANUAL
/// Estimates normal directions of the `[first, beyond)` range of points
/// using the Voronoi Covariance Measure (see `compute_vcm` for more details on the VCM).
/// The output normals are randomly oriented.
///
/// @tparam ForwardIterator iterator over input points.
/// @tparam PointPMap is a model of `ReadablePropertyMap` with a value_type = `Kernel::Point_3`.
/// @tparam NormalPMap is a model of `WritablePropertyMap` with a value_type = `Kernel::Vector_3`.
/// @tparam Kernel Geometric traits class.
/// @tparam Covariance Covariance matrix type. It is similar to an array with a length of 6.
/// @pre If `nb_neighbors_convolve` is equal to -1, then the convolution is made using a radius.
/// On the contrary, if `nb_neighbors_convolve` is different from -1, the convolution is made using
/// this number of neighbors.
// This variant requires all of the parameters.
template < typename VCM_traits,
typename ForwardIterator,
typename PointPMap,
typename NormalPMap,
typename Kernel
>
void
vcm_estimate_normals (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.
NormalPMap normal_pmap, ///< property map: value_type of ForwardIterator -> Vector_3.
double offset_radius, ///< offset radius.
double convolution_radius, ///< convolution radius.
const Kernel & kernel, ///< geometric traits.
int nb_neighbors_convolve = -1 ///< number of neighbors used during the convolution.
)
{
typedef cpp11::array<double, 6> Covariance;
// Compute the VCM and convolve it
std::vector<Covariance> cov;
if (nb_neighbors_convolve == -1) {
compute_vcm(first, beyond,
point_pmap,
cov,
offset_radius,
convolution_radius,
kernel);
} else {
internal::vcm_offset(first, beyond,
point_pmap,
cov,
offset_radius,
20,
kernel);
if (nb_neighbors_convolve > 0)
{
std::vector<Covariance> ccov;
ccov.reserve(cov.size());
internal::vcm_convolve(first, beyond,
point_pmap,
cov,
ccov,
(unsigned int) nb_neighbors_convolve,
kernel);
cov.clear();
std::copy(ccov.begin(), ccov.end(), std::back_inserter(cov));
}
}
// And finally, compute the normals
int i = 0;
for (ForwardIterator it = first; it != beyond; ++it) {
cpp11::array<double, 3> enormal = {{ 0,0,0 }};
VCM_traits::extract_largest_eigenvector_of_covariance_matrix
(cov[i], enormal);
typename Kernel::Vector_3 normal(enormal[0],
enormal[1],
enormal[2]);
put(normal_pmap, *it, normal);
i++;
}
}
/// @endcond
/// \ingroup PkgPointSetProcessing
/// Estimates normal directions of the points in the range `[first, beyond)`
/// using the Voronoi Covariance Measure with a radius for the convolution.
/// The output normals are randomly oriented.
///
/// See `compute_vcm()` for a detailed description of the parameters `offset_radius` and `convolution_radius`
/// and of the Voronoi Covariance Measure.
///
/// @tparam ForwardIterator iterator over input points.
/// @tparam PointPMap is a model of `ReadablePropertyMap` with a value_type = `Kernel::Point_3`.
/// @tparam NormalPMap is a model of `WritablePropertyMap` with a value_type = `Kernel::Vector_3`.
/// \tparam VCM_traits is a model of `VCMTraits`. If Eigen 3 (or greater) is available and `CGAL_EIGEN3_ENABLED` is defined
/// then an overload using `Eigen_vcm_traits` is provided and this template parameter can be omitted.
// This variant deduces the kernel from the point property map
// and uses a radius for the convolution.
template < typename ForwardIterator,
typename PointPMap,
typename NormalPMap,
typename VCM_traits
>
void
vcm_estimate_normals (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.
NormalPMap normal_pmap, ///< property map: value_type of ForwardIterator -> Vector_3.
double offset_radius, ///< offset radius.
double convolution_radius, ///< convolution radius.
VCM_traits
)
{
typedef typename boost::property_traits<PointPMap>::value_type Point;
typedef typename Kernel_traits<Point>::Kernel Kernel;
vcm_estimate_normals<VCM_traits>(first, beyond,
point_pmap, normal_pmap,
offset_radius, convolution_radius,
Kernel());
}
/// \ingroup PkgPointSetProcessing
/// Estimates normal directions of the points in the range `[first, beyond)`
/// using the Voronoi Covariance Measure with a number of neighbors for the convolution.
/// The output normals are randomly oriented.
///
/// See `compute_vcm()` for a detailed description of the parameter `offset_radius`
/// and of the Voronoi Covariance Measure.
///
/// @tparam ForwardIterator iterator over input points.
/// @tparam PointPMap is a model of `ReadablePropertyMap` with a value_type = `Kernel::Point_3`.
/// @tparam NormalPMap is a model of `WritablePropertyMap` with a value_type = `Kernel::Vector_3`.
/// \tparam VCM_traits is a model of `VCMTraits`. If Eigen 3 (or greater) is available and `CGAL_EIGEN3_ENABLED` is defined
/// then an overload using `Eigen_vcm_traits` is provided and this template parameter can be omitted.
// This variant deduces the kernel from the point property map
// and uses a number of neighbors for the convolution.
template < typename ForwardIterator,
typename PointPMap,
typename NormalPMap,
typename VCM_traits
>
void
vcm_estimate_normals (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.
NormalPMap normal_pmap, ///< property map: value_type of ForwardIterator -> Vector_3.
double offset_radius, ///< offset radius.
unsigned int k, ///< number of neighbor points used for the convolution.
VCM_traits
)
{
typedef typename boost::property_traits<PointPMap>::value_type Point;
typedef typename Kernel_traits<Point>::Kernel Kernel;
vcm_estimate_normals<VCM_traits>(first, beyond,
point_pmap, normal_pmap,
offset_radius, 0,
Kernel(),
k);
}
#ifdef CGAL_EIGEN3_ENABLED
template < typename ForwardIterator,
typename PointPMap,
typename NormalPMap
>
void
vcm_estimate_normals (ForwardIterator first,
ForwardIterator beyond,
PointPMap point_pmap,
NormalPMap normal_pmap,
double offset_radius,
double convolution_radius)
{
vcm_estimate_normals(first, beyond, point_pmap, normal_pmap, offset_radius, convolution_radius, Eigen_vcm_traits());
}
template < typename ForwardIterator,
typename PointPMap,
typename NormalPMap
>
void
vcm_estimate_normals (ForwardIterator first,
ForwardIterator beyond,
PointPMap point_pmap,
NormalPMap normal_pmap,
double offset_radius,
unsigned int nb_neighbors_convolve)
{
vcm_estimate_normals(first, beyond, point_pmap, normal_pmap, offset_radius, nb_neighbors_convolve, Eigen_vcm_traits());
}
#endif
/// @cond SKIP_IN_MANUAL
// This variant creates a default point property map = Identity_property_map
// and use a radius for the convolution.
template < typename ForwardIterator,
typename NormalPMap
>
void
vcm_estimate_normals (ForwardIterator first,
ForwardIterator beyond,
NormalPMap normal_pmap,
double offset_radius,
double convolution_radius) {
vcm_estimate_normals(first, beyond,
make_identity_property_map(typename std::iterator_traits<ForwardIterator>::value_type()),
normal_pmap,
offset_radius, convolution_radius);
}
/// @endcond
/// @cond SKIP_IN_MANUAL
// This variant creates a default point property map = Identity_property_map
// and use a number of neighbors for the convolution.
template < typename ForwardIterator,
typename NormalPMap
>
void
vcm_estimate_normals (ForwardIterator first,
ForwardIterator beyond,
NormalPMap normal_pmap,
double offset_radius,
unsigned int nb_neighbors_convolve) {
vcm_estimate_normals(first, beyond,
make_identity_property_map(typename std::iterator_traits<ForwardIterator>::value_type()),
normal_pmap,
offset_radius, nb_neighbors_convolve);
}
/// @endcond
} // namespace CGAL
#endif // CGAL_VCM_ESTIMATE_NORMALS_H

View File

@ -76,6 +76,8 @@ if ( CGAL_FOUND )
# Executables that require Eigen or BLAS and LAPACK
create_single_source_cgal_program( "normal_estimation_test.cpp" )
create_single_source_cgal_program( "smoothing_test.cpp" )
create_single_source_cgal_program( "vcm_plane_test.cpp" )
create_single_source_cgal_program( "vcm_all_test.cpp" )
else(EIGEN3_FOUND OR LAPACK_FOUND)

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,41 @@
#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
#include <CGAL/vcm_estimate_normals.h>
#include <cassert>
#include <vector>
typedef CGAL::Exact_predicates_inexact_constructions_kernel Kernel;
typedef CGAL::Point_3<Kernel> Point_3;
typedef CGAL::Vector_3<Kernel> Vector_3;
typedef std::pair<Point_3, Vector_3> PointWithNormal;
int main (void) {
// Generate points on a plane
int k = 100;
float r = 10;
std::vector<PointWithNormal> points;
points.push_back(std::make_pair(Point_3(0, 0, 0), Vector_3(0, 0, 0)));
for (int i = 0; i < k; ++i) {
float theta = 2 * i * M_PI / k;
points.push_back(std::make_pair(Point_3(r * cos(theta), r * sin(theta), 0),
Vector_3(0, 0, 0)));
}
// Estimate the normals using VCM
float R = 20;
vcm_estimate_normals(points.begin(), points.end(),
CGAL::First_of_pair_property_map<PointWithNormal>(),
CGAL::Second_of_pair_property_map<PointWithNormal>(),
R, 0.0);
std::cout << "Normal is " << points[0].second << std::endl;
// The normal at the origin should be (0, 0, -1)
double epsilon=1e-5;
assert(points[0].second.x() < epsilon && points[0].second.x() > -epsilon);
assert(points[0].second.y() < epsilon && points[0].second.y() > -epsilon);
assert(points[0].second.z() < -1+epsilon && points[0].second.z() > -1-epsilon);
return 0;
}

View File

@ -372,6 +372,14 @@ if(CGAL_Qt4_FOUND AND QT4_FOUND AND OPENGL_FOUND AND QGLVIEWER_FOUND)
qt4_wrap_ui( normal_estimationUI_FILES Polyhedron_demo_normal_estimation_plugin.ui)
polyhedron_demo_plugin(normal_estimation_plugin Polyhedron_demo_normal_estimation_plugin ${normal_estimationUI_FILES})
target_link_libraries(normal_estimation_plugin scene_points_with_normal_item)
qt4_wrap_ui( vcm_normal_estimationUI_FILES Polyhedron_demo_vcm_normal_estimation_plugin.ui)
polyhedron_demo_plugin(vcm_normal_estimation_plugin Polyhedron_demo_vcm_normal_estimation_plugin ${vcm_normal_estimationUI_FILES})
target_link_libraries(vcm_normal_estimation_plugin scene_points_with_normal_item)
qt4_wrap_ui( features_detection_pluginUI_FILES Polyhedron_demo_features_detection_plugin.ui)
polyhedron_demo_plugin(features_detection_plugin Polyhedron_demo_features_detection_plugin ${features_detection_pluginUI_FILES})
target_link_libraries(features_detection_plugin scene_points_with_normal_item)
polyhedron_demo_plugin(point_set_smoothing_plugin Polyhedron_demo_point_set_smoothing_plugin)
target_link_libraries(point_set_smoothing_plugin scene_points_with_normal_item)
@ -384,11 +392,12 @@ if(CGAL_Qt4_FOUND AND QT4_FOUND AND OPENGL_FOUND AND QGLVIEWER_FOUND)
else(EIGEN3_FOUND)
message(STATUS "NOTICE: Eigen 3.1 (or greater) was not found. Parameterization plugin will not be available.")
message(STATUS "NOTICE: Eigen 3.1 (or greater) was not found. Poisson reconstruction plugin will not be available.")
message(STATUS "NOTICE: Eigen 3.1 (or greater) was not found. Normal estimation plugin will not be available.")
message(STATUS "NOTICE: Eigen 3.1 (or greater) was not found. Normal estimation plugins will not be available.")
message(STATUS "NOTICE: Eigen 3.1 (or greater) was not found. Smoothing plugin will not be available.")
message(STATUS "NOTICE: Eigen 3.1 (or greater) was not found. Average spacing plugin will not be available.")
message(STATUS "NOTICE: Eigen 3.1 (or greater) was not found. Jet fitting plugin will not be available.")
message(STATUS "NOTICE: Eigen 3.1 (or greater) was not found. Scale space reconstruction plugin will not be available.")
message(STATUS "NOTICE: Eigen 3.1 (or greater) was not found. Feature detection plugin will not be available.")
endif(EIGEN3_FOUND)
polyhedron_demo_plugin(self_intersection_plugin Polyhedron_demo_self_intersection_plugin)

View File

@ -0,0 +1,116 @@
#include <QApplication>
#include <QMessageBox>
#include <QMainWindow>
#include "config.h"
#include "Scene_points_with_normal_item.h"
#include "Polyhedron_demo_plugin_helper.h"
#include "Polyhedron_demo_plugin_interface.h"
#include <CGAL/vcm_estimate_edges.h>
#include <CGAL/Timer.h>
#include "ui_Polyhedron_demo_features_detection_plugin.h"
class Polyhedron_demo_features_detection_plugin :
public QObject,
public Polyhedron_demo_plugin_helper
{
Q_OBJECT
Q_INTERFACES(Polyhedron_demo_plugin_interface)
QAction* actionDetectFeatures;
public:
QList<QAction*> actions() const { return QList<QAction*>() << actionDetectFeatures; }
void init(QMainWindow* mainWindow, Scene_interface* scene_interface)
{
actionDetectFeatures= new QAction(tr("VCM features estimation"), mainWindow);
actionDetectFeatures->setObjectName("actionDetectFeatures");
Polyhedron_demo_plugin_helper::init(mainWindow, scene_interface);
}
bool applicable() const {
return qobject_cast<Scene_points_with_normal_item*>(scene->item(scene->mainSelectionIndex()));
}
public slots:
void on_actionDetectFeatures_triggered();
}; // end Polyhedron_demo_features_detection_plugin
class Polyhedron_demo_features_detection_dialog : public QDialog, private Ui::VCMFeaturesDetectionDialog
{
Q_OBJECT
public:
Polyhedron_demo_features_detection_dialog(QWidget* /*parent*/ = 0)
{
setupUi(this);
}
float offsetRadius() const { return m_inputOffsetRadius->value(); }
float convolveRadius() const { return m_inputConvolveRadius->value(); }
float threshold() const { return m_inputFeaturesThreshold->value(); }
};
void Polyhedron_demo_features_detection_plugin::on_actionDetectFeatures_triggered()
{
const Scene_interface::Item_id index = scene->mainSelectionIndex();
Scene_points_with_normal_item* item =
qobject_cast<Scene_points_with_normal_item*>(scene->item(index));
if(item)
{
// Gets point set
Point_set* points = item->point_set();
if(points == NULL)
return;
// Gets options
Polyhedron_demo_features_detection_dialog dialog;
if(!dialog.exec())
return;
typedef CGAL::cpp11::array<double,6> Covariance;
std::vector<Covariance> cov;
std::cerr << "Compute VCM (offset_radius="
<< dialog.offsetRadius() << " and convolution radius=" << dialog.convolveRadius() << ")...\n";
CGAL::Timer task_timer; task_timer.start();
CGAL::compute_vcm(points->begin(), points->end(),
CGAL::make_identity_property_map(Point_set::value_type()),
cov, dialog.offsetRadius(), dialog.convolveRadius(),
Kernel());
task_timer.stop();
std::cerr << "done: " << task_timer.time() << " seconds\n";
Scene_points_with_normal_item* new_item = new Scene_points_with_normal_item();
task_timer.reset(); task_timer.start();
std::size_t i=0;
std::cerr << "Select feature points (threshold=" << dialog.threshold() << ")...\n";
BOOST_FOREACH(const Point_set::value_type& p, *points)
{
if (CGAL::vcm_is_on_feature_edge(cov[i], dialog.threshold()))
new_item->point_set()->push_back(p);
++i;
}
task_timer.stop();
std::cerr << "done: " << task_timer.time() << " seconds\n";
if ( !new_item->point_set()->empty() )
{
new_item->setName(tr("Features of %1").arg(item->name()));
new_item->setColor(Qt::red);
scene->addItem(new_item);
item->setVisible(false);
}
else
delete new_item;
}
}
Q_EXPORT_PLUGIN2(Polyhedron_demo_features_detection_plugin, Polyhedron_demo_features_detection_plugin)
#include "Polyhedron_demo_features_detection_plugin.moc"

View File

@ -0,0 +1,133 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>VCMFeaturesDetectionDialog</class>
<widget class="QDialog" name="VCMFeaturesDetectionDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>318</width>
<height>208</height>
</rect>
</property>
<property name="windowTitle">
<string>Normal estimation</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QLabel" name="label">
<property name="text">
<string>Offset Radius:</string>
</property>
</widget>
</item>
<item>
<widget class="QDoubleSpinBox" name="m_inputOffsetRadius">
<property name="decimals">
<number>3</number>
</property>
<property name="singleStep">
<double>0.050000000000000</double>
</property>
<property name="value">
<double>0.100000000000000</double>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
<widget class="QLabel" name="label_2">
<property name="text">
<string>Convolution Radius:</string>
</property>
</widget>
</item>
<item>
<widget class="QDoubleSpinBox" name="m_inputConvolveRadius">
<property name="decimals">
<number>3</number>
</property>
<property name="singleStep">
<double>0.050000000000000</double>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QLabel" name="label_3">
<property name="text">
<string>Threshold:</string>
</property>
</widget>
</item>
<item>
<widget class="QDoubleSpinBox" name="m_inputFeaturesThreshold">
<property name="decimals">
<number>3</number>
</property>
<property name="singleStep">
<double>0.020000000000000</double>
</property>
<property name="value">
<double>0.160000000000000</double>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>VCMFeaturesDetectionDialog</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>248</x>
<y>254</y>
</hint>
<hint type="destinationlabel">
<x>157</x>
<y>274</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>VCMFeaturesDetectionDialog</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>316</x>
<y>260</y>
</hint>
<hint type="destinationlabel">
<x>286</x>
<y>274</y>
</hint>
</hints>
</connection>
</connections>
</ui>

View File

@ -0,0 +1,155 @@
#include "config.h"
#include "Scene_points_with_normal_item.h"
#include "Polyhedron_demo_plugin_helper.h"
#include "Polyhedron_demo_plugin_interface.h"
#include <CGAL/vcm_estimate_normals.h>
#include <CGAL/mst_orient_normals.h>
#include <CGAL/Timer.h>
#include <CGAL/Memory_sizer.h>
#include <QObject>
#include <QAction>
#include <QMainWindow>
#include <QApplication>
#include <QtPlugin>
#include <QInputDialog>
#include <QMessageBox>
#include "ui_Polyhedron_demo_vcm_normal_estimation_plugin.h"
class Polyhedron_demo_vcm_normal_estimation_plugin :
public QObject,
public Polyhedron_demo_plugin_helper
{
Q_OBJECT
Q_INTERFACES(Polyhedron_demo_plugin_interface)
QAction* actionVCMNormalEstimation;
QAction* actionNormalInversion;
public:
void init(QMainWindow* mainWindow, Scene_interface* scene_interface) {
actionVCMNormalEstimation = new QAction(tr("VCM normal estimation"), mainWindow);
actionVCMNormalEstimation->setObjectName("actionVCMNormalEstimation");
Polyhedron_demo_plugin_helper::init(mainWindow, scene_interface);
}
QList<QAction*> actions() const {
return QList<QAction*>() << actionVCMNormalEstimation;
}
bool applicable() const {
return qobject_cast<Scene_points_with_normal_item*>(scene->item(scene->mainSelectionIndex()));
}
public slots:
void on_actionVCMNormalEstimation_triggered();
}; // end Polyhedron_demo_vcm_normal_estimation_plugin
class Point_set_demo_normal_estimation_dialog : public QDialog, private Ui::VCMNormalEstimationDialog
{
Q_OBJECT
public:
Point_set_demo_normal_estimation_dialog(QWidget* /*parent*/ = 0)
{
setupUi(this);
}
float offsetRadius() const { return m_inputOffsetRadius->value(); }
float convolveRadius() const { return m_inputConvolveRadius->value(); }
unsigned int convolveNeighbors() const { return m_inputConvolveNeighbors->value(); }
bool convolveUsingRadius() const { return m_inputUseConvRad->isChecked(); }
};
void Polyhedron_demo_vcm_normal_estimation_plugin::on_actionVCMNormalEstimation_triggered()
{
const Scene_interface::Item_id index = scene->mainSelectionIndex();
Scene_points_with_normal_item* item =
qobject_cast<Scene_points_with_normal_item*>(scene->item(index));
if(item)
{
// Gets point set
Point_set* points = item->point_set();
if(points == NULL)
return;
// Gets options
Point_set_demo_normal_estimation_dialog dialog;
if(!dialog.exec())
return;
QApplication::setOverrideCursor(Qt::WaitCursor);
// First point to delete
Point_set::iterator first_unoriented_point = points->end();
//***************************************
// VCM normal estimation
//***************************************
CGAL::Timer task_timer; task_timer.start();
// Estimates normals direction.
if (dialog.convolveUsingRadius())
{
std::cerr << "Estimates Normals Direction using VCM (R="
<< dialog.offsetRadius() << " and r=" << dialog.convolveRadius() << ")...\n";
CGAL::vcm_estimate_normals(points->begin(), points->end(),
CGAL::make_normal_of_point_with_normal_pmap(Point_set::value_type()),
dialog.offsetRadius(), dialog.convolveRadius());
}
else
{
std::cerr << "Estimates Normals Direction using VCM (R="
<< dialog.offsetRadius() << " and k=" << dialog.convolveNeighbors() << ")...\n";
CGAL::vcm_estimate_normals(points->begin(), points->end(),
CGAL::make_normal_of_point_with_normal_pmap(Point_set::value_type()),
dialog.offsetRadius(), dialog.convolveNeighbors());
}
// Mark all normals as unoriented
first_unoriented_point = points->begin();
std::size_t memory = CGAL::Memory_sizer().virtual_size();
task_timer.stop();
std::cerr << "Estimates normal direction: " << task_timer.time() << " seconds, "
<< (memory>>20) << " Mb allocated"
<< std::endl;
//***************************************
// normal orientation
//***************************************
unsigned int neighbors = 18;
task_timer.reset();
task_timer.start();
std::cerr << "Orient normals with a Minimum Spanning Tree (k=" << neighbors << ")...\n";
// Tries to orient normals
first_unoriented_point = CGAL::mst_orient_normals(points->begin(), points->end(),
CGAL::make_normal_of_point_with_normal_pmap(Point_set::value_type()),
neighbors);
std::size_t nb_unoriented_normals = std::distance(first_unoriented_point, points->end());
memory = CGAL::Memory_sizer().virtual_size();
std::cerr << "Orient normals: " << nb_unoriented_normals << " point(s) with an unoriented normal are selected ("
<< task_timer.time() << " seconds, "
<< (memory>>20) << " Mb allocated)"
<< std::endl;
// Updates scene
scene->itemChanged(index);
QApplication::restoreOverrideCursor();
}
}
Q_EXPORT_PLUGIN2(Polyhedron_demo_vcm_normal_estimation_plugin, Polyhedron_demo_vcm_normal_estimation_plugin)
#include "Polyhedron_demo_vcm_normal_estimation_plugin.moc"

View File

@ -0,0 +1,149 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>VCMNormalEstimationDialog</class>
<widget class="QDialog" name="VCMNormalEstimationDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>462</width>
<height>401</height>
</rect>
</property>
<property name="windowTitle">
<string>Normal estimation</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QLabel" name="label">
<property name="text">
<string>Offset Radius:</string>
</property>
</widget>
</item>
<item>
<widget class="QDoubleSpinBox" name="m_inputOffsetRadius">
<property name="decimals">
<number>3</number>
</property>
<property name="singleStep">
<double>0.050000000000000</double>
</property>
<property name="value">
<double>0.100000000000000</double>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QLabel" name="label_2">
<property name="text">
<string>Convolution radius:</string>
</property>
</widget>
</item>
<item>
<widget class="QDoubleSpinBox" name="m_inputConvolveRadius">
<property name="decimals">
<number>3</number>
</property>
<property name="singleStep">
<double>0.050000000000000</double>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
<widget class="QLabel" name="label_3">
<property name="text">
<string>Convolution Neighbors:</string>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="m_inputConvolveNeighbors">
<property name="suffix">
<string> neighbors</string>
</property>
<property name="value">
<number>40</number>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QCheckBox" name="m_inputUseConvRad">
<property name="text">
<string>Use Convolution Radius</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
</layout>
<zorder>label</zorder>
<zorder>buttonBox</zorder>
<zorder>m_inputOffsetRadius</zorder>
<zorder>m_inputConvolveRadius</zorder>
<zorder>label_2</zorder>
<zorder>label_3</zorder>
<zorder>m_inputConvolveNeighbors</zorder>
<zorder>label_2</zorder>
<zorder>m_inputUseConvRad</zorder>
</widget>
<resources/>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>VCMNormalEstimationDialog</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>248</x>
<y>254</y>
</hint>
<hint type="destinationlabel">
<x>157</x>
<y>274</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>VCMNormalEstimationDialog</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>316</x>
<y>260</y>
</hint>
<hint type="destinationlabel">
<x>286</x>
<y>274</y>
</hint>
</hints>
</connection>
</connections>
</ui>