ReUseX  0.0.1
3D Point Cloud Processing for Building Reuse
Loading...
Searching...
No Matches
CellComplexFaceCoverage.hpp
Go to the documentation of this file.
1// SPDX-FileCopyrightText: 2025 Povl Filip Sonne-Frederiksen
2//
3// SPDX-License-Identifier: GPL-3.0-or-later
4
5#pragma once
8#include <spdmon/spdmon.hpp>
9
10#include <CGAL/Boolean_set_operations_2.h>
11#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
12#include <CGAL/Polygon_2.h>
13#include <CGAL/Polygon_2_algorithms.h>
14#include <CGAL/circulator.h>
15
16#include <range/v3/range/concepts.hpp>
17#include <range/v3/to_container.hpp>
18#include <range/v3/view/filter.hpp>
19#include <range/v3/view/transform.hpp>
20#include <range/v3/view/zip.hpp>
21
22template <class Kernel>
23static double
24compute_grid_coverage(typename CGAL::Polygon_2<Kernel> const &polygon,
25 std::vector<typename Kernel::Point_2> const &points,
26 const double cell_size = 0.2) {
27 // Bounding box
28 double min_x = polygon[0].x(), max_x = polygon[0].x();
29 double min_y = polygon[0].y(), max_y = polygon[0].y();
30 for (auto &p : polygon) {
31 min_x = std::min(min_x, p.x());
32 max_x = std::max(max_x, p.x());
33 min_y = std::min(min_y, p.y());
34 max_y = std::max(max_y, p.y());
35 }
36
37 int nx = static_cast<int>(std::ceil((max_x - min_x) / cell_size));
38 int ny = static_cast<int>(std::ceil((max_y - min_y) / cell_size));
39
40 int covered_cells = 0;
41 int total_cells = 0;
42
43 for (int i = 0; i < nx; ++i) {
44 for (int j = 0; j < ny; ++j) {
45 const double cx = min_x + (i + 0.5) * cell_size;
46 const double cy = min_y + (j + 0.5) * cell_size;
47 typename Kernel::Point_2 cell_center(cx, cy);
48 if (polygon.bounded_side(cell_center) == CGAL::ON_UNBOUNDED_SIDE)
49 continue;
50
51 ++total_cells;
52 for (auto &pt : points) {
53 if (std::abs(pt.x() - cx) <= cell_size / 2 &&
54 std::abs(pt.y() - cy) <= cell_size / 2) {
55 ++covered_cells;
56 break;
57 }
58 }
59 }
60 }
61 if (total_cells == 0)
62 return 0.0;
63
64 return static_cast<double>(covered_cells) / total_cells;
65}
66namespace ReUseX::geometry {
67template <typename PointT>
68auto CellComplex::compute_face_coverage(pcl::PointCloud<PointT>::ConstPtr cloud,
70 std::vector<pcl::IndicesPtr> &inliers,
71 const double grid_size) -> void {
72
73 using EPICK = CGAL::Exact_predicates_inexact_constructions_kernel;
74
75 using Point_3 = EPICK::Point_3;
76 using Plane_3 = EPICK::Plane_3;
77
78 using Polygon_2 = CGAL::Polygon_2<EPICK>;
79 using Point_2 = Polygon_2::Point_2;
80
81 // INFO: Compute face probabilities
82 spdlog::trace("calling compute_face_coverage");
83 auto f_sp =
84 this->add_property_map<Vertex, double>("f:support_probability").first;
85
86 {
87 auto logger =
88 spdmon::LoggerProgress("Computing face coverage", this->num_faces());
89
90 std::vector<ReUseX::geometry::CellComplex::Vertex> face_list;
91 face_list.reserve(this->num_faces());
92 for (auto fit = this->faces_begin(); fit != this->faces_end(); ++fit)
93 face_list.push_back(*fit);
94
95#pragma omp parallel for schedule(dynamic)
96 for (size_t face_idx = 0; face_idx < face_list.size(); ++face_idx) {
97 auto fit = face_list[face_idx];
98
99 // for (size_t face_idx = 0; face_idx < this->num_faces(); ++face_idx) {
100 // auto fit = this->faces_begin();
101
102 const int plane_id = std::get<FaceData>((*this)[fit].data).plane_id;
103
104 auto get_comverage = [&](const int id) {
105 // if (id < 0) {
106 // spdlog::warn("Face {} has no associated plane",
107 // (*this)[*fit].id); f_sp[*fit] = -1.0;
108 // ++logger;
109 // continue;
110 // }
111
112 const auto plane_vec = planes[id];
113 const auto indices = inliers[id];
114
115 const Plane_3 plane(plane_vec[0], plane_vec[1], plane_vec[2],
116 plane_vec[3]);
117
118 if (indices->empty())
119 return 0.0;
120
121 Polygon_2 polygon{};
122 std::transform(this->vertices_begin(fit), this->vertices_end(fit),
123 std::back_inserter(polygon), [&](Vertex v) {
124 const auto pos = (*this)[v].pos;
125 return plane.to_2d(Point_3(pos[0], pos[1], pos[2]));
126 });
127
128 // if (!polygon.is_simple()) {
129 // spdlog::warn("Face {} polygon not is simple",
130 // (*this)[fit].id); f_sp[fit] = -1.0;
131 // ++logger;
132 // continue;
133 // }
134
135 const auto inliers = *indices | ranges::views::transform([&](int idx) {
136 const PointT &p = cloud->points[idx];
137 return plane.to_2d(Point_3(p.x, p.y, p.z));
138 }) | ranges::views::filter([&](const Point_2 &p) {
139 return polygon.bounded_side(p) == CGAL::ON_BOUNDED_SIDE;
140 }) | ranges::to<std::vector>();
141
142 if (inliers.empty())
143 return 0.0;
144
145 return compute_grid_coverage<EPICK>(polygon, inliers, grid_size);
146 };
147
148 auto val = get_comverage(plane_id);
149
150#pragma omp critical
151 f_sp[fit] = val;
152
153 ++logger;
154 }
155 }
156}
157} // namespace ReUseX::geometry
static double compute_grid_coverage(typename CGAL::Polygon_2< Kernel > const &polygon, std::vector< typename Kernel::Point_2 > const &points, const double cell_size=0.2)
auto faces_end() const -> FaceIterator
auto vertices_begin() const -> VertexIterator
boost::graph_traits< Graph >::vertex_descriptor Vertex
auto faces_begin() const -> FaceIterator
auto compute_face_coverage(pcl::PointCloud< PointT >::ConstPtr cloud, EigenVectorContainer< double, 4 > &planes, std::vector< pcl::IndicesPtr > &inliers, const double grid_size=0.2) -> void
auto vertices_end() const -> VertexIterator
std::pair< boost::associative_property_map< std::map< Key, T > >, bool > add_property_map(const std::string &name)
Definition Registry.hpp:27
std::vector< Eigen::Matrix< Scalar, Rows, 1 >, Eigen::aligned_allocator< Eigen::Matrix< Scalar, Rows, 1 > > > EigenVectorContainer
Definition types.hpp:38
pcl::PointXYZRGB PointT
Definition types.hpp:12