cpp-toolbox  0.0.1
A toolbox library for C++
Loading...
Searching...
No Matches
four_pcs_registration_impl.hpp
Go to the documentation of this file.
1#pragma once
2
3#include <numeric>
4#include <unordered_set>
5
11
12namespace toolbox::pcl
13{
14
15template<typename DataType>
17{
18 // 初始化结果 / Initialize result
19 result.transformation.setIdentity();
20 result.fitness_score = std::numeric_limits<DataType>::max();
21 result.inliers.clear();
22 result.num_iterations = 0;
23 result.converged = false;
24
25 LOG_DEBUG_S << "4PCS: 开始配准 / Starting registration"
26 << ", 源点云大小 / source size: "
27 << this->get_source_cloud()->size()
28 << ", 目标点云大小 / target size: "
29 << this->get_target_cloud()->size()
30 << ", 重叠率 / overlap: " << m_overlap
31 << ", 精度 / delta: " << m_delta;
32
33 // 计时器 / Timer
35 timer.start();
36
37 // 采样点云 / Sample point clouds
38 if (m_source_samples.empty() || m_target_samples.empty()) {
39 LOG_ERROR_S << "4PCS: 点云采样失败 / Point cloud sampling failed";
40 return false;
41 }
42
43 LOG_DEBUG_S << "4PCS: 采样完成 / Sampling complete"
44 << ", 源采样数 / source samples: " << m_source_samples.size()
45 << ", 目标采样数 / target samples: " << m_target_samples.size();
46
47 // 提取源点云的共面4点基 / Extract coplanar 4-point bases from source
48 auto source_bases = extract_coplanar_bases(
49 m_source_samples, this->get_source_cloud(), m_num_bases);
50 if (source_bases.empty()) {
52 << "4PCS: 无法提取有效的4点基 / Failed to extract valid 4-point bases";
53 return false;
54 }
55
56 LOG_DEBUG_S << "4PCS: 提取了 " << source_bases.size()
57 << " 个4点基 / 4-point bases";
58
59 // 最佳候选 / Best candidate
60 candidate_t best_candidate;
61 best_candidate.lcp_score = std::numeric_limits<DataType>::max();
62 best_candidate.num_inliers = 0;
63
64 // 对每个源基寻找匹配 / Find matches for each source base
65 std::size_t total_candidates = 0;
66 for (std::size_t base_idx = 0; base_idx < source_bases.size(); ++base_idx) {
67 result.num_iterations = base_idx + 1;
68 const auto& source_base = source_bases[base_idx];
69
70 // 寻找匹配的目标基 / Find congruent target bases
71 auto target_bases = find_congruent_sets(
72 source_base, m_target_samples, this->get_target_cloud());
73 total_candidates += target_bases.size();
74
75 // 评估每个候选匹配 / Evaluate each candidate match
76 for (const auto& target_base : target_bases) {
77 candidate_t candidate;
78 candidate.source_base = source_base;
79 candidate.target_base = target_base;
80
81 // 估计变换 / Estimate transformation
82 candidate.transform = estimate_transformation(source_base, target_base);
83
84 if (!is_valid_transformation(candidate.transform)) {
85 continue;
86 }
87
88 // 计算LCP评分 / Compute LCP score
89 std::vector<std::size_t> inliers;
90 candidate.lcp_score = compute_lcp_score(candidate.transform, inliers);
91 candidate.num_inliers = inliers.size();
92
93 // 更新最佳候选 / Update best candidate
94 if (candidate.num_inliers > best_candidate.num_inliers
95 || (candidate.num_inliers == best_candidate.num_inliers
96 && candidate.lcp_score < best_candidate.lcp_score))
97 {
98 best_candidate = candidate;
99 result.inliers = std::move(inliers);
100 }
101 }
102
103 // 早停检查 / Early stopping check
104 if (best_candidate.num_inliers >= m_source_samples.size() * m_overlap * 0.9)
105 {
107 << "4PCS: 早停,找到足够好的匹配 / Early stopping, found good match";
108 result.converged = true;
109 break;
110 }
111 }
112
113 timer.stop();
114 double elapsed_time = timer.elapsed_time();
115 LOG_DEBUG_S << "4PCS: 完成 " << result.num_iterations << " 个基的匹配,共 "
116 << total_candidates
117 << " 个候选,耗时 / bases with candidates in: " << elapsed_time
118 << " 秒/s";
119
120 // 检查是否找到有效的匹配 / Check if valid match found
121 if (best_candidate.num_inliers < this->get_min_inliers()) {
122 LOG_ERROR_S << "4PCS: 内点数量不足 / Insufficient inliers: "
123 << best_candidate.num_inliers << " < "
124 << this->get_min_inliers();
125 return false;
126 }
127
128 // 精炼最佳候选 / Refine best candidate
129 refine_candidate(best_candidate);
130
131 // 设置结果 / Set result
132 result.transformation = best_candidate.transform;
133 result.fitness_score = best_candidate.lcp_score;
134 result.converged = result.converged
135 || (best_candidate.num_inliers >= this->get_min_inliers());
136
137 LOG_DEBUG_S << "4PCS: 配准完成 / Registration complete"
138 << ", 内点 / inliers: " << result.inliers.size()
139 << ", LCP评分 / LCP score: " << result.fitness_score;
140
141 return true;
142}
143
144template<typename DataType>
146{
147 // 检查点云大小 / Check point cloud sizes
148 auto source_cloud = this->get_source_cloud();
149 auto target_cloud = this->get_target_cloud();
150
151 if (source_cloud->size() < 4 || target_cloud->size() < 4) {
152 LOG_ERROR_S << "4PCS: 点云太小,至少需要4个点 / Point clouds too small, "
153 "need at least 4 points";
154 return false;
155 }
156
157 // 检查参数 / Check parameters
158 if (m_delta <= 0) {
159 LOG_ERROR_S << "4PCS: 无效的delta值 / Invalid delta value: " << m_delta;
160 return false;
161 }
162
163 if (m_overlap <= 0 || m_overlap > 1) {
164 LOG_ERROR_S << "4PCS: 无效的重叠率 / Invalid overlap ratio: " << m_overlap;
165 return false;
166 }
167
168 return true;
169}
170
171template<typename DataType>
173 const point_cloud_ptr& source)
174{
175 // 采样源点云 / Sample source point cloud
176 sample_points(source, m_sample_size, m_source_samples);
177}
178
179template<typename DataType>
181 const point_cloud_ptr& target)
182{
183 // 采样目标点云 / Sample target point cloud
184 sample_points(target, m_sample_size, m_target_samples);
185
186 // 构建目标点云的KD树 / Build KD-tree for target cloud
187 m_target_kdtree = std::make_shared<kdtree_t>();
188 m_target_kdtree->set_input(target);
189}
190
191template<typename DataType>
193 const point_cloud_ptr& cloud,
194 std::size_t num_samples,
195 std::vector<std::size_t>& indices)
196{
197 indices.clear();
198
199 if (!cloud || cloud->empty()) {
200 return;
201 }
202
203 // 如果请求的样本数大于等于点云大小,使用所有点 / If requested samples >=
204 // cloud size, use all points
205 if (num_samples >= cloud->size()) {
206 indices.resize(cloud->size());
207 std::iota(indices.begin(), indices.end(), 0);
208 return;
209 }
210
211 // 均匀随机采样 / Uniform random sampling
212 indices.reserve(num_samples);
213 std::vector<std::size_t> all_indices(cloud->size());
214 std::iota(all_indices.begin(), all_indices.end(), 0);
215
216 // 使用随机采样 / Use random sampling
217 std::mt19937 generator(this->get_random_seed());
218 std::shuffle(all_indices.begin(), all_indices.end(), generator);
219
220 indices.assign(all_indices.begin(), all_indices.begin() + num_samples);
221}
222
223template<typename DataType>
224std::vector<typename four_pcs_registration_t<DataType>::base_4pcs_t>
226 const std::vector<std::size_t>& indices,
227 const point_cloud_ptr& cloud,
228 std::size_t num_bases) const
229{
230 std::vector<base_4pcs_t> bases;
231 bases.reserve(num_bases);
232
233 if (indices.size() < 4) {
234 return bases;
235 }
236
237 std::mt19937 generator(this->get_random_seed());
238 std::uniform_int_distribution<std::size_t> dist(0, indices.size() - 1);
239
240 const DataType coplanar_tolerance =
241 m_delta * 2; // 共面容差 / Coplanarity tolerance
242 std::size_t attempts = 0;
243 const std::size_t max_attempts =
244 num_bases * 100; // 最大尝试次数 / Max attempts
245
246 while (bases.size() < num_bases && attempts < max_attempts) {
247 attempts++;
248
249 // 随机选择4个不同的点 / Randomly select 4 different points
250 std::array<std::size_t, 4> selected_indices;
251 std::unordered_set<std::size_t> used;
252
253 for (std::size_t i = 0; i < 4; ++i) {
254 std::size_t idx;
255 do {
256 idx = dist(generator);
257 } while (used.find(idx) != used.end());
258 used.insert(idx);
259 selected_indices[i] = indices[idx];
260 }
261
262 // 提取点坐标 / Extract point coordinates
263 base_4pcs_t base;
264 base.indices = selected_indices;
265 for (std::size_t i = 0; i < 4; ++i) {
266 const auto& pt = cloud->points[selected_indices[i]];
267 base.points[i] = vector3_t(pt.x, pt.y, pt.z);
268 }
269
270 // 检查是否共面 / Check if coplanar
271 if (!are_coplanar(base.points, coplanar_tolerance)) {
272 continue;
273 }
274
275 // 计算平面参数 / Compute plane parameters
276 vector3_t v1 = base.points[1] - base.points[0];
277 vector3_t v2 = base.points[2] - base.points[0];
278 base.normal = v1.cross(v2).normalized();
279 base.d = -base.normal.dot(base.points[0]);
280
281 // 计算仿射不变量 / Compute affine invariants
282 compute_invariants(base);
283
284 // 检查基的有效性(点不能太近) / Check base validity (points not too close)
285 bool valid = true;
286 for (std::size_t i = 0; i < 4; ++i) {
287 for (std::size_t j = i + 1; j < 4; ++j) {
288 DataType dist = (base.points[i] - base.points[j]).norm();
289 if (dist
290 < m_delta * 10) { // 点间最小距离 / Min distance between points
291 valid = false;
292 break;
293 }
294 }
295 if (!valid)
296 break;
297 }
298
299 if (valid) {
300 bases.push_back(base);
301 }
302 }
303
304 return bases;
305}
306
307template<typename DataType>
309 const std::array<vector3_t, 4>& points, DataType tolerance) const
310{
311 // 使用前三个点定义平面 / Use first three points to define plane
312 vector3_t v1 = points[1] - points[0];
313 vector3_t v2 = points[2] - points[0];
314 vector3_t normal = v1.cross(v2);
315
316 // 检查是否退化(三点共线) / Check if degenerate (three points collinear)
317 if (normal.norm() < std::numeric_limits<DataType>::epsilon()) {
318 return false;
319 }
320
321 normal.normalize();
322 DataType d = -normal.dot(points[0]);
323
324 // 检查第四个点到平面的距离 / Check distance of fourth point to plane
325 DataType dist = std::abs(normal.dot(points[3]) + d);
326
327 return dist <= tolerance;
328}
329
330template<typename DataType>
332 base_4pcs_t& base) const
333{
334 // 4PCS使用两个交点的比率作为仿射不变量 / 4PCS uses ratios of intersection
335 // points as affine invariants 对角线AC和BD的交点 / Intersection of diagonals
336 // AC and BD
337
338 // 计算对角线AC: P0-P2 / Compute diagonal AC: P0-P2
339 vector3_t ac_dir = base.points[2] - base.points[0];
340 DataType ac_len = ac_dir.norm();
341
342 // 计算对角线BD: P1-P3 / Compute diagonal BD: P1-P3
343 vector3_t bd_dir = base.points[3] - base.points[1];
344 DataType bd_len = bd_dir.norm();
345
346 // 计算交点(使用参数方程) / Compute intersection (using parametric
347 // equations) P0 + s * AC_dir = P1 + t * BD_dir
348
349 // 构建线性系统 / Build linear system
350 matrix3_t A;
351 A.col(0) = ac_dir;
352 A.col(1) = -bd_dir;
353 A.col(2) = base.normal; // 添加法向量确保系统非奇异 / Add normal to ensure
354 // non-singular
355
356 vector3_t b = base.points[1] - base.points[0];
357
358 // 求解 / Solve
359 vector3_t params = A.fullPivLu().solve(b);
360 DataType s = params[0];
361 DataType t = params[1];
362
363 // 计算不变量(交点在对角线上的位置比率) / Compute invariants (position
364 // ratios on diagonals)
365 base.invariant1 = s; // 交点在AC上的位置 / Position on AC
366 base.invariant2 = t; // 交点在BD上的位置 / Position on BD
367}
368
369template<typename DataType>
370std::vector<typename four_pcs_registration_t<DataType>::base_4pcs_t>
372 const base_4pcs_t& source_base,
373 const std::vector<std::size_t>& target_indices,
374 const point_cloud_ptr& target_cloud) const
375{
376 std::vector<base_4pcs_t> congruent_bases;
377
378 // 容差 / Tolerances
379 const DataType invariant_tolerance = 0.1; // 不变量容差 / Invariant tolerance
380 const DataType distance_tolerance =
381 m_delta * 2; // 距离容差 / Distance tolerance
382
383 // 计算源基的边长 / Compute source base edge lengths
384 std::array<DataType, 6> source_distances; // 6条边 / 6 edges
385 std::size_t edge_idx = 0;
386 for (std::size_t i = 0; i < 4; ++i) {
387 for (std::size_t j = i + 1; j < 4; ++j) {
388 source_distances[edge_idx++] =
389 (source_base.points[i] - source_base.points[j]).norm();
390 }
391 }
392
393 // 寻找所有可能的匹配 / Find all possible matches
394 // 这里简化实现,实际4PCS使用更复杂的搜索策略 / Simplified implementation,
395 // actual 4PCS uses more complex search
396
397 if (target_indices.size() < 4) {
398 return congruent_bases;
399 }
400
401 // 随机尝试一些组合 / Try some random combinations
402 std::mt19937 generator(this->get_random_seed());
403 std::uniform_int_distribution<std::size_t> dist(0, target_indices.size() - 1);
404
405 const std::size_t max_tries = 1000;
406 for (std::size_t try_idx = 0; try_idx < max_tries; ++try_idx) {
407 // 随机选择4个目标点 / Randomly select 4 target points
408 std::array<std::size_t, 4> selected_indices;
409 std::unordered_set<std::size_t> used;
410
411 bool valid_selection = true;
412 for (std::size_t i = 0; i < 4; ++i) {
413 std::size_t idx;
414 std::size_t attempts = 0;
415 do {
416 idx = dist(generator);
417 attempts++;
418 if (attempts > 100) {
419 valid_selection = false;
420 break;
421 }
422 } while (used.find(idx) != used.end());
423
424 if (!valid_selection)
425 break;
426 used.insert(idx);
427 selected_indices[i] = target_indices[idx];
428 }
429
430 if (!valid_selection)
431 continue;
432
433 // 构建目标基 / Build target base
434 base_4pcs_t target_base;
435 target_base.indices = selected_indices;
436 for (std::size_t i = 0; i < 4; ++i) {
437 const auto& pt = target_cloud->points[selected_indices[i]];
438 target_base.points[i] = vector3_t(pt.x, pt.y, pt.z);
439 }
440
441 // 检查是否共面 / Check if coplanar
442 if (!are_coplanar(target_base.points, distance_tolerance)) {
443 continue;
444 }
445
446 // 计算目标基的边长 / Compute target base edge lengths
447 std::array<DataType, 6> target_distances;
448 edge_idx = 0;
449 for (std::size_t i = 0; i < 4; ++i) {
450 for (std::size_t j = i + 1; j < 4; ++j) {
451 target_distances[edge_idx++] =
452 (target_base.points[i] - target_base.points[j]).norm();
453 }
454 }
455
456 // 检查边长是否匹配(允许不同的点对应关系) / Check if edge lengths match
457 // (allow different correspondences)
458 bool distances_match = true;
459 for (std::size_t i = 0; i < 6; ++i) {
460 bool found_match = false;
461 for (std::size_t j = 0; j < 6; ++j) {
462 if (std::abs(source_distances[i] - target_distances[j])
463 < distance_tolerance)
464 {
465 found_match = true;
466 break;
467 }
468 }
469 if (!found_match) {
470 distances_match = false;
471 break;
472 }
473 }
474
475 if (!distances_match) {
476 continue;
477 }
478
479 // 计算不变量 / Compute invariants
480 compute_invariants(target_base);
481
482 // 检查不变量是否匹配 / Check if invariants match
483 if (std::abs(source_base.invariant1 - target_base.invariant1)
484 < invariant_tolerance
485 && std::abs(source_base.invariant2 - target_base.invariant2)
486 < invariant_tolerance)
487 {
488 congruent_bases.push_back(target_base);
489 }
490 }
491
492 return congruent_bases;
493}
494
495template<typename DataType>
498 const base_4pcs_t& source_base, const base_4pcs_t& target_base) const
499{
500 transformation_t transform;
501 transform.setIdentity();
502
503 // 使用SVD估计变换(类似RANSAC) / Estimate transformation using SVD (similar
504 // to RANSAC)
505
506 // 计算质心 / Compute centroids
507 vector3_t source_centroid = vector3_t::Zero();
508 vector3_t target_centroid = vector3_t::Zero();
509
510 for (std::size_t i = 0; i < 4; ++i) {
511 source_centroid += source_base.points[i];
512 target_centroid += target_base.points[i];
513 }
514
515 source_centroid /= 4;
516 target_centroid /= 4;
517
518 // 构建协方差矩阵 / Build covariance matrix
519 matrix3_t H = matrix3_t::Zero();
520
521 for (std::size_t i = 0; i < 4; ++i) {
522 vector3_t src_centered = source_base.points[i] - source_centroid;
523 vector3_t tgt_centered = target_base.points[i] - target_centroid;
524 H += src_centered * tgt_centered.transpose();
525 }
526
527 // SVD分解 / SVD decomposition
528 Eigen::JacobiSVD<matrix3_t> svd(H, Eigen::ComputeFullU | Eigen::ComputeFullV);
529 matrix3_t U = svd.matrixU();
530 matrix3_t V = svd.matrixV();
531
532 // 计算旋转矩阵 / Compute rotation matrix
533 matrix3_t R = V * U.transpose();
534
535 // 处理反射情况 / Handle reflection case
536 if (R.determinant() < 0) {
537 V.col(2) *= -1;
538 R = V * U.transpose();
539 }
540
541 // 计算平移向量 / Compute translation vector
542 vector3_t t = target_centroid - R * source_centroid;
543
544 // 构建4x4变换矩阵 / Build 4x4 transformation matrix
545 transform.template block<3, 3>(0, 0) = R;
546 transform.template block<3, 1>(0, 3) = t;
547
548 return transform;
549}
550
551template<typename DataType>
553 const transformation_t& transform, std::vector<std::size_t>& inliers) const
554{
555 // 使用 LCPMetric 计算评分 / Use LCPMetric to compute score
556 toolbox::metrics::LCPMetric<DataType> lcp_metric(m_delta);
557
558 auto source_cloud = this->get_source_cloud();
559 auto target_cloud = this->get_target_cloud();
560
561 // 创建采样点云用于LCP计算 / Create sampled point cloud for LCP computation
562 toolbox::types::point_cloud_t<DataType> sampled_source, sampled_target;
563 sampled_source.points.reserve(m_source_samples.size());
564 sampled_target.points.reserve(m_target_samples.size());
565
566 for (std::size_t src_idx : m_source_samples) {
567 sampled_source.points.push_back(source_cloud->points[src_idx]);
568 }
569
570 for (std::size_t tgt_idx : m_target_samples) {
571 sampled_target.points.push_back(target_cloud->points[tgt_idx]);
572 }
573
574 // 使用基本版本的LCP计算 / Use basic version of LCP computation
575 std::vector<std::size_t> sampled_inliers;
576 DataType score = lcp_metric.compute_lcp_score(
577 sampled_source, sampled_target, transform, &sampled_inliers);
578
579 // 将采样索引映射回原始索引 / Map sampled indices back to original indices
580 inliers.clear();
581 inliers.reserve(sampled_inliers.size());
582 for (std::size_t idx : sampled_inliers) {
583 inliers.push_back(m_source_samples[idx]);
584 }
585
586 return score;
587}
588
589template<typename DataType>
591 candidate_t& candidate) const
592{
593 // 可以在这里添加ICP或其他精炼方法 / Can add ICP or other refinement methods
594 // here 目前保持简单,只重新计算LCP / Keep it simple for now, just recompute
595 // LCP
596
597 std::vector<std::size_t> refined_inliers;
598 candidate.lcp_score = compute_lcp_score(candidate.transform, refined_inliers);
599 candidate.num_inliers = refined_inliers.size();
600}
601
602template<typename DataType>
604 const transformation_t& transform) const
605{
606 // 检查旋转部分是否有效 / Check if rotation part is valid
607 matrix3_t R = transform.template block<3, 3>(0, 0);
608
609 // 检查行列式(应该接近1) / Check determinant (should be close to 1)
610 DataType det = R.determinant();
611 if (std::abs(det - 1.0) > 0.1) {
612 return false;
613 }
614
615 // 检查正交性 / Check orthogonality
616 matrix3_t should_be_identity = R * R.transpose();
617 matrix3_t identity = matrix3_t::Identity();
618 DataType error = (should_be_identity - identity).norm();
619
620 return error < 0.1;
621}
622
623// 显式实例化 / Explicit instantiation
624template class four_pcs_registration_t<float>;
626
627} // namespace toolbox::pcl
LCP (Largest Common Pointset) metric for evaluating point cloud registration.
Definition point_cloud_metrics.hpp:465
T compute_lcp_score(const cloud_type &source, const cloud_type &target, const transformation_type &transform, std::vector< std::size_t > *inliers=nullptr) const
Compute LCP score between two point clouds.
Definition point_cloud_metrics.hpp:492
4PCS(4-Point Congruent Sets)粗配准算法 / 4PCS coarse registration algorithm
Definition four_pcs_registration.hpp:42
Eigen::Matrix< DataType, 3, 1 > vector3_t
Definition four_pcs_registration.hpp:51
std::vector< base_4pcs_t > extract_coplanar_bases(const std::vector< std::size_t > &indices, const point_cloud_ptr &cloud, std::size_t num_bases) const
提取共面4点基 / Extract coplanar 4-point bases
Definition four_pcs_registration_impl.hpp:225
void compute_invariants(base_4pcs_t &base) const
计算4点基的仿射不变量 / Compute affine invariants for 4-point base
Definition four_pcs_registration_impl.hpp:331
void set_target_impl(const point_cloud_ptr &target)
钩子函数:设置目标点云时的处理 / Hook: processing when setting target cloud
Definition four_pcs_registration_impl.hpp:180
bool validate_input_impl() const
额外的输入验证 / Additional input validation
Definition four_pcs_registration_impl.hpp:145
Eigen::Matrix< DataType, 3, 3 > matrix3_t
Definition four_pcs_registration.hpp:52
std::vector< base_4pcs_t > find_congruent_sets(const base_4pcs_t &source_base, const std::vector< std::size_t > &target_indices, const point_cloud_ptr &target_cloud) const
寻找匹配的4点集 / Find congruent 4-point sets
Definition four_pcs_registration_impl.hpp:371
Eigen::Matrix< DataType, 4, 4 > transformation_t
Definition four_pcs_registration.hpp:50
bool are_coplanar(const std::array< vector3_t, 4 > &points, DataType tolerance) const
检查4个点是否共面 / Check if 4 points are coplanar
Definition four_pcs_registration_impl.hpp:308
DataType compute_lcp_score(const transformation_t &transform, std::vector< std::size_t > &inliers) const
计算LCP(最大公共点集)评分 / Compute LCP (Largest Common Pointset) score
Definition four_pcs_registration_impl.hpp:552
transformation_t estimate_transformation(const base_4pcs_t &source_base, const base_4pcs_t &target_base) const
估计两个4点基之间的变换 / Estimate transformation between two 4-point bases
Definition four_pcs_registration_impl.hpp:497
void sample_points(const point_cloud_ptr &cloud, std::size_t num_samples, std::vector< std::size_t > &indices)
采样点云 / Sample point cloud
Definition four_pcs_registration_impl.hpp:192
bool align_impl(result_type &result)
派生类实现的配准算法 / Registration algorithm implementation
Definition four_pcs_registration_impl.hpp:16
void refine_candidate(candidate_t &candidate) const
精炼候选变换 / Refine candidate transformation
Definition four_pcs_registration_impl.hpp:590
void set_source_impl(const point_cloud_ptr &source)
钩子函数:设置源点云时的处理 / Hook: processing when setting source cloud
Definition four_pcs_registration_impl.hpp:172
bool is_valid_transformation(const transformation_t &transform) const
验证变换的有效性 / Verify transformation validity
Definition four_pcs_registration_impl.hpp:603
包含点和相关数据的点云类 / 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
A high-resolution stopwatch timer for measuring elapsed time.
Definition timer.hpp:41
void stop()
Stop (pause) the timer and accumulate the duration.
auto elapsed_time() const -> double
Get the total elapsed time in seconds.
void start()
Start or resume the timer.
#define LOG_DEBUG_S
DEBUG级别流式日志的宏 / Macro for DEBUG level stream logging.
Definition thread_logger.hpp:1329
#define LOG_ERROR_S
ERROR级别流式日志的宏 / Macro for ERROR level stream logging.
Definition thread_logger.hpp:1332
Definition base_correspondence_generator.hpp:18
4点基结构 / 4-point base structure
Definition four_pcs_registration.hpp:59
std::array< std::size_t, 4 > indices
点索引 / Point indices
Definition four_pcs_registration.hpp:60
DataType invariant2
第二个仿射不变量 / Second affine invariant
Definition four_pcs_registration.hpp:63
DataType invariant1
第一个仿射不变量 / First affine invariant
Definition four_pcs_registration.hpp:62
std::array< vector3_t, 4 > points
点坐标 / Point coordinates
Definition four_pcs_registration.hpp:61
DataType d
平面方程参数 / Plane equation parameter
Definition four_pcs_registration.hpp:65
vector3_t normal
平面法向量 / Plane normal
Definition four_pcs_registration.hpp:64
候选匹配结构 / Candidate match structure
Definition four_pcs_registration.hpp:72
std::size_t num_inliers
内点数量 / Number of inliers
Definition four_pcs_registration.hpp:77
base_4pcs_t source_base
源点云基 / Source base
Definition four_pcs_registration.hpp:73
DataType lcp_score
LCP(最大公共点集)评分 / LCP score.
Definition four_pcs_registration.hpp:76
transformation_t transform
估计的变换 / Estimated transformation
Definition four_pcs_registration.hpp:75
base_4pcs_t target_base
目标点云基 / Target base
Definition four_pcs_registration.hpp:74