基于octree的空间划分及搜索操作
K近鄰搜索(K Nearest Neighbor Search)
所謂K近鄰算法,即是給定一個訓練數據集,對新的輸入實例,在訓練數據集中找到與該實例最鄰近的K個實例(也就是上面所說的K個鄰居), 這K個實例的多數屬于某個類,就把該輸入實例分類到這個類中。
如果,有兩類不同的樣本數據,分別用藍色的小正方形和紅色的小三角形表示,而圖正中間的那個綠色的圓所標示的數據則是待分類的數據。也就是說,現在, 我們不知道中間那個綠色的數據是從屬于哪一類(藍色小正方形or紅色小三角形),下面,我們就要解決這個問題:給這個綠色的圓分類。我們常說,物以類聚,人以群分,判別一個人是一個什么樣品質特征的人,常常可以從他/她身邊的朋友入手,所謂觀其友,而識其人。我們不是要判別上圖中那個綠色的圓是屬于哪一類數據么,好說,從它的鄰居下手。但一次性看多少個鄰居呢?從上圖中,你還能看到:
- 如果K=3,綠色圓點的最近的3個鄰居是2個紅色小三角形和1個藍色小正方形,少數從屬于多數,基于統計的方法,判定綠色的這個待分類點屬于紅色的三角形一類。
- 如果K=5,綠色圓點的最近的5個鄰居是2個紅色三角形和3個藍色的正方形,還是少數從屬于多數,基于統計的方法,判定綠色的這個待分類點屬于藍色的正方形一類。
- K 值的選擇會對算法的結果產生重大影響。K值較小意味著只有與輸入實例較近的訓練實例才會對預測結果起作用,但容易發生過擬合;如果 K 值較大,優點是可以減少學習的估計誤差,但缺點是學習的近似誤差增大,這時與輸入實例較遠的訓練實例也會對預測起作用,使預測發生錯誤。在實際應用中,K 值一般選擇一個較小的數值,通常采用交叉驗證的方法來選擇最優的 K 值。隨著訓練實例數目趨向于無窮和 K=1 時,誤差率不會超過貝葉斯誤差率的2倍,如果K也趨向于無窮,則誤差率趨向于貝葉斯誤差率。
- 該算法中的分類決策規則往往是多數表決,即由輸入實例的 K 個最臨近的訓練實例中的多數類決定輸入實例的類別
- 距離度量一般采用 Lp 距離,當p=2時,即為歐氏距離,在度量之前,應該將每個屬性的值規范化,這樣有助于防止具有較大初始值域的屬性比具有較小初始值域的屬性的權重過大。
半徑內近鄰搜索"(Neighbors within Radius Search)
代碼解析,新建工程文件ch3_3,及文件octree_search.cpp
octree_search.cpp? 內容
#include <pcl/point_cloud.h> //點云頭文件 #include <pcl/octree/octree.h> //八叉樹頭文件 #include <iostream> #include <vector> #include <ctime>int main (int argc, char** argv) {srand ((unsigned int) time (NULL)); //用系統時間初始化隨機種子與 srand (time (NULL))的區別 pcl::PointCloud<pcl::PointXYZ>::Ptr cloud (new pcl::PointCloud<pcl::PointXYZ>);// 創建點云數據cloud->width = 1000;cloud->height = 1; //無序cloud->points.resize (cloud->width * cloud->height);for (size_t i = 0; i < cloud->points.size (); ++i) //隨機循環產生點云的坐標值 {cloud->points[i].x = 1024.0f * rand () / (RAND_MAX + 1.0f);cloud->points[i].y = 1024.0f * rand () / (RAND_MAX + 1.0f);cloud->points[i].z = 1024.0f * rand () / (RAND_MAX + 1.0f);}/****************************************************************************創建一個octree實例,用設置分辨率進行初始化,該octree用它的頁節點存放點索引向量,分辨率參數描述最低一級octree的最小體素的尺寸,因此octree的深度是分辨率和點云空間維度的函數,如果知道點云的邊界框,應該用defineBoundingbox方法把它分配給octree然后通過點云指針把所有點增加到ctree中。*****************************************************************************/float resolution = 128.0f;pcl::octree::OctreePointCloudSearch<pcl::PointXYZ> octree (resolution); //初始化Octree octree.setInputCloud (cloud); //設置輸入點云 這兩句是最關鍵的建立PointCloud和octree之間的聯系octree.addPointsFromInputCloud (); //構建octree pcl::PointXYZ searchPoint; //設置searchPoint searchPoint.x = 1024.0f * rand () / (RAND_MAX + 1.0f);searchPoint.y = 1024.0f * rand () / (RAND_MAX + 1.0f);searchPoint.z = 1024.0f * rand () / (RAND_MAX + 1.0f);/*************************************************************************************一旦PointCloud和octree聯系一起,就能進行搜索操作,這里使用的是“體素近鄰搜索”,把查詢點所在體素中其他點的索引作為查詢結果返回,結果以點索引向量的形式保存,因此搜索點和搜索結果之間的距離取決于octree的分辨率參數 *****************************************************************************************/std::vector<int> pointIdxVec; //存儲體素近鄰搜索結果向量if (octree.voxelSearch (searchPoint, pointIdxVec)) //執行搜索,返回結果到pointIdxVec {std::cout << "Neighbors within voxel search at (" << searchPoint.x << " " << searchPoint.y << " " << searchPoint.z << ")" << std::endl;for (size_t i = 0; i < pointIdxVec.size (); ++i) //打印結果點坐標std::cout << " " << cloud->points[pointIdxVec[i]].x << " " << cloud->points[pointIdxVec[i]].y << " " << cloud->points[pointIdxVec[i]].z << std::endl;}/**********************************************************************************K 被設置為10 ,K近鄰搜索 方法把搜索結果寫到兩個分開的向量,第一個pointIdxNKNSearch包含搜索結果(結果點的索引的向量) 第二個向量pointNKNSquaredDistance存儲搜索點與近鄰之間的距離的平方。 *************************************************************************************///K 近鄰搜索int K = 10;std::vector<int> pointIdxNKNSearch; //結果點的索引的向量std::vector<float> pointNKNSquaredDistance; //搜索點與近鄰之間的距離的平方 std::cout << "K nearest neighbor search at (" << searchPoint.x << " " << searchPoint.y << " " << searchPoint.z<< ") with K=" << K << std::endl;if (octree.nearestKSearch (searchPoint, K, pointIdxNKNSearch, pointNKNSquaredDistance) > 0){for (size_t i = 0; i < pointIdxNKNSearch.size (); ++i)std::cout << " " << cloud->points[ pointIdxNKNSearch[i] ].x << " " << cloud->points[ pointIdxNKNSearch[i] ].y << " " << cloud->points[ pointIdxNKNSearch[i] ].z << " (squared distance: " << pointNKNSquaredDistance[i] << ")" << std::endl;}// 半徑內近鄰搜索 std::vector<int> pointIdxRadiusSearch;std::vector<float> pointRadiusSquaredDistance;float radius = 256.0f * rand () / (RAND_MAX + 1.0f);std::cout << "Neighbors within radius search at (" << searchPoint.x << " " << searchPoint.y << " " << searchPoint.z<< ") with radius=" << radius << std::endl;if (octree.radiusSearch (searchPoint, radius, pointIdxRadiusSearch, pointRadiusSquaredDistance) > 0){for (size_t i = 0; i < pointIdxRadiusSearch.size (); ++i)std::cout << " " << cloud->points[ pointIdxRadiusSearch[i] ].x << " " << cloud->points[ pointIdxRadiusSearch[i] ].y << " " << cloud->points[ pointIdxRadiusSearch[i] ].z << " (squared distance: " << pointRadiusSquaredDistance[i] << ")" << std::endl;}}
建立八叉樹,根據不同的搜索形式搜索體素周圍的點云。
結果如下:
Octree類關鍵點的說明
?? PCL octree組件提供了幾個octree類型,它們各自的葉節點特征基本上是不同的,
OctreePointCloudVector(等于OctreePointCloud):該octree能夠保存每一個節點上的點索引列。
OctreePointCloudSinglePoint: 該octree類僅僅保存每一個節點上的單個點的索引,僅僅保存最后分配給葉節點的點索引
OctreePointCloudOccupancy: 該octree類不存儲它的葉節點上的任何信息,它能用于空間填充情況檢查
OctreePointCloudDensity:存儲每一個葉節點體素中點的數目,它可以進行空間點集密集程度的查詢
?
(2)? 無序點云數據集的空間變化檢測
octree是一種管理稀疏3D數據的樹狀結構,利用octree實現多個無序點云之間的空間變化檢測,這些點云可能在尺寸。分辨率 密度,和點順序等方面有所差異,通過遞歸的比較octree的樹結構,可以鑒定出由octree產生的體素組成的區別所代表的空間變化,還要學習關于octree的“雙緩沖”技術,以便實時的探測多個點云之間的空間組成的差異。
代碼分析
octree_change_detection.cpp? :
#include <pcl/point_cloud.h> #include <pcl/octree/octree.h>#include <iostream> #include <vector> #include <ctime>int main (int argc, char** argv) {srand ((unsigned int) time (NULL)); //用系統時間初始化隨機種子// 八叉樹的分辨率,即體素的大小float resolution = 32.0f;// 初始化空間變化檢測對象pcl::octree::OctreePointCloudChangeDetector<pcl::PointXYZ> octree (resolution);pcl::PointCloud<pcl::PointXYZ>::Ptr cloudA (new pcl::PointCloud<pcl::PointXYZ> ); //創建點云實例cloudA生成的點云數據用于建立八叉樹octree對象// 為cloudA點云填充點數據cloudA->width = 128; //設置點云cloudA的點數cloudA->height = 1; //無序點cloudA->points.resize (cloudA->width * cloudA->height); //總數for (size_t i = 0; i < cloudA->points.size (); ++i) //循環填充 {cloudA->points[i].x = 64.0f * rand () / (RAND_MAX + 1.0f);cloudA->points[i].y = 64.0f * rand () / (RAND_MAX + 1.0f);cloudA->points[i].z = 64.0f * rand () / (RAND_MAX + 1.0f);}// 添加點云到八叉樹中,構建八叉樹octree.setInputCloud (cloudA); //設置輸入點云octree.addPointsFromInputCloud (); //從輸入點云構建八叉樹/***********************************************************************************點云cloudA是參考點云用其建立的八叉樹對象描述它的空間分布,octreePointCloudChangeDetector類繼承自Octree2BufBae類,Octree2BufBae類允許同時在內存中保存和管理兩個octree,另外它應用了內存池該機制能夠重新利用已經分配了的節點對象,因此減少了在生成點云八叉樹對象時昂貴的內存分配和釋放操作通過訪問 octree.switchBuffers ()重置八叉樹 octree對象的緩沖區,但把之前的octree數據仍然保留在內存中************************************************************************************/// 交換八叉樹的緩沖,但是CloudA對應的八叉樹結構仍然在內存中 octree.switchBuffers ();//cloudB點云用于建立新的八叉樹結構,與前一個cloudA對應的八叉樹共享octree對象,同時在內存中駐留pcl::PointCloud<pcl::PointXYZ>::Ptr cloudB (new pcl::PointCloud<pcl::PointXYZ> ); //實例化點云對象cloudB// 為cloudB創建點云 cloudB->width = 128;cloudB->height = 1;cloudB->points.resize (cloudB->width * cloudB->height);for (size_t i = 0; i < cloudB->points.size (); ++i){cloudB->points[i].x = 64.0f * rand () / (RAND_MAX + 1.0f);cloudB->points[i].y = 64.0f * rand () / (RAND_MAX + 1.0f);cloudB->points[i].z = 64.0f * rand () / (RAND_MAX + 1.0f);}// 添加cloudB到八叉樹中 octree.setInputCloud (cloudB);octree.addPointsFromInputCloud ();/**************************************************************************************************************為了檢索獲取存在于couodB的點集R,此R并沒有cloudA中的元素,可以調用getPointIndicesFromNewVoxels方法,通過探測兩個八叉樹之間體素的不同,它返回cloudB 中新加點的索引的向量,通過索引向量可以獲取R點集 很明顯這樣就探測了cloudB相對于cloudA變化的點集,但是只能探測到在cloudA上增加的點集,二不能探測減少的 ****************************************************************************************************************/std::vector<int> newPointIdxVector; //存儲新添加的索引的向量// 獲取前一cloudA對應八叉樹在cloudB對應在八叉樹中沒有的點集 octree.getPointIndicesFromNewVoxels (newPointIdxVector);// 打印點集std::cout << "Output from getPointIndicesFromNewVoxels:" << std::endl;for (size_t i = 0; i < newPointIdxVector.size (); ++i)std::cout << i << "# Index:" << newPointIdxVector[i]<< " Point:" << cloudB->points[newPointIdxVector[i]].x << " "<< cloudB->points[newPointIdxVector[i]].y << " "<< cloudB->points[newPointIdxVector[i]].z << std::endl;}
本實驗是為了檢索獲取存在于couodB的點集R,此R并沒有cloudA中的元素,可以調用getPointIndicesFromNewVoxels方法,通過探測兩個八叉樹之間體素的不同,它返回cloudB 中新加點的索引的向量,通過索引向量可以獲取R點集 很明顯這樣就探測了cloudB相對于cloudA變化的點集,但是只能探測到在cloudA上增加的點集,二不能探測減少的
編譯運行的結果如下(因為生成的隨機點云,所以運行的結果每次都會不一樣的):
微信公眾號號可掃描二維碼一起共同學習交流
未完待續*******************************************8888
?
總結
以上是生活随笔為你收集整理的基于octree的空间划分及搜索操作的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: PCL 可视化
- 下一篇: PCLVisualizer可视化类