1. 程式人生 > >點雲的平滑與法線計算

點雲的平滑與法線計算

需要平滑的情況:

1、用RGB-D鐳射掃描器等裝置掃描物體,尤其是比較小的物體時,往往會有測量誤差。這些誤差所造成的不規則資料如果直接拿來曲面重建的話,會使得重建的曲面不光滑或者有漏洞,而且這種不規則資料很難用前面我們提到過的統計分析等濾波方法消除;

2、後處理過程中,對同一個物體從不同方向進行了多次掃描,然後把掃描結果進行配準,最後得到一個完整的模型,但是你配準的結果不一定準,比如,同一面牆壁由於配準誤差變成了“兩面牆”,並不能完全重疊。

通過重取樣實現點雲平滑

      點雲重取樣是通過“移動最小二乘”(MLS, Moving Least Squares )法來實現的,對應的類名叫做pcl::MovingLeastSquares

移動最小二乘法與傳統的最小二乘法相比,有兩個比較大的改進:

( 1)擬合函式的建立不同。這種方法建立擬合函式不是採用傳統的多項式或其它函式,而是由一個係數向量 a(x)和基函式 p(x)構成, 這裡 a(x)不是常數,而是座標 x 的函式。 
( 2)引入緊支( Compact Support)概念,認為點 x 處的值 y 只受 x 附近子域內節點影響,這個子域稱作點 x 的影響區域, 影響區域外的節點對 x的取值沒有影響。在影響區域上定義一個權函式w(x), 如果權函式在整個區域取為常數, 就得到傳統的最小二乘法。

1、擬合函式的建立:
在擬合區域的一個局域子域 U 上,擬合函式表示:

式中,a( x) = [a1 ( x) ,a2 ( x) …am ( x) ]T為為待求係數,它是座標 x 的函式,p( x) = [p1 ( x) ,p2 ( x) …pm ( x) ]T 稱為基
函式,它是一個階完備的多項式,是基函式的項式[7],常用的二次基為 [1,u,v,u^{2}v^{2},uv]T.所以式( 1) 中的擬合函
數通常表示為 

   

 使上式的函式最小化,w( x - xi ) 是節點 xi 的權函式。

2 、權函式的確定

     權函式是移動最小二乘法的核心。在移動最小二乘法中,權函式 w( x - xi ) 應該具有緊支性,即權函式在 x 的一個子域內不等於 0,在這個子域外全是 0,這個子域稱為權函式的支援域( 即 x 的影響域) ,其半徑記為 s。常用的權函式為立方樣條權函式:

                           

hi 為第 i 節點的權函式支援域的大小,β 為引入的影響係數。

// 對點雲重取樣  
pcl::search::KdTree<PointT>::Ptr treeSampling (new pcl::search::KdTree<PointT>); // 建立用於最近鄰搜尋的KD-Tree
pcl::PointCloud<PointT> mls_points;   //輸出MLS
pcl::MovingLeastSquares<PointT, PointT> mls;  // 定義最小二乘實現的物件mls
mls.setComputeNormals (false);  //設定在最小二乘計算中是否需要儲存計算的法線
mls.setInputCloud (cloud_filtered);        //設定待處理點雲
mls.setPolynomialOrder(2);             // 擬合2階多項式擬合,一般取2-5
mls.setPolynomialFit (false);  // 設定為false可以 加速 smooth
mls.setSearchMethod (treeSampling);    // 設定KD-Tree作為搜尋方法
mls.setSearchRadius (0.05); // 單位m.設定用於擬合的K近鄰半徑,越大平滑力度越大
mls.process (mls_points);        //輸出

估計點雲的表面法線

點雲的法線計算一般有兩種方法:
1、使用曲面重建方法,從點雲資料中得到取樣點對應的曲面,然後再用曲面模型計算其表面的法線
2、直接使用近似值直接從點雲資料集推斷出曲面法線,這裡主要用第2種方法來近似估計點雲中每個點的表面法線。具體來說,就是把估計某個點的表面法線問題簡化為:從該點最近鄰計算的協方差矩陣的特徵向量和特徵值的分析,

從該點的周圍點鄰域(也稱為k鄰域)估計一點處的表面法線 ,所以這個K鄰域的選取也很關鍵。

// 法線估計
pcl::NormalEstimation<PointT, pcl::Normal> normalEstimation;                    //建立法線估計的物件
normalEstimation.setInputCloud(cloud_smoothed);                                    //輸入點雲
pcl::search::KdTree<PointT>::Ptr tree(new pcl::search::KdTree<PointT>);          // 建立用於最近鄰搜尋的KD-Tree
normalEstimation.setSearchMethod(tree);
pcl::PointCloud<pcl::Normal>::Ptr normals (new pcl::PointCloud<pcl::Normal>);     // 定義輸出的點雲法線
// K近鄰確定方法,使用k個最近點,或者確定一個以r為半徑的圓內的點集來確定都可以,兩者選1即可
normalEstimation.setKSearch(10);                    // 使用當前點周圍最近的10個點
//normalEstimation.setRadiusSearch(0.03);            //對於每一個點都用半徑為3cm的近鄰搜尋方式
normalEstimation.compute(*normals);                 //計演算法線

點雲法向量計算步驟: