1. 程式人生 > >估計PointCloud中的曲面法線(Estimating Surface Normals in a PointCloud)

估計PointCloud中的曲面法線(Estimating Surface Normals in a PointCloud)

表面法線是幾何表面的重要屬性,並在許多領域(如計算機圖形應用)中大量使用,以應用生成陰影和其他視覺效果的正確光源。

給定一個幾何曲面,通常很簡單的方法是將曲面上某一點的法線方向作為垂直於該曲面的向量。但是,由於我們獲取的點雲資料集代表了一組實際表面上的點樣本,因此有兩種可能性:

1、從獲取的點雲資料集中獲取底層曲面,使用曲面網格劃分技術,然後從網格中計算曲面法線;
2、使用近似值直接從點雲資料集中推斷出曲面法線。

本教程將介紹後者,即給定點雲資料集,直接計算雲中每個點的表面法線。

#理論引入
雖然存在許多不同的正常估算方法,但我們將專注於本教程的方法是最簡單的方法之一,具體如下。確定表面上的點的法線的問題近似於估計與表面相切的平面的法線的問題,這又成為最小平方平面擬合估計問題。

    注意:
    欲瞭解更多資訊,包括最小二乘問題的數學方程,請參閱[RusuDissertation](http://files.rbrusu.com/publications/RusuPhDThesis.pdf)。

因此,用於估計表面法線的解決方案被簡化為分析從查詢點的最近鄰居建立的協方差矩陣的特徵向量和特徵值(或PCA-主分量分析)。更具體地說,對於每個點\ boldsymbol {P} _i,協方差矩陣\ mathcal {C}如下:

\大數符號{} {} {} {} {} {\大寫符號\ \ frac {1} {k} \ sum_ {i = 1} ^ cdot大膽的符號{p} _i- \ overline {\ boldsymbol {p}})^ {T}},〜\ mathcal {C} \ cdot \ vec {{\ mathsf v} _j} = \ lambda_j \ cdot \ vec {{\ mathsf v} _j},〜j \ in \ {0,1,2 }

ķ為考慮點鄰居的數目在 \ boldsymbol {P} _i附近,\劃線{\ boldsymbol {P}} 表示最近鄰居的3D質心,\ lambda_j是協方差矩陣的第Ĵ特徵值,並且\ vec {{\ mathsf v} _j} 表示第Ĵ個特徵向量。

要從PCL中的一組點中估計協方差矩陣,可以使用:


// Placeholder for the 3x3 covariance matrix at each surface patch

  Eigen::Matrix3f covariance_matrix;

  // 16-bytes aligned placeholder for the XYZ centroid of a surface patch

  Eigen::Vector4f xyz_centroid;



  // Estimate the XYZ centroid

  compute3DCentroid (cloud, xyz_centroid);



  // Compute the 3x3 covariance matrix

  computeCovarianceMatrix (cloud, xyz_centroid, covariance_matrix);

一般來說,由於沒有數學方法來解決法線的符號,所以通過如上所示的主分量分析(PCA)計算的方位是不明確的,並且不一致地針對整個點雲資料集。下圖顯示了代表廚房環境一部分的較大資料集的兩個部分的這些影響。 該圖的右邊部分顯示了擴充套件高斯影象(EGI),也被稱為正常球體,它描述了所有法線從點雲的方向。 由於資料集是2.5D,因此從一個單一的觀點獲得,法線應該只出現在EGI球體的一半。 但是,由於方向不一致,它們遍佈整個球體。

_images/unflipped_scene1.jpg_images/unflipped_scene2.jpg_images/unflipped_sphere.jpg

如果事實上已經知道{\ mathsf v} _p這個viewpoint,那麼解決這個問題是微不足道的。為了使所有法線\ VEC {\ boldsymbol {N}} _我 始終朝向這個viewpoint,他們需要滿足以下等式:

\ vec {\ boldsymbol {n}} _ i \ cdot({\ mathsf v} _p - \ boldsymbol {p} _i)> 0

下圖顯示了上述資料集中的所有法線一直朝向這個viewpoint的結果。

_images / flipped_scene1.jpg_images / flipped_scene2.jpg_images / flipped_sphere.jpg

要在PCL中手動重新定位給定點,可以使用:

flipNormalTowardsViewpoint (const PointT &point, float vp_x, float vp_y, float vp_z, Eigen::Vector4f &normal);
   注意:
   如果資料集具有多個採集視點,則上述正常的重新定位方法不成立,需要實施更復雜的演算法。請參閱[Rusu論文](http://files.rbrusu.com/publications/RusuPhDThesis.pdf)瞭解更多資訊。

#選擇正確的比例
如前所述,需要根據該點(也稱為k鄰域)的周圍點附近支援來估計一個點處的表面法線 。

最近鄰估計問題的具體細節提出了正確比例因子的問題:給定一個取樣點雲資料集,什麼是正確的k(通過pcl::Feature::setKSearch給出)或r(通過pcl::Feature::setRadiusSearch給出)值應該用於確定一個點的最近鄰居的集合?

這個問題是非常重要的並且構成點特徵表示的自動估計(即,沒有使用者給定的閾值)的限制因素。為了更好地說明這個問題,下圖給出了選擇較小尺度(即較小的r或k)與較大尺度(即較大的r或k))。圖的左邊部分描繪了合理的精選比例因子,估計的表面法線近似垂直於兩個平面表面,並且在整個桌子上可見小邊緣。然而,如果比例因子太大(右部分),並且因此鄰居集合相鄰的表面的覆蓋點較大,則估計的點特徵表示被扭曲,在兩個平坦表面的邊緣處旋轉表面法線,並且模糊邊緣和壓制細節。

_images / normals_different_radii.jpg_images / curvature_different_radii.jpg

沒有太多的細節,只要假定現在確定一個點的鄰域的尺度必須根據應用所要求的細節水平來選擇。簡而言之,如果杯子的手柄和圓柱形部分之間的邊緣處的曲率很重要,則比例因子需要足夠小以捕獲這些細節,否則就需要較大的比例。

#估計法線
雖然在特徵中已經給出了一個正常估計的例子 ,但我們會在這裡修改其中的一個,目的是為了更好地解釋在幕後發生的事情。

以下程式碼片段將估計輸入資料集中所有點的一組曲面法線。

#include <pcl/point_types.h>
#include <pcl/features/normal_3d.h>

{
  pcl::PointCloud<pcl::PointXYZ>::Ptr cloud (new pcl::PointCloud<pcl::PointXYZ>);

  ... read, pass in or create a point cloud ...

  // Create the normal estimation class, and pass the input dataset to it
  pcl::NormalEstimation<pcl::PointXYZ, pcl::Normal> ne;
  ne.setInputCloud (cloud);

  // Create an empty kdtree representation, and pass it to the normal estimation object.
  // Its content will be filled inside the object, based on the given input dataset (as no other search surface is given).
  pcl::search::KdTree<pcl::PointXYZ>::Ptr tree (new pcl::search::KdTree<pcl::PointXYZ> ());
  ne.setSearchMethod (tree);

  // Output datasets
  pcl::PointCloud<pcl::Normal>::Ptr cloud_normals (new pcl::PointCloud<pcl::Normal>);

  // Use all neighbors in a sphere of radius 3cm
  ne.setRadiusSearch (0.03);

  // Compute the features
  ne.compute (*cloud_normals);

  // cloud_normals->points.size () should have the same size as the input cloud->points.size ()*
}

NormalEstimation類的實際計算呼叫在內部不做任何事情,但是:

for each point p in cloud P

  1. get the nearest neighbors of p

  2. compute the surface normal n of p

  3. check if n is consistently oriented towards the viewpoint and flip otherwise

Viewpoint 預設為(0,0,0),可以通過以下方式進行更改:

setViewPoint (float vpx, float vpy, float vpz);

要計算單點法線,使用:

computePointNormal (const pcl::PointCloud<PointInT> &cloud, const std::vector<int> &indices, Eigen::Vector4f &plane_parameters, float &curvature);

其中cloud是包含點的輸入點雲,indices 表示來自cloud的k個最近鄰居的集合,並且plane_parameters和curvature(曲率)表示正常估計的輸出,其中plane_parameters保持正常(nx,ny,nz)在前3個座標,第四個座標是D = nc。p_plane(質心在這裡)+ p。輸出表面曲率被估計為協方差矩陣(如上所述)的特徵值之間的關係,如下所示:

\ sigma = \ frac {\ lambda_0} {\ lambda_0 + \ lambda_1 + \ lambda_2}

#使用OpenMP加速正常估計
對於精通速度的使用者來說,PCL提供了額外的曲面法線估計實現,使用OpenMP的多核/多執行緒範例來加速計算。該類的名稱是pcl::NormalEstimationOMP,它的API與單執行緒pcl::NormalEstimation完全相容,這使得它適合作為一個插入式替換。在一個有8個核心的系統上,你應該得到6-8倍的計算時間。

   注意:
   如果您的資料集是有組織的(例如,使用TOF相機,立體相機等獲取 - 即它具有寬度和高度),為了更快的結果,請參閱[使用積分影象的法向量估計](http://pointclouds.org/documentation/tutorials/normal_estimation_using_integral_images.php#normal-estimation-using-integral-images)。

Estimating Surface Normals in a PointCloud