graphics - 使用 PCA 的 3D 面向对象边界框

标签 graphics computational-geometry eigen pca bounding-box

我正在尝试计算一组点的面向对象的边界框。我正在使用 C++ 和 Eigen 线性代数库。

我一直使用两篇博客文章作为指导,但我的边界框仍然不正确(参见图片)。

blog post 1 blog post 2

我希望我的注释代码清楚地表达了我的尝试,但该算法的要点是使用 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

enter image description here enter image description here

enter image description here enter image description here

最佳答案

您必须计算 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 .

关于graphics - 使用 PCA 的 3D 面向对象边界框,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32769771/

相关文章:

c++大型特征分解速度

matrix - 如何将 Eigen 中的 1*1 矩阵更改为 float ?

java - Graphics 和 Graphics2D 之间的区别?

graphics - 实际使用 OCaml Graphics 更改文本大小

java - 四边数据结构 makeEdge 逻辑

geometry - 如何计算垂线的坐标?

c++ - CGAL 在 3D 中按 x 轴旋转

c++ - 制作矩阵的更好方法 - 在 Eigen 中记录操作?

visual-c++ - 从整数值中提取 rgb 颜色分量

java - 为什么 JPanel(面板)不绘制在绿色背景(jpanel)上?