Reputation: 4832
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<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();
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!
Upvotes: 2
Views: 2157
Reputation: 1671
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<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 (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<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());
Upvotes: 2
Reputation: 2170
To compute the principal components (compute the eigen vectors), you need:
pcl::PCA<pcl::PointXYZ> pca(cloud); // computed in the constructor
Eigen::Matrix3f eigen_vectors = pca.getEigenVectors(); // returns computed eigen vectors as a matrix
or alternatively (getEigenVectors()):
pcl::PCA<pcl::PointNormal> 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<pcl::PointNormal> pca;
pca.setInputCloud(cloud_a);
pca.project(cloud_b, projection); // calculate projection based on cloud_a, and apply the projection to cloud_b
Upvotes: 2