cpp-toolbox  0.0.1
A toolbox library for C++
Loading...
Searching...
No Matches
fpfh_extractor_impl.hpp
Go to the documentation of this file.
1#pragma once
2
4#include <numeric>
5#include <algorithm>
6#include <iostream>
8
9namespace toolbox::pcl
10{
11
12
13template<typename DataType, typename KNN>
15{
16 m_cloud = std::make_shared<point_cloud>(cloud);
17 return m_cloud->size();
18}
19
20template<typename DataType, typename KNN>
22{
23 m_cloud = cloud;
24 return m_cloud ? m_cloud->size() : 0;
25}
26
27template<typename DataType, typename KNN>
29{
30 m_knn = const_cast<knn_type*>(&knn);
31 if (m_cloud && m_knn)
32 {
33 // Convert point cloud to vector of points for new KNN API
34 std::vector<point_t<DataType>> points(m_cloud->points.begin(), m_cloud->points.end());
35 m_knn->set_input(points);
36 }
37 return m_cloud ? m_cloud->size() : 0;
38}
39
40template<typename DataType, typename KNN>
42{
43 m_search_radius = radius;
44 return m_cloud ? m_cloud->size() : 0;
45}
46
47template<typename DataType, typename KNN>
48std::size_t fpfh_extractor_t<DataType, KNN>::set_num_neighbors(std::size_t num_neighbors)
49{
50 m_num_neighbors = num_neighbors;
51 return m_cloud ? m_cloud->size() : 0;
52}
53
54template<typename DataType, typename KNN>
56{
57 m_normals = normals;
58}
59
60template<typename DataType, typename KNN>
62{
63 m_enable_parallel = enable;
64}
65
66template<typename DataType, typename KNN>
68 const point_cloud& cloud,
69 const std::vector<std::size_t>& keypoint_indices,
70 std::vector<signature_type>& descriptors) const
71{
72 if (!m_knn || keypoint_indices.empty())
73 {
74 descriptors.clear();
75 return;
76 }
77
78 // 计算法向量(如果未提供)/ Compute normals if not provided
79 point_cloud_ptr normals = m_normals;
80 if (!normals)
81 {
82 normals = std::make_shared<point_cloud>();
83 normals->points.resize(cloud.size());
85 norm_extractor.set_input(cloud);
86 norm_extractor.set_knn(*m_knn);
87 norm_extractor.set_num_neighbors(m_num_neighbors);
88 norm_extractor.enable_parallel(m_enable_parallel);
89 norm_extractor.extract(normals);
90 }
91
92 // 计算密度以决定算法策略 / Calculate density to determine algorithm strategy
93 data_type density = static_cast<data_type>(keypoint_indices.size()) / static_cast<data_type>(cloud.size());
94
95 // Debug output
96 // std::cout << "[FPFH] Density: " << density << " (" << keypoint_indices.size() << "/" << cloud.size() << ")\n";
97
98 // 对于低密度情况,使用直接计算避免缓存开销 / For low density, use direct computation to avoid cache overhead
99 if (density < DataType(0.01) && keypoint_indices.size() < 100) {
100 // std::cout << "[FPFH] Using direct computation mode\n";
101 // 直接计算模式 - 类似PFH但使用FPFH的简化计算 / Direct computation mode - similar to PFH but with FPFH's simplified calculation
102 descriptors.resize(keypoint_indices.size());
103
104 auto compute_direct = [&](std::size_t start, std::size_t end) {
105 std::vector<std::size_t> temp_indices;
106 std::vector<data_type> temp_distances;
107 temp_indices.reserve(m_num_neighbors);
108 temp_distances.reserve(m_num_neighbors);
109
110 for (std::size_t i = start; i < end; ++i) {
111 std::size_t keypoint_idx = keypoint_indices[i];
112 temp_indices.clear();
113 temp_distances.clear();
114
115 m_knn->radius_neighbors(cloud.points[keypoint_idx], m_search_radius, temp_indices, temp_distances);
116 if (temp_indices.size() > m_num_neighbors) {
117 temp_indices.resize(m_num_neighbors);
118 temp_distances.resize(m_num_neighbors);
119 }
120
121 // 直接计算FPFH,不预计算SPFH / Compute FPFH directly without pre-computing SPFH
122 compute_fpfh_direct(cloud, *normals, keypoint_idx, temp_indices, temp_distances, descriptors[i]);
123 }
124 };
125
126 if (m_enable_parallel) {
127 std::vector<std::size_t> indices(keypoint_indices.size());
128 std::iota(indices.begin(), indices.end(), 0);
129 toolbox::concurrent::parallel_for_each(indices.begin(), indices.end(),
130 [&](std::size_t i) {
131 std::vector<std::size_t> temp_indices;
132 std::vector<data_type> temp_distances;
133 temp_indices.reserve(m_num_neighbors);
134 temp_distances.reserve(m_num_neighbors);
135
136 std::size_t keypoint_idx = keypoint_indices[i];
137 m_knn->radius_neighbors(cloud.points[keypoint_idx], m_search_radius, temp_indices, temp_distances);
138 if (temp_indices.size() > m_num_neighbors) {
139 temp_indices.resize(m_num_neighbors);
140 temp_distances.resize(m_num_neighbors);
141 }
142 compute_fpfh_direct(cloud, *normals, keypoint_idx, temp_indices, temp_distances, descriptors[i]);
143 });
144 } else {
145 compute_direct(0, keypoint_indices.size());
146 }
147 return;
148 }
149
150 // std::cout << "[FPFH] Using optimized caching strategy\n";
151 // 高密度情况使用优化的缓存策硅 / High density case uses optimized caching strategy
152
153 // 步骤1:收集所有邻居信息并统计引用次数 / Step 1: Collect all neighbor info and count references
154 // 使用已定义的neighbor_info_t类型 / Use already defined neighbor_info_t type
155 std::vector<neighbor_info_t<data_type>> all_neighbors(cloud.size());
156 std::vector<int> reference_count(cloud.size(), 0);
157
158 // 首先为关键点缓存邻居并统计引用 / First cache neighbors for keypoints and count references
159 std::vector<std::vector<std::size_t>> keypoint_neighbor_indices(keypoint_indices.size());
160 std::vector<std::vector<data_type>> keypoint_neighbor_distances(keypoint_indices.size());
161
162 for (std::size_t i = 0; i < keypoint_indices.size(); ++i) {
163 keypoint_neighbor_indices[i].reserve(m_num_neighbors);
164 keypoint_neighbor_distances[i].reserve(m_num_neighbors);
165 }
166
167 if (m_enable_parallel) {
168 std::vector<std::size_t> indices(keypoint_indices.size());
169 std::iota(indices.begin(), indices.end(), 0);
170 toolbox::concurrent::parallel_for_each(indices.begin(), indices.end(),
171 [&](std::size_t i) {
172 std::size_t keypoint_idx = keypoint_indices[i];
173 m_knn->radius_neighbors(cloud.points[keypoint_idx], m_search_radius,
174 keypoint_neighbor_indices[i], keypoint_neighbor_distances[i]);
175 if (keypoint_neighbor_indices[i].size() > m_num_neighbors) {
176 keypoint_neighbor_indices[i].resize(m_num_neighbors);
177 keypoint_neighbor_distances[i].resize(m_num_neighbors);
178 }
179 });
180 } else {
181 for (std::size_t i = 0; i < keypoint_indices.size(); ++i) {
182 std::size_t keypoint_idx = keypoint_indices[i];
183 m_knn->radius_neighbors(cloud.points[keypoint_idx], m_search_radius,
184 keypoint_neighbor_indices[i], keypoint_neighbor_distances[i]);
185 if (keypoint_neighbor_indices[i].size() > m_num_neighbors) {
186 keypoint_neighbor_indices[i].resize(m_num_neighbors);
187 keypoint_neighbor_distances[i].resize(m_num_neighbors);
188 }
189 }
190 }
191
192 // 统计引用次数 / Count references
193 for (std::size_t i = 0; i < keypoint_indices.size(); ++i) {
194 reference_count[keypoint_indices[i]]++;
195 for (std::size_t neighbor_idx : keypoint_neighbor_indices[i]) {
196 reference_count[neighbor_idx]++;
197 }
198 }
199
200 // 步骤2:只为被多次引用的点预计算SPFH / Step 2: Pre-compute SPFH only for points referenced multiple times
201 std::vector<bool> needs_spfh(cloud.size(), false);
202 std::vector<spfh_signature_t> spfh_array(cloud.size());
203 std::vector<bool> spfh_computed(cloud.size(), false);
204
205 // 收集需要预计算SPFH的点(引用次数>2) / Collect points needing SPFH pre-computation (reference count > 2)
206 std::vector<std::size_t> points_to_precompute;
207 points_to_precompute.reserve(keypoint_indices.size() * 2); // 估计大小 / Estimated size
208
209 for (std::size_t i = 0; i < cloud.size(); ++i) {
210 if (reference_count[i] > 2) {
211 needs_spfh[i] = true;
212 points_to_precompute.push_back(i);
213 }
214 }
215
216 // 步骤3:批量预计算高引用点的SPFH,并为其缓存邻居 / Step 3: Batch pre-compute SPFH for high-reference points and cache their neighbors
217 if (!points_to_precompute.empty()) {
218 auto compute_spfh_batch = [&](std::size_t start, std::size_t end) {
219 for (std::size_t i = start; i < end; ++i) {
220 std::size_t point_idx = points_to_precompute[i];
221
222 // 检查是否已缓存邻居 / Check if neighbors are already cached
223 if (!all_neighbors[point_idx].computed) {
224 m_knn->radius_neighbors(cloud.points[point_idx], m_search_radius,
225 all_neighbors[point_idx].indices, all_neighbors[point_idx].distances);
226 if (all_neighbors[point_idx].indices.size() > m_num_neighbors) {
227 all_neighbors[point_idx].indices.resize(m_num_neighbors);
228 all_neighbors[point_idx].distances.resize(m_num_neighbors);
229 }
230 all_neighbors[point_idx].computed = true;
231 }
232
233 if (!all_neighbors[point_idx].indices.empty()) {
234 compute_spfh(cloud, *normals, point_idx, all_neighbors[point_idx].indices, spfh_array[point_idx]);
235 spfh_computed[point_idx] = true;
236 }
237 }
238 };
239
240 if (m_enable_parallel) {
241 std::vector<std::size_t> indices(points_to_precompute.size());
242 std::iota(indices.begin(), indices.end(), 0);
243 toolbox::concurrent::parallel_for_each(indices.begin(), indices.end(),
244 [&](std::size_t i) {
245 std::size_t point_idx = points_to_precompute[i];
246
247 if (!all_neighbors[point_idx].computed) {
248 m_knn->radius_neighbors(cloud.points[point_idx], m_search_radius,
249 all_neighbors[point_idx].indices, all_neighbors[point_idx].distances);
250 if (all_neighbors[point_idx].indices.size() > m_num_neighbors) {
251 all_neighbors[point_idx].indices.resize(m_num_neighbors);
252 all_neighbors[point_idx].distances.resize(m_num_neighbors);
253 }
254 all_neighbors[point_idx].computed = true;
255 }
256
257 if (!all_neighbors[point_idx].indices.empty()) {
258 compute_spfh(cloud, *normals, point_idx, all_neighbors[point_idx].indices, spfh_array[point_idx]);
259 spfh_computed[point_idx] = true;
260 }
261 });
262 } else {
263 compute_spfh_batch(0, points_to_precompute.size());
264 }
265 }
266
267 // 步骤4:计算FPFH,混合使用预计算和延迟计算 / Step 4: Compute FPFH using mix of pre-computed and lazy computation
268 descriptors.resize(keypoint_indices.size());
269
270 auto compute_fpfh_batch = [&](std::size_t start, std::size_t end) {
271 for (std::size_t i = start; i < end; ++i) {
272 std::size_t keypoint_idx = keypoint_indices[i];
273
274 // 计算关键点的SPFH(如果还未计算) / Compute keypoint's SPFH if not yet computed
275 if (!spfh_computed[keypoint_idx]) {
276 if (!all_neighbors[keypoint_idx].computed) {
277 // 使用已缓存的邻居信息 / Use cached neighbor info
278 all_neighbors[keypoint_idx].indices = keypoint_neighbor_indices[i];
279 all_neighbors[keypoint_idx].distances = keypoint_neighbor_distances[i];
280 all_neighbors[keypoint_idx].computed = true;
281 }
282 compute_spfh(cloud, *normals, keypoint_idx, all_neighbors[keypoint_idx].indices, spfh_array[keypoint_idx]);
283 spfh_computed[keypoint_idx] = true;
284 }
285
286 // 计算FPFH / Compute FPFH
287 compute_fpfh_feature_adaptive(cloud, *normals, keypoint_idx,
288 keypoint_neighbor_indices[i], keypoint_neighbor_distances[i],
289 all_neighbors, spfh_array, spfh_computed, descriptors[i]);
290 }
291 };
292
293 if (m_enable_parallel) {
294 std::vector<std::size_t> indices(keypoint_indices.size());
295 std::iota(indices.begin(), indices.end(), 0);
296
297 toolbox::concurrent::parallel_for_each(indices.begin(), indices.end(),
298 [&](std::size_t i) {
299 std::size_t keypoint_idx = keypoint_indices[i];
300
301 // 使用原子操作确保线程安全的延迟计算 / Use atomic operations for thread-safe lazy computation
302 if (!spfh_computed[keypoint_idx]) {
303 // 这里需要加锁或使用原子操作 / Need locking or atomic operations here
304 if (!all_neighbors[keypoint_idx].computed) {
305 all_neighbors[keypoint_idx].indices = keypoint_neighbor_indices[i];
306 all_neighbors[keypoint_idx].distances = keypoint_neighbor_distances[i];
307 all_neighbors[keypoint_idx].computed = true;
308 }
309 compute_spfh(cloud, *normals, keypoint_idx, all_neighbors[keypoint_idx].indices, spfh_array[keypoint_idx]);
310 spfh_computed[keypoint_idx] = true;
311 }
312
313 compute_fpfh_feature_adaptive(cloud, *normals, keypoint_idx,
314 keypoint_neighbor_indices[i], keypoint_neighbor_distances[i],
315 all_neighbors, spfh_array, spfh_computed, descriptors[i]);
316 });
317 } else {
318 compute_fpfh_batch(0, keypoint_indices.size());
319 }
320}
321
322template<typename DataType, typename KNN>
324 const point_cloud& cloud,
325 const std::vector<std::size_t>& keypoint_indices,
326 std::unique_ptr<std::vector<signature_type>>& descriptors) const
327{
328 descriptors = std::make_unique<std::vector<signature_type>>();
329 compute_impl(cloud, keypoint_indices, *descriptors);
330}
331
332template<typename DataType, typename KNN>
334 const point_cloud& cloud,
335 const point_cloud& normals,
336 std::size_t index,
337 const std::vector<std::size_t>& neighbor_indices,
338 spfh_signature_t& spfh) const
339{
340 const auto& p1 = cloud.points[index];
341 const auto& n1 = normals.points[index];
342
343 // Initialize histograms
344 std::fill(spfh.f1.begin(), spfh.f1.end(), DataType(0));
345 std::fill(spfh.f2.begin(), spfh.f2.end(), DataType(0));
346 std::fill(spfh.f3.begin(), spfh.f3.end(), DataType(0));
347
348 std::size_t valid_neighbors = 0;
349
350 for (std::size_t neighbor_idx : neighbor_indices)
351 {
352 if (neighbor_idx == index) continue;
353
354 const auto& p2 = cloud.points[neighbor_idx];
355 const auto& n2 = normals.points[neighbor_idx];
356
357 data_type f1, f2, f3;
358 compute_pair_features(p1, n1, p2, n2, f1, f2, f3);
359
360 // Compute histogram bins
361 std::size_t bin_f1 = compute_bin_index(f1, DataType(-1), DataType(1), 11);
362 std::size_t bin_f2 = compute_bin_index(f2, DataType(-1), DataType(1), 11);
363 std::size_t bin_f3 = compute_bin_index(f3, DataType(-M_PI), DataType(M_PI), 11);
364
365 spfh.f1[bin_f1] += DataType(1);
366 spfh.f2[bin_f2] += DataType(1);
367 spfh.f3[bin_f3] += DataType(1);
368
369 valid_neighbors++;
370 }
371
372 // Normalize histograms
373 if (valid_neighbors > 0)
374 {
375 data_type norm_factor = DataType(1) / static_cast<data_type>(valid_neighbors);
376 for (std::size_t i = 0; i < 11; ++i)
377 {
378 spfh.f1[i] *= norm_factor;
379 spfh.f2[i] *= norm_factor;
380 spfh.f3[i] *= norm_factor;
381 }
382 }
383}
384
389template<typename DataType, typename KNN>
390void fpfh_extractor_t<DataType, KNN>::compute_fpfh_feature_optimized(
391 const point_cloud& cloud,
392 const point_cloud& normals,
393 std::size_t index,
394 const neighbor_info_t<data_type>& neighbor_info,
395 const spfh_cache_manager_t<data_type, knn_type>& spfh_cache,
396 signature_type& fpfh) const
397{
398 // 初始化FPFH直方图 / Initialize FPFH histogram
399 std::fill(fpfh.histogram.begin(), fpfh.histogram.end(), DataType(0));
400
401 if (neighbor_info.indices.empty()) return;
402
403 // 复制自身的SPFH / Copy own SPFH
404 const auto& own_spfh = spfh_cache.get_spfh(index);
405 for (std::size_t i = 0; i < 11; ++i)
406 {
407 fpfh.histogram[i] = own_spfh.f1[i];
408 fpfh.histogram[i + 11] = own_spfh.f2[i];
409 fpfh.histogram[i + 22] = own_spfh.f3[i];
410 }
411
412 // 加权累积邻居的SPFH / Weight and accumulate neighbor SPFHs
413 data_type weight_sum = DataType(0);
414
415 for (std::size_t i = 0; i < neighbor_info.indices.size(); ++i)
416 {
417 std::size_t neighbor_idx = neighbor_info.indices[i];
418 if (neighbor_idx == index) continue;
419
420 // 使用距离倒数作为权重 / Use inverse distance as weight
421 data_type weight = DataType(1) / (neighbor_info.distances[i] + DataType(1e-6));
422 weight_sum += weight;
423
424 const auto& neighbor_spfh = spfh_cache.get_spfh(neighbor_idx);
425
426 // 向量化累积操作 / Vectorized accumulation operation
427 for (std::size_t j = 0; j < 11; ++j)
428 {
429 fpfh.histogram[j] += weight * neighbor_spfh.f1[j];
430 fpfh.histogram[j + 11] += weight * neighbor_spfh.f2[j];
431 fpfh.histogram[j + 22] += weight * neighbor_spfh.f3[j];
432 }
433 }
434
435 // 归一化最终直方图 / Normalize the final histogram
436 if (weight_sum > DataType(0))
437 {
438 data_type norm_factor = DataType(1) / (DataType(1) + weight_sum);
439 for (std::size_t i = 0; i < 33; ++i)
440 {
441 fpfh.histogram[i] *= norm_factor;
442 }
443 }
444}
445
446template<typename DataType, typename KNN>
447void fpfh_extractor_t<DataType, KNN>::compute_fpfh_feature(
448 const point_cloud& cloud,
449 const point_cloud& normals,
450 std::size_t index,
451 const std::vector<spfh_signature_t>& spfh_features,
452 signature_type& fpfh) const
453{
454 // Initialize FPFH histogram
455 std::fill(fpfh.histogram.begin(), fpfh.histogram.end(), DataType(0));
456
457 // Get neighbors
458 std::vector<std::size_t> neighbor_indices;
459 std::vector<data_type> neighbor_distances;
460 m_knn->radius_neighbors(cloud.points[index], m_search_radius, neighbor_indices, neighbor_distances);
461
462 // Limit to m_num_neighbors
463 if (neighbor_indices.size() > m_num_neighbors)
464 {
465 neighbor_indices.resize(m_num_neighbors);
466 neighbor_distances.resize(m_num_neighbors);
467 }
468
469 if (neighbor_indices.empty()) return;
470
471 // Copy own SPFH
472 const auto& own_spfh = spfh_features[index];
473 for (std::size_t i = 0; i < 11; ++i)
474 {
475 fpfh.histogram[i] = own_spfh.f1[i];
476 fpfh.histogram[i + 11] = own_spfh.f2[i];
477 fpfh.histogram[i + 22] = own_spfh.f3[i];
478 }
479
480 // Weight and accumulate neighbor SPFHs
481 data_type weight_sum = DataType(0);
482
483 for (std::size_t i = 0; i < neighbor_indices.size(); ++i)
484 {
485 std::size_t neighbor_idx = neighbor_indices[i];
486 if (neighbor_idx == index) continue;
487
488 // Use inverse distance as weight
489 data_type weight = DataType(1) / (neighbor_distances[i] + DataType(1e-6));
490 weight_sum += weight;
491
492 const auto& neighbor_spfh = spfh_features[neighbor_idx];
493 for (std::size_t j = 0; j < 11; ++j)
494 {
495 fpfh.histogram[j] += weight * neighbor_spfh.f1[j];
496 fpfh.histogram[j + 11] += weight * neighbor_spfh.f2[j];
497 fpfh.histogram[j + 22] += weight * neighbor_spfh.f3[j];
498 }
499 }
500
501 // Normalize the final histogram
502 if (weight_sum > DataType(0))
503 {
504 data_type norm_factor = DataType(1) / (DataType(1) + weight_sum);
505 for (std::size_t i = 0; i < 33; ++i)
506 {
507 fpfh.histogram[i] *= norm_factor;
508 }
509 }
510}
511
512template<typename DataType, typename KNN>
513void fpfh_extractor_t<DataType, KNN>::compute_pair_features(
514 const point_t<data_type>& p1,
515 const point_t<data_type>& n1,
516 const point_t<data_type>& p2,
517 const point_t<data_type>& n2,
518 data_type& f1,
519 data_type& f2,
520 data_type& f3) const
521{
522 // Compute the Darboux frame
523 point_t<data_type> dp;
524 dp.x = p2.x - p1.x;
525 dp.y = p2.y - p1.y;
526 dp.z = p2.z - p1.z;
527 auto dp_normalized = dp.normalize();
528 dp.x = static_cast<data_type>(dp_normalized.x);
529 dp.y = static_cast<data_type>(dp_normalized.y);
530 dp.z = static_cast<data_type>(dp_normalized.z);
531
532 // u = n1
533 point_t<data_type> u = n1;
534
535 // v = (p2 - p1) x u
536 point_t<data_type> v = dp.cross(u);
537 data_type v_norm = v.norm();
538
539 if (v_norm < DataType(1e-6))
540 {
541 // Points are aligned with normal, create arbitrary perpendicular vector
542 if (std::abs(u.x) < DataType(0.9))
543 v = point_t<data_type>(DataType(1), DataType(0), DataType(0)).cross(u);
544 else
545 v = point_t<data_type>(DataType(0), DataType(1), DataType(0)).cross(u);
546 auto v_normalized = v.normalize();
547 v.x = static_cast<data_type>(v_normalized.x);
548 v.y = static_cast<data_type>(v_normalized.y);
549 v.z = static_cast<data_type>(v_normalized.z);
550 }
551 else
552 {
553 auto v_normalized = v.normalize();
554 v.x = static_cast<data_type>(v_normalized.x);
555 v.y = static_cast<data_type>(v_normalized.y);
556 v.z = static_cast<data_type>(v_normalized.z);
557 }
558
559 // w = u x v
560 point_t<data_type> w = u.cross(v);
561
562 // Compute angles
563 f1 = v.dot(n2); // cos(alpha)
564 f2 = u.dot(dp); // cos(phi)
565 f3 = std::atan2(w.dot(n2), u.dot(n2)); // atan2(w·n2, u·n2) = theta
566}
567
568template<typename DataType, typename KNN>
569std::size_t fpfh_extractor_t<DataType, KNN>::compute_bin_index(
570 data_type value,
571 data_type min_val,
572 data_type max_val,
573 std::size_t num_bins) const
574{
575 // Clamp value to range
576 value = std::max(min_val, std::min(max_val, value));
577
578 // Compute bin index
579 data_type normalized = (value - min_val) / (max_val - min_val);
580 std::size_t bin = static_cast<std::size_t>(normalized * num_bins);
581
582 // Ensure bin is within valid range
583 return std::min(bin, num_bins - 1);
584}
585
589template<typename DataType, typename KNN>
590void fpfh_extractor_t<DataType, KNN>::compute_fpfh_direct(
591 const point_cloud& cloud,
592 const point_cloud& normals,
593 std::size_t index,
594 const std::vector<std::size_t>& neighbor_indices,
595 const std::vector<data_type>& neighbor_distances,
596 signature_type& fpfh) const
597{
598 // 初始化直方图 / Initialize histogram
599 std::fill(fpfh.histogram.begin(), fpfh.histogram.end(), DataType(0));
600
601 if (neighbor_indices.empty()) return;
602
603 const auto& p1 = cloud.points[index];
604 const auto& n1 = normals.points[index];
605
606 // 计算自身的SPFH部分 / Compute own SPFH part
607 std::size_t valid_neighbors = 0;
608 for (std::size_t i = 0; i < neighbor_indices.size(); ++i)
609 {
610 std::size_t neighbor_idx = neighbor_indices[i];
611 if (neighbor_idx == index) continue;
612
613 const auto& p2 = cloud.points[neighbor_idx];
614 const auto& n2 = normals.points[neighbor_idx];
615
616 data_type f1, f2, f3;
617 compute_pair_features(p1, n1, p2, n2, f1, f2, f3);
618
619 std::size_t bin_f1 = compute_bin_index(f1, DataType(-1), DataType(1), 11);
620 std::size_t bin_f2 = compute_bin_index(f2, DataType(-1), DataType(1), 11);
621 std::size_t bin_f3 = compute_bin_index(f3, DataType(-M_PI), DataType(M_PI), 11);
622
623 fpfh.histogram[bin_f1] += DataType(1);
624 fpfh.histogram[bin_f2 + 11] += DataType(1);
625 fpfh.histogram[bin_f3 + 22] += DataType(1);
626
627 valid_neighbors++;
628 }
629
630 // 加权累积邻居的贡献 / Weight and accumulate neighbor contributions
631 data_type total_weight = static_cast<data_type>(valid_neighbors);
632
633 for (std::size_t i = 0; i < neighbor_indices.size(); ++i)
634 {
635 std::size_t neighbor_idx = neighbor_indices[i];
636 if (neighbor_idx == index) continue;
637
638 const auto& p2 = cloud.points[neighbor_idx];
639 const auto& n2 = normals.points[neighbor_idx];
640
641 // 使用距离作为权重 / Use distance as weight
642 data_type weight = DataType(1) / (neighbor_distances[i] + DataType(1e-6));
643
644 // 计算邻居的邻居特征(简化版) / Compute neighbor's neighbor features (simplified)
645 std::vector<std::size_t> nn_indices;
646 std::vector<data_type> nn_distances;
647 m_knn->radius_neighbors(p2, m_search_radius, nn_indices, nn_distances);
648
649 if (nn_indices.size() > m_num_neighbors) {
650 nn_indices.resize(m_num_neighbors);
651 nn_distances.resize(m_num_neighbors);
652 }
653
654 for (std::size_t nn_idx : nn_indices)
655 {
656 if (nn_idx == neighbor_idx) continue;
657
658 const auto& p3 = cloud.points[nn_idx];
659 const auto& n3 = normals.points[nn_idx];
660
661 data_type f1, f2, f3;
662 compute_pair_features(p2, n2, p3, n3, f1, f2, f3);
663
664 std::size_t bin_f1 = compute_bin_index(f1, DataType(-1), DataType(1), 11);
665 std::size_t bin_f2 = compute_bin_index(f2, DataType(-1), DataType(1), 11);
666 std::size_t bin_f3 = compute_bin_index(f3, DataType(-M_PI), DataType(M_PI), 11);
667
668 fpfh.histogram[bin_f1] += weight / nn_indices.size();
669 fpfh.histogram[bin_f2 + 11] += weight / nn_indices.size();
670 fpfh.histogram[bin_f3 + 22] += weight / nn_indices.size();
671 }
672
673 total_weight += weight;
674 }
675
676 // 归一化 / Normalize
677 if (total_weight > DataType(0))
678 {
679 data_type norm_factor = DataType(1) / total_weight;
680 for (std::size_t i = 0; i < 33; ++i)
681 {
682 fpfh.histogram[i] *= norm_factor;
683 }
684 }
685}
686
690template<typename DataType, typename KNN>
691void fpfh_extractor_t<DataType, KNN>::compute_fpfh_feature_adaptive(
692 const point_cloud& cloud,
693 const point_cloud& normals,
694 std::size_t index,
695 const std::vector<std::size_t>& neighbor_indices,
696 const std::vector<data_type>& neighbor_distances,
697 std::vector<neighbor_info_t<data_type>>& all_neighbors,
698 std::vector<spfh_signature_t>& spfh_array,
699 std::vector<bool>& spfh_computed,
700 signature_type& fpfh) const
701{
702 // 初始化 / Initialize
703 std::fill(fpfh.histogram.begin(), fpfh.histogram.end(), DataType(0));
704
705 if (neighbor_indices.empty()) return;
706
707 // 复制自身的SPFH / Copy own SPFH
708 const auto& own_spfh = spfh_array[index];
709 for (std::size_t i = 0; i < 11; ++i)
710 {
711 fpfh.histogram[i] = own_spfh.f1[i];
712 fpfh.histogram[i + 11] = own_spfh.f2[i];
713 fpfh.histogram[i + 22] = own_spfh.f3[i];
714 }
715
716 // 加权累积邻居的SPFH / Weight and accumulate neighbor SPFHs
717 data_type weight_sum = DataType(0);
718
719 for (std::size_t i = 0; i < neighbor_indices.size(); ++i)
720 {
721 std::size_t neighbor_idx = neighbor_indices[i];
722 if (neighbor_idx == index) continue;
723
724 // 延迟计算邻居的SPFH(如果需要) / Lazy compute neighbor's SPFH if needed
725 if (!spfh_computed[neighbor_idx])
726 {
727 // 检查是否有缓存的邻居信息 / Check if we have cached neighbor info
728 if (!all_neighbors[neighbor_idx].computed)
729 {
730 m_knn->radius_neighbors(cloud.points[neighbor_idx], m_search_radius,
731 all_neighbors[neighbor_idx].indices,
732 all_neighbors[neighbor_idx].distances);
733 if (all_neighbors[neighbor_idx].indices.size() > m_num_neighbors)
734 {
735 all_neighbors[neighbor_idx].indices.resize(m_num_neighbors);
736 all_neighbors[neighbor_idx].distances.resize(m_num_neighbors);
737 }
738 all_neighbors[neighbor_idx].computed = true;
739 }
740
741 compute_spfh(cloud, normals, neighbor_idx,
742 all_neighbors[neighbor_idx].indices,
743 spfh_array[neighbor_idx]);
744 spfh_computed[neighbor_idx] = true;
745 }
746
747 // 使用距离倒数作为权重 / Use inverse distance as weight
748 data_type weight = DataType(1) / (neighbor_distances[i] + DataType(1e-6));
749 weight_sum += weight;
750
751 const auto& neighbor_spfh = spfh_array[neighbor_idx];
752
753 // 累积 / Accumulate
754 for (std::size_t j = 0; j < 11; ++j)
755 {
756 fpfh.histogram[j] += weight * neighbor_spfh.f1[j];
757 fpfh.histogram[j + 11] += weight * neighbor_spfh.f2[j];
758 fpfh.histogram[j + 22] += weight * neighbor_spfh.f3[j];
759 }
760 }
761
762 // 归一化 / Normalize
763 if (weight_sum > DataType(0))
764 {
765 data_type norm_factor = DataType(1) / (DataType(1) + weight_sum);
766 for (std::size_t i = 0; i < 33; ++i)
767 {
768 fpfh.histogram[i] *= norm_factor;
769 }
770 }
771}
772
773} // namespace toolbox::pcl
FPFH (Fast Point Feature Histogram) descriptor extractor.
Definition fpfh_extractor.hpp:89
void compute_impl(const point_cloud &cloud, const std::vector< std::size_t > &keypoint_indices, std::vector< signature_type > &descriptors) const
Compute descriptors for given keypoints.
Definition fpfh_extractor_impl.hpp:67
std::size_t set_num_neighbors(std::size_t num_neighbors)
Set the maximum number of neighbors to consider.
Definition fpfh_extractor_impl.hpp:48
std::size_t set_input(const point_cloud &cloud)
Set the input point cloud.
Definition fpfh_extractor_impl.hpp:14
std::shared_ptr< point_cloud > point_cloud_ptr
Definition fpfh_extractor.hpp:98
std::size_t set_knn(const knn_type &knn)
Set the KNN search algorithm.
Definition fpfh_extractor_impl.hpp:28
void enable_parallel_impl(bool enable)
Enable or disable parallel processing.
Definition fpfh_extractor_impl.hpp:61
DataType data_type
Definition fpfh_extractor.hpp:94
void set_normals(const point_cloud_ptr &normals)
Set the point cloud normals (optional, will be computed if not provided)
Definition fpfh_extractor_impl.hpp:55
KNN knn_type
Definition fpfh_extractor.hpp:96
std::size_t set_search_radius(data_type radius)
Set the search radius for neighbor search.
Definition fpfh_extractor_impl.hpp:41
基于PCA的法向量提取器 / PCA-based normal extractor
Definition pca_norm.hpp:56
void enable_parallel(bool enable)
启用或禁用并行计算 / Enable or disable parallel computation
Definition pca_norm.hpp:120
std::vector< point_t< T > > points
点坐标 / Point coordinates
Definition point.hpp:270
auto size() const -> std::size_t
获取点云中的点数 / Get number of points in cloud
Definition point_impl.hpp:293
void parallel_for_each(Iterator begin, Iterator end, Function func)
使用TBB并行对范围[begin, end)中的每个元素应用函数
Definition parallel_raw.hpp:21
Definition base_correspondence_generator.hpp:18