cpp-toolbox  0.0.1
A toolbox library for C++
Loading...
Searching...
No Matches
pfh_extractor_impl.hpp
Go to the documentation of this file.
1#pragma once
2
4#include <numeric>
6
7namespace toolbox::pcl
8{
9
10template<typename DataType, typename KNN>
12{
13 m_cloud = std::make_shared<point_cloud>(cloud);
14 return m_cloud->size();
15}
16
17template<typename DataType, typename KNN>
19{
20 m_cloud = cloud;
21 return m_cloud ? m_cloud->size() : 0;
22}
23
24template<typename DataType, typename KNN>
26{
27 m_knn = const_cast<knn_type*>(&knn);
28 if (m_cloud && m_knn)
29 {
30 // Use the points vector directly from the point cloud for new KNN API
31 m_knn->set_input(m_cloud->points);
32 }
33 return m_cloud ? m_cloud->size() : 0;
34}
35
36template<typename DataType, typename KNN>
38{
39 m_search_radius = radius;
40 return m_cloud ? m_cloud->size() : 0;
41}
42
43template<typename DataType, typename KNN>
44std::size_t pfh_extractor_t<DataType, KNN>::set_num_neighbors(std::size_t num_neighbors)
45{
46 m_num_neighbors = num_neighbors;
47 return m_cloud ? m_cloud->size() : 0;
48}
49
50template<typename DataType, typename KNN>
52{
53 m_normals = normals;
54}
55
56template<typename DataType, typename KNN>
58{
59 m_num_subdivisions = subdivisions;
60}
61
62template<typename DataType, typename KNN>
64{
65 m_enable_parallel = enable;
66}
67
68template<typename DataType, typename KNN>
70 const point_cloud& cloud,
71 const std::vector<std::size_t>& keypoint_indices,
72 std::vector<signature_type>& descriptors) const
73{
74 if (!m_knn || keypoint_indices.empty())
75 {
76 descriptors.clear();
77 return;
78 }
79
80 // Compute normals if not provided
81 point_cloud_ptr normals = m_normals;
82 if (!normals)
83 {
84 normals = std::make_shared<point_cloud>();
85 normals->points.resize(cloud.size());
87 norm_extractor.set_input(cloud);
88 norm_extractor.set_knn(*m_knn);
89 norm_extractor.set_num_neighbors(m_num_neighbors);
90 norm_extractor.enable_parallel(m_enable_parallel);
91 norm_extractor.extract(normals);
92 }
93
94 descriptors.resize(keypoint_indices.size());
95
96 auto compute_pfh_range = [&](std::size_t start, std::size_t end) {
97 for (std::size_t i = start; i < end; ++i)
98 {
99 std::size_t keypoint_idx = keypoint_indices[i];
100
101 // Get neighbors
102 std::vector<std::size_t> neighbor_indices;
103 std::vector<data_type> neighbor_distances;
104 m_knn->radius_neighbors(cloud.points[keypoint_idx], m_search_radius, neighbor_indices, neighbor_distances);
105
106 // Limit to m_num_neighbors
107 if (neighbor_indices.size() > m_num_neighbors)
108 {
109 neighbor_indices.resize(m_num_neighbors);
110 neighbor_distances.resize(m_num_neighbors);
111 }
112
113 if (neighbor_indices.size() < 3) // Need at least 3 neighbors
114 {
115 // Set empty descriptor
116 std::fill(descriptors[i].histogram.begin(), descriptors[i].histogram.end(), DataType(0));
117 continue;
118 }
119
120 // Compute PFH descriptor
121 compute_pfh_feature(cloud, *normals, keypoint_idx, neighbor_indices, descriptors[i]);
122 }
123 };
124
125 if (m_enable_parallel)
126 {
127 // Create index range for keypoints
128 std::vector<std::size_t> indices(keypoint_indices.size());
129 std::iota(indices.begin(), indices.end(), 0);
130
131 toolbox::concurrent::parallel_for_each(indices.begin(), indices.end(),
132 [&](std::size_t i) {
133 std::size_t keypoint_idx = keypoint_indices[i];
134
135 // Get neighbors
136 std::vector<std::size_t> neighbor_indices;
137 std::vector<data_type> neighbor_distances;
138 m_knn->radius_neighbors(cloud.points[keypoint_idx], m_search_radius, neighbor_indices, neighbor_distances);
139
140 // Limit to m_num_neighbors
141 if (neighbor_indices.size() > m_num_neighbors)
142 {
143 neighbor_indices.resize(m_num_neighbors);
144 neighbor_distances.resize(m_num_neighbors);
145 }
146
147 if (neighbor_indices.size() < 3) // Need at least 3 neighbors
148 {
149 // Set empty descriptor
150 std::fill(descriptors[i].histogram.begin(), descriptors[i].histogram.end(), DataType(0));
151 return;
152 }
153
154 // Compute PFH descriptor
155 compute_pfh_feature(cloud, *normals, keypoint_idx, neighbor_indices, descriptors[i]);
156 });
157 }
158 else
159 {
160 compute_pfh_range(0, keypoint_indices.size());
161 }
162}
163
164template<typename DataType, typename KNN>
166 const point_cloud& cloud,
167 const std::vector<std::size_t>& keypoint_indices,
168 std::unique_ptr<std::vector<signature_type>>& descriptors) const
169{
170 descriptors = std::make_unique<std::vector<signature_type>>();
171 compute_impl(cloud, keypoint_indices, *descriptors);
172}
173
174template<typename DataType, typename KNN>
176 const point_cloud& cloud,
177 const point_cloud& normals,
178 std::size_t index,
179 const std::vector<std::size_t>& neighbor_indices,
180 signature_type& pfh) const
181{
182 // Initialize histogram
183 std::fill(pfh.histogram.begin(), pfh.histogram.end(), DataType(0));
184
185 // Compute features for all pairs of neighbors
186 std::size_t num_pairs = 0;
187
188 for (std::size_t i = 0; i < neighbor_indices.size(); ++i)
189 {
190 for (std::size_t j = i + 1; j < neighbor_indices.size(); ++j)
191 {
192 std::size_t idx1 = neighbor_indices[i];
193 std::size_t idx2 = neighbor_indices[j];
194
195 const auto& p1 = cloud.points[idx1];
196 const auto& n1 = normals.points[idx1];
197 const auto& p2 = cloud.points[idx2];
198 const auto& n2 = normals.points[idx2];
199
200 data_type f1, f2, f3, f4;
201 compute_pair_features(p1, n1, p2, n2, f1, f2, f3, f4);
202
203 // Compute histogram bin
204 std::size_t bin_idx = compute_feature_bin_index(f1, f2, f3, f4);
205 if (bin_idx < signature_type::HISTOGRAM_SIZE)
206 {
207 pfh.histogram[bin_idx] += DataType(1);
208 num_pairs++;
209 }
210 }
211 }
212
213 // Normalize histogram
214 if (num_pairs > 0)
215 {
216 data_type norm_factor = DataType(1) / static_cast<data_type>(num_pairs);
217 for (std::size_t i = 0; i < signature_type::HISTOGRAM_SIZE; ++i)
218 {
219 pfh.histogram[i] *= norm_factor;
220 }
221 }
222}
223
224template<typename DataType, typename KNN>
225void pfh_extractor_t<DataType, KNN>::compute_pair_features(
226 const point_t<data_type>& p1,
227 const point_t<data_type>& n1,
228 const point_t<data_type>& p2,
229 const point_t<data_type>& n2,
230 data_type& f1,
231 data_type& f2,
232 data_type& f3,
233 data_type& f4) const
234{
235 // Compute the Darboux frame
236 point_t<data_type> dp;
237 dp.x = p2.x - p1.x;
238 dp.y = p2.y - p1.y;
239 dp.z = p2.z - p1.z;
240 data_type distance = dp.norm();
241
242 if (distance < DataType(1e-6))
243 {
244 f1 = f2 = f3 = f4 = DataType(0);
245 return;
246 }
247
248 auto dp_normalized = dp.normalize();
249 dp.x = static_cast<data_type>(dp_normalized.x);
250 dp.y = static_cast<data_type>(dp_normalized.y);
251 dp.z = static_cast<data_type>(dp_normalized.z);
252
253 // Source frame
254 point_t<data_type> u = n1;
255 point_t<data_type> v = dp.cross(u);
256 data_type v_norm = v.norm();
257
258 if (v_norm < DataType(1e-6))
259 {
260 // Points are aligned with normal, create arbitrary perpendicular vector
261 if (std::abs(u.x) < DataType(0.9))
262 v = point_t<data_type>(DataType(1), DataType(0), DataType(0)).cross(u);
263 else
264 v = point_t<data_type>(DataType(0), DataType(1), DataType(0)).cross(u);
265 auto v_normalized = v.normalize();
266 v.x = static_cast<data_type>(v_normalized.x);
267 v.y = static_cast<data_type>(v_normalized.y);
268 v.z = static_cast<data_type>(v_normalized.z);
269 }
270 else
271 {
272 auto v_normalized = v.normalize();
273 v.x = static_cast<data_type>(v_normalized.x);
274 v.y = static_cast<data_type>(v_normalized.y);
275 v.z = static_cast<data_type>(v_normalized.z);
276 }
277
278 point_t<data_type> w = u.cross(v);
279
280 // Compute the angles
281 data_type n2_u = n2.dot(u);
282 data_type n2_v = n2.dot(v);
283 data_type n2_w = n2.dot(w);
284 data_type n1_dp = n1.dot(dp);
285
286 // Clamp values to avoid numerical errors
287 n2_u = std::max(DataType(-1), std::min(DataType(1), n2_u));
288 n1_dp = std::max(DataType(-1), std::min(DataType(1), n1_dp));
289
290 // The four features
291 f1 = n2_v;
292 f2 = n2_u;
293 f3 = std::atan2(n2_w, n2_u); // atan2 gives angle in [-pi, pi]
294 f4 = distance;
295
296 // Normalize f4 to [0, 1] range (assuming max distance is search radius)
297 f4 = std::min(f4 / m_search_radius, DataType(1));
298}
299
300template<typename DataType, typename KNN>
301std::size_t pfh_extractor_t<DataType, KNN>::compute_feature_bin_index(
302 data_type f1,
303 data_type f2,
304 data_type f3,
305 data_type f4) const
306{
307 // Map features to bin indices
308 // f1, f2 are in [-1, 1]
309 // f3 is in [-pi, pi]
310 // f4 is in [0, 1]
311
312 // For simplicity, we only use f1, f2, f3 (as in original PFH)
313 // Each feature is divided into m_num_subdivisions bins
314
315 // Map f1 from [-1, 1] to [0, m_num_subdivisions)
316 std::size_t bin_f1 = static_cast<std::size_t>((f1 + DataType(1)) * DataType(0.5) * m_num_subdivisions);
317 bin_f1 = std::min(bin_f1, m_num_subdivisions - 1);
318
319 // Map f2 from [-1, 1] to [0, m_num_subdivisions)
320 std::size_t bin_f2 = static_cast<std::size_t>((f2 + DataType(1)) * DataType(0.5) * m_num_subdivisions);
321 bin_f2 = std::min(bin_f2, m_num_subdivisions - 1);
322
323 // Map f3 from [-pi, pi] to [0, m_num_subdivisions)
324 std::size_t bin_f3 = static_cast<std::size_t>((f3 + DataType(M_PI)) / (DataType(2) * DataType(M_PI)) * m_num_subdivisions);
325 bin_f3 = std::min(bin_f3, m_num_subdivisions - 1);
326
327 // Compute linear index
328 return bin_f1 * m_num_subdivisions * m_num_subdivisions +
329 bin_f2 * m_num_subdivisions +
330 bin_f3;
331}
332
333} // namespace toolbox::pcl
基于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
PFH (Point Feature Histogram) descriptor extractor.
Definition pfh_extractor.hpp:89
void enable_parallel_impl(bool enable)
Enable or disable parallel processing.
Definition pfh_extractor_impl.hpp:63
void set_num_subdivisions(std::size_t subdivisions)
Set the number of subdivision for each angular feature (default: 5)
Definition pfh_extractor_impl.hpp:57
KNN knn_type
Definition pfh_extractor.hpp:96
std::size_t set_knn(const knn_type &knn)
Set the KNN search algorithm.
Definition pfh_extractor_impl.hpp:25
std::size_t set_search_radius(data_type radius)
Set the search radius for neighbor search.
Definition pfh_extractor_impl.hpp:37
std::size_t set_num_neighbors(std::size_t num_neighbors)
Set the maximum number of neighbors to consider.
Definition pfh_extractor_impl.hpp:44
std::size_t set_input(const point_cloud &cloud)
Set the input point cloud.
Definition pfh_extractor_impl.hpp:11
std::shared_ptr< point_cloud > point_cloud_ptr
Definition pfh_extractor.hpp:98
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 pfh_extractor_impl.hpp:69
void set_normals(const point_cloud_ptr &normals)
Set the point cloud normals (optional, will be computed if not provided)
Definition pfh_extractor_impl.hpp:51
DataType data_type
Definition pfh_extractor.hpp:94
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