使用 PCA 的 3D 面向对象边界框
3D Object Oriented Bounding Box using PCA
我正在尝试为一组点计算面向对象的边界框。我正在使用 C++ 和 Eigen 线性代数库。
我一直在使用两篇博文作为指南,但我的边界框仍然不正确(见图片)。
我希望我的评论代码清楚我的尝试,但该算法的要点是使用 PCA 找到面向对象坐标系的基向量。
然后将所有点投影到新框架中,找到定义框的最小和最大点,然后将这些点投影到原始坐标框架中并渲染它们。
我可以成功渲染一个框,但它不是边界框并且看起来与正常的 x、y、z 轴对齐。对于显示的两个对象中的每一个,这在第一张图片中都很清楚。
任何帮助将不胜感激。提前致谢。
// iglVertices is a X by 3 Eigen::::MatrixXf
// Covariance matrix and eigen decomposition
Eigen::MatrixXf centered = iglVertices.rowwise() - iglVertices.colwise().mean();
Eigen::MatrixXf cov = centered.adjoint() * centered;
Eigen::SelfAdjointEigenSolver<Eigen::MatrixXf> eig(cov);
//Setup homogenous tranformation to act as new basis functions for new coordinate frame
auto basis = Eigen::Matrix4f(eig.eigenvectors().colwise().homogeneous().rowwise().homogeneous());
basis.row(3) = Eigen::Vector4f::Zero();
basis.col(3) = Eigen::Vector4f::Zero();
basis(3,3) = 1.0f;
std::cout << "eig.eigenvectors() " << eig.eigenvectors() << std::endl;
std::cout << "Basis " << basis << std::endl;
//invert matrix and and transform points into new coordinate frame
auto invBasis = basis.inverse();
auto newVertices = invBasis * iglVertices.rowwise().homogeneous().transpose();
//Find max and min for all of the new axis
auto maxP = newVertices.rowwise().maxCoeff();
auto minP = newVertices.rowwise().minCoeff();
std::cout << "max " << maxP << std::endl;
std::cout << "min " << minP << std::endl;
//Find center and half extent in new coordinate frame
auto center = Eigen::Vector4f((maxP + minP) / 2.0);
auto half_extent = Eigen::Vector4f((maxP - minP) / 2.0);
auto t = Eigen::Vector4f((basis * center));
std::cout << "t " << t << std::endl;
//Update basis function with the translation between two coordinate origins
//I don't actually understand why I need this and have tried without it but still my bounding
//box is wrong
basis.col(3) = Eigen::Vector4f(t[0], t[1], t[2], t[3]);
std::cout << "Basis complete " << basis << std::endl;
std::cout << "center " << center << std::endl;
std::cout << "half_extent " << half_extent << std::endl;
//This is the same as the previous minP/maxP but thought i should try this as
// box is paramaterised with center and half-extent
auto max = center + half_extent;
auto min = center - half_extent;
//Transform back into the original coordinates
auto minNormalBasis = (basis * min).hnormalized();
auto maxNormalBasis = (basis * max).hnormalized();
std::cout << "min new coord" << min << std::endl;
std::cout << "max new coord"<< max << std::endl;
std::cout << "min old coord" << minNormalBasis << std::endl;
std::cout << "max old coord"<< maxNormalBasis << std::endl;
//Extract min and max
auto min_x = minNormalBasis[0];
auto min_y = minNormalBasis[1];
auto min_z = minNormalBasis[2];
auto max_x = maxNormalBasis[0];
auto max_y = maxNormalBasis[1];
auto max_z = maxNormalBasis[2];
bBox.clear();
//Build box for rendering
//Ordering specific to the faces I have manually generated
bBox.push_back(trimesh::point(min_x, min_y, min_z));
bBox.push_back(trimesh::point(min_x, max_y, min_z));
bBox.push_back(trimesh::point(min_x, min_y, max_z));
bBox.push_back(trimesh::point(min_x, max_y, max_z));
bBox.push_back(trimesh::point(max_x, min_y, max_z));
bBox.push_back(trimesh::point(max_x, max_y, max_z));
bBox.push_back(trimesh::point(max_x, min_y, min_z));
bBox.push_back(trimesh::point(max_x, max_y, min_z));
喷雾瓶示例的打印输出是
eig.eigenvectors() 0 -0.999992 -0.00411613
-0.707107 -0.00291054 0.707101
0.707107 -0.00291054 0.707101
Basis 0 -0.999992 -0.00411613 0
-0.707107 -0.00291054 0.707101 0
0.707107 -0.00291054 0.707101 0
0 0 0 1
max 2.98023e-08
0.216833
0.582629
1
min -2.98023e-08
-0.215
-0.832446
1
t -0.000402254
-0.0883253
-0.0883253
1
Basis complete 0 -0.999992 -0.00411613 -0.000402254
-0.707107 -0.00291054 0.707101 -0.0883253
0.707107 -0.00291054 0.707101 -0.0883253
0 0 0 1
center 0
0.000916399
-0.124908
1
half_extent 2.98023e-08
0.215916
0.707537
0
min new coord-2.98023e-08
-0.215
-0.832446
1
max new coord2.98023e-08
0.216833
0.582629
1
min old coord 0.218022
-0.676322
-0.676322
max old coord-0.219631
0.323021
0.323021
您必须计算 PCA 框架内轴对齐框的 8 个角,然后对它们应用旋转:
bBox.push_back(eig.eigenvectors() * Vector3f(minP.x(), minP.y(), minP.z()));
bBox.push_back(eig.eigenvectors() * Vector3f(minP.x(), maxP.y(), minP.z()));
bBox.push_back(eig.eigenvectors() * Vector3f(minP.x(), minP.y(), maxP.z()));
bBox.push_back(eig.eigenvectors() * Vector3f(minP.x(), maxP.y(), maxP.z()));
...
你也可以直接计算newVertices
为:
Matrix<float,3,Dynamic> newVertices = eig.eigenvectors().transpose() * iglVertices.transpose();
进行这些更改后,您的代码将减少一半;)
更重要的是,除非您知道自己在做什么,否则请避免使用 auto
关键字。在您的示例中,它的大部分用法都是非常糟糕的做法,更不用说错误了。请阅读此 page.
我正在尝试为一组点计算面向对象的边界框。我正在使用 C++ 和 Eigen 线性代数库。
我一直在使用两篇博文作为指南,但我的边界框仍然不正确(见图片)。
我希望我的评论代码清楚我的尝试,但该算法的要点是使用 PCA 找到面向对象坐标系的基向量。
然后将所有点投影到新框架中,找到定义框的最小和最大点,然后将这些点投影到原始坐标框架中并渲染它们。
我可以成功渲染一个框,但它不是边界框并且看起来与正常的 x、y、z 轴对齐。对于显示的两个对象中的每一个,这在第一张图片中都很清楚。
任何帮助将不胜感激。提前致谢。
// iglVertices is a X by 3 Eigen::::MatrixXf
// Covariance matrix and eigen decomposition
Eigen::MatrixXf centered = iglVertices.rowwise() - iglVertices.colwise().mean();
Eigen::MatrixXf cov = centered.adjoint() * centered;
Eigen::SelfAdjointEigenSolver<Eigen::MatrixXf> eig(cov);
//Setup homogenous tranformation to act as new basis functions for new coordinate frame
auto basis = Eigen::Matrix4f(eig.eigenvectors().colwise().homogeneous().rowwise().homogeneous());
basis.row(3) = Eigen::Vector4f::Zero();
basis.col(3) = Eigen::Vector4f::Zero();
basis(3,3) = 1.0f;
std::cout << "eig.eigenvectors() " << eig.eigenvectors() << std::endl;
std::cout << "Basis " << basis << std::endl;
//invert matrix and and transform points into new coordinate frame
auto invBasis = basis.inverse();
auto newVertices = invBasis * iglVertices.rowwise().homogeneous().transpose();
//Find max and min for all of the new axis
auto maxP = newVertices.rowwise().maxCoeff();
auto minP = newVertices.rowwise().minCoeff();
std::cout << "max " << maxP << std::endl;
std::cout << "min " << minP << std::endl;
//Find center and half extent in new coordinate frame
auto center = Eigen::Vector4f((maxP + minP) / 2.0);
auto half_extent = Eigen::Vector4f((maxP - minP) / 2.0);
auto t = Eigen::Vector4f((basis * center));
std::cout << "t " << t << std::endl;
//Update basis function with the translation between two coordinate origins
//I don't actually understand why I need this and have tried without it but still my bounding
//box is wrong
basis.col(3) = Eigen::Vector4f(t[0], t[1], t[2], t[3]);
std::cout << "Basis complete " << basis << std::endl;
std::cout << "center " << center << std::endl;
std::cout << "half_extent " << half_extent << std::endl;
//This is the same as the previous minP/maxP but thought i should try this as
// box is paramaterised with center and half-extent
auto max = center + half_extent;
auto min = center - half_extent;
//Transform back into the original coordinates
auto minNormalBasis = (basis * min).hnormalized();
auto maxNormalBasis = (basis * max).hnormalized();
std::cout << "min new coord" << min << std::endl;
std::cout << "max new coord"<< max << std::endl;
std::cout << "min old coord" << minNormalBasis << std::endl;
std::cout << "max old coord"<< maxNormalBasis << std::endl;
//Extract min and max
auto min_x = minNormalBasis[0];
auto min_y = minNormalBasis[1];
auto min_z = minNormalBasis[2];
auto max_x = maxNormalBasis[0];
auto max_y = maxNormalBasis[1];
auto max_z = maxNormalBasis[2];
bBox.clear();
//Build box for rendering
//Ordering specific to the faces I have manually generated
bBox.push_back(trimesh::point(min_x, min_y, min_z));
bBox.push_back(trimesh::point(min_x, max_y, min_z));
bBox.push_back(trimesh::point(min_x, min_y, max_z));
bBox.push_back(trimesh::point(min_x, max_y, max_z));
bBox.push_back(trimesh::point(max_x, min_y, max_z));
bBox.push_back(trimesh::point(max_x, max_y, max_z));
bBox.push_back(trimesh::point(max_x, min_y, min_z));
bBox.push_back(trimesh::point(max_x, max_y, min_z));
喷雾瓶示例的打印输出是
eig.eigenvectors() 0 -0.999992 -0.00411613
-0.707107 -0.00291054 0.707101
0.707107 -0.00291054 0.707101
Basis 0 -0.999992 -0.00411613 0
-0.707107 -0.00291054 0.707101 0
0.707107 -0.00291054 0.707101 0
0 0 0 1
max 2.98023e-08
0.216833
0.582629
1
min -2.98023e-08
-0.215
-0.832446
1
t -0.000402254
-0.0883253
-0.0883253
1
Basis complete 0 -0.999992 -0.00411613 -0.000402254
-0.707107 -0.00291054 0.707101 -0.0883253
0.707107 -0.00291054 0.707101 -0.0883253
0 0 0 1
center 0
0.000916399
-0.124908
1
half_extent 2.98023e-08
0.215916
0.707537
0
min new coord-2.98023e-08
-0.215
-0.832446
1
max new coord2.98023e-08
0.216833
0.582629
1
min old coord 0.218022
-0.676322
-0.676322
max old coord-0.219631
0.323021
0.323021
您必须计算 PCA 框架内轴对齐框的 8 个角,然后对它们应用旋转:
bBox.push_back(eig.eigenvectors() * Vector3f(minP.x(), minP.y(), minP.z()));
bBox.push_back(eig.eigenvectors() * Vector3f(minP.x(), maxP.y(), minP.z()));
bBox.push_back(eig.eigenvectors() * Vector3f(minP.x(), minP.y(), maxP.z()));
bBox.push_back(eig.eigenvectors() * Vector3f(minP.x(), maxP.y(), maxP.z()));
...
你也可以直接计算newVertices
为:
Matrix<float,3,Dynamic> newVertices = eig.eigenvectors().transpose() * iglVertices.transpose();
进行这些更改后,您的代码将减少一半;)
更重要的是,除非您知道自己在做什么,否则请避免使用 auto
关键字。在您的示例中,它的大部分用法都是非常糟糕的做法,更不用说错误了。请阅读此 page.