(2)關於上一篇博文中提到的歐幾里德分割法稱之為標准的距離分離,當然接下來介紹其他的與之相關的延伸出來的聚類的方法,我稱之為條件歐幾里德聚類法,(是我的個人理解),這個條件的設置是可以由我們自定義的,因為除了距離檢查,聚類的點還需要滿足一個特殊的自定義的要求,就是以第一個點為標准作為種子點,候選其周邊的點作為它的對比或者比較的對象,如果滿足條件就加入到聚類的對象中,至於到底怎么翻譯我也蒙了,只能這樣理解了
主要的缺點:該算法沒有初始化種子系統,沒有過度分割或者分割不足的控制,還有就是從主循環運算中調用條件函數時,效率比較低,
看一下代碼吧,至於到底怎么理解各個有個人的理解吧
#include <pcl/io/pcd_io.h> #include <pcl/segmentation/conditional_euclidean_clustering.h> #include <iostream> //如果此函數返回true,則將添加候選點到種子點的簇類中。 bool customCondition(const pcl::PointXYZ& seedPoint, const pcl::PointXYZ& candidatePoint, float squaredDistance) { // 在這里你可以添加你自定義的條件 if (candidatePoint.y < seedPoint.y) return false; return true; } int main(int argc, char** argv) { pcl::PointCloud<pcl::PointXYZ>::Ptr cloud(new pcl::PointCloud<pcl::PointXYZ>); if (pcl::io::loadPCDFile<pcl::PointXYZ>(argv[1], *cloud) != 0) { return -1; } // 申明一個條件聚類的對象 pcl::ConditionalEuclideanClustering<pcl::PointXYZ> clustering; clustering.setClusterTolerance(0.02); clustering.setMinClusterSize(100); clustering.setMaxClusterSize(25000); clustering.setInputCloud(cloud); // 設置要檢查每對點的函數。 clustering.setConditionFunction(&customCondition); std::vector<pcl::PointIndices> clusters; clustering.segment(clusters); // 對於每一個聚類結果 int currentClusterNum = 1; for (std::vector<pcl::PointIndices>::const_iterator i = clusters.begin(); i != clusters.end(); ++i) { // ...add all its points to a new cloud... pcl::PointCloud<pcl::PointXYZ>::Ptr cluster(new pcl::PointCloud<pcl::PointXYZ>); for (std::vector<int>::const_iterator point = i->indices.begin(); point != i->indices.end(); point++) cluster->points.push_back(cloud->points[*point]); cluster->width = cluster->points.size(); cluster->height = 1; cluster->is_dense = true; // ...and save it to disk. if (cluster->points.size() <= 0) break; std::cout << "Cluster " << currentClusterNum << " has " << cluster->points.size() << " points." << std::endl; std::string fileName = "cluster" + boost::to_string(currentClusterNum) + ".pcd"; pcl::io::savePCDFileASCII(fileName, *cluster); currentClusterNum++; } }
上面執行的條件是檢查候選點的Y坐標是否小於種子的Y坐標,沒有什么實際意義。所以我就再查看結果了。
那么同時我暫時也用不到,如果有想法的時候再回來研究吧
(2)最小分割算法
該算法是將一幅點雲圖像分割為兩部分:前景點雲(目標物體)和背景物體(剩余部分)
關於該算法的論文的地址:http://gfx.cs.princeton.edu/pubs/Golovinskiy_2009_MBS/paper_small.pdf
The Min-Cut (minimum cut) algorithm最小割算法是圖論中的一個概念,其作用是以某種方式,將兩個點分開,當然這兩個點中間可能是通過無數的點再相連的。如圖
如果要分開最左邊的點和最右邊的點,紅綠兩種割法都是可行的,但是紅線跨過了三條線,綠線只跨過了兩條。單從跨線數量上來論可以得出綠線這種切割方法更優 的結論。但假設線上有不同的權值,那么最優切割則和權值有關了。當你給出了點之間的 “圖” ,以及連線的權值時,最小割算法就能按照要求把圖分開。
所以那么怎么來理解點雲的圖呢?
顯而易見,切割有兩個非常重要的因素,第一個是獲得點與點之間的拓撲關系,這種拓撲關系就是生成一張 “圖”。第二個是給圖中的連線賦予合適的權值。只要這兩個要素合適,最小割算法就會正確的分割出想要的結果。點雲是分開的點。只要把點雲中所有的點連起來就可以了。連接算法如下:
- 找到每個點臨近的n個點
- 將這n個點和父點連接
- 找到距離最小的兩個塊(A塊中某點與B塊中某點距離最小),並連接
- 重復3,直至只剩一個塊
經過上面的步驟現在已經有了點雲的“圖”,只要給圖附上合適的權值,就滿足了最小分割的前提條件。物體分割比如圖像分割給人一個直觀印象就是屬於該物體的點,應該相互之間不會太遠。也就是說,可以用點與點之間的歐式距離來構造權值。所有線的權值可映射為線長的函數。
我們知道這種分割是需要指定對象的,也就是我們指定聚類的中心點(center)以及聚類的半徑(radius),當然我們指定了中心點和聚類的半徑,那么就要被保護起來,保護的方法就是增加它的權值
接下來我們就來看看代碼
#include <pcl/io/pcd_io.h> #include <pcl/search/kdtree.h> #include <pcl/features/normal_3d.h> #include <pcl/segmentation/region_growing.h> #include <pcl/segmentation/min_cut_segmentation.h> #include <iostream> #include <pcl/segmentation/region_growing_rgb.h> int main(int argc, char** argv) { //申明點雲的類型 pcl::PointCloud<pcl::PointXYZ>::Ptr cloud(new pcl::PointCloud<pcl::PointXYZ>); // 法線 pcl::PointCloud<pcl::Normal>::Ptr normals(new pcl::PointCloud<pcl::Normal>); if (pcl::io::loadPCDFile<pcl::PointXYZ>(argv[1], *cloud) != 0) { return -1; } // 申明一個Min-cut的聚類對象 pcl::MinCutSegmentation<pcl::PointXYZ> clustering; clustering.setInputCloud(cloud); //設置輸入 //創建一個點雲,列出所知道的所有屬於對象的點 // (前景點)在這里設置聚類對象的中心點(想想是不是可以可以使用鼠標直接選擇聚類中心點的方法呢?) pcl::PointCloud<pcl::PointXYZ>::Ptr foregroundPoints(new pcl::PointCloud<pcl::PointXYZ>()); pcl::PointXYZ point; point.x = 100.0; point.y = 100.0; point.z = 100.0; foregroundPoints->points.push_back(point); clustering.setForegroundPoints(foregroundPoints); //設置聚類對象的前景點 //設置sigma,它影響計算平滑度的成本。它的設置取決於點雲之間的間隔(分辨率) clustering.setSigma(0.02); // 設置聚類對象的半徑. clustering.setRadius(0.01); //設置需要搜索的臨近點的個數,增加這個也就是要增加邊界處圖的個數 clustering.setNumberOfNeighbours(20); //設置前景點的權重(也就是排除在聚類對象中的點,它是點雲之間線的權重,) clustering.setSourceWeight(0.6); std::vector <pcl::PointIndices> clusters; clustering.extract(clusters); std::cout << "Maximum flow is " << clustering.getMaxFlow() << "." << std::endl; int currentClusterNum = 1; for (std::vector<pcl::PointIndices>::const_iterator i = clusters.begin(); i != clusters.end(); ++i) { //設置聚類后點雲的屬性 pcl::PointCloud<pcl::PointXYZ>::Ptr cluster(new pcl::PointCloud<pcl::PointXYZ>); for (std::vector<int>::const_iterator point = i->indices.begin(); point != i->indices.end(); point++) cluster->points.push_back(cloud->points[*point]); cluster->width = cluster->points.size(); cluster->height = 1; cluster->is_dense = true; //保存聚類的結果 if (cluster->points.size() <= 0) break; std::cout << "Cluster " << currentClusterNum << " has " << cluster->points.size() << " points." << std::endl; std::string fileName = "cluster" + boost::to_string(currentClusterNum) + ".pcd"; pcl::io::savePCDFileASCII(fileName, *cluster); currentClusterNum++; } }
看一下實際運行的最小分割法的結果
原始的點雲
最小分割法的結果
對於實際應用中我們應該設置正確的參數這是最為關鍵的!