progNewbie
progNewbie

Reputation: 4832

Projecting pointcloud on its eigenvectors

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

Answers (2)

Sneaky Polar Bear
Sneaky Polar Bear

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

Mark Loyman
Mark Loyman

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

Related Questions