ReUseX  0.0.1
3D Point Cloud Processing for Building Reuse
Loading...
Searching...
No Matches
CellComplexRoomProbabilites.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#include "spdmon/spdmon.hpp"
6
7#include <embree4/rtcore.h>
8
9#include <pcl/filters/uniform_sampling.h>
10
11#include <fmt/color.h>
12#include <fmt/core.h>
13#include <fmt/ranges.h>
14
15#include <Eigen/Core>
16
17#include <cmath>
18#include <vector>
19
20// Spherical Fibonacci
21static std::vector<Eigen::Vector3d> sampleSphericalFibonacci(size_t N) {
22 std::vector<Eigen::Vector3d> out;
23 out.reserve(N);
24 if (N == 0)
25 return out;
26 constexpr double golden_angle =
27 M_PI * (3.0 - std::sqrt(5.0)); // ~2.3999632297
28
29 for (size_t i = 0; i < N; ++i) {
30 double t = (double)i;
31 double z = 1.0 - 2.0 * t / (double)(N - 1); // maps to [1, -1] if N>1
32 double phi = golden_angle * t;
33 double r = std::sqrt(std::max(0.0, 1.0 - z * z));
34 double x = std::cos(phi) * r;
35 double y = std::sin(phi) * r;
36 out.emplace_back(x, y, z);
37 }
38 return out;
39}
40
41namespace ReUseX::geometry {
42template <typename PointT, typename PointN, typename PointL>
44 pcl::PointCloud<PointT>::ConstPtr cloud_,
45 pcl::PointCloud<PointN>::ConstPtr normals_,
46 pcl::PointCloud<PointL>::ConstPtr labels_, const double grid_size) -> void {
47
48 using RTCVertex = struct RTCVertex {
49 float x, y, z, r; // x, y, z coordinates and radius
50 };
51
52 using RTCNormal = struct RTCNormal {
53 float x, y, z; // x, y, z components of the normal vector
54 };
55
56 using RTCData = struct RTCData {
57 size_t label_index;
58 };
59
60 spdlog::trace("calling compute_room_probabilities");
61
62 assert(cloud_->size() == labels_->size() &&
63 "Point cloud and label cloud must have the same size");
64 assert(cloud_->size() == normals_->size() &&
65 "Point cloud and normal cloud must have the same size");
66
67 const double radius_ = grid_size * M_SQRT2;
68
69 spdlog::debug(
70 "Using ray tracing for {} points with grid size {:.3} and radius {:.3}",
71 cloud_->size(), grid_size, radius_);
72
74 "c:room_probabilities")
75 .first;
76
77 // Get unique labels
78 std::vector<unsigned int> labels;
79 labels.reserve(labels_->points.size());
80
81 for (const auto &p : labels_->points)
82 if (p.label != static_cast<unsigned int>(-1))
83 labels.push_back(p.label);
84
85 std::sort(labels.begin(), labels.end());
86 labels.erase(std::unique(labels.begin(), labels.end()), labels.end());
87 spdlog::debug("Room labels: {}", fmt::join(labels, ", "));
88
89 for (auto cit = this->cells_begin(); cit != this->cells_end(); ++cit)
90 c_rp[*cit] = std::vector<double>(labels.size() + 1, 0.0);
91 this->n_rooms = labels.size();
92 spdlog::debug("Number of rooms (labels): {}", labels.size());
93
94 unsigned int old_mxcsr = _mm_getcsr(); // save current flagsq
95 _MM_SET_FLUSH_ZERO_MODE(_MM_FLUSH_ZERO_ON);
96 _MM_SET_DENORMALS_ZERO_MODE(_MM_DENORMALS_ZERO_ON);
97
98 spdlog::trace("Creating device and scene for ray tracing");
99 RTCDevice device_ = rtcNewDevice("verbose=0"); // 0-3
100 RTCScene scene_ = rtcNewScene(device_);
101 rtcSetSceneFlags(scene_, RTC_SCENE_FLAG_ROBUST);
102 rtcSetSceneBuildQuality(scene_, RTC_BUILD_QUALITY_HIGH);
103 assert(device_ != nullptr && "Error creating Embree device");
104 assert(scene_ != nullptr && "Error creating Embree scene");
105 rtcSetDeviceErrorFunction(
106 device_,
107 [](void *, RTCError err, const char *str) {
108 spdlog::error("Embree error {}:", str);
109 switch (err) {
110 case RTC_ERROR_NONE:
111 break;
112 case RTC_ERROR_UNKNOWN:
113 throw std::runtime_error("Embree: An unknown error has occurred.");
114 break;
115 case RTC_ERROR_INVALID_ARGUMENT:
116 throw std::runtime_error(
117 "Embree: An invalid argument was specified.");
118 break;
119 case RTC_ERROR_INVALID_OPERATION:
120 throw std::runtime_error(
121 "Embree: The operation is not allowed for the specified object.");
122 break;
123 case RTC_ERROR_OUT_OF_MEMORY:
124 throw std::runtime_error("Embree: There is not enough memory left to "
125 "complete the operation.");
126 break;
127 case RTC_ERROR_UNSUPPORTED_CPU:
128 throw std::runtime_error(
129 "Embree: The CPU is not supported as it does not support SSE2.");
130 break;
131 case RTC_ERROR_CANCELLED:
132 throw std::runtime_error(
133 "Embree: The operation got cancelled by an Memory Monitor "
134 "Callback or Progress Monitor Callback function.");
135 break;
136 default:
137 throw std::runtime_error("Embree: An invalid error has occurred.");
138 break;
139 }
140 },
141 nullptr);
142
143 // Intersect with ground plane to get line/ INFO: Create Embree scene
144 spdlog::trace("Creating scene geometry for ray tracing");
145
146 pcl::UniformSampling<PointT> us;
147 us.setInputCloud(cloud_);
148 us.setRadiusSearch(grid_size);
149
150 using RTCDataPtr = std::shared_ptr<RTCData>;
151 std::vector<RTCDataPtr> rtc_data_vec{}; // To keep data alive
152 for (size_t i = 0; i < labels.size(); ++i) {
153
154 pcl::IndicesPtr indices(new pcl::Indices);
155 for (size_t j = 0; j < cloud_->size(); ++j)
156 if (labels_->points[j].label == labels[i])
157 indices->push_back(static_cast<int>(j));
158
159 spdlog::trace("Room label {}: {} points", labels[i], indices->size());
160
161 // size_t n_points_before = indices->size();
162 us.setIndices(indices);
163 us.filter(*indices);
164 // spdlog::trace(
165 // "Label {}: Reduced from {} to {} points after uniform sampling",
166 // labels[i], n_points_before, indices->size());
167
168 RTCGeometry geometry_ =
169 rtcNewGeometry(device_, RTC_GEOMETRY_TYPE_ORIENTED_DISC_POINT);
170
171 RTCVertex *vb = (RTCVertex *)rtcSetNewGeometryBuffer(
172 geometry_, RTC_BUFFER_TYPE_VERTEX, 0, RTC_FORMAT_FLOAT4,
173 sizeof(RTCVertex), indices->size());
174
175 RTCNormal *nb = (RTCNormal *)rtcSetNewGeometryBuffer(
176 geometry_, RTC_BUFFER_TYPE_NORMAL, 0, RTC_FORMAT_FLOAT3,
177 sizeof(RTCNormal), indices->size());
178
179 for (size_t i = 0; i < indices->size(); ++i) {
180 const auto &p = cloud_->points[indices->at(i)];
181 const auto &n = normals_->points[indices->at(i)];
182
183 vb[i].x = static_cast<float>(p.x);
184 vb[i].y = static_cast<float>(p.y);
185 vb[i].z = static_cast<float>(p.z);
186 vb[i].r = static_cast<float>(radius_) * 1.05f;
187
188 nb[i].x = static_cast<float>(n.normal_x);
189 nb[i].y = static_cast<float>(n.normal_y);
190 nb[i].z = static_cast<float>(n.normal_z);
191 }
192
193 rtc_data_vec.emplace_back(new RTCData{i});
194 rtcSetGeometryUserData(geometry_, rtc_data_vec.back().get());
195
196 rtcCommitGeometry(geometry_);
197 rtcAttachGeometry(scene_, geometry_);
198 rtcReleaseGeometry(geometry_);
199 }
200
201 spdlog::trace("Committing scene");
202 rtcCommitScene(scene_);
203
204 {
205 const auto dirs = sampleSphericalFibonacci(100);
206 //
207 auto logger = spdmon::LoggerProgress("Computing room probabilities",
208 this->num_cells());
209
210#pragma omp parallel for schedule(dynamic)
211 for (size_t cell_idx = 0; cell_idx < this->num_cells(); ++cell_idx) {
212 auto cit = this->cells_begin();
213 std::advance(cit, cell_idx);
214 // const size_t idx = (*this)[*cit].id;
215 // For each cell create n random rays
216 // Count the number of intersections per id nad normalize
217
218 std::vector<double> local_accum(c_rp[*cit].size(), 0.0);
219 double *accum_ptr = local_accum.data();
220 const int N = static_cast<int>(c_rp[*cit].size());
221
222#pragma omp parallel for reduction(+ : accum_ptr[ : N])
223 for (size_t i = 0; i < dirs.size(); ++i) {
224 const auto dir = dirs[i];
225 auto c = (*this)[*cit].pos;
226
227 RTCRayHit rayhit;
228 rayhit.ray.org_x = static_cast<float>(c.x());
229 rayhit.ray.org_y = static_cast<float>(c.y());
230 rayhit.ray.org_z = static_cast<float>(c.z());
231 rayhit.ray.dir_x = static_cast<float>(dir.x());
232 rayhit.ray.dir_y = static_cast<float>(dir.y());
233 rayhit.ray.dir_z = static_cast<float>(dir.z());
234
235 rayhit.ray.tnear = 0.001f; // Start a bit away from the origin
236 rayhit.ray.tfar = std::numeric_limits<float>::infinity();
237 rayhit.hit.geomID = RTC_INVALID_GEOMETRY_ID;
238
239 rayhit.ray.mask = -1;
240 rayhit.ray.flags = 0;
241 rayhit.hit.instID[0] = RTC_INVALID_GEOMETRY_ID;
242
243 rtcIntersect1(scene_, &rayhit);
244
245 if (rayhit.hit.geomID == RTC_INVALID_GEOMETRY_ID) { // No hit
246 accum_ptr[0] += 1;
247 // spdlog::trace(fmt::format(fmt::fg(fmt::color::red), "No hit"));
248 continue;
249 }
250
251 // Check if backside
252 const auto normal =
253 Eigen::Vector3f(rayhit.hit.Ng_x, rayhit.hit.Ng_y, rayhit.hit.Ng_z)
254 .normalized();
255 const Eigen::Vector3f dir_vec(dir.x(), dir.y(), dir.z());
256 if (normal.dot(dir_vec) > 0) {
257 accum_ptr[0] += 1;
258 // spdlog::trace(
259 // fmt::format(fmt::fg(fmt::color::yellow), "Backside hit"));
260 continue; // Backside
261 }
262 // spdlog::trace(fmt::format(fmt::fg(fmt::color::green), "Frontside
263 // hit"));
264
265 auto geometry = rtcGetGeometry(scene_, rayhit.hit.geomID);
266 rtcGetGeometryUserData(geometry);
267 RTCData *data = (RTCData *)rtcGetGeometryUserData(geometry);
268
269 // const auto label = labels[data->label_index];
270 // spdlog::trace("Cell {:>3} ray {} hit label {} (index {})",
271 // (*this)[*cit].id, i, label, data->label_index);
272
273 // #pragma omp critical
274 accum_ptr[data->label_index + 1] += 1;
275 // spdlog::trace("Cell {:>3} hit label {} (index {})", idx,
276 // labels[data->label_index], data->label_index);
277 }
278
279 // Compute probabilities
280 // double sum = std::accumulate(c_rp[*cit].begin(), c_rp[*cit].end(),
281 // 0.0); spdlog::trace("Cell {:>3} sum = {:.3f}", idx, sum); if (sum > 0)
282 // for (size_t j = 0; j < c_rp[*cit].size(); ++j)
283 // c_rp[*cit][j] /= sum;
284 //
285 for (auto &p : local_accum)
286 p /= dirs.size();
287 c_rp[*cit] = std::move(local_accum);
288
289 ++logger;
290 }
291 }
292 rtcReleaseScene(scene_);
293 rtcReleaseDevice(device_);
294
295 _mm_setcsr(old_mxcsr); // restore old flags
296
297 // Log sum of probabilities
298 auto sum_results = std::vector<double>(labels.size() + 1, 0.0);
299 for (auto cit = this->cells_begin(); cit != this->cells_end(); ++cit)
300 for (size_t i = 0; i < c_rp[*cit].size(); ++i)
301 sum_results[i] += c_rp[*cit][i];
302 spdlog::trace("Sum probabilities => [{:.3f}]", fmt::join(sum_results, ", "));
303}
304} // namespace ReUseX::geometry
static std::vector< Eigen::Vector3d > sampleSphericalFibonacci(size_t N)
auto compute_room_probabilities(pcl::PointCloud< PointT >::ConstPtr cloud, pcl::PointCloud< PointN >::ConstPtr normals, pcl::PointCloud< PointL >::ConstPtr labels, const double grid_size=0.2) -> void
auto cells_end() const -> CellIterator
auto cells_begin() const -> CellIterator
std::pair< boost::associative_property_map< std::map< Key, T > >, bool > add_property_map(const std::string &name)
Definition Registry.hpp:27