Merge pull request #150 from 'cjamin/Point_set_shape_detection_3-cjamin'

Add an algorithm to fit geometric primitives on a point set

Conflicts:
	Installation/changes.html
	Polyhedron/demo/Polyhedron/CMakeLists.txt
This commit is contained in:
Sébastien Loriot 2015-07-17 14:32:09 +02:00
commit 2668d83d4e
66 changed files with 108182 additions and 3 deletions

View File

@ -151957,3 +151957,14 @@ pages = {179--189}
keywords = {Design and analysis of algorithms, computational geometry, shortest path problems}, keywords = {Design and analysis of algorithms, computational geometry, shortest path problems},
} }
@inproceedings{schnabel2007efficient,
title={Efficient RANSAC for Point-Cloud Shape Detection},
author={Schnabel, Ruwen and Wahl, Roland and Klein, Reinhard},
booktitle={Computer graphics forum},
volume={26},
number={2},
pages={214--226},
year={2007},
organization={Wiley Online Library}
}

View File

@ -101,6 +101,7 @@ IMAGE_PATH = ${CMAKE_SOURCE_DIR}/Documentation/doc/Documentation/fig \
${CMAKE_SOURCE_DIR}/Ridges_3/doc/Ridges_3/fig \ ${CMAKE_SOURCE_DIR}/Ridges_3/doc/Ridges_3/fig \
${CMAKE_SOURCE_DIR}/Point_set_processing_3/doc/Point_set_processing_3/fig \ ${CMAKE_SOURCE_DIR}/Point_set_processing_3/doc/Point_set_processing_3/fig \
${CMAKE_SOURCE_DIR}/Point_set_processing_3/doc/Property_map/fig \ ${CMAKE_SOURCE_DIR}/Point_set_processing_3/doc/Property_map/fig \
${CMAKE_SOURCE_DIR}/Point_set_shape_detection_3/doc/Point_set_shape_detection_3/fig \
${CMAKE_SOURCE_DIR}/Kinetic_data_structures/doc/Kinetic_framework/fig \ ${CMAKE_SOURCE_DIR}/Kinetic_data_structures/doc/Kinetic_framework/fig \
${CMAKE_SOURCE_DIR}/Periodic_2_triangulation_2/doc/Periodic_2_triangulation_2/fig \ ${CMAKE_SOURCE_DIR}/Periodic_2_triangulation_2/doc/Periodic_2_triangulation_2/fig \
${CMAKE_SOURCE_DIR}/Periodic_3_triangulation_3/doc/Periodic_3_triangulation_3/fig \ ${CMAKE_SOURCE_DIR}/Periodic_3_triangulation_3/doc/Periodic_3_triangulation_3/fig \

View File

@ -65,6 +65,7 @@ Triangulation_3
Triangulation Triangulation
Ridges_3 Ridges_3
Point_set_processing_3 Point_set_processing_3
Point_set_shape_detection_3
Polyline_simplification_2 Polyline_simplification_2
Property_map Property_map
Kinetic_framework Kinetic_framework

View File

@ -111,6 +111,7 @@ h1 {
\package_listing{Ridges_3} \package_listing{Ridges_3}
\package_listing{Jet_fitting_3} \package_listing{Jet_fitting_3}
\package_listing{Point_set_processing_3} \package_listing{Point_set_processing_3}
\package_listing{Point_set_shape_detection_3}
\package_listing{Stream_lines_2} \package_listing{Stream_lines_2}

View File

@ -187,6 +187,18 @@ and <code>src/</code> directories).
<!-- Point_set_shape_detection_3-cjamin begin --> <!-- Point_set_shape_detection_3-cjamin begin -->
<h3>3D Point-Set Shape Detection (new package)</h3>
<ul>
<li>
This package implements the efficient RANSAC method for shape detection,
contributed by Schnabel et al. From an unstructured point set with
unoriented normals, the algorithm detects a set of shapes. Five
types of primitive shapes are provided by this package: plane,
sphere, cylinder, cone and torus. Detecting other types of shapes
is possible by implementing a class derived from a base shape.
</li>
</ul>
<!-- Point_set_shape_detection_3-cjamin end --> <!-- Point_set_shape_detection_3-cjamin end -->
<!-- gsoc2013-Visibility_doc-hemmer begin --> <!-- gsoc2013-Visibility_doc-hemmer begin -->

View File

@ -77,6 +77,7 @@ read_xyz_points_and_normals(
//typedef typename value_type_traits<OutputIterator>::type Enriched_point; //typedef typename value_type_traits<OutputIterator>::type Enriched_point;
typedef OutputIteratorValueType Enriched_point; typedef OutputIteratorValueType Enriched_point;
typedef typename Kernel::FT FT;
typedef typename Kernel::Point_3 Point; typedef typename Kernel::Point_3 Point;
typedef typename Kernel::Vector_3 Vector; typedef typename Kernel::Vector_3 Vector;
@ -95,8 +96,8 @@ read_xyz_points_and_normals(
while(getline(stream,line)) while(getline(stream,line))
{ {
// position + normal // position + normal
double x,y,z; FT x,y,z;
double nx,ny,nz; FT nx,ny,nz;
lineNumber++; lineNumber++;

View File

@ -0,0 +1,350 @@
/*!
\ingroup PkgPointSetShapeDetection3Concepts
\cgalConcept
Concept describing the set of types required by the class `CGAL::Shape_detection_3::Efficient_RANSAC` and all shape classes.
To avoid copying potentially large input data, the shape detection class
`CGAL::Shape_detection_3::Efficient_RANSAC` will work on the input
data directly and no internal copy will be made. For this reason, the
input data has to be provided in form of a random access iterator.
Point and normal property maps have to
be provided to extract the points and the normals from the input.
\cgalHasModel `CGAL::Shape_detection_3::Efficient_RANSAC_traits`
*/
class EfficientRANSACTraits{
public:
/// \name Types
/// @{
/// The point type
typedef unspecified_type Point_3;
/// The vector type
typedef unspecified_type Vector_3;
/// The sphere type, only required if you want to detect spheres or tori
typedef unspecified_type Sphere_3;
/// The line type, only required if you want to detect cylinders
typedef unspecified_type Line_3;
/// The plane type, only required if you want to detect planes
typedef unspecified_type Plane_3;
/// The 2D point type, only required if you want to detect tori
typedef unspecified_type Point_2;
/// The circle type, only required if you want to detect tori
typedef unspecified_type Circle_2;
/// The 2D vector type, only required if you want to detect tori
typedef unspecified_type Vector_3;
/// The number type of the Cartesian coordinates of types Point_3
typedef unspecified_type FT;
/// A model of the concept `Range` with random access iterators, providing input points and normals
/// through the two property maps `Point_map` and `Normal_map`.
typedef unspecified_type Input_range;
/// A model of `ReadablePropertyMap` with
/// `std::iterator_traits<Input_range::iterator>::%value_type`
/// as key type and `Point_3` as value type.
typedef unspecified_type Point_map;
/// A model of `ReadablePropertyMap` with
/// `std::iterator_traits<Input_range::iterator>::%value_type`
/// as key type and `Vector_3` as value type.
typedef unspecified_type Normal_map;
/// a model of `SearchTraits`
/// where `SearchTraits::point_d` is `Point_3`,
/// `SearchTraits::Dimension` is ` CGAL::Dimension_tag<3>`,
/// and `SearchTraits::FT` is ` FT`,
typedef unspecified_type Search_traits;
/*!
* Function object type that provides
* `Point_3 operator()(FT x, FT y, FT z)`
* returning the point with `x`, `y` and `z` as Cartesian coordinates.
*/
typedef unspecified_type Construct_point_3;
/*!
* Function object type that provides
* `Vector_3 operator()(Point_3 p1, Point_3 p2)` and
* `Vector_3 operator()(Origin p1, Point_3 p2)`
* returning the vector `p1p2`, `Vector_3 operator()(NULL_VECTOR)` returning
* the null vector, and `Vector_3 operator()(Line_3 l)` returning
* a vector having the same direction as `l`
* (this last one is only required if you want to detect cylinders).
*/
typedef unspecified_type Construct_vector_3;
/*!
* Function object type that provides `Sphere_3 operator()(Point_3 c, FT r)`
* returning the sphere of center `p` and radius `r`.
* Only required if you want to detect spheres or tori.
*/
typedef unspecified_type Construct_sphere_3;
/*!
* Function object type that provides `Line_3 operator()(Point_3 p, Vector_3 d)`
* returning the line going through `p` in the direction of `d`.
* Only required if you want to detect cylinders.
*/
typedef unspecified_type Construct_line_3;
/*!
* Function object type that provides
* `Point_3 operator()(Line_3 l, int i)`
* returning an arbitrary point on `l`. `i` is not used and can vbe of
* any value.
* Only required if you want to detect cylinders.
*/
typedef unspecified_type Construct_point_on_3;
/*!
* Function object type that provides
* `Point_2 operator()(FT x, FT y)`
* returning the 2D point with `x` and `y` as Cartesian coordinates.
* Only required if you want to detect tori.
*/
typedef unspecified_type Construct_point_2;
/*!
* Function object type that provides
* `Vector_2 operator()(Point_2 p1, Point_2 p2)`
* returning the vector `p1p2`, `Vector_2 operator()(NULL_VECTOR)` returning
* the null vector.
* Only required if you want to detect tori.
*/
typedef unspecified_type Construct_vector_2;
/*!
* Function object type that provides
* `Circle_2 operator()(Point_2 p1, Point_2 p2, Point_2 p3)`
* returning the circle going through `p1`, `p2` and `p3`.
* Only required if you want to detect tori.
*/
typedef unspecified_type Construct_circle_2;
/*!
* Function object type that provides
* `FT operator()(Point_3 p)` and `FT operator()(Vector_3 v)`
* returning the `x` coordinate of a point and a vector respectively.
*/
typedef unspecified_type Compute_x_3;
/*!
* Function object type that provides
* `FT operator()(Point_3 p)` and `FT operator()(Vector_3 v)`
* returning the `y` coordinate of a point and a vector respectively.
*/
typedef unspecified_type Compute_y_3;
/*!
* Function object type that provides
* `FT operator()(Point_3 p)` and `FT operator()(Vector_3 v)`
* returning the `z` coordinate of a point and a vector respectively.
*/
typedef unspecified_type Compute_z_3;
/*!
* Function object type that provides
* `FT operator()(Point_2 p)` and `FT operator()(Vector_2 v)`
* returning the `x` coordinate of a point and a vector respectively.
* Only required if you want to detect tori.
*/
typedef unspecified_type Compute_x_2;
/*!
* Function object type that provides
* `FT operator()(Point_2 p)` and `FT operator()(Vector_2 v)`
* returning the `y` coordinate of a point and a vector respectively.
* Only required if you want to detect tori.
*/
typedef unspecified_type Compute_y_2;
/*!
* Function object type that provides
* `FT operator()(Vector_3 v)`
* returning the squared length of `v`.
*/
typedef unspecified_type Compute_squared_length_3;
/*!
* Function object type that provides
* `FT operator()(Vector_2 v)`
* returning the squared length of `v`.
* Only required if you want to detect tori.
*/
typedef unspecified_type Compute_squared_length_2;
/*!
* Function object type that provides
* `Vector_3 operator()(Vector_3 v, FT t)`
* returning the vector `t * v`.
*/
typedef unspecified_type Construct_scaled_vector_3;
/*!
* Function object type that provides
* `Vector_3 operator()(Vector_3 v1, Vector_3 v2)`
* returning the `v1+v2`.
*/
typedef unspecified_type Construct_sum_of_vectors_3;
/*!
* Function object type that provides:
* `Point_3 operator()(Point_3 p, Vector_3 v)`
* returning the point obtained by translating `p` by the vector `v`.
*/
typedef unspecified_type Construct_translated_point_3;
/*!
* Function object type that provides
* `FT operator()(Vector_3 v1, Vector_3 v2)`
* returning the scalar product of `v1` and `v2`.
*/
typedef unspecified_type Compute_scalar_product_3;
/*!
* Function object type that provides
* `Vector_3 operator()(Vector_3 v1, Vector_3 v2)`
* returning the cross-product vector of `v1` and `v2`.
*/
typedef unspecified_type Construct_cross_product_vector_3;
/*!
* Function object type that provides
* `Point_3 operator()(Sphere_3 s)`
* returning the center of the sphere `s`.
* Only required if you want to detect spheres or tori.
*/
typedef unspecified_type Construct_center_3;
/*!
* Function object type that provides
* `Point_2 operator()(Circle_2 c)`
* returning the center of the circle `c`.
* Only required if you want to detect tori.
*/
typedef unspecified_type Construct_center_2;
/*!
* Function object type that provides
* `FT operator()(Sphere_3 s)`
* returning the squared radius of the sphere `s`.
* Only required if you want to detect spheres or tori.
*/
typedef unspecified_type Compute_squared_radius_3;
/*!
* Function object type that provides
* `FT operator()(Circle_2 c)`
* returning the squared radius of the circle `c`.
* Only required if you want to detect tori.
*/
typedef unspecified_type Compute_squared_radius_2;
/*!
* Function object type that provides
* `bool operator(Point_2 p, Point_2 q, Point_2 r)`
* returning true if the points `p`, `q`, and `r` are collinear and
* false otherwise.
* Only required if you want to detect tori.
*/
typedef unspecified_type Collinear_2;
/// @}
/// \name Access to Function Objects
/// @{
Construct_point_3
construct_point_3_object();
Construct_vector_3
construct_vector_3_object();
/// Only required if you want to detect spheres or tori.
Construct_sphere_3
construct_sphere_3_object();
/// Only required if you want to detect cylinders.
Construct_line_3
construct_line_3_object();
/// Only required if you want to detect cylinders.
Construct_point_on_3
construct_point_on_3_object();
/// Only required if you want to detect tori.
Construct_point_2
construct_point_2_object();
/// Only required if you want to detect tori.
Construct_vector_2
construct_vector_2_object();
/// Only required if you want to detect tori.
Construct_circle_2
construct_circle_2_object();
Compute_x_3
compute_x_3_object();
Compute_y_3
compute_y_3_object();
Compute_z_3
compute_z_3_object();
/// Only required if you want to detect tori.
Compute_x_2
compute_x_2_object();
/// Only required if you want to detect tori.
Compute_y_2
compute_y_2_object();
Compute_squared_length_3
compute_squared_length_3_object();
/// Only required if you want to detect tori.
Compute_squared_length_2
compute_squared_length_2_object();
Construct_scaled_vector_3
construct_scaled_vector_3_object();
Construct_sum_of_vectors_3
construct_sum_of_vectors_3_object();
Compute_scalar_product_3
compute_scalar_product_3_object();
Construct_cross_product_vector_3
construct_cross_product_vector_3_object();
Construct_translated_point_3
construct_translated_point_3_object();
/// Only required if you want to detect spheres.
Construct_center_3
construct_center_3_object();
/// Only required if you want to detect tori.
Construct_center_2
construct_center_2_object();
/// Only required if you want to detect spheres or tori.
Compute_squared_radius_3
compute_squared_radius_3_object();
/// Only required if you want to detect tori.
Compute_squared_radius_2
compute_squared_radius_2_object();
/// Only required if you want to detect tori.
Collinear_2
collinear_2_object();
/// @}
};

View File

@ -0,0 +1,9 @@
@INCLUDE = ${CGAL_DOC_PACKAGE_DEFAULTS}
PROJECT_NAME = "CGAL ${CGAL_CREATED_VERSION_NUM} - Point Set Shape Detection"
INPUT = ${CMAKE_SOURCE_DIR}/Point_set_shape_detection_3/doc/Point_set_shape_detection_3/ \
${CMAKE_SOURCE_DIR}/Point_set_shape_detection_3/include/
EXTRACT_ALL = NO
HIDE_UNDOC_CLASSES = YES
WARN_IF_UNDOCUMENTED = NO

View File

@ -0,0 +1,51 @@
/*!
\defgroup PkgPointSetShapeDetection3 Point Set Shape Detection Reference
\defgroup PkgPointSetShapeDetection3Concepts Concepts
\ingroup PkgPointSetShapeDetection3
\defgroup PkgPointSetShapeDetection3Shapes Shapes
\ingroup PkgPointSetShapeDetection3
\addtogroup PkgPointSetShapeDetection3
\cgalPkgDescriptionBegin{Point Set Shape Detection, PkgPointSetShapeDetection3Summary}
\cgalPkgPicture{shapes_detail.png}
\cgalPkgSummaryBegin
\cgalPkgAuthors{Sven Oesau, Yannick Verdie, Clément Jamin, Pierre Alliez}
\cgalPkgDesc{This component implements an efficient RANSAC-based primitive shape detection algorithm for point sets with unoriented normals. Five types of primitive shapes are detected: plane, sphere, cylinder, cone and torus. Other types of shapes can be detected through implementing a class deriving from the base shape class.}
\cgalPkgManuals{Chapter_Point_Set_Shape_Detection, PkgPointSetShapeDetection3}
\cgalPkgSummaryEnd
\cgalPkgShortInfoBegin
\cgalPkgSince{4.7}
\cgalPkgBib{cgal:ovja-pssd}
\cgalPkgLicense{\ref licensesGPL "GPL"}
\cgalPkgDemo{Operations on Polyhedra,polyhedron_3.zip}
\cgalPkgShortInfoEnd
\cgalPkgDescriptionEnd
\cgalClassifedRefPages
## Concept ##
- `EfficientRANSACTraits`
## Main Classes ##
- `CGAL::Shape_detection_3::Efficient_RANSAC<Traits>`
- `CGAL::Shape_detection_3::Efficient_RANSAC_traits`
## Shape Interface ##
- `CGAL::Shape_detection_3::Shape_base<Traits>`
## Shape Classes ##
- `CGAL::Shape_detection_3::Plane<Traits>`
- `CGAL::Shape_detection_3::Sphere<Traits>`
- `CGAL::Shape_detection_3::Cylinder<Traits>`
- `CGAL::Shape_detection_3::Cone<Traits>`
- `CGAL::Shape_detection_3::Torus<Traits>`
*/

View File

@ -0,0 +1,122 @@
namespace CGAL {
/*!
\mainpage User Manual
\anchor Chapter_Point_Set_Shape_Detection
\cgalAutoToc
\authors Sven Oesau, Yannick Verdie, Clément Jamin, Pierre Alliez
\section Point_set_shape_detection_3Introduction Introduction
This \cgal component implements the efficient RANSAC method for shape detection, contributed by Schnabel et al. \cgalCite{schnabel2007efficient}. From an unstructured point set with unoriented normals, the algorithm detects a set of shapes (see \cgalFigureRef{Point_set_shape_detection_3_overview}). Five types of primitive shapes are provided by this package: plane, sphere, cylinder, cone and torus. Detecting other types of shapes requires implementing a class derived from a base shape.
\cgalFigureBegin{Point_set_shape_detection_3_overview,overview2.png}
(a) Input point set. (b) Point set depicted with one color per detected shape.
\cgalFigureEnd
\section Point_set_shape_detection_3Method Method
The method takes as input a point set with unoriented normals and provides as output a set of detected shapes with associated input points. The shapes are detected via a RANSAC-type approach, i.e., a random sample consensus. The basic RANSAC approach repeats the following steps: (1) Randomly select samples from the input points. (2) Fit a shape to the selected samples. (3) Count the number of inliers to the shape, inliers being within a user-specified error tolerance to the shape. Steps 1-3 are repeated for a prescribed number of iterations and the shape with highest number of inliers, referred to as largest shape, is kept.
In our context, the error between a point and a shape is defined by its distance and normal deviation to the shape. A random subset corresponds to the minimal number of points (with normals) required to uniquely define a primitive.
For very large point sets the basic RANSAC method is not practical when testing all possible shape candidates against the input data in order to find the largest shape. The main idea behind the efficient RANSAC method consists in testing shape candidates against \a subsets of the input data. Shape candidates are constructed until the probability to miss the largest candidate is lower than a user-specified parameter. The largest shape is repeatedly extracted until no more shapes, restricted to cover a minimum number of points, can be extracted. An additional gain in efficiency is achieved through exploiting the normal attributes during initial shape construction and enumeration of inliers.
The \a support of a shape refers to the footprint of the points covered by the primitive. To avoid generating shapes with fragmented support we enforce a connectivity constraint by considering only one connected component, referred to as cluster, selected as the one covering the largest number of inliers. See Section \ref Point_set_shape_detection_3Parameters for more details.
The output of the shape detection algorithm is a set of detected shapes with assigned points, and all remaining points not covered by shapes. Each input point can be assigned to at most one detected shape.
\section Point_set_shape_detection_3Parameters Parameters
The algorithm has five parameters:
- `epsilon` and `normal_threshold`:
The error between a point-with-normal \f$p\f$ and a shape \f$S\f$ is defined by its Euclidean distance and normal deviation to \f$S\f$. The normal deviation is computed between the normal at \f$p\f$ and the normal of \f$S\f$ at the closest projection of \f$p\f$ onto \f$S\f$. Parameter `epsilon` defines the absolute maximum tolerance Euclidean distance between a point and a shape. A high value for `epsilon` leads to the detection of fewer large shapes and hence a less detailed detection. A low value for `epsilon` yields a more detailed detection, but may lead to either lower coverage or over-segmentation. Over-segmentation translates into detection of fragmented shapes when `epsilon` is within or below the noise level. When the input point set is made of free-form parts, a higher tolerance `epsilon` allows for detecting more primitive shapes that approximate some of the free-form surfaces. The impact of this parameter is depicted by Figure \cgalFigureRef{Point_set_shape_detection_3_parameter_epsilon_variation}. Its impact on performance is evaluated in Section \ref Point_set_shape_detection_3Performance.
\cgalFigureBegin{Point_set_shape_detection_3_parameter_epsilon_variation,epsilon_variation2.png}
Impact of epsilon over level of details of the detection. (a) Input point set. (b) Detection of planar shapes with epsilon set to 2.0 (one color per detected shape). Most details such as chimneys on the roof are not distinguished. (c) Detection with epsilon set to 0.5. The facades are correctly detected and some details of the roof are detected. (d) Setting epsilon to 0.25 yields a more detailed but slightly over-segmented detection.
\cgalFigureEnd
- `cluster_epsilon`:
Clustering of the points into connected components covered by a detected shape is controlled via parameter `cluster_epsilon`. For developable shapes that admit a trivial planar parameterization (plane, cylinder, cone) the points covered by a shape are mapped to a 2D parameter space chosen to minimize distortion and best preserve arc-length distances. This 2D parameter space is discretized using a regular grid, and a connected component search is performed to identify the largest cluster. Parameter `cluster_epsilon` defines the spacing between two cells of the regular grid, so that two points separated by a distance of at most \f$2\sqrt{2}\f$, `cluster_epsilon` are considered adjacent. For non-developable shapes
the connected components are identified by computing a neighboring graph in 3D and walking in the graph. The impact of parameter cluster_epsilon is depicted by Figure \cgalFigureRef{Point_set_shape_detection_3_parameter_connectivity}.
\cgalFigureBegin{Point_set_shape_detection_3_parameter_connectivity,varying_connectivity.png}
Parameter cluster_epsilon controls the connectivity of the points covered by a detected shape. The input point set is sampled on four coplanar squares. (a) A large value for cluster_epsilon leads to detecting a single planar shape. (b) A moderate value for cluster_epsilon yields the detection of four squares. Notice that few points within the squares are not detected as not connected. (c) A small value for cluster_epsilon leads to over-segmentation.
\cgalFigureEnd
- `probability`:
This parameter defines the probability to miss the largest candidate shape. A lower probability provides a higher reliability and determinism at the cost of longer running times due to higher search endurance.
- `min_points`:
This minimum number of points controls the termination of the algorithm. The shape search is iterated until no further shapes can be found with a higher support. Note that this parameter is not strict: depending on the chosen probability, shapes may be extracted with a number of points lower than the specified parameter.
\section Point_set_shape_detection_3Usage Examples
The main class `Shape_detection_3::Efficient_RANSAC` takes a template parameter `Shape_detection_3::Efficient_RANSAC_traits` that defines the geometric types and input format. Property maps provide a means to interface with user-specific data structures. The first parameter of the `Shape_detection_3::Efficient_RANSAC_traits` class is the common Kernel. In order to match the constraints of property maps, an iterator type and two maps that map an iterator to a point and a normal are specified in the `Shape_detection_3::Efficient_RANSAC_traits` class. The concept behind property maps is detailed in \ref chapterProperty_map "a dedicated chapter".
Typical usage consists in five steps:
-# Provide input data via a range iterator
-# Register shape factories
-# Choose parameters
-# Detect
-# Retrieve detected shapes
\subsection Point_set_shape_detection_3Usage_minimal Basic Planar Shape Detection
The following minimal example reads a point set from a file and detects only planar shapes. The default parameters are used for detection.
\cgalExample{Point_set_shape_detection_3/efficient_RANSAC_basic.cpp}
\subsection Point_set_shape_detection_3Usage_parameters Setting Parameters and Using Different Types of Shape
This example illustrates the user selection of parameters using the `Shape_detection_3::Efficient_RANSAC::Parameters` struct. Shape detection is performed on five types of shapes (plane, cylinder, sphere, cone, torus). Basic information of the detected shapes is written to the standard output. The input point set is sampled on a surface mostly composed of piece-wise planar and cylindrical parts, in addition to free-form parts.
\cgalExample{Point_set_shape_detection_3/efficient_RANSAC_parameters.cpp}
\subsection Point_set_shape_detection_3Usage_point_access Retrieving Points Assigned to Shapes
This example illustrates how to access the points assigned to each shape and compute the mean error. A timer measures the running performance.
\cgalExample{Point_set_shape_detection_3/efficient_RANSAC_point_access.cpp}
\section Point_set_shape_detection_3Arbitrary_shapes Adding More Shapes
Other types of shapes can be detected by implementing a shape class derived from the class `Shape_base` and registering it to the shape detection factory. This class must provide the following functions: construct a shape from a small set of given points, compute the squared distance from a query point and the shape and compute the normal deviation between a query point with normal and the normal to the shape at the closest point from the query. The used shape parameters are added as members to the derived class.
Note that the RANSAC approach is efficient for shapes that are uniquely defined by a small number of points, denoted by the number of required samples. The algorithm aims at detected the largest shape via many random samples, and the combinatorial complexity of possible samples increases rapidly with the number of required samples.
More specifically, the functions to be implemented are defined in the base class
`Shape_detection_3::Shape_base`:
- `Shape_detection_3::Shape_base::minimum_sample_size()` const: Returns the minimal number of required samples.
- `Shape_detection_3::Shape_base::create_shape(const std::vector<size_t> &indices)`: The randomly generated samples are provided via a vector of indices. `Shape_detection_3::Shape_base::point``(size_t i)` and `Shape_detection_3::Shape_base::normal``(size_t i)` are used to retrieve the actual points and normals (see example below). The provided number of samples might actually be larger than the set minimal number of required samples, depending on the other types of shape types. If the provided samples are not sufficient for define a unique shape, e.g., in a degenerated case the shape is considered invalid.
- `Shape_detection_3::Shape_base::squared_distance``(const Point &p)` const: This function computes the squared distance from a query point to the shape. It is used for traversing the hierarchical spatial data structure.
- `Shape_detection_3::Shape_base::squared_distance(std::vector<FT> &dists, const std::vector<size_t> &indices)`
- `Shape_detection_3::Shape_base::cos_to_normal``(const std::vector<size_t> &indice, sstd::vector<FT> &angles)` const:
These functions are used to determine the number of inlier points to the shape. They compute respectively the squared distance from a set of points to the shape, and the dot product between the point's normals and the normals at the shape for the closest points on the shape. The access to the actual point and normal data is carried out via `Shape_detection_3::Shape_base::point``(size_t i)` and `Shape_detection_3::Shape_base::normal``(size_t i)` (see example below). The resulting squared distance/dot product is stored in the vector provided as the first argument.
By default the connected component is detected via the neighbor graph as mentioned above. However, for shapes that admit a faster approach to detect a connected component, the user can provide his/her own implementation to extract the connected component via:
- `Shape_detection_3::Shape_base::connected_component``(std::vector<std::size_t>& indices, FT cluster_epsilon)`: The indices of all supporting points are stored in the vector `indices`. All points not belonging to the largest cluster of points are removed from the vector `indices`.
Another optional method can be implemented to provide a helper function providing the shape parameters written to a string:
- `Shape_detection_3::Shape_base::info``()`: This function returns a string suitable for printing the shape parameters into a log/console. The default solution provides an empty string.
The property maps are used to map the indices to the corresponding points and normals. The following example shows an implementation of a planar shape primitive,
which is used by the example \ref Point_set_shape_detection_3/efficient_RANSAC_custom_shape.cpp.
\cgalExample{Point_set_shape_detection_3/efficient_RANSAC_custom_shape.h}
\section Point_set_shape_detection_3Performance Performance
The running time and detection performance depend on the chosen parameters. A selective error tolerance parameter leads to higher running times and smaller shapes, as many shape candidates are generated to find the largest shape. We plot the detection performance against the epsilon error tolerance parameter for detecting planes in a complex scene with 5M points, see \cgalFigureRef{Point_set_shape_detection_3_performace_epsilon}. The probability parameter controls the endurance when searching for the largest candidate at each iteration. It barely impacts the number of detected shapes, has a moderate impact on the size of the detected shapes and increases the running times. We plot the performance against the probability parameter, see \cgalFigureRef{Point_set_shape_detection_3_performace_probability}.
\cgalFigureBegin{Point_set_shape_detection_3_performace_epsilon,epsilon_graph.png}
The graph depicts the number of detected shapes (purple) and the coverage (green), i.e., the ratio assignedPoints / totalPoints, against the epsilon tolerance parameter. A higher value for epsilon, i.e., a more tolerant error, leads to fewer but larger shapes and shorter running times.
\cgalFigureEnd
\cgalFigureBegin{Point_set_shape_detection_3_performace_probability,prob_graph.png}
The graph depicts the time, coverage and number of detected primitives against the search endurance parameter, i.e., the probability to miss the largest shape at each iteration. The number of shapes is stable and the coverage increases when the probability is lowered. The running times increase significantly as many more candidates are generated during each iteration of the algorithm.
\cgalFigureEnd
*/
} /* namespace CGAL */

View File

@ -0,0 +1,6 @@
Manual
STL_Extension
Circulator
Point_set_processing_3
Property_map
Kernel_23

View File

@ -0,0 +1,7 @@
/*!
\example Point_set_shape_detection_3/efficient_RANSAC_basic.cpp
\example Point_set_shape_detection_3/efficient_RANSAC_parameters.cpp
\example Point_set_shape_detection_3/efficient_RANSAC_point_access.cpp
\example Point_set_shape_detection_3/efficient_RANSAC_custom_shape.cpp
\example Point_set_shape_detection_3/efficient_RANSAC_custom_shape.h
*/

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 259 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 119 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 54 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

View File

@ -0,0 +1,10 @@
depends
GNUmakefile
Doxyfile
TODO
applications
applications.README
README
makefile.dependencies
doc
Efficient_RANSAC_fake_traits_for_testing.h

View File

@ -0,0 +1,36 @@
# Created by the script cgal_create_cmake_script
# This is the CMake script for compiling a CGAL application.
project( Point_set_shape_detection_3_example )
cmake_minimum_required(VERSION 2.6.2)
if("${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION}" VERSION_GREATER 2.6)
if("${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION}.${CMAKE_PATCH_VERSION}" VERSION_GREATER 2.8.3)
cmake_policy(VERSION 2.8.4)
else()
cmake_policy(VERSION 2.6)
endif()
endif()
find_package(CGAL QUIET COMPONENTS Core )
if ( CGAL_FOUND )
include( ${CGAL_USE_FILE} )
include( CGAL_CreateSingleSourceCGALProgram )
include_directories (BEFORE "../../include")
create_single_source_cgal_program( "efficient_RANSAC_basic.cpp" )
create_single_source_cgal_program( "efficient_RANSAC_custom_shape.cpp" )
create_single_source_cgal_program( "efficient_RANSAC_parameters.cpp" )
create_single_source_cgal_program( "efficient_RANSAC_point_access.cpp" )
else()
message(STATUS "This program requires the CGAL library, and will not be compiled.")
endif()

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,61 @@
#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
#include <CGAL/IO/read_xyz_points.h>
#include <CGAL/Point_with_normal_3.h>
#include <CGAL/property_map.h>
#include <CGAL/Shape_detection_3.h>
#include <iostream>
#include <fstream>
// Type declarations
typedef CGAL::Exact_predicates_inexact_constructions_kernel Kernel;
typedef std::pair<Kernel::Point_3, Kernel::Vector_3> Point_with_normal;
typedef std::vector<Point_with_normal> Pwn_vector;
typedef CGAL::First_of_pair_property_map<Point_with_normal> Point_map;
typedef CGAL::Second_of_pair_property_map<Point_with_normal> Normal_map;
// In Efficient_RANSAC_traits the basic types, i.e., Point and Vector types
// as well as iterator type and property maps, are defined.
typedef CGAL::Shape_detection_3::Efficient_RANSAC_traits
<Kernel, Pwn_vector, Point_map, Normal_map> Traits;
typedef CGAL::Shape_detection_3::Efficient_RANSAC<Traits> Efficient_ransac;
typedef CGAL::Shape_detection_3::Plane<Traits> Plane;
int main()
{
// Points with normals.
Pwn_vector points;
// Loads point set from a file.
// read_xyz_points_and_normals takes an OutputIterator for storing the points
// and a property map to store the normal vector with each point.
std::ifstream stream("data/cube.pwn");
if (!stream ||
!CGAL::read_xyz_points_and_normals(stream,
std::back_inserter(points),
Point_map(),
Normal_map()))
{
std::cerr << "Error: cannot read file cube.pwn" << std::endl;
return EXIT_FAILURE;
}
// Instantiates shape detection engine.
Efficient_ransac ransac;
// Provides the input data.
ransac.set_input(points);
// Registers planar shapes via template method.
ransac.add_shape_factory<Plane>();
// Detects registered shapes with default parameters.
ransac.detect();
// Prints number of detected shapes.
std::cout << ransac.shapes().end() - ransac.shapes().begin() << " shapes detected." << std::endl;
return EXIT_SUCCESS;
}

View File

@ -0,0 +1,62 @@
#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
#include <CGAL/IO/read_xyz_points.h>
#include <CGAL/Point_with_normal_3.h>
#include <CGAL/property_map.h>
#include <CGAL/Shape_detection_3.h>
#include "efficient_RANSAC_custom_shape.h"
#include <iostream>
#include <fstream>
// Type declarations
typedef CGAL::Exact_predicates_inexact_constructions_kernel Kernel;
typedef std::pair<Kernel::Point_3, Kernel::Vector_3> Point_with_normal;
typedef std::vector<Point_with_normal> Pwn_vector;
typedef CGAL::First_of_pair_property_map<Point_with_normal> Point_map;
typedef CGAL::Second_of_pair_property_map<Point_with_normal> Normal_map;
// In Efficient_RANSAC_traits the basic types, i.e., Point and Vector types
// as well as iterator type and property maps, are defined.
typedef CGAL::Shape_detection_3::Efficient_RANSAC_traits
<Kernel, Pwn_vector, Point_map, Normal_map> Traits;
typedef CGAL::Shape_detection_3::Efficient_RANSAC<Traits> Efficient_ransac;
typedef My_Plane<Traits> Plane;
int main()
{
// Points with normals.
Pwn_vector points;
// Loads point set from a file.
// read_xyz_points_and_normals takes an OutputIterator for storing the points
// and a property map to store the normal vector with each point.
std::ifstream stream("data/cube.pwn");
if (!stream ||
!CGAL::read_xyz_points_and_normals(stream,
std::back_inserter(points),
Point_map(),
Normal_map()))
{
std::cerr << "Error: cannot read file cube.pwn" << std::endl;
return EXIT_FAILURE;
}
// Instantiates shape detection engine.
Efficient_ransac ransac;
// Provides the input data.
ransac.set_input(points);
// Registers planar shapes via template method.
ransac.add_shape_factory<Plane>();
// Detects registered shapes with default parameters.
ransac.detect();
// Prints number of detected shapes.
std::cout << ransac.shapes().end() - ransac.shapes().begin() << " shapes detected." << std::endl;
return EXIT_SUCCESS;
}

View File

@ -0,0 +1,84 @@
#ifndef MY_PLANE_SHAPE_H
#define MY_PLANE_SHAPE_H
#include <CGAL/Shape_detection_3.h>
#include <CGAL/number_utils.h>
/*
My_Plane derives from Shape_base. The plane is represented by
its normal vector and distance to the origin.
*/
template <class Traits>
class My_Plane : public CGAL::Shape_detection_3::Shape_base<Traits> {
public:
typedef typename Traits::FT FT;///< number type.
typedef typename Traits::Point_3 Point;///< point type.
typedef typename Traits::Vector_3 Vector;///< point type.
public:
My_Plane()
: CGAL::Shape_detection_3::Shape_base<Traits>()
{}
// Computes squared Euclidean distance from query point to the shape.
virtual FT squared_distance(const Point &p) const {
const FT sd = (this->constr_vec(m_point_on_primitive, p)) * m_normal;
return sd * sd;
}
protected:
// Constructs shape based on minimal set of samples from the input data.
virtual void create_shape(const std::vector<std::size_t> &indices) {
const Point p1 = this->point(indices[0]);
const Point p2 = this->point(indices[1]);
const Point p3 = this->point(indices[2]);
m_normal = this->cross_pdct(p1 - p2, p1 - p3);
m_normal = m_normal * (1.0 / sqrt(this->sqlen(m_normal)));
m_d = -(p1[0] * m_normal[0] + p1[1] * m_normal[1] + p1[2] * m_normal[2]);
this->m_is_valid = true;
}
// Computes squared Euclidean distance from a set of points.
virtual void squared_distance(const std::vector<std::size_t> &indices,
std::vector<FT> &dists) const {
for (std::size_t i = 0; i < indices.size(); i++) {
const FT sd = (this->point(indices[i])
- m_point_on_primitive) * m_normal;
dists[i] = sd * sd;
}
}
/*
Computes the normal deviation between shape and
a set of points with normals.
*/
virtual void cos_to_normal(const std::vector<std::size_t> &indices,
std::vector<FT> &angles) const {
for (std::size_t i = 0; i < indices.size(); i++)
angles[i] = CGAL::abs(this->normal(indices[i]) * m_normal);
}
// Returns the number of required samples for construction.
virtual std::size_t minimum_sample_size() const {
return 3;
}
// Returns a string with shape parameters.
virtual std::string info() const {
std::stringstream sstr;
sstr << "Type: plane (" << this->get_x(m_normal) << ", "
<< this->get_y(m_normal) << ", " << this->get_z(m_normal) << ")x - " <<
m_d << " = 0" << " #Pts: " << this->m_indices.size();
return sstr.str();
}
private:
Point m_point_on_primitive;
Vector m_normal;
FT m_d;
};
#endif

View File

@ -0,0 +1,108 @@
#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
#include <CGAL/IO/read_xyz_points.h>
#include <CGAL/Point_with_normal_3.h>
#include <CGAL/property_map.h>
#include <CGAL/Shape_detection_3.h>
#include <iostream>
#include <fstream>
// Type declarations
typedef CGAL::Exact_predicates_inexact_constructions_kernel Kernel;
typedef Kernel::FT FT;
typedef std::pair<Kernel::Point_3, Kernel::Vector_3> Point_with_normal;
typedef std::vector<Point_with_normal> Pwn_vector;
typedef CGAL::First_of_pair_property_map<Point_with_normal> Point_map;
typedef CGAL::Second_of_pair_property_map<Point_with_normal> Normal_map;
// In Efficient_RANSAC_traits the basic types, i.e., Point and Vector types
// as well as iterator type and property maps, are defined.
typedef CGAL::Shape_detection_3::Efficient_RANSAC_traits<Kernel,
Pwn_vector, Point_map, Normal_map> Traits;
typedef CGAL::Shape_detection_3::Efficient_RANSAC<Traits> Efficient_ransac;
typedef CGAL::Shape_detection_3::Cone<Traits> Cone;
typedef CGAL::Shape_detection_3::Cylinder<Traits> Cylinder;
typedef CGAL::Shape_detection_3::Plane<Traits> Plane;
typedef CGAL::Shape_detection_3::Sphere<Traits> Sphere;
typedef CGAL::Shape_detection_3::Torus<Traits> Torus;
int main()
{
// Points with normals.
Pwn_vector points;
// Loads point set from a file.
// read_xyz_points_and_normals takes an OutputIterator for storing the points
// and a property map to store the normal vector with each point.
std::ifstream stream("data/cube.pwn");
if (!stream ||
!CGAL::read_xyz_points_and_normals(stream,
std::back_inserter(points),
Point_map(),
Normal_map()))
{
std::cerr << "Error: cannot read file cube.pwn" << std::endl;
return EXIT_FAILURE;
}
std::cout << points.size() << " points" << std::endl;
// Instantiates shape detection engine.
Efficient_ransac ransac;
// Provides the input data.
ransac.set_input(points);
// Register shapes for detection
ransac.add_shape_factory<Plane>();
ransac.add_shape_factory<Sphere>();
ransac.add_shape_factory<Cylinder>();
ransac.add_shape_factory<Cone>();
ransac.add_shape_factory<Torus>();
// Sets parameters for shape detection.
Efficient_ransac::Parameters parameters;
// Sets probability to miss the largest primitive at each iteration.
parameters.probability = 0.05;
// Detect shapes with at least 500 points.
parameters.min_points = 200;
// Sets maximum Euclidean distance between a point and a shape.
parameters.epsilon = 0.002;
// Sets maximum Euclidean distance between points to be clustered.
parameters.cluster_epsilon = 0.01;
// Sets maximum normal deviation.
// 0.9 < dot(surface_normal, point_normal);
parameters.normal_threshold = 0.9;
// Detects shapes
ransac.detect(parameters);
// Prints number of detected shapes and unassigned points.
std::cout << ransac.shapes().end() - ransac.shapes().begin() << " detected shapes, "
<< ransac.number_of_unassigned_points()
<< " unassigned points." << std::endl;
// Efficient_ransac::shapes() provides
// an iterator range to the detected shapes.
Efficient_ransac::Shape_range shapes = ransac.shapes();
Efficient_ransac::Shape_range::iterator it = shapes.begin();
while (it != shapes.end()) {
// Prints the parameters of the detected shape.
std::cout << (*it)->info() << std::endl;
// Proceeds with next detected shape.
it++;
}
return EXIT_SUCCESS;
}

View File

@ -0,0 +1,139 @@
#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
#include <CGAL/IO/read_xyz_points.h>
#include <CGAL/Point_with_normal_3.h>
#include <CGAL/property_map.h>
#include <CGAL/Timer.h>
#include <CGAL/number_utils.h>
#include <CGAL/Shape_detection_3.h>
#include <iostream>
#include <fstream>
// Type declarations
typedef CGAL::Exact_predicates_inexact_constructions_kernel Kernel;
typedef Kernel::FT FT;
typedef std::pair<Kernel::Point_3, Kernel::Vector_3> Point_with_normal;
typedef std::vector<Point_with_normal> Pwn_vector;
typedef CGAL::First_of_pair_property_map<Point_with_normal> Point_map;
typedef CGAL::Second_of_pair_property_map<Point_with_normal> Normal_map;
// In Efficient_RANSAC_traits the basic types, i.e., Point and Vector types
// as well as iterator type and property maps, are defined.
typedef CGAL::Shape_detection_3::Efficient_RANSAC_traits<Kernel,
Pwn_vector, Point_map, Normal_map> Traits;
typedef CGAL::Shape_detection_3::Efficient_RANSAC<Traits> Efficient_ransac;
typedef CGAL::Shape_detection_3::Plane<Traits> Plane;
int main()
{
// Points with normals.
Pwn_vector points;
// Loads point set from a file.
// read_xyz_points_and_normals takes an OutputIterator for storing the points
// and a property map to store the normal vector with each point.
std::ifstream stream("data/cube.pwn");
if (!stream ||
!CGAL::read_xyz_points_and_normals(stream,
std::back_inserter(points),
Point_map(),
Normal_map()))
{
std::cerr << "Error: cannot read file cube.pwn" << std::endl;
return EXIT_FAILURE;
}
// Instantiates shape detection engine.
Efficient_ransac ransac;
// Provides the input data.
ransac.set_input(points);
// Registers detection of planes
ransac.add_shape_factory<Plane>();
// Measures time before setting up the shape detection.
CGAL::Timer time;
time.start();
// Build internal data structures.
ransac.preprocess();
// Measures time after preprocessing.
time.stop();
std::cout << "preprocessing took: " << time.time() * 1000 << "ms" << std::endl;
// Perform detection several times and choose result with highest coverage.
Efficient_ransac::Shape_range shapes = ransac.shapes();
FT best_coverage = 0;
for (size_t i = 0;i<3;i++) {
// Reset timer.
time.reset();
time.start();
// Detects shapes.
ransac.detect();
// Measures time after detection.
time.stop();
// Compute coverage, i.e. ratio of the points assigned to a shape.
FT coverage = FT(points.size() - ransac.number_of_unassigned_points())
/ FT(points.size());
// Prints number of assigned shapes and unsassigned points.
std::cout << "time: " << time.time() * 1000 << "ms" << std::endl;
std::cout << ransac.shapes().end() - ransac.shapes().begin() << " primitives, "
<< coverage << " coverage" << std::endl;
// Choose result with highest coverage.
if (coverage > best_coverage) {
best_coverage = coverage;
// Efficient_ransac::shapes() provides
// an iterator range to the detected shapes.
shapes = ransac.shapes();
}
}
Efficient_ransac::Shape_range::iterator it = shapes.begin();
while (it != shapes.end()) {
boost::shared_ptr<Efficient_ransac::Shape> shape = *it;
// Using Shape_base::info() for printing
// the parameters of the detected shape.
std::cout << (*it)->info();
// Sums distances of points to detected shapes.
FT sum_distances = 0;
// Iterates through point indices assigned to each detected shape.
std::vector<std::size_t>::const_iterator
index_it = (*it)->indices_of_assigned_points().begin();
while (index_it != (*it)->indices_of_assigned_points().end()) {
// Retrieves point
const Point_with_normal &p = *(points.begin() + (*index_it));
// Adds Euclidean distance between point and shape.
sum_distances += CGAL::sqrt((*it)->squared_distance(p.first));
// Proceeds with next point.
index_it++;
}
// Computes and prints average distance.
FT average_distance = sum_distances / shape->indices_of_assigned_points().size();
std::cout << " average distance: " << average_distance << std::endl;
// Proceeds with next detected shape.
it++;
}
return EXIT_SUCCESS;
}

View File

@ -0,0 +1,40 @@
// Copyright (c) 2015 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) : Sven Oesau, Yannick Verdie, Clément Jamin, Pierre Alliez
//
/**
* \ingroup PkgPointSetShapeDetection3
* \file CGAL/Shape_detection_3.h
* Convenience header file including the headers of this package.
*/
#ifndef CGAL_SHAPE_DETECTION_3_H
#define CGAL_SHAPE_DETECTION_3_H
#include <CGAL/Shape_detection_3/Efficient_RANSAC.h>
#include <CGAL/Shape_detection_3/Efficient_RANSAC_traits.h>
#include <CGAL/Shape_detection_3/Plane.h>
#include <CGAL/Shape_detection_3/Cylinder.h>
#include <CGAL/Shape_detection_3/Cone.h>
#include <CGAL/Shape_detection_3/Torus.h>
#include <CGAL/Shape_detection_3/Sphere.h>
#endif //CGAL_SHAPE_DETECTION_3_H

View File

@ -0,0 +1,479 @@
// Copyright (c) 2015 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) : Sven Oesau, Yannick Verdie, Clément Jamin, Pierre Alliez
//
#ifndef CGAL_SHAPE_DETECTION_3_CONE_H
#define CGAL_SHAPE_DETECTION_3_CONE_H
#include <CGAL/Shape_detection_3/Shape_base.h>
#include <CGAL/number_utils.h>
#include <cmath>
/*!
\file Cone.h
*/
namespace CGAL {
namespace Shape_detection_3 {
/*!
\brief Cone implements Shape_base.
The cone is represented by its apex, the axis and the opening angle.
This representation models an open infinite single-cone.
\tparam Traits a model of `EfficientRANSACTraits`
\ingroup PkgPointSetShapeDetection3Shapes
*/
template <class Traits>
class Cone : public Shape_base<Traits> {
using Shape_base<Traits>::update_label;
public:
/// \cond SKIP_IN_MANUAL
typedef typename Traits::Point_map Point_map;
///< property map to access the location of an input point.
typedef typename Traits::Normal_map Normal_map;
///< property map to access the unoriented normal of an input point.
typedef typename Traits::FT FT; ///< number type.
typedef typename Traits::Point_3 Point_3;///< point type.
typedef typename Traits::Vector_3 Vector_3;///< vector type.
/// \endcond
Cone() : Shape_base<Traits>(), m_wrap(false) {}
/*!
The opening angle between the axis and the surface of the cone.
*/
FT angle() const {
return m_angle;
}
/*!
The apex of the cone.
*/
Point_3 apex() const {
return m_apex;
}
/*!
The axis points from the apex into the cone.
*/
Vector_3 axis() const {
return m_axis;
}
/*!
Helper function to write apex, axis and angle of the cone and
number of assigned points into a string.
*/
/// \cond SKIP_IN_MANUAL
std::string info() const {
std::stringstream sstr;
sstr << "Type: cone apex: (" << this->get_x(m_apex) << ", " << this->get_y(m_apex);
sstr << ", " << this->get_z(m_apex) << ") axis: (" << this->get_x(m_axis) << ", ";
sstr << this->get_y(m_axis) << ", " << this->get_z(m_axis) << ") angle:" << m_angle;
sstr << " #Pts: " << this->m_indices.size();
return sstr.str();
}
/*!
Computes squared Euclidean distance from query point to the shape.
*/
FT squared_distance(const Point_3 &p) const {
Vector_3 toApex = this->constr_vec(m_apex, p);
FT a = this->sqlen(toApex);
// projection on axis
FT b = this->scalar_pdct(toApex, m_axis);
// distance to axis
if (a - b * b <= 0)
return 0;
FT l = CGAL::sqrt(a - b * b);
FT c = m_cos_ang * l;
FT d = m_neg_sin_ang * b;
// far on other side?
return (b < 0 && c - d < 0) ? a : CGAL::abs(c + d) * CGAL::abs(c + d);
}
/// \endcond
protected:
/// \cond SKIP_IN_MANUAL
virtual void create_shape(const std::vector<std::size_t> &indices) {
Point_3 p1 = this->point(indices[0]);
Point_3 p2 = this->point(indices[1]);
Point_3 p3 = this->point(indices[2]);
Vector_3 n1 = this->normal(indices[0]);
Vector_3 n2 = this->normal(indices[1]);
Vector_3 n3 = this->normal(indices[2]);
// first calculate intersection of three planes -> apex
Vector_3 lineDir = this->cross_pdct(n1, n2);
FT length = CGAL::sqrt(this->sqlen(lineDir));
if (length == 0)
return;
lineDir = this->scale(lineDir, (FT)1.0 / length);
// lineDir not normalized direction of intersection lines
// of two planes (p1, n1) and (p2, n2)
// get point on line by moving point p1 onto line
Vector_3 orthLineInPlane = this->cross_pdct(n1, lineDir);
length = CGAL::sqrt(this->sqlen(orthLineInPlane));
if (length == 0)
return;
orthLineInPlane = this->scale(orthLineInPlane, (FT)1.0 / length);
// distance of p1 to (p2, n2)
FT d = this->scalar_pdct(this->constr_vec(CGAL::ORIGIN, p1), n2)
- this->scalar_pdct(this->constr_vec(CGAL::ORIGIN, p2), n2);
// projection of orthLineInPlane onto p2
FT l = this->scalar_pdct(orthLineInPlane, n2);
if (l == 0)
return;
Point_3 pointOnLine = this->transl(
p1, this->scale(orthLineInPlane, -d/l));
// distance of pLineDir to (p3, n3)
d = this->scalar_pdct(this->constr_vec(CGAL::ORIGIN, pointOnLine), n3)
- this->scalar_pdct(this->constr_vec(CGAL::ORIGIN, p3), n3);
l = this->scalar_pdct(lineDir, n3);
if (l == 0)
return;
m_apex = this->transl(pointOnLine, this->scale(lineDir, -d/l));
// 2. find axis
Vector_3 v1 = this->constr_vec(m_apex, p1);
length = CGAL::sqrt(this->sqlen(v1));
if (length == 0)
return;
v1 = this->scale(v1, (FT)1.0 / length);
Point_3 c1 = this->transl(m_apex, v1);
Vector_3 v2 = this->constr_vec(m_apex, p2);
length = CGAL::sqrt(this->sqlen(v2));
if (length == 0)
return;
v2 = this->scale(v2, (FT)1.0 / length);
Point_3 c2 = this->transl(m_apex, v2);
Vector_3 v3 = this->constr_vec(m_apex, p3);
length = CGAL::sqrt(this->sqlen(v3));
if (length == 0)
return;
v3 = this->scale(v3, (FT)1.0 / length);
Point_3 c3 = this->transl(m_apex, v3);
m_axis = this->cross_pdct(this->constr_vec(c2, c1), this->constr_vec(c3, c1));
m_axis = (this->scalar_pdct(orthLineInPlane, m_axis) < 0) ?
this->scale(m_axis, FT(-1)) : m_axis;
length = CGAL::sqrt(this->sqlen(m_axis));
if (length == 0)
return;
m_axis = this->scale(m_axis, (FT)1.0 / length);
m_angle = acos(this->scalar_pdct(v1, m_axis)) + acos(this->scalar_pdct(v2, m_axis)) + acos(this->scalar_pdct(v3, m_axis));
m_angle /= (FT)3.0;
if (m_angle < 0 || m_angle > CGAL_PI / (FT)2.12)
return;
m_neg_sin_ang = -sin(m_angle);
m_cos_ang = cos(m_angle);
this->m_is_valid = true;
}
virtual void squared_distance(const std::vector<std::size_t> &indices,
std::vector<FT> &dists) const {
for (std::size_t i = 0;i<indices.size();i++) {
Vector_3 to_apex = this->constr_vec(m_apex, this->point(indices[i]));
FT a = this->sqlen(to_apex);
// projection on axis
FT b = this->scalar_pdct(to_apex, m_axis);
// distance to axis
FT l = CGAL::sqrt(a - b * b);
FT c = m_cos_ang * l;
FT d = m_neg_sin_ang * b;
// far on other side?
dists[i] =
(b < 0 && c - d < 0) ? a : CGAL::abs(c + d) * CGAL::abs(c + d);
}
}
virtual void cos_to_normal(const std::vector<std::size_t> &indices,
std::vector<FT> &angles) const {
for (std::size_t i = 0;i<indices.size();i++) {
// construct vector orthogonal to axis in direction of the point
Vector_3 a = this->constr_vec(m_apex, this->point(indices[i]));
Vector_3 b = this->cross_pdct(m_axis,
this->cross_pdct(m_axis, a));
b = (this->scalar_pdct(a, b) < 0) ? this->scale(b, FT(-1)) : b;
FT length = CGAL::sqrt(this->sqlen(b));
if (length == 0) {
angles[i] = (FT)1.0;
continue;
}
b = this->scale(b, (FT)1.0 / length);
b = this->sum_vectors(
this->scale(b, m_cos_ang),
this->scale(m_axis, m_neg_sin_ang));
angles[i] = CGAL::abs(this->scalar_pdct(this->normal(indices[i]), b));
}
}
virtual FT cos_to_normal(const Point_3 &p, const Vector_3 &n) const {
// construct vector orthogonal to axis in direction of the point
Vector_3 a = this->constr_vec(m_apex, p);
Vector_3 b = this->cross_pdct(m_axis, this->cross_pdct(m_axis, a));
b = (this->scalar_pdct(a, b) < 0) ? this->scale(b, FT(-1)) : b;
FT length = CGAL::sqrt(this->sqlen(b));
if (length == 0) {
return (FT)1.0;
}
b = this->scale(b, (FT)1.0 / length);
b = this->sum_vectors(
this->scale(b, m_cos_ang),
this->scale(m_axis, m_neg_sin_ang));
return CGAL::abs(this->scalar_pdct(n, b));
}
virtual std::size_t minimum_sample_size() const {
return 3;
}
virtual void post_wrap(const std::vector<unsigned int> &bitmap,
const std::size_t &u_extent,
const std::size_t &v_extent,
std::vector<unsigned int> &labels) const {
if (!m_wrap)
return;
// handle top index separately
unsigned int nw = bitmap[u_extent - 1];
unsigned int l = bitmap[0];
// Special case v_extent is just 1
if (v_extent == 1) {
if (nw && nw != l) {
l = (std::min<unsigned int>)(nw, l);
update_label(labels, (std::max<unsigned int>)(nw, l), l);
}
return;
}
unsigned int w = bitmap[2 * u_extent - 1];
unsigned int sw;
if (l) {
if (nw && nw != l) {
l = (std::min<unsigned int>)(nw, l);
update_label(labels, (std::max<unsigned int>)(nw, l), l);
}
else if (w && w != l) {
l = (std::min<unsigned int>)(w, l);
update_label(labels, (std::max<unsigned int>)(w, l), l);
}
}
// handle mid indices
for (std::size_t y = 1;y<v_extent - 1;y++) {
l = bitmap[y * u_extent];
if (!l)
continue;
nw = bitmap[y * u_extent - 1];
w = bitmap[(y + 1) * u_extent - 1];
sw = bitmap[(y + 2) * u_extent - 1];
if (nw && nw != l) {
l = (std::min<unsigned int>)(nw, l);
update_label(labels, (std::max<unsigned int>)(nw, l), l);
}
if (w && w != l) {
l = (std::min<unsigned int>)(w, l);
update_label(labels, (std::max<unsigned int>)(w, l), l);
}
else if (sw && sw != l) {
l = (std::min<unsigned int>)(sw, l);
update_label(labels, (std::max<unsigned int>)(sw, l), l);
}
}
// handle last index
l = bitmap[(v_extent - 1) * u_extent];
if (!l)
return;
nw = bitmap[(v_extent - 1) * u_extent - 1];
w = bitmap[u_extent * v_extent - 1];
if (nw && nw != l) {
l = (std::min<unsigned int>)(nw, l);
update_label(labels, (std::max<unsigned int>)(nw, l), l);
}
else if (w && w != l) {
l = (std::min<unsigned int>)(w, l);
update_label(labels, (std::max<unsigned int>)(w, l), l);
}
}
virtual void parameters(const std::vector<std::size_t> &indices,
std::vector<std::pair<FT, FT> > &parameterSpace,
FT &cluster_epsilon,
FT min[2],
FT max[2]) const {
// Create basis d1, d2
Vector_3 d1 = Vector_3((FT) 0, (FT) 0, (FT) 1);
Vector_3 d2 = this->cross_pdct(m_axis, d1);
FT l = this->sqlen(d2);
if (l < (FT)0.0001) {
d1 = Vector_3((FT) 1, (FT) 0, (FT) 0);
d2 = this->cross_pdct(m_axis, d1);
l = this->sqlen(d2);
}
d2 = this->scale(d2, FT(1) / CGAL::sqrt(l));
d1 = this->cross_pdct(m_axis, d2);
l = CGAL::sqrt(this->sqlen(d1));
if (l == 0)
return;
d1 = this->scale(d1, (FT)1.0 / l);
if (m_angle > CGAL_M_PI_4) {
// Projection onto a disk preserving distance to apex
m_wrap = false;
// First index separately to initialize min/max
Vector_3 d = this->constr_vec(m_apex, this->point(indices[0]));
FT l = this->scalar_pdct(d, m_axis) / m_cos_ang;
FT u = this->scalar_pdct(d, d1);
FT v = this->scalar_pdct(d, d2);
FT l2 = CGAL::sqrt(u * u + v * v);
u = u * l/l2;
v = v * l/l2;
min[0] = max[0] = u;
min[1] = max[1] = v;
parameterSpace[0] = std::pair<FT, FT>(u, v);
for (std::size_t i = 1;i<indices.size();i++) {
d = this->constr_vec(m_apex, this->point(indices[i]));
l = this->scalar_pdct(d, m_axis) / m_cos_ang;
u = this->scalar_pdct(d, d1);
v = this->scalar_pdct(d, d2);
l2 = CGAL::sqrt(u * u + v * v);
u = u * l/l2;
v = v * l/l2;
min[0] = (std::min<FT>)(min[0], u);
max[0] = (std::max<FT>)(max[0], u);
min[1] = (std::min<FT>)(min[1], v);
max[1] = (std::max<FT>)(max[1], v);
parameterSpace[i] = std::pair<FT, FT>(u, v);
}
}
else {
// Map onto triangle.
// u coordinate is arclength
// v coordinate is distance to apex
Vector_3 d = this->constr_vec(m_apex, this->point(indices[0]));
FT v = this->scalar_pdct(d, m_axis) / m_cos_ang;
FT phi = atan2(this->scalar_pdct(d, d2), this->scalar_pdct(d, d1));
FT radPerDist = -m_neg_sin_ang * v;
FT u = FT(phi + CGAL_PI);// * radPerDist;
FT avg_v = v;
min[0] = max[0] = u;
min[1] = max[1] = v;
parameterSpace[0] = std::pair<FT, FT>(u, v);
for (std::size_t i = 1;i<indices.size();i++) {
d = this->constr_vec(m_apex, this->point(indices[i]));
v = this->scalar_pdct(d, m_axis) / m_cos_ang;
phi = atan2(this->scalar_pdct(d, d2), this->scalar_pdct(d, d1));
radPerDist = -m_neg_sin_ang * v;
u = FT(phi + CGAL_PI);// * radPerDist;
min[0] = (std::min<FT>)(min[0], u);
max[0] = (std::max<FT>)(max[0], u);
min[1] = (std::min<FT>)(min[1], v);
max[1] = (std::max<FT>)(max[1], v);
avg_v += v;
parameterSpace[i] = std::pair<FT, FT>(u, v);
}
// Scale u parameter by average circumference to arc length
avg_v /= indices.size();
const FT scale = -m_neg_sin_ang * avg_v;
m_wrap = (min[0] + 2 * CGAL_PI - max[0]) * scale < cluster_epsilon;
for (std::size_t i = 0;i<parameterSpace.size();i++) {
std::pair<FT, FT> p = parameterSpace[i];
parameterSpace[i] = std::pair<FT, FT>(p.first * scale, p.second);
}
min[0] *= scale;
max[0] *= scale;
}
}
virtual bool supports_connected_component() const {
return true;
}
private:
FT m_angle;
Point_3 m_apex;
Vector_3 m_axis;
FT m_neg_sin_ang, m_cos_ang;
mutable bool m_wrap;
/// \endcond
};
}
}
#endif

View File

@ -0,0 +1,398 @@
// Copyright (c) 2015 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) : Sven Oesau, Yannick Verdie, Clément Jamin, Pierre Alliez
//
#ifndef CGAL_SHAPE_DETECTION_3_CYLINDER_H
#define CGAL_SHAPE_DETECTION_3_CYLINDER_H
#include <CGAL/Shape_detection_3/Shape_base.h>
#include <CGAL/number_utils.h>
#include <cmath>
/*!
\file Cylinder.h
*/
namespace CGAL {
namespace Shape_detection_3 {
/*!
\brief Cylinder implements Shape_base. The cylinder is represented
by the axis, i.e. a point and direction, and the radius. The cylinder is
unbounded, thus caps are not modelled.
\tparam Traits a model of `EfficientRANSACTraits` with the additional
requirement for cylinders (see `EfficientRANSACTraits` documentation).
\ingroup PkgPointSetShapeDetection3Shapes
*/
template <class Traits>
class Cylinder : public Shape_base<Traits> {
using Shape_base<Traits>::update_label;
public:
/// \cond SKIP_IN_MANUAL
typedef typename Traits::Point_map Point_map;
///< property map to access the location of an input point.
typedef typename Traits::Normal_map Normal_map;
///< property map to access the unoriented normal of an input point.
typedef typename Traits::Vector_3 Vector_3; ///< vector type.
typedef typename Traits::Point_3 Point_3; ///< point type.
typedef typename Traits::FT FT; ///< number type.
/// \endcond
typedef typename Traits::Line_3 Line_3; ///< line type.
public:
Cylinder() : Shape_base<Traits>() {}
/*!
Axis of the cylinder.
*/
Line_3 axis() const {
return m_axis;
}
/*!
Radius of the cylinder.
*/
FT radius() const {
return m_radius;
}
/// \cond SKIP_IN_MANUAL
/*!
Helper function to write axis and radius of the cylinder and number of assigned points into a string.
*/
std::string info() const {
std::stringstream sstr;
Point_3 c = this->constr_point_on(m_axis);
Vector_3 a = this->constr_vec(m_axis);
sstr << "Type: cylinder center: (" << this->get_x(c) << ", " << this->get_y(c) << ", " << this->get_z(c) << ") axis: (" << this->get_x(a) << ", " << this->get_y(a) << ", " << this->get_z(a) << ") radius:" << m_radius
<< " #Pts: " << this->m_indices.size();
return sstr.str();
}
/*!
Computes squared Euclidean distance from query point to the shape.
*/
FT squared_distance(const Point_3 &p) const {
Vector_3 a = this->constr_vec(m_axis);
a = this->scale(a, (FT)1.0 / CGAL::sqrt(this->sqlen(a)));
Vector_3 v = this->constr_vec(m_point_on_axis, p);
v = this->sum_vectors(v, this->scale(a, -this->scalar_pdct(v, a)));
FT d = CGAL::sqrt(this->sqlen(v)) - m_radius;
return d * d;
}
/// \endcond
protected:
/// \cond SKIP_IN_MANUAL
// ------------------------------------------------------------------------
// Utilities
// ------------------------------------------------------------------------
using Shape_base<Traits>::constr_vec;
Vector_3 constr_vec(const Line_3& l) const
{ return this->m_traits.construct_vector_3_object()(l); }
Point_3 constr_point_on(const Line_3& l) const
{ return this->m_traits.construct_point_on_3_object()(l, 0); }
virtual void create_shape(const std::vector<std::size_t> &indices) {
Point_3 p1 = this->point(indices[0]);
Point_3 p2 = this->point(indices[1]);
Vector_3 n1 = this->normal(indices[0]);
Vector_3 n2 = this->normal(indices[1]);
Vector_3 axis = this->cross_pdct(n1, n2);
FT axisL = CGAL::sqrt(this->sqlen(axis));
if (axisL < (FT)0.0001) {
return;
}
axis = this->scale(axis, FT(1.0) / axisL);
// establish two directions in the plane axis * x = 0,
// whereas xDir is the projected n1
Vector_3 xDir = this->sum_vectors(
n1, this->scale(axis, -this->scalar_pdct(n1, axis)));
xDir = this->scale(xDir, (FT)1.0 / CGAL::sqrt(this->sqlen(xDir)));
Vector_3 yDir = this->cross_pdct(axis, xDir);
yDir = this->scale(yDir, (FT)1.0 / CGAL::sqrt(this->sqlen(yDir)));
FT n2x = this->scalar_pdct(n2, yDir);
FT n2y = -this->scalar_pdct(n2, xDir);
Vector_3 dist = this->constr_vec(p1, p2);
FT Ox = this->scalar_pdct(xDir, dist);
FT Oy = this->scalar_pdct(yDir, dist);
FT lineDist = n2x * Ox + n2y * Oy;
m_radius = lineDist / n2x;
m_point_on_axis = this->transl(p1, this->scale(xDir, m_radius));
m_radius = CGAL::abs(m_radius);
m_axis = this->m_traits.construct_line_3_object()(m_point_on_axis, axis);
if (squared_distance(p1) > this->m_epsilon ||
(cos_to_normal(p1, n1) < this->m_normal_threshold))
return;
this->m_is_valid = true;
this->m_wrap_u = true;
}
virtual void parameters(const std::vector<std::size_t> &indices,
std::vector<std::pair<FT, FT> > &parameterSpace,
FT &cluster_epsilon,
FT min[2],
FT max[2]) const {
Vector_3 d1 = Vector_3((FT) 0, (FT) 0, (FT) 1);
Vector_3 a = this->constr_vec(m_axis);
a = this->scale(a, (FT)1.0 / CGAL::sqrt(this->sqlen(a)));
Vector_3 d2 = this->cross_pdct(a, d1);
FT l = this->sqlen(d2);
if (l < (FT)0.0001) {
d1 = Vector_3((FT) 1, (FT) 0, (FT) 0);
d2 = this->cross_pdct(this->constr_vec(m_axis), d1);
l = this->sqlen(d2);
}
d2 = this->scale(d2, FT(1) / CGAL::sqrt(l));
d1 = this->cross_pdct(this->constr_vec(m_axis), d2);
FT length = CGAL::sqrt(this->sqlen(d1));
if (length == 0)
return;
d1 = this->scale(d1, (FT)1.0 / length);
// first one separate for initializing min/max
Vector_3 vec = this->constr_vec(m_point_on_axis, this->point(indices[0]));
FT v = this->scalar_pdct(vec, a);
vec = this->sum_vectors(vec, this->scale(a, -this->scalar_pdct(vec, a)));
length = CGAL::sqrt(this->sqlen(vec));
vec = this->scale(vec, (FT)1.0 / length);
FT a1 = this->scalar_pdct(vec, d1);
a1 = (a1 < (FT) -1.0) ? (FT) -1.0 : ((a1 > (FT) 1.0) ? (FT) 1.0 : a1);
a1 = acos(a1);
FT a2 = this->scalar_pdct(vec, d2);
a2 = (a2 < (FT) -1.0) ? (FT) -1.0 : ((a2 > (FT) 1.0) ? (FT) 1.0 : a2);
a2 = acos(a2);
FT u = FT((a2 < CGAL_M_PI_2) ? 2 * CGAL_PI - a1 : a1) * m_radius;
parameterSpace[0] = std::pair<FT, FT>(u, v);
min[0] = max[0] = u;
min[1] = max[1] = v;
for (std::size_t i = 0;i<indices.size();i++) {
vec = this->constr_vec(m_point_on_axis, this->point(indices[i]));
v = this->scalar_pdct(vec, a);
vec = this->sum_vectors(vec, this->scale(a, -this->scalar_pdct(vec, a)));
length = CGAL::sqrt(this->sqlen(vec));
vec = this->scale(vec, (FT)1.0 / length);
a1 = this->scalar_pdct(vec, d1);
a1 = (a1 < (FT) -1.0) ? (FT) -1.0 : ((a1 > (FT) 1.0) ? (FT) 1.0 : a1);
a1 = acos(a1);
a2 = this->scalar_pdct(vec, d2);
a2 = (a2 < (FT) -1.0) ? (FT) -1.0 : ((a2 > (FT) 1.0) ? (FT) 1.0 : a2);
a2 = acos(a2);
u = FT((a2 < CGAL_M_PI_2) ? 2 * CGAL_PI - a1 : a1) * m_radius;
min[0] = (std::min<FT>)(min[0], u);
max[0] = (std::max<FT>)(max[0], u);
min[1] = (std::min<FT>)(min[1], v);
max[1] = (std::max<FT>)(max[1], v);
parameterSpace[i] = std::pair<FT, FT>(u, v);
}
// Is close to wrapping around?
FT diff_to_full_range = min[0] + FT(CGAL_PI * 2.0 * m_radius) - max[0];
if (diff_to_full_range < cluster_epsilon) {
m_wrap_u = true;
FT frac = (max[0] - min[0]) / cluster_epsilon;
if (frac < 1)
return;
FT trunc = floor(frac);
frac = frac - trunc;
if (frac < (FT) 0.5) {
cluster_epsilon = (max[0] - min[0]) / (trunc * FT(0.99999));
}
}
else m_wrap_u = false;
}
// The u coordinate corresponds to the rotation around the axis and
// therefore needs to be wrapped around.
virtual void post_wrap(const std::vector<unsigned int> &bitmap,
const std::size_t &u_extent,
const std::size_t &v_extent,
std::vector<unsigned int> &labels) const {
if (!m_wrap_u)
return;
// handle top index separately
unsigned int nw = bitmap[u_extent - 1];
unsigned int l = bitmap[0];
// Special case v_extent is just 1
if (v_extent == 1) {
if (nw && nw != l) {
l = (std::min<unsigned int>)(nw, l);
update_label(labels, (std::max<unsigned int>)(nw, l), l);
}
return;
}
unsigned int w = bitmap[2 * u_extent - 1];
unsigned int sw;
if (l) {
if (nw && nw != l) {
l = (std::min<unsigned int>)(nw, l);
update_label(labels, (std::max<unsigned int>)(nw, l), l);
}
else if (w && w != l) {
l = (std::min<unsigned int>)(w, l);
update_label(labels, (std::max<unsigned int>)(w, l), l);
}
}
// handle mid indices
for (std::size_t y = 1;y<v_extent - 1;y++) {
l = bitmap[y * u_extent];
if (!l)
continue;
nw = bitmap[y * u_extent - 1];
w = bitmap[(y + 1) * u_extent - 1];
sw = bitmap[(y + 2) * u_extent - 1];
if (nw && nw != l) {
l = (std::min<unsigned int>)(nw, l);
update_label(labels, (std::max<unsigned int>)(nw, l), l);
}
if (w && w != l) {
l = (std::min<unsigned int>)(w, l);
update_label(labels, (std::max<unsigned int>)(w, l), l);
}
else if (sw && sw != l) {
l = (std::min<unsigned int>)(sw, l);
update_label(labels, (std::max<unsigned int>)(sw, l), l);
}
}
// handle last index
l = bitmap[(v_extent - 1) * u_extent];
if (!l)
return;
nw = bitmap[(v_extent - 1) * u_extent - 1];
w = bitmap[u_extent * v_extent - 1];
if (nw && nw != l) {
l = (std::min<unsigned int>)(nw, l);
update_label(labels, (std::max<unsigned int>)(nw, l), l);
}
else if (w && w != l) {
l = (std::min<unsigned int>)(w, l);
update_label(labels, (std::max<unsigned int>)(w, l), l);
}
}
virtual void squared_distance(const std::vector<std::size_t> &indices,
std::vector<FT> &dists) const {
Vector_3 a = this->constr_vec(m_axis);
a = this->scale(a, (FT)1.0 / CGAL::sqrt(this->sqlen(a)));
for (std::size_t i = 0;i<indices.size();i++) {
Vector_3 v = this->constr_vec(m_point_on_axis, this->point(indices[i]));
v = this->sum_vectors(v, this->scale(a, -this->scalar_pdct(v, a)));
dists[i] = CGAL::sqrt(this->sqlen(v)) - m_radius;
dists[i] = dists[i] * dists[i];
}
}
virtual void cos_to_normal(const std::vector<std::size_t> &indices,
std::vector<FT> &angles) const {
Vector_3 a = this->constr_vec(m_axis);
a = this->scale(a, (FT)1.0 / CGAL::sqrt(this->sqlen(a)));
for (std::size_t i = 0;i<indices.size();i++) {
Vector_3 v = this->constr_vec(m_point_on_axis, this->point(indices[i]));
v = this->sum_vectors(v, this->scale(a, -this->scalar_pdct(v, a)));
FT length = CGAL::sqrt(this->sqlen(v));
if (length == 0) {
angles[i] = (FT)1.0;
continue;
}
v = this->scale(v, (FT)1.0 / length);
angles[i] = CGAL::abs(this->scalar_pdct(v, this->normal(indices[i])));
}
}
FT cos_to_normal(const Point_3 &p, const Vector_3 &n) const {
Vector_3 a = this->constr_vec(m_axis);
a = this->scale(a, (FT)1.0 / CGAL::sqrt(this->sqlen(a)));
Vector_3 v = this->constr_vec(m_point_on_axis, p);
v = this->sum_vectors(v, this->scale(a, -this->scalar_pdct(v, a)));
FT length = CGAL::sqrt(this->sqlen(v));
if (length == 0)
return (FT)1.0;
v = this->scale(v, (FT)1.0 / length);
return CGAL::abs(this->scalar_pdct(v, n));
}
virtual std::size_t minimum_sample_size() const {
return 2;
}
virtual bool supports_connected_component() const {
return true;
}
private:
FT m_radius;
Line_3 m_axis;
Point_3 m_point_on_axis;
mutable bool m_wrap_u;
/// \endcond
};
}
}
#endif

View File

@ -0,0 +1,909 @@
// Copyright (c) 2015 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) : Sven Oesau, Yannick Verdie, Clément Jamin, Pierre Alliez
//
#ifndef CGAL_SHAPE_DETECTION_3_EFFICIENT_RANSAC_H
#define CGAL_SHAPE_DETECTION_3_EFFICIENT_RANSAC_H
#include <CGAL/Shape_detection_3/Octree.h>
#include <CGAL/Shape_detection_3/Shape_base.h>
#include <CGAL/Random.h>
//for octree ------------------------------
#include <boost/iterator/filter_iterator.hpp>
#include <CGAL/bounding_box.h>
#include <CGAL/Iterator_range.h>
//----------
#include <vector>
#include <cmath>
#include <limits>
#include <fstream>
#include <sstream>
//boost --------------
#include <boost/iterator/counting_iterator.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/make_shared.hpp>
//---------------------
/*!
\file Efficient_RANSAC.h
*/
namespace CGAL {
namespace Shape_detection_3 {
/*!
\ingroup PkgPointSetShapeDetection3
\brief A shape detection algorithm using a RANSAC method.
Given a point set in 3D space with unoriented normals, sampled on surfaces,
this class enables to detect subsets of connected points lying on the surface of primitive shapes.
Each input point is assigned to either none or at most one detected primitive
shape. The implementation follows \cgalCite{schnabel2007efficient}.
\tparam Traits a model of `EfficientRANSACTraits`
*/
template <class Traits>
class Efficient_RANSAC {
public:
/// \cond SKIP_IN_MANUAL
struct Filter_unassigned_points {
Filter_unassigned_points() : m_shape_index(dummy) {}
Filter_unassigned_points(const std::vector<int> &shapeIndex)
: m_shape_index(shapeIndex) {}
bool operator()(std::size_t x) {
if (x < m_shape_index.size())
return m_shape_index[x] == -1;
else return true; // to prevent infinite incrementing
}
const std::vector<int>& m_shape_index;
std::vector<int> dummy;
};
typedef boost::filter_iterator<Filter_unassigned_points,
boost::counting_iterator<std::size_t> > Point_index_iterator;
///< iterator for indices of points.
/// \endcond
/// \name Types
/// @{
/// \cond SKIP_IN_MANUAL
typedef typename Traits::Input_range::iterator Input_iterator;
typedef typename Traits::FT FT; ///< number type.
typedef typename Traits::Point_3 Point; ///< point type.
typedef typename Traits::Vector_3 Vector; ///< vector type.
/// \endcond
typedef typename Traits::Input_range Input_range;
///< Model of the concept `Range` with random access iterators, providing input points and normals
/// through the following two property maps.
typedef typename Traits::Point_map Point_map;
///< property map to access the location of an input point.
typedef typename Traits::Normal_map Normal_map;
///< property map to access the unoriented normal of an input point
typedef Shape_base<Traits> Shape; ///< shape type.
#ifdef DOXYGEN_RUNNING
typedef unspecified_type Shape_range;
#else
struct Shape_range : public Iterator_range<
typename std::vector<boost::shared_ptr<Shape> >::const_iterator> {
typedef Iterator_range<
typename std::vector<boost::shared_ptr<Shape> >::const_iterator> Base;
Shape_range(boost::shared_ptr<std::vector<boost::shared_ptr<Shape> > >
extracted_shapes) : Base(make_range(extracted_shapes->begin(),
extracted_shapes->end())), m_extracted_shapes(extracted_shapes) {}
private:
boost::shared_ptr<std::vector<boost::shared_ptr<Shape> > >
m_extracted_shapes; // keeps a reference to the shape vector
};
#endif
///< An `Iterator_range` with a bidirectional constant iterator type with value type `boost::shared_ptr<Shape>`.
#ifdef DOXYGEN_RUNNING
typedef unspecified_type Point_index_range;
///< An `Iterator_range` with a bidirectional iterator with value type `std::size_t`
/// as indices into the input data that has not been assigned to a shape.
/// As this range class has no `size()` method, the method
/// `Efficient_RANSAC::number_of_unassigned_points()` is provided.
#else
typedef Iterator_range<Point_index_iterator>
Point_index_range;
#endif
/// @}
/// \name Parameters
/// @{
/*!
%Parameters for the shape detection algorithm. They are explained in detail
in Section \ref Point_set_shape_detection_3Parameters of the User Manual.
*/
struct Parameters {
Parameters()
: probability((FT) 0.01)
, min_points((std::numeric_limits<std::size_t>::max)())
, epsilon(-1)
, normal_threshold((FT) 0.9)
, cluster_epsilon(-1)
{}
FT probability; ///< Probability to control search endurance. %Default value: 5%.
std::size_t min_points; ///< Minimum number of points of a shape. %Default value: 1% of total number of input points.
FT epsilon; ///< Maximum tolerance Euclidian distance from a point and a shape. %Default value: 1% of bounding box diagonal.
FT normal_threshold; ///< Maximum tolerance normal deviation from a point's normal to the normal on shape at projected point. %Default value: 0.9 (around 25 degrees).
FT cluster_epsilon; ///< Maximum distance between points to be considered connected. %Default value: 1% of bounding box diagonal.
};
/// @}
private:
typedef internal::Octree<internal::DirectPointAccessor<Traits> >
Direct_octree;
typedef internal::Octree<internal::IndexedPointAccessor<Traits> >
Indexed_octree;
//--------------------------------------------typedef
// Creates a function pointer for instancing shape instances.
template <class ShapeT>
static Shape *factory() {
return new ShapeT;
}
public:
/// \name Initialization
/// @{
/*!
Constructs an empty shape detection engine.
*/
Efficient_RANSAC(Traits t = Traits())
: m_traits(t)
, m_direct_octrees(NULL)
, m_global_octree(NULL)
, m_num_subsets(0)
, m_num_available_points(0)
, m_num_total_points(0)
, m_valid_iterators(false)
{}
/*!
Releases all memory allocated by this instances including shapes.
*/
~Efficient_RANSAC() {
clear();
}
/*!
Retrieves the traits class.
*/
const Traits&
traits() const
{
return m_traits;
}
/*!
Sets the input data. The range must stay valid
until the detection has been performed and the access to the
results is no longer required. The data in the input is reordered by the methods
`detect()` and `preprocess()`. This function first calls `clear()`.
*/
void set_input(
Input_range& input_range,
///< range of input data.
Point_map point_map = Point_map(),
///< property map to access the position of an input point.
Normal_map normal_map = Normal_map()
///< property map to access the normal of an input point.
) {
m_point_pmap = point_map;
m_normal_pmap = normal_map;
m_input_iterator_first = input_range.begin();
m_input_iterator_beyond = input_range.end();
clear();
m_extracted_shapes =
boost::make_shared<std::vector<boost::shared_ptr<Shape> > >();
m_num_available_points = m_num_total_points = std::distance(
m_input_iterator_first, m_input_iterator_beyond);
m_valid_iterators = true;
}
/*!
Registers in the detection engine the shape type `ShapeType` that must inherit from `Shape_base`.
For example, for registering a plane as detectable shape you should call
`ransac.add_shape_factory< Shape_detection_3::Plane<Traits> >();`. Note
that if your call is within a template, you should add the `template`
keyword just before `add_shape_factory`:
`ransac.template add_shape_factory< Shape_detection_3::Plane<Traits> >();`.
*/
template <class Shape_type>
void add_shape_factory() {
m_shape_factories.push_back(factory<Shape_type>);
}
/*!
Constructs internal data structures required for the shape detection.
These structures only depend on the input data, i.e. the points and
normal vectors. This method is called by `detect()`, if it was not called
before by the user.
*/
bool preprocess() {
if (m_num_total_points == 0)
return false;
// Generation of subsets
m_num_subsets = (std::size_t)(std::max<std::ptrdiff_t>)((std::ptrdiff_t)
std::floor(std::log(double(m_num_total_points))/std::log(2.))-9, 2);
// SUBSET GENERATION ->
// approach with increasing subset sizes -> replace with octree later on
Input_iterator last = m_input_iterator_beyond - 1;
std::size_t remainingPoints = m_num_total_points;
m_available_octree_sizes.resize(m_num_subsets);
m_direct_octrees = new Direct_octree *[m_num_subsets];
for (int s = int(m_num_subsets) - 1;s >= 0;--s) {
std::size_t subsetSize = remainingPoints;
std::vector<std::size_t> indices(subsetSize);
if (s) {
subsetSize >>= 1;
for (std::size_t i = 0;i<subsetSize;i++) {
std::size_t index = default_random(2);
index = index + (i<<1);
index = (index >= remainingPoints) ? remainingPoints - 1 : index;
indices[i] = index;
}
// move points to the end of the point vector
std::size_t j = subsetSize;
do {
j--;
typename std::iterator_traits<Input_iterator>::value_type
tmp = (*last);
*last = m_input_iterator_first[indices[std::size_t(j)]];
m_input_iterator_first[indices[std::size_t(j)]] = tmp;
last--;
} while (j > 0);
m_direct_octrees[s] = new Direct_octree(
m_traits, last + 1,
last + subsetSize + 1,
remainingPoints - subsetSize);
}
else
m_direct_octrees[0] = new Direct_octree(
m_traits, m_input_iterator_first,
m_input_iterator_first + (subsetSize),
0);
m_available_octree_sizes[s] = subsetSize;
m_direct_octrees[s]->createTree();
remainingPoints -= subsetSize;
}
m_global_octree = new Indexed_octree(
m_traits, m_input_iterator_first, m_input_iterator_beyond);
m_global_octree->createTree();
return true;
}
/// @}
/// \name Memory Management
/// @{
/*!
Removes all shape types registered for detection.
*/
void clear_shape_factories() {
m_shape_factories.clear();
}
/*!
Frees memory allocated for the internal search structures but keeps the detected shapes.
It invalidates the range retrieved using `unassigned_points()`.
*/
void clear_octrees() {
// If there is no data yet, there are no data structures.
if (!m_valid_iterators)
return;
if (m_global_octree) {
delete m_global_octree;
m_global_octree = NULL;
}
if (m_direct_octrees) {
for (std::size_t i = 0;i<m_num_subsets;i++)
delete m_direct_octrees[i];
delete [] m_direct_octrees;
m_direct_octrees = NULL;
}
m_num_subsets = 0;
}
/*!
Calls `clear_octrees()` and removes all detected shapes.
All internal structures are cleaned, including formerly detected shapes.
Thus iterators and ranges retrieved through `shapes()` and `indices_of_unassigned_points()`
are invalidated.
*/
void clear() {
// If there is no data yet, there are no data structures.
if (!m_valid_iterators)
return;
std::vector<int>().swap(m_shape_index);
m_extracted_shapes =
boost::make_shared<std::vector<boost::shared_ptr<Shape> > >();
m_num_available_points = m_num_total_points;
clear_octrees();
}
/// @}
/// \name Detection
/// @{
/*!
Performs the shape detection. Shape types considered during the detection
are those registered using `add_shape_factory()`.
\return `true` if shape types have been registered and
input data has been set. Otherwise, `false` is returned.
*/
bool detect(
const Parameters &options = Parameters()
///< %Parameters for shape detection.
) {
// No shape types for detection or no points provided, exit
if (m_shape_factories.size() == 0 ||
(m_input_iterator_beyond - m_input_iterator_first) == 0)
return false;
if (m_num_subsets == 0 || m_global_octree == 0) {
if (!preprocess())
return false;
}
// Reset data structures possibly used by former search
m_extracted_shapes =
boost::make_shared<std::vector<boost::shared_ptr<Shape> > >();
m_num_available_points = m_num_total_points;
for (std::size_t i = 0;i<m_num_subsets;i++) {
m_available_octree_sizes[i] = m_direct_octrees[i]->size();
}
// Use bounding box diagonal as reference for default values
Bbox_3 bbox = m_global_octree->boundingBox();
FT bbox_diagonal = (FT) CGAL::sqrt(
(bbox.xmax() - bbox.xmin()) * (bbox.xmax() - bbox.xmin())
+ (bbox.ymax() - bbox.ymin()) * (bbox.ymax() - bbox.ymin())
+ (bbox.zmax() - bbox.zmin()) * (bbox.zmax() - bbox.zmin()));
m_options = options;
// Epsilon or cluster_epsilon have been set by the user?
// If not, derive from bounding box diagonal
m_options.epsilon = (m_options.epsilon < 0)
? bbox_diagonal * (FT) 0.01 : m_options.epsilon;
m_options.cluster_epsilon = (m_options.cluster_epsilon < 0)
? bbox_diagonal * (FT) 0.01 : m_options.cluster_epsilon;
// Minimum number of points has been set?
m_options.min_points =
(m_options.min_points >= m_num_available_points) ?
(std::size_t)((FT)0.01 * m_num_available_points) :
m_options.min_points;
m_options.min_points = (m_options.min_points < 10) ? 10 : m_options.min_points;
// Initializing the shape index
m_shape_index.assign(m_num_available_points, -1);
// List of all randomly drawn candidates
// with the minimum number of points
std::vector<Shape *> candidates;
// Identifying minimum number of samples
std::size_t required_samples = 0;
for (std::size_t i = 0;i<m_shape_factories.size();i++) {
Shape *tmp = (Shape *) m_shape_factories[i]();
required_samples = (std::max<std::size_t>)(required_samples, tmp->minimum_sample_size());
delete tmp;
}
std::size_t first_sample; // first sample for RANSAC
FT best_expected = 0;
// number of points that have been assigned to a shape
std::size_t num_invalid = 0;
std::size_t generated_candidates = 0;
std::size_t failed_candidates = 0;
bool force_exit = false;
bool keep_searching = true;
do { // main loop
best_expected = 0;
if (keep_searching)
do {
// Generate candidates
//1. pick a point p1 randomly among available points
std::set<std::size_t> indices;
bool done = false;
do {
do
first_sample = default_random(m_num_available_points);
while (m_shape_index[first_sample] != -1);
done = m_global_octree->drawSamplesFromCellContainingPoint(
get(m_point_pmap,
*(m_input_iterator_first + first_sample)),
select_random_octree_level(),
indices,
m_shape_index,
required_samples);
} while (m_shape_index[first_sample] != -1 || !done);
generated_candidates++;
//add candidate for each type of primitives
for(typename std::vector<Shape *(*)()>::iterator it =
m_shape_factories.begin(); it != m_shape_factories.end(); it++) {
Shape *p = (Shape *) (*it)();
//compute the primitive and says if the candidate is valid
p->compute(indices,
m_input_iterator_first,
m_traits,
m_point_pmap,
m_normal_pmap,
m_options.epsilon,
m_options.normal_threshold);
if (p->is_valid()) {
improve_bound(p, m_num_available_points - num_invalid, 1, 500);
//evaluate the candidate
if(p->max_bound() >= m_options.min_points && p->score() > 0) {
if (best_expected < p->expected_value())
best_expected = p->expected_value();
candidates.push_back(p);
}
else {
failed_candidates++;
delete p;
}
}
else {
failed_candidates++;
delete p;
}
}
if (failed_candidates >= 10000)
force_exit = true;
keep_searching = (stop_probability(m_options.min_points,
m_num_available_points - num_invalid,
generated_candidates, m_global_octree->maxLevel())
> m_options.probability);
} while( !force_exit
&& stop_probability((std::size_t) best_expected,
m_num_available_points - num_invalid,
generated_candidates,
m_global_octree->maxLevel())
> m_options.probability
&& keep_searching);
// end of generate candidate
if (force_exit) {
break;
}
if (candidates.empty())
continue;
// Now get the best candidate in the current set of all candidates
// Note that the function sorts the candidates:
// the best candidate is always the last element of the vector
Shape *best_candidate =
get_best_candidate(candidates, m_num_available_points - num_invalid);
// If search is done and the best candidate is too small, we are done.
if (!keep_searching && best_candidate->m_score < m_options.min_points)
break;
if (!best_candidate)
continue;
best_candidate->m_indices.clear();
best_candidate->m_score =
m_global_octree->score(best_candidate,
m_shape_index,
FT(3) * m_options.epsilon,
m_options.normal_threshold);
best_expected = static_cast<FT>(best_candidate->m_score);
best_candidate->connected_component(best_candidate->m_indices,
m_options.cluster_epsilon);
// check score against min_points and clear out candidates if too low
if (best_candidate->indices_of_assigned_points().size() <
m_options.min_points)
{
for (std::size_t i = 0;i < candidates.size() - 1;i++) {
if (best_candidate->is_same(candidates[i])) {
delete candidates[i];
candidates[i] = NULL;
}
}
candidates.back() = NULL;
delete best_candidate;
best_candidate = NULL;
// Trimming candidates list
std::size_t empty = 0, occupied = 0;
while (empty < candidates.size()) {
while (empty < candidates.size() && candidates[empty]) empty++;
if (empty >= candidates.size())
break;
if (occupied < empty)
occupied = empty + 1;
while (occupied < candidates.size() && !candidates[occupied])
occupied++;
if (occupied >= candidates.size())
break;
candidates[empty] = candidates[occupied];
candidates[occupied] = NULL;
empty++;
occupied++;
}
candidates.resize(empty);
}
else
if (stop_probability((std::size_t) best_candidate->expected_value(),
(m_num_available_points - num_invalid),
generated_candidates,
m_global_octree->maxLevel())
<= m_options.probability) {
// Remove candidate from list
candidates.back() = NULL;
//1. add best candidate to final result.
m_extracted_shapes->push_back(
boost::shared_ptr<Shape>(best_candidate));
//2. remove the points
const std::vector<std::size_t> &indices_points_best_candidate =
best_candidate->indices_of_assigned_points();
for (std::size_t i = 0;i<indices_points_best_candidate.size();i++) {
m_shape_index[indices_points_best_candidate.at(i)] =
int(m_extracted_shapes->size()) - 1;
num_invalid++;
for (std::size_t j = 0;j<m_num_subsets;j++) {
if (m_direct_octrees[j] && m_direct_octrees[j]->m_root) {
std::size_t offset = m_direct_octrees[j]->offset();
if (offset <= indices_points_best_candidate.at(i) &&
(indices_points_best_candidate.at(i) - offset)
< m_direct_octrees[j]->size()) {
m_available_octree_sizes[j]--;
}
}
}
}
//2.3 Remove the points from the subtrees
generated_candidates--;
failed_candidates = 0;
best_expected = 0;
std::vector<std::size_t> subset_sizes(m_num_subsets);
subset_sizes[0] = m_available_octree_sizes[0];
for (std::size_t i = 1;i<m_num_subsets;i++) {
subset_sizes[i] = subset_sizes[i-1] + m_available_octree_sizes[i];
}
//3. Remove points from candidates common with extracted primitive
//#pragma omp parallel for
best_expected = 0;
for (std::size_t i=0;i< candidates.size()-1;i++) {
if (candidates[i]) {
candidates[i]->update_points(m_shape_index);
candidates[i]->compute_bound(
subset_sizes[candidates[i]->m_nb_subset_used - 1],
m_num_available_points - num_invalid);
if (candidates[i]->max_bound() < m_options.min_points) {
delete candidates[i];
candidates[i] = NULL;
}
else {
best_expected = (candidates[i]->expected_value() > best_expected) ?
candidates[i]->expected_value() : best_expected;
}
}
}
std::size_t start = 0, end = candidates.size() - 1;
while (start < end) {
while (candidates[start] && start < end) start++;
while (!candidates[end] && start < end) end--;
if (!candidates[start] && candidates[end] && start < end) {
candidates[start] = candidates[end];
candidates[end] = NULL;
start++;
end--;
}
}
if (candidates[end]) end++;
candidates.resize(end);
}
keep_searching = (stop_probability(m_options.min_points,
m_num_available_points - num_invalid,
generated_candidates,
m_global_octree->maxLevel())
> m_options.probability);
}
while((keep_searching
&& FT(m_num_available_points - num_invalid) >= m_options.min_points)
|| best_expected >= m_options.min_points);
// Clean up remaining candidates.
for (std::size_t i = 0;i<candidates.size();i++)
delete candidates[i];
candidates.resize(0);
m_num_available_points -= num_invalid;
return true;
}
/// @}
/// \name Access
/// @{
/*!
Returns an `Iterator_range` with a bidirectional iterator with value type
`boost::shared_ptr<Shape>` over the detected shapes in the order of detection.
Depending on the chosen probability
for the detection, the shapes are ordered with decreasing size.
*/
Shape_range shapes() const {
return Shape_range(m_extracted_shapes);
}
/*!
Number of points not assigned to a shape.
*/
std::size_t number_of_unassigned_points() {
return m_num_available_points;
}
/*!
Returns an `Iterator_range` with a bidirectional iterator with value type `std::size_t`
as indices into the input data that has not been assigned to a shape.
*/
Point_index_range indices_of_unassigned_points() {
Filter_unassigned_points fup(m_shape_index);
Point_index_iterator p1 =
boost::make_filter_iterator<Filter_unassigned_points>(
fup,
boost::counting_iterator<std::size_t>(0),
boost::counting_iterator<std::size_t>(m_shape_index.size()));
return make_range(p1, Point_index_iterator(p1.end()));
}
/// @}
private:
int select_random_octree_level() {
return (int) default_random(m_global_octree->maxLevel() + 1);
}
Shape* get_best_candidate(std::vector<Shape* >& candidates,
const std::size_t num_available_points) {
if (candidates.size() == 1)
return candidates.back();
int index_worse_candidate = 0;
bool improved = true;
while (index_worse_candidate < (int)candidates.size() - 1 && improved) {
improved = false;
typename Shape::Compare_by_max_bound comp;
std::sort(candidates.begin() + index_worse_candidate,
candidates.end(),
comp);
//refine the best one
improve_bound(candidates.back(),
num_available_points, m_num_subsets,
m_options.min_points);
int position_stop;
//Take all those intersecting the best one, check for equal ones
for (position_stop = int(candidates.size()) - 1;
position_stop > index_worse_candidate;
position_stop--) {
if (candidates.back()->min_bound() >
candidates.at(position_stop)->max_bound())
break;//the intervals do not overlaps anymore
if (candidates.at(position_stop)->max_bound()
<= m_options.min_points)
break; //the following candidate doesnt have enough points!
//if we reach this point, there is an overlap
// between best one and position_stop
//so request refining bound on position_stop
improved |= improve_bound(candidates.at(position_stop),
num_available_points,
m_num_subsets,
m_options.min_points);
//test again after refined
if (candidates.back()->min_bound() >
candidates.at(position_stop)->max_bound())
break;//the intervals do not overlaps anymore
}
index_worse_candidate = position_stop;
}
return candidates.back();
}
bool improve_bound(Shape *candidate,
std::size_t num_available_points,
std::size_t max_subset,
std::size_t min_points) {
if (candidate->m_nb_subset_used >= max_subset)
return false;
if (candidate->m_nb_subset_used >= m_num_subsets)
return false;
candidate->m_nb_subset_used =
(candidate->m_nb_subset_used >= m_num_subsets) ?
m_num_subsets - 1 : candidate->m_nb_subset_used;
//what it does is add another subset and recompute lower and upper bound
//the next subset to include is provided by m_nb_subset_used
std::size_t num_points_evaluated = 0;
for (std::size_t i=0;i<candidate->m_nb_subset_used;i++)
num_points_evaluated += m_available_octree_sizes[i];
// need score of new subset as well as sum of
// the score of the previous considered subset
std::size_t new_score = 0;
std::size_t new_sampled_points = 0;
do {
new_score = m_direct_octrees[candidate->m_nb_subset_used]->score(
candidate,
m_shape_index,
m_options.epsilon,
m_options.normal_threshold);
candidate->m_score += new_score;
num_points_evaluated +=
m_available_octree_sizes[candidate->m_nb_subset_used];
new_sampled_points +=
m_available_octree_sizes[candidate->m_nb_subset_used];
candidate->m_nb_subset_used++;
} while (new_sampled_points < min_points &&
candidate->m_nb_subset_used < m_num_subsets);
candidate->m_score = candidate->m_indices.size();
candidate->compute_bound(num_points_evaluated, num_available_points);
return true;
}
inline FT stop_probability(std::size_t largest_candidate, std::size_t num_pts, std::size_t num_candidates, std::size_t octree_depth) const {
return (std::min<FT>)(std::pow((FT) 1.f - (FT) largest_candidate / FT(num_pts * octree_depth * 4), (int) num_candidates), (FT) 1);
}
private:
Parameters m_options;
// Traits class.
Traits m_traits;
// Octrees build on input data for quick shape evaluation and
// sample selection within an octree cell.
Direct_octree **m_direct_octrees;
Indexed_octree *m_global_octree;
std::vector<std::size_t> m_available_octree_sizes;
std::size_t m_num_subsets;
// maps index into points to assigned extracted primitive
std::vector<int> m_shape_index;
std::size_t m_num_available_points;
std::size_t m_num_total_points;
//give the index of the subset of point i
std::vector<int> m_index_subsets;
boost::shared_ptr<std::vector<boost::shared_ptr<Shape> > > m_extracted_shapes;
std::vector<Shape *(*)()> m_shape_factories;
// iterators of input data
bool m_valid_iterators;
Input_iterator m_input_iterator_first, m_input_iterator_beyond;
Point_map m_point_pmap;
Normal_map m_normal_pmap;
};
}
}
#endif // CGAL_SHAPE_DETECTION_3_EFFICIENT_RANSAC_H

View File

@ -0,0 +1,317 @@
// Copyright (c) 2015 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) : Sven Oesau, Yannick Verdie, Clément Jamin, Pierre Alliez
//
#ifndef CGAL_SHAPE_DETECTION_3_EFFICIENT_RANSAC_TRAITS_COMPILATIO_TEST_H
#define CGAL_SHAPE_DETECTION_3_EFFICIENT_RANSAC_TRAITS_COMPILATIO_TEST_H
#include <CGAL/Origin.h>
#include <CGAL/Search_traits_3.h>
namespace CGAL {
struct Point__3{
Point__3(){}
Point__3(Origin const&){}
Point__3(double, double, double){}
};
struct Vector__3 {
Vector__3(){}
Vector__3(Null_vector const&){}
Vector__3(double, double, double){}
};
namespace Shape_detection_3 {
template <class Gt,
class InputRange,
class InputPointMap,
class InputNormalMap>
struct Efficient_RANSAC_traits {
typedef Efficient_RANSAC_traits<
Gt, InputRange, InputPointMap, InputNormalMap> Self;
///
typedef double FT;
typedef Point__3 Point_3;
typedef Vector__3 Vector_3;
///
struct Sphere_3 {};
///
struct Line_3 {};
///
struct Circle_2 {};
///
struct Vector_2 {};
///
struct Plane_3 {};
///
struct Point_2 {};
///
typedef InputRange Input_range;
///
typedef InputPointMap Point_map;
///
typedef InputNormalMap Normal_map;
///
class Search_traits {
public:
typedef Dimension_tag<3> Dimension;
typedef typename Self::Point_3 Point_d;
struct Cartesian_const_iterator_d
{
typedef Cartesian_const_iterator_d self;
typedef std::random_access_iterator_tag iterator_category;
typedef FT value_type;
typedef std::ptrdiff_t difference_type;
typedef const value_type* pointer;
typedef const value_type& reference;
self& operator++() { return *this; }
self operator++(int) { return *this; }
self& operator--() { return *this; }
self operator--(int) { return *this; }
self& operator+=(difference_type) { return *this; }
self& operator-=(difference_type) { return *this; }
self operator+(difference_type) const { return *this; }
self operator-(difference_type) const { return *this; }
difference_type operator-(self ) const { return 0; }
value_type operator*() const { return value_type(); }
value_type operator[](difference_type ) const { return value_type(); }
bool operator==(const self& ) const { return true; }
bool operator!=(const self& ) const { return true; }
bool operator<(const self& ) const { return true; }
};
struct Construct_cartesian_const_iterator_d{
typedef Point_3 Point_d;
typedef Cartesian_const_iterator_d result_type;
Cartesian_const_iterator_d operator()(Point_d const& ) const { return Cartesian_const_iterator_d(); }
Cartesian_const_iterator_d operator()(Point_d const& , int) const { return Cartesian_const_iterator_d(); }
};
struct Iso_box_d{};
struct Sphere_d{};
struct Construct_iso_box_d{};
struct Construct_min_vertex_d{};
struct Construct_max_vertex_d{};
struct Construct_center_d{};
struct Compute_squared_radius_d{};
typedef typename Self::FT FT;
Construct_cartesian_const_iterator_d construct_cartesian_const_iterator_d_object() const {
return Construct_cartesian_const_iterator_d();
}
};
struct Construct_point_3
{
Point_3 operator()(FT, FT, FT) { return Point_3(); }
};
Construct_point_3 construct_point_3_object() const
{ return Construct_point_3(); }
struct Construct_point_2
{
Point_2 operator()(FT, FT) { return Point_2(); }
};
Construct_point_2 construct_point_2_object() const
{ return Construct_point_2(); }
struct Construct_vector_3
{
Vector_3 operator()(Null_vector const&) { return NULL_VECTOR; }
Vector_3 operator()(Point_3 const&, Point_3 const&) { return Vector_3(); }
Vector_3 operator()(Origin const&, Point_3 const&) { return Vector_3(); }
Vector_3 operator()(Line_3 const&) { return Vector_3(); }
};
Construct_vector_3 construct_vector_3_object() const
{ return Construct_vector_3(); }
struct Construct_vector_2
{
Vector_2 operator()(Null_vector const&) { return NULL_VECTOR; }
Vector_2 operator()(Point_2 const&, Point_2 const&) { return Vector_2(); }
};
Construct_vector_2 construct_vector_2_object() const
{ return Construct_vector_2(); }
struct Construct_sphere_3
{
Sphere_3 operator()(Point_3 const&, FT) { return Sphere_3(); }
};
Construct_sphere_3 construct_sphere_3_object() const
{ return Construct_sphere_3(); }
struct Construct_circle_2
{
Circle_2 operator()(Point_2 const&, Point_2 const&, Point_2 const&) { return Circle_2(); }
};
Construct_circle_2 construct_circle_2_object() const
{ return Construct_circle_2(); }
struct Construct_line_3
{
Line_3 operator()(Point_3 const&, Vector_3 const&) { return Line_3(); }
};
Construct_line_3 construct_line_3_object() const
{ return Construct_line_3(); }
struct Construct_point_on_3
{
Point_3 operator()(Line_3 const&, int) { return Point_3(); }
};
Construct_point_on_3 construct_point_on_3_object() const
{ return Construct_point_on_3(); }
struct Compute_x_2
{
FT operator()(Point_2 const&) const { return 0; }
FT operator()(Vector_2 const&) const { return 0; }
};
Compute_x_2 compute_x_2_object() const
{ return Compute_x_2(); }
struct Compute_y_2
{
FT operator()(Point_2 const&) const { return 0; }
FT operator()(Vector_2 const&) const { return 0; }
};
Compute_y_2 compute_y_2_object() const
{ return Compute_y_2(); }
struct Compute_x_3
{
FT operator()(Point_3 const&) const { return 0; }
FT operator()(Vector_3 const&) const { return 0; }
};
Compute_x_3 compute_x_3_object() const
{ return Compute_x_3(); }
struct Compute_y_3
{
FT operator()(Point_3 const&) const { return 0; }
FT operator()(Vector_3 const&) const { return 0; }
};
Compute_y_3 compute_y_3_object() const
{ return Compute_y_3(); }
struct Compute_z_3
{
FT operator()(Point_3 const&) const { return 0; }
FT operator()(Vector_3 const&) const { return 0; }
};
Compute_z_3 compute_z_3_object() const
{ return Compute_z_3(); }
struct Compute_squared_length_3
{ FT operator()(Vector_3 const&) const { return 0; } };
Compute_squared_length_3 compute_squared_length_3_object() const
{ return Compute_squared_length_3(); }
struct Compute_squared_length_2
{ FT operator()(Vector_2 const&) const { return 0; } };
Compute_squared_length_2 compute_squared_length_2_object() const
{ return Compute_squared_length_2(); }
struct Construct_scaled_vector_3
{
Vector_3 operator()(Vector_3 const&, FT) { return Vector_3(); }
};
Construct_scaled_vector_3 construct_scaled_vector_3_object() const
{ return Construct_scaled_vector_3(); }
struct Construct_sum_of_vectors_3
{
Vector_3 operator()(Vector_3 const&, Vector_3 const&) { return Vector_3(); }
};
Construct_sum_of_vectors_3 construct_sum_of_vectors_3_object() const
{ return Construct_sum_of_vectors_3(); }
struct Construct_translated_point_3
{ Point_3 operator()(Point_3 const&, Vector_3 const&) const { return Point_3(); } };
Construct_translated_point_3 construct_translated_point_3_object() const
{ return Construct_translated_point_3(); }
struct Compute_scalar_product_3
{ FT operator()(Vector_3 const&, Vector_3 const&) const { return 0; } };
Compute_scalar_product_3 compute_scalar_product_3_object() const
{ return Compute_scalar_product_3(); }
struct Construct_cross_product_vector_3
{ Vector_3 operator()(Vector_3 const&, Vector_3 const&) const { return Vector_3(); } };
Construct_cross_product_vector_3 construct_cross_product_vector_3_object() const
{ return Construct_cross_product_vector_3(); }
struct Construct_center_3
{ Point_3 operator()(Sphere_3 const&) const { return Point_3(); } };
Construct_center_3 construct_center_3_object() const
{ return Construct_center_3(); }
struct Compute_squared_radius_3
{ FT operator()(Sphere_3 const&) const { return 0; } };
Compute_squared_radius_3 compute_squared_radius_3_object() const
{ return Compute_squared_radius_3(); }
struct Compute_squared_radius_2
{ FT operator()(Circle_2 const&) const { return 0; } };
Compute_squared_radius_2 compute_squared_radius_2_object() const
{ return Compute_squared_radius_2(); }
struct Construct_center_2
{ Point_2 operator()(Circle_2 const&) const { return Point_2(); } };
Construct_center_2 construct_center_2_object() const
{ return Construct_center_2(); }
struct Collinear_2
{
bool operator()(const Point_2& p, const Point_2& q, const Point_2& r) const
{ return false; }
};
Collinear_2 collinear_2_object() const
{ return Collinear_2(); }
/*struct Compute_squared_distance_3
{ FT operator()(Point_3 const&, Point_3 const&) const { return 0; } };
Compute_squared_distance_3 compute_squared_distance_3_object() const
{ return Compute_squared_distance_3(); }*/
///
Efficient_RANSAC_traits(const Gt& gt = Gt())
: m_gt(gt) {}
private:
Gt m_gt;
};
} } // end of namespace CGAL::Shape_detection_3
#endif // CGAL_SHAPE_DETECTION_3_EFFICIENT_RANSAC_TRAITS_COMPILATIO_TEST_H

View File

@ -0,0 +1,185 @@
// Copyright (c) 2015 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) : Sven Oesau, Yannick Verdie, Clément Jamin, Pierre Alliez
//
#ifndef CGAL_SHAPE_DETECTION_3_EFFICIENT_RANSAC_TRAITS_H
#define CGAL_SHAPE_DETECTION_3_EFFICIENT_RANSAC_TRAITS_H
#include <CGAL/Search_traits_3.h>
namespace CGAL {
namespace Shape_detection_3 {
/*!
\ingroup PkgPointSetShapeDetection3
\brief %Default traits class to use the shape detection class `Efficient_RANSAC`.
\cgalModels `EfficientRANSACTraits`
\tparam Gt a model of the concept `#Kernel` with `Gt::FT` being `float` or `double`.
\tparam InputRange is a model of `Range` with random access iterators,
providing input points and normals through the following two property maps.
\tparam InputPointMap is a model of `ReadablePropertyMap` with `std::iterator_traits<Input_range::iterator>::%value_type` as key type and `Geom_traits::Point_3` as value type.
\tparam InputNormalMap is a model of `ReadablePropertyMap` with `std::iterator_traits<Input_range::iterator>::%value_type` as key type and `Geom_traits::Vector_3` as value type.
*/
template <class Gt,
class InputRange,
class InputPointMap,
class InputNormalMap>
struct Efficient_RANSAC_traits {
///
typedef typename Gt::FT FT;
///
typedef typename Gt::Point_3 Point_3;
///
typedef typename Gt::Vector_3 Vector_3;
///
typedef typename Gt::Sphere_3 Sphere_3;
///
typedef typename Gt::Line_3 Line_3;
///
typedef typename Gt::Circle_2 Circle_2;
///
typedef typename Gt::Plane_3 Plane_3;
///
typedef typename Gt::Point_2 Point_2;
///
typedef typename Gt::Vector_2 Vector_2;
///
typedef InputRange Input_range;
///
typedef InputPointMap Point_map;
///
typedef InputNormalMap Normal_map;
///
typedef CGAL::Search_traits_3<Gt> Search_traits;
///
Efficient_RANSAC_traits(const Gt& gt = Gt())
: m_gt(gt) {}
typedef typename Gt::Construct_point_3 Construct_point_3;
Construct_point_3 construct_point_3_object() const
{ return m_gt.construct_point_3_object(); }
typedef typename Gt::Construct_vector_3 Construct_vector_3;
Construct_vector_3 construct_vector_3_object() const
{ return m_gt.construct_vector_3_object(); }
typedef typename Gt::Construct_point_2 Construct_point_2;
Construct_point_2 construct_point_2_object() const
{ return m_gt.construct_point_2_object(); }
typedef typename Gt::Construct_vector_2 Construct_vector_2;
Construct_vector_2 construct_vector_2_object() const
{ return m_gt.construct_vector_2_object(); }
typedef typename Gt::Construct_sphere_3 Construct_sphere_3;
Construct_sphere_3 construct_sphere_3_object() const
{ return m_gt.construct_sphere_3_object(); }
typedef typename Gt::Construct_line_3 Construct_line_3;
Construct_line_3 construct_line_3_object() const
{ return m_gt.construct_line_3_object(); }
typedef typename Gt::Construct_circle_2 Construct_circle_2;
Construct_circle_2 construct_circle_2_object() const
{ return m_gt.construct_circle_2_object(); }
typedef typename Gt::Construct_point_on_3 Construct_point_on_3;
Construct_point_on_3 construct_point_on_3_object() const
{ return m_gt.construct_point_on_3_object(); }
typedef typename Gt::Compute_x_2 Compute_x_2;
Compute_x_2 compute_x_2_object() const
{ return m_gt.compute_x_2_object(); }
typedef typename Gt::Compute_y_2 Compute_y_2;
Compute_y_2 compute_y_2_object() const
{ return m_gt.compute_y_2_object(); }
typedef typename Gt::Compute_x_3 Compute_x_3;
Compute_x_3 compute_x_3_object() const
{ return m_gt.compute_x_3_object(); }
typedef typename Gt::Compute_y_3 Compute_y_3;
Compute_y_3 compute_y_3_object() const
{ return m_gt.compute_y_3_object(); }
typedef typename Gt::Compute_z_3 Compute_z_3;
Compute_z_3 compute_z_3_object() const
{ return m_gt.compute_z_3_object(); }
typedef typename Gt::Compute_squared_length_3 Compute_squared_length_3;
Compute_squared_length_3 compute_squared_length_3_object() const
{ return m_gt.compute_squared_length_3_object(); }
typedef typename Gt::Compute_squared_length_2 Compute_squared_length_2;
Compute_squared_length_2 compute_squared_length_2_object() const
{ return m_gt.compute_squared_length_2_object(); }
typedef typename Gt::Construct_scaled_vector_3 Construct_scaled_vector_3;
Construct_scaled_vector_3 construct_scaled_vector_3_object() const
{ return m_gt.construct_scaled_vector_3_object(); }
typedef typename Gt::Construct_sum_of_vectors_3 Construct_sum_of_vectors_3;
Construct_sum_of_vectors_3 construct_sum_of_vectors_3_object() const
{ return m_gt.construct_sum_of_vectors_3_object(); }
typedef typename Gt::Construct_translated_point_3 Construct_translated_point_3;
Construct_translated_point_3 construct_translated_point_3_object() const
{ return m_gt.construct_translated_point_3_object(); }
typedef typename Gt::Compute_scalar_product_3 Compute_scalar_product_3;
Compute_scalar_product_3 compute_scalar_product_3_object() const
{ return m_gt.compute_scalar_product_3_object(); }
typedef typename Gt::Construct_cross_product_vector_3 Construct_cross_product_vector_3;
Construct_cross_product_vector_3 construct_cross_product_vector_3_object() const
{ return m_gt.construct_cross_product_vector_3_object(); }
typedef typename Gt::Construct_center_3 Construct_center_3;
Construct_center_3 construct_center_3_object() const
{ return m_gt.construct_center_3_object(); }
typedef typename Gt::Construct_center_2 Construct_center_2;
Construct_center_2 construct_center_2_object() const
{ return m_gt.construct_center_2_object(); }
typedef typename Gt::Compute_squared_radius_3 Compute_squared_radius_3;
Compute_squared_radius_3 compute_squared_radius_3_object() const
{ return m_gt.compute_squared_radius_3_object(); }
typedef typename Gt::Compute_squared_radius_2 Compute_squared_radius_2;
Compute_squared_radius_2 compute_squared_radius_2_object() const
{ return m_gt.compute_squared_radius_2_object(); }
typedef typename Gt::Collinear_2 Collinear_2;
Collinear_2 collinear_2_object() const
{ return m_gt.collinear_2_object(); }
private:
Gt m_gt;
};
} } // end of namespace CGAL::Shape_detection_3
#endif // CGAL_SHAPE_DETECTION_3_EFFICIENT_RANSAC_TRAITS_H

View File

@ -0,0 +1,723 @@
// Copyright (c) 2015 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) : Sven Oesau, Yannick Verdie, Clément Jamin, Pierre Alliez
//
#ifndef CGAL_SHAPE_DETECTION_3_OCTREE_H
#define CGAL_SHAPE_DETECTION_3_OCTREE_H
#include <limits>
#include <stack>
#include <CGAL/Random.h>
#include <CGAL/Bbox_3.h>
#include <CGAL/Shape_detection_3/Shape_base.h>
extern int scoreTime;
namespace CGAL {
namespace Shape_detection_3 {
template<class Traits>
class Efficient_RANSAC;
namespace internal {
const std::size_t size_t_max = (std::numeric_limits<std::size_t>::max)();
template<class Sdt>
class DirectPointAccessor {
public:
typedef Sdt Sd_traits;
typedef typename Sd_traits::Input_range::iterator Input_iterator;
DirectPointAccessor() {}
DirectPointAccessor(const Input_iterator &begin,
const Input_iterator &beyond,
std::size_t offset) : m_first(begin), m_offset(offset) {
m_beyond = (beyond == begin) ? begin : beyond - 1;
}
Input_iterator at(std::size_t i) {
return m_first + i;
}
std::size_t index(std::size_t i) {
return i + m_offset;
}
std::size_t offset() {
return m_offset;
}
std::size_t size() {
return m_beyond - m_first + 1;
}
Input_iterator first() {
return m_first;
}
Input_iterator beyond() {
return m_beyond;
}
void setData(Input_iterator &begin, Input_iterator &beyond) {
m_beyond = (beyond == begin) ? begin : beyond - 1;
}
void swap(std::size_t a, std::size_t b) {
typename std::iterator_traits<Input_iterator>::value_type tmp;
tmp = m_first[a];
m_first[a] = m_first[b];
m_first[b] = tmp;
}
protected:
Input_iterator m_first;
private:
Input_iterator m_beyond;
std::size_t m_offset;
};
template<class Sdt>
class IndexedPointAccessor {
public:
typedef Sdt Sd_traits;
typedef typename Sd_traits::Input_range::iterator Input_iterator;
IndexedPointAccessor() {}
IndexedPointAccessor(const Input_iterator &begin,
const Input_iterator &beyond, std::size_t)
: m_first(begin) {
m_beyond = (beyond == begin) ? begin : beyond - 1;
m_indices.resize(size());
for (std::size_t i = 0;i<size();i++)
m_indices[i] = i;
}
Input_iterator at(std::size_t i) {
return m_first + m_indices[i];
}
std::size_t index(std::size_t i) {
return m_indices[i];
}
std::size_t offset() {
return 0;
}
Input_iterator first() {
return m_first;
}
Input_iterator beyond() {
return m_beyond;
}
void setData(Input_iterator &begin, Input_iterator &beyond) {
m_beyond = (beyond == begin) ? begin : beyond - 1;
m_indices.resize(size());
for (std::size_t i = 0;i<size();i++)
m_indices[i] = i;
}
std::size_t size() {
return m_beyond - m_first + 1;
}
void swap(std::size_t a, std::size_t b) {
std::size_t tmp = m_indices[a];
m_indices[a] = m_indices[b];
m_indices[b] = tmp;
}
protected:
Input_iterator m_first;
private:
std::vector<std::size_t> m_indices;
Input_iterator m_beyond;
};
template<class PointAccessor>
class Octree : public PointAccessor {
typedef typename PointAccessor::Sd_traits Sd_traits;
typedef typename Sd_traits::Input_range::iterator Input_iterator;
typedef Shape_base<Sd_traits> Shape;
typedef typename Sd_traits::Point_3 Point_3;
typedef typename Sd_traits::Vector_3 Vector_3;
typedef typename Sd_traits::FT FT;
typedef typename Sd_traits::Point_map Point_map;
typedef typename Sd_traits::Normal_map Normal_map;
template<class Sd_traits>
friend class ::CGAL::Shape_detection_3::Efficient_RANSAC;
struct Cell {
std::size_t first, last;
Cell *child[8];
Point_3 center;
std::size_t level;
Cell(std::size_t first, std::size_t last, Point_3 center, std::size_t level)
: first(first), last(last), center(center), level(level) {
memset(child, 0, sizeof(Cell *) * 8);
}
bool isLeaf() const {
for (std::size_t i = 0;i<8;i++) {
if (child[i])
return false;
}
return true;
}
std::size_t size() const {
if (first == size_t_max || last == size_t_max)
return 0;
else return (last - first + 1);
}
};
// --------------------------------------------------------------------------
// Utilities
// --------------------------------------------------------------------------
FT get_x(const Vector_3& v){ return m_traits.compute_x_3_object()(v); }
FT get_y(const Vector_3& v){ return m_traits.compute_y_3_object()(v); }
FT get_z(const Vector_3& v){ return m_traits.compute_z_3_object()(v); }
FT get_x(const Point_3& p){ return m_traits.compute_x_3_object()(p); }
FT get_y(const Point_3& p){ return m_traits.compute_y_3_object()(p); }
FT get_z(const Point_3& p){ return m_traits.compute_z_3_object()(p); }
FT get_coord(const Point_3& p, unsigned int d)
{
CGAL_assertion(d >= 0 && d < 3);
switch (d)
{
case 0: return get_x(p);
case 1: return get_y(p);
case 2: return get_z(p);
default: return FT(0);
}
}
Point_3 constr_pt(FT x, FT y, FT z) const
{ return m_traits.construct_point_3_object()(x, y, z); }
Vector_3 constr_vec(const Point_3& p, const Point_3& q) const
{ return m_traits.construct_vector_3_object()(p, q); }
Point_3 transl(const Point_3& p, const Vector_3 &v)
{ return m_traits.construct_translated_point_3_object()(p, v); }
public:
Octree(Sd_traits const& traits)
: m_traits(traits), m_bucket_size(20), m_set_max_level(10), m_root(NULL) {}
Octree(Sd_traits const& traits,
const Input_iterator &first,
const Input_iterator &beyond,
std::size_t offset = 0,
std::size_t bucketSize = 20,
std::size_t maxLevel = 10)
: PointAccessor(first, beyond, offset),
m_traits(traits),
m_root(NULL),
m_bucket_size(bucketSize),
m_set_max_level(maxLevel) {}
~Octree() {
if (!m_root)
return;
std::stack<Cell *> stack;
stack.push(m_root);
while (!stack.empty()) {
Cell *cell = stack.top();
stack.pop();
for (std::size_t i = 0;i<8;i++)
if (cell->child[i])
stack.push(cell->child[i]);
delete cell;
}
}
// Sorting data in a way such that points of one cell
// are always in one range and ordered child-wise:
// +---+---+
// | 1.| 0.|
// +---+---+
// | 3.| 2.|
// +---+---+
// z max before z min, then y max before y min, then x max before x min
void createTree() {
buildBoundingCube();
std::size_t count = 0;
m_max_level = 0;
std::stack<Cell *> stack;
m_root = new Cell(0, this->size() - 1, m_center, 0);
stack.push(m_root);
while (!stack.empty()) {
Cell *cell= stack.top();
stack.pop();
m_max_level = std::max<std::size_t>(m_max_level, cell->level);
if (cell->level == m_set_max_level)
continue;
std::size_t zLowYHighXSplit, zLowYLowXSplit, zLowYSplit;
std::size_t zHighYSplit, zHighYHighXSplit, zHighYLowXSplit;
std::size_t zSplit = split(cell->first, cell->last, 2, get_z(cell->center));
if (zSplit != size_t_max) {
zLowYSplit = split(cell->first, zSplit, 1, get_y(cell->center));
if (zLowYSplit != size_t_max) {
zLowYLowXSplit = split(cell->first,
zLowYSplit, 0, get_x(cell->center));
zLowYHighXSplit = split(zLowYSplit + 1,
zSplit, 0, get_x(cell->center));
}
else {
zLowYLowXSplit = size_t_max;
zLowYHighXSplit = split(cell->first, zSplit, 0, get_x(cell->center));
}
zHighYSplit = split(zSplit + 1, cell->last, 1, get_y(cell->center));
if (zHighYSplit != size_t_max) {
zHighYHighXSplit = split(zHighYSplit + 1,
cell->last, 0, get_x(cell->center));
zHighYLowXSplit = split(zSplit + 1,
zHighYSplit, 0, get_x(cell->center));
}
else {
zHighYLowXSplit = size_t_max;
zHighYHighXSplit = split(zSplit + 1,
cell->last, 0, get_x(cell->center));
}
}
else {
zLowYSplit = size_t_max;
zLowYLowXSplit = size_t_max;
zLowYHighXSplit = size_t_max;
zHighYSplit = split(cell->first,
cell->last,
1,
get_y(cell->center));
if (zHighYSplit != size_t_max) {
zHighYHighXSplit = split(zHighYSplit + 1,
cell->last,
0,
get_x(cell->center));
zHighYLowXSplit = split(cell->first,
zHighYSplit,
0,
get_x(cell->center));
}
else {
zHighYLowXSplit = size_t_max;
zHighYHighXSplit = split(cell->first,
cell->last,
0,
get_x(cell->center));
}
}
FT width = m_width / (1<<(cell->level + 1));
if (zSplit != size_t_max) {
if (zLowYSplit != size_t_max) {
if (zLowYLowXSplit != size_t_max) {
if (cell->first <= zLowYLowXSplit) {
//---
cell->child[7] = new Cell(cell->first,
zLowYLowXSplit,
transl(cell->center, constr_vec(
ORIGIN, constr_pt(-width,-width,-width))),
cell->level + 1);
if (cell->child[7]->size() > m_bucket_size)
stack.push(cell->child[7]);
}
}
else zLowYLowXSplit = cell->first - 1;
if (zLowYLowXSplit < zLowYSplit || zLowYLowXSplit == size_t_max) {
//+--
cell->child[6] = new Cell(zLowYLowXSplit + 1,
zLowYSplit,
transl(cell->center, constr_vec(
ORIGIN, constr_pt(width,-width,-width))),
cell->level + 1);
if (cell->child[6]->size() > m_bucket_size)
stack.push(cell->child[6]);
}
}
else zLowYSplit = cell->first - 1;
if (zLowYHighXSplit != size_t_max) {
if (zLowYSplit < zLowYHighXSplit || zLowYSplit == size_t_max) {
//-+-
cell->child[5] = new Cell(zLowYSplit + 1,
zLowYHighXSplit,
transl(cell->center, constr_vec(
ORIGIN, constr_pt(-width, width,-width))),
cell->level + 1);
if (cell->child[5]->size() > m_bucket_size)
stack.push(cell->child[5]);
}
}
else zLowYHighXSplit = zLowYSplit;
if (zLowYHighXSplit < zSplit || zLowYHighXSplit == size_t_max) {
//++-
cell->child[4] = new Cell(zLowYHighXSplit + 1,
zSplit,
transl(cell->center, constr_vec(
ORIGIN, constr_pt(width, width,-width))),
cell->level + 1);
if (cell->child[4]->size() > m_bucket_size)
stack.push(cell->child[4]);
}
}
else zSplit = cell->first - 1;
if (zHighYSplit != size_t_max) {
if (zHighYLowXSplit != size_t_max) {
if (zSplit < zHighYLowXSplit || zSplit == size_t_max) {
//--+
cell->child[3] = new Cell(zSplit + 1,
zHighYLowXSplit,
transl(cell->center, constr_vec(
ORIGIN, constr_pt(-width,-width, width))),
cell->level + 1);
if (cell->child[3]->size() > m_bucket_size)
stack.push(cell->child[3]);
}
}
else zHighYLowXSplit = zSplit;
if (zHighYLowXSplit < zHighYSplit || zHighYLowXSplit == size_t_max) {
//+-+
cell->child[2] = new Cell(zHighYLowXSplit + 1,
zHighYSplit,
transl(cell->center, constr_vec(
ORIGIN, constr_pt(width,-width, width))),
cell->level + 1);
if (cell->child[2]->size() > m_bucket_size)
stack.push(cell->child[2]);
}
}
else zHighYSplit = zSplit;
if (zHighYHighXSplit != size_t_max) {
if (zHighYSplit < zHighYHighXSplit || zHighYSplit == size_t_max) {
//-++
cell->child[1] = new Cell(zHighYSplit + 1,
zHighYHighXSplit,
transl(cell->center, constr_vec(
ORIGIN, constr_pt(-width, width, width))),
cell->level + 1);
if (cell->child[1]->size() > m_bucket_size)
stack.push(cell->child[1]);
}
}
else zHighYHighXSplit = zHighYSplit;
if (zHighYHighXSplit <= cell->last || zHighYHighXSplit == size_t_max) {
if (zHighYHighXSplit < cell->last || zHighYHighXSplit == size_t_max) {
//+++
cell->child[0] = new Cell(zHighYHighXSplit + 1,
cell->last,
transl(cell->center, constr_vec(
ORIGIN, constr_pt(width, width, width))),
cell->level + 1);
if (cell->child[0]->size() > m_bucket_size)
stack.push(cell->child[0]);
}
}
std::size_t sum = 0;
for (std::size_t i = 0;i<8;i++)
sum += (cell->child[i]) ? cell->child[i]->size() : 0;
count++;
}
}
bool drawSamplesFromCellContainingPoint(const Point_3 &p,
std::size_t level,
std::set<std::size_t>& indices,
const std::vector<int>& shapeIndex,
std::size_t requiredSamples) {
bool upperZ, upperY, upperX;
Cell *cur = m_root;
while (cur && cur->level < level) {
upperX = get_x(cur->center) <= get_x(p);
upperY = get_y(cur->center) <= get_y(p);
upperZ = get_z(cur->center) <= get_z(p);
if (upperZ) {
if (upperY)
cur = (upperX) ? cur->child[0] : cur->child[1];
else cur = (upperX) ? cur->child[2] : cur->child[3];
}
else {
if (upperY)
cur = (upperX) ? cur->child[4] : cur->child[5];
else cur = (upperX) ? cur->child[6] : cur->child[7];
}
}
if (cur) {
std::size_t enough = 0;
for (std::size_t i = cur->first;i<=cur->last;i++) {
std::size_t j = this->index(i);
if (shapeIndex[j] == -1) {
enough++;
if (enough >= requiredSamples)
break;
}
}
if (enough >= requiredSamples) {
do {
std::size_t p = CGAL::default_random.
uniform_int<std::size_t>(0, cur->size() - 1);
std::size_t j = this->index(cur->first + p);
if (shapeIndex[j] == -1)
indices.insert(j);
} while (indices.size() < requiredSamples);
return true;
}
else return false;
}
else return false;
}
std::size_t maxLevel() {
return m_max_level;
}
std::size_t fullScore(Shape *candidate,
std::vector<int> &shapeIndex,
FT epsilon,
FT normal_threshold) {
std::vector<std::size_t> indices(m_root->size());
for (std::size_t i = 0;i<m_root->size();i++) {
indices[i] = index(m_root->first + i);
}
candidate->cost_function(this->begin() + m_root->first,
this->begin() + m_root->last,
shapeIndex,
epsilon,
normal_threshold,
indices);
return candidate->m_indices.size();
}
std::size_t score(Shape *candidate,
std::vector<int> &shapeIndex,
FT epsilon,
FT normal_threshold) {
std::stack<Cell *> stack;
stack.push(m_root);
while(!stack.empty()) {
Cell *cell = stack.top();
stack.pop();
FT width = m_width / (1<<(cell->level));
FT diag = CGAL::sqrt(FT(3) * width * width) + epsilon;
FT dist = candidate->squared_distance(cell->center);
if (dist > (diag * diag))
continue;
// differ between full or partial overlap?
// if full overlap further traversal of this branch is not necessary
if (cell->isLeaf()) {
std::vector<std::size_t> indices;
indices.reserve(cell->size());
for (std::size_t i = 0;i<cell->size();i++) {
if (shapeIndex[this->index(cell->first + i)] == -1) {
indices.push_back(this->index(cell->first + i));
}
}
candidate->cost_function(epsilon,
normal_threshold,
indices);
}
else {
for (std::size_t i = 0;i<8;i++)
if (cell->child[i])
stack.push(cell->child[i]);
}
}
return candidate->m_indices.size();
}
void setBucketSize(std::size_t bucketSize) {
m_bucket_size = bucketSize;
}
const Bbox_3 &boundingBox() {
return m_bBox;
}
const Bbox_3 &buildBoundingCube() {
FT min[] = {(std::numeric_limits<FT>::max)(),
(std::numeric_limits<FT>::max)(),
(std::numeric_limits<FT>::max)()};
FT max[] = {(std::numeric_limits<FT>::min)(),
(std::numeric_limits<FT>::min)(),
(std::numeric_limits<FT>::min)()};
for (std::size_t i = 0;i<this->size();i++) {
Point_3 p = get(m_point_pmap, *this->at(i));
min[0] = (std::min<FT>)(min[0], get_x(p));
min[1] = (std::min<FT>)(min[1], get_y(p));
min[2] = (std::min<FT>)(min[2], get_z(p));
max[0] = (std::max<FT>)(max[0], get_x(p));
max[1] = (std::max<FT>)(max[1], get_y(p));
max[2] = (std::max<FT>)(max[2], get_z(p));
}
m_bBox = Bbox_3(min[0], min[1], min[2], max[0], max[1], max[2]);
m_width = (std::max)(max[0] - min[0],
(std::max)(max[1] - min[1], max[2] - min[2])) * (FT) 0.5;
m_center = constr_pt((min[0] + max[0]) * (FT) 0.5,
(min[1] + max[1]) * (FT) 0.5,
(min[2] + max[2]) * (FT) 0.5);
return m_bBox;
}
// returns index of last point below threshold
std::size_t split(std::size_t first, std::size_t last, std::size_t dimension, FT threshold) {
if (last == size_t_max || first == size_t_max)
return size_t_max;
if (first > last)
return first - 1;
std::size_t origFirst = first;
while(first < last) {
// find first above threshold
Point_3 p1 = get(m_point_pmap, *this->at(first));
FT v1 = get_coord(p1, static_cast<unsigned int>(dimension));
while (get_coord(
get(m_point_pmap, *this->at(first)),
static_cast<unsigned int>(dimension)) < threshold
&& first < last) {
first++;
}
// check if last has been reached
if (first == last) {
return (get_coord(
get(m_point_pmap, *this->at(first)),
static_cast<unsigned int>(dimension)) < threshold) ?
first : (first == origFirst) ? size_t_max : first - 1;
}
// find last below threshold
p1 = get(m_point_pmap, *this->at(last));
v1 = get_coord(p1, static_cast<unsigned int>(dimension));
while (get_coord(
get(m_point_pmap, *this->at(last)),
static_cast<unsigned int>(dimension)) >= threshold
&& last > first) {
last--;
}
// check if first has been reached
if (last == first) {
return (get_coord(
get(m_point_pmap, *this->at(first)),
static_cast<unsigned int>(dimension)) < threshold) ?
first : (first == origFirst) ? size_t_max : first - 1;
}
this->swap(first, last);
p1 = get(m_point_pmap, *this->at(first));
v1 = get_coord(p1, static_cast<unsigned int>(dimension));
p1 = get(m_point_pmap, *this->at(last));
v1 = get_coord(p1, static_cast<unsigned int>(dimension));
first++;
last--;
}
return (get_coord(
get(m_point_pmap, *this->at(first)),
static_cast<unsigned int>(dimension)) < threshold) ?
first : (first == origFirst) ? size_t_max : first - 1;
}
Sd_traits m_traits;
Bbox_3 m_bBox;
Cell *m_root;
Point_3 m_center;
FT m_width;
std::size_t m_bucket_size;
std::size_t m_set_max_level;
std::size_t m_max_level;
Point_map m_point_pmap;
Normal_map m_normal_pmap;
};
}
}
}
#endif

View File

@ -0,0 +1,211 @@
// Copyright (c) 2015 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) : Sven Oesau, Yannick Verdie, Clément Jamin, Pierre Alliez
//
#ifndef CGAL_SHAPE_DETECTION_3_PLANE_H
#define CGAL_SHAPE_DETECTION_3_PLANE_H
#include <CGAL/Shape_detection_3/Shape_base.h>
#include <CGAL/number_utils.h>
/*!
\file Plane.h
*/
namespace CGAL {
namespace Shape_detection_3 {
/*!
\ingroup PkgPointSetShapeDetection3Shapes
\brief Plane implements Shape_base. The plane is represented by the normal vector and the distance to the origin.
\tparam Traits a model of `EfficientRANSACTraits` with the additional
requirement for planes (see `EfficientRANSACTraits` documentation).
*/
template <class Traits>
class Plane : public Shape_base<Traits> {
public:
/// \cond SKIP_IN_MANUAL
typedef typename Traits::Point_map Point_map;
///< property map to access the location of an input point.
typedef typename Traits::Normal_map Normal_map;
///< property map to access the unoriented normal of an input point.
typedef typename Traits::FT FT; ///< number type.
typedef typename Traits::Point_3 Point_3; ///< point type.
typedef typename Traits::Vector_3 Vector_3;
/// \endcond
typedef typename Traits::Plane_3 Plane_3;///< plane type for conversion operator.
public:
Plane() : Shape_base<Traits>() {}
/*!
Conversion operator to `Plane_3` type.
*/
operator Plane_3() const {
return Plane_3(this->get_x(m_normal), this->get_y(m_normal), this->get_z(m_normal), m_d);
}
/*!
Normal vector of the plane.
*/
Vector_3 plane_normal() const {
return m_normal;
}
/*!
Signed distance from the origin.
*/
FT d() const {
return m_d;
}
/// \cond SKIP_IN_MANUAL
/*!
Computes squared Euclidean distance from query point to the shape.
*/
FT squared_distance(const Point_3 &p) const {
FT d = this->scalar_pdct(
this->constr_vec(p, m_point_on_primitive), m_normal);
return d * d;
}
/*!
Helper function to write the plane equation and
number of assigned points into a string.
*/
std::string info() const {
std::stringstream sstr;
sstr << "Type: plane (" << this->get_x(m_normal) << ", " << this->get_y(m_normal)
<< ", " << this->get_z(m_normal) << ")x - " << m_d << "= 0"
<< " #Pts: " << this->m_indices.size();
return sstr.str();
}
/// \endcond
protected:
/// \cond SKIP_IN_MANUAL
virtual void create_shape(const std::vector<std::size_t> &indices) {
Point_3 p1 = this->point(indices[0]);
Point_3 p2 = this->point(indices[1]);
Point_3 p3 = this->point(indices[2]);
m_normal = this->cross_pdct(
this->constr_vec(p2, p1), this->constr_vec(p3, p1));
FT length = CGAL::sqrt(this->sqlen(m_normal));
// Are the points almost singular?
if (length < (FT)0.0001) {
return;
}
m_normal = this->scale(m_normal, (FT)1.0 / length);
m_d = -(this->get_x(p1) * this->get_x(m_normal)
+ this->get_y(p1) * this->get_y(m_normal)
+ this->get_z(p1) * this->get_z(m_normal));
//check deviation of the 3 normal
Vector_3 l_v;
for (std::size_t i = 0;i<3;i++) {
l_v = this->normal(indices[i]);
if (CGAL::abs(this->scalar_pdct(l_v, m_normal))
< this->m_normal_threshold * CGAL::sqrt(this->sqlen(l_v))) {
this->m_is_valid = false;
return;
}
m_point_on_primitive = p1;
m_base1 = this->cross_pdct(this->constr_vec(p2, p1), m_normal);
m_base1 = this->scale(m_base1, ((FT)1.0 / CGAL::sqrt(this->sqlen(m_base1))));
m_base2 = this->cross_pdct(m_base1, m_normal);
m_base2 = this->scale(m_base2, ((FT)1.0 / CGAL::sqrt(this->sqlen(m_base2))));
}
this->m_is_valid = true;
}
virtual void parameters(const std::vector<std::size_t> &indices,
std::vector<std::pair<FT, FT> > &parameterSpace,
FT &,
FT min[2],
FT max[2]) const {
// Transform first point before to initialize min/max
Vector_3 p = this->constr_vec(
m_point_on_primitive, this->point(indices[0]));
FT u = this->scalar_pdct(p, m_base1);
FT v = this->scalar_pdct(p, m_base2);
parameterSpace[0] = std::pair<FT, FT>(u, v);
min[0] = max[0] = u;
min[1] = max[1] = v;
for (std::size_t i = 1;i<indices.size();i++) {
p = this->constr_vec(m_point_on_primitive, this->point(indices[i]));
u = this->scalar_pdct(p, m_base1);
v = this->scalar_pdct(p, m_base2);
min[0] = (std::min<FT>)(min[0], u);
max[0] = (std::max<FT>)(max[0], u);
min[1] = (std::min<FT>)(min[1], v);
max[1] = (std::max<FT>)(max[1], v);
parameterSpace[i] = std::pair<FT, FT>(u, v);
}
}
virtual void squared_distance(const std::vector<std::size_t> &indices,
std::vector<FT> &dists) const {
for (std::size_t i = 0;i<indices.size();i++) {
const FT d = this->scalar_pdct(
this->constr_vec(m_point_on_primitive, this->point(indices[i])),
m_normal);
dists[i] = d * d;
}
}
virtual void cos_to_normal(const std::vector<std::size_t> &indices,
std::vector<FT> &angles) const {
for (std::size_t i = 0;i<indices.size();i++) {
angles[i] = CGAL::abs(
this->scalar_pdct(this->normal(indices[i]), m_normal));
}
}
FT cos_to_normal(const Point_3 &, const Vector_3 &n) const{
return CGAL::abs(this->scalar_pdct(n, m_normal));
}
virtual std::size_t minimum_sample_size() const {
return 3;
}
virtual bool supports_connected_component() const {
return true;
}
private:
Point_3 m_point_on_primitive;
Vector_3 m_base1, m_base2, m_normal;
FT m_d;
/// \endcond
};
}
}
#endif

View File

@ -0,0 +1,695 @@
// Copyright (c) 2015 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) : Sven Oesau, Yannick Verdie, Clément Jamin, Pierre Alliez
//
#ifndef CGAL_SHAPE_DETECTION_3_SHAPE_BASE_H
#define CGAL_SHAPE_DETECTION_3_SHAPE_BASE_H
#include <vector>
#include <set>
#include <boost/tuple/tuple.hpp>
#include <CGAL/Kd_tree.h>
#include <CGAL/Fuzzy_sphere.h>
#include <CGAL/property_map.h>
#include <CGAL/number_utils.h>
#include <CGAL/Random.h>
/*!
\file Shape_base.h
*/
#ifndef CGAL_M_PI_2
#define CGAL_M_PI_2 1.57079632679489661923
#endif
#ifndef CGAL_M_PI_4
#define CGAL_M_PI_4 0.785398163397448309616
#endif
namespace CGAL {
namespace Shape_detection_3 {
namespace internal {
template<class PointAccessor>
class Octree;
}
/*!
\ingroup PkgPointSetShapeDetection3Shapes
\brief Base class for shape types defining an interface to construct a
shape from a set of points and to compute the point distance and normal
deviation from the surface normal. It is used during detection to
identify the inliers from the input data and to extract the largest
connected component in inlier points.
*/
template <class Traits>
class Shape_base {
/// \cond SKIP_IN_MANUAL
template <class T>
friend class Efficient_RANSAC;
template<class PointAccessor>
friend class internal::Octree;
/// \endcond
public:
/// \cond SKIP_IN_MANUAL
typedef typename Traits::Input_range::iterator Input_iterator;
///< random access iterator for input data.
typedef typename Traits::Point_map Point_map;
///< property map to access the location of an input point.
typedef typename Traits::Normal_map Normal_map;
///< property map to access the unoriented normal of an input point.
typedef Shape_base<Traits> Shape;
///< own type.
/// \endcond
typedef typename Traits::FT FT; ///< number type.
typedef typename Traits::Point_3 Point_3; ///< point type.
typedef typename Traits::Vector_3 Vector_3; ///< vector type.
// \todo The property maps should be passed here instead of `compute`
Shape_base() :
m_is_valid(false),
m_lower_bound((std::numeric_limits<FT>::min)()),
m_upper_bound((std::numeric_limits<FT>::min)()),
m_score(0),
m_sum_expected_value(0),
m_nb_subset_used(0),
m_has_connected_component(false) {
}
virtual ~Shape_base() {}
/*!
returns the indices of the points in the input range assigned to this shape.
*/
const std::vector<std::size_t> & indices_of_assigned_points() const {
return m_indices;
}
/*!
returns a string containing the shape type
and the numerical parameters.
*/
virtual std::string info() const {
return std::string();
}
/*!
Computes the squared Euclidean distance from the query point `p` to the shape.
*/
virtual FT squared_distance(const Point_3 &p) const = 0;
protected:
/*!
Constructs the shape based on a minimal set of samples from the
input data.
*/
virtual void create_shape(const std::vector<std::size_t> &indices) = 0;
/*!
Determines the largest cluster of inlier points. A point belongs to a cluster
if there is a point in the cluster closer than `cluster_epsilon` distance.
*/
std::size_t connected_component(std::vector<std::size_t> &indices, FT cluster_epsilon) {
if (indices.size() == 0)
return 0;
if (m_has_connected_component)
return m_score;
m_has_connected_component = true;
if (!this->supports_connected_component())
return connected_component_kdTree(indices, cluster_epsilon);
// Fetching parameters
FT min[] = {0,0}, max[] = {0,0};
std::vector<std::pair<FT, FT> > parameter_space;
parameter_space.resize(indices.size());
parameters(m_indices, parameter_space, cluster_epsilon, min, max);
// Determine required size of bitmap
std::size_t u_extent = std::size_t(ceil((max[0] - min[0]) / cluster_epsilon));
std::size_t v_extent = std::size_t(ceil((max[1] - min[1]) / cluster_epsilon));
// Handle singular case
u_extent = (u_extent == 0) ? 1 : u_extent;
v_extent = (v_extent == 0) ? 1 : v_extent;
std::vector<unsigned int> bitmap;
bitmap.resize(u_extent * v_extent, 0);
// Fill bitmap
for (std::size_t i = 0;i<parameter_space.size();i++) {
int u = (int)((parameter_space[i].first - min[0]) / cluster_epsilon);
int v = (int)((parameter_space[i].second - min[1]) / cluster_epsilon);
u = (u < 0) ? 0 : (((std::size_t)u >= u_extent) ? (int)u_extent - 1 : u);
v = (v < 0) ? 0 : (((std::size_t)v >= v_extent) ? (int)v_extent - 1 : v);
bitmap[v * int(u_extent) + u] = true;
}
// Iterate through the bitmap
std::vector<unsigned int> map;
map.reserve(64);
map.resize(2);
for (std::size_t y = 0;y<v_extent;y++) {
for (std::size_t x = 0;x<u_extent;x++) {
if (!bitmap[y * u_extent + x])
continue;
unsigned int w = (x > 0) ?
bitmap[y * u_extent + x - 1] : 0;
unsigned int n = (y > 0) ?
bitmap[(y - 1) * u_extent + x] : 0;
unsigned int nw = (x > 0 && y > 0) ?
bitmap[(y - 1) * u_extent + x - 1] : 0;
unsigned int ne = ((x + 1 < u_extent) && y > 0) ?
bitmap[(y - 1) * u_extent + x + 1] : 0;
// Find smallest set label;
unsigned int curLabel = static_cast<unsigned int>(map.size());
curLabel = (w != 0) ?
(std::min<unsigned int>)(curLabel, w) : curLabel;
curLabel = (n != 0) ?
(std::min<unsigned int>)(curLabel, n) : curLabel;
curLabel = (nw != 0) ?
(std::min<unsigned int>)(curLabel, nw) : curLabel;
curLabel = (ne != 0) ?
(std::min<unsigned int>)(curLabel, ne) : curLabel;
// Update merge map.
if (curLabel != map.size()) {
if (w > curLabel) update_label(map, w, curLabel);
if (nw > curLabel) update_label(map, nw, curLabel);
if (n > curLabel) update_label(map, n, curLabel);
if (ne > curLabel) update_label(map, ne, curLabel);
}
else map.push_back(static_cast<unsigned int>(map.size()));
bitmap[y * u_extent + x] = curLabel;
}
}
// post_wrap to handle boundaries in different shape types.
if (map.size() > 3)
post_wrap(bitmap, u_extent, v_extent, map);
// Propagate label changes
for (unsigned int j = 3; j < static_cast<unsigned int>(map.size()); j++)
update_label(map, j, map[j]);
// Update labels
for (std::size_t y = 0;y<v_extent;y++)
for (std::size_t x = 0;x<u_extent;x++) {
unsigned int label = bitmap[y * u_extent + x];
if (!label)
continue;
if (map[label] != label)
bitmap[y * u_extent + x] = map[label];
}
// Count points per label.
std::vector<unsigned int> count(map.size(), 0);
for (std::size_t i = 0;i<parameter_space.size();i++) {
int u = (int)((parameter_space[i].first - min[0]) / cluster_epsilon);
int v = (int)((parameter_space[i].second - min[1]) / cluster_epsilon);
u = (u < 0) ? 0 : (((std::size_t)u >= u_extent) ? (int)u_extent - 1 : u);
v = (v < 0) ? 0 : (((std::size_t)v >= v_extent) ? (int)v_extent - 1 : v);
count[bitmap[v * int(u_extent) + u]]++;
}
// Find largest component. Start at index 2 as 0/1 are reserved for
// basic free/occupied bitmap labels.
unsigned int largest = 2;
for (unsigned int i = 3; i < static_cast<unsigned int>(count.size()); i++)
largest = (count[largest] < count[i]) ? i : largest;
// Extract sought-after indices.
std::vector<std::size_t> comp_indices;
comp_indices.reserve(count[largest]);
for (std::size_t i = 0;i<parameter_space.size();i++) {
int u = (int)((parameter_space[i].first - min[0]) / cluster_epsilon);
int v = (int)((parameter_space[i].second - min[1]) / cluster_epsilon);
u = (u < 0) ? 0 : (((std::size_t)u >= u_extent) ? (int)u_extent - 1 : u);
v = (v < 0) ? 0 : (((std::size_t)v >= v_extent) ? (int)v_extent - 1 : v);
if (bitmap[v * int(u_extent) + u] == largest)
comp_indices.push_back(indices[i]);
}
indices = comp_indices;
return m_score = indices.size();
}
/*!
Determines the largest cluster with a point-to-point
distance not larger than `cluster_epsilon`. This general version performs
a region growing within the inliers using a kd-tree.
*/
std::size_t connected_component_kdTree(std::vector<std::size_t> &indices,
FT cluster_epsilon) {
typedef boost::tuple<Point_3, std::size_t> Point_and_size_t;
typedef CGAL::Search_traits_adapter<Point_and_size_t,
CGAL::Nth_of_tuple_property_map<0, Point_and_size_t>,
typename Traits::Search_traits> Search_traits_adapter;
typedef CGAL::Kd_tree<Search_traits_adapter> Kd_Tree;
typedef CGAL::Fuzzy_sphere<Search_traits_adapter> Fuzzy_sphere;
m_has_connected_component = true;
std::vector<Point_and_size_t> pts;
std::vector<std::size_t> label_map;
pts.resize(indices.size());
label_map.resize(indices.size(), 0);
for (std::size_t i = 0;i < indices.size();i++) {
pts[i] = Point_and_size_t(point(indices[i]), i);
}
// construct kd tree
Kd_Tree tree(pts.begin(), pts.end());
std::stack<std::size_t> stack;
std::size_t unlabeled = pts.size();
std::size_t label = 1;
std::size_t best = 0;
std::size_t best_size = 0;
for (std::size_t i = 0;i<pts.size();i++) {
if (label_map[i] != 0)
continue;
std::size_t assigned = 0;
stack.push(i);
while(!stack.empty()) {
std::vector<Point_and_size_t> near_points;
std::size_t p = stack.top();
stack.pop();
Fuzzy_sphere fs(pts[p], cluster_epsilon, 0);
tree.search(std::back_inserter(near_points), fs);
for (std::size_t j = 0;j<near_points.size();j++) {
std::size_t index = boost::get<1>(near_points[j]);
if (index == p)
continue;
if (label_map[index] != label) {
label_map[index] = label;
assigned++;
stack.push(index);
}
}
}
// Track most prominent label and remaining points
unlabeled -= assigned;
if (assigned > best_size) {
best = label;
best_size = assigned;
}
label++;
// Can we stop already?
if (unlabeled <= best_size)
break;
}
std::vector<std::size_t> tmp_indices;
tmp_indices.reserve(best_size);
for (std::size_t i = 0;i<pts.size();i++) {
if (label_map[i] == best)
tmp_indices.push_back(indices[i]);
}
indices = tmp_indices;
return indices.size();
}
/*!
Computes the squared Euclidean distance from a set of points to the shape.
The distances will be stored in the so called parameter.
*/
virtual void squared_distance(const std::vector<std::size_t> &indices,
std::vector<FT> &distances) const = 0;
/*!
Computes the deviation of the point normal from the surface normal at the
projected point in form of the dot product and writes the result into the
provided `angles` vector.
*/
virtual void cos_to_normal(const std::vector<std::size_t> &indices,
std::vector<FT> &angles) const = 0;
/*!
Returns minimal number of sample points required for construction.
*/
virtual std::size_t minimum_sample_size() const = 0;
/*!
Retrieves the point location from its index.
*/
typename boost::property_traits< typename Traits::Point_map >::reference
point(std::size_t i) const {
return get(this->m_point_pmap, *(this->m_first + i));
}
/*!
Retrieves the normal vector from its index.
*/
typename boost::property_traits< typename Traits::Normal_map >::reference
normal(std::size_t i) const {
return get(this->m_normal_pmap, *(this->m_first + i));
}
/*!
Retrieves the traits class.
*/
const Traits&
traits() const
{
return m_traits;
}
/// \cond SKIP_IN_MANUAL
struct Compare_by_max_bound {
bool operator() (const Shape *a, const Shape *b) {
return a->max_bound() < b->max_bound();
}
};
FT expected_value() const {
return (m_lower_bound + m_upper_bound) / 2.f;
}
FT inline min_bound() const {
return m_lower_bound;
}
FT inline max_bound() const {
return m_upper_bound;
}
virtual void post_wrap(const std::vector<unsigned int> &bitmap,
const std::size_t &u_extent,
const std::size_t &v_extent,
std::vector<unsigned int> &labels) const {
// Avoid compiler warnings about unused parameters.
(void) bitmap;
(void) u_extent;
(void) v_extent;
(void) labels;
}
// return last computed score, or -1 if no score yet
FT inline score() const {
return FT(m_score);
}
int inline subsets() const {
return m_nb_subset_used;
}
// sorting is performed by expected value
operator FT() const {
return expected_value();
}
void inline update_label(std::vector<unsigned int> &labels, unsigned int i,
unsigned int &new_value) const {
if (labels[i] != i)
update_label(labels, labels[i], new_value);
if (new_value < labels[i])
labels[i] = new_value;
else
new_value = labels[i];
}
void update_points(const std::vector<int> &shape_index) {
if (!m_indices.size())
return;
std::size_t start = 0, end = m_indices.size() - 1;
while (start < end) {
while (shape_index[m_indices[start]] == -1
&& start < end) start++;
while (shape_index[m_indices[end]] != -1
&& start < end) end--;
if (shape_index[m_indices[start]] != -1
&& shape_index[m_indices[end]] == -1
&& start < end) {
std::size_t tmp = m_indices[start];
m_indices[start] = m_indices[end];
m_indices[end] = tmp;
}
}
m_indices.resize(end);
m_score = m_indices.size();
}
bool is_valid() const {
return m_is_valid;
}
// Two shapes are considered the same if from 18 points randomly selected
// from the shapes at least 12 match both shapes
bool is_same(const Shape_base *other) const {
if (!other)
return false;
if (other->m_indices.size() == 0)
return true;
const std::size_t num = 9;
int score = 0;
std::vector<std::size_t> indices(num);
for (std::size_t i = 0;i<num;i++)
indices[i] = m_indices[default_random(m_indices.size())];
std::vector<FT> dists(num), angles(num);
other->squared_distance(indices, dists);
other->cos_to_normal(indices, angles);
for (std::size_t i = 0;i<num;i++)
if (dists[i] <= m_epsilon && angles[i] > m_normal_threshold)
score++;
if (score < 3)
return false;
for (std::size_t i = 0;i<num;i++)
indices[i] = other->m_indices[default_random(other->m_indices.size())];
this->squared_distance(indices, dists);
this->cos_to_normal(indices, angles);
for (std::size_t i = 0;i<num;i++)
if (dists[i] <= m_epsilon && angles[i] > m_normal_threshold)
score++;
return (score >= 12);
}
virtual void parameters(const std::vector<std::size_t>& indices,
std::vector<std::pair<FT, FT> >& parameter_space,
FT &cluster_epsilon,
FT min[2],
FT max[2]) const {
// Avoid compiler warnings about unused parameters.
(void)indices;
(void)parameter_space;
(void)cluster_epsilon;
(void)min;
(void)max;
}
void compute(const std::set<std::size_t>& indices,
Input_iterator first,
Traits traits,
Point_map point_pmap,
Normal_map normal_pmap,
FT epsilon,
FT normal_threshold) {
if (indices.size() < minimum_sample_size())
return;
m_first = first;
m_traits = traits;
m_point_pmap = point_pmap;
m_normal_pmap = normal_pmap;
m_epsilon = epsilon;
m_normal_threshold = normal_threshold;
std::vector<std::size_t> output(indices.begin(), indices.end());
create_shape(output);
}
inline bool operator<(const Shape &c) const {
return expected_value() < c.expected_value();
}
std::size_t cost_function(FT epsilon,
FT normal_threshold,
const std::vector<std::size_t> &indices) {
std::vector<FT> dists, angles;
dists.resize(indices.size());
squared_distance(indices, dists);
angles.resize(indices.size());
cos_to_normal(indices, angles);
std::size_t score_before = m_indices.size();
FT eps = epsilon * epsilon;
for (std::size_t i = 0;i<indices.size();i++) {
if (dists[i] <= eps && angles[i] > normal_threshold)
m_indices.push_back(indices[i]);
}
return m_indices.size() - score_before;
}
template<typename T> bool is_finite(T arg) {
return arg == arg &&
arg != std::numeric_limits<T>::infinity() &&
arg != -std::numeric_limits<T>::infinity();
}
void compute_bound(const std::size_t num_evaluated_points,
const std::size_t num_available_points) {
hypergeometrical_dist(-2 - num_evaluated_points,
-2 - num_available_points,
-1 - signed(m_indices.size()),
m_lower_bound, m_upper_bound);
m_lower_bound = -1 - m_lower_bound;
m_upper_bound = -1 - m_upper_bound;
}
void hypergeometrical_dist(const std::ptrdiff_t UN,
const std::ptrdiff_t x,
const std::ptrdiff_t n,
FT &low,
FT &high) {
const FT q = FT(x * n * double(UN - x) * (UN - n) / (UN - 1));
const FT sq = CGAL::sqrt(q);
low = (x * n - sq) / UN;
high = (x * n + sq)/UN;
if (!is_finite<FT>(low) || !is_finite<FT>(high)) {
low = high = 0;
}
}
virtual bool supports_connected_component() const {
return false;
};
// ------------------------------------------------------------------------
// Utilities
// ------------------------------------------------------------------------
FT get_x(const Vector_3& v) const { return m_traits.compute_x_3_object()(v); }
FT get_y(const Vector_3& v) const { return m_traits.compute_y_3_object()(v); }
FT get_z(const Vector_3& v) const { return m_traits.compute_z_3_object()(v); }
FT get_x(const Point_3& p) const { return m_traits.compute_x_3_object()(p); }
FT get_y(const Point_3& p) const { return m_traits.compute_y_3_object()(p); }
FT get_z(const Point_3& p) const { return m_traits.compute_z_3_object()(p); }
Vector_3 constr_vec(const Point_3& p, const Point_3& q) const
{ return m_traits.construct_vector_3_object()(p, q); }
FT sqlen(const Vector_3& v) const
{ return m_traits.compute_squared_length_3_object()(v); }
Vector_3 scale(const Vector_3& v, FT scale) const
{ return m_traits.construct_scaled_vector_3_object()(v, scale); }
Vector_3 sum_vectors(const Vector_3& u, const Vector_3& v) const
{ return m_traits.construct_sum_of_vectors_3_object()(u, v); }
Point_3 transl(const Point_3& p, const Vector_3 &v) const
{ return m_traits.construct_translated_point_3_object()(p, v); }
FT scalar_pdct(const Vector_3& u, const Vector_3& v) const
{ return m_traits.compute_scalar_product_3_object()(u, v); }
Vector_3 cross_pdct(const Vector_3& u, const Vector_3& v) const
{ return m_traits.construct_cross_product_vector_3_object()(u, v); }
protected:
/// \endcond
//
/// \cond SKIP_IN_MANUAL
/*!
Contains indices of the inliers of the candidate, access
to the point and normal data is provided via property maps.
*/
std::vector<std::size_t> m_indices;
FT m_epsilon;
//deviation of normal, used during first check of the 3 normal
FT m_normal_threshold;
bool m_is_valid;
FT m_lower_bound;
FT m_upper_bound;
std::size_t m_score;
FT m_sum_expected_value;
//count the number of subset used so far for the score,
//and thus indicate the next one to use
std::size_t m_nb_subset_used;
bool m_has_connected_component;
Input_iterator m_first;
Traits m_traits;
Point_map m_point_pmap;
Normal_map m_normal_pmap;
/// \endcond
};
}
}
#endif

View File

@ -0,0 +1,562 @@
// Copyright (c) 2015 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) : Sven Oesau, Yannick Verdie, Clément Jamin, Pierre Alliez
//
#ifndef CGAL_SHAPE_DETECTION_3_SPHERE_H
#define CGAL_SHAPE_DETECTION_3_SPHERE_H
#include <CGAL/Shape_detection_3/Shape_base.h>
#include <CGAL/number_utils.h>
#include <cmath>
/*!
\file Sphere.h
*/
namespace CGAL {
namespace Shape_detection_3 {
/*!
\ingroup PkgPointSetShapeDetection3Shapes
\brief Sphere implements Shape_base. The sphere is represented by its center and the radius.
\tparam Traits a model of `EfficientRANSACTraits` with the additional
requirement for spheres (see `EfficientRANSACTraits` documentation).
*/
template <class Traits>
class Sphere : public Shape_base<Traits> {
using Shape_base<Traits>::update_label;
public:
/// \cond SKIP_IN_MANUAL
typedef typename Traits::Point_map Point_map;
///< property map to access the location of an input point.
typedef typename Traits::Normal_map Normal_map;
///< property map to access the unoriented normal of an input point.
typedef typename Traits::Vector_3 Vector_3;
///< vector type.
typedef typename Traits::Sphere_3 Sphere_3;
///< sphere type.
typedef typename Traits::FT FT;
///< number type.
typedef typename Traits::Point_3 Point_3;
///< point type.
/// \endcond
public:
Sphere() : Shape_base<Traits>() {}
/*!
Conversion operator to convert to `Sphere_3` type.
*/
operator Sphere_3() const {
return m_sphere;
}
/*!
Access to the center.
*/
Point_3 center() const {
return this->sph_center(m_sphere);
}
/*!
Access to the radius of the sphere.
*/
FT radius() const {
return CGAL::sqrt(this->sqradius(m_sphere));
}
/// \cond SKIP_IN_MANUAL
/*!
Computes the squared Euclidean distance from query point to the shape.
*/
FT squared_distance(const Point_3 &p) const {
const FT d = CGAL::sqrt(
this->sqlen(this->constr_vec(
p, this->sph_center(m_sphere))))
- CGAL::sqrt(this->sqradius(m_sphere));
return d * d;
}
/*!
Helper function to write center,
radius of the sphere and number of assigned points into a string.
*/
std::string info() const {
std::stringstream sstr;
Point_3 c = this->sph_center(m_sphere);
FT r = CGAL::sqrt(this->sqradius(m_sphere));
sstr << "Type: sphere center: (" << this->get_x(c) << ", " << this->get_y(c);
sstr << ", " << this->get_z(c) << ") radius:" << r;
sstr << " #Pts: " << this->m_indices.size();
return sstr.str();
}
/// \endcond
protected:
/// \cond SKIP_IN_MANUAL
// ------------------------------------------------------------------------
// Utilities
// ------------------------------------------------------------------------
Sphere_3 constr_sphere(const Point_3& c, FT r) const
{ return this->m_traits.construct_sphere_3_object()(c, r); }
Point_3 sph_center(const Sphere_3& s) const
{ return this->m_traits.construct_center_3_object()(s); }
FT sqradius(const Sphere_3& s) const
{ return this->m_traits.compute_squared_radius_3_object()(s); }
void create_shape(const std::vector<std::size_t> &indices) {
Point_3 p1 = this->point(indices[0]);
Point_3 p2 = this->point(indices[1]);
Point_3 p3 = this->point(indices[2]);
Vector_3 n1 = this->normal(indices[0]);
Vector_3 n2 = this->normal(indices[1]);
Vector_3 n3 = this->normal(indices[2]);
// Determine center: select midpoint of shortest line segment
// between p1 and p2. Implemented from "3D game engine design" by Eberly 2001
Vector_3 diff = this->constr_vec(p2, p1);
FT a = this->scalar_pdct(n1, n1);
FT b = -this->scalar_pdct(n1, n2);
FT c = this->scalar_pdct(n2, n2);
FT d = this->scalar_pdct(n1, diff);
FT det = CGAL::abs(a * c - b * b);
// degenerated when nearly parallel
if (det < (FT)0.00001) {
this->m_is_valid = false;
return;
}
FT e = -this->scalar_pdct(n2, diff);
FT invDet = (FT) 1.0 / det;
FT s = (b * e - c * d) * invDet;
FT t = (d * b - a * e) * invDet;
Vector_3 v_transl = this->sum_vectors(
this->constr_vec(CGAL::ORIGIN, this->transl(p1, this->scale(n1, s))),
this->constr_vec(CGAL::ORIGIN, this->transl(p2, this->scale(n2, t))));
Point_3 center = this->transl(
CGAL::ORIGIN, this->scale(v_transl, (FT)0.5));
Vector_3 v1 = (this->constr_vec(center, p1));
Vector_3 v2 = (this->constr_vec(center, p2));
FT d1 = CGAL::sqrt(this->sqlen(v1));
FT d2 = CGAL::sqrt(this->sqlen(v2));
if (CGAL::abs(d1 - d2) > (FT)2.0 * this->m_epsilon) {
this->m_is_valid = false;
return;
}
v1 = this->scale(v1, (FT)1.0 / d1);
v2 = this->scale(v2, (FT)1.0 / d2);
if (this->scalar_pdct(n1, v1) < this->m_normal_threshold ||
this->scalar_pdct(n2, v2) < this->m_normal_threshold) {
this->m_is_valid = false;
return;
}
Vector_3 v3 = this->constr_vec(center, p3);
FT d3 = CGAL::sqrt(this->sqlen(v3));
v3 = this->scale(v3, (FT)1.0 / d3);
FT radius = (d1 + d2) * (FT)0.5;
if (CGAL::abs(d3 - radius) > this->m_epsilon ||
this->scalar_pdct(n3, v3) < this->m_normal_threshold) {
this->m_is_valid = false;
return;
}
this->m_is_valid = true;
m_sphere = this->constr_sphere(center, radius * radius);
}
virtual void squared_distance(const std::vector<std::size_t> &indices,
std::vector<FT> &dists) const {
FT radius = CGAL::sqrt(this->sqradius(m_sphere));
for (std::size_t i = 0;i<indices.size();i++) {
dists[i] = CGAL::sqrt(this->sqlen(this->constr_vec(
this->sph_center(m_sphere), this->point(indices[i]))))
- radius;
dists[i] = dists[i] * dists[i];
}
}
virtual void cos_to_normal(const std::vector<std::size_t> &indices,
std::vector<FT> &angles) const {
for (std::size_t i = 0;i<indices.size();i++) {
Vector_3 n = this->constr_vec(
this->point(indices[i]),
this->sph_center(m_sphere));
FT length = CGAL::sqrt(this->sqlen(n));
if (length == 0) {
angles[i] = (FT)1.0;
continue;
}
n = this->scale(n, (FT)1.0 / length);
angles[i] = CGAL::abs(this->scalar_pdct(this->normal(indices[i]), n));
}
}
virtual FT cos_to_normal(const Point_3 &p, const Vector_3 &n) const {
Vector_3 sphere_normal = this->constr_vec(p, this->sph_center(m_sphere));
FT length = (FT)(CGAL::sqrt(this->sqlen(n)));
if (length == 0)
return 1;
sphere_normal = this->scale(sphere_normal, (FT)1.0 / length);
return CGAL::abs(this->scalar_pdct(sphere_normal, n));
}
virtual std::size_t minimum_sample_size() const {
return 3;
}
// Maps to the range [-1,1]^2
static void concentric_mapping(FT phi, FT proj, FT rad, FT &x, FT &y) {
phi = (phi < FT(-CGAL_M_PI_4)) ? phi + FT(2 * CGAL_PI) : phi;
proj = (proj < FT(-1.0)) ? FT(-1.0) : ((proj > FT(1.0)) ? FT(1.0) : proj);
FT r = FT(acos(double(CGAL::abs(proj)))) / FT(CGAL_M_PI_2);
FT a = 0, b = 0;
if (phi < FT(CGAL_M_PI_4)) {
a = r;
b = phi * r / FT(CGAL_M_PI_4);
}
else if (phi < FT(3.0 * CGAL_M_PI_4)) {
a = -FT(phi - CGAL_M_PI_2) * r / FT(CGAL_M_PI_4);
b = r;
}
else if (phi < FT(5.0 * CGAL_M_PI_4)) {
a = -r;
b = (phi - FT(CGAL_PI)) * (-r) / FT(CGAL_M_PI_4);
}
else {
a = (phi - 3 * FT(CGAL_M_PI_2)) * r / FT(CGAL_M_PI_4);
b = -r;
}
x = a;
y = b;
// Map into hemisphere
if (proj >= 0)
y += 1;
else
y = -1 - y;
// Scale to surface distance
x = FT(x * CGAL_M_PI_2 * rad);
y = FT(y * CGAL_M_PI_2 * rad);
}
virtual void parameters(const std::vector<std::size_t> &indices,
std::vector<std::pair<FT, FT> > &parameterSpace,
FT &cluster_epsilon,
FT min[2],
FT max[2]) const {
Vector_3 axis;
FT rad = radius();
// Take average normal as axis
for (std::size_t i = 0;i<indices.size();i++)
axis = this->sum_vectors(axis, this->normal(indices[i]));
axis = this->scale(axis, FT(1) / CGAL::sqrt(this->sqlen(axis)));
// create basis d1, d2
Vector_3 d1 = Vector_3((FT) 0, (FT) 0, (FT) 1);
Vector_3 d2 = this->cross_pdct(axis, d1);
FT l = this->sqlen(d2);
if (l < (FT)0.0001) {
d1 = Vector_3((FT) 1, (FT) 0, (FT) 0);
d2 = this->cross_pdct(axis, d1);
l = this->sqlen(d2);
}
d2 = this->scale(d2, FT(1) / CGAL::sqrt(l));
d1 = this->cross_pdct(axis, d2);
l = CGAL::sqrt(this->sqlen(d1));
if (l == 0)
return;
d1 = this->scale(d1, (FT)1.0 / l);
// Process first point separately to initialize min/max
Vector_3 vec = this->constr_vec(
this->sph_center(m_sphere), this->point(indices[0]));
// sign indicates northern or southern hemisphere
FT proj = (this->scalar_pdct(axis, vec)) / rad;
FT phi = atan2(this->scalar_pdct(vec, d2), this->scalar_pdct(vec, d1));
FT x = FT(0), y = FT(0);
concentric_mapping(phi, proj, rad, x, y);
min[0] = max[0] = x;
min[1] = max[1] = y;
parameterSpace[0] = std::pair<FT, FT>(x, y);
for (std::size_t i = 1;i<indices.size();i++) {
Vector_3 vec = this->constr_vec(
this->sph_center(m_sphere), this->point(indices[i]));
// sign indicates northern or southern hemisphere
proj = (this->scalar_pdct(axis, vec)) / rad;
phi = atan2(this->scalar_pdct(vec, d2), this->scalar_pdct(vec, d1));
concentric_mapping(phi, proj, rad, x, y);
min[0] = (std::min<FT>)(min[0], x);
max[0] = (std::max<FT>)(max[0], x);
min[1] = (std::min<FT>)(min[1], y);
max[1] = (std::max<FT>)(max[1], y);
parameterSpace[i] = std::pair<FT, FT>(x, y);
}
// Is close to wrapping around? Check all three directions separately
m_wrap_right = abs(max[0] - CGAL_M_PI_2 * rad) < (cluster_epsilon * 0.5);
m_wrap_left = abs(min[0] + CGAL_M_PI_2 * rad) < (cluster_epsilon * 0.5);
FT diff_top = CGAL::abs(-FT(CGAL_PI * rad) - min[1])
+ FT(CGAL_PI * rad) - max[1];
m_wrap_top = diff_top < cluster_epsilon;
if (m_wrap_top || m_wrap_left || m_wrap_right) {
FT fl = FT(floor((CGAL_PI * rad) / cluster_epsilon));
if (fl > 0.9) {
FT adjusted_cf = FT(CGAL_PI * rad) / fl;
if ( (adjusted_cf < (2 * cluster_epsilon)))
cluster_epsilon = adjusted_cf;
}
// center bitmap at equator
FT required_space = ceil(
(std::max<FT>)(CGAL::abs(min[1]), max[1]) / cluster_epsilon)
* cluster_epsilon;
min[1] = -required_space;
max[1] = required_space;
}
m_equator = std::size_t((abs(min[1])) / cluster_epsilon - 0.5);
}
virtual void post_wrap(const std::vector<unsigned int> &bitmap,
const std::size_t &u_extent,
const std::size_t &v_extent,
std::vector<unsigned int> &labels) const {
unsigned int l;
unsigned int nw, n, ne;
if (m_wrap_top && v_extent > 2) {
// Handle first index separately.
l = bitmap[0];
if (l) {
n = bitmap[(v_extent - 1) * u_extent];
if (u_extent == 1) {
if (n && l != n) {
l = (std::min<unsigned int>)(n, l);
update_label(labels, (std::max<unsigned int>)(n, l), l);
return;
}
}
ne = bitmap[(v_extent - 1) * u_extent + 1];
if (n && n != l) {
l = (std::min<unsigned int>)(n, l);
update_label(labels, (std::max<unsigned int>)(n, l), l);
}
else if (ne && ne != l) {
l = (std::min<unsigned int>)(ne, l);
update_label(labels, (std::max<unsigned int>)(ne, l), l);
}
}
for (std::size_t i = 1;i<u_extent - 1;i++) {
l = bitmap[i];
if (!l)
continue;
nw = bitmap[(v_extent - 1) * u_extent + i - 1];
n = bitmap[(v_extent - 1) * u_extent + i];
ne = bitmap[(v_extent - 1) * u_extent + i + 1];
if (nw && nw != l) {
l = (std::min<unsigned int>)(nw, l);
update_label(labels, (std::max<unsigned int>)(nw, l), l);
}
if (n && n != l) {
l = (std::min<unsigned int>)(n, l);
update_label(labels, (std::max<unsigned int>)(n, l), l);
}
else if (ne && ne != l) {
l = (std::min<unsigned int>)(ne, l);
update_label(labels, (std::max<unsigned int>)(ne, l), l);
}
}
// Handle last index separately
l = bitmap[u_extent - 1];
if (l) {
n = bitmap[u_extent * v_extent - 1];
nw = bitmap[u_extent * v_extent - 2];
if (n && n != l) {
l = (std::min<unsigned int>)(n, l);
update_label(labels, (std::max<unsigned int>)(n, l), l);
}
else if (nw && nw != l) {
l = (std::min<unsigned int>)(nw, l);
update_label(labels, (std::max<unsigned int>)(nw, l), l);
}
}
}
// Walk upwards on the right side in the northern hemisphere
if (m_wrap_right && v_extent > 2) {
// First index
l = bitmap[(m_equator + 1) * u_extent - 1];
unsigned int ws = bitmap[(m_equator + 3) * u_extent - 1];
if (l && ws && l != ws) {
l = (std::min<unsigned int>)(ws, l);
update_label(labels, (std::max<unsigned int>)(ws, l), l);
}
for (std::size_t i = 1;i<(v_extent>>1) - 1;i++) {
l = bitmap[(m_equator - i + 1) * u_extent - 1];
if (!l)
continue;
unsigned int wn = bitmap[(m_equator + i) * u_extent - 1];
unsigned int w = bitmap[(m_equator + i + 1) * u_extent - 1];
ws = bitmap[(m_equator + i + 2) * u_extent - 1];
if (wn && wn != l) {
l = (std::min<unsigned int>)(wn, l);
update_label(labels, (std::max<unsigned int>)(wn, l), l);
}
if (w && w != l) {
l = (std::min<unsigned int>)(w, l);
update_label(labels, (std::max<unsigned int>)(w, l), l);
}
else if (ws && ws != l) {
l = (std::min<unsigned int>)(ws, l);
update_label(labels, (std::max<unsigned int>)(ws, l), l);
}
}
// Last index
l = bitmap[u_extent - 1];
if (l) {
unsigned int w = bitmap[u_extent * v_extent - 1];
unsigned int wn = bitmap[(v_extent - 1) * u_extent - 1];
if (w && w != l) {
l = (std::min<unsigned int>)(w, l);
update_label(labels, (std::max<unsigned int>)(w, l), l);
}
else if (wn && wn != l) {
l = (std::min<unsigned int>)(wn, l);
update_label(labels, (std::max<unsigned int>)(wn, l), l);
}
}
}
if (m_wrap_left && v_extent > 2) {
// First index
l = bitmap[(m_equator) * u_extent];
unsigned int es = bitmap[(m_equator + 2) * u_extent];
if (l && l != es) {
l = (std::min<unsigned int>)(es, l);
update_label(labels, (std::max<unsigned int>)(es, l), l);
}
for (std::size_t i = 1;i<(v_extent>>1) - 1;i++) {
l = bitmap[(m_equator - i) * u_extent];
if (!l)
continue;
unsigned int en = bitmap[(m_equator + i) * u_extent];
unsigned int e = bitmap[(m_equator + i + 1) * u_extent];
es = bitmap[(m_equator + i + 2) * u_extent];
if (en && en != l) {
l = (std::min<unsigned int>)(en, l);
update_label(labels, (std::max<unsigned int>)(en, l), l);
}
if (e && e != l) {
l = (std::min<unsigned int>)(e, l);
update_label(labels, (std::max<unsigned int>)(e, l), l);
}
else if (es && es != l) {
l = (std::min<unsigned int>)(es, l);
update_label(labels, (std::max<unsigned int>)(es, l), l);
}
}
// Last index
l = bitmap[0];
if (l) {
unsigned int w = bitmap[(v_extent - 1) * u_extent];
unsigned int wn = bitmap[(v_extent - 2) * u_extent];
if (w && w != l) {
l = (std::min<unsigned int>)(w, l);
update_label(labels, (std::max<unsigned int>)(w, l), l);
}
else if (wn && wn != l) {
l = (std::min<unsigned int>)(wn, l);
update_label(labels, (std::max<unsigned int>)(wn, l), l);
}
}
}
}
virtual bool supports_connected_component() const {
return true;
}
private:
Sphere_3 m_sphere;
mutable bool m_wrap_right, m_wrap_top, m_wrap_left;
mutable std::size_t m_equator;
/// \endcond
};
}
}
#endif

View File

@ -0,0 +1,407 @@
// Copyright (c) 2015 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) : Sven Oesau, Yannick Verdie, Clément Jamin, Pierre Alliez
//
#ifndef CGAL_SHAPE_DETECTION_3_TORUS_H
#define CGAL_SHAPE_DETECTION_3_TORUS_H
#include <CGAL/Shape_detection_3/Shape_base.h>
#include <CGAL/Circle_2.h>
#include <CGAL/number_utils.h>
#include <cmath>
/*!
\file Torus.h
*/
namespace CGAL {
namespace Shape_detection_3 {
/*!
\ingroup PkgPointSetShapeDetection3Shapes
\brief Torus implements Shape_base. The torus is represented by the
symmetry axis, its center on the axis and the major and minor radii.
\tparam Traits a model of `EfficientRANSACTraits` with the additional
requirement for tori (see `EfficientRANSACTraits` documentation).
*/
template <class Traits>
class Torus : public Shape_base<Traits> {
public:
/// \cond SKIP_IN_MANUAL
typedef typename Traits::Point_map Point_map;
///< property map to access the location of an input point.
typedef typename Traits::Normal_map Normal_map;
///< property map to access the unoriented normal of an input point.
typedef typename Traits::FT FT;
///< number type.
typedef typename Traits::Point_3 Point_3;
///< point type.
typedef typename Traits::Vector_3 Vector_3;
///< vector type.
typedef typename Traits::Vector_2 Vector_2;
///< 2D vector type.
typedef typename Traits::Point_2 Point_2;
///< 2D point type used during construction.
typedef typename Traits::Circle_2 Circle_2;
///< cricle type used during construction.
/// \endcond
Torus() : Shape_base<Traits>() {}
/*!
Direction of symmetry axis.
*/
Vector_3 axis() const {
return m_axis;
}
/*!
Center point on symmetry axis.
*/
Point_3 center() const {
return m_center;
}
/*!
Major radius of the torus.
*/
FT major_radius() const {
return m_majorRad;
}
/*!
Minor radius of the torus.
*/
FT minor_radius() const {
return m_minorRad;
}
/// \cond SKIP_IN_MANUAL
/*!
Helper function to write center point, symmetry axis
and the two radii into a string.
*/
std::string info() const {
std::stringstream sstr;
sstr << "Type: torus center(" << this->get_x(m_center) << ", " << this->get_y(m_center);
sstr << ", " << this->get_z(m_center) << ") axis(" << this->get_x(m_axis) << ", ";
sstr << this->get_y(m_axis) << ", " << this->get_z(m_axis) << ") major radius = ";
sstr << m_majorRad << " minor radius = " << m_minorRad << " #Pts: ";
sstr << this->m_indices.size();
return sstr.str();
}
/*!
Computes squared Euclidean distance from query point to the shape.
*/
FT squared_distance(const Point_3 &p) const {
const Vector_3 d = this->constr_vec(m_center, p);
// height over symmetry plane
const FT height = this->scalar_pdct(d, m_axis);
// distance from axis in plane
const FT l = CGAL::sqrt(this->scalar_pdct(d, d) - height * height);
// inPlane distance from circle
const FT l2 = m_majorRad - l;
// distance from torus
const FT squared_dist = CGAL::sqrt(height * height + l2 * l2) - m_minorRad;
return squared_dist * squared_dist;
}
/// \endcond
protected:
/// \cond SKIP_IN_MANUAL
// ------------------------------------------------------------------------
// Utilities
// ------------------------------------------------------------------------
FT get_x_2(const Vector_2& v) const { return this->m_traits.compute_x_2_object()(v); }
FT get_y_2(const Vector_2& v) const { return this->m_traits.compute_y_2_object()(v); }
FT get_x_2(const Point_2& p) const { return this->m_traits.compute_x_2_object()(p); }
FT get_y_2(const Point_2& p) const { return this->m_traits.compute_y_2_object()(p); }
Circle_2 constr_circle(const Point_2& a, const Point_2& b,const Point_2& c) const
{ return this->m_traits.construct_circle_2_object()(a, b, c); }
Point_2 circle_center(const Circle_2& s) const
{ return this->m_traits.construct_center_2_object()(s); }
FT sqradius(const Circle_2& s) const
{ return this->m_traits.compute_squared_radius_2_object()(s); }
Point_2 constr_point_2(FT a, FT b) const
{ return this->m_traits.construct_point_2_object()(a, b); }
Vector_2 constr_vec_2(const Point_2 &a, const Point_2 &b) const
{ return this->m_traits.construct_vector_2_object()(a, b); }
FT sqlen_2(const Vector_2& v) const
{ return this->m_traits.compute_squared_length_2_object()(v); }
bool collinear_2(const Point_2& p, const Point_2& q, const Point_2& r) const
{ return this->m_traits.collinear_2_object()(p, q, r); }
void create_shape(const std::vector<std::size_t> &indices) {
std::vector<Point_3> p;
std::vector<Vector_3> n;
p.resize(indices.size());
n.resize(indices.size());
for (std::size_t i = 0;i<indices.size();i++) {
p[i] = this->point(indices[i]);
n[i] = this->normal(indices[i]);
}
// Implemented method from 'Geometric least-squares fitting of spheres, cylinders, cones and tori' by G. Lukacs,A.D. Marshall, R. R. Martin
const FT a01 = this->scalar_pdct(this->cross_pdct(n[0], n[1]), n[2]);
const FT b01 = this->scalar_pdct(this->cross_pdct(n[0], n[1]), n[3]);
const FT a0 = this->scalar_pdct(this->cross_pdct(this->constr_vec(p[1], p[2]), n[0]), n[2]);
const FT b0 = this->scalar_pdct(this->cross_pdct(this->constr_vec(p[1], p[3]), n[0]), n[3]);
const FT a1 = this->scalar_pdct(this->cross_pdct(this->constr_vec(p[2], p[0]), n[1]), n[2]);
const FT b1 = this->scalar_pdct(this->cross_pdct(this->constr_vec(p[3], p[0]), n[1]), n[3]);
const FT a = this->scalar_pdct(this->cross_pdct(this->constr_vec(p[2], p[0]), this->constr_vec(p[0], p[1])), n[2]);
const FT b = this->scalar_pdct(this->cross_pdct(this->constr_vec(p[3], p[0]), this->constr_vec(p[0], p[1])), n[3]);
FT div = (b1 * a01 - b01 * a1);
if (div == 0)
return;
div = (FT)1.0 / div;
const FT r = ((a01 * b + b1 * a0 - b0 * a1 - b01 * a)) * div * (FT)0.5;
const FT q = (b * a0 - b0 * a) * div;
FT root = r * r - q;
if (r * r - q < 0)
root = 0;
const FT y1 = -r - CGAL::sqrt(root);
const FT y2 = -r + CGAL::sqrt(root);
FT x1 = (a01 * y1 + a0);
FT x2 = (a01 * y2 + a0);
if (x1 == 0 || x2 == 0)
return;
x1 = -(a1 * y1 + a) / x1;
x2 = -(a1 * y2 + a) / x2;
// 1. center + axis
FT majorRad1 = FLT_MAX, minorRad1 = FLT_MAX, dist1 = FLT_MAX;
Point_3 c1;
Vector_3 axis1;
if (is_finite(x1) && is_finite(y1)) {
c1 = this->transl(p[0], this->scale(n[0], x1));
axis1 = this->constr_vec(this->transl(p[1], this->scale(n[1], y1)), c1);
FT l = this->sqlen(axis1);
if (l > (FT)0.00001 && l == l) {
axis1 = this->scale(axis1, FT(1.0) / CGAL::sqrt(l));
dist1 = getCircle(c1, axis1, p, majorRad1, minorRad1);
}
}
// 2. center + axis
FT majorRad2 = 0, minorRad2 = 0, dist2 = FLT_MAX;
Point_3 c2;
Vector_3 axis2;
if (is_finite(x2) && is_finite(y2)) {
c2 = this->transl(p[0], this->scale(n[0], x2));
axis2 = this->constr_vec(this->transl(p[1], this->scale(n[1], y2)), c2);
FT l = this->sqlen(axis2);
if (l > (FT)0.00001 && l == l) {
axis2 = this->scale(axis2, FT(1.0) / CGAL::sqrt(l));
dist2 = getCircle(c2, axis2, p, majorRad2, minorRad2);
}
}
if (dist1 < dist2) {
m_center = c1;
m_axis = axis1;
m_majorRad = majorRad1;
m_minorRad = CGAL::sqrt(minorRad1);
}
else {
m_center = c2;
m_axis = axis2;
m_majorRad = majorRad2;
m_minorRad = CGAL::sqrt(minorRad2);
}
// Drop if shape is probably sphere
if (m_majorRad < this->m_epsilon) {
this->m_is_valid = false;
return;
}
//validate points and normals
for (std::size_t i = 0;i<indices.size();i++) {
// check distance
if (squared_distance(p[i]) > this->m_epsilon) {
this->m_is_valid = false;
return;
}
// check normal deviation
Vector_3 d = this->constr_vec(m_center, p[i]);
Vector_3 in_plane = this->cross_pdct(m_axis,
this->cross_pdct(m_axis, d));
if (this->scalar_pdct(in_plane, d) < 0)
in_plane = this->scale(in_plane, FT(-1.0));
FT length = CGAL::sqrt(this->sqlen(in_plane));
if (length == 0)
return;
in_plane = this->scale(in_plane, FT(1.0) / length);
d = this->constr_vec((this->transl(m_center, this->scale(in_plane, m_majorRad))), p[i]);
length = CGAL::sqrt(this->sqlen(d));
if (length == 0)
return;
d = this->scale(d, FT(1.0) / length);
if (CGAL::abs(this->scalar_pdct(d, n[i])) < this->m_normal_threshold) {
this->m_is_valid = false;
return;
}
}
this->m_is_valid = true;
}
virtual void squared_distance(const std::vector<std::size_t> &indices,
std::vector<FT> &dists) const {
for (std::size_t i = 0;i<indices.size();i++) {
Point_3 po = this->point(indices[i]);
Vector_3 d = this->constr_vec(m_center, po);
// height over symmetry plane
const FT p = this->scalar_pdct(d, m_axis);
// distance from axis in plane
FT l = CGAL::sqrt(this->scalar_pdct(d, d) - p * p);
// inPlane distance from circle
const FT l2 = m_majorRad - l;
// distance from torus
l = CGAL::sqrt(p * p + l2 * l2) - m_minorRad;
dists[i] = l * l;
}
}
virtual void cos_to_normal(const std::vector<std::size_t> &indices,
std::vector<FT> &angles) const {
for (std::size_t i = 0;i<indices.size();i++) {
Vector_3 d = this->constr_vec(m_center, this->point(indices[i]));
Vector_3 in_plane = this->cross_pdct(m_axis,
this->cross_pdct(m_axis, d));
if (this->scalar_pdct(in_plane, d) < 0)
in_plane = this->scale(in_plane, FT(-1.0));
FT length = (FT) CGAL::sqrt(this->sqlen(in_plane));
// If length is 0 the point is on the axis, maybe in the apex. We
// accept any normal for that position.
if (length == 0) {
angles[i] = (FT)1.0;
continue;
}
in_plane = this->scale(in_plane,FT(1.0) / CGAL::sqrt(this->sqlen(in_plane)));
d = this->constr_vec((this->transl(m_center, this->scale(in_plane, m_majorRad))), this->point(indices[i]));
d = this->scale(d, FT(1.0) / CGAL::sqrt(this->sqlen(d)));
angles[i] = CGAL::abs(this->scalar_pdct(d, this->normal(indices[i])));
}
}
FT cos_to_normal(const Point_3 &p, const Vector_3 &n) const {
Vector_3 d = this->constr_vec(m_center, p);
Vector_3 in_plane = this->cross_pdct(m_axis,
this->cross_pdct(m_axis, d));
if (this->scalar_pdct(in_plane, d) < 0)
in_plane = -in_plane;
float length = CGAL::sqrt(this->sqlen(in_plane));
// If length is 0 the point is on the axis, maybe in the apex. We
// accept any normal for that position.
if (length == 0) {
return (FT)1.0;
}
in_plane = in_plane / CGAL::sqrt(this->sqlen(in_plane));
d = p - (m_center + this->scalar_pdct(in_plane, m_majorRad));
d = d / CGAL::sqrt(this->sqlen(d));
return CGAL::abs(d * n);
}
virtual std::size_t minimum_sample_size() const {
return 4;
}
virtual bool supports_connected_component() const {
return false;
}
private:
FT getCircle(Point_3 &center, const Vector_3 &axis, std::vector<Point_3> p, FT &majorRad, FT &minorRad) const {
// create spin image
std::vector<Point_2> pts;
pts.resize(p.size());
for (unsigned int i = 0;i<p.size();i++) {
Vector_3 d = this->constr_vec(center, p[i]);
FT e = this->scalar_pdct(d, axis);
FT f = this->scalar_pdct(d, d) - e * e;
if (f <= 0)
pts[i] = this->constr_point_2(e, (FT) 0);
else
pts[i] = this->constr_point_2(e, CGAL::sqrt(this->scalar_pdct(d, d) - e * e));
}
if (this->collinear_2(pts[0], pts[1], pts[2])) {
return (std::numeric_limits<FT>::max)();
}
Circle_2 c = this->constr_circle(pts[0], pts[1], pts[2]);
minorRad = this->sqradius(c);
majorRad = this->get_y_2(this->circle_center(c));
center = this->transl(center, this->scale(axis, this->get_x_2(this->circle_center(c))));
return CGAL::abs(
this->sqlen_2(this->constr_vec_2(this->circle_center(c), pts[3])) - this->sqradius(c));
}
Point_3 m_center;
Vector_3 m_axis;
FT m_majorRad;
FT m_minorRad;
/// \endcond
};
}
}
#endif

View File

@ -0,0 +1,2 @@
INRIA Sophia-Antipolis (France)

View File

@ -0,0 +1,3 @@
Point Set Shape Detection
This CGAL package implements an efficient RANSAC approach for detecting shapes in an unorganized point set with unoriented normals.

View File

@ -0,0 +1 @@
GPL (v3 or later)

View File

@ -0,0 +1,7 @@
Point Set Shape Detection
Sven Oesau, Yannick Verdie, Clément Jamin and Pierre Alliez
This CGAL package implements an efficient RANSAC (random sample consensus) approach for detecting shapes in a point set with unoriented normals.
Five canonical shapes can be detected: planes, spheres, cylinders, cones and tori.
Additional shapes can be detected, provided a class derived from the base shape is implemented.

View File

@ -0,0 +1,2 @@
Clément Jamin <clement.jamin.pro@gmail.com>
Sven Oesau <sven.oesau@inria.fr>

View File

@ -0,0 +1,43 @@
# Created by the script cgal_create_cmake_script
# This is the CMake script for compiling a CGAL application.
project( Point_set_shape_detection_3_test )
cmake_minimum_required(VERSION 2.6.2)
if("${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION}" VERSION_GREATER 2.6)
if("${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION}.${CMAKE_PATCH_VERSION}" VERSION_GREATER 2.8.3)
cmake_policy(VERSION 2.8.4)
else()
cmake_policy(VERSION 2.6)
endif()
endif()
find_package(CGAL QUIET COMPONENTS Core )
if ( CGAL_FOUND )
include( ${CGAL_USE_FILE} )
include( CGAL_CreateSingleSourceCGALProgram )
include_directories (BEFORE "../../include")
create_single_source_cgal_program( "test_cone_connected_component.cpp" )
create_single_source_cgal_program( "test_cone_parameters.cpp" )
create_single_source_cgal_program( "test_cylinder_connected_component.cpp" )
create_single_source_cgal_program( "test_cylinder_parameters.cpp" )
create_single_source_cgal_program( "test_plane_connected_component.cpp" )
create_single_source_cgal_program( "test_plane_parameters.cpp" )
create_single_source_cgal_program( "test_sphere_connected_component.cpp" )
create_single_source_cgal_program( "test_sphere_parameters.cpp" )
create_single_source_cgal_program( "test_torus_connected_component.cpp" )
create_single_source_cgal_program( "test_torus_parameters.cpp" )
create_single_source_cgal_program( "test_scene.cpp" )
else()
message(STATUS "This program requires the CGAL library, and will not be compiled.")
endif()

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,332 @@
#ifndef GENERATORS_H
#define GENERATORS_H
#include <CGAL/Random.h>
#include <CGAL/Point_with_normal_3.h>
#include <CGAL/Kernel/global_functions.h>
#include <CGAL/squared_distance_3.h>
#include <string>
#include <fstream>
template <typename fl_t>
fl_t random_float(fl_t min, fl_t max) {
return fl_t(CGAL::default_random.get_double(min, max));
}
template <typename K>
CGAL::Vector_3<K> random_normal() {
typedef typename K::FT FT;
CGAL::Vector_3<K> n;
do {
n = CGAL::Vector_3<K>(random_float((FT) -1.0, (FT) 1.0),
random_float((FT) -1.0, (FT) 1.0),
random_float((FT) -1.0, (FT) 1.0));
} while (n.squared_length() < (FT) 0.001);
n = n * (FT) 1.0 / (CGAL::sqrt(n.squared_length()));
return n;
}
template <class K>
CGAL::Point_3<K> random_point_in(const CGAL::Bbox_3& bbox) {
typedef typename K::FT FT;
FT x = random_float((FT) bbox.xmin(), (FT) bbox.xmax());
FT y = random_float((FT) bbox.ymin(), (FT) bbox.ymax());
FT z = random_float((FT) bbox.zmin(), (FT) bbox.zmax());
return CGAL::Point_3<K>(x, y, z);
}
template <class K>
CGAL::Point_with_normal_3<K> random_pwn_in(const CGAL::Bbox_3 &bbox) {
return CGAL::Point_with_normal_3<K>(random_point_in<K>(bbox),
random_normal<K>());
}
template <class K>
CGAL::Vector_3<K> normalize(CGAL::Vector_3<K> const& v) {
typedef typename K::FT FT;
FT l = CGAL::sqrt(v.squared_length());
if (l < (FT) 0.00001)
return CGAL::Vector_3<K>((FT) 0, (FT) 0, (FT) 0);
else return v * l;
}
template <typename K, typename OutputIterator>
void sample_sphere(const std::size_t num_points,
CGAL::Point_3<K> const& center, typename K::FT radius,
OutputIterator points) {
typedef typename K::FT FT;
for (std::size_t i = 0;i < num_points;++i) {
CGAL::Vector_3<K> direction(random_float((FT) -1, (FT) 1),
random_float((FT) -1, (FT) 1),
random_float((FT) -1,(FT) 1));
direction = direction * ((FT) 1.0 / CGAL::sqrt(direction.squared_length()));
CGAL::Point_3<K> p = center + direction * radius;
*points = CGAL::Point_with_normal_3<K>(p, direction);
++points;
}
}
template <typename K, typename OutputIterator>
void sample_random_sphere_in_box(const std::size_t num_points,
const CGAL::Bbox_3 &bbox, CGAL::Point_3<K> &center,
typename K::FT &radius, OutputIterator points) {
typedef typename K::FT FT;
// Generate random parameters
if (radius < 0.00001)
radius = random_float<FT>((FT) 0.01, (FT) 5);
else radius = random_float<FT>(radius / (FT) 10.0, (FT) radius);
center = random_point_in<K>(bbox);
sample_sphere(num_points, center, radius, points);
}
template <typename K, typename OutputIterator>
void sample_cylinder(const std::size_t num_points,
const CGAL::Point_3<K> &center,
const CGAL::Vector_3<K> &axis, typename K::FT radius,
typename K::FT length, OutputIterator points) {
typedef typename K::FT FT;
// Sample shape
for (size_t i = 0 ; i < num_points ; ++i) {
CGAL::Vector_3<K> normal(
random_float((FT) -1, (FT) 1),
random_float((FT) -1, (FT) 1),
random_float((FT) -1, (FT) 1));
normal = normal - ((normal * axis) * axis);
if (normal.squared_length() < (FT)0.0001) {
i--;
continue;
}
normal = normal * ((FT)1.0 / CGAL::sqrt(normal.squared_length()));
FT l = random_float(-length, length);
CGAL::Point_3<K> p = center + axis * l + radius * normal;
*points = CGAL::Point_with_normal_3<K>(p, normal);
++points;
}
}
template <typename K, typename OutputIterator>
void sample_random_cylinder(const std::size_t num_points,
CGAL::Point_3<K> &center,
CGAL::Vector_3<K> &axis, typename K::FT &radius,
OutputIterator points) {
typedef typename K::FT FT;
// Generate random parameters
axis = random_normal<K>();
radius = random_float((FT) 0.5, (FT) 5);
FT length = random_float((FT) 0.2, (FT) 10);
// Find random center point placed on the plane through
// the origin with 'axis' as normal.
CGAL::Vector_3<K> u = random_normal<K>();
CGAL::Vector_3<K> v = CGAL::cross_product(u, axis);
v = v * 1.0 / CGAL::sqrt(v.squared_length());
u = CGAL::cross_product(v, axis);
center = CGAL::ORIGIN + random_float((FT) -5, (FT) 5) * u + random_float((FT) -5, (FT) 5) * v;
sample_cylinder(num_points, center, axis, radius, length, points);
}
template <typename K, typename OutputIterator>
void sample_cone(const std::size_t num_points,
CGAL::Point_3<K> const& apex,
CGAL::Vector_3<K> const& axis, typename K::FT angle,
typename K::FT s, typename K::FT e, OutputIterator points) {
typedef typename K::FT FT;
typedef CGAL::Vector_3<K> Vector;
FT radiusGrow = std::tan(angle);
FT cosAng = std::cos(angle);
FT sinAng = std::sin(angle);
for (size_t i = 0 ; i < num_points ; ++i)
{
Vector normal(random_float((FT) -1, (FT) 1),
random_float((FT)-1, (FT) 1),
random_float((FT)-1, (FT) 1));
normal = normal - ((normal * axis) * axis);
if (normal.squared_length() < (FT) 0.0001) {
i--;
continue;
}
normal = normal * ((FT) 1.0 / CGAL::sqrt(normal.squared_length()));
FT l = random_float(s, e);
CGAL::Point_3<K> p = apex + axis * l + (l * radiusGrow) * normal;
// axis is pointing down from apex
normal = normal * (FT) 1.0 / (CGAL::sqrt(normal.squared_length()));
// normal is pointing from axis to surface point
normal = normal * cosAng - axis * sinAng;
l = CGAL::sqrt(normal.squared_length());
if ((FT) 0.95 > l || l > (FT) 1.05)
std::cout << "normal not normalized" << std::endl;
*points = CGAL::Point_with_normal_3<K>(p, normal);
++points;
}
}
template <typename K, typename OutputIterator>
void sample_random_cone(const std::size_t num_points,
CGAL::Point_3<K> &apex, CGAL::Vector_3<K> &axis,
typename K::FT &angle, typename K::FT &mid, OutputIterator points) {
typedef typename K::FT FT;
// Generate random parameters
apex = random_point_in<K>(CGAL::Bbox_3(-5, -5, -5, 5, 5, 5));
axis = random_normal<K>();
angle = random_float((FT) 0.2, (FT) 1.4);
FT start = random_float((FT) 0, (FT) 1.0);
FT end = start + random_float((FT) 0.5, (FT) 1.5);
mid = (start + end) / (FT) 2.0;
sample_cone(num_points, apex, axis, angle, start, end, points);
}
template <typename K, typename OutputIterator>
void sample_random_parallelogram_in_box(const std::size_t num_points,
const CGAL::Bbox_3 &bbox, CGAL::Vector_3<K> &normal,
typename K::FT &dist, OutputIterator points) {
typedef typename K::FT FT;
// Generate random plane from 3 non collinear points.
CGAL::Vector_3<K> u, v;
CGAL::Point_3<K> p[3];
do {
p[0] = random_point_in<K>(bbox);
p[1] = random_point_in<K>(bbox);
p[2] = random_point_in<K>(bbox);
CGAL::Vector_3<K> a = p[1] - p[0];
CGAL::Vector_3<K> b = p[2] - p[0];
if (a.squared_length() < (FT) 4.0 || b.squared_length() < (FT) 4.0) {
normal = CGAL::Vector_3<K>((FT) 0, (FT) 0, (FT) 0);
continue;
}
a = a * 1.0 / CGAL::sqrt(a.squared_length());
b = b * 1.0 / CGAL::sqrt(b.squared_length());
normal = CGAL::cross_product(a, b);
// repeat if angle between a and b is small
} while (normal.squared_length() < (FT) 0.2);
normal = normal * (FT) 1.0 / CGAL::sqrt(normal.squared_length());
dist = -((p[0] - CGAL::ORIGIN) * normal);
// sample
u = p[1] - p[0];
v = p[2] - p[0];
for (std::size_t i = 0;i < num_points; ++i) {
FT s = random_float((FT) 0, (FT) 1);
FT t = random_float((FT) 0, (FT) 1);
*points = CGAL::Point_with_normal_3<K>(p[0] + s * u + t * v, normal);
++points;
}
}
template <typename K, typename OutputIterator>
void sample_torus(const std::size_t num_points,
const CGAL::Point_3<K> &center,
const CGAL::Vector_3<K> &axis,
typename K::FT major_radius,
typename K::FT minor_radius,
OutputIterator points) {
typedef typename K::FT FT;
// calculate basis
CGAL::Vector_3<K> b1, b2;
b1 = CGAL::cross_product(axis, CGAL::Vector_3<K>((FT) 0, (FT) 0, (FT) 1));
if (b1.squared_length() < (FT) 0.1)
b1 = CGAL::cross_product(axis, CGAL::Vector_3<K>((FT) 0, (FT) 1, (FT) 0));
b1 = b1 / CGAL::sqrt(b1.squared_length());
b2 = CGAL::cross_product(axis, b1);
b2 = b2 / CGAL::sqrt(b2.squared_length());
for (size_t i = 0 ; i < num_points ; ++i)
{
FT tau = random_float((FT) 0, (FT) (2 * 3.141592656));
FT phi = random_float((FT) 0, (FT) (2 * 3.141592656));
CGAL::Vector_3<K> normal = sin(tau) * b1 + cos(tau) * b2;
normal = normal / CGAL::sqrt(normal.squared_length());
CGAL::Point_3<K> p = center + major_radius * normal;
normal = sin(phi) * normal + cos(phi) * axis;
p = p + minor_radius * normal;
*points = CGAL::Point_with_normal_3<K>(p, normal);
++points;
}
}
template <typename K, typename OutputIterator>
void sample_random_torus(const std::size_t num_points, CGAL::Point_3<K> &center,
CGAL::Vector_3<K> &axis, typename K::FT &major_radius,
typename K::FT &minor_radius, OutputIterator points) {
typedef typename K::FT FT;
// Generate random parameters
center = random_point_in<K>(CGAL::Bbox_3(-5, -5, -5, 5, 5, 5));
axis = random_normal<K>();
major_radius = random_float((FT) 1.0, (FT) 5.0);
minor_radius = random_float((FT) 0.1, (FT) 1.0);
sample_torus(num_points, center, axis, major_radius, minor_radius, points);
}
template <typename K, typename P>
void filter_by_distance(
const CGAL::Plane_3<K> &plane, typename K::FT dist,
std::vector<P> &points) {
typename K::FT d2 = dist * dist;
typename std::vector<P>::iterator it = points.begin();
while (it != points.end()) {
if (CGAL::squared_distance(plane, *it) < d2)
it = points.erase(it);
else it++;
}
}
template <typename K>
void save_scene(const std::string &fn, const std::vector<CGAL::Point_with_normal_3<K> > &pts) {
std::ofstream plyFile(fn.c_str());
plyFile << "ply" << std::endl;
plyFile << "format ascii 1.0" << std::endl;
plyFile << "element vertex " << pts.size() << std::endl;
plyFile << "property float x" << std::endl;
plyFile << "property float y" << std::endl;
plyFile << "property float z" << std::endl;
plyFile << "property float nx" << std::endl;
plyFile << "property float ny" << std::endl;
plyFile << "property float nz" << std::endl;
plyFile << "end_header" << std::endl;
plyFile << std::setprecision(6);
for (size_t i = 0;i<pts.size();i++) {
plyFile << pts[i][0] << " " << pts[i][1] << " " << pts[i][2];
CGAL::Vector_3<K> n = pts[i].normal();
plyFile << " " << n[0] << " " << n[1] << " " << n[2];
plyFile << std::endl;
}
plyFile.close();
}
#endif

View File

@ -0,0 +1,124 @@
#include "generators.h"
#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
#include <CGAL/Simple_cartesian.h>
#include <CGAL/Shape_detection_3.h>
#include <CGAL/Point_with_normal_3.h>
#include <CGAL/property_map.h>
template <class K>
bool test_cone_connected_component() {
const int NB_ROUNDS = 10;
const int NB_POINTS = 2000;
typedef typename K::FT FT;
typedef CGAL::Point_with_normal_3<K> Pwn;
typedef CGAL::Point_3<K> Point;
typedef CGAL::Vector_3<K> Vector;
typedef std::vector<Pwn> Pwn_vector;
typedef CGAL::Identity_property_map<Pwn> Point_map;
typedef CGAL::Normal_of_point_with_normal_pmap<K> Normal_map;
typedef CGAL::Shape_detection_3::Efficient_RANSAC_traits<
K, Pwn_vector, Point_map, Normal_map> Traits;
typedef CGAL::Shape_detection_3::Efficient_RANSAC<Traits> Efficient_ransac;
typedef CGAL::Shape_detection_3::Cone<Traits> Cone;
std::size_t success = 0;
for (int i = 0; i < NB_ROUNDS ; i++) {
Pwn_vector points;
// generate random points on a cone
FT spacing;
do {
points.clear();
CGAL::Bbox_3 bbox(-10, -10, -10, 10, 10, 10);
Vector axis = random_normal<K>();
Point apex = random_point_in<K>(bbox);
FT angle = random_float((FT) 0.1, (FT) 1.4);
FT start = random_float((FT) 0, (FT) 2.0);
FT end = start + random_float((FT) 0.5, (FT) 2.0);
FT mid = (start + end) / (FT)2.0;
sample_cone(NB_POINTS, apex, axis, angle, start, end,
std::back_inserter(points));
CGAL::Vector_3<K> n = random_normal<K>();
CGAL::Plane_3<K> pl(apex + mid * axis, n);
spacing = FT(0.75) * (mid / cos(angle)) * sin(angle);
filter_by_distance(pl, spacing * FT(0.5), points);
} while (points.size() < NB_POINTS / 2.0);
Efficient_ransac ransac;
ransac.template add_shape_factory<Cone>();
ransac.set_input(points);
// Same parameters as for the parameters unit tests, besides
// the cluster_epsilon.
typename Efficient_ransac::Parameters parameters;
parameters.probability = 0.05f;
parameters.min_points = points.size()/4;
parameters.epsilon = 0.002f;
parameters.normal_threshold = 0.9f;
// The first half of rounds choose a high cluster_epsilon to get only
// a single shape and a lower cluster_epsilon for the second half
// to get two separated shapes.
if (i < NB_ROUNDS/2)
parameters.cluster_epsilon = spacing * FT(1.1);
else
parameters.cluster_epsilon = spacing * FT(0.35);
if (!ransac.detect(parameters)) {
std::cout << " aborted" << std::endl;
return false;
}
typename Efficient_ransac::Shape_range shapes = ransac.shapes();
if (i < NB_ROUNDS/2 && shapes.size() != 1)
continue;
if (i >= NB_ROUNDS/2 && shapes.size() != 2)
continue;
success++;
}
if (success >= NB_ROUNDS * 0.8) {
std::cout << " succeeded" << std::endl;
return true;
}
else {
std::cout << " failed" << std::endl;
return false;
}
}
int main() {
bool success = true;
std::cout << "test_cone_connected_component<CGAL::Simple_cartesian<float>> ";
if (!test_cone_connected_component<CGAL::Simple_cartesian<float> >())
success = false;
std::cout << "test_cone_connected_component<CGAL::Simple_cartesian<double>> ";
if (!test_cone_connected_component<CGAL::Simple_cartesian<double> >())
success = false;
std::cout << "test_cone_connected_component<CGAL::Exact_predicates_inexact_constructions_kernel> ";
if (!test_cone_connected_component<CGAL::Exact_predicates_inexact_constructions_kernel>())
success = false;
return (success) ? EXIT_SUCCESS : EXIT_FAILURE;
}

View File

@ -0,0 +1,128 @@
#include "generators.h"
#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
#include <CGAL/Simple_cartesian.h>
#include <CGAL/Shape_detection_3.h>
#include <CGAL/Point_with_normal_3.h>
#include <CGAL/property_map.h>
#include <CGAL/number_utils.h>
template <class K>
bool test_cone_parameters() {
const int NB_ROUNDS = 10;
const int NB_POINTS = 1000;
typedef typename K::FT FT;
typedef CGAL::Point_with_normal_3<K> Pwn;
typedef CGAL::Point_3<K> Point;
typedef CGAL::Vector_3<K> Vector;
typedef std::vector<Pwn> Pwn_vector;
typedef CGAL::Identity_property_map<Pwn> Point_map;
typedef CGAL::Normal_of_point_with_normal_pmap<K> Normal_map;
typedef CGAL::Shape_detection_3::Efficient_RANSAC_traits<
K, Pwn_vector, Point_map, Normal_map> Traits;
typedef CGAL::Shape_detection_3::Efficient_RANSAC<Traits> Efficient_ransac;
typedef CGAL::Shape_detection_3::Cone<Traits> Cone;
std::size_t success = 0;
for (int i = 0 ; i < NB_ROUNDS ; i++) {
Pwn_vector points;
// generate random points on random cone
Vector axis;
Point apex;
FT angle = 0;
FT mid = 0;
CGAL::Bbox_3 bbox(-10, -10, -10, 10, 10, 10);
sample_random_cone(NB_POINTS, apex, axis, angle, mid,
std::back_inserter(points));
// add outliers in second half of rounds
if (i >= NB_ROUNDS / 2)
for (std::size_t j = 0; j < NB_POINTS / 2; j++)
points.push_back(random_pwn_in<K>(bbox));
Efficient_ransac ransac;
Traits traits = ransac.traits();
ransac.template add_shape_factory<Cone>();
ransac.set_input(points);
// Set cluster epsilon to a high value as just the parameters of
// the extracted primitives are to be tested.
typename Efficient_ransac::Parameters parameters;
parameters.probability = 0.05f;
parameters.min_points = NB_POINTS/10;
parameters.epsilon = 0.002f;
parameters.cluster_epsilon = 1.0f;
parameters.normal_threshold = 0.9f;
if (!ransac.detect(parameters)) {
std::cout << " aborted" << std::endl;
return false;
}
typename Efficient_ransac::Shape_range shapes = ransac.shapes();
// check: unique shape detected
if (shapes.size() != 1)
continue;
boost::shared_ptr<Cone> cone = boost::dynamic_pointer_cast<Cone>((*shapes.first));
// check: shape detected is a cone
if (!cone)
continue;
// Check radius and alignment with axis.
if (CGAL::abs(angle - cone->angle()) > (FT) 0.02
|| CGAL::abs(CGAL::abs(axis * cone->axis()) - (FT) 1.0) > (FT) 0.02)
continue;
// Check apex.
Point pos = cone->apex();
FT apex_pos_sqlen = traits.compute_squared_length_3_object()(
traits.construct_vector_3_object()(apex, pos));
if (apex_pos_sqlen > FT(0.0004))
continue;
std::string info = cone->info();
success++;
}
if (success >= NB_ROUNDS * 0.8) {
std::cout << " succeeded" << std::endl;
return true;
}
else {
std::cout << " failed" << std::endl;
return false;
}
}
int main() {
bool success = true;
std::cout << "test_cone_parameters<CGAL::Simple_cartesian<float>> ";
if (!test_cone_parameters<CGAL::Simple_cartesian<float> >())
success = false;
std::cout << "test_cone_parameters<CGAL::Simple_cartesian<double>> ";
if (!test_cone_parameters<CGAL::Simple_cartesian<double> >())
success = false;
std::cout << "test_cone_parameters<CGAL::Exact_predicates_inexact_constructions_kernel> ";
if (!test_cone_parameters<CGAL::Exact_predicates_inexact_constructions_kernel>())
success = false;
return (success) ? EXIT_SUCCESS : EXIT_FAILURE;
}

View File

@ -0,0 +1,118 @@
#include "generators.h"
#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
#include <CGAL/Simple_cartesian.h>
#include <CGAL/Shape_detection_3.h>
#include <CGAL/Point_with_normal_3.h>
#include <CGAL/property_map.h>
template <class K>
bool test_cylinder_connected_component() {
const int NB_ROUNDS = 10;
const int NB_POINTS = 4000;
typedef typename K::FT FT;
typedef typename CGAL::Point_with_normal_3<K> Pwn;
typedef typename CGAL::Point_3<K> Point;
typedef typename CGAL::Vector_3<K> Vector;
typedef std::vector<Pwn> Pwn_vector;
typedef typename CGAL::Identity_property_map<Pwn> Point_map;
typedef typename CGAL::Normal_of_point_with_normal_pmap<K> Normal_map;
typedef CGAL::Shape_detection_3::Efficient_RANSAC_traits<
K, Pwn_vector, Point_map, Normal_map> Traits;
typedef CGAL::Shape_detection_3::Efficient_RANSAC<Traits> Efficient_ransac;
typedef CGAL::Shape_detection_3::Cylinder<Traits> Cylinder;
std::size_t success = 0;
for (int i = 0 ; i < NB_ROUNDS ; i++) {
Pwn_vector points;
// generate random points on random cylinder
CGAL::Bbox_3 bbox(-10, -10, -10, 10, 10, 10);
FT radius = FT(1.0);
Vector axis = random_normal<K>();
Point center = random_point_in<K>(bbox);
sample_cylinder(NB_POINTS, center, axis,
radius, 0.5, std::back_inserter(points));
CGAL::Vector_3<K> n = random_normal<K>();
n = CGAL::cross_product(axis, n);
n = n * (FT) 1.0 / (CGAL::sqrt(n.squared_length()));
CGAL::Plane_3<K> pl(center, n);
FT spacing = (FT) 1.0;
filter_by_distance(pl, spacing * FT(0.5), points);
Efficient_ransac ransac;
ransac.template add_shape_factory<Cylinder>();
ransac.set_input(points);
// Same parameters as for the parameters unit tests, besides
// the cluster_epsilon.
typename Efficient_ransac::Parameters parameters;
parameters.probability = 0.05f;
parameters.min_points = points.size()/5;
parameters.epsilon = 0.002f;
parameters.normal_threshold = 0.9f;
// The first half of rounds choose a high cluster_epsilon to get only
// a single shape and a lower cluster_epsilon for the second half
// to get two separated shapes.
if (i < NB_ROUNDS/2)
parameters.cluster_epsilon = spacing * (FT) 1.1;
else
parameters.cluster_epsilon = spacing * (FT) 0.35;
if (!ransac.detect(parameters)) {
std::cout << " aborted" << std::endl;
return false;
}
typename Efficient_ransac::Shape_range shapes = ransac.shapes();
if (i < NB_ROUNDS/2 && shapes.size() != 1)
continue;
if (i >= NB_ROUNDS/2 && shapes.size() != 2)
continue;
success++;
}
if (success >= NB_ROUNDS * 0.8) {
std::cout << " succeeded" << std::endl;
return true;
}
else {
std::cout << " failed" << std::endl;
return false;
}
}
int main() {
bool success = true;
std::cout << "test_cylinder_connected_component<CGAL::Simple_cartesian<float>> ";
if (!test_cylinder_connected_component<CGAL::Simple_cartesian<float> >())
success = false;
std::cout << "test_cylinder_connected_component<CGAL::Simple_cartesian<double>> ";
if (!test_cylinder_connected_component<CGAL::Simple_cartesian<double> >())
success = false;
std::cout << "test_cylinder_connected_component<CGAL::Exact_predicates_inexact_constructions_kernel> ";
if (!test_cylinder_connected_component<CGAL::Exact_predicates_inexact_constructions_kernel>())
success = false;
return (success) ? EXIT_SUCCESS : EXIT_FAILURE;
}

View File

@ -0,0 +1,130 @@
#include "generators.h"
#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
#include <CGAL/Simple_cartesian.h>
#include <CGAL/Shape_detection_3.h>
#include <CGAL/Point_with_normal_3.h>
#include <CGAL/property_map.h>
#include <CGAL/number_utils.h>
template <class K>
bool test_cylinder_parameters() {
const int NB_ROUNDS = 10;
const int NB_POINTS = 1000;
typedef typename K::FT FT;
typedef CGAL::Point_with_normal_3<K> Pwn;
typedef CGAL::Point_3<K> Point;
typedef CGAL::Vector_3<K> Vector;
typedef std::vector<Pwn> Pwn_vector;
typedef CGAL::Identity_property_map<Pwn> Point_map;
typedef CGAL::Normal_of_point_with_normal_pmap<K> Normal_map;
typedef CGAL::Shape_detection_3::Efficient_RANSAC_traits<
K, Pwn_vector, Point_map, Normal_map> Traits;
typedef CGAL::Shape_detection_3::Efficient_RANSAC<Traits> Efficient_ransac;
typedef CGAL::Shape_detection_3::Cylinder<Traits> Cylinder;
std::size_t success = 0;
for (int i = 0 ; i < NB_ROUNDS ; i++) {
Pwn_vector points;
// generate random points on random cylinder
typename K::FT radius = 0; // radius will be randomly generated by sampleRandomCylinderInBox
Vector axis;
Point center;
CGAL::Bbox_3 bbox(-10, -10, -10, 10, 10, 10);
sample_random_cylinder(NB_POINTS, center, axis, // fix case, sample_random...
radius, std::back_inserter(points));
// add outliers in second half of rounds
if (i >= NB_ROUNDS / 2)
for (std::size_t j = 0; j < NB_POINTS / 2; j++)
points.push_back(random_pwn_in<K>(bbox));
Efficient_ransac ransac;
Traits traits = ransac.traits();
ransac.template add_shape_factory<Cylinder>();
ransac.set_input(points);
// Set cluster epsilon to a high value as just the parameters of
// the extracted primitives are to be tested.
typename Efficient_ransac::Parameters parameters;
parameters.probability = 0.05f;
parameters.min_points = NB_POINTS/10;
parameters.epsilon = 0.002f;
parameters.cluster_epsilon = 1.0f;
parameters.normal_threshold = 0.9f;
if (!ransac.detect(parameters)) {
std::cout << " aborted" << std::endl;
return false;
}
typename Efficient_ransac::Shape_range shapes = ransac.shapes();
// check: unique shape detected
if (shapes.size() != 1)
continue;
boost::shared_ptr<Cylinder> cyl = boost::dynamic_pointer_cast<Cylinder>((*shapes.first));
// check: shape detected is a cylinder
if (!cyl)
continue;
Point pos = cyl->axis().point(0);
// Check radius and alignment with axis.
if (CGAL::abs(radius - cyl->radius()) > 0.02
|| CGAL::abs(CGAL::abs(axis * cyl->axis().to_vector()) - 1.0) > 0.02)
continue;
pos = pos - ((pos - CGAL::ORIGIN) * axis) * axis;
FT center_pos_sqlen = traits.compute_squared_length_3_object()(
traits.construct_vector_3_object()(center, pos));
if (center_pos_sqlen > FT(0.0004))
continue;
std::string info = cyl->info();
success++;
}
if (success >= NB_ROUNDS * 0.8) {
std::cout << " succeeded" << std::endl;
return true;
}
else {
std::cout << " failed" << std::endl;
return false;
}
}
int main() {
bool success = true;
std::cout << "test_cylinder_parameters<CGAL::Simple_cartesian<float>> ";
if (!test_cylinder_parameters<CGAL::Simple_cartesian<float> >())
success = false;
std::cout << "test_cylinder_parameters<CGAL::Simple_cartesian<double>> ";
if (!test_cylinder_parameters<CGAL::Simple_cartesian<double> >())
success = false;
std::cout << "test_cylinder_parameters<CGAL::Exact_predicates_inexact_constructions_kernel> ";
if (!test_cylinder_parameters<CGAL::Exact_predicates_inexact_constructions_kernel>())
success = false;
return (success) ? EXIT_SUCCESS : EXIT_FAILURE;
}

View File

@ -0,0 +1,118 @@
#include "generators.h"
#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
#include <CGAL/Simple_cartesian.h>
#include <CGAL/Shape_detection_3.h>
#include <CGAL/Point_with_normal_3.h>
#include <CGAL/property_map.h>
template <class K>
bool test_plane_connected_component() {
const int NB_ROUNDS = 10;
typedef typename K::FT FT;
typedef CGAL::Point_with_normal_3<K> Pwn;
typedef CGAL::Point_3<K> Point;
typedef CGAL::Vector_3<K> Vector;
typedef std::vector<Pwn> Pwn_vector;
typedef CGAL::Identity_property_map<Pwn> Point_map;
typedef CGAL::Normal_of_point_with_normal_pmap<K> Normal_map;
typedef typename CGAL::Shape_detection_3::Efficient_RANSAC_traits<K,
Pwn_vector, Point_map, Normal_map> Traits;
typedef typename CGAL::Shape_detection_3::Efficient_RANSAC<Traits>
Efficient_ransac;
typedef typename CGAL::Shape_detection_3::Plane<Traits> Plane;
std::size_t success = 0;
for (int i = 0 ; i < NB_ROUNDS ; i++) {
Pwn_vector points;
Vector normal;
CGAL::Bbox_3 bbox(-10, -10, -10, 10, 10, 10);
// Sample 4 rectangles with 0.05 spacing between points
// and 0.2 spacing between rectangles.
Vector offset[] = {Vector((FT) 0, (FT) 0, (FT) 0),
Vector((FT) 1.2, (FT) 0, (FT) 0),
Vector((FT) 0, (FT) 1.2, (FT) 0),
Vector((FT) 1.2, (FT) 1.2, (FT) 0)};
for (std::size_t j = 0;j<4;j++) {
for (std::size_t x = 0;x<=20;x++)
for (std::size_t y = 0;y<=20;y++)
points.push_back(Pwn(Point(FT(x * 0.05), FT(y * 0.05), (FT) 1.0) + offset[j],
Vector((FT) 0, (FT) 0, (FT) 1)));
}
Efficient_ransac ransac;
ransac.template add_shape_factory<Plane>();
ransac.set_input(points);
// Same parameters as for the parameters unit tests, besides
// the cluster_epsilon.
typename Efficient_ransac::Parameters parameters;
parameters.probability = 0.05f;
parameters.min_points = 100;
parameters.epsilon = 0.002f;
parameters.normal_threshold = 0.9f;
// For the first half the rounds chose a high cluster_epsilon to find one
// shape and for the second half choose a small cluster_epsilon to find
// four separated shapes.
if (i < NB_ROUNDS/2)
parameters.cluster_epsilon = 0.201f;
else
parameters.cluster_epsilon = 0.051f;
if (!ransac.detect(parameters)) {
std::cout << " aborted" << std::endl;
return false;
}
typename Efficient_ransac::Shape_range shapes = ransac.shapes();
if (i < NB_ROUNDS/2 && shapes.size() != 1)
continue;
if (i >= NB_ROUNDS/2 && shapes.size() != 4)
continue;
success++;
}
if (success >= NB_ROUNDS * 0.8) {
std::cout << " succeeded" << std::endl;
return true;
}
else {
std::cout << " failed" << std::endl;
return false;
}
}
int main() {
bool success = true;
std::cout << "test_plane_connected_component<CGAL::Simple_cartesian<float>> ";
if (!test_plane_connected_component<CGAL::Simple_cartesian<float> >())
success = false;
std::cout << "test_plane_connected_component<CGAL::Simple_cartesian<double>> ";
if (!test_plane_connected_component<CGAL::Simple_cartesian<double> >())
success = false;
std::cout << "test_plane_connected_component<CGAL::Exact_predicates_inexact_constructions_kernel> ";
if (!test_plane_connected_component<CGAL::Exact_predicates_inexact_constructions_kernel>())
success = false;
return (success) ? EXIT_SUCCESS : EXIT_FAILURE;
}

View File

@ -0,0 +1,118 @@
#include "generators.h"
#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
#include <CGAL/Simple_cartesian.h>
#include <CGAL/Shape_detection_3.h>
#include <CGAL/Point_with_normal_3.h>
#include <CGAL/property_map.h>
#include <CGAL/number_utils.h>
template <class K>
bool test_plane_parameters() {
const int NB_ROUNDS = 10;
const int NB_POINTS = 1000;
typedef typename K::FT FT;
typedef typename CGAL::Point_with_normal_3<K> Pwn;
typedef typename CGAL::Vector_3<K> Vector;
typedef std::vector<Pwn> Pwn_vector;
typedef typename CGAL::Identity_property_map<Pwn> Point_map;
typedef typename CGAL::Normal_of_point_with_normal_pmap<K> Normal_map;
typedef CGAL::Shape_detection_3::Efficient_RANSAC_traits<K,
Pwn_vector, Point_map, Normal_map> Traits;
typedef CGAL::Shape_detection_3::Efficient_RANSAC<Traits> Efficient_ransac;
typedef CGAL::Shape_detection_3::Plane<Traits> Plane;
std::size_t success = 0;
for (int i = 0;i<NB_ROUNDS;i++) {
Pwn_vector points;
FT dist = 0;
Vector normal;
CGAL::Bbox_3 bbox(-10, -10, -10, 10, 10, 10);
sample_random_parallelogram_in_box(NB_POINTS, bbox, normal,
dist, std::back_inserter(points));
if (i >= NB_ROUNDS / 2)
for (std::size_t j = 0;j<NB_POINTS/2;j++) {
points.push_back(random_pwn_in<K>(bbox));
}
Efficient_ransac ransac;
Traits const& traits = ransac.traits();
ransac.template add_shape_factory<Plane>();
ransac.set_input(points);
// Set cluster epsilon to a high value as just the parameters of
// the extracted primitives are to be tested.
typename Efficient_ransac::Parameters parameters;
parameters.probability = 0.05f;
parameters.min_points = 100;
parameters.epsilon = 0.002f;
parameters.cluster_epsilon = 1.0f;
parameters.normal_threshold = 0.9f;
if (!ransac.detect(parameters)) {
std::cout << " aborted" << std::endl;
return false;
}
typename Efficient_ransac::Shape_range shapes = ransac.shapes();
if (shapes.size() != 1)
continue;
boost::shared_ptr<Plane> pl = boost::dynamic_pointer_cast<Plane>((*shapes.first));
if (!pl)
continue;
const FT phi = traits.compute_scalar_product_3_object()(
normal, pl->plane_normal());
const FT sign = (phi < 0) ? -1.0f : 1.0f;
const FT dist2 = pl->d();
if (CGAL::abs(phi) < 0.98 || CGAL::abs(dist2 - sign * dist) > 0.02)
continue;
std::string info = pl->info();
success++;
}
if (success >= NB_ROUNDS * 0.8) {
std::cout << " succeeded" << std::endl;
return true;
}
else {
std::cout << " failed" << std::endl;
return false;
}
}
int main() {
bool success = true;
std::cout << "test_plane_parameters<CGAL::Simple_cartesian<float>> ";
if (!test_plane_parameters<CGAL::Simple_cartesian<float> >())
success = false;
std::cout << "test_plane_parameters<CGAL::Simple_cartesian<double>> ";
if (!test_plane_parameters<CGAL::Simple_cartesian<double> >())
success = false;
std::cout << "test_plane_parameters<CGAL::Exact_predicates_inexact_constructions_kernel> ";
if (!test_plane_parameters<CGAL::Exact_predicates_inexact_constructions_kernel>())
success = false;
return (success) ? EXIT_SUCCESS : EXIT_FAILURE;
}

View File

@ -0,0 +1,153 @@
#include "generators.h"
#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
#include <CGAL/IO/read_xyz_points.h>
#include <CGAL/Simple_cartesian.h>
#include <CGAL/Shape_detection_3.h>
#include <CGAL/Point_with_normal_3.h>
#include <CGAL/property_map.h>
template <class K>
bool test_scene() {
typedef typename K::FT FT;
typedef CGAL::Point_with_normal_3<K> Pwn;
typedef std::vector<Pwn> Pwn_vector;
typedef CGAL::Identity_property_map<Pwn> Point_map;
typedef CGAL::Normal_of_point_with_normal_pmap<K> Normal_map;
typedef CGAL::Shape_detection_3::Efficient_RANSAC_traits<
K, Pwn_vector, Point_map, Normal_map> Traits;
typedef CGAL::Shape_detection_3::Efficient_RANSAC<Traits>
Efficient_ransac;
typedef typename Efficient_ransac::Point_index_range Point_index_range;
typedef CGAL::Shape_detection_3::Plane<Traits> Plane;
typedef CGAL::Shape_detection_3::Cone<Traits> Cone;
typedef CGAL::Shape_detection_3::Cylinder<Traits> Cylinder;
typedef CGAL::Shape_detection_3::Sphere<Traits> Sphere;
typedef CGAL::Shape_detection_3::Torus<Traits> Torus;
Pwn_vector points;
// Loads point set from a file.
// read_xyz_points_and_normals takes an OutputIterator for storing the points
// and a property map to store the normal vector with each point.
std::ifstream stream("data/cube.pwn");
if (!stream ||
!CGAL::read_xyz_points_and_normals(stream,
std::back_inserter(points),
Point_map(),
Normal_map()))
{
std::cerr << "Error: cannot read file cube.pwn" << std::endl;
return EXIT_FAILURE;
}
Efficient_ransac ransac;
ransac.template add_shape_factory<Cone>();
ransac.clear_shape_factories();
ransac.template add_shape_factory<Cone>();
ransac.template add_shape_factory<Cylinder>();
ransac.template add_shape_factory<Plane>();
ransac.template add_shape_factory<Sphere>();
ransac.template add_shape_factory<Torus>();
ransac.set_input(points);
ransac.preprocess();
ransac.clear_octrees();
if (!ransac.detect()) {
std::cout << " aborted" << std::endl;
return false;
}
typename Efficient_ransac::Shape_range shapes = ransac.shapes();
typename Efficient_ransac::Shape_range::iterator it = shapes.begin();
FT average_distance = 0;
// Iterate through all shapes and access each point.
while (it != shapes.end()) {
boost::shared_ptr<typename Efficient_ransac::Shape> shape = *it;
// Sums distances of points to detected shapes.
FT sum_distances = 0;
// Iterates through point indices assigned to each detected shape.
std::vector<std::size_t>::const_iterator
index_it = (*it)->indices_of_assigned_points().begin();
while (index_it != (*it)->indices_of_assigned_points().end()) {
// Retrieves point
const Pwn &p = *(points.begin() + (*index_it));
// Adds Euclidean distance between point and shape.
sum_distances += CGAL::sqrt((*it)->squared_distance(p));
// Proceeds with next point.
index_it++;
}
// Computes average distance.
average_distance += sum_distances / shape->indices_of_assigned_points().size();
// Proceeds with next detected shape.
it++;
}
// Check coverage. For this scene it should not fall below 85%
double coverage = double(points.size() - ransac.number_of_unassigned_points()) / double(points.size());
if (coverage < 0.85) {
std::cout << " failed" << std::endl;
return false;
}
// Check average distance. It should not lie above 0.02.
average_distance = average_distance / shapes.size();
std::cout << average_distance << " " << std::endl;
if (average_distance > 0.01) {
std::cout << " failed" << std::endl;
return false;
}
Point_index_range pts = ransac.indices_of_unassigned_points();
std::cout << " succeeded" << std::endl;
return true;
}
int main() {
bool success = true;
std::cout << "test_scene<CGAL::Simple_cartesian<float>> ";
if (!test_scene<CGAL::Simple_cartesian<float> >())
success = false;
std::cout << "test_scene<CGAL::Simple_cartesian<double>> ";
if (!test_scene<CGAL::Simple_cartesian<double> >())
success = false;
std::cout << "test_scene<CGAL::Exact_predicates_inexact_constructions_kernel> ";
if (!test_scene<CGAL::Exact_predicates_inexact_constructions_kernel>())
success = false;
return (success) ? EXIT_SUCCESS : EXIT_FAILURE;
}

View File

@ -0,0 +1,114 @@
#include "generators.h"
#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
#include <CGAL/Simple_cartesian.h>
#include <CGAL/Shape_detection_3.h>
#include <CGAL/Point_with_normal_3.h>
#include <CGAL/property_map.h>
template <class K>
bool test_sphere_connected_component() {
const int NB_ROUNDS = 10;
const int NB_POINTS = 4000;
typedef typename K::FT FT;
typedef CGAL::Point_with_normal_3<K> Pwn;
typedef CGAL::Point_3<K> Point;
typedef std::vector<Pwn> Pwn_vector;
typedef CGAL::Identity_property_map<Pwn> Point_map;
typedef CGAL::Normal_of_point_with_normal_pmap<K> Normal_map;
typedef CGAL::Shape_detection_3::Efficient_RANSAC_traits<
K, Pwn_vector, Point_map, Normal_map> Traits;
typedef CGAL::Shape_detection_3::Efficient_RANSAC<Traits> Efficient_ransac;
typedef CGAL::Shape_detection_3::Sphere<Traits> Sphere;
std::size_t success = 0;
for (int i = 0;i<NB_ROUNDS;i++) {
Pwn_vector points;
// generate random points on random sphere
FT radius = 1.0;
Point center;
CGAL::Bbox_3 bbox(-10, -10, -10, 10, 10, 10);
sample_random_sphere_in_box(NB_POINTS, bbox, center,
radius, std::back_inserter(points));
CGAL::Vector_3<K> n = random_normal<K>();
CGAL::Plane_3<K> pl(center, n);
FT spacing = radius / FT(4);
filter_by_distance(pl, spacing * FT(0.5), points);
Efficient_ransac ransac;
ransac.template add_shape_factory<Sphere>();
ransac.set_input(points);
// Same parameters as for the parameters unit tests, besides
// the cluster_epsilon.
typename Efficient_ransac::Parameters parameters;
parameters.probability = 0.05f;
parameters.min_points = points.size()/5;
parameters.epsilon = 0.002f;
parameters.normal_threshold = 0.9f;
// The first half of rounds choose a high cluster_epsilon to get only
// a single shape and a lower cluster_epsilon for the second half
// to get two separated shapes.
if (i < NB_ROUNDS/2)
parameters.cluster_epsilon = spacing * FT(1.1);
else
parameters.cluster_epsilon = spacing * FT(0.35);
if (!ransac.detect(parameters)) {
std::cout << " aborted" << std::endl;
return false;
}
typename Efficient_ransac::Shape_range shapes = ransac.shapes();
if (i < NB_ROUNDS/2 && shapes.size() != 1)
continue;
if (i >= NB_ROUNDS/2 && shapes.size() != 2)
continue;
success++;
}
if (success >= NB_ROUNDS * 0.8) {
std::cout << " succeeded" << std::endl;
return true;
}
else {
std::cout << " failed" << std::endl;
return false;
}
}
int main() {
bool success = true;
std::cout << "test_sphere_connected_component<CGAL::Simple_cartesian<float>> ";
if (!test_sphere_connected_component<CGAL::Simple_cartesian<float> >())
success = false;
std::cout << "test_sphere_connected_component<CGAL::Simple_cartesian<double>> ";
if (!test_sphere_connected_component<CGAL::Simple_cartesian<double> >())
success = false;
std::cout << "test_sphere_connected_component<CGAL::Exact_predicates_inexact_constructions_kernel> ";
if (!test_sphere_connected_component<CGAL::Exact_predicates_inexact_constructions_kernel>())
success = false;
return (success) ? EXIT_SUCCESS : EXIT_FAILURE;
}

View File

@ -0,0 +1,124 @@
#include "generators.h"
#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
#include <CGAL/Simple_cartesian.h>
#include <CGAL/Shape_detection_3.h>
#include <CGAL/Point_with_normal_3.h>
#include <CGAL/property_map.h>
#include <CGAL/number_utils.h>
template <class K>
bool test_sphere_parameters() {
const int NB_ROUNDS = 10;
const int NB_POINTS = 1000;
typedef typename K::FT FT;
typedef CGAL::Point_with_normal_3<K> Pwn;
typedef CGAL::Point_3<K> Point;
typedef std::vector<Pwn> Pwn_vector;
typedef CGAL::Identity_property_map<Pwn> Point_map;
typedef CGAL::Normal_of_point_with_normal_pmap<K> Normal_map;
typedef CGAL::Shape_detection_3::Efficient_RANSAC_traits<
K, Pwn_vector, Point_map, Normal_map> Traits;
typedef CGAL::Shape_detection_3::Efficient_RANSAC<Traits> Efficient_ransac;
typedef CGAL::Shape_detection_3::Sphere<Traits> Sphere;
std::size_t success = 0;
for (int i = 0;i<NB_ROUNDS;i++) {
Pwn_vector points;
// generate random points on random sphere
FT radius = 0;
Point center;
CGAL::Bbox_3 bbox(-10, -10, -10, 10, 10, 10);
sample_random_sphere_in_box(NB_POINTS, bbox, center,
radius, std::back_inserter(points));
// add outliers in second half of rounds
if (i >= NB_ROUNDS / 2)
for (std::size_t j = 0; j < NB_POINTS / 2; j++)
points.push_back(random_pwn_in<K>(bbox));
Efficient_ransac ransac;
Traits traits = ransac.traits();
ransac.template add_shape_factory<Sphere>();
ransac.set_input(points);
// Set cluster epsilon to a high value as just the parameters of
// the extracted primitives are to be tested.
typename Efficient_ransac::Parameters parameters;
parameters.probability = 0.05f;
parameters.min_points = NB_POINTS/10;
parameters.epsilon = 0.002f;
parameters.cluster_epsilon = 1.0f;
parameters.normal_threshold = 0.9f;
if (!ransac.detect(parameters)) {
std::cout << " aborted" << std::endl;
return false;
}
typename Efficient_ransac::Shape_range shapes = ransac.shapes();
// check: unique shape detected
if (shapes.size() != 1)
continue;
boost::shared_ptr<Sphere> sphere = boost::dynamic_pointer_cast<Sphere>((*shapes.first));
// check: shape detected is a cylinder
if (!sphere)
continue;
// Check radius and alignment with axis.
if (CGAL::abs(radius - sphere->radius()) > (FT) 0.02)
continue;
// Check center.
Point pos = sphere->center();
FT center_pos_sqlen = traits.compute_squared_length_3_object()(
traits.construct_vector_3_object()(center, pos));
if (center_pos_sqlen > FT(0.0004))
continue;
std::string info = sphere->info();
success++;
}
if (success >= NB_ROUNDS * 0.8) {
std::cout << " succeeded" << std::endl;
return true;
}
else {
std::cout << " failed" << std::endl;
return false;
}
}
int main() {
bool success = true;
std::cout << "test_sphere_parameters<CGAL::Simple_cartesian<float>> ";
if (!test_sphere_parameters<CGAL::Simple_cartesian<float> >())
success = false;
std::cout << "test_sphere_parameters<CGAL::Simple_cartesian<double>> ";
if (!test_sphere_parameters<CGAL::Simple_cartesian<double> >())
success = false;
std::cout << "test_sphere_parameters<CGAL::Exact_predicates_inexact_constructions_kernel> ";
if (!test_sphere_parameters<CGAL::Exact_predicates_inexact_constructions_kernel>())
success = false;
return (success) ? EXIT_SUCCESS : EXIT_FAILURE;
}

View File

@ -0,0 +1,120 @@
#include "generators.h"
#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
#include <CGAL/Simple_cartesian.h>
#include <CGAL/Shape_detection_3.h>
#include <CGAL/Point_with_normal_3.h>
#include <CGAL/property_map.h>
template <class K>
bool test_torus_connected_component() {
const int NB_ROUNDS = 10;
const int NB_POINTS = 2000;
typedef typename K::FT FT;
typedef CGAL::Point_with_normal_3<K> Pwn;
typedef CGAL::Point_3<K> Point;
typedef CGAL::Vector_3<K> Vector;
typedef std::vector<Pwn> Pwn_vector;
typedef CGAL::Identity_property_map<Pwn> Point_map;
typedef CGAL::Normal_of_point_with_normal_pmap<K> Normal_map;
typedef CGAL::Shape_detection_3::Efficient_RANSAC_traits<
K, Pwn_vector, Point_map, Normal_map> Traits;
typedef CGAL::Shape_detection_3::Efficient_RANSAC<Traits> Efficient_ransac;
typedef CGAL::Shape_detection_3::Torus<Traits> Torus;
std::size_t success = 0;
for (int i = 0;i<NB_ROUNDS;i++) {
Pwn_vector points;
// generate random points on torus
CGAL::Bbox_3 bbox(-10, -10, -10, 10, 10, 10);
FT minor_radius = (FT) 0.7;
FT major_radius = (FT) 2.0;
Vector axis = random_normal<K>();
Point center = random_point_in<K>(bbox);
sample_torus(NB_POINTS, center, axis,
major_radius, minor_radius, std::back_inserter(points));
CGAL::Vector_3<K> n = random_normal<K>();
n = CGAL::cross_product(axis, n);
n = n * (FT) 1.0 / (CGAL::sqrt(n.squared_length()));
CGAL::Plane_3<K> pl(center, n);
FT spacing = (FT) 1;
filter_by_distance(pl, spacing * FT(0.5), points);
Efficient_ransac ransac;
ransac.template add_shape_factory<Torus>();
ransac.set_input(points);
// Same parameters as for the parameters unit tests, besides
// the cluster_epsilon.
typename Efficient_ransac::Parameters parameters;
parameters.probability = 0.05f;
parameters.min_points = points.size()/10;
parameters.epsilon = 0.002f;
parameters.normal_threshold = 0.9f;
// The first half of rounds choose a high cluster_epsilon to get only
// a single shape and a lower cluster_epsilon for the second half
// to get two separated shapes.
if (i < NB_ROUNDS/2)
parameters.cluster_epsilon = spacing * (FT) 1.5;
else
parameters.cluster_epsilon = spacing * (FT) 0.9;
if (!ransac.detect(parameters)) {
std::cout << " aborted" << std::endl;
return false;
}
typename Efficient_ransac::Shape_range shapes = ransac.shapes();
if (i < NB_ROUNDS/2 && shapes.size() != 1)
continue;
if (i >= NB_ROUNDS/2 && shapes.size() != 2)
continue;
success++;
}
if (success >= NB_ROUNDS * 0.8) {
std::cout << " succeeded" << std::endl;
return true;
}
else {
std::cout << " failed" << std::endl;
return false;
}
}
int main() {
bool success = true;
std::cout << "test_torus_connected_component<CGAL::Simple_cartesian<float>> ";
if (!test_torus_connected_component<CGAL::Simple_cartesian<float> >())
success = false;
std::cout << "test_torus_connected_component<CGAL::Simple_cartesian<double>> ";
if (!test_torus_connected_component<CGAL::Simple_cartesian<double> >())
success = false;
std::cout << "test_torus_connected_component<CGAL::Exact_predicates_inexact_constructions_kernel> ";
if (!test_torus_connected_component<CGAL::Exact_predicates_inexact_constructions_kernel>())
success = false;
return (success) ? EXIT_SUCCESS : EXIT_FAILURE;
}

View File

@ -0,0 +1,132 @@
#include "generators.h"
#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
#include <CGAL/Simple_cartesian.h>
#include <CGAL/Shape_detection_3.h>
#include <CGAL/Point_with_normal_3.h>
#include <CGAL/property_map.h>
#include <CGAL/number_utils.h>
template <class K>
bool test_torus_parameters() {
const int NB_ROUNDS = 10;
const int NB_POINTS = 1000;
typedef typename K::FT FT;
typedef CGAL::Point_with_normal_3<K> Pwn;
typedef CGAL::Point_3<K> Point;
typedef CGAL::Vector_3<K> Vector;
typedef std::vector<Pwn> Pwn_vector;
typedef CGAL::Identity_property_map<Pwn> Point_map;
typedef CGAL::Normal_of_point_with_normal_pmap<K> Normal_map;
typedef CGAL::Shape_detection_3::Efficient_RANSAC_traits<
K, Pwn_vector, Point_map, Normal_map> Traits;
typedef CGAL::Shape_detection_3::Efficient_RANSAC<Traits> Efficient_ransac;
typedef CGAL::Shape_detection_3::Torus<Traits> Torus;
std::size_t success = 0;
for (int i = 0;i<NB_ROUNDS;i++) {
Pwn_vector points;
// generate random points on random cylinder
FT minor_radius = (FT) 0;
FT major_radius = (FT) 0;
Vector axis;
Point center;
CGAL::Bbox_3 bbox(-10, -10, -10, 10, 10, 10);
sample_random_torus(NB_POINTS, center, axis,
major_radius, minor_radius, std::back_inserter(points));
// add outliers in second half of rounds
if (i >= NB_ROUNDS / 2)
for (std::size_t j = 0; j < NB_POINTS / 2; j++)
points.push_back(random_pwn_in<K>(bbox));
Efficient_ransac ransac;
Traits traits = ransac.traits();
ransac.template add_shape_factory<Torus>();
ransac.set_input(points);
// Set cluster epsilon to a high value as just the parameters of
// the extracted primitives are to be tested.
typename Efficient_ransac::Parameters parameters;
parameters.probability = 0.05f;
parameters.min_points = NB_POINTS/10;
parameters.epsilon = 0.002f;
parameters.cluster_epsilon = 1.0f;
parameters.normal_threshold = 0.9f;
if (!ransac.detect(parameters)) {
std::cout << " aborted" << std::endl;
return false;
}
typename Efficient_ransac::Shape_range shapes = ransac.shapes();
// check: unique shape detected
if (shapes.size() != 1)
continue;
boost::shared_ptr<Torus> torus =
boost::dynamic_pointer_cast<Torus>((*shapes.first));
// check: shape detected is a torus
if (!torus)
continue;
Point pos = torus->center();
// Check radii and alignment with axis.
if (CGAL::abs(major_radius - torus->major_radius()) > (FT) 0.02
|| CGAL::abs(minor_radius - torus->minor_radius()) > (FT) 0.02
|| CGAL::abs(CGAL::abs(axis * torus->axis()) - 1.0) > (FT) 0.02)
continue;
// Check center.
FT center_pos_sqlen = traits.compute_squared_length_3_object()(
traits.construct_vector_3_object()(center, pos));
if (center_pos_sqlen > FT(0.0004))
continue;
std::string info = torus->info();
success++;
}
if (success >= NB_ROUNDS * 0.8) {
std::cout << " succeeded" << std::endl;
return true;
}
else {
std::cout << " failed" << std::endl;
return false;
}
}
int main() {
bool success = true;
std::cout << "test_torus_parameters<CGAL::Simple_cartesian<float>> ";
if (!test_torus_parameters<CGAL::Simple_cartesian<float> >())
success = false;
std::cout << "test_torus_parameters<CGAL::Simple_cartesian<double>> ";
if (!test_torus_parameters<CGAL::Simple_cartesian<double> >())
success = false;
std::cout << "test_torus_parameters<CGAL::Exact_predicates_inexact_constructions_kernel> ";
if (!test_torus_parameters<CGAL::Exact_predicates_inexact_constructions_kernel>())
success = false;
return (success) ? EXIT_SUCCESS : EXIT_FAILURE;
}

View File

@ -438,6 +438,11 @@ if(CGAL_Qt5_FOUND AND Qt5_FOUND AND OPENGL_FOUND AND QGLVIEWER_FOUND)
target_link_libraries(point_set_simplification_plugin scene_points_with_normal_item) target_link_libraries(point_set_simplification_plugin scene_points_with_normal_item)
qt5_wrap_ui( ps_outliers_removal_UI_FILES Polyhedron_demo_point_set_outliers_removal_plugin.ui) qt5_wrap_ui( ps_outliers_removal_UI_FILES Polyhedron_demo_point_set_outliers_removal_plugin.ui)
qt5_wrap_ui(point_set_shape_detectionUI_FILES Polyhedron_demo_point_set_shape_detection_plugin.ui)
polyhedron_demo_plugin(point_set_shape_detection_plugin Polyhedron_demo_point_set_shape_detection_plugin ${point_set_shape_detectionUI_FILES})
target_link_libraries(point_set_shape_detection_plugin scene_polyhedron_item scene_points_with_normal_item)
polyhedron_demo_plugin(point_set_outliers_removal_plugin Polyhedron_demo_point_set_outliers_removal_plugin ${ps_outliers_removal_UI_FILES}) polyhedron_demo_plugin(point_set_outliers_removal_plugin Polyhedron_demo_point_set_outliers_removal_plugin ${ps_outliers_removal_UI_FILES})
target_link_libraries(point_set_outliers_removal_plugin scene_points_with_normal_item) target_link_libraries(point_set_outliers_removal_plugin scene_points_with_normal_item)

View File

@ -0,0 +1,190 @@
#include "config.h"
#include "Scene_points_with_normal_item.h"
#include "Polyhedron_demo_plugin_helper.h"
#include "Polyhedron_demo_plugin_interface.h"
#include <CGAL/grid_simplify_point_set.h>
#include <CGAL/random_simplify_point_set.h>
#include <CGAL/compute_average_spacing.h>
#include <CGAL/Timer.h>
#include <CGAL/Memory_sizer.h>
#include <CGAL/Random.h>
#include <CGAL/Shape_detection_3.h>
#include <QObject>
#include <QAction>
#include <QMainWindow>
#include <QApplication>
#include <QtPlugin>
#include <QMessageBox>
#include <boost/foreach.hpp>
#include "ui_Polyhedron_demo_point_set_shape_detection_plugin.h"
typedef CGAL::Exact_predicates_inexact_constructions_kernel Epic_kernel;
typedef Epic_kernel::Point_3 Point;
//typedef CGAL::Point_with_normal_3<Epic_kernel> Point_with_normal;
//typedef std::vector<Point_with_normal> Point_list;
//typedef CGAL::Identity_property_map<Point_with_normal> PointPMap;
//typedef CGAL::Normal_of_point_with_normal_pmap<Epic_kernel> NormalPMap;
class Polyhedron_demo_point_set_shape_detection_plugin :
public QObject,
public Polyhedron_demo_plugin_helper
{
Q_OBJECT
Q_INTERFACES(Polyhedron_demo_plugin_interface)
Q_PLUGIN_METADATA(IID "com.geometryfactory.PolyhedronDemo.PluginInterface/1.0")
QAction* actionDetect;
public:
void init(QMainWindow* mainWindow, Scene_interface* scene_interface) {
actionDetect = new QAction(tr("Point set shape detection"), mainWindow);
actionDetect->setObjectName("actionDetect");
Polyhedron_demo_plugin_helper::init(mainWindow, scene_interface);
}
bool applicable(QAction*) const {
Scene_points_with_normal_item* item =
qobject_cast<Scene_points_with_normal_item*>(scene->item(scene->mainSelectionIndex()));
if (item && item->has_normals())
return true;
return false;
}
QList<QAction*> actions() const {
return QList<QAction*>() << actionDetect;
}
public Q_SLOTS:
void on_actionDetect_triggered();
}; // end Polyhedron_demo_point_set_shape_detection_plugin
class Point_set_demo_point_set_shape_detection_dialog : public QDialog, private Ui::PointSetShapeDetectionDialog
{
Q_OBJECT
public:
Point_set_demo_point_set_shape_detection_dialog(QWidget * /*parent*/ = 0)
{
setupUi(this);
}
//QString shapeDetectionMethod() const { return m_shapeDetectionMethod->currentText(); }
double cluster_epsilon() const { return m_cluster_epsilon_field->value(); }
double epsilon() const { return m_epsilon_field->value(); }
unsigned int min_points() const { return m_min_pts_field->value(); }
double normal_tolerance() const { return m_normal_tolerance_field->value(); }
double search_probability() const { return m_probability_field->value(); }
double gridCellSize() const { return 1.0; }
};
void Polyhedron_demo_point_set_shape_detection_plugin::on_actionDetect_triggered() {
CGAL::Random rand(time(0));
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;
//Epic_kernel::FT diag = sqrt(((points->bounding_box().max)() - (points->bounding_box().min)()).squared_length());
// Gets options
Point_set_demo_point_set_shape_detection_dialog dialog;
if(!dialog.exec())
return;
QApplication::setOverrideCursor(Qt::WaitCursor);
CGAL::Timer task_timer; task_timer.start();
typedef CGAL::Identity_property_map<Point_set::Point_with_normal> PointPMap;
typedef CGAL::Normal_of_point_with_normal_pmap<Point_set::Geom_traits> NormalPMap;
typedef CGAL::Shape_detection_3::Efficient_RANSAC_traits<Epic_kernel, Point_set, PointPMap, NormalPMap> Traits;
typedef CGAL::Shape_detection_3::Efficient_RANSAC<Traits> Shape_detection;
Shape_detection shape_detection;
shape_detection.set_input(*points);
// Shapes to be searched for are registered by using the template Shape_factory
shape_detection.add_shape_factory<CGAL::Shape_detection_3::Plane<Traits> >();
shape_detection.add_shape_factory<CGAL::Shape_detection_3::Cylinder<Traits> >();
// shape_detection.add_shape_factory< CGAL::Shape_detection_3::Torus<Traits> >();
// shape_detection.add_shape_factory< CGAL::Shape_detection_3::Cone<Traits> >();
// shape_detection.add_shape_factory< CGAL::Shape_detection_3::Sphere<Traits> >();
// Parameterization of the shape detection using the Parameters structure.
Shape_detection::Parameters op;
op.probability = dialog.search_probability(); // probability to miss the largest primitive on each iteration.
op.min_points = dialog.min_points(); // Only extract shapes with a minimum number of points.
op.epsilon = dialog.epsilon(); // maximum euclidean distance between point and shape.
op.cluster_epsilon = dialog.cluster_epsilon(); // maximum euclidean distance between points to be clustered.
op.normal_threshold = dialog.normal_tolerance(); // normal_threshold < dot(surface_normal, point_normal); maximum normal deviation.
// The actual shape detection.
shape_detection.detect(op);
std::cout << shape_detection.shapes().size() << " shapes found" << std::endl;
//print_message(QString("%1 shapes found.").arg(shape_detection.number_of_shapes()));
int index = 0;
BOOST_FOREACH(boost::shared_ptr<Shape_detection::Shape> shape, shape_detection.shapes())
{
Scene_points_with_normal_item *point_item = new Scene_points_with_normal_item;
BOOST_FOREACH(std::size_t i, shape->indices_of_assigned_points())
point_item->point_set()->push_back((*points)[i]);
unsigned char r, g, b;
r = static_cast<unsigned char>(64 + rand.get_int(0, 192));
g = static_cast<unsigned char>(64 + rand.get_int(0, 192));
b = static_cast<unsigned char>(64 + rand.get_int(0, 192));
point_item->setRbgColor(r, g, b);
// Providing a useful name consisting of the order of detection, name of type and number of inliers
std::stringstream ss;
if (dynamic_cast<CGAL::Shape_detection_3::Cylinder<Traits> *>(shape.get()))
ss << item->name().toStdString() << "_cylinder_";
else if (dynamic_cast<CGAL::Shape_detection_3::Plane<Traits> *>(shape.get()))
ss << item->name().toStdString() << "_plane_";
ss << shape->indices_of_assigned_points().size();
//names[i] = ss.str(
point_item->setName(QString::fromStdString(ss.str()));
point_item->set_has_normals(true);
point_item->setRenderingMode(item->renderingMode());
scene->addItem(point_item);
++index;
}
// Updates scene
scene->itemChanged(index);
QApplication::restoreOverrideCursor();
// Warn user, maybe choice of parameters is unsuitable
// if (nb_points_to_remove > 0)
// {
// QMessageBox::information(NULL,
// tr("Points selected for removal"),
// tr("%1 point(s) are selected for removal.\nYou may delete or reset the selection using the item context menu.")
// .arg(nb_points_to_remove));
// }
item->setVisible(false);
}
}
#include <QtPlugin>
#include "Polyhedron_demo_point_set_shape_detection_plugin.moc"

View File

@ -0,0 +1,202 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>PointSetShapeDetectionDialog</class>
<widget class="QDialog" name="PointSetShapeDetectionDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>444</width>
<height>181</height>
</rect>
</property>
<property name="windowTitle">
<string>Shape Detection</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QLabel" name="label_2">
<property name="text">
<string>Epsilon:</string>
</property>
</widget>
</item>
<item>
<widget class="QDoubleSpinBox" name="m_epsilon_field">
<property name="toolTip">
<string>Fitting tolerance in Euclidean distance</string>
</property>
<property name="decimals">
<number>3</number>
</property>
<property name="maximum">
<double>1.000000000000000</double>
</property>
<property name="singleStep">
<double>0.001000000000000</double>
</property>
<property name="value">
<double>0.002000000000000</double>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QLabel" name="label">
<property name="text">
<string>Normal tolerance:</string>
</property>
</widget>
</item>
<item>
<widget class="QDoubleSpinBox" name="m_normal_tolerance_field">
<property name="toolTip">
<string>Normal angle deviation tolerance as cosine of the angle</string>
</property>
<property name="singleStep">
<double>0.001000000000000</double>
</property>
<property name="value">
<double>0.900000000000000</double>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
<widget class="QLabel" name="label_3">
<property name="text">
<string>minimum no. of points:</string>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="m_min_pts_field">
<property name="toolTip">
<string>Smallest allowed size for a primitive</string>
</property>
<property name="maximum">
<number>10000</number>
</property>
<property name="singleStep">
<number>50</number>
</property>
<property name="value">
<number>100</number>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_4">
<item>
<widget class="QLabel" name="label_4">
<property name="text">
<string>Connectivity epsilon:</string>
</property>
</widget>
</item>
<item>
<widget class="QDoubleSpinBox" name="m_cluster_epsilon_field">
<property name="toolTip">
<string>Maximum world distance between points on a shape to be considered as connected</string>
</property>
<property name="decimals">
<number>2</number>
</property>
<property name="maximum">
<double>1.000000000000000</double>
</property>
<property name="singleStep">
<double>0.010000000000000</double>
</property>
<property name="value">
<double>0.020000000000000</double>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_5">
<item>
<widget class="QLabel" name="label_5">
<property name="text">
<string>Search rigorosity (probability):</string>
</property>
</widget>
</item>
<item>
<widget class="QDoubleSpinBox" name="m_probability_field">
<property name="toolTip">
<string>Probability to overlook the largest primitive in one extraction iteration</string>
</property>
<property name="maximum">
<double>1.000000000000000</double>
</property>
<property name="singleStep">
<double>0.010000000000000</double>
</property>
<property name="value">
<double>0.050000000000000</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>PointSetShapeDetectionDialog</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>PointSetShapeDetectionDialog</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

@ -130,6 +130,7 @@ else
point_inside_polyhedron_plugin \ point_inside_polyhedron_plugin \
point_set_average_spacing_plugin \ point_set_average_spacing_plugin \
point_set_outliers_removal_plugin \ point_set_outliers_removal_plugin \
point_set_shape_detection_plugin \
point_set_simplification_plugin \ point_set_simplification_plugin \
point_set_smoothing_plugin \ point_set_smoothing_plugin \
poisson_plugin \ poisson_plugin \

View File

@ -60,6 +60,13 @@ namespace CGAL {
{ {
return this->second; return this->second;
} }
/// returns `std::distance(begin(), end())`
typename std::iterator_traits<I>::difference_type
size() const
{
return std::distance(begin(), end());
}
}; };
template <typename T> template <typename T>

View File

@ -58,6 +58,13 @@ namespace CGAL {
{ {
return this->second; return this->second;
} }
/// returns `std::distance(begin(), end())`
typename std::iterator_traits<I>::difference_type
size() const
{
return std::distance(begin(), end());
}
}; };
template <typename T> template <typename T>