将点云投影到其特征向量上。

huangapple go评论109阅读模式
英文:

Projecting pointcloud on its eigenvectors

问题

关于我在另一个问题上的回答,我尝试将两个点云投影到它们的特征向量上。

我正在使用 c++PointCloudLibrary。不幸的是,我无法找到 PCA 类的良好文档。

我尝试了以下方法来进行投影,其中 model_cloud 是我的点云:

pcl::PCA<pcl::PointNormal> pca;
pca.setInputCloud(model_cloud_ptr);
pcl::PointCloud<pcl::PointNormal> projection;
pca.project(model_cloud_nt, projection);

Eigen::Matrix3f ev_M = pca.getEigenVectors();

我不明白为什么我必须设置这个 inputCloud,然后将特定的点云作为投影的参数。我只想将它降维到2D并获取特征向量。

有人可以帮助我吗?非常感谢!

英文:

Referring to the answer on my other question I tried to project two pointclouds to their eigenvectors.

I am using c++ and the PointCloudLibrary. Unfortunately I wasn't able to find a good documentation of the PCA class.

I tried the following to do the projectiong while model_cloud is my pointcloud:

pcl::PCA&lt;pcl::PointNormal&gt; pca;
pca.setInputCloud(model_cloud_ptr);
pcl::PointCloud&lt;pcl::PointNormal&gt; projection;
pca.project(model_cloud_nt, projection);

Eigen::Matrix3f ev_M = pca.getEigenVectors();

I don't understand why I have to set this inputCloud but then give a specific cloud as parameter for the projection. I just want to PCA it down to 2D and get the Eigenvectors.

Can anyone help me? Thanks alot!

答案1

得分: 2

为了计算主成分(计算特征向量),您需要:

pcl::PCA<pcl::PointXYZ> pca(cloud); // 在构造函数中计算
Eigen::Matrix3f eigen_vectors = pca.getEigenVectors(); // 返回计算的特征向量作为矩阵

或者,可以使用以下方法(getEigenVectors()):

pcl::PCA<pcl::PointNormal> pca;
pca.setInputCloud(cloud);
Eigen::Matrix3f eigen_vectors = pca.getEigenVectors(); // 在此处计算PCA

这两种方法都是合法的,在这一步,您已经计算出了投影(主成分)。请注意,这是一个从3D到3D的投影(基本上是一个旋转)。2D投影只是忽略了第三轴(方差最小的轴)的结果。您可以通过以下方式获得相关的轴(特征向量):

Eigen::Vector3f x_axis = eigen_vectors.col(0);
Eigen::Vector3f y_axis = eigen_vectors.col(1);

一旦计算出投影,您可以将其应用于任何点云。

pca.project(cloud, projection);  // 投影用于计算投影的点云
pca.project(another_cloud, projection); // 投影到任何其他点云上

最后的示例:

pcl::PCA<pcl::PointNormal> pca;
pca.setInputCloud(cloud_a);
pca.project(cloud_b, projection);  // 基于cloud_a计算投影,然后将投影应用于cloud_b
英文:

To compute the principal components (compute the eigen vectors), you need:

pcl::PCA&lt;pcl::PointXYZ&gt; pca(cloud); // computed in the constructor
Eigen::Matrix3f eigen_vectors = pca.getEigenVectors(); // returns computed eigen vectors as a matrix

or alternatively (getEigenVectors()):

pcl::PCA&lt;pcl::PointNormal&gt; pca;
pca.setInputCloud(cloud);
Eigen::Matrix3f eigen_vectors = pca.getEigenVectors(); // pca computed here

Both methods are legitimate, and at this point, you've computed the projection (the principal components). Please note this is a 3D to 3D projection (basically a rotation). 2D is just the result of ignoring the 3rd axis (with least variance). You can get the relevant axes (eigen vectors) by:

Eigen::Vector3f x_axis = eigen_vector.col(0);
Eigen::Vector3f y_axis = eigen_vector.col(1);

Once the projection is calculated, you can apply it to any cloud.

pca.project(cloud, projection);  // project the cloud that was used to calculate the projection
pca.project(another_cloud, projection); // project any other cloud

Final example:

pcl::PCA&lt;pcl::PointNormal&gt; pca;
pca.setInputCloud(cloud_a);
pca.project(cloud_b, projection);  // calculate projection based on cloud_a, and apply the projection to cloud_b 

答案2

得分: 2

I believe that you are trying to put a cloud into its eigenspace (orientedGolden is the cloud in eigenspace). This is how that could be done:

pcl::PCA<pcl::PointXYZ> pcaGolden;
pcl::PointCloud<pcl::PointXYZ>::Ptr orientedGolden(new pcl::PointCloud<pcl::PointXYZ>);
pcaGolden.setInputCloud(goldenCloud);
pcaGolden.project(*goldenCloud, *orientedGolden);

// This is the scale factor described in the other question
pcl::PointXYZ goldenMin, goldenMax;
pcl::getMinMax3D(*orientedGolden, goldenMin, goldenMax);
double scale = goldenMax.x - goldenMin.x;

Explanation: PCA is used to calculate the mean and primary axes of variation. The eigenvectors from PCA can be directly inserted into a transformation matrix as a rotation matrix because they are always mutually orthogonal (i.e., represent a frame). The midpoint is also taken so that in combination with the vectors, a full transformation matrix can be made, which, when applied to the target cloud, will move it such that its mean is on the origin, and its primary axes of variation align with the Cartesian coordinate system (xyz). This can be useful to get what is called an oriented bounding box (what I think you are trying to do in your other question), which is the bounding box calculated about a cloud in its eigen space. The reason an oriented bounding box is better than a general bound box is that it will remain the same for a given cloud despite any number of rotations to that cloud, whereas a standard bounding box will vary in dimensions.

Project function:

pcl::PointCloud<pcl::PointXYZ>::Ptr orientedGolden(new pcl::PointCloud<pcl::PointXYZ>);
pcl::PCA<pcl::PointXYZ> pcaGolden;
pcaGolden.setInputCloud(goldenCloud);
pcaGolden.project(*goldenCloud, *orientedGolden);

Is equivalent to this:

pcl::PointCloud<pcl::PointXYZ>::Ptr orientedGolden(new pcl::PointCloud<pcl::PointXYZ>);
pcl::PCA<pcl::PointXYZ> pcaGolden;
pcaGolden.setInputCloud(goldenCloud);
Eigen::Matrix3f goldenEVs_Dir = pcaGolden.getEigenVectors();
Eigen::Vector4f goldenMidPt = pcaGolden.getMean();
Eigen::Matrix4f goldenTransform = Eigen::Matrix4f::Identity();
goldenTransform.block<3, 3>(0, 0) = goldenEVs_Dir;
goldenTransform.block<4, 1>(0, 3) = goldenMidPt;
pcl::transformPointCloud(*goldenCloud, *orientedGolden, goldenTransform.inverse());
英文:

I believe that you are trying to put a cloud into it's eigenspace (orientedGolden is the cloud in eigenspace). This is how that could be done:

pcl::PCA&lt;pcl::PointXYZ&gt; pcaGolden;
pcl::PointCloud&lt;pcl::PointXYZ&gt;::Ptr orientedGolden(new pcl::PointCloud&lt;pcl::PointXYZ&gt;);
pcaGolden.setInputCloud(goldenCloud);
pcaGolden.project(*goldenCloud, *orientedGolden);

//this is the scale factor described in the other question
pcl::PointXYZ goldenMin, goldenMax;
pcl::getMinMax3D(*orientedGolden, goldenMin, goldenMax);
double scale = goldenMax.x - goldenMin.x;

Explanation: PCA is used to calculate the mean and primary axes of variation. The eigenvectors from PCA can be directly inserted into a transformation matrix as a rotation matrix because they are always mutually orthogonal (ie represent a frame). The midpoint is also taken so that in combination with the vectors, a full transformation matrix can be made which (when applied to the target cloud) will move it such that its mean is on origin and its primary axes of variation align with the cartesian coordinate system (xyz). This can be useful to get what is called an oriented bounding box (what I think you are trying to do in your other question), which is the bounding box calculated about a cloud in its eigen space. The reason an oriented bounding box is better than a general bound box is that it will remain the same for a given cloud despite any number of rotations to that cloud whereas a standard bounding box will vary in dimensions.

Project function:

This:

pcl::PointCloud&lt;pcl::PointXYZ&gt;::Ptr orientedGolden(new pcl::PointCloud&lt;pcl::PointXYZ&gt;);
pcl::PCA&lt;pcl::PointXYZ&gt; pcaGolden;
pcaGolden.setInputCloud(goldenCloud);
pcaGolden.project(*goldenCloud, *orientedGolden);

Is equivalent to this:

pcl::PointCloud&lt;pcl::PointXYZ&gt;::Ptr orientedGolden(new pcl::PointCloud&lt;pcl::PointXYZ&gt;);
pcl::PCA&lt;pcl::PointXYZ&gt; pcaGolden;
pcaGolden.setInputCloud(goldenCloud);
Eigen::Matrix3f goldenEVs_Dir = pcaGolden.getEigenVectors();
Eigen::Vector4f goldenMidPt = pcaGolden.getMean();
Eigen::Matrix4f goldenTransform = Eigen::Matrix4f::Identity();
goldenTransform.block&lt;3, 3&gt;(0, 0) = goldenEVs_Dir;
goldenTransform.block&lt;4, 1&gt;(0, 3) = goldenMidPt;
pcl::transformPointCloud(*goldenCloud, *orientedGolden, goldenTransform.inverse());

huangapple
  • 本文由 发表于 2020年1月3日 20:52:59
  • 转载请务必保留本文链接:https://go.coder-hub.com/59578991.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定