cpp-toolbox  0.0.1
A toolbox library for C++
Loading...
Searching...
No Matches
prosac_registration_impl.hpp
Go to the documentation of this file.
1#pragma once
2
3#include <set>
4
6
7namespace toolbox::pcl
8{
9
10template<typename DataType>
12{
13 // 获取基类成员访问 / Get base class member access
14 const auto& source_cloud = this->get_source_cloud();
15 const auto& target_cloud = this->get_target_cloud();
16 const auto& correspondences = this->get_correspondences();
17 const auto max_iterations = this->get_max_iterations();
18 const auto inlier_threshold = this->get_inlier_threshold();
19 const auto min_inliers = this->get_min_inliers();
20 const auto random_seed = this->get_random_seed();
21
22 // 初始化结果 / Initialize result
23 result.transformation = transformation_t::Identity();
24 result.fitness_score = std::numeric_limits<DataType>::max();
25 result.inliers.clear();
26 result.converged = false;
27 result.num_iterations = 0;
28
29 // 检查是否有足够的对应关系 / Check if there are enough correspondences
30 const std::size_t num_correspondences = correspondences->size();
31 if (num_correspondences < m_sample_size) {
32 LOG_ERROR_S << "错误:对应关系数量不足 / Error: Insufficient correspondences: "
33 << num_correspondences << " < " << m_sample_size;
34 return false;
35 }
36
37 // 预计算采样调度 / Precompute sampling schedule
38 precompute_sampling_schedule(num_correspondences);
39
40 // 初始化随机数生成器 / Initialize random number generator
41 std::mt19937 generator(random_seed);
42
43 // PROSAC主循环变量 / PROSAC main loop variables
44 std::size_t n = m_sample_size; // 当前采样池大小 / Current sampling pool size
45 std::size_t t = 0; // 迭代计数器 / Iteration counter
46 std::size_t best_inlier_count = 0;
47 transformation_t best_transformation = transformation_t::Identity();
48 std::vector<std::size_t> best_inliers;
49
50 // 用于早停的变量 / Variables for early stopping
51 auto start_time = std::chrono::steady_clock::now();
52 const auto max_time = std::chrono::seconds(300); // 5分钟超时 / 5 minutes timeout
53
54 LOG_INFO_S << "开始PROSAC配准 / Starting PROSAC registration with "
55 << num_correspondences << " correspondences, sample size "
56 << m_sample_size;
57
58 // PROSAC主循环 / PROSAC main loop
59 while (t < max_iterations) {
60 // 检查超时 / Check timeout
61 auto current_time = std::chrono::steady_clock::now();
62 if (current_time - start_time > max_time) {
63 LOG_WARN_S << "警告:PROSAC达到时间限制 / Warning: PROSAC reached time limit";
64 break;
65 }
66
67 // 更新采样池大小 / Update sampling pool size
68 if (t == m_T_n[n - 1] && n < num_correspondences) {
69 n++;
70 }
71
72 // 渐进式采样 / Progressive sampling
73 std::vector<correspondence_t> sample;
74 sample.reserve(m_sample_size);
75 progressive_sample(sample, n, t, generator);
76
77 // 检查样本有效性 / Check sample validity
78 if (!is_sample_valid(sample)) {
79 t++;
80 continue;
81 }
82
83 // 估计变换 / Estimate transformation
84 transformation_t transform = estimate_transformation(sample);
85
86 // 计算内点 / Count inliers
87 std::vector<std::size_t> inliers;
88 std::size_t inlier_count = count_inliers(transform, inliers);
89
90 // 更新最佳模型 / Update best model
91 if (inlier_count > best_inlier_count) {
92 best_inlier_count = inlier_count;
93 best_transformation = transform;
94 best_inliers = inliers;
95
96 LOG_INFO_S << "迭代 / Iteration " << t << ": 找到更好的模型 / found better model with "
97 << inlier_count << " inliers (n=" << n << ")";
98
99 // 检查早停条件 / Check early stopping condition
100 DataType inlier_ratio = static_cast<DataType>(inlier_count) /
101 static_cast<DataType>(num_correspondences);
102 if (inlier_ratio >= m_early_stop_ratio) {
103 LOG_INFO_S << "达到早停条件 / Reached early stop condition: inlier ratio = "
104 << inlier_ratio;
105 break;
106 }
107
108 // 检查非随机性准则 / Check non-randomness criterion
109 if (check_non_randomness(inlier_count, n)) {
110 LOG_INFO_S << "满足非随机性准则 / Non-randomness criterion satisfied";
111 break;
112 }
113 }
114
115 // 检查最大性准则 / Check maximality criterion
116 if (best_inlier_count >= min_inliers &&
117 check_maximality(best_inlier_count, n, t)) {
118 LOG_INFO_S << "满足最大性准则 / Maximality criterion satisfied";
119 break;
120 }
121
122 t++;
123 m_total_samples++;
124 }
125
126 // 设置结果 / Set results
127 result.num_iterations = t;
128 m_best_inlier_count = best_inlier_count;
129
130 if (best_inlier_count >= min_inliers) {
131 // 如果需要,使用所有内点精炼结果 / Refine result using all inliers if needed
132 if (m_refine_result && best_inlier_count > m_sample_size) {
133 LOG_INFO_S << "使用 / Using " << best_inlier_count
134 << " 个内点精炼变换 / inliers to refine transformation";
135 best_transformation = refine_transformation(best_inliers);
136
137 // 重新计算内点 / Recompute inliers
138 best_inlier_count = count_inliers(best_transformation, best_inliers);
139 }
140
141 result.transformation = best_transformation;
142 result.inliers = best_inliers;
143 result.fitness_score = compute_fitness_score(best_transformation, best_inliers);
144 result.converged = true;
145
146 LOG_INFO_S << "PROSAC配准成功 / PROSAC registration successful: "
147 << best_inlier_count << " inliers in " << t << " iterations";
148 } else {
149 LOG_WARN_S << "警告:PROSAC未找到足够的内点 / Warning: PROSAC did not find enough inliers: "
150 << best_inlier_count << " < " << min_inliers;
151 }
152
153 return result.converged;
154}
155
156template<typename DataType>
158{
159 const auto& correspondences = this->get_correspondences();
160
161 if (!correspondences || correspondences->empty()) {
162 LOG_ERROR_S << "错误:对应关系为空 / Error: Correspondences are empty";
163 return false;
164 }
165
166 if (m_sorted_indices.empty()) {
167 LOG_WARN_S << "警告:未提供排序索引,假设对应关系已排序 / "
168 "Warning: No sorted indices provided, assuming correspondences are sorted";
169 } else if (m_sorted_indices.size() != correspondences->size()) {
170 LOG_ERROR_S << "错误:排序索引大小与对应关系不匹配 / "
171 "Error: Sorted indices size doesn't match correspondences";
172 return false;
173 }
174
175 return true;
176}
177
178template<typename DataType>
180 std::size_t n_correspondences)
181{
182 m_T_n.clear();
183 m_T_n.reserve(n_correspondences);
184
185 // T_m初始值 / Initial value of T_m
186 DataType T_m = static_cast<DataType>(n_correspondences) *
187 std::pow(1.0 - m_initial_inlier_ratio,
188 static_cast<DataType>(m_sample_size));
189
190 // 添加前m个T值(都是1) / Add first m T values (all are 1)
191 for (std::size_t i = 0; i < m_sample_size; ++i) {
192 m_T_n.push_back(1);
193 }
194
195 // 计算T_n for n = m+1 to N / Compute T_n for n = m+1 to N
196 for (std::size_t n = m_sample_size + 1; n <= n_correspondences; ++n) {
197 // T_n = T_{n-1} + ceil(T_m * (n - m) / (m * C(n, m)))
198 std::size_t T_n_minus_1 = m_T_n.back();
199
200 // 避免整数溢出,使用对数计算 / Avoid integer overflow, use logarithmic computation
201 DataType log_numerator = std::log(static_cast<DataType>(n - m_sample_size)) +
202 std::log(T_m);
203 DataType log_denominator = std::log(static_cast<DataType>(m_sample_size));
204
205 // 计算组合数的对数 / Compute logarithm of binomial coefficient
206 for (std::size_t i = 0; i < m_sample_size; ++i) {
207 log_denominator += std::log(static_cast<DataType>(n - i)) -
208 std::log(static_cast<DataType>(m_sample_size - i));
209 }
210
211 DataType increment = std::exp(log_numerator - log_denominator);
212 std::size_t T_n = T_n_minus_1 + static_cast<std::size_t>(std::ceil(increment));
213
214 m_T_n.push_back(T_n);
215 }
216}
217
218template<typename DataType>
219void prosac_registration_t<DataType>::progressive_sample(
220 std::vector<correspondence_t>& sample, std::size_t n, std::size_t t,
221 std::mt19937& generator) const
222{
223 const auto& correspondences = this->get_correspondences();
224 sample.clear();
225
226 if (t >= m_T_n[n - 1]) {
227 // PROSAC采样:选择第n个对应关系和前n-1个中的m-1个 /
228 // PROSAC sampling: select nth correspondence and m-1 from first n-1
229
230 // 添加第n个对应关系 / Add nth correspondence
231 if (!m_sorted_indices.empty()) {
232 sample.push_back((*correspondences)[m_sorted_indices[n - 1]]);
233 } else {
234 sample.push_back((*correspondences)[n - 1]);
235 }
236
237 // 从前n-1个中随机选择m-1个 / Randomly select m-1 from first n-1
238 std::vector<std::size_t> indices;
239 indices.reserve(n - 1);
240 for (std::size_t i = 0; i < n - 1; ++i) {
241 indices.push_back(i);
242 }
243
244 std::shuffle(indices.begin(), indices.end(), generator);
245
246 for (std::size_t i = 0; i < m_sample_size - 1; ++i) {
247 if (!m_sorted_indices.empty()) {
248 sample.push_back((*correspondences)[m_sorted_indices[indices[i]]]);
249 } else {
250 sample.push_back((*correspondences)[indices[i]]);
251 }
252 }
253 } else {
254 // 标准RANSAC采样:从前n个中随机选择m个 /
255 // Standard RANSAC sampling: randomly select m from first n
256 std::vector<std::size_t> indices;
257 indices.reserve(n);
258 for (std::size_t i = 0; i < n; ++i) {
259 indices.push_back(i);
260 }
261
262 std::shuffle(indices.begin(), indices.end(), generator);
263
264 for (std::size_t i = 0; i < m_sample_size; ++i) {
265 if (!m_sorted_indices.empty()) {
266 sample.push_back((*correspondences)[m_sorted_indices[indices[i]]]);
267 } else {
268 sample.push_back((*correspondences)[indices[i]]);
269 }
270 }
271 }
272}
273
274template<typename DataType>
276prosac_registration_t<DataType>::estimate_transformation(
277 const std::vector<correspondence_t>& sample) const
278{
279 const auto& source_cloud = this->get_source_cloud();
280 const auto& target_cloud = this->get_target_cloud();
281
282 // 提取样本点 / Extract sample points
283 Eigen::Matrix<DataType, 3, Eigen::Dynamic> src_points(3, sample.size());
284 Eigen::Matrix<DataType, 3, Eigen::Dynamic> tgt_points(3, sample.size());
285
286 for (std::size_t i = 0; i < sample.size(); ++i) {
287 const auto& src_pt = source_cloud->points[sample[i].src_idx];
288 const auto& tgt_pt = target_cloud->points[sample[i].dst_idx];
289
290 src_points.col(i) = vector3_t(src_pt.x, src_pt.y, src_pt.z);
291 tgt_points.col(i) = vector3_t(tgt_pt.x, tgt_pt.y, tgt_pt.z);
292 }
293
294 // 计算质心 / Compute centroids
295 vector3_t src_centroid = src_points.rowwise().mean();
296 vector3_t tgt_centroid = tgt_points.rowwise().mean();
297
298 // 中心化点云 / Center point clouds
299 Eigen::Matrix<DataType, 3, Eigen::Dynamic> src_centered =
300 src_points.colwise() - src_centroid;
301 Eigen::Matrix<DataType, 3, Eigen::Dynamic> tgt_centered =
302 tgt_points.colwise() - tgt_centroid;
303
304 // 计算协方差矩阵 / Compute covariance matrix
305 matrix3_t H = src_centered * tgt_centered.transpose();
306
307 // SVD分解 / SVD decomposition
308 Eigen::JacobiSVD<matrix3_t> svd(H, Eigen::ComputeFullU | Eigen::ComputeFullV);
309 matrix3_t U = svd.matrixU();
310 matrix3_t V = svd.matrixV();
311
312 // 计算旋转矩阵 / Compute rotation matrix
313 matrix3_t R = V * U.transpose();
314
315 // 处理反射情况 / Handle reflection case
316 if (R.determinant() < 0) {
317 V.col(2) *= -1;
318 R = V * U.transpose();
319 }
320
321 // 计算平移向量 / Compute translation vector
322 vector3_t t = tgt_centroid - R * src_centroid;
323
324 // 构建变换矩阵 / Build transformation matrix
325 transformation_t transform = transformation_t::Identity();
326 transform.template block<3, 3>(0, 0) = R;
327 transform.template block<3, 1>(0, 3) = t;
328
329 return transform;
330}
331
332template<typename DataType>
333std::size_t prosac_registration_t<DataType>::count_inliers(
334 const transformation_t& transform, std::vector<std::size_t>& inliers) const
335{
336 const auto& source_cloud = this->get_source_cloud();
337 const auto& target_cloud = this->get_target_cloud();
338 const auto& correspondences = this->get_correspondences();
339 const auto inlier_threshold = this->get_inlier_threshold();
340
341 inliers.clear();
342 inliers.reserve(correspondences->size());
343
344 // 对每个对应关系检查是否为内点 / Check each correspondence for inlier status
345 for (std::size_t i = 0; i < correspondences->size(); ++i) {
346 const auto& corr = (*correspondences)[i];
347 const auto& src_pt = source_cloud->points[corr.src_idx];
348 const auto& tgt_pt = target_cloud->points[corr.dst_idx];
349
350 // 变换源点 / Transform source point
351 vector3_t src_vec(src_pt.x, src_pt.y, src_pt.z);
352 vector3_t transformed = transform.template block<3, 3>(0, 0) * src_vec +
353 transform.template block<3, 1>(0, 3);
354
355 // 计算距离 / Compute distance
356 DataType dist = std::sqrt((transformed[0] - tgt_pt.x) * (transformed[0] - tgt_pt.x) +
357 (transformed[1] - tgt_pt.y) * (transformed[1] - tgt_pt.y) +
358 (transformed[2] - tgt_pt.z) * (transformed[2] - tgt_pt.z));
359
360 if (dist <= inlier_threshold) {
361 inliers.push_back(i);
362 }
363 }
364
365 return inliers.size();
366}
367
368template<typename DataType>
369bool prosac_registration_t<DataType>::check_non_randomness(
370 std::size_t inlier_count, std::size_t n) const
371{
372 // 计算观察到这么多内点的概率 / Compute probability of observing this many inliers
373 DataType p_good = 1.0;
374
375 for (std::size_t j = m_sample_size; j <= inlier_count; ++j) {
376 DataType beta = compute_beta(j, m_sample_size, n);
377 p_good *= (1.0 - beta);
378 }
379
380 p_good = 1.0 - p_good;
381
382 return p_good < m_non_randomness_threshold;
383}
384
385template<typename DataType>
386bool prosac_registration_t<DataType>::check_maximality(
387 std::size_t inlier_count, std::size_t n, std::size_t t) const
388{
389 // 计算找到更好模型所需的期望迭代次数 /
390 // Compute expected iterations to find better model
391 DataType inlier_ratio = static_cast<DataType>(inlier_count) /
392 static_cast<DataType>(n);
393
394 if (inlier_ratio <= 0) {
395 return false;
396 }
397
398 DataType p_better = std::pow(inlier_ratio, static_cast<DataType>(m_sample_size));
399
400 if (p_better <= 0) {
401 return true; // 不可能找到更好的模型 / Impossible to find better model
402 }
403
404 DataType k_max = std::log(1.0 - m_confidence) / std::log(1.0 - p_better);
405
406 return static_cast<DataType>(t) >= k_max;
407}
408
409template<typename DataType>
411prosac_registration_t<DataType>::refine_transformation(
412 const std::vector<std::size_t>& inlier_indices) const
413{
414 const auto& source_cloud = this->get_source_cloud();
415 const auto& target_cloud = this->get_target_cloud();
416 const auto& correspondences = this->get_correspondences();
417
418 // 提取所有内点 / Extract all inlier points
419 Eigen::Matrix<DataType, 3, Eigen::Dynamic> src_points(3, inlier_indices.size());
420 Eigen::Matrix<DataType, 3, Eigen::Dynamic> tgt_points(3, inlier_indices.size());
421
422 for (std::size_t i = 0; i < inlier_indices.size(); ++i) {
423 const auto& corr = (*correspondences)[inlier_indices[i]];
424 const auto& src_pt = source_cloud->points[corr.src_idx];
425 const auto& tgt_pt = target_cloud->points[corr.dst_idx];
426
427 src_points.col(i) = vector3_t(src_pt.x, src_pt.y, src_pt.z);
428 tgt_points.col(i) = vector3_t(tgt_pt.x, tgt_pt.y, tgt_pt.z);
429 }
430
431 // 使用SVD计算最优变换(与estimate_transformation相同的方法) /
432 // Compute optimal transformation using SVD (same method as estimate_transformation)
433 vector3_t src_centroid = src_points.rowwise().mean();
434 vector3_t tgt_centroid = tgt_points.rowwise().mean();
435
436 Eigen::Matrix<DataType, 3, Eigen::Dynamic> src_centered =
437 src_points.colwise() - src_centroid;
438 Eigen::Matrix<DataType, 3, Eigen::Dynamic> tgt_centered =
439 tgt_points.colwise() - tgt_centroid;
440
441 matrix3_t H = src_centered * tgt_centered.transpose();
442
443 Eigen::JacobiSVD<matrix3_t> svd(H, Eigen::ComputeFullU | Eigen::ComputeFullV);
444 matrix3_t U = svd.matrixU();
445 matrix3_t V = svd.matrixV();
446
447 matrix3_t R = V * U.transpose();
448
449 if (R.determinant() < 0) {
450 V.col(2) *= -1;
451 R = V * U.transpose();
452 }
453
454 vector3_t t = tgt_centroid - R * src_centroid;
455
456 transformation_t transform = transformation_t::Identity();
457 transform.template block<3, 3>(0, 0) = R;
458 transform.template block<3, 1>(0, 3) = t;
459
460 return transform;
461}
462
463template<typename DataType>
464std::size_t prosac_registration_t<DataType>::compute_binomial_coefficient(
465 std::size_t n, std::size_t k) const
466{
467 if (k > n) return 0;
468 if (k == 0 || k == n) return 1;
469
470 // 使用Pascal三角形的性质优化计算 / Optimize using Pascal's triangle property
471 k = std::min(k, n - k);
472
473 std::size_t result = 1;
474 for (std::size_t i = 0; i < k; ++i) {
475 result = result * (n - i) / (i + 1);
476 }
477
478 return result;
479}
480
481template<typename DataType>
482DataType prosac_registration_t<DataType>::compute_beta(
483 std::size_t i, std::size_t m, std::size_t n) const
484{
485 if (i < m) return 0;
486 if (i > n) return 0;
487
488 // beta(i, m, n) = C(i-1, m-1) * C(n-i, 1) / C(n, m)
489 // = i * C(i-1, m-1) / C(n, m)
490
491 // 使用对数避免溢出 / Use logarithm to avoid overflow
492 DataType log_beta = std::log(static_cast<DataType>(i));
493
494 // log(C(i-1, m-1))
495 for (std::size_t j = 0; j < m - 1; ++j) {
496 log_beta += std::log(static_cast<DataType>(i - 1 - j)) -
497 std::log(static_cast<DataType>(j + 1));
498 }
499
500 // log(C(n, m))
501 for (std::size_t j = 0; j < m; ++j) {
502 log_beta -= std::log(static_cast<DataType>(n - j)) -
503 std::log(static_cast<DataType>(j + 1));
504 }
505
506 return std::exp(log_beta);
507}
508
509template<typename DataType>
510bool prosac_registration_t<DataType>::is_sample_valid(
511 const std::vector<correspondence_t>& sample) const
512{
513 if (sample.size() < 3) {
514 return false;
515 }
516
517 const auto& source_cloud = this->get_source_cloud();
518
519 // 检查是否有重复的对应关系 / Check for duplicate correspondences
520 std::set<std::size_t> src_indices, dst_indices;
521 for (const auto& corr : sample) {
522 if (!src_indices.insert(corr.src_idx).second ||
523 !dst_indices.insert(corr.dst_idx).second) {
524 return false;
525 }
526 }
527
528 // 检查源点是否共线 / Check if source points are collinear
529 const auto& p1 = source_cloud->points[sample[0].src_idx];
530 const auto& p2 = source_cloud->points[sample[1].src_idx];
531 const auto& p3 = source_cloud->points[sample[2].src_idx];
532
533 vector3_t v1(p2.x - p1.x, p2.y - p1.y, p2.z - p1.z);
534 vector3_t v2(p3.x - p1.x, p3.y - p1.y, p3.z - p1.z);
535
536 vector3_t cross = v1.cross(v2);
537 DataType cross_norm = cross.norm();
538
539 const DataType collinear_threshold = static_cast<DataType>(1e-6);
540 return cross_norm > collinear_threshold;
541}
542
543template<typename DataType>
544DataType prosac_registration_t<DataType>::compute_fitness_score(
545 const transformation_t& transform,
546 const std::vector<std::size_t>& inliers) const
547{
548 if (inliers.empty()) {
549 return std::numeric_limits<DataType>::max();
550 }
551
552 const auto& source_cloud = this->get_source_cloud();
553 const auto& target_cloud = this->get_target_cloud();
554 const auto& correspondences = this->get_correspondences();
555
556 DataType total_distance = 0;
557
558 // 计算所有内点的平均距离 / Compute average distance of all inliers
559 for (std::size_t idx : inliers) {
560 const auto& corr = (*correspondences)[idx];
561 const auto& src_pt = source_cloud->points[corr.src_idx];
562 const auto& tgt_pt = target_cloud->points[corr.dst_idx];
563
564 // 变换源点 / Transform source point
565 vector3_t src_vec(src_pt.x, src_pt.y, src_pt.z);
566 vector3_t transformed = transform.template block<3, 3>(0, 0) * src_vec +
567 transform.template block<3, 1>(0, 3);
568
569 // 计算距离 / Compute distance
570 DataType dist = std::sqrt((transformed[0] - tgt_pt.x) * (transformed[0] - tgt_pt.x) +
571 (transformed[1] - tgt_pt.y) * (transformed[1] - tgt_pt.y) +
572 (transformed[2] - tgt_pt.z) * (transformed[2] - tgt_pt.z));
573
574 total_distance += dist;
575 }
576
577 return total_distance / static_cast<DataType>(inliers.size());
578}
579
580} // namespace toolbox::pcl
PROSAC (渐进式采样一致性) 粗配准算法 / PROSAC (Progressive Sample Consensus) coarse registration algorithm.
Definition prosac_registration.hpp:60
bool validate_input_impl() const
额外的输入验证 / Additional input validation
Definition prosac_registration_impl.hpp:157
bool align_impl(result_type &result)
派生类实现的配准算法 / Registration algorithm implementation by derived class
Definition prosac_registration_impl.hpp:11
Eigen::Matrix< DataType, 4, 4 > transformation_t
Definition prosac_registration.hpp:72
#define LOG_INFO_S
INFO级别流式日志的宏 / Macro for INFO level stream logging.
Definition thread_logger.hpp:1328
#define LOG_ERROR_S
Definition memory_mapped_file.cpp:4
#define LOG_WARN_S
Definition memory_mapped_file.cpp:5
Definition base_correspondence_generator.hpp:18
std::vector< T > sample(const std::vector< T > &population, size_t k)
从vector中随机采样k个元素/Randomly sample k elements from vector
Definition random.hpp:518