1.超體聚類——一種來自圖像的分割方法
超體(supervoxel)是一種集合,集合的元素是“體”。與體素濾波器中的體類似,其本質是一個個的小方塊。與之前提到的所有分割手段不同,超體聚類的目的並不是分割出某種特定物體,其對點雲實施過分割(over segmentation),將場景點雲化成很多小塊,並研究每個小塊之間的關系。這種將更小單元合並的分割思路已經出現了有些年份了,在圖像分割中,像素聚類形成超像素,以超像素關系來理解圖像已經廣為研究。本質上這種方法是對局部的一種總結,紋理,材質,顏色類似的部分會被自動的分割成一塊,有利於后續識別工作。比如對人的識別,如果能將頭發,面部,四肢,軀干分開,則能更好的對各種姿態,性別的人進行識別。
點雲和圖像不一樣,其不存在像素鄰接關系。所以,超體聚類之前,必須以八叉樹對點雲進行划分,獲得不同點團之間的鄰接關系。與圖像相似點雲的鄰接關系也有很多,如面鄰接,線鄰接,點鄰接。其具體解釋如下圖:
基於超體聚類的點雲分割,使用點鄰接(藍色)作為相鄰判據。
2.超體聚類的實現步驟
舉個簡單的例子來體會下超體聚類,其過程和結晶類似。但不是水結晶成冰,而是鹽溶液過飽和狀態下的多晶核結晶。所有的晶核(seed)同時開始生長,最終填滿整個空間,使物質具有晶體結構。 超體聚類實際上是一種特殊的區域生長算法,和無限制的生長不同,超體聚類首先需要規律的布置區域生長“晶核”。晶核在空間中實際上是均勻分布的,並指定晶核距離(Rseed)。再指定粒子距離(Rvoxel)。再指定最小晶粒(MOV),過小的晶粒需要融入最近的大晶粒。關系如圖所示:
有了晶粒和結晶范圍之后,我們只需要控制結晶過程,就能將整個空間划分開了。結晶過程的本質就是不斷吸納類似的粒子(八分空間)。類似是一個比較模糊的概念,關於類似的定義有以下公式:
公式中的Dc,表示顏色上的差異,Dn表示法線上的差異,Ds代表點距離上的差異。w_*表示一系列權重。用於控制結晶形狀。在晶核周圍尋找一圈,D最小的體素被認為是下一個“被發展的黨員”。需要注意的是,結晶過程並不是長完一個晶核再長下一個,二是所有的晶核同時開始生長(雖然計算機計算時必然有先后,但從層次上來說是同時的)。其生長順序如下圖所示:
接下來所有晶核繼續公平競爭,發展第二個“黨員”,以此循環,最終所有晶體應該幾乎同時完成生長。整個點雲也被晶格所分割開來。並且保證了一個晶包里的粒子都是類似的。
3.PCL對超體聚類的實現
//設定結晶參數 float voxel_resolution = 0.008f; float seed_resolution = 0.1f; float color_importance = 0.2f; float spatial_importance = 0.4f; float normal_importance = 1.0f; //生成結晶器 pcl::SupervoxelClustering<PointT> super (voxel_resolution, seed_resolution); //和點雲形式有關 if (disable_transform) super.setUseSingleCameraTransform (false); //輸入點雲及結晶參數 super.setInputCloud (cloud); super.setColorImportance (color_importance); super.setSpatialImportance (spatial_importance); super.setNormalImportance (normal_importance); //輸出結晶分割結果:結果是一個映射表 std::map <uint32_t, pcl::Supervoxel<PointT>::Ptr > supervoxel_clusters; super.extract (supervoxel_clusters);
//獲得晶體中心
PointCloudT::Ptr voxel_centroid_cloud = super.getVoxelCentroidCloud ();
//獲得晶體
PointLCloudT::Ptr labeled_voxel_cloud = super.getLabeledVoxelCloud ();
執行上訴過程后,會將晶體映射成一系列數。數代表的是指向各個晶體的指針。可以通過getter函數,把晶體有關的信息拖出來。拖出來的是點雲。
//將相連的晶體中心連起來並顯示
std::multimap<uint32_t, uint32_t> supervoxel_adjacency; super.getSupervoxelAdjacency (supervoxel_adjacency);
std::multimap<uint32_t,uint32_t>::iterator label_itr = supervoxel_adjacency.begin ();
for ( ; label_itr != supervoxel_adjacency.end (); ) { //First get the label uint32_t supervoxel_label = label_itr->first; //Now get the supervoxel corresponding to the label pcl::Supervoxel<PointT>::Ptr supervoxel = supervoxel_clusters.at (supervoxel_label); //Now we need to iterate through the adjacent supervoxels and make a point cloud of them PointCloudT adjacent_supervoxel_centers; std::multimap<uint32_t,uint32_t>::iterator adjacent_itr = supervoxel_adjacency.equal_range (supervoxel_label).first; for ( ; adjacent_itr!=supervoxel_adjacency.equal_range (supervoxel_label).second; ++adjacent_itr) { pcl::Supervoxel<PointT>::Ptr neighbor_supervoxel = supervoxel_clusters.at (adjacent_itr->second); adjacent_supervoxel_centers.push_back (neighbor_supervoxel->centroid_); } //Now we make a name for this polygon std::stringstream ss; ss << "supervoxel_" << supervoxel_label; //This function is shown below, but is beyond the scope of this tutorial - basically it just generates a "star" polygon mesh from the points given addSupervoxelConnectionsToViewer (supervoxel->centroid_, adjacent_supervoxel_centers, ss.str (), viewer); //Move iterator forward to next label label_itr = supervoxel_adjacency.upper_bound (supervoxel_label); }
至此,生成了不同的晶體之間的鄰接關系。結果如下所示(不同晶核距離0.1m,0.05m)
此方法主要為識別做前期准備,但我認為,這種東西用在三維視覺+有限元倒是極好的。可以在不使用應變片的前提下對物體各個部分應變進行直接測量。在已知力的情況下可以建立物體剛度和應變的關系,貌似鋼包回轉台的手里分析可以這樣解決。蛋疼的是實際工業機械哪有那么多花花綠綠的給你分割,很難形成有效的對應點匹配。