天天看點

PCL點雲庫——PCA粗配準

PCA粗配準

  主成分分析法( Principal Component Analysis,簡稱PCA)是使資料簡化的算法,通過揭露資料内部的主要分布方向,減少了資料集的維數,進而保留了點雲集中貢獻最大的特征,更好地解釋資料的變化規律。PCA 算法進行點雲配準是以主方向為依據,根據兩點雲的主方向計算出對應的三個旋轉和三個平移量,可大緻對齊兩點雲資料。

  PCA算法流程如下:

  (1)原始特征矩陣歸一化處理(假設M和樣本,每個樣本n個特征,則對M*N的X資料,進行零均值化,即減去這一列的均值);

  (2)求取歸一化處理後特征矩陣的協方差矩陣;

  (3)計算協方差矩陣的特征值及其對應的特征向量;

  (4)将特征向量按對應特征值大小從上到下按行排列成矩陣,取前k行組成矩陣P;

  (5)Y=PX即為降維到k維後的資料。

#include <pcl/visualization/cloud_viewer.h>
#include <pcl/io/pcd_io.h>
#include <pcl/common/transforms.h>

//計算向量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("cloud\\target.pcd", *target);
	pcl::io::loadPCDFile("cloud\\source.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);

	pcl::PointCloud<pcl::PointXYZ>::Ptr translationCloud(new pcl::PointCloud<pcl::PointXYZ>);
	Eigen::Vector3f n0, n1, n2;
	double angle0, angle1, angle2;
	calRotation(eigenVectorsPCA.col(0), model_eigenVectorsPCA.col(0), angle0, n0);
	calRotation(eigenVectorsPCA.col(1), model_eigenVectorsPCA.col(1), angle1, n1);
	calRotation(eigenVectorsPCA.col(2), model_eigenVectorsPCA.col(2), angle2, n2);
	Eigen::Matrix4f transform0(Eigen::Matrix4f::Identity());
	Eigen::Matrix4f transform1(Eigen::Matrix4f::Identity());
	Eigen::Matrix4f transform2(Eigen::Matrix4f::Identity());
	transform0 = RodriguesMatrixTranslation(n0, angle0);
	transform1 = RodriguesMatrixTranslation(n1, angle1);
	transform2 = RodriguesMatrixTranslation(n2, angle2);
	pcl::transformPointCloud(*source_t, *translationCloud, transform2);  //源點雲整體旋轉,最大主方向對齊
	pcl::transformPointCloud(*translationCloud, *translationCloud, transform0);  //源點雲整體旋轉,最小主方向對齊
	//pcl::transformPointCloud(*translationCloud, *translationCloud, transform1);  //源點雲整體旋轉,

	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);
	viewer.addCoordinateSystem(0.5, 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);

	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);
	viewer.addCoordinateSystem(0.5, 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>(translationCloud, "translationCloud", 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;
}
           
PCL點雲庫——PCA粗配準

圖1 最大主方向對齊

PCL點雲庫——PCA粗配準

圖2 最小主方向對齊

PCL點雲庫——PCA粗配準

圖3 次大主方向對齊

PCL點雲庫——PCA粗配準

圖4 最大與最小主方向對齊

繼續閱讀