mirror of https://github.com/CGAL/cgal
added a region growing example with an extra paragraph in docs
This commit is contained in:
parent
53fd5feaf5
commit
5220b7d260
|
|
@ -153,11 +153,14 @@ sharp edges in the final model.
|
||||||
|
|
||||||
\subsection subsecExampleNoInputPlane Reconstruction from Point Clouds
|
\subsection subsecExampleNoInputPlane Reconstruction from Point Clouds
|
||||||
|
|
||||||
The method assumes that all necessary planes can be extracted from the input point set. The following example
|
The method assumes that all necessary planes can be extracted from the input point set. The following examples
|
||||||
first extracts planes from the input point cloud and then reconstructs the surface model.
|
first extract planes from the input point cloud and then reconstruct the surface model. In the first example,
|
||||||
|
the \ref Shape_detection_RANSAC "Efficient RANSAC approach" to extract planes is used. It is very fast,
|
||||||
|
but not deterministic, oppose to the \ref Shape_detection_RegionGrowing "region growing approach" from the second example
|
||||||
|
that is slower, but more precise and always returns the same result for the same given parameters.
|
||||||
|
|
||||||
\cgalExample{Polygonal_surface_reconstruction/polyfit_example_without_input_planes.cpp}
|
\cgalExample{Polygonal_surface_reconstruction/polyfit_example_without_input_planes.cpp}
|
||||||
|
\cgalExample{Polygonal_surface_reconstruction/polyfit_example_with_region_growing.cpp}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -201,7 +204,7 @@ may result in an extremely large number of candidate faces, and thus a huge inte
|
||||||
to be solved.
|
to be solved.
|
||||||
|
|
||||||
The reconstruction time of a single object with moderate complexity is typically within a few seconds.
|
The reconstruction time of a single object with moderate complexity is typically within a few seconds.
|
||||||
Among the three steps, the face selection step deminates the reconstruction pipeline when the number
|
Among the three steps, the face selection step dominates the reconstruction pipeline when the number
|
||||||
of candidate faces is large (e.g., more than 5,000).
|
of candidate faces is large (e.g., more than 5,000).
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,3 +6,4 @@ Alpha_shapes_2
|
||||||
Surface_mesh
|
Surface_mesh
|
||||||
Point_set_processing_3
|
Point_set_processing_3
|
||||||
Solver_interface
|
Solver_interface
|
||||||
|
Shape_detection
|
||||||
|
|
|
||||||
|
|
@ -2,4 +2,5 @@
|
||||||
\example Polygonal_surface_reconstruction/polyfit_example_without_input_planes.cpp
|
\example Polygonal_surface_reconstruction/polyfit_example_without_input_planes.cpp
|
||||||
\example Polygonal_surface_reconstruction/polyfit_example_user_provided_planes.cpp
|
\example Polygonal_surface_reconstruction/polyfit_example_user_provided_planes.cpp
|
||||||
\example Polygonal_surface_reconstruction/polyfit_example_model_complexty_control.cpp
|
\example Polygonal_surface_reconstruction/polyfit_example_model_complexty_control.cpp
|
||||||
|
\example Polygonal_surface_reconstruction/polyfit_example_with_region_growing.cpp
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -39,6 +39,7 @@ include( CGAL_CreateSingleSourceCGALProgram )
|
||||||
create_single_source_cgal_program( "polyfit_example_without_input_planes.cpp" )
|
create_single_source_cgal_program( "polyfit_example_without_input_planes.cpp" )
|
||||||
create_single_source_cgal_program( "polyfit_example_user_provided_planes.cpp" )
|
create_single_source_cgal_program( "polyfit_example_user_provided_planes.cpp" )
|
||||||
create_single_source_cgal_program( "polyfit_example_model_complexty_control.cpp" )
|
create_single_source_cgal_program( "polyfit_example_model_complexty_control.cpp" )
|
||||||
|
create_single_source_cgal_program( "polyfit_example_with_region_growing.cpp" )
|
||||||
|
|
||||||
find_package(Eigen3 3.1.0) #(requires 3.1.0 or greater)
|
find_package(Eigen3 3.1.0) #(requires 3.1.0 or greater)
|
||||||
if(EIGEN3_FOUND)
|
if(EIGEN3_FOUND)
|
||||||
|
|
@ -63,6 +64,8 @@ if(EIGEN3_FOUND)
|
||||||
target_compile_definitions(polyfit_example_user_provided_planes PRIVATE -DCGAL_USE_GLPK)
|
target_compile_definitions(polyfit_example_user_provided_planes PRIVATE -DCGAL_USE_GLPK)
|
||||||
target_link_libraries( polyfit_example_model_complexty_control PRIVATE ${GLPK_LIBRARIES} )
|
target_link_libraries( polyfit_example_model_complexty_control PRIVATE ${GLPK_LIBRARIES} )
|
||||||
target_compile_definitions(polyfit_example_model_complexty_control PRIVATE -DCGAL_USE_GLPK)
|
target_compile_definitions(polyfit_example_model_complexty_control PRIVATE -DCGAL_USE_GLPK)
|
||||||
|
target_link_libraries( polyfit_example_with_region_growing PRIVATE ${GLPK_LIBRARIES} )
|
||||||
|
target_compile_definitions(polyfit_example_with_region_growing PRIVATE -DCGAL_USE_GLPK)
|
||||||
|
|
||||||
message("GLPK found and used")
|
message("GLPK found and used")
|
||||||
|
|
||||||
|
|
@ -78,6 +81,8 @@ if(EIGEN3_FOUND)
|
||||||
target_compile_definitions(polyfit_example_user_provided_planes PRIVATE -DCGAL_USE_SCIP)
|
target_compile_definitions(polyfit_example_user_provided_planes PRIVATE -DCGAL_USE_SCIP)
|
||||||
target_link_libraries( polyfit_example_model_complexty_control PRIVATE ${SCIP_LIBRARIES} )
|
target_link_libraries( polyfit_example_model_complexty_control PRIVATE ${SCIP_LIBRARIES} )
|
||||||
target_compile_definitions(polyfit_example_model_complexty_control PRIVATE -DCGAL_USE_SCIP)
|
target_compile_definitions(polyfit_example_model_complexty_control PRIVATE -DCGAL_USE_SCIP)
|
||||||
|
target_link_libraries( polyfit_example_with_region_growing PRIVATE ${SCIP_LIBRARIES} )
|
||||||
|
target_compile_definitions(polyfit_example_with_region_growing PRIVATE -DCGAL_USE_SCIP)
|
||||||
|
|
||||||
message("SCIP found and used")
|
message("SCIP found and used")
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,199 @@
|
||||||
|
#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
|
||||||
|
#include <CGAL/IO/read_xyz_points.h>
|
||||||
|
#include <CGAL/IO/Writer_OFF.h>
|
||||||
|
#include <CGAL/property_map.h>
|
||||||
|
#include <CGAL/Surface_mesh.h>
|
||||||
|
#include <CGAL/Shape_detection/Region_growing/Region_growing.h>
|
||||||
|
#include <CGAL/Shape_detection/Region_growing/Region_growing_on_point_set.h>
|
||||||
|
#include <CGAL/Polygonal_surface_reconstruction.h>
|
||||||
|
|
||||||
|
#ifdef CGAL_USE_SCIP
|
||||||
|
|
||||||
|
#include <CGAL/SCIP_mixed_integer_program_traits.h>
|
||||||
|
typedef CGAL::SCIP_mixed_integer_program_traits<double> MIP_Solver;
|
||||||
|
|
||||||
|
#elif defined(CGAL_USE_GLPK)
|
||||||
|
|
||||||
|
#include <CGAL/GLPK_mixed_integer_program_traits.h>
|
||||||
|
typedef CGAL::GLPK_mixed_integer_program_traits<double> MIP_Solver;
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(CGAL_USE_GLPK) || defined(CGAL_USE_SCIP)
|
||||||
|
|
||||||
|
#include <fstream>
|
||||||
|
#include <CGAL/Timer.h>
|
||||||
|
|
||||||
|
typedef CGAL::Exact_predicates_inexact_constructions_kernel Kernel;
|
||||||
|
|
||||||
|
typedef Kernel::FT FT;
|
||||||
|
typedef Kernel::Point_3 Point;
|
||||||
|
typedef Kernel::Vector_3 Vector;
|
||||||
|
|
||||||
|
// Point with normal, and plane index.
|
||||||
|
typedef boost::tuple<Point, Vector, int> PNI;
|
||||||
|
typedef std::vector<PNI> Point_vector;
|
||||||
|
|
||||||
|
typedef CGAL::Nth_of_tuple_property_map<0, PNI> Point_map;
|
||||||
|
typedef CGAL::Nth_of_tuple_property_map<1, PNI> Normal_map;
|
||||||
|
typedef CGAL::Nth_of_tuple_property_map<2, PNI> Plane_index_map;
|
||||||
|
|
||||||
|
typedef CGAL::Shape_detection::Point_set::
|
||||||
|
Sphere_neighbor_query<Kernel, Point_vector, Point_map> Neighbor_query;
|
||||||
|
typedef CGAL::Shape_detection::Point_set::
|
||||||
|
Least_squares_plane_fit_region<Kernel, Point_vector, Point_map, Normal_map> Region_type;
|
||||||
|
typedef CGAL::Shape_detection::
|
||||||
|
Region_growing<Point_vector, Neighbor_query, Region_type> Region_growing;
|
||||||
|
|
||||||
|
typedef CGAL::Surface_mesh<Point> Surface_mesh;
|
||||||
|
typedef CGAL::Polygonal_surface_reconstruction<Kernel> Polygonal_surface_reconstruction;
|
||||||
|
|
||||||
|
class Index_map {
|
||||||
|
|
||||||
|
public:
|
||||||
|
using key_type = std::size_t;
|
||||||
|
using value_type = int;
|
||||||
|
using reference = value_type;
|
||||||
|
using category = boost::readable_property_map_tag;
|
||||||
|
|
||||||
|
Index_map() { }
|
||||||
|
template<typename PointRange>
|
||||||
|
Index_map(
|
||||||
|
const PointRange& points,
|
||||||
|
const std::vector< std::vector<std::size_t> >& regions) :
|
||||||
|
m_indices(new std::vector<int>(points.size(), -1)) {
|
||||||
|
|
||||||
|
for (std::size_t i = 0; i < regions.size(); ++i)
|
||||||
|
for (const std::size_t idx : regions[i])
|
||||||
|
(*m_indices)[idx] = static_cast<int>(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline friend value_type get(
|
||||||
|
const Index_map& index_map,
|
||||||
|
const key_type key) {
|
||||||
|
|
||||||
|
const auto& indices = *(index_map.m_indices);
|
||||||
|
return indices[key];
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::shared_ptr< std::vector<int> > m_indices;
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This example first extracts planes from the input point cloud
|
||||||
|
* (using region growing) and then reconstructs
|
||||||
|
* the surface model from the planes.
|
||||||
|
*/
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
Point_vector points;
|
||||||
|
|
||||||
|
// Load point set from a file.
|
||||||
|
const std::string input_file("data/cube.pwn");
|
||||||
|
std::ifstream input_stream(input_file.c_str());
|
||||||
|
if (input_stream.fail()) {
|
||||||
|
std::cerr << "Failed open file \'" << input_file << "\'" << std::endl;
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
std::cout << "Loading point cloud: " << input_file << "...";
|
||||||
|
|
||||||
|
CGAL::Timer t;
|
||||||
|
t.start();
|
||||||
|
if (!input_stream ||
|
||||||
|
!CGAL::read_xyz_points(input_stream,
|
||||||
|
std::back_inserter(points),
|
||||||
|
CGAL::parameters::point_map(Point_map()).normal_map(Normal_map()))) {
|
||||||
|
|
||||||
|
std::cerr << "Error: cannot read file " << input_file << std::endl;
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
std::cout << " Done. " << points.size() << " points. Time: "
|
||||||
|
<< t.time() << " sec." << std::endl;
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
// Shape detection.
|
||||||
|
|
||||||
|
// Default parameter values for the data file cube.pwn.
|
||||||
|
const FT search_sphere_radius = FT(2) / FT(100);
|
||||||
|
const FT max_distance_to_plane = FT(2) / FT(1000);
|
||||||
|
const FT max_accepted_angle = FT(25);
|
||||||
|
const std::size_t min_region_size = 200;
|
||||||
|
|
||||||
|
// Create instances of the classes Neighbor_query and Region_type.
|
||||||
|
Neighbor_query neighbor_query(
|
||||||
|
points,
|
||||||
|
search_sphere_radius);
|
||||||
|
|
||||||
|
Region_type region_type(
|
||||||
|
points,
|
||||||
|
max_distance_to_plane, max_accepted_angle, min_region_size);
|
||||||
|
|
||||||
|
// Create an instance of the region growing class.
|
||||||
|
Region_growing region_growing(
|
||||||
|
points, neighbor_query, region_type);
|
||||||
|
|
||||||
|
std::cout << "Extracting planes...";
|
||||||
|
std::vector< std::vector<std::size_t> > regions;
|
||||||
|
t.reset();
|
||||||
|
region_growing.detect(std::back_inserter(regions));
|
||||||
|
std::cout << " Done. " << regions.size() << " planes extracted. Time: "
|
||||||
|
<< t.time() << " sec." << std::endl;
|
||||||
|
|
||||||
|
// Stores the plane index of each point as the third element of the tuple.
|
||||||
|
Index_map index_map(points, regions);
|
||||||
|
for (std::size_t i = 0; i < points.size(); ++i) {
|
||||||
|
// Uses the get function from the property map that accesses the 3rd element of the tuple.
|
||||||
|
const int plane_index = get(index_map, i);
|
||||||
|
points[i].get<2>() = plane_index;
|
||||||
|
}
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
// Reconstruction.
|
||||||
|
|
||||||
|
std::cout << "Generating candidate faces...";
|
||||||
|
t.reset();
|
||||||
|
Polygonal_surface_reconstruction algo(
|
||||||
|
points,
|
||||||
|
Point_map(),
|
||||||
|
Normal_map(),
|
||||||
|
Plane_index_map()
|
||||||
|
);
|
||||||
|
std::cout << " Done. Time: " << t.time() << " sec." << std::endl;
|
||||||
|
|
||||||
|
Surface_mesh model;
|
||||||
|
std::cout << "Reconstructing...";
|
||||||
|
t.reset();
|
||||||
|
if (!algo.reconstruct<MIP_Solver>(model)) {
|
||||||
|
std::cerr << "Failed: " << algo.error_message() << std::endl;
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
std::cout << " Done. Time: " << t.time() << " sec." << std::endl;
|
||||||
|
|
||||||
|
std::cout << "Saving...";
|
||||||
|
t.reset();
|
||||||
|
const std::string& output_file("data/cube_result.off");
|
||||||
|
std::ofstream output_stream(output_file.c_str());
|
||||||
|
if (output_stream && CGAL::write_off(output_stream, model))
|
||||||
|
std::cout << " Done. Saved to " << output_file << ". Time: " << t.time() << " sec." << std::endl;
|
||||||
|
else {
|
||||||
|
std::cerr << " Failed saving file." << std::endl;
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
int main(int, char**)
|
||||||
|
{
|
||||||
|
std::cerr << "This test requires either GLPK or SCIP.\n";
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // defined(CGAL_USE_GLPK) || defined(CGAL_USE_SCIP)
|
||||||
Loading…
Reference in New Issue