目录
主成分分析(Principal Component Analysis, PCA)是一种统计学方法,用于降维、特征提取以及数据分析。PCA不仅可以用于数据的降维,还可以用于计算点云的最优拟合包围盒,即最小包围盒(Oriented Bounding Box, OBB)。与AABB不同,OBB可以任意旋转,能够更紧密地包围点云数据。
1 原理介绍
PCA的基本思想是通过线性变换,将原始数据投影到一个新的坐标系中,这个坐标系由数据的主要方向组成。对于点云数据,PCA可以找出点云的主要方向,从而计算出一个与主要方向对齐的最小包围盒。
2 数学公式推导
数据中心化:
-
对于给定点云 P={p1,p2,…,pn},首先计算点云的质心 pˉ:
- 将点云中心化:
协方差矩阵:
- 计算中心化点云的协方差矩阵 Σ:
特征值分解:
- 对协方差矩阵进行特征值分解,得到特征值和特征向量:
-
计算OBB:
- 利用特征向量组成的新坐标系,对点云进行变换,计算在新坐标系下的AABB,即为OBB。
3 算法流程
-
数据准备:
- 输入点云数据。
-
PCA分析:
- 计算点云的质心并中心化,计算协方差矩阵。
-
特征值分解:
- 获取协方差矩阵的特征值和特征向量。
-
计算OBB:
- 将点云投影到特征向量构成的新坐标系中,计算最小包围盒。
-
结果输出:
- 返回OBB的尺寸、位置以及方向。
4 示例代码
#include <pcl/point_types.h>
#include <pcl/io/pcd_io.h>
#include <pcl/common/common.h>
#include <pcl/common/pca.h>
#include <pcl/common/transforms.h>
#include <pcl/visualization/pcl_visualizer.h>
int main(int argc, char** argv) {
// 创建点云对象并加载点云数据
pcl::PointCloud<pcl::PointXYZ>::Ptr cloud(new pcl::PointCloud<pcl::PointXYZ>);
if (pcl::io::loadPCDFile<pcl::PointXYZ>("bunny.pcd", *cloud) == -1) {
PCL_ERROR("Couldn't read file input.pcd \n");
return (-1);
}
// 创建PCA对象并进行计算
pcl::PCA<pcl::PointXYZ> pca;
pca.setInputCloud(cloud);
// 获取点云的质心
Eigen::Vector4f centroid;
pcl::compute3DCentroid(*cloud, centroid);
// 获取特征向量
Eigen::Matrix3f eigen_vectors = pca.getEigenVectors();
// 将点云变换到PCA坐标系
Eigen::Matrix4f projection_transform(Eigen::Matrix4f::Identity());
projection_transform.block<3, 3>(0, 0) = eigen_vectors.transpose();
projection_transform.block<3, 1>(0, 3) = -1.0f * (projection_transform.block<3, 3>(0, 0) * centroid.head<3>());
pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_transformed(new pcl::PointCloud<pcl::PointXYZ>);
pcl::transformPointCloud(*cloud, *cloud_transformed, projection_transform);
// 计算变换后的AABB
pcl::PointXYZ min_point, max_point;
pcl::getMinMax3D(*cloud_transformed, min_point, max_point);
// OBB尺寸
Eigen::Vector3f obb_dimensions = 0.5f * (max_point.getVector3fMap() + min_point.getVector3fMap());
// OBB变换矩阵的创建
Eigen::Matrix4f projection_transform_inv = projection_transform.inverse();
Eigen::Affine3f obb_transform(projection_transform_inv);
Eigen::Vector3f obb_dimensions2;
pcl::transformPoint(obb_dimensions, obb_dimensions2, obb_transform);
Eigen::Vector3f whd;
whd = max_point.getVector3fMap() - min_point.getVector3fMap();
const Eigen::Quaternionf bboxQ(projection_transform_inv.block<3, 3>(0, 0));
const Eigen::Vector3f bboxT(obb_dimensions2);
// 可视化
pcl::visualization::PCLVisualizer::Ptr viewer(new pcl::visualization::PCLVisualizer("3D Viewer"));
viewer->setBackgroundColor(0, 0, 0);
viewer->addPointCloud<pcl::PointXYZ>(cloud, "sample cloud");
// 通过变换矩阵和尺寸添加OBB
viewer->addCube(bboxT, bboxQ, whd(0), whd(1), whd(2), "OBB");
viewer->setShapeRenderingProperties(pcl::visualization::PCL_VISUALIZER_REPRESENTATION, pcl::visualization::PCL_VISUALIZER_REPRESENTATION_WIREFRAME, "OBB");
viewer->addCoordinateSystem(0.01);
viewer->initCameraParameters();
while (!viewer->wasStopped()) {
viewer->spinOnce(100);
}
return 0;
}