cpp-toolbox  0.0.1
A toolbox library for C++
Loading...
Searching...
No Matches
voxel_grid_downsampling_impl.hpp
Go to the documentation of this file.
1#pragma once
2
3#include <cmath>
4#include <tuple>
5#include <unordered_map>
6#include <vector>
7
11
12namespace toolbox::pcl
13{
14
15// 实现 voxel_data_soa_t 的方法
16
17template<typename DataType>
19{
20 std::size_t idx = sum_x.size();
21 sum_x.push_back(0);
22 sum_y.push_back(0);
23 sum_z.push_back(0);
24 sum_nx.push_back(0);
25 sum_ny.push_back(0);
26 sum_nz.push_back(0);
27 sum_r.push_back(0);
28 sum_g.push_back(0);
29 sum_b.push_back(0);
30 counts.push_back(0);
31 voxel_indices.push_back(idx);
32 return idx;
33}
34
35template<typename DataType>
37{
38 return sum_x.size();
39}
40
41template<typename DataType>
43 std::size_t reserve_size)
44{
45 sum_x.reserve(reserve_size);
46 sum_y.reserve(reserve_size);
47 sum_z.reserve(reserve_size);
48 sum_nx.reserve(reserve_size);
49 sum_ny.reserve(reserve_size);
50 sum_nz.reserve(reserve_size);
51 sum_r.reserve(reserve_size);
52 sum_g.reserve(reserve_size);
53 sum_b.reserve(reserve_size);
54 counts.reserve(reserve_size);
55 voxel_indices.reserve(reserve_size);
56}
57
58template<typename DataType>
60{
61 sum_x.clear();
62 sum_y.clear();
63 sum_z.clear();
64 sum_nx.clear();
65 sum_ny.clear();
66 sum_nz.clear();
67 sum_r.clear();
68 sum_g.clear();
69 sum_b.clear();
70 counts.clear();
71 voxel_indices.clear();
72}
73
74// 实现 key_hash 的哈希函数
75template<typename DataType>
77 const voxel_key_t& key) const noexcept
78{
79 // 对于整数键,直接使用标准哈希函数
80 return std::hash<voxel_key_t> {}(key);
81}
82
83// 实现计算点云边界的方法
84template<typename DataType>
86{
87 if (!m_cloud || m_cloud->empty()) {
88 m_bounds_computed = false;
89 return;
90 }
91
92 // 使用现有的 minmax 工具计算点云边界
93 constexpr std::size_t k_parallel_threshold = 1024;
94
95 // 计算点云边界
96 auto bounds = m_enable_parallel && m_cloud->size() > k_parallel_threshold
98 : toolbox::types::calculate_minmax(*m_cloud);
99
100 // 计算体素索引范围
101 m_min_ix = static_cast<int>(std::floor(bounds.min.x / m_voxel_size));
102 m_min_iy = static_cast<int>(std::floor(bounds.min.y / m_voxel_size));
103 m_min_iz = static_cast<int>(std::floor(bounds.min.z / m_voxel_size));
104
105 m_max_ix = static_cast<int>(std::floor(bounds.max.x / m_voxel_size));
106 m_max_iy = static_cast<int>(std::floor(bounds.max.y / m_voxel_size));
107 m_max_iz = static_cast<int>(std::floor(bounds.max.z / m_voxel_size));
108
109 // 计算跨度用于键值计算
110 m_span_x = m_max_ix - m_min_ix + 1;
111 m_span_y = m_max_iy - m_min_iy + 1;
112
113 m_bounds_computed = true;
114}
115
116// 实现计算体素键值的方法
117template<typename DataType>
119voxel_grid_downsampling_t<DataType>::compute_voxel_key(int ix,
120 int iy,
121 int iz) const
122{
123 // 确保边界已计算
124 if (!m_bounds_computed) {
125 // 如果边界未计算,使用简单的哈希方法
126 // 这不是最优的,但可以作为后备方案
127 constexpr std::uint64_t kBitMask = 0x1FFFFF; // 21位掩码
128 constexpr int kYShift = 21;
129 constexpr int kZShift = 42;
130
131 // 确保坐标在合理范围内
132 const std::uint64_t x_bits =
133 static_cast<std::uint64_t>(static_cast<std::uint32_t>(ix)) & kBitMask;
134 const std::uint64_t y_bits =
135 static_cast<std::uint64_t>(static_cast<std::uint32_t>(iy)) & kBitMask;
136 const std::uint64_t z_bits =
137 static_cast<std::uint64_t>(static_cast<std::uint32_t>(iz)) & kBitMask;
138
139 return (z_bits << static_cast<unsigned>(kZShift))
140 | (y_bits << static_cast<unsigned>(kYShift)) | x_bits;
141 }
142
143 // 使用点云边界计算相对坐标
144 const int rel_x = ix - m_min_ix;
145 const int rel_y = iy - m_min_iy;
146 const int rel_z = iz - m_min_iz;
147
148 // 计算一维索引
149 return (static_cast<std::uint64_t>(rel_z)
150 * static_cast<std::uint64_t>(m_span_y)
151 * static_cast<std::uint64_t>(m_span_x))
152 + (static_cast<std::uint64_t>(rel_y)
153 * static_cast<std::uint64_t>(m_span_x))
154 + static_cast<std::uint64_t>(rel_x);
155}
156
157// 实现估计体素数量的方法
158template<typename DataType>
159std::size_t voxel_grid_downsampling_t<DataType>::estimate_voxel_count() const
160{
161 if (!m_bounds_computed || !m_cloud || m_cloud->empty()) {
162 // 如果边界未计算或点云为空,使用默认估计
163 constexpr int kDefaultDivisor = 10; // 假设平均每10个点一个体素
164 return m_cloud ? m_cloud->size() / kDefaultDivisor : 0;
165 }
166
167 // 基于点云边界估计体素数量
168 const std::size_t total_voxels = static_cast<std::size_t>(m_span_x)
169 * static_cast<std::size_t>(m_span_y)
170 * static_cast<std::size_t>(m_max_iz - m_min_iz + 1);
171
172 // 假设点云不是均匀分布的,实际体素数量会小于总体素数量
173 // 使用一个经验系数来估计
174 const double fill_factor = 0.1; // 假设只有10%的体素包含点
175
176 return std::min(static_cast<std::size_t>(total_voxels * fill_factor),
177 m_cloud->size());
178}
179
180// 实现 process_point 方法
181template<typename DataType>
182void voxel_grid_downsampling_t<DataType>::process_point(
183 std::size_t idx,
184 std::unordered_map<voxel_key_t, std::size_t, key_hash>& voxel_map,
185 voxel_data_soa_t& voxel_data)
186{
187 const auto& point = m_cloud->points[idx];
188 int voxel_x = static_cast<int>(std::floor(point.x / m_voxel_size));
189 int voxel_y = static_cast<int>(std::floor(point.y / m_voxel_size));
190 int voxel_z = static_cast<int>(std::floor(point.z / m_voxel_size));
191 voxel_key_t key = compute_voxel_key(voxel_x, voxel_y, voxel_z);
192
193 // 查找或创建体素
194 std::size_t voxel_idx = 0;
195 auto iter = voxel_map.find(key);
196 if (iter == voxel_map.end()) {
197 voxel_idx = voxel_data.add_voxel();
198 voxel_map[key] = voxel_idx;
199 } else {
200 voxel_idx = iter->second;
201 }
202
203 // 累加点数据
204 voxel_data.sum_x[voxel_idx] += point.x;
205 voxel_data.sum_y[voxel_idx] += point.y;
206 voxel_data.sum_z[voxel_idx] += point.z;
207
208 // 如果有法线,累加法线数据
209 if (!m_cloud->normals.empty()) {
210 const auto& normal = m_cloud->normals[idx];
211 voxel_data.sum_nx[voxel_idx] += normal.x;
212 voxel_data.sum_ny[voxel_idx] += normal.y;
213 voxel_data.sum_nz[voxel_idx] += normal.z;
214 }
215
216 // 如果有颜色,累加颜色数据
217 if (!m_cloud->colors.empty()) {
218 const auto& color = m_cloud->colors[idx];
219 voxel_data.sum_r[voxel_idx] += color.x;
220 voxel_data.sum_g[voxel_idx] += color.y;
221 voxel_data.sum_b[voxel_idx] += color.z;
222 }
223
224 // 增加计数
225 voxel_data.counts[voxel_idx]++;
226}
227
228// 实现 merge_thread_data 方法
229template<typename DataType>
230void voxel_grid_downsampling_t<DataType>::merge_thread_data(
231 const std::vector<std::unordered_map<voxel_key_t, std::size_t, key_hash>>&
232 thread_maps,
233 const std::vector<voxel_data_soa_t>& thread_data,
234 std::unordered_map<voxel_key_t, std::size_t, key_hash>& merged_map,
235 voxel_data_soa_t& merged_data)
236{
237 const bool has_normals = !m_cloud->normals.empty();
238 const bool has_colors = !m_cloud->colors.empty();
239
240 // 遍历所有线程的数据
241 for (std::size_t thread_id = 0; thread_id < thread_maps.size(); ++thread_id) {
242 const auto& thread_map = thread_maps[thread_id];
243 const auto& thread_voxel_data = thread_data[thread_id];
244
245 // 遍历该线程的所有体素
246 for (const auto& [key, thread_voxel_idx] : thread_map) {
247 std::size_t merged_voxel_idx = 0;
248 auto iter = merged_map.find(key);
249
250 if (iter == merged_map.end()) {
251 // 创建新体素
252 merged_voxel_idx = merged_data.add_voxel();
253 merged_map[key] = merged_voxel_idx;
254
255 // 复制数据
256 merged_data.sum_x[merged_voxel_idx] =
257 thread_voxel_data.sum_x[thread_voxel_idx];
258 merged_data.sum_y[merged_voxel_idx] =
259 thread_voxel_data.sum_y[thread_voxel_idx];
260 merged_data.sum_z[merged_voxel_idx] =
261 thread_voxel_data.sum_z[thread_voxel_idx];
262
263 if (has_normals) {
264 merged_data.sum_nx[merged_voxel_idx] =
265 thread_voxel_data.sum_nx[thread_voxel_idx];
266 merged_data.sum_ny[merged_voxel_idx] =
267 thread_voxel_data.sum_ny[thread_voxel_idx];
268 merged_data.sum_nz[merged_voxel_idx] =
269 thread_voxel_data.sum_nz[thread_voxel_idx];
270 }
271
272 if (has_colors) {
273 merged_data.sum_r[merged_voxel_idx] =
274 thread_voxel_data.sum_r[thread_voxel_idx];
275 merged_data.sum_g[merged_voxel_idx] =
276 thread_voxel_data.sum_g[thread_voxel_idx];
277 merged_data.sum_b[merged_voxel_idx] =
278 thread_voxel_data.sum_b[thread_voxel_idx];
279 }
280
281 merged_data.counts[merged_voxel_idx] =
282 thread_voxel_data.counts[thread_voxel_idx];
283 } else {
284 // 合并到现有体素
285 merged_voxel_idx = iter->second;
286
287 // 累加数据
288 merged_data.sum_x[merged_voxel_idx] +=
289 thread_voxel_data.sum_x[thread_voxel_idx];
290 merged_data.sum_y[merged_voxel_idx] +=
291 thread_voxel_data.sum_y[thread_voxel_idx];
292 merged_data.sum_z[merged_voxel_idx] +=
293 thread_voxel_data.sum_z[thread_voxel_idx];
294
295 if (has_normals) {
296 merged_data.sum_nx[merged_voxel_idx] +=
297 thread_voxel_data.sum_nx[thread_voxel_idx];
298 merged_data.sum_ny[merged_voxel_idx] +=
299 thread_voxel_data.sum_ny[thread_voxel_idx];
300 merged_data.sum_nz[merged_voxel_idx] +=
301 thread_voxel_data.sum_nz[thread_voxel_idx];
302 }
303
304 if (has_colors) {
305 merged_data.sum_r[merged_voxel_idx] +=
306 thread_voxel_data.sum_r[thread_voxel_idx];
307 merged_data.sum_g[merged_voxel_idx] +=
308 thread_voxel_data.sum_g[thread_voxel_idx];
309 merged_data.sum_b[merged_voxel_idx] +=
310 thread_voxel_data.sum_b[thread_voxel_idx];
311 }
312
313 merged_data.counts[merged_voxel_idx] +=
314 thread_voxel_data.counts[thread_voxel_idx];
315 }
316 }
317 }
318}
319
320template<typename DataType>
322 const point_cloud& cloud)
323{
324 m_cloud = std::make_shared<point_cloud>(cloud);
325
326 // 计算点云边界,用于优化体素索引计算
327 m_bounds_computed = false;
328 if (!m_cloud->empty()) {
329 compute_point_cloud_bounds();
330 }
331
332 return m_cloud->size();
333}
334
335template<typename DataType>
337 const point_cloud_ptr& cloud)
338{
339 m_cloud = cloud;
340
341 // 计算点云边界,用于优化体素索引计算
342 m_bounds_computed = false;
343 if (m_cloud && !m_cloud->empty()) {
344 compute_point_cloud_bounds();
345 }
346
347 return m_cloud ? m_cloud->size() : 0U;
348}
349
350template<typename DataType>
352{
353 m_enable_parallel = enable;
354}
355
356template<typename DataType>
359{
360 auto output = std::make_shared<point_cloud>();
361 filter_impl(output);
362 return *output;
363}
364
365template<typename DataType>
367{
368 if (!output) {
369 return;
370 }
371 if (!m_cloud || m_cloud->empty()) {
372 output->clear();
373 return;
374 }
375
376 // 定义常量
377 constexpr std::size_t k_parallel_threshold = 1024;
378 constexpr std::size_t kMaxVoxelsPerThread = 1000;
379
380 const std::size_t total_points = m_cloud->size();
381 const bool has_normals = !m_cloud->normals.empty();
382 const bool has_colors = !m_cloud->colors.empty();
383
384 // 确定线程数量
385 const std::size_t num_threads =
386 m_enable_parallel && total_points > k_parallel_threshold
387 ? std::thread::hardware_concurrency()
388 : 1;
389
390 // 预估每个线程的体素数量 - 假设点云均匀分布,每个体素包含多个点
391 constexpr int kDefaultDivisor = 10; // 假设平均每10个点一个体素
392 const std::size_t estimated_voxels_per_thread =
393 std::min(total_points / kDefaultDivisor, kMaxVoxelsPerThread);
394
395 // 创建线程局部哈希表和数据结构
396 std::vector<std::unordered_map<voxel_key_t, std::size_t, key_hash>>
397 thread_voxel_maps(num_threads);
398 std::vector<voxel_data_soa_t> thread_voxel_data(num_threads);
399
400 // 为每个线程预分配内存
401 for (auto& voxel_data : thread_voxel_data) {
402 voxel_data.reserve(estimated_voxels_per_thread);
403 }
404
405 // 并行或串行处理点云
406 if (m_enable_parallel && total_points > k_parallel_threshold) {
407 // 计算每个线程处理的点数
408 const std::size_t points_per_thread =
409 (total_points + num_threads - 1) / num_threads;
410
411 // 创建并行任务
412 std::vector<std::future<void>> futures;
413 futures.reserve(num_threads);
414
415 for (std::size_t thread_id = 0; thread_id < num_threads; ++thread_id) {
416 // 计算每个线程处理的点范围
417 const std::size_t start_idx = thread_id * points_per_thread;
418 const std::size_t end_idx =
419 std::min(start_idx + points_per_thread, total_points);
420
421 if (start_idx >= end_idx) {
422 continue;
423 }
424
425 futures.emplace_back(toolbox::concurrent::default_pool().submit(
426 [this,
427 thread_id,
428 start_idx,
429 end_idx,
430 has_normals,
431 has_colors,
432 &thread_voxel_maps,
433 &thread_voxel_data]()
434 {
435 auto& voxel_map = thread_voxel_maps[thread_id];
436 auto& voxel_data = thread_voxel_data[thread_id];
437
438 // 处理该线程负责的点
439 for (std::size_t i = start_idx; i < end_idx; ++i) {
440 process_point(i, voxel_map, voxel_data);
441 }
442 }));
443 }
444
445 // 等待所有线程完成
446 for (auto& future : futures) {
447 future.get();
448 }
449 } else {
450 // 单线程处理
451 auto& voxel_map = thread_voxel_maps[0];
452 auto& voxel_data = thread_voxel_data[0];
453
454 for (std::size_t i = 0; i < total_points; ++i) {
455 process_point(i, voxel_map, voxel_data);
456 }
457 }
458
459 // 合并所有线程的结果
460 std::unordered_map<voxel_key_t, std::size_t, key_hash> merged_voxel_map;
461 voxel_data_soa_t merged_voxel_data;
462
463 // 估计合并后的体素数量
464 std::size_t total_voxels = 0;
465 for (const auto& voxel_data : thread_voxel_data) {
466 total_voxels += voxel_data.size();
467 }
468 merged_voxel_data.reserve(total_voxels);
469
470 // 合并线程数据
471 merge_thread_data(thread_voxel_maps,
472 thread_voxel_data,
473 merged_voxel_map,
474 merged_voxel_data);
475
476 // 生成输出点云
477 const std::size_t num_voxels = merged_voxel_data.size();
478 output->points.resize(num_voxels);
479 if (has_normals) {
480 output->normals.resize(num_voxels);
481 }
482 if (has_colors) {
483 output->colors.resize(num_voxels);
484 }
485 output->intensity = m_cloud->intensity;
486
487 // 并行或串行计算质心
488 if (m_enable_parallel && num_voxels > k_parallel_threshold) {
489 std::vector<std::future<void>> futures;
490 futures.reserve(num_threads);
491
492 const std::size_t voxels_per_thread =
493 (num_voxels + num_threads - 1) / num_threads;
494
495 for (std::size_t thread_id = 0; thread_id < num_threads; ++thread_id) {
496 const std::size_t start_idx = thread_id * voxels_per_thread;
497 const std::size_t end_idx =
498 std::min(start_idx + voxels_per_thread, num_voxels);
499
500 if (start_idx >= end_idx) {
501 continue;
502 }
503
504 futures.emplace_back(toolbox::concurrent::default_pool().submit(
505 [start_idx,
506 end_idx,
507 has_normals,
508 has_colors,
509 &merged_voxel_data,
510 &output]()
511 {
512 for (std::size_t i = start_idx; i < end_idx; ++i) {
513 const DataType inv_count =
514 static_cast<DataType>(1.0) / merged_voxel_data.counts[i];
515
516 // 计算质心
517 output->points[i].x = merged_voxel_data.sum_x[i] * inv_count;
518 output->points[i].y = merged_voxel_data.sum_y[i] * inv_count;
519 output->points[i].z = merged_voxel_data.sum_z[i] * inv_count;
520
521 // 计算法线
522 if (has_normals) {
523 output->normals[i].x = merged_voxel_data.sum_nx[i] * inv_count;
524 output->normals[i].y = merged_voxel_data.sum_ny[i] * inv_count;
525 output->normals[i].z = merged_voxel_data.sum_nz[i] * inv_count;
526 }
527
528 // 计算颜色
529 if (has_colors) {
530 output->colors[i].x = merged_voxel_data.sum_r[i] * inv_count;
531 output->colors[i].y = merged_voxel_data.sum_g[i] * inv_count;
532 output->colors[i].z = merged_voxel_data.sum_b[i] * inv_count;
533 }
534 }
535 }));
536 }
537
538 // 等待所有线程完成
539 for (auto& future : futures) {
540 future.get();
541 }
542 } else {
543 // 单线程计算质心
544 for (std::size_t i = 0; i < num_voxels; ++i) {
545 const DataType inv_count =
546 static_cast<DataType>(1.0) / merged_voxel_data.counts[i];
547
548 // 计算质心
549 output->points[i].x = merged_voxel_data.sum_x[i] * inv_count;
550 output->points[i].y = merged_voxel_data.sum_y[i] * inv_count;
551 output->points[i].z = merged_voxel_data.sum_z[i] * inv_count;
552
553 // 计算法线
554 if (has_normals) {
555 output->normals[i].x = merged_voxel_data.sum_nx[i] * inv_count;
556 output->normals[i].y = merged_voxel_data.sum_ny[i] * inv_count;
557 output->normals[i].z = merged_voxel_data.sum_nz[i] * inv_count;
558 }
559
560 // 计算颜色
561 if (has_colors) {
562 output->colors[i].x = merged_voxel_data.sum_r[i] * inv_count;
563 output->colors[i].y = merged_voxel_data.sum_g[i] * inv_count;
564 output->colors[i].z = merged_voxel_data.sum_b[i] * inv_count;
565 }
566 }
567 }
568}
569
570} // namespace toolbox::pcl
Definition voxel_grid_downsampling.hpp:13
point_cloud filter_impl()
Definition voxel_grid_downsampling_impl.hpp:358
std::size_t set_input_impl(const point_cloud &cloud)
Definition voxel_grid_downsampling_impl.hpp:321
std::uint64_t voxel_key_t
Definition voxel_grid_downsampling.hpp:21
std::shared_ptr< toolbox::types::point_cloud_t< data_type > > point_cloud_ptr
Definition voxel_grid_downsampling.hpp:19
void enable_parallel_impl(bool enable)
Definition voxel_grid_downsampling_impl.hpp:351
包含点和相关数据的点云类 / A point cloud class containing points and associated data
Definition point.hpp:268
void clear()
清除点云中的所有数据 / Clear all data from cloud
Definition point_impl.hpp:305
base::thread_pool_singleton_t & default_pool()
获取默认线程池实例/Get the default thread pool instance
Definition parallel.hpp:22
Definition base_correspondence_generator.hpp:18
auto calculate_minmax_parallel(const InputType &input) -> std::enable_if_t<!detail::is_calculable_container_v< InputType >, minmax_t< std::decay_t< InputType > > >
并行计算非容器类型的最小最大值 / Calculate minmax for non-container type in parallel
Definition minmax_impl.hpp:227
std::size_t operator()(const voxel_key_t &key) const noexcept
Definition voxel_grid_downsampling_impl.hpp:76
Definition voxel_grid_downsampling.hpp:25
std::vector< DataType > sum_g
Definition voxel_grid_downsampling.hpp:38
std::vector< DataType > sum_b
Definition voxel_grid_downsampling.hpp:39
std::size_t size() const
Definition voxel_grid_downsampling_impl.hpp:36
std::vector< DataType > sum_r
Definition voxel_grid_downsampling.hpp:37
std::size_t add_voxel()
Definition voxel_grid_downsampling_impl.hpp:18
void reserve(std::size_t reserve_size)
Definition voxel_grid_downsampling_impl.hpp:42
std::vector< DataType > sum_z
Definition voxel_grid_downsampling.hpp:29
std::vector< std::size_t > voxel_indices
Definition voxel_grid_downsampling.hpp:43
std::vector< std::size_t > counts
Definition voxel_grid_downsampling.hpp:42
void clear()
Definition voxel_grid_downsampling_impl.hpp:59
std::vector< DataType > sum_nz
Definition voxel_grid_downsampling.hpp:34
std::vector< DataType > sum_nx
Definition voxel_grid_downsampling.hpp:32
std::vector< DataType > sum_y
Definition voxel_grid_downsampling.hpp:28
std::vector< DataType > sum_ny
Definition voxel_grid_downsampling.hpp:33
std::vector< DataType > sum_x
Definition voxel_grid_downsampling.hpp:27