使用 CGAL 的主成分分析进行点云对齐

发布于 2024-10-28 05:36:29 字数 1788 浏览 2 评论 0原文

我在 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。出于测试目的,我将旋转点的质心平移回原始位置,因此两个点云不会重叠。

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 的结果给出的答案略有不同,因为其中一个平面相对于另一个平面稍微旋转了一点。

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

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

I have sets of randomly sampled points on the surface of 3D objects. I want to be able to compute the similarity between two different objects. To make that work, I first have to make sure that the sample points of both objects I want to compare do have the same rotation and scale. I thought I could do this by orienting the principal component axes along the x/y/z axes, and scaling such that the longest principal component does have unit length.

I first compute the centroid of the point set, and translate all points such that the origin becomes the new centroid.

I do the principal component analysis using the CGAL linear_least_squares_fitting_3 function, which gives the best fitting plane through the points. I compute the normal of this plane by taking the cross product of both base vectors:

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();

The dir2vec function converts a CGAL::Direction_3 object to an equivalent osg::Vec3d object (I am using the OpenSceneGraph graphics engine). Finally, I rotate everything to the unit axes using the following code:

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);

Here, osgPoints is an vector<osg::Vec3d>. For testing purposes, I translate the centroid of the rotated points back to original location, so both point clouds don't overlap.

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

To test it, I use two copies of the same point set, however one is transformed (rotated and translated). The above code should undo the rotations, however the results are not what I did expect: See this image. The red lines indicate the base vectors of the best fitting planes and their normal. It looks like that the results of both calls to linear_least_squares_fitting_3 gives slightly different answers, as one of the planes is rotated a little bit with respect to the other.

Here is another image where both objects are positioned with their centroid in the origin. It is now clearly visible that the normals and base vectors fall together, but the points do not.

Does anybody know why this happens, and, how I can prevent it?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

尬尬 2024-11-04 05:36:29

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

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

Fitting a plane to a set of points leaves one degree of freedom unconstrained. The plane is free to spin around its normal and the fit is equal. I don't know anything about CGAL, but I wouldn't be surprised to discover that they just find a convenient plane when finding the fit (probably a nearest projection from the original axes of the space).

If you did real PCA on the point cloud, I don't think you'd have that problem. Alternatively, perhaps you could rescale (stretch) your data along the normal discovered by the fitting algorithm and then find another fit. If you stretch the data out sufficiently, then the first plane found shouldn't be as good a fit as some orthogonal plane.

还在原地等你 2024-11-04 05:36:29

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

It indeed seemed that CGAL does not compute all principal components, as JCooper suggested. I switched to the ALGLIB library to do the PCA and now it works.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文