2

我在 3D 对象的表面上有一组随机采样点。我希望能够计算两个不同对象之间的相似性。为了完成这项工作,我首先必须确保我要比较的两个对象的样本点确实具有相同的旋转和比例。我想我可以通过沿 x/y/z 轴定向主分量轴并缩放以使最长的主分量确实具有单位长度来做到这一点。

我首先计算点集的质心,然后平移所有点,使原点成为新的质心。

我使用 CGAL linear_least_squares_fitting_3 函数进行主成分分析,它给出了通过点的最佳拟合平面。我通过取两个基向量的叉积来计算这个平面的法线:

Plane plane;
linear_least_squares_fitting_3(points.begin(), points.end(), 
    plane, CGAL::Dimension_tag<0>());

auto dir1 = dir2vec(plane.base1().direction());
auto dir2 = dir2vec(plane.base2().direction());
auto normal = dir1 ^ dir2; // cross product
normal.normalize(); dir1.normalize(); dir2.normalize();

dir2vec函数将CGAL::Direction_3对象转换为等效osg::Vec3d对象(我使用的是 OpenSceneGraph 图形引擎)。最后,我使用以下代码将所有内容旋转到单位轴:

Matrixd r1, r2, r3;
r1.makeRotate(normal, Vec3d(1,0,0));
r2.makeRotate(dir1 * r1, Vec3d(0,1,0));
r3.makeRotate(dir2 * r1 * r2, Vec3d(0,0,1));
auto rotate = [&](Vec3d const &p) {
    return p * r1 * r2 * r3;
};
transform(osgPoints.begin(), osgPoints.end(), osgPoints.begin(), rotate);

这里,osgPoints是一个vector<osg::Vec3d>. 出于测试目的,我将旋转点的质心平移回原始位置,因此两个点云不会重叠。

Vec3d center = point2vec(centroid);
auto tocentroid = [&](Vec3d const &v) {
    return v + center;
};
transform(osgPoints.begin(), osgPoints.end(), osgPoints.begin(), tocentroid);

为了测试它,我使用了相同点集的两个副本,但是一个被转换(旋转和平移)。上面的代码应该撤消旋转,但是结果不是我所期望的:见这张图片。红线表示最佳拟合平面的基向量及其法线。看起来这两个调用的结果linear_least_squares_fitting_3给出的答案略有不同,因为其中一个平面相对于另一个平面旋转了一点。

这是另一个图像,其中两个对象的质心都位于原点。现在可以清楚地看到法线和基本向量一起下降,但点没有。

有谁知道为什么会发生这种情况,以及如何防止它?

4

2 回答 2

3

将平面拟合到一组点会使一个自由度不受约束。飞机可以围绕其法线自由旋转,并且适合度相等。我对CGAL一无所知,但我不会惊讶地发现他们只是在找到合适的平面时找到了一个方便的平面(可能是距离空间原始轴最近的投影)。

如果你在点云上做了真正的 PCA,我认为你不会有这个问题。或者,也许您可​​以沿着拟合算法发现的法线重新缩放(拉伸)数据,然后找到另一个拟合。如果您充分拉伸数据,那么找到的第一个平面不应该像某些正交平面那样适合。

于 2011-03-30T16:03:01.230 回答
0

正如 JCooper 所建议的那样,CGAL 似乎确实没有计算所有主成分。我切换到 ALGLIB 库来执行 PCA,现在它可以工作了。

于 2011-03-30T21:49:38.450 回答