2011-03-30 56 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,現在它可以工作。