轉載:http://m.blog.csdn.net/article/details?id=51094808
我這裏不介紹pcl裏面的類的命名規範,及點的類型。爲什麼呢?pcl裏面類的命名規範,比較繁瑣,而且主要是針對要對pcl這個庫要開源的人士,而pcl裏面的點的類型太多,足夠滿足你的要求。所以我們要走一條具有中國特色的pcl主義道路。
今天來介紹一下pcl裏面的3D特徵。
點是用來給一個給定區域來用笛卡爾座標系x,y,z來進行描繪的一種方式。假定座標系統的座標原點沒有變,可能會有兩個點比如p1和p2,成爲t1和t2,但是它們的座標是沒變的。
比較這些點雲是不穩定的,因爲即使他們有相同的距離,但是它們可能採自不同的表面,因此他們可能會代表不同的信息,當把它們和表面上臨近的點結合在一起的時候。這是因爲很難確保現實世界裏面的t1和t2沒有變化。有一些獲取點雲的裝置可能會獲取一些額外的數據,比如亮度(intensity),比如表面光滑度(翻譯不一定對,surface remission value),和顏色等等,但是以上的這些往往還是不能解決問題。
程序往往需要更好的特徵或者是矩來區別幾何表面。所以把3D點看成笛卡爾座標系裏面的一個單一的實體這個概念消失了,我們引入了一個局部座標系。文化是豐富多彩,森羅萬象的,我們可以對同樣的概念取很多的命名組合,比如形狀描述,幾何特徵,而在pcl裏面是用點的特徵來描述。
通過包含周邊的點,採樣表面的幾何特徵可以被我們給推理或者捕獲,這將解決我們蛋疼的比較問題。理想情況下,同一個或者相似表面上的點的特徵是很相似的。那麼怎麼來找到一個比較好的,描述點的特徵呢?當這個特徵在下面這3個因素的影響下,還能或得同樣的表面特點的時候就是好的特徵。
1.剛體轉換。這包括了3D旋轉與3D轉換,好的特徵應該不會被此影響,特徵向量F(feature)不會改變。
2.不同的採樣密度。總體來說,一個局部的表面區域,不管以高密度還是低密度的採樣,應該具有相同的特徵向量。
3.噪聲。這個不需要過多的解釋。
總體來說,pcl裏面使用相似的方法來查詢指定點的最近鄰,如使用kd-trees。下面是我們感興趣的2個查詢時需要的類型;
1.k。即查詢時的最近鄰的點的個數。
2.r。即搜索半徑。
專業術語:
term |
explanation |
---|
Foo |
a class named Foo
//英文裏面很多隨便取的類叫foo或bar類似張三,李四 |
FooPtr |
a boost shared pointer to a class Foo,//boost共享指針
e.g., boost::shared_ptr<Foo>
|
FooConstPtr |
a const boost shared pointer to a classFoo,//boost共享常量指針
e.g., const
boost::shared_ptr<const Foo>
|
怎麼進行輸入?
pcl裏面基本上所有的類都繼承自pcl::PCLBase這個類,所有pcl::Feature有兩個方式來獲取輸入數據。
1.輸入爲整個點雲數據集,setInputCloud(PointCloudConstPtr&) 強制的
這將計算每一個輸入點的特徵。
2.輸入爲一個點雲數據集的子集。setInputCloud(PointCloudPtr&)和setIndices(IndicesConstPtr&) 後面那個是可選的。
這將計算給定下標的輸入點的特徵。默認情況下下標是不會給的,也就意味着每個點的特徵都會被計算。
除此之外,我們還可以通過別的調用,如setSearchSurface(PointCloudConstPtr &)來搜索表面上的點。
1.setIndices()=false,setSearchSurface()=false
這是pcl裏面用的最多的,不用任何的特徵來排除點雲,即包含所有的點。
因爲我們不希望根據是否有下標或者是否有表面來確定不同的點雲的拷貝,pcl內部創造了一系列的向量,來指向整個數據集。
最左邊的那幅圖,它先找到p1的最近鄰,再找p2的最近鄰,直到 把P這個點雲裏面的點窮盡。
2.setIndices()=true,setSearchSurface()=false
這隻會計算在給定的下標向量裏面的下標所對應點的特徵,比如p1是屬於下標向量裏面的,而p2不屬於,則只會計算p1附近的點的特徵。
3.setIndices()=false,setSearchSurface()=true
只會計算潛在表面的最近鄰的點的特徵,而不是整個點雲。比如P和Q是兩個點雲,然後把P作爲要被搜索的表面(潛在的表面),則Q的每個元素(q1,q2)會在P中找到最近的幾個點。
4.setIndices()=true,setSearchSurface=true.這可能是最稀少的情況了,因爲這既要滿足下標,又要滿足表面搜索。最後一幅圖裏面,先通過下標把q2給排除,然後再搜索q1在P裏面的最近鄰。
我們使用setSearchSurface()最多的例子,是當我們有一個高密度的輸入數據集的時候,但是我們不想去計算裏面所有點的特徵,在這個例子裏面我們通過setSearchSurface()來解決這個問題,而不是通過downsample(降低採樣,往往通過pcl::VoxelGrid<T> filter)或者keypoints(關鍵採樣)。我們把downsampled/keypoints作爲setInputCloud()的輸入,即是setSearchSurface()的原始數據集。
法線估計的一個例子
一旦決定了某個要查詢點的周圍的局部特徵代表,就可以捕獲查詢點的潛在表面的幾何特徵。在描述一個表面的幾何特徵的第一步是得到它在某個座標下的方向,因此要估計它的法線。下面的代碼段將預測輸入數據集的一系列表面法線。
#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 ()
}
下面的代碼段將通過一個輸入數據集預測一系列表面法線,用另一個輸入數據集評估最近鄰。像前面提到的,這是一個降低表面採樣得到的數據集,作爲輸入。
#include <pcl/point_types.h>
#include <pcl/features/normal_3d.h>
{
pcl::PointCloud<pcl::PointXYZ>::Ptr cloud (new pcl::PointCloud<pcl::PointXYZ>);
pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_downsampled (new pcl::PointCloud<pcl::PointXYZ>);
... read, pass in or create a point cloud ...
... create a downsampled version of it ...
// Create the normal estimation class, and pass the input dataset to it
pcl::NormalEstimation<pcl::PointXYZ, pcl::Normal> ne;
ne.setInputCloud (cloud_downsampled);
// Pass the original data (before downsampling) as the search surface
ne.setSearchSurface (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 surface dataset.
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_downsampled->points.size ()
}
還有通過下標來降低採樣的,下面這個例子
#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 a set of indices to be used. For simplicity, we're going to be using the first 10% of the points in cloud
std::vector<int> indices (floor (cloud->points.size () / 10));
for (size_t i = 0; indices.size (); ++i) indices[i] = i;
// Create the normal estimation class, and pass the input dataset to it
pcl::NormalEstimation<pcl::PointXYZ, pcl::Normal> ne;
ne.setInputCloud (cloud);
// Pass the indices
boost::shared_ptr<std::vector<int> > indicesptr (new std::vector<int> (indices));
ne.setIndices (indicesptr);
// 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 indicesptr->size ()
}