激光掃描通常會產生密度不均勻的點雲數據集。另外,測量中的誤差會產生稀疏的離群點,使效果更糟。估計局部點雲特征(例如采樣點處法向量或曲率變化率)的運算很復雜,這會導致錯誤的數值,反過來有可能導致點雲的配准等后期處理失敗。
以下方法可以解決其中部分問題:對每個點的鄰域進行一個統計分析,並修剪掉那些不符合一定標准的點。我們的稀疏離群點移除方法基於在輸入數據中對點到臨近點的距離分布的計算。對每個點,我們計算它到它的所有臨近點的平均距離。假設得到的結果是一個高斯分布,其形狀由均值和標准差決定,平均距離在標准范圍(由全局距離平均值和方差定義)之外的點,可被定義為離群點並可從數據集中去除掉。
使用StatisticalOutlierRemoval濾波器移除離群點
統計濾波器StatisticalOutlierRemoval用於去除明顯離群點(離群點往往由測量噪聲引入)。其特征是在空間中分布稀疏,可以理解為:每個點都表達一定信息量,某個區域點越密集則可能信息量越大。噪聲信息屬於無用信息,信息量較小。所以離群點表達的信息可以忽略不計。考慮到離群點的特征,則可以定義某處點雲小於某個密度,既點雲無效。計算每個點到其最近的k個點平均距離。則點雲中所有點的距離應構成高斯分布。給定均值與方差,可剔除離群點。
圖1展示了稀疏離群點分析和移除的效果:左圖為原始數據集,右圖為處理結果。該圖展示了處理前和處理后,一個點鄰域范圍內的k近鄰鄰域平均距離。
圖1 稀疏離群點分析和移除的效果對比圖
代碼
首先,在PCL(Point Cloud Learning)中國協助發行的書[1]提供光盤的第8章例3文件夾中,打開名為statistical_removal.cpp的代碼文件,
同文件夾下可以找到相關的測試點雲文件table_scene_lms400.pcd
下面來解析上面打開源代碼的關鍵語句。下列代碼將從磁盤中讀取點雲數據。
pcl::PCDReaderreader;//定義讀取對象
reader.read<pcl::PointXYZ>("table_scene_lms400.pcd",*cloud);//讀取點雲文件
然后,創建了一個pcl::StatisticalOutlierRemoval濾波器,將對每個點分析的臨近點個數設為50,並將標准差倍數設為1,這意味着如果一個點的距離超出平均距離一個標准差以上,則該點被標記為離群點,並將被移除。計算后的輸出結果儲存在cloud_filtered中。
pcl::StatisticalOutlierRemoval<pcl::PointXYZ> sor;// 創建濾波器對象
sor.setInputCloud(cloud); //設置呆濾波的點雲
sor.setMeanK(50); //設置在進行統計時考慮查詢點鄰近點數
sor.setStddevMulThresh(1.0); //設置判斷是否為離群點的閾值
sor.filter(*cloud_filtered); //執行濾波處理保存內點到cloud_filtered
剩下的數據(內部點)將被存入磁盤,以供其他使用,例如可視化等。
pcl::PCDWriterwriter;
writer.write<pcl::PointXYZ>("table_scene_lms400_inliers.pcd",*cloud_filtered,false);
然后,使用同樣的參數再次調用該濾波器,但是利用函數setNegative設置使輸出取外點,以獲取離群點數據(也就是原本濾除掉的點)。
sor.setNegative(true);
sor.filter(*cloud_filtered);
並將數據寫回到磁盤。
writer.write<pcl::PointXYZ>("table_scene_lms400_outliers.pcd",*cloud_filtered,false);
#include <iostream> #include <pcl/io/pcd_io.h> #include <pcl/point_types.h> #include <pcl/filters/statistical_outlier_removal.h> int main (int argc, char** argv) { pcl::PointCloud<pcl::PointXYZ>::Ptr cloud (new pcl::PointCloud<pcl::PointXYZ>); pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_filtered (new pcl::PointCloud<pcl::PointXYZ>); // 填入點雲數據 pcl::PCDReader reader; // 把路徑改為自己存放文件的路徑 reader.read<pcl::PointXYZ> ("table_scene_lms400.pcd", *cloud); std::cerr << "Cloud before filtering: " << std::endl; std::cerr << *cloud << std::endl; // 創建濾波器對象 pcl::StatisticalOutlierRemoval<pcl::PointXYZ> sor; sor.setInputCloud (cloud); sor.setMeanK (50); sor.setStddevMulThresh (1.0); sor.filter (*cloud_filtered); std::cerr << "Cloud after filtering: " << std::endl; std::cerr << *cloud_filtered << std::endl; pcl::PCDWriter writer; writer.write<pcl::PointXYZ> ("table_scene_lms400_inliers.pcd", *cloud_filtered, false); sor.setNegative (true); sor.filter (*cloud_filtered); writer.write<pcl::PointXYZ> ("table_scene_lms400_outliers.pcd", *cloud_filtered, false); return (0); }
可以直接在VS中執行控制台會輸出點雲濾波的信息,並得到兩個pcb分別存儲內點和外點:
table_scene_lms400_inliers.pcd
table_scene_lms400_outliers.pcd
原點雲可視化結果:
濾波處理后:
參考:http://www.pclcn.org/study/shownews.php?lang=cn&id=68
http://blog.sina.com.cn/s/blog_a2b9923f0102wrwy.html