cpp-toolbox  0.0.1
A toolbox library for C++
Loading...
Searching...
No Matches
cvfh_extractor.hpp
Go to the documentation of this file.
1#pragma once
2
3#include <algorithm>
4#include <array>
5#include <vector>
6
13
14namespace toolbox::pcl
15{
16
17template<typename DataType>
18struct cvfh_signature_t : public base_signature_t<DataType, cvfh_signature_t<DataType>>
19{
20 static constexpr std::size_t HISTOGRAM_SIZE = 308; // Same as VFH
21 std::array<DataType, HISTOGRAM_SIZE> histogram {};
22
23 DataType distance_impl(const cvfh_signature_t& other) const
24 {
25 DataType dist = 0;
26 for (std::size_t i = 0; i < HISTOGRAM_SIZE; ++i) {
27 DataType diff = histogram[i] - other.histogram[i];
28 dist += diff * diff;
29 }
30 return std::sqrt(dist);
31 }
32};
33
34template<typename DataType,
35 typename KNN = kdtree_generic_t<point_t<DataType>, toolbox::metrics::L2Metric<DataType>>>
37 : public base_descriptor_extractor_t<cvfh_extractor_t<DataType, KNN>,
38 DataType,
39 cvfh_signature_t<DataType>>
40{
41public:
42 cvfh_extractor_t() = default;
43
45 {
46 cloud_ = &cloud;
47 return cloud.size();
48 }
49
50 std::size_t set_knn(KNN& knn)
51 {
52 knn_ = &knn;
53 return cloud_ ? cloud_->size() : 0;
54 }
55
56 std::size_t set_search_radius(DataType radius)
57 {
58 search_radius_ = radius;
59 return cloud_ ? cloud_->size() : 0;
60 }
61
62 std::size_t set_num_neighbors(std::size_t num_neighbors)
63 {
64 num_neighbors_ = num_neighbors;
65 return cloud_ ? cloud_->size() : 0;
66 }
67
68 std::size_t set_cluster_tolerance(DataType tolerance)
69 {
70 cluster_tolerance_ = tolerance;
71 return cloud_ ? cloud_->size() : 0;
72 }
73
74 std::size_t set_eps_angle_threshold(DataType threshold)
75 {
76 eps_angle_threshold_ = threshold;
77 return cloud_ ? cloud_->size() : 0;
78 }
79
80 std::size_t set_curvature_threshold(DataType threshold)
81 {
82 curvature_threshold_ = threshold;
83 return cloud_ ? cloud_->size() : 0;
84 }
85
86 void enable_parallel_impl(bool enable) { enable_parallel_ = enable; }
87
89 const std::vector<std::size_t>& keypoint_indices,
90 std::vector<cvfh_signature_t<DataType>>& descriptors) const
91 {
92 descriptors.clear();
93
94 if (!cloud_ || !knn_)
95 return;
96
97 // Compute normals
98 std::vector<toolbox::types::point_t<DataType>> normals;
99 compute_normals(cloud, normals);
100
101 // Segment cloud into smooth clusters
102 std::vector<std::vector<std::size_t>> clusters;
103 segment_smooth_clusters(cloud, normals, clusters);
104
105 // Compute CVFH for each cluster
106 for (const auto& cluster : clusters) {
107 if (cluster.size() < 3)
108 continue;
109
111 compute_cluster_vfh(cloud, normals, cluster, cvfh);
112 descriptors.push_back(cvfh);
113 }
114 }
115
117 const std::vector<std::size_t>& keypoint_indices,
118 std::unique_ptr<std::vector<cvfh_signature_t<DataType>>>
119 descriptors) const
120 {
121 descriptors->clear();
122 compute_impl(cloud, keypoint_indices, *descriptors);
123 }
124
125private:
126 void compute_normals(
128 std::vector<toolbox::types::point_t<DataType>>& normals) const
129 {
130 if (!knn_)
131 return;
132
134 normal_estimator.set_input(cloud);
135 normal_estimator.set_knn(*knn_);
136 normal_estimator.set_num_neighbors(num_neighbors_);
137 normal_estimator.enable_parallel(enable_parallel_);
138
139 auto normal_cloud = normal_estimator.extract();
140 normals.clear();
141 normals.reserve(normal_cloud.size());
142 for (const auto& pt : normal_cloud.points) {
143 normals.push_back(pt);
144 }
145 }
146
147 void segment_smooth_clusters(
149 const std::vector<toolbox::types::point_t<DataType>>& normals,
150 std::vector<std::vector<std::size_t>>& clusters) const
151 {
152 std::vector<bool> processed(cloud.points.size(), false);
153
154 for (std::size_t i = 0; i < cloud.points.size(); ++i) {
155 if (processed[i])
156 continue;
157
158 std::vector<std::size_t> cluster;
159 std::vector<std::size_t> seeds;
160 seeds.push_back(i);
161 processed[i] = true;
162
163 while (!seeds.empty()) {
164 std::size_t current = seeds.back();
165 seeds.pop_back();
166 cluster.push_back(current);
167
168 // Find neighbors
169 std::vector<std::size_t> neighbors;
170 std::vector<DataType> distances;
171 knn_->radius_neighbors(
172 cloud.points[current], cluster_tolerance_, neighbors, distances);
173
174 for (const auto& neighbor_idx : neighbors) {
175 if (processed[neighbor_idx])
176 continue;
177
178 // Check normal similarity
179 const auto& n1 = normals[current];
180 const auto& n2 = normals[neighbor_idx];
181
182 DataType dot = n1.x * n2.x + n1.y * n2.y + n1.z * n2.z;
183 DataType angle =
184 std::acos(std::min<DataType>(1.0, std::max<DataType>(-1.0, dot)));
185
186 if (angle < eps_angle_threshold_) {
187 seeds.push_back(neighbor_idx);
188 processed[neighbor_idx] = true;
189 }
190 }
191 }
192
193 if (cluster.size() >= 3) {
194 clusters.push_back(cluster);
195 }
196 }
197 }
198
199 void compute_cluster_vfh(
201 const std::vector<toolbox::types::point_t<DataType>>& normals,
202 const std::vector<std::size_t>& cluster,
203 cvfh_signature_t<DataType>& cvfh) const
204 {
205 // Compute cluster centroid
206 toolbox::types::point_t<DataType> centroid(0, 0, 0);
207 for (const auto& idx : cluster) {
208 centroid.x += cloud.points[idx].x;
209 centroid.y += cloud.points[idx].y;
210 centroid.z += cloud.points[idx].z;
211 }
212 centroid.x /= cluster.size();
213 centroid.y /= cluster.size();
214 centroid.z /= cluster.size();
215
216 // Compute viewpoint
217 toolbox::types::point_t<DataType> viewpoint(0, 0, 100);
218
219 // Initialize histogram
220 std::fill(cvfh.histogram.begin(), cvfh.histogram.end(), 0);
221
222 const std::size_t nr_bins_f1 = 45;
223 const std::size_t nr_bins_f2 = 45;
224 const std::size_t nr_bins_f3 = 45;
225 const std::size_t nr_bins_f4 = 45;
226 const std::size_t nr_bins_vp = 128;
227
228 // Viewpoint direction
229 auto vp_dir = viewpoint;
230 vp_dir.x -= centroid.x;
231 vp_dir.y -= centroid.y;
232 vp_dir.z -= centroid.z;
233 auto vp_norm = vp_dir.normalize();
234
235 // Compute shape distribution
236 for (std::size_t i = 0; i < cluster.size(); ++i) {
237 const auto& idx_i = cluster[i];
238 const auto& pi = cloud.points[idx_i];
239 const auto& ni = normals[idx_i];
240
241 // Viewpoint angle
242 DataType vp_angle = std::acos(std::min<DataType>(
243 1.0,
244 std::max<DataType>(
245 -1.0, ni.x * vp_norm.x + ni.y * vp_norm.y + ni.z * vp_norm.z)));
246
247 std::size_t vp_bin =
248 static_cast<std::size_t>(vp_angle / M_PI * nr_bins_vp);
249 if (vp_bin >= nr_bins_vp)
250 vp_bin = nr_bins_vp - 1;
251 cvfh.histogram[180 + vp_bin] += 1.0;
252
253 // Compute pairwise features
254 for (std::size_t j = i + 1; j < cluster.size(); ++j) {
255 const auto& idx_j = cluster[j];
256 const auto& pj = cloud.points[idx_j];
257 const auto& nj = normals[idx_j];
258
259 // Compute features
260 auto d = pj;
261 d.x -= pi.x;
262 d.y -= pi.y;
263 d.z -= pi.z;
264 DataType dist = std::sqrt(d.x * d.x + d.y * d.y + d.z * d.z);
265
266 if (dist < 1e-8)
267 continue;
268
269 d.x /= dist;
270 d.y /= dist;
271 d.z /= dist;
272
273 // Compute angles
274 DataType f1 = ni.x * d.x + ni.y * d.y + ni.z * d.z;
275 DataType f2 = (d.x * nj.x + d.y * nj.y + d.z * nj.z) - f1;
276 DataType f3 = std::atan2(ni.y * d.z - ni.z * d.y,
277 ni.x * d.x + ni.y * d.y + ni.z * d.z);
278 DataType f4 = std::atan2(nj.y * d.z - nj.z * d.y,
279 nj.x * d.x + nj.y * d.y + nj.z * d.z)
280 - f3;
281
282 // Normalize
283 f1 = (f1 + 1.0) * 0.5;
284 f2 = (f2 + 1.0) * 0.5;
285 f3 = (f3 + M_PI) / (2.0 * M_PI);
286 f4 = (f4 + M_PI) / (2.0 * M_PI);
287
288 // Bins
289 std::size_t bin_f1 = static_cast<std::size_t>(f1 * nr_bins_f1);
290 std::size_t bin_f2 = static_cast<std::size_t>(f2 * nr_bins_f2);
291 std::size_t bin_f3 = static_cast<std::size_t>(f3 * nr_bins_f3);
292 std::size_t bin_f4 = static_cast<std::size_t>(f4 * nr_bins_f4);
293
294 if (bin_f1 >= nr_bins_f1)
295 bin_f1 = nr_bins_f1 - 1;
296 if (bin_f2 >= nr_bins_f2)
297 bin_f2 = nr_bins_f2 - 1;
298 if (bin_f3 >= nr_bins_f3)
299 bin_f3 = nr_bins_f3 - 1;
300 if (bin_f4 >= nr_bins_f4)
301 bin_f4 = nr_bins_f4 - 1;
302
303 // Update histogram with distance weighting
304 DataType weight = 1.0 / (1.0 + dist);
305 cvfh.histogram[bin_f1] += weight;
306 cvfh.histogram[45 + bin_f2] += weight;
307 cvfh.histogram[90 + bin_f3] += weight;
308 cvfh.histogram[135 + bin_f4] += weight;
309 }
310 }
311
312 // Normalize
313 DataType sum = 0;
314 for (auto& val : cvfh.histogram) {
315 sum += val;
316 }
317 if (sum > 0) {
318 for (auto& val : cvfh.histogram) {
319 val /= sum;
320 }
321 }
322 }
323
324private:
325 const toolbox::types::point_cloud_t<DataType>* cloud_ = nullptr;
326 KNN* knn_ = nullptr;
327 DataType search_radius_ = 0.1;
328 DataType cluster_tolerance_ = 0.05;
329 DataType eps_angle_threshold_ = 0.08; // ~5 degrees
330 DataType curvature_threshold_ = 0.1;
331 std::size_t num_neighbors_ = 10;
332 bool enable_parallel_ = true;
333};
334
335} // namespace toolbox::pcl
Definition vector_metrics.hpp:18
描述子提取器的基类(CRTP模式) / Base class for descriptor extractors (CRTP pattern)
Definition base_descriptor_extractor.hpp:91
Definition cvfh_extractor.hpp:40
std::size_t set_input(const toolbox::types::point_cloud_t< DataType > &cloud)
Definition cvfh_extractor.hpp:44
void compute_impl(const toolbox::types::point_cloud_t< DataType > &cloud, const std::vector< std::size_t > &keypoint_indices, std::unique_ptr< std::vector< cvfh_signature_t< DataType > > > descriptors) const
Definition cvfh_extractor.hpp:116
void compute_impl(const toolbox::types::point_cloud_t< DataType > &cloud, const std::vector< std::size_t > &keypoint_indices, std::vector< cvfh_signature_t< DataType > > &descriptors) const
Definition cvfh_extractor.hpp:88
std::size_t set_num_neighbors(std::size_t num_neighbors)
Definition cvfh_extractor.hpp:62
std::size_t set_curvature_threshold(DataType threshold)
Definition cvfh_extractor.hpp:80
std::size_t set_search_radius(DataType radius)
Definition cvfh_extractor.hpp:56
std::size_t set_cluster_tolerance(DataType tolerance)
Definition cvfh_extractor.hpp:68
void enable_parallel_impl(bool enable)
Definition cvfh_extractor.hpp:86
std::size_t set_eps_angle_threshold(DataType threshold)
Definition cvfh_extractor.hpp:74
std::size_t set_knn(KNN &knn)
Definition cvfh_extractor.hpp:50
基于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
包含点和相关数据的点云类 / A point cloud class containing points and associated data
Definition point.hpp:268
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
TContainer::value_type sum(const TContainer &data)
计算容器中元素的总和(返回容器元素类型)/Compute the sum of elements in the container (returns container element type)
Definition statistics.hpp:288
Definition base_correspondence_generator.hpp:18
描述子签名的基类 / Base class for descriptor signatures
Definition base_descriptor_extractor.hpp:38
Definition cvfh_extractor.hpp:19
DataType distance_impl(const cvfh_signature_t &other) const
Definition cvfh_extractor.hpp:23
std::array< DataType, HISTOGRAM_SIZE > histogram
Definition cvfh_extractor.hpp:21
static constexpr std::size_t HISTOGRAM_SIZE
Definition cvfh_extractor.hpp:20
3D点/向量模板类 / A 3D point/vector template class
Definition point.hpp:48