2011-03-30 243 views
1

我在3D对象的表面上有随机采样点的集合。我想能够计算两个不同对象之间的相似度。为了做到这一点,我首先必须确保我要比较的两个对象的采样点具有相同的旋转和缩放比例。我认为我可以通过沿x/y/z轴定位主分量轴并缩放,使得最长的主分量具有单位长度。使用CGAL进行主成分分析的点云对齐

我首先计算点集的质心,并翻译所有点,使得原点变成新的质心。

我使用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对象(我使用OSG的图形引擎)。最后,我旋转一切使用下面的代码的单位轴:

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

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

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

为了测试它,我使用了相同点集的两个副本,但是一个被转换(旋转和翻译)。上面的代码应该取消旋转,但结果并不是我所期望的:请参阅this image。红线表示最佳拟合平面的基向量及其正常值。看起来这两个调用linear_least_squares_fitting_3的结果给出了稍微不同的答案,因为其中一个飞机相对于另一个飞机旋转了一点点。

Here is another图像中两个对象的位置均以原点为中心。现在可以清楚地看到法线和基本矢量集中在一起,但这些点却没有。

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

回答

3

将平面拟合成一组点将一个自由度限制为不受约束。这架飞机可以自由地绕其正常方向旋转,而且飞行姿势相同。我对CGAL一无所知,但当发现它们在找到合适的时候发现它们只是找到一个方便的平面(可能是距离原始轴的最近投影)时,我并不会感到惊讶。

如果你在点云上做了真正的PCA,我认为你不会有这个问题。或者,也许您可​​以根据拟合算法发现的法线重新调整(拉伸)数据,然后找到另一个拟合。如果将数据拉伸得足够大,那么找到的第一个平面不应该与某个正交平面一样好。

+0

谢谢你的回答,但不幸的是你的建议不起作用,也许是因为我理解错了。你知道一个真正的PCA的C++库吗? – muksie 2011-03-30 20:42:21

0

CGoo并没有像JCooper所说的那样计算所有主要组件。我切换到ALGLIB库来完成PCA,现在它可以工作。