cgal/Shape_detection/doc/Shape_detection/Shape_detection.txt

597 lines
39 KiB
Plaintext

namespace CGAL {
/*!
\mainpage User Manual
\anchor Chapter_Shape_Detection
\cgalAutoToc
\authors Sven Oesau, Yannick Verdie, Clément Jamin, Pierre Alliez, Florent Lafarge, Simon Giraudot, Thien Hoang, and Dmitry Anisimov
\section Shape_detection_Introduction Introduction
This \cgal component implements two algorithms for shape detection:
- the *Efficient RANSAC (RANdom SAmple Consensus)* method, contributed by Schnabel et al. \cgalCite{schnabel2007efficient};
- the *Region Growing* method, contributed by Lafarge and Mallet \cgalCite{cgal:lm-clscm-12}.
\section Shape_detection_RANSAC Efficient RANSAC
From an unstructured point set with unoriented normals, this algorithm detects a set of shapes (see Figure \cgalFigureRef{Efficient_RANSAC_overview}).
Five types of primitive shapes are provided by this package: plane, sphere, cylinder, cone, and torus. Other primitive shapes can be easily added
by the user (see Section \ref Shape_detection_RANSACExample_with_custom_shapes).
\cgalFigureBegin{Efficient_RANSAC_overview, Efficient_RANSAC/overview2.png}
Input and output of the Efficient RANSAC method.
(a) Input point set.
(b) Point set depicted with one color per detected shape.
\cgalFigureEnd
This method takes as input a point set with unoriented normals and provides as output a set of detected shapes with associated input points.
The output of the algorithm is a set of detected shapes with assigned points and all remaining points not covered by these shapes.
Each input point can be assigned to at most one detected shape.
The shapes are detected via a RANSAC-type approach, that is a random sample consensus. The basic RANSAC approach repeats the following steps:
-# Randomly select samples from the input points;
-# Fit a shape to the selected samples;
-# 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 the highest number of inliers, referred to as the 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 minimum 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 is testing shape candidates against subsets of the input data.
Shape candidates are constructed until the probability to miss the largest candidate is lower than a user-specified threshold.
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 *support* of a shape refers to the footprint of the points covered by the primitive.
To avoid generating shapes with the 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 Shape_detection_RANSACParameters for more details).
\subsection Shape_detection_RANSACParameters 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$.
The parameter `epsilon` defines the absolute maximum tolerance Euclidean distance between a point and a shape.
A high value of `epsilon` leads to the detection of fewer large shapes and hence a less detailed detection.
A low value of `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` enables to detect more primitive shapes that approximate some of the free-form surfaces.
The impact of this parameter is depicted by Figure \cgalFigureRef{Efficient_RANSAC_parameter_epsilon_variation}. Its impact on performance is evaluated in Section \ref Shape_detection_RANSACPerformance.
\cgalFigureBegin{Efficient_RANSAC_parameter_epsilon_variation, Efficient_RANSAC/epsilon_variation2.png}
Impact of the epsilon parameter over the levels of detail 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`:
The Efficient RANSAC uses this parameter to cluster the points into connected components covered by a detected shape.
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.
The 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 the parameter `cluster_epsilon` is depicted in Figure \cgalFigureRef{Efficient_RANSAC_parameter_connectivity}.
\cgalFigureBegin{Efficient_RANSAC_parameter_connectivity, Efficient_RANSAC/varying_connectivity.png}
The 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 of `cluster_epsilon` leads to detecting a single planar shape.
(b) A moderate value of `cluster_epsilon` yields the detection of four squares. Notice that a few points within the squares are not detected as not connected.
(c) A small value of `cluster_epsilon` leads to over-segmentation.
\cgalFigureEnd
- `min_points`:
The 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.
- `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 time due to a higher search endurance.
\subsection Shape_detection_RANSACExamples Examples
The main class `Shape_detection::Efficient_RANSAC` takes a template parameter `Shape_detection::Efficient_RANSAC_traits` that defines the geometric types and input format.
Property maps provide a means to interface with the user-specific data structures.
The first parameter of the `Shape_detection::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::Efficient_RANSAC_traits` class.
The concept behind property maps is detailed in Manual \ref chapterProperty_map "CGAL and Property Maps".
Typical usage consists of five steps:
-# Provide input data via a range iterator;
-# Register shape factories;
-# Choose parameters;
-# Detect;
-# Retrieve detected shapes.
\subsubsection Shape_detection_RANSACExample_basic Basic Plane Detection
The following example reads a point set from a file and detects only planar shapes. The default parameters are used for detection.
\cgalExample{Shape_detection/efficient_RANSAC_basic.cpp}
\subsubsection Shape_detection_RANSACExample_with_callback Plane Detection With Callback
The Efficient RANSAC class provides a callback mechanism that enables the user to track the progress of the algorithm.
It can be used, for example, to terminate the algorithm based on a timeout.
In the following example, the algorithm stops if it takes more than half a second and prints out the progress made.
\cgalExample{Shape_detection/efficient_RANSAC_with_callback.cpp}
\subsubsection Shape_detection_RANSACExample_with_parameters Setting Parameters And Using Different Shape Types
This example illustrates the user selection of parameters using the `Shape_detection::Efficient_RANSAC::Parameters` class.
Shape detection is performed on five shape types (plane, cylinder, sphere, cone, and torus).
The input point set is sampled on a surface mostly composed of piecewise planar and cylindrical parts, in addition to free-form parts.
Basic information of the detected shapes is written to the standard output: if the shape is either a plane or a cylinder,
specific parameters are recovered, otherwise the general method `info()` is used to get the shape parameters in a string object.
Note that specific parameters can be recovered for any of the provided shapes.
\cgalExample{Shape_detection/efficient_RANSAC_with_parameters.cpp}
\subsubsection Shape_detection_RANSACExample_with_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{Shape_detection/efficient_RANSAC_with_point_access.cpp}
\subsubsection Shape_detection_RANSACExample_with_custom_shapes Custom Shapes
Other shape types can be detected by implementing a shape class derived from the class `Shape_detection::Shape_base` and registering it to the shape detection factory of the Efficient RANSAC object.
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 to the shape, and
compute the normal deviation between a query point with the 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 detecting 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::Shape_base`:
- `Shape_detection::Shape_base::minimum_sample_size()` const: Returns the minimum number of required samples.
- `Shape_detection::Shape_base::create_shape(const std::vector<size_t>& indices)`: The randomly generated samples are provided via a vector of indices. `Shape_detection::Shape_base::point``(std::size_t index)`
and `Shape_detection::Shape_base::normal``(std::size_t index)` are used to retrieve the actual points and normals (see the example below).
The provided number of samples might actually be larger than the above minimum number of required samples, depending on the other shape types.
If the provided samples are not sufficient to define a unique shape, for example in a degenerated case, the shape is considered invalid.
- `Shape_detection::Shape_base::squared_distance``(const Point& point)` 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::Shape_base::squared_distance(std::vector<FT>& distances, const std::vector<size_t>& indices)` and
- `Shape_detection::Shape_base::cos_to_normal``(const std::vector<size_t>& indices, std::vector<FT>& angles)` const.
The last two 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 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::Shape_base::point``(std::size_t index)` and `Shape_detection::Shape_base::normal``(std::size_t index)` (see the 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::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 that do not belong 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::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 header shows an implementation of a planar shape primitive,
which is used by the example \ref Shape_detection/efficient_RANSAC_with_custom_shape.cpp.
\cgalExample{Shape_detection/include/efficient_RANSAC_with_custom_shape.h}
\subsection Shape_detection_RANSACPerformance Performance
The running time and detection performance of the Efficient RANSAC depend on the chosen parameters.
A selective error tolerance parameter leads to higher running time and fewer 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 Figure \cgalFigureRef{Efficient_RANSAC_performance_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 time.
We plot the performance against the `probability` parameter (see Figure \cgalFigureRef{Efficient_RANSAC_performance_probability}).
\cgalFigureBegin{Efficient_RANSAC_performance_epsilon, Efficient_RANSAC/epsilon_graph.png}
The graph depicts the number of detected shapes (purple) and the coverage (green), that is the ratio assignedPoints / totalPoints, against the `epsilon` tolerance parameter.
A higher value for `epsilon`, that is a more tolerant error, leads to fewer but larger shapes and shorter running times.
\cgalFigureEnd
\cgalFigureBegin{Efficient_RANSAC_performance_probability, Efficient_RANSAC/prob_graph.png}
The graph depicts the time, coverage, and the number of detected primitives against the search endurance parameter, that is `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 time increases significantly as many more candidates are generated during each iteration of the algorithm.
\cgalFigureEnd
\section Shape_detection_RegionGrowing Region Growing
This shape detection component is based on the region growing algorithm applied to a set of user-specified items.
Shapes are detected by growing regions from seed items, where each region is created as follows:
-# Pick the next available seed item;
-# Find its neighbors in the data set;
-# Include those neighbors, which satisfy the region requirements;
-# Repeat the procedure for all included neighbors;
-# If no further neighbor satisfies the requirements, start a new region.
Together with the generic algorithm's implementation `CGAL::Shape_detection::Region_growing`, three particular instances of this algorithm are provided:
- Line detection in a \ref Shape_detection_RegionGrowingPoints "2D point set";
- Plane detection in a \ref Shape_detection_RegionGrowingPoints "3D point set";
- Plane detection on a \ref Shape_detection_RegionGrowingMesh "polygon mesh".
Other instances can be easily added by the user, as explained below.
\subsection Shape_detection_RegionGrowingFramework Framework
The main class `CGAL::Shape_detection::Region_growing` is parameterized by
- `InputRange` that stores a range of user-defined input items;
- \ref Shape_detection_RegionGrowingFramework_connectivity "NeighborQuery" that provides the means for accessing neighbors of an item;
- \ref Shape_detection_RegionGrowingFramework_conditions "RegionType" that provides the means for validating regions;
- \ref Shape_detection_RegionGrowingFramework_seeding "SeedMap" that defines the seeding order of items.
Using this generic framework, users can grow any type of regions on a set of arbitrary items with
their own propagation and seeding conditions (see \ref Shape_detection_RegionGrowingFramework_examples "an example").
\subsubsection Shape_detection_RegionGrowingFramework_connectivity Neighborhood
The concept `NeighborQuery` provides the means for accessing neighbors of an item.
To create a model that respects this concept, the user has to provide an overload of the operator:
- `NeighborQuery::operator()()` that has to fill a vector with indices of all items, which
are neighbors of the query item.
\subsubsection Shape_detection_RegionGrowingFramework_conditions Regions
The concept `RegionType` provides the means for validating regions. In fact, a model
of this concept maintains a description of the region type that is used in region growing.
To create a model that respect this concept, three functions have to be defined:
- `RegionType::is_part_of_region()` This function checks if an item satisfies
all necessary region requirements and can be added to a region. It is called per item.
- `RegionType::is_valid_region()` This function checks if a region satisfies
all necessary region requirements. It is called per region.
- `RegionType::update()` This utility function enables to update any information,
which is maintained with the region.
\subsubsection Shape_detection_RegionGrowingFramework_seeding Seeding
The `SeedMap` property map, provided as an optional parameter to the main class, enables
to define the seeding order of items that is which items are used first to grow regions from.
Such items are referred to as *seed* items. The `SeedMap` maps the index of an item to its order
number in the overall region growing processing queue. The default map is the identity one
that is the seed index of the item equals to the item's index in the `input_range`.
If it maps to `std::size_t(-1)`, then the corresponding item is skipped.
\subsubsection Shape_detection_RegionGrowingFramework_examples Examples
This toy example shows how to define one's own \ref Shape_detection_RegionGrowingFramework_connectivity "NeighborQuery" and
\ref Shape_detection_RegionGrowingFramework_conditions "RegionType" classes, which are used to parameterize the
`CGAL::Shape_detection::Region_growing`. It also shows how to skip unnecessary items and change their default seeding order.
We choose a simple custom object item. We define four such objects, where for each object, we manually
store indices of its neighbors. The operator `NeighborQuery::operator()()` does nothing but accessing these neighbors.
The `RegionType` class defines the three necessary functions:
- `RegionType::is_part_of_region()` - `true` if the first and second objects are neighbors,
- `RegionType::is_valid_region()` - always `true` after the first call to the function `update()`,
- `RegionType::update()` - updates the internal flag from the default `false` to `true`.
We also define a `SeedMap`, such that the second object is handled first, while the first object follows.
Moreover, the last object is always skipped. Notice that in this example, the container with objects is `std::list`,
which is not a random access. In general, it is much slower, but illustrates that the
generic region growing algorithm can be applied to any type of a container.
The result of using these classes with the region growing main class is that the first two objects form the first region,
the third object forms the second region, and the last object is skipped.
\cgalExample{Shape_detection/region_growing_with_custom_classes.cpp}
\subsection Shape_detection_RegionGrowingPoints Point Set
If one wants to detect lines (see \ref Shape_detection_RegionGrowingPoints_examples "2D Example")
\cgalFigureBegin{Region_growing_on_point_set_2, Region_growing/region_growing_on_point_set_2.png}
A 2D point set depicted with one color per detected line.
\cgalFigureEnd
or planes (see \ref Shape_detection_RegionGrowingPoints_examples "3D Example")
\cgalFigureBegin{Region_growing_on_point_set_3, Region_growing/region_growing_on_point_set_3.png}
A 3D point set depicted with one color per detected plane.
\cgalFigureEnd
in a 2D or 3D point set respectively, this \cgal component provides the corresponding models of the concepts \ref Shape_detection_RegionGrowingFramework_connectivity "NeighborQuery"
and \ref Shape_detection_RegionGrowingFramework_conditions "RegionType". In particular, it provides two different ways to define neighbors of a point:
- Fuzzy sphere neighbors search via `CGAL::Shape_detection::Point_set::Sphere_neighbor_query`.
This class creates a circle (in 2D case) or a sphere (in 3D case) centered at the query point with a user-specified sphere radius.
All points, which belong to the sphere, will be treated as neighbors of the query point;
- Nearest neighbors search via `CGAL::Shape_detection::Point_set::K_neighbor_query`.
This class finds K (specified by the user) nearest neighbors of the query point either 2D or 3D.
The component also provides
- `CGAL::Shape_detection::Point_set::Least_squares_line_fit_region` - least squares line fit type of region for 2D points;
- `CGAL::Shape_detection::Point_set::Least_squares_plane_fit_region` - least squares plane fit type of region for 3D points.
The program associates all points from a region to the best-fit hyperplane (2D line or 3D plane)
and controls the quality of this fit.
The quality of region growing in a point set (2D or 3D) can be improved by slightly sacrificing the running time.
To achieve this, one can sort indices of input points with respect to some quality criteria. These quality criteria
can be included through the \ref Shape_detection_RegionGrowingFramework_seeding "SeedMap" input parameter.
We provide a quality sorting both for 2D and 3D points:
- `CGAL::Shape_detection::Point_set::Least_squares_line_fit_sorting` - indices of 2D input points are sorted with respect
to the quality of the least squares line fit applied to the neighbors of each point;
- `CGAL::Shape_detection::Point_set::Least_squares_plane_fit_sorting` - indices of 3D input points are sorted with respect
to the quality of the least squares plane fit applied to the neighbors of each point.
\subsubsection Shape_detection_RegionGrowingPoints_parameters Parameters
The classes in Section \ref Shape_detection_RegionGrowingPoints "Region Growing On Point Set" depend on a few parameters
that should be defined by the user. They also have default values, but these values do not necessarily guarantee
to produce pleasant results.
The `NeighborQuery` related classes depend on the following parameters:
- `sphere_radius` is used by the class `CGAL::Shape_detection::Point_set::Sphere_neighbor_query`
and defines the radius of the fuzzy search sphere centered at the query point;
- `k` is used by the class `CGAL::Shape_detection::Point_set::K_neighbor_query`
and defines the number K of nearest neighbors of the query point.
The right choice of `sphere_radius` or `k` parameters plays an important role in producing a good result.
For example, if we consider the fuzzy sphere neighborhood, when `sphere_radius` is too large, we have fewer regions, and the details are not clearly separated.
Meanwhile, if `sphere_radius` is too small, we produce more regions, and the point set may be over-segmented.
Consider a 2D map of an intersection of streets in a city as in Figure \cgalFigureRef{Region_growing_parameter_sphere_radius_variation}.
Each region is painted with a unique color. As `sphere_radius` increases, the details become less clear. When `sphere_radius` = 0.3 (c), the best visual result is produced.
\cgalFigureBegin{Region_growing_parameter_sphere_radius_variation, Region_growing/sphere_radius_parameter_2D.png}
(a) Input 2D point set;
(b) 17 regions are found when `sphere_radius` = 0.1;
(c) 8 regions are found when `sphere_radius` = 0.3;
(d) 4 regions are found when `sphere_radius` = 1.2.
\cgalFigureEnd
The `RegionType` related classes depend on the following parameters:
- `distance_threshold` - the maximum distance from a point to a line/plane;
- `angle_threshold` - the maximum accepted angle between the normal associated with a point and the normal of a line/plane;
- `min_region_size` - the minimum number of points a region must have.
The first two parameters are used by the functions `RegionType::is_part_of_region()` and `RegionType::update()`, while the third parameter is used by the function `RegionType::is_valid_region()`
explained in Section \ref Shape_detection_RegionGrowingFramework_conditions "Framework Region Type".
The right choice of `distance_threshold` and `angle_threshold` parameters is also very important.
For example, Figure \cgalFigureRef{Region_growing_parameter_angle_threshold_variation} shows that the roof top of the house can be distinguished as two planes (painted in blue and dark red)
when `angle_threshold` is strict enough (c), or it can be recognized as only one plane (painted in pale yellow) in the other case (b).
\cgalFigureBegin{Region_growing_parameter_angle_threshold_variation, Region_growing/angle_threshold_parameter_3D.png}
(a) Input 3D point cloud;
(b) Result when `angle_threshold` = 60 degrees;
(c) Result when `angle_threshold` = 25 degrees.
\cgalFigureEnd
\subsubsection Shape_detection_RegionGrowingPoints_examples Examples
Typical usage of the Region Growing component consists of five steps:
-# Define an input range with points;
-# Create instances of the classes `NeighborQuery` and `RegionType` with the proper parameters;
-# Create an instance of the class `CGAL::Shape_detection::Region_growing`;
-# Detect;
-# Postprocess.
Given a 2D point set, we detect 2D lines using the fuzzy sphere neighborhood. We then color all points from the found regions and save them in a file (see Figure \cgalFigureRef{Region_growing_on_point_set_2}).
The points with assigned to them normal vectors are stored in `std::vector` and the used `Kernel` is `CGAL::Simple_cartesian`, where the number type is `double`.
\cgalExample{Shape_detection/region_growing_on_point_set_2.cpp}
If we are given a 3D point set, then the example below shows how to detect 3D planes using the K nearest neighbors search. We color all points from the found regions
and save them in a file (see Figure \cgalFigureRef{Region_growing_on_point_set_3}). The example also shows how to retrieve all points, which are not assigned to any region, and how to use a custom output iterator.
The point set with associated normals is stored in `CGAL::Point_set_3` and the used `Kernel` is `CGAL::Exact_predicates_inexact_constructions_kernel`.
\cgalExample{Shape_detection/region_growing_on_point_set_3.cpp}
\subsubsection Shape_detection_RegionGrowingPoints_performance Performance
The main parameter that affects the region growing algorithm on a point set is the neighborhood size at each retrieval (`sphere_radius` or `k`).
Larger neighbor lists are often followed by a smaller number of regions, larger coverage (the ratio between the number of points assigned to regions and the total number of input points),
and longer running time. For example, for a test of about 70k 2D points with the fuzzy sphere neighborhood, the following table is produced:
<table class="markdownTable" align="center">
<tr class="markdownTableHead">
<th class="markdownTableHeadCenter"><code>sphere_radius</code> </th><th class="markdownTableHeadCenter">Time (in seconds) </th><th class="markdownTableHeadCenter">Number of regions </th><th class="markdownTableHeadCenter">Number of assigned points </th></tr>
<tr class="markdownTableBody">
<td class="markdownTableBodyCenter">1 </td><td class="markdownTableBodyCenter">0.138831 </td><td class="markdownTableBodyCenter">794 </td><td class="markdownTableBodyCenter">4483 </td></tr>
<tr class="markdownTableBody">
<td class="markdownTableBodyCenter">3 </td><td class="markdownTableBodyCenter">0.069098 </td><td class="markdownTableBodyCenter">3063 </td><td class="markdownTableBodyCenter">63038 </td></tr>
<tr class="markdownTableBody">
<td class="markdownTableBodyCenter">6 </td><td class="markdownTableBodyCenter">0.077703 </td><td class="markdownTableBodyCenter">2508 </td><td class="markdownTableBodyCenter">64906 </td></tr>
<tr class="markdownTableBody">
<td class="markdownTableBodyCenter">9 </td><td class="markdownTableBodyCenter">0.093415 </td><td class="markdownTableBodyCenter">2302 </td><td class="markdownTableBodyCenter">65334 </td></tr>
</table>
If the neighborhood size is set too low, some points might be isolated, the region size would not reach a critical mass and so will be discarded.
This does not only cause the latency in the program, but also reduces the coverage value, as can be seen when the `sphere_radius = 1`. A typical time measure for a 3D point set with the K nearest neighborhood
and well-defined parameters can be found in the following table:
<table class="markdownTable" align="center">
<tr class="markdownTableHead">
<th class="markdownTableHeadCenter"><code>Number of points</code> </th> <th class="markdownTableHeadCenter">Time (in seconds) </th></tr>
<tr class="markdownTableBody">
<td class="markdownTableBodyCenter">300k </td><td class="markdownTableBodyCenter">0.761617 </td></tr>
<tr class="markdownTableBody">
<td class="markdownTableBodyCenter">600k </td><td class="markdownTableBodyCenter">1.68735 </td></tr>
<tr class="markdownTableBody">
<td class="markdownTableBodyCenter">900k </td><td class="markdownTableBodyCenter">2.80346 </td></tr>
<tr class="markdownTableBody">
<td class="markdownTableBodyCenter">1200k </td><td class="markdownTableBodyCenter">4.06246 </td></tr>
</table>
\subsection Shape_detection_RegionGrowingMesh Polygon Mesh
If one wants to detect planes on a polygon mesh, this \cgal component provides the corresponding models of the concepts
\ref Shape_detection_RegionGrowingFramework_connectivity "NeighborQuery" and \ref Shape_detection_RegionGrowingFramework_conditions "RegionType".
In particular, it has
- `CGAL::Shape_detection::Polygon_mesh::One_ring_neighbor_query` class that retrieves all edge-adjacent faces of a face, and
- `CGAL::Shape_detection::Polygon_mesh::Least_squares_plane_fit_region` class that fits a 3D plane to the vertices of
all mesh faces, which have been added to the region so far, and controls the quality of this fit.
This component accepts any model of the concept `FaceListGraph` as a polygon mesh. A picture below gives an \ref Shape_detection_RegionGrowingMesh_examples "example"
of the region growing algorithm for detecting 3D planes on `CGAL::Surface_mesh`.
\cgalFigureBegin{Region_growing_on_surface_mesh, Region_growing/region_growing_on_polygon_mesh.png}
A surface mesh depicted with one color per detected plane.
\cgalFigureEnd
The quality of region growing on a polygon mesh can be improved by slightly sacrificing the running time. To achieve
this, one can sort indices of input faces with respect to some quality criteria. These quality criteria
can be included in region growing through the \ref Shape_detection_RegionGrowingFramework_seeding "SeedMap"
input parameter. We provide such a quality sorting:
- `CGAL::Shape_detection::Polygon_mesh::Least_squares_plane_fit_sorting` - indices of input faces are sorted with respect
to the quality of the least squares plane fit applied to the neighbors of each face.
\subsubsection Shape_detection_RegionGrowingMesh_parameters Parameters
The `NeighborQuery` related class does not require any parameters, because edge-adjacent faces
are found using the internal face graph connectivity, while the `RegionType` related class depends on three parameters:
- `distance_threshold` - the maximum distance from the furthest face vertex to a plane;
- `angle_threshold` - the maximum accepted angle between the face normal and the normal of a plane;
- `min_region_size` - the minimum number of mesh faces a region must have.
The first two parameters are used by the functions `RegionType::is_part_of_region()` and `RegionType::update()`, while the third parameter is used by the function `RegionType::is_valid_region()`
explained in Section \ref Shape_detection_RegionGrowingFramework_conditions "Framework Regions". The right choice of these parameters is as important as the one explained
in Section \ref Shape_detection_RegionGrowingPoints_parameters "Parameters For Region Growing On Point Set".
\subsubsection Shape_detection_RegionGrowingMesh_examples Examples
In the example below, we show how to use region growing to detect planes on a polygon mesh that can be either stored as `CGAL::Surface_mesh` or `CGAL::Polyhedron_3`.
If it is a surface mesh, this example also provides a way to save the result in a file (see Figure \cgalFigureRef{Region_growing_on_surface_mesh}). The used `Kernel` here is `CGAL::Exact_predicates_exact_constructions_kernel`.
Though this exact kernel provides better quality results, please note that it may significantly slow down the execution of the program, so if you need
a faster version of region growing, use a floating type based kernel.
We can improve the quality of region growing by providing a different seeding order (analogously to \ref Shape_detection_RegionGrowingPoints "Point Sets") that is why in this example we also
sort indices of input faces using the `CGAL::Shape_detection::Polygon_mesh::Least_squares_plane_fit_sorting` and only then detect regions.
\cgalExample{Shape_detection/region_growing_on_polygon_mesh.cpp}
\subsubsection Shape_detection_RegionGrowingMesh_performance Performance
Since accessing neighbors of a face in a polygon mesh is fast, performance of the region growing on a polygon mesh
mostly depends on the `RegionType` model's implementation, which is usually fast, too.
\section Shape_detection_Comparison Comparison
The Efficient RANSAC algorithm is very quick, however, since it is not deterministic, some small shapes might be missed in the detection process.
Instead, the region growing algorithm usually takes longer to complete, but it may provide better quality output in the presence of large scenes
with numerous small details. Since it iterates throughout all items of the scene, there are fewer chances to miss a shape.
In addition, it is deterministic (for a given input and a given set of parameters, it always returns the same output,
whereas the Efficient RANSAC algorithm is randomized and so the output may vary at each run), see Figure \cgalFigureRef{Shape_detection_comparison}.
\cgalFigureBegin{Shape_detection_comparison, Efficient_RANSAC/comparison.png}
Comparison of the Efficient RANSAC and region growing algorithms.
Top: the input point set.
Bottom left: the output of the Efficient RANSAC, \f$78\%\f$ of the shapes are correctly detected in 8 seconds.
Bottom right: the output of the region growing, \f$100\%\f$ of the shapes detected in 15 seconds.
Unassigned points are in black in both output images.
\cgalFigureEnd
\section Shape_detection_PlaneRegularization Plane Regularization
Shape detection applies to man-made scenes or objects such as urban scenes or scans of mechanical parts.
Such scenes often contain a wide range of geometric regularities such as parallelism, orthogonality, or symmetry.
This package offers a function to reinforce four types of regularities for planar shapes, `CGAL::regularize_planes()`:
- Planes that are near *parallel* are made parallel: normal vectors of planes that form angles smaller than a user-defined threshold are made exactly equal;
- Parallel planes that are near *coplanar* are made exactly coplanar;
- Planes that are near *orthogonal* are made exactly orthogonal;
- Planes that are near *symmetrical* with respect to a user-defined axis are made exactly symmetrical.
The user can choose to regularize only one or several of these four properties (see \link CGAL::regularize_planes() Reference Manual\endlink).
The process is greedy and based on a hierarchical decomposition (coplanar clusters are subgroups of parallel clusters,
which are subgroups of axis-symmetric and orthogonal clusters) as described by Verdie et al. \cgalCite{cgal:vla-lod-15}
\cgalExample{Shape_detection/efficient_RANSAC_and_plane_regularization.cpp}
\section Shape_detection_DeprecatedComponents Deprecated Components
The new version (see all examples above) of the class `CGAL::Shape_detection::Region_growing`
is not compatible with the old API. For the old API, see an example below and use
`CGAL::Shape_detection::deprecated::Region_growing_depr` instead.
\cgalExample{Shape_detection/shape_detection_basic_deprecated.cpp}
The old API is still supported, but will be removed in the next releases.
In particular, the classes `CGAL::Shape_detection::deprecated::Region_growing_depr` and
`CGAL::Shape_detection::deprecated::Shape_detection_traits` will be removed.
Please update your code.
\section Shape_detection_History History
The Efficient RANSAC component was developed by Sven Oesau based on the prototype version created by Yannick Verdie,
with the help of Clément Jamin and under the supervision of Pierre Alliez. Plane regularization was added by Simon Giraudot
based on the prototype version developed by Florent Lafarge.
The region growing algorithm on a 3D point set was first implemented by Simon Giraudot based on the prototype version developed by Florent Lafarge
and then generalized to arbitrary items including versions for a 2D point set, a 3D point set, and a polygon mesh by Thien Hoang during the
Google Summer of Code 2018 under the supervision of Dmitry Anisimov.
\section Shape_detection_Acknowledgments Acknowledgments
The authors wish to thank our reviewers Andreas Fabri, Sébastien Loriot, and Simon Giraudot
for helpful comments and discussions.
*/
} /* namespace CGAL */