不知道这个框怎么删掉???
前言
在计算机视觉和三维建模领域,点云配准是一个基础且关键的任务。它涉及将来自不同视角或不同时间点的点云数据对齐到一个统一的坐标系统中。本文将深入探讨如何利用主成分分析(PCA)来实现高效且准确的点云配准。
一、主成分分析(PCA)概述
点云配准是三维重建、机器人导航、文化遗产保护等领域的关键技术。它的目标是找到两个或多个点云之间的最优对齐方式,以便于分析和处理。。
二、PCA点云配准的原理
PCA通过线性变换将原始数据转换到一个新的坐标系统中,使得在这个新坐标系统的第一坐标轴上的数据方差最大,第二坐标轴上的数据方差次之,依此类推。
在点云数据中,PCA可以识别出数据的主要方向(即主成分),这些方向通常对应于点云的几何特征,如长度、宽度和深度。
三、适用于PCA点云配准的点云特点
结构化点云:
- PCA配准更适合于具有明显几何结构和特征的点云,如建筑物、机械零件等。
- 在这些应用中,点云的主要方向和平面可以清晰地通过PCA识别出来。
规模适中的点云:
- 对于规模适中的点云,PCA可以有效地处理和提取关键特征。对于极大规模的点云,可能需要先进行降采样或分割。
噪声水平适中的点云:
- 虽然PCA具有一定的抗噪声能力,但过高的噪声水平仍然会影响其性能。因此,适度的预处理(如滤波)对于提高PCA配准的准确性是有帮助的。
最后,尤其注意点云密度对主成分方向的影响,吃过大亏。
代码:
// https://blog.csdn.net/fei_12138/article/details/118677416
// 运行成功了
#include <iostream>
#include <pcl/io/pcd_io.h>
#include <pcl/point_types.h>
#include <pcl/console/time.h>
#include <boost/thread/thread.hpp>
#include <pcl/visualization/pcl_visualizer.h>
#include <pcl/filters/voxel_grid.h>
#include <vtkAutoInit.h>
#include <Eigen/Core>
#include <Eigen/Eigenvalues>
#include<pcl/registration/correspondence_estimation.h>
#include <pcl/common/impl/transforms.hpp>
//计算向量u与向量v之间的夹角
void calRotation(Eigen::Vector3f u, Eigen::Vector3f v, double& angle, Eigen::Vector3f& vec);
//获取罗德里格斯旋转矩阵
Eigen::Matrix4f RodriguesMatrixTranslation(Eigen::Vector3f n, double angle);
int main()
{
pcl::PointCloud<pcl::PointXYZ>::Ptr target(new pcl::PointCloud<pcl::PointXYZ>());//目标点云
pcl::PointCloud<pcl::PointXYZ>::Ptr source(new pcl::PointCloud<pcl::PointXYZ>());//源点云
//pcl::io::loadPCDFile("E:\\File\\gradPaper\\data\\station\\3Clip.pcd", *target);
//pcl::io::loadPCDFile("E:\\File\\gradPaper\\data\\station\\4Clip.pcd", *source);
pcl::io::loadPCDFile("E:\\File\\gradPaper\\data\\cube1.pcd", *target);
pcl::io::loadPCDFile("E:\\File\\gradPaper\\data\\cube2.pcd", *source);
Eigen::Vector4f pcaCentroidtarget;//容量为4的列向量
pcl::compute3DCentroid(*target, pcaCentroidtarget);//计算目标点云质心
Eigen::Matrix3f covariance;//创建一个3行3列的矩阵,里面每个元素均为float类型
pcl::computeCovarianceMatrixNormalized(*target, pcaCentroidtarget, covariance);//计算目标点云协方差矩阵
Eigen::SelfAdjointEigenSolver<Eigen::Matrix3f> eigen_solver(covariance, Eigen::ComputeEigenvectors);//构造一个计算特定矩阵的类对象
Eigen::Matrix3f eigenVectorsPCA = eigen_solver.eigenvectors();//eigenvectors计算特征向量
//Eigen::Vector3f eigenValuesPCA = eigen_solver.eigenvalues();//eigenvalues计算特征值
Eigen::Vector4f pcaCentroidsource;
pcl::compute3DCentroid(*source, pcaCentroidsource);//计算源点云质心
Eigen::Matrix3f model_covariance;
pcl::computeCovarianceMatrixNormalized(*source, pcaCentroidsource, model_covariance);//计算源点云协方差矩阵
Eigen::SelfAdjointEigenSolver<Eigen::Matrix3f> modeleigen_solver(model_covariance, Eigen::ComputeEigenvectors);
Eigen::Matrix3f model_eigenVectorsPCA = modeleigen_solver.eigenvectors();
//Eigen::Vector3f model_eigenValuesPCA = modeleigen_solver.eigenvalues();
pcl::PointCloud<pcl::PointXYZ>::Ptr target_t(new pcl::PointCloud<pcl::PointXYZ>);
//平移目标点云,将目标点云通过质心平移到原点
Eigen::Matrix4f translation_t = Eigen::Matrix4f::Identity();
//设置矩阵的元素
translation_t(0, 3) = -pcaCentroidtarget[0];
translation_t(1, 3) = -pcaCentroidtarget[1];
translation_t(2, 3) = -pcaCentroidtarget[2];
pcl::transformPointCloud(*target, *target_t, translation_t);
pcl::PointCloud<pcl::PointXYZ>::Ptr source_t(new pcl::PointCloud<pcl::PointXYZ>);
//平移源点云,将源点云通过质心平移到原点
Eigen::Matrix4f translation_s = Eigen::Matrix4f::Identity();
//设置矩阵的元素
translation_s(0, 3) = -pcaCentroidsource[0];
translation_s(1, 3) = -pcaCentroidsource[1];
translation_s(2, 3) = -pcaCentroidsource[2];
pcl::transformPointCloud(*source, *source_t, translation_s);
// 计算旋转
Eigen::Vector3f n0;
double angle0;
calRotation(eigenVectorsPCA.col(2), model_eigenVectorsPCA.col(2), angle0, n0); // 假设最大特征值在最后一列
Eigen::Matrix4f transform0 = RodriguesMatrixTranslation(n0, angle0);
// 应用旋转
pcl::PointCloud<pcl::PointXYZ>::Ptr transformed_source(new pcl::PointCloud<pcl::PointXYZ>);
pcl::transformPointCloud(*source_t, *transformed_source, transform0);
// 输出旋转矩阵
std::cout << "Rotation Matrix:" << std::endl;
std::cout << transform0 << std::endl;
// 可视化
pcl::visualization::PCLVisualizer viewer("PCLVisualizer");
viewer.initCameraParameters();
int v1(0);
viewer.createViewPort(0.0, 0.0, 0.5, 1.0, v1);
viewer.setBackgroundColor(128.0 / 255.0, 138.0 / 255.0, 135.0 / 255.0, v1);
viewer.addText("Cloud before transforming", 10, 10, "v1 test", v1);
pcl::visualization::PointCloudColorHandlerCustom<pcl::PointXYZ> color_v1(target, 0, 255, 0);
viewer.addPointCloud<pcl::PointXYZ>(target, color_v1, "color_v1", v1);
viewer.addPointCloud<pcl::PointXYZ>(source, "source", v1);
viewer.addCoordinateSystem(0.5, "v1 test", v1);
int v2(0);
viewer.createViewPort(0.5, 0.0, 1.0, 1.0, v2);
viewer.setBackgroundColor(128.0 / 255.0, 138.0 / 255.0, 135.0 / 255.0, v2);
viewer.addText("Cloud after transforming", 10, 10, "v2 test", v2);
pcl::visualization::PointCloudColorHandlerCustom<pcl::PointXYZ> color_v2(target_t, 0, 255, 0);
viewer.addPointCloud<pcl::PointXYZ>(target_t, color_v2, "color_v2", v2);
viewer.addPointCloud<pcl::PointXYZ>(transformed_source, "transformed_source", v2);
viewer.addCoordinateSystem(0.5, "v2 test", v2);
while (!viewer.wasStopped())
{
viewer.spinOnce(100);
}
return 0;
}
// 计算向量u与向量v之间的夹角
void calRotation(Eigen::Vector3f u, Eigen::Vector3f v, double& angle, Eigen::Vector3f& vec)
{
angle = acos(u.dot(v) / (u.norm() * v.norm()));
if (angle > M_PI / 2)
{
u = -u;
angle = M_PI - angle;
}
float i, j, k;
i = u(1) * v(2) - u(2) * v(1), j = v(0) * u(2) - v(2) * u(0), k = u(0) * v(1) - u(1) * v(0);
vec << i, j, k;
double value = sqrt(i * i + j * j + k * k);
vec(0) = vec(0) / value;
vec(1) = vec(1) / value;
vec(2) = vec(2) / value;
}
//获取罗德里格斯旋转矩阵
Eigen::Matrix4f RodriguesMatrixTranslation(Eigen::Vector3f n, double angle)
{
//罗德里格斯公式求旋转矩阵
Eigen::Matrix4f x_transform(Eigen::Matrix4f::Identity());
x_transform(0, 0) = cos(angle) + n(0) * n(0) * (1 - cos(angle));
x_transform(1, 0) = n(2) * sin(angle) + n(0) * n(1) * (1 - cos(angle));
x_transform(2, 0) = -n(1) * sin(angle) + n(0) * n(2) * (1 - cos(angle));
x_transform(0, 1) = n(0) * n(1) * (1 - cos(angle)) - n(2) * sin(angle);
x_transform(1, 1) = cos(angle) + n(1) * n(1) * (1 - cos(angle));
x_transform(2, 1) = n(0) * sin(angle) + n(1) * n(2) * (1 - cos(angle));
x_transform(0, 2) = n(1) * sin(angle) + n(0) * n(2) * (1 - cos(angle));
x_transform(1, 2) = -n(0) * sin(angle) + n(1) * n(2) * (1 - cos(angle));
x_transform(2, 2) = cos(angle) + n(2) * n(2) * (1 - cos(angle));
return x_transform;
}
最大主成分对齐效果:
结论:
PCA在点云配准中的应用提供了一种有效的方法来处理和分析具有明显几何结构的点云。通过识别和对齐点云的主要特征,PCA可以实现快速且准确的配准,尤其适用于结构化且噪声水平适中的点云数据。