cpp-toolbox  0.0.1
A toolbox library for C++
Loading...
Searching...
No Matches
3dsc_extractor.hpp
Go to the documentation of this file.
1#pragma once
2
3#include <array>
4#include <cmath>
5#include <vector>
6
13
14namespace toolbox::pcl
15{
16
38template<typename DataType>
39struct dsc3d_signature_t : public base_signature_t<DataType, dsc3d_signature_t<DataType>>
40{
41 using value_type = DataType;
42 using data_type = DataType;
43 static constexpr std::size_t HISTOGRAM_SIZE = 1980;
44 std::array<DataType, HISTOGRAM_SIZE> histogram {};
45
50 const DataType* data() const { return histogram.data(); }
51 DataType* data() { return histogram.data(); }
52
57 constexpr std::size_t size() const { return HISTOGRAM_SIZE; }
58
64 DataType distance_impl(const dsc3d_signature_t& other) const
65 {
66 DataType dist = 0;
67 for (std::size_t i = 0; i < HISTOGRAM_SIZE; ++i) {
68 DataType diff = histogram[i] - other.histogram[i];
69 dist += diff * diff;
70 }
71 return std::sqrt(dist);
72 }
73};
74
75template<typename DataType,
76 typename KNN = kdtree_generic_t<point_t<DataType>, toolbox::metrics::L2Metric<DataType>>>
78 : public base_descriptor_extractor_t<dsc3d_extractor_t<DataType, KNN>,
79 DataType,
80 dsc3d_signature_t<DataType>>
81{
82public:
83 dsc3d_extractor_t() = default;
84
86 {
87 cloud_ = &cloud;
88 return cloud.size();
89 }
90
91 std::size_t set_knn(KNN& knn)
92 {
93 knn_ = &knn;
94 return cloud_ ? cloud_->size() : 0;
95 }
96
97 std::size_t set_search_radius(DataType radius)
98 {
99 search_radius_ = radius;
100 return cloud_ ? cloud_->size() : 0;
101 }
102
103 std::size_t set_num_neighbors(std::size_t num_neighbors)
104 {
105 num_neighbors_ = num_neighbors;
106 return cloud_ ? cloud_->size() : 0;
107 }
108
109 std::size_t set_minimal_radius(DataType radius)
110 {
111 minimal_radius_ = radius;
112 return cloud_ ? cloud_->size() : 0;
113 }
114
115 std::size_t set_point_density_radius(DataType radius)
116 {
117 point_density_radius_ = radius;
118 return cloud_ ? cloud_->size() : 0;
119 }
120
121 void enable_parallel_impl(bool enable) { enable_parallel_ = enable; }
122
124 const std::vector<std::size_t>& keypoint_indices,
125 std::vector<dsc3d_signature_t<DataType>>& descriptors) const
126 {
127 descriptors.clear();
128 descriptors.resize(keypoint_indices.size());
129
130 if (!cloud_ || !knn_)
131 return;
132
133 // Compute normals if not provided
134 std::vector<toolbox::types::point_t<DataType>> normals;
135 compute_normals(cloud, normals);
136
137 auto compute_descriptor = [&](std::size_t idx)
138 {
139 const auto keypoint_idx = keypoint_indices[idx];
140 compute_3dsc(cloud, normals, keypoint_idx, descriptors[idx]);
141 };
142
143 if (enable_parallel_) {
144// Use a simple parallel for loop
145#pragma omp parallel for
146 for (int i = 0; i < static_cast<int>(keypoint_indices.size()); ++i) {
147 compute_descriptor(static_cast<std::size_t>(i));
148 }
149 } else {
150 for (std::size_t i = 0; i < keypoint_indices.size(); ++i) {
151 compute_descriptor(i);
152 }
153 }
154 }
155
157 const std::vector<std::size_t>& keypoint_indices,
158 std::unique_ptr<std::vector<dsc3d_signature_t<DataType>>>
159 descriptors) const
160 {
161 descriptors->clear();
162 compute_impl(cloud, keypoint_indices, *descriptors);
163 }
164
165private:
166 void compute_normals(
168 std::vector<toolbox::types::point_t<DataType>>& normals) const
169 {
170 if (!knn_)
171 return;
172
174 normal_estimator.set_input(cloud);
175 normal_estimator.set_knn(*knn_);
176 normal_estimator.set_num_neighbors(num_neighbors_);
177 normal_estimator.enable_parallel(enable_parallel_);
178
179 auto normal_cloud = normal_estimator.extract();
180 normals.clear();
181 normals.reserve(normal_cloud.size());
182 for (const auto& pt : normal_cloud.points) {
183 normals.push_back(pt);
184 }
185 }
186
187 void compute_3dsc(
189 const std::vector<toolbox::types::point_t<DataType>>& normals,
190 std::size_t keypoint_idx,
191 dsc3d_signature_t<DataType>& descriptor) const
192 {
193 // Initialize histogram
194 std::fill(descriptor.histogram.begin(), descriptor.histogram.end(), 0);
195
196 const auto& keypoint = cloud.points[keypoint_idx];
197 const auto& normal = normals[keypoint_idx];
198
199 // Find neighbors
200 std::vector<std::size_t> neighbors;
201 std::vector<DataType> distances;
202 knn_->radius_neighbors(keypoint, search_radius_, neighbors, distances);
203
204 if (neighbors.size() < 3)
205 return;
206
207 // Create local reference frame
208 auto lrf = compute_local_reference_frame(
209 cloud, keypoint, normal, neighbors, keypoint_idx);
210
211 // Compute shape context
212 const std::size_t nr_bins_r = 11; // Radial bins
213 const std::size_t nr_bins_theta = 12; // Azimuth bins
214 const std::size_t nr_bins_phi = 15; // Elevation bins
215
216 DataType min_radius = minimal_radius_;
217 DataType max_radius = search_radius_;
218 DataType log_factor =
219 (std::log(max_radius) - std::log(min_radius)) / nr_bins_r;
220
221 for (const auto& neighbor_idx : neighbors) {
222 if (neighbor_idx == keypoint_idx)
223 continue;
224
225 const auto& neighbor = cloud.points[neighbor_idx];
226
227 // Transform to local frame
228 auto local_pt = transform_to_local_frame(neighbor, keypoint, lrf);
229
230 // Convert to spherical coordinates
231 DataType r = std::sqrt(local_pt.x * local_pt.x + local_pt.y * local_pt.y
232 + local_pt.z * local_pt.z);
233
234 if (r < min_radius)
235 continue;
236
237 DataType theta = std::atan2(local_pt.y, local_pt.x) + M_PI; // [0, 2*pi]
238 DataType phi = std::acos(std::min<DataType>(
239 1.0, std::max<DataType>(-1.0, local_pt.z / r))); // [0, pi]
240
241 // Compute bins
242 std::size_t bin_r = static_cast<std::size_t>(
243 (std::log(r) - std::log(min_radius)) / log_factor);
244 std::size_t bin_theta =
245 static_cast<std::size_t>(theta / (2.0 * M_PI) * nr_bins_theta);
246 std::size_t bin_phi = static_cast<std::size_t>(phi / M_PI * nr_bins_phi);
247
248 // Clamp bins
249 if (bin_r >= nr_bins_r)
250 bin_r = nr_bins_r - 1;
251 if (bin_theta >= nr_bins_theta)
252 bin_theta = nr_bins_theta - 1;
253 if (bin_phi >= nr_bins_phi)
254 bin_phi = nr_bins_phi - 1;
255
256 // Update histogram
257 std::size_t bin_idx = bin_r * nr_bins_theta * nr_bins_phi
258 + bin_theta * nr_bins_phi + bin_phi;
259 descriptor.histogram[bin_idx] += 1.0;
260 }
261
262 // Normalize by point density
263 DataType density_weight = compute_point_density(cloud, keypoint);
264
265 // Normalize histogram
266 DataType sum = 0;
267 for (auto& val : descriptor.histogram) {
268 sum += val;
269 }
270 if (sum > 0) {
271 for (auto& val : descriptor.histogram) {
272 val = (val / sum) * density_weight;
273 }
274 }
275 }
276
277 struct LocalReferenceFrame
278 {
282 };
283
284 LocalReferenceFrame compute_local_reference_frame(
286 const toolbox::types::point_t<DataType>& keypoint,
288 const std::vector<std::size_t>& neighbors,
289 std::size_t keypoint_idx) const
290 {
291 LocalReferenceFrame lrf;
292
293 // Z-axis is the normal
294 lrf.z_axis = normal;
295 auto z_norm = lrf.z_axis.norm();
296 if (z_norm > 0) {
297 lrf.z_axis.x /= z_norm;
298 lrf.z_axis.y /= z_norm;
299 lrf.z_axis.z /= z_norm;
300 }
301
302 // Find the point with maximum angle from normal
303 DataType max_angle = 0;
304 toolbox::types::point_t<DataType> max_point(0, 0, 0);
305
306 for (const auto& idx : neighbors) {
307 if (idx == keypoint_idx)
308 continue;
309
310 auto diff = cloud.points[idx];
311 diff.x -= keypoint.x;
312 diff.y -= keypoint.y;
313 diff.z -= keypoint.z;
314
315 auto d_norm = diff.norm();
316 if (d_norm > 0) {
317 diff.x /= d_norm;
318 diff.y /= d_norm;
319 diff.z /= d_norm;
320 }
321
322 DataType angle = std::acos(std::min<DataType>(
323 1.0,
324 std::max<DataType>(
325 -1.0,
326 std::abs(diff.x * lrf.z_axis.x + diff.y * lrf.z_axis.y
327 + diff.z * lrf.z_axis.z))));
328
329 if (angle > max_angle) {
330 max_angle = angle;
331 max_point = diff;
332 }
333 }
334
335 // X-axis perpendicular to Z
336 lrf.x_axis = max_point;
337 DataType dot = lrf.x_axis.x * lrf.z_axis.x + lrf.x_axis.y * lrf.z_axis.y
338 + lrf.x_axis.z * lrf.z_axis.z;
339 lrf.x_axis.x -= dot * lrf.z_axis.x;
340 lrf.x_axis.y -= dot * lrf.z_axis.y;
341 lrf.x_axis.z -= dot * lrf.z_axis.z;
342
343 auto x_norm = lrf.x_axis.norm();
344 if (x_norm > 0) {
345 lrf.x_axis.x /= x_norm;
346 lrf.x_axis.y /= x_norm;
347 lrf.x_axis.z /= x_norm;
348 }
349
350 // Y-axis = Z x X
351 lrf.y_axis.x = lrf.z_axis.y * lrf.x_axis.z - lrf.z_axis.z * lrf.x_axis.y;
352 lrf.y_axis.y = lrf.z_axis.z * lrf.x_axis.x - lrf.z_axis.x * lrf.x_axis.z;
353 lrf.y_axis.z = lrf.z_axis.x * lrf.x_axis.y - lrf.z_axis.y * lrf.x_axis.x;
354
355 return lrf;
356 }
357
358 toolbox::types::point_t<DataType> transform_to_local_frame(
361 const LocalReferenceFrame& lrf) const
362 {
363 // Translate
364 auto p = point;
365 p.x -= origin.x;
366 p.y -= origin.y;
367 p.z -= origin.z;
368
369 // Rotate
371 result.x = p.x * lrf.x_axis.x + p.y * lrf.x_axis.y + p.z * lrf.x_axis.z;
372 result.y = p.x * lrf.y_axis.x + p.y * lrf.y_axis.y + p.z * lrf.y_axis.z;
373 result.z = p.x * lrf.z_axis.x + p.y * lrf.z_axis.y + p.z * lrf.z_axis.z;
374
375 return result;
376 }
377
378 DataType compute_point_density(
380 const toolbox::types::point_t<DataType>& point) const
381 {
382 std::vector<std::size_t> neighbors;
383 std::vector<DataType> distances;
384 knn_->radius_neighbors(point, point_density_radius_, neighbors, distances);
385
386 DataType volume = (4.0 / 3.0) * M_PI * point_density_radius_
387 * point_density_radius_ * point_density_radius_;
388
389 return static_cast<DataType>(neighbors.size()) / volume;
390 }
391
392private:
393 const toolbox::types::point_cloud_t<DataType>* cloud_ = nullptr;
394 KNN* knn_ = nullptr;
395 DataType search_radius_ = 0.5;
396 DataType minimal_radius_ = 0.01;
397 DataType point_density_radius_ = 0.05;
398 std::size_t num_neighbors_ = 10;
399 bool enable_parallel_ = true;
400};
401
402} // namespace toolbox::pcl
Definition vector_metrics.hpp:18
描述子提取器的基类(CRTP模式) / Base class for descriptor extractors (CRTP pattern)
Definition base_descriptor_extractor.hpp:91
Definition 3dsc_extractor.hpp:81
void enable_parallel_impl(bool enable)
Definition 3dsc_extractor.hpp:121
std::size_t set_point_density_radius(DataType radius)
Definition 3dsc_extractor.hpp:115
std::size_t set_search_radius(DataType radius)
Definition 3dsc_extractor.hpp:97
std::size_t set_minimal_radius(DataType radius)
Definition 3dsc_extractor.hpp:109
void compute_impl(const toolbox::types::point_cloud_t< DataType > &cloud, const std::vector< std::size_t > &keypoint_indices, std::unique_ptr< std::vector< dsc3d_signature_t< DataType > > > descriptors) const
Definition 3dsc_extractor.hpp:156
std::size_t set_knn(KNN &knn)
Definition 3dsc_extractor.hpp:91
std::size_t set_input(const toolbox::types::point_cloud_t< DataType > &cloud)
Definition 3dsc_extractor.hpp:85
std::size_t set_num_neighbors(std::size_t num_neighbors)
Definition 3dsc_extractor.hpp:103
void compute_impl(const toolbox::types::point_cloud_t< DataType > &cloud, const std::vector< std::size_t > &keypoint_indices, std::vector< dsc3d_signature_t< DataType > > &descriptors) const
Definition 3dsc_extractor.hpp:123
基于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
3D形状上下文描述子签名 / 3D Shape Context descriptor signature
Definition 3dsc_extractor.hpp:40
const DataType * data() const
提供data()方法以兼容IMetric接口 / Provide data() method for IMetric interface compatibility
Definition 3dsc_extractor.hpp:50
DataType distance_impl(const dsc3d_signature_t &other) const
计算与另一个描述子的欧氏距离 / Compute Euclidean distance to another descriptor
Definition 3dsc_extractor.hpp:64
std::array< DataType, HISTOGRAM_SIZE > histogram
直方图数据 / Histogram data
Definition 3dsc_extractor.hpp:44
DataType value_type
为了兼容KNN接口 / For KNN interface compatibility
Definition 3dsc_extractor.hpp:41
DataType data_type
为了兼容描述子匹配接口 / For descriptor matching interface compatibility
Definition 3dsc_extractor.hpp:42
DataType * data()
Definition 3dsc_extractor.hpp:51
static constexpr std::size_t HISTOGRAM_SIZE
直方图大小:11 × 12 × 15 / Histogram size: 11 × 12 × 15
Definition 3dsc_extractor.hpp:43
constexpr std::size_t size() const
获取直方图大小 / Get histogram size
Definition 3dsc_extractor.hpp:57
3D点/向量模板类 / A 3D point/vector template class
Definition point.hpp:48
T x
X坐标 / X coordinate.
Definition point.hpp:51
T z
Z坐标 / Z coordinate.
Definition point.hpp:53
T y
Y坐标 / Y coordinate.
Definition point.hpp:52