cpp-toolbox  0.0.1
A toolbox library for C++
Loading...
Searching...
No Matches
point_cloud_metrics.hpp
Go to the documentation of this file.
1#pragma once
2
3#include <algorithm>
4#include <cmath>
5#include <limits>
6#include <numeric>
7#include <unordered_map>
8#include <vector>
9
13
14namespace toolbox::metrics
15{
16
23template<typename T>
24class HausdorffMetric : public base_metric_t<HausdorffMetric<T>, T>
25{
26public:
27 using element_type = T;
30
31 // For compatibility with base_metric_t, we use pointers to clouds
32 constexpr T distance_impl(const T* cloud_a, const T* cloud_b, std::size_t size) const
33 {
34 // This overload is not used for point clouds
35 throw std::runtime_error("HausdorffMetric requires point cloud objects, not raw arrays");
36 }
37
38 T distance(const cloud_type& cloud_a, const cloud_type& cloud_b) const
39 {
40 if (cloud_a.empty() || cloud_b.empty()) {
41 return std::numeric_limits<T>::infinity();
42 }
43
44 // Compute directed Hausdorff distance from A to B
45 T max_dist_a_to_b = 0;
46 for (const auto& point_a : cloud_a.points) {
47 T min_dist = std::numeric_limits<T>::max();
48 for (const auto& point_b : cloud_b.points) {
49 T dist = point_a.distance(point_b);
50 min_dist = std::min(min_dist, dist);
51 }
52 max_dist_a_to_b = std::max(max_dist_a_to_b, min_dist);
53 }
54
55 // Compute directed Hausdorff distance from B to A
56 T max_dist_b_to_a = 0;
57 for (const auto& point_b : cloud_b.points) {
58 T min_dist = std::numeric_limits<T>::max();
59 for (const auto& point_a : cloud_a.points) {
60 T dist = point_b.distance(point_a);
61 min_dist = std::min(min_dist, dist);
62 }
63 max_dist_b_to_a = std::max(max_dist_b_to_a, min_dist);
64 }
65
66 // Return the maximum of both directions
67 return std::max(max_dist_a_to_b, max_dist_b_to_a);
68 }
69
70 constexpr T squared_distance_impl(const T* a, const T* b, std::size_t size) const
71 {
72 T dist = distance_impl(a, b, size);
73 return dist * dist;
74 }
75};
76
82template<typename T>
83class ModifiedHausdorffMetric : public base_metric_t<ModifiedHausdorffMetric<T>, T>
84{
85public:
86 using element_type = T;
89
90 explicit ModifiedHausdorffMetric(std::size_t k = 1) : k_(k) {}
91
92 constexpr T distance_impl(const T* cloud_a, const T* cloud_b, std::size_t size) const
93 {
94 throw std::runtime_error("ModifiedHausdorffMetric requires point cloud objects");
95 }
96
97 T distance(const cloud_type& cloud_a, const cloud_type& cloud_b) const
98 {
99 if (cloud_a.empty() || cloud_b.empty()) {
100 return std::numeric_limits<T>::infinity();
101 }
102
103 // Ensure k is valid
104 std::size_t k_a = std::min(k_, cloud_a.size());
105 std::size_t k_b = std::min(k_, cloud_b.size());
106
107 // Compute distances from each point in A to all points in B
108 std::vector<T> min_dists_a;
109 min_dists_a.reserve(cloud_a.size());
110
111 for (const auto& point_a : cloud_a.points) {
112 T min_dist = std::numeric_limits<T>::max();
113 for (const auto& point_b : cloud_b.points) {
114 T dist = point_a.distance(point_b);
115 min_dist = std::min(min_dist, dist);
116 }
117 min_dists_a.push_back(min_dist);
118 }
119
120 // Sort and take average of k smallest
121 std::partial_sort(min_dists_a.begin(), min_dists_a.begin() + k_a, min_dists_a.end());
122 T avg_a_to_b = std::accumulate(min_dists_a.begin(), min_dists_a.begin() + k_a, T(0)) / k_a;
123
124 // Compute distances from each point in B to all points in A
125 std::vector<T> min_dists_b;
126 min_dists_b.reserve(cloud_b.size());
127
128 for (const auto& point_b : cloud_b.points) {
129 T min_dist = std::numeric_limits<T>::max();
130 for (const auto& point_a : cloud_a.points) {
131 T dist = point_b.distance(point_a);
132 min_dist = std::min(min_dist, dist);
133 }
134 min_dists_b.push_back(min_dist);
135 }
136
137 // Sort and take average of k smallest
138 std::partial_sort(min_dists_b.begin(), min_dists_b.begin() + k_b, min_dists_b.end());
139 T avg_b_to_a = std::accumulate(min_dists_b.begin(), min_dists_b.begin() + k_b, T(0)) / k_b;
140
141 return std::max(avg_a_to_b, avg_b_to_a);
142 }
143
144 constexpr T squared_distance_impl(const T* a, const T* b, std::size_t size) const
145 {
146 T dist = distance_impl(a, b, size);
147 return dist * dist;
148 }
149
150private:
151 std::size_t k_;
152};
153
159template<typename T>
160class ChamferMetric : public base_metric_t<ChamferMetric<T>, T>
161{
162public:
163 using element_type = T;
166
167 constexpr T distance_impl(const T* cloud_a, const T* cloud_b, std::size_t size) const
168 {
169 throw std::runtime_error("ChamferMetric requires point cloud objects");
170 }
171
172 T distance(const cloud_type& cloud_a, const cloud_type& cloud_b) const
173 {
174 if (cloud_a.empty() || cloud_b.empty()) {
175 return std::numeric_limits<T>::infinity();
176 }
177
178 // Compute average distance from A to B
179 T sum_a_to_b = 0;
180 for (const auto& point_a : cloud_a.points) {
181 T min_dist = std::numeric_limits<T>::max();
182 for (const auto& point_b : cloud_b.points) {
183 T dist = point_a.distance(point_b);
184 min_dist = std::min(min_dist, dist);
185 }
186 sum_a_to_b += min_dist;
187 }
188 T avg_a_to_b = sum_a_to_b / cloud_a.size();
189
190 // Compute average distance from B to A
191 T sum_b_to_a = 0;
192 for (const auto& point_b : cloud_b.points) {
193 T min_dist = std::numeric_limits<T>::max();
194 for (const auto& point_a : cloud_a.points) {
195 T dist = point_b.distance(point_a);
196 min_dist = std::min(min_dist, dist);
197 }
198 sum_b_to_a += min_dist;
199 }
200 T avg_b_to_a = sum_b_to_a / cloud_b.size();
201
202 // Return average of both directions
203 return (avg_a_to_b + avg_b_to_a) / 2;
204 }
205
206 constexpr T squared_distance_impl(const T* a, const T* b, std::size_t size) const
207 {
208 T dist = distance_impl(a, b, size);
209 return dist * dist;
210 }
211};
212
218template<typename T>
219class PointCloudEMDMetric : public base_metric_t<PointCloudEMDMetric<T>, T>
220{
221public:
222 using element_type = T;
225
226 constexpr T distance_impl(const T* cloud_a, const T* cloud_b, std::size_t size) const
227 {
228 throw std::runtime_error("PointCloudEMDMetric requires point cloud objects");
229 }
230
231 T distance(const cloud_type& cloud_a, const cloud_type& cloud_b) const
232 {
233 if (cloud_a.empty() || cloud_b.empty()) {
234 return std::numeric_limits<T>::infinity();
235 }
236
237 // For equal-sized clouds, compute minimum matching cost
238 if (cloud_a.size() == cloud_b.size()) {
239 // Simple greedy matching (not optimal but fast)
240 std::vector<bool> matched_b(cloud_b.size(), false);
241 T total_cost = 0;
242
243 for (const auto& point_a : cloud_a.points) {
244 T min_dist = std::numeric_limits<T>::max();
245 std::size_t best_match = 0;
246
247 for (std::size_t j = 0; j < cloud_b.size(); ++j) {
248 if (!matched_b[j]) {
249 T dist = point_a.distance(cloud_b.points[j]);
250 if (dist < min_dist) {
251 min_dist = dist;
252 best_match = j;
253 }
254 }
255 }
256
257 matched_b[best_match] = true;
258 total_cost += min_dist;
259 }
260
261 return total_cost / cloud_a.size();
262 }
263 else {
264 // For different-sized clouds, use Chamfer distance as approximation
265 ChamferMetric<T> chamfer;
266 return chamfer.distance(cloud_a, cloud_b);
267 }
268 }
269
270 constexpr T squared_distance_impl(const T* a, const T* b, std::size_t size) const
271 {
272 T dist = distance_impl(a, b, size);
273 return dist * dist;
274 }
275};
276
282template<typename T>
283class CentroidMetric : public base_metric_t<CentroidMetric<T>, T>
284{
285public:
286 using element_type = T;
289
290 constexpr T distance_impl(const T* cloud_a, const T* cloud_b, std::size_t size) const
291 {
292 throw std::runtime_error("CentroidMetric requires point cloud objects");
293 }
294
295 T distance(const cloud_type& cloud_a, const cloud_type& cloud_b) const
296 {
297 if (cloud_a.empty() || cloud_b.empty()) {
298 return std::numeric_limits<T>::infinity();
299 }
300
301 // Compute centroid of cloud A
302 point_type centroid_a(0, 0, 0);
303 for (const auto& point : cloud_a.points) {
304 centroid_a.x += point.x;
305 centroid_a.y += point.y;
306 centroid_a.z += point.z;
307 }
308 centroid_a.x /= cloud_a.size();
309 centroid_a.y /= cloud_a.size();
310 centroid_a.z /= cloud_a.size();
311
312 // Compute centroid of cloud B
313 point_type centroid_b(0, 0, 0);
314 for (const auto& point : cloud_b.points) {
315 centroid_b.x += point.x;
316 centroid_b.y += point.y;
317 centroid_b.z += point.z;
318 }
319 centroid_b.x /= cloud_b.size();
320 centroid_b.y /= cloud_b.size();
321 centroid_b.z /= cloud_b.size();
322
323 return centroid_a.distance(centroid_b);
324 }
325
326 constexpr T squared_distance_impl(const T* a, const T* b, std::size_t size) const
327 {
328 T dist = distance_impl(a, b, size);
329 return dist * dist;
330 }
331};
332
338template<typename T>
339class BoundingBoxMetric : public base_metric_t<BoundingBoxMetric<T>, T>
340{
341public:
342 using element_type = T;
345
346 enum class Mode {
347 CENTER_DISTANCE, // Distance between bounding box centers
348 MIN_DISTANCE, // Minimum distance between bounding boxes
349 IOU_DISTANCE // 1 - IoU (Intersection over Union)
350 };
351
352 explicit BoundingBoxMetric(Mode mode = Mode::CENTER_DISTANCE) : mode_(mode) {}
353
354 constexpr T distance_impl(const T* cloud_a, const T* cloud_b, std::size_t size) const
355 {
356 throw std::runtime_error("BoundingBoxMetric requires point cloud objects");
357 }
358
359 T distance(const cloud_type& cloud_a, const cloud_type& cloud_b) const
360 {
361 if (cloud_a.empty() || cloud_b.empty()) {
362 return std::numeric_limits<T>::infinity();
363 }
364
365 // Compute bounding box for cloud A
366 auto [min_a, max_a] = compute_bounding_box(cloud_a);
367
368 // Compute bounding box for cloud B
369 auto [min_b, max_b] = compute_bounding_box(cloud_b);
370
371 switch (mode_) {
373 point_type center_a((min_a.x + max_a.x) / 2,
374 (min_a.y + max_a.y) / 2,
375 (min_a.z + max_a.z) / 2);
376 point_type center_b((min_b.x + max_b.x) / 2,
377 (min_b.y + max_b.y) / 2,
378 (min_b.z + max_b.z) / 2);
379 return center_a.distance(center_b);
380 }
381
382 case Mode::MIN_DISTANCE: {
383 // Check if boxes overlap
384 bool overlap_x = !(max_a.x < min_b.x || max_b.x < min_a.x);
385 bool overlap_y = !(max_a.y < min_b.y || max_b.y < min_a.y);
386 bool overlap_z = !(max_a.z < min_b.z || max_b.z < min_a.z);
387
388 if (overlap_x && overlap_y && overlap_z) {
389 return T(0); // Boxes overlap
390 }
391
392 // Compute minimum distance between boxes
393 T dx = std::max(T(0), std::max(min_a.x - max_b.x, min_b.x - max_a.x));
394 T dy = std::max(T(0), std::max(min_a.y - max_b.y, min_b.y - max_a.y));
395 T dz = std::max(T(0), std::max(min_a.z - max_b.z, min_b.z - max_a.z));
396
397 return std::sqrt(dx * dx + dy * dy + dz * dz);
398 }
399
400 case Mode::IOU_DISTANCE: {
401 // Compute intersection
402 T inter_x = std::max(T(0), std::min(max_a.x, max_b.x) - std::max(min_a.x, min_b.x));
403 T inter_y = std::max(T(0), std::min(max_a.y, max_b.y) - std::max(min_a.y, min_b.y));
404 T inter_z = std::max(T(0), std::min(max_a.z, max_b.z) - std::max(min_a.z, min_b.z));
405 T intersection_volume = inter_x * inter_y * inter_z;
406
407 // Compute volumes
408 T volume_a = (max_a.x - min_a.x) * (max_a.y - min_a.y) * (max_a.z - min_a.z);
409 T volume_b = (max_b.x - min_b.x) * (max_b.y - min_b.y) * (max_b.z - min_b.z);
410 T union_volume = volume_a + volume_b - intersection_volume;
411
412 if (union_volume < std::numeric_limits<T>::epsilon()) {
413 return T(1); // Both boxes are degenerate
414 }
415
416 T iou = intersection_volume / union_volume;
417 return T(1) - iou;
418 }
419 }
420
421 return T(0);
422 }
423
424 constexpr T squared_distance_impl(const T* a, const T* b, std::size_t size) const
425 {
426 T dist = distance_impl(a, b, size);
427 return dist * dist;
428 }
429
430private:
431 Mode mode_;
432
433 std::pair<point_type, point_type> compute_bounding_box(const cloud_type& cloud) const
434 {
435 point_type min_pt = cloud.points[0];
436 point_type max_pt = cloud.points[0];
437
438 for (const auto& point : cloud.points) {
439 min_pt.x = std::min(min_pt.x, point.x);
440 min_pt.y = std::min(min_pt.y, point.y);
441 min_pt.z = std::min(min_pt.z, point.z);
442
443 max_pt.x = std::max(max_pt.x, point.x);
444 max_pt.y = std::max(max_pt.y, point.y);
445 max_pt.z = std::max(max_pt.z, point.z);
446 }
447
448 return {min_pt, max_pt};
449 }
450};
451
452} // namespace toolbox::metrics
Bounding box distance between two point clouds.
Definition point_cloud_metrics.hpp:340
toolbox::types::point_cloud_t< T > cloud_type
Definition point_cloud_metrics.hpp:344
constexpr T squared_distance_impl(const T *a, const T *b, std::size_t size) const
Definition point_cloud_metrics.hpp:424
Mode
Definition point_cloud_metrics.hpp:346
constexpr T distance_impl(const T *cloud_a, const T *cloud_b, std::size_t size) const
Definition point_cloud_metrics.hpp:354
toolbox::types::point_t< T > point_type
Definition point_cloud_metrics.hpp:343
T distance(const cloud_type &cloud_a, const cloud_type &cloud_b) const
Definition point_cloud_metrics.hpp:359
BoundingBoxMetric(Mode mode=Mode::CENTER_DISTANCE)
Definition point_cloud_metrics.hpp:352
T element_type
Definition point_cloud_metrics.hpp:342
Centroid distance between two point clouds.
Definition point_cloud_metrics.hpp:284
constexpr T distance_impl(const T *cloud_a, const T *cloud_b, std::size_t size) const
Definition point_cloud_metrics.hpp:290
T element_type
Definition point_cloud_metrics.hpp:286
constexpr T squared_distance_impl(const T *a, const T *b, std::size_t size) const
Definition point_cloud_metrics.hpp:326
T distance(const cloud_type &cloud_a, const cloud_type &cloud_b) const
Definition point_cloud_metrics.hpp:295
Chamfer distance between two point clouds.
Definition point_cloud_metrics.hpp:161
constexpr T distance_impl(const T *cloud_a, const T *cloud_b, std::size_t size) const
Definition point_cloud_metrics.hpp:167
T element_type
Definition point_cloud_metrics.hpp:163
T distance(const cloud_type &cloud_a, const cloud_type &cloud_b) const
Definition point_cloud_metrics.hpp:172
constexpr T squared_distance_impl(const T *a, const T *b, std::size_t size) const
Definition point_cloud_metrics.hpp:206
Hausdorff distance between two point clouds.
Definition point_cloud_metrics.hpp:25
T distance(const cloud_type &cloud_a, const cloud_type &cloud_b) const
Definition point_cloud_metrics.hpp:38
constexpr T distance_impl(const T *cloud_a, const T *cloud_b, std::size_t size) const
Definition point_cloud_metrics.hpp:32
constexpr T squared_distance_impl(const T *a, const T *b, std::size_t size) const
Definition point_cloud_metrics.hpp:70
T element_type
Definition point_cloud_metrics.hpp:27
Modified Hausdorff distance (average of k-th smallest distances)
Definition point_cloud_metrics.hpp:84
T distance(const cloud_type &cloud_a, const cloud_type &cloud_b) const
Definition point_cloud_metrics.hpp:97
constexpr T distance_impl(const T *cloud_a, const T *cloud_b, std::size_t size) const
Definition point_cloud_metrics.hpp:92
constexpr T squared_distance_impl(const T *a, const T *b, std::size_t size) const
Definition point_cloud_metrics.hpp:144
ModifiedHausdorffMetric(std::size_t k=1)
Definition point_cloud_metrics.hpp:90
T element_type
Definition point_cloud_metrics.hpp:86
Earth Mover's Distance (EMD) for point clouds.
Definition point_cloud_metrics.hpp:220
T element_type
Definition point_cloud_metrics.hpp:222
T distance(const cloud_type &cloud_a, const cloud_type &cloud_b) const
Definition point_cloud_metrics.hpp:231
constexpr T distance_impl(const T *cloud_a, const T *cloud_b, std::size_t size) const
Definition point_cloud_metrics.hpp:226
constexpr T squared_distance_impl(const T *a, const T *b, std::size_t size) const
Definition point_cloud_metrics.hpp:270
Definition base_metric.hpp:13
包含点和相关数据的点云类 / 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
auto empty() const -> bool
检查点云是否为空 / Check if cloud is empty
Definition point_impl.hpp:299
Definition angular_metrics.hpp:11
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
auto distance(const point_t &other) const -> double
计算到另一点的欧几里得距离 / Calculate Euclidean distance to another point
Definition point_impl.hpp:144
T y
Y坐标 / Y coordinate.
Definition point.hpp:52