上一篇講了超體聚類,也就是把點雲按照顏色和空間位置進行有意義的分割,將其分割成小塊,分割之後看起來還是很亂,但是基於聚類之後的結果2014年CVPR上有人基於這個聚類結果提出了利用凹凸性進行物體分割的方法,這個方法好的一點是不想我之前提到過的一個點雲分割的框架需要進行訓練,用SVM分類然後再利用模型進行分割,這個算法可以進行直接分割,且分割效果也很不錯。
具體原理可以參考http://www.cnblogs.com/ironstark/p/5027269.html,在這裏我把他的原理部分貼過來,再加上自己的一些理解。
LCCP是Locally Convex Connected Patches的縮寫,翻譯成中文叫做”局部凸連接打包一波帶走“~~~算法大致可以分成兩個部分:1.基於超體聚類的過分割。2.在超體聚類的基礎上再聚類。超體聚類作爲一種過分割方法,在理想情況下是不會引入錯誤信息的,也就是說適合在此基礎上再進行處理。LCCP方法並不依賴於點雲顏色,所以只使用空間信息和法線信息。
這個是這個方法的流程圖:
點雲完成超體聚類之後,對於過分割的點雲需要計算不同的塊之間凹凸關係。凹凸關係通過CC(Extended Convexity Criterion) 和 SC (Sanity criterion)判據來進行判斷。其中 CC 利用相鄰兩片中心連線向量與法向量夾角來判斷兩片是凹是凸。顯然,如果圖中a1>a2則爲凹,反之則爲凸。
考慮到測量噪聲等因素,需要在實際使用過程中引入門限值(a1需要比a2大出一定量)來濾出較小的凹凸誤判。此外,爲去除一些小噪聲引起的誤判,還需要引入“第三方驗證”,如果某塊和相鄰兩塊都相交,則其凹凸關係必相同。CC 判據最終如CCe:
如果相鄰兩面中,有一個面是單獨的,cc判據是無法將其分開的。舉個簡單的例子,兩本厚度不同的書並排放置,視覺算法應該將兩本書分割開。如果是臺階,則視覺算法應該將臺階作爲一個整體。本質上就是因爲厚度不同的書存在surface-singularities。爲此需要引入SC判據,來對此進行區分。
如圖所示,相鄰兩面是否真正聯通,是否存在單獨面,與θ角有關,θ角越大,則兩面真的形成凸關係的可能性就越大。據此,可以設計SC判據:
其中S(向量)爲兩平面法向量的叉積。
最終,兩相鄰面之間凸邊判據爲:
在標記完各個小區域的凹凸關係後,則採用區域增長算法將小區域聚類成較大的物體。此區域增長算法受到小區域凹凸性限制,既:只允許區域跨越凸邊增長。
至此,分割完成,在濾去多餘噪聲後既獲得點雲分割結果。此外:考慮到RGB-D圖像隨深度增加而離散,難以確定八叉樹尺寸,故在z方向使用對數變換以提高精度。分割結果如圖:
從圖中可知,糾纏在一起,顏色形狀相近的物體完全被分割開了。在這個基礎上我們可以做進一步處理以得到更好的分割結果,進一步的處理我會慢慢寫出來。
下面是我寫的聯合超體聚類和LCCP的代碼,運行結果還可以,不過實時性暫時就不用想了。
#include <stdlib.h>
#include <cmath>
#include <limits.h>
#include <boost/format.hpp>
#include <pcl/console/parse.h>
#include <pcl/io/pcd_io.h>
#include <pcl/visualization/pcl_visualizer.h>
#include <pcl/visualization/point_cloud_color_handlers.h>
#include <pcl/filters/passthrough.h>
#include <pcl/segmentation/supervoxel_clustering.h>
#include <pcl/segmentation/lccp_segmentation.h>
typedef pcl::PointXYZRGBA PointT;
typedef pcl::LCCPSegmentation<PointT>::SupervoxelAdjacencyList SuperVoxelAdjacencyList;
int main(int argc, char ** argv)
{
//輸入點雲
pcl::PointCloud<PointT>::Ptr input_cloud_ptr(new pcl::PointCloud<PointT>);
pcl::PCLPointCloud2 input_pointcloud2;
if (pcl::io::loadPCDFile("milk_cartoon_all_small_clorox.pcd", input_pointcloud2))
{
PCL_ERROR("ERROR: Could not read input point cloud ");
return (3);
}
pcl::fromPCLPointCloud2(input_pointcloud2, *input_cloud_ptr);
PCL_INFO("Done making cloud\n");
//超體聚類 參數依次是粒子距離、晶核距離、顏色容差、
float voxel_resolution = 0.0075f;
float seed_resolution = 0.03f;
float color_importance = 0.0f;
float spatial_importance = 1.0f;
float normal_importance = 4.0f;
bool use_single_cam_transform = false;
bool use_supervoxel_refinement = false;
unsigned int k_factor = 0;
//voxel_resolution is the resolution (in meters) of voxels used、seed_resolution is the average size (in meters) of resulting supervoxels
pcl::SupervoxelClustering<PointT> super(voxel_resolution, seed_resolution);
super.setUseSingleCameraTransform(use_single_cam_transform);
super.setInputCloud(input_cloud_ptr);
//Set the importance of color for supervoxels.
super.setColorImportance(color_importance);
//Set the importance of spatial distance for supervoxels.
super.setSpatialImportance(spatial_importance);
//Set the importance of scalar normal product for supervoxels.
super.setNormalImportance(normal_importance);
std::map<uint32_t, pcl::Supervoxel<PointT>::Ptr> supervoxel_clusters;
PCL_INFO("Extracting supervoxels\n");
super.extract(supervoxel_clusters);
PCL_INFO("Getting supervoxel adjacency\n");
std::multimap<uint32_t, uint32_t> supervoxel_adjacency;
super.getSupervoxelAdjacency(supervoxel_adjacency);
pcl::PointCloud<pcl::PointNormal>::Ptr sv_centroid_normal_cloud = pcl::SupervoxelClustering<PointT>::makeSupervoxelNormalCloud(supervoxel_clusters);
//LCCP分割
float concavity_tolerance_threshold = 10;
float smoothness_threshold = 0.1;
uint32_t min_segment_size = 0;
bool use_extended_convexity = false;
bool use_sanity_criterion = false;
PCL_INFO("Starting Segmentation\n");
pcl::LCCPSegmentation<PointT> lccp;
lccp.setConcavityToleranceThreshold(concavity_tolerance_threshold);
lccp.setSmoothnessCheck(true, voxel_resolution, seed_resolution, smoothness_threshold);
lccp.setKFactor(k_factor);
lccp.setInputSupervoxels(supervoxel_clusters, supervoxel_adjacency);
lccp.setMinSegmentSize(min_segment_size);
lccp.segment();
PCL_INFO("Interpolation voxel cloud -> input cloud and relabeling\n");
pcl::PointCloud<pcl::PointXYZL>::Ptr sv_labeled_cloud = super.getLabeledCloud();
pcl::PointCloud<pcl::PointXYZL>::Ptr lccp_labeled_cloud = sv_labeled_cloud->makeShared();
lccp.relabelCloud(*lccp_labeled_cloud);
SuperVoxelAdjacencyList sv_adjacency_list;
lccp.getSVAdjacencyList(sv_adjacency_list);
return 0;
}