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