LCCP算法研究基础
区域生长法的实质是将具有相似性的点云集合起来构成某片区域。区域生长分为两个步骤,首先通过随机采样或其他方法从点云中获取一组种子点,其次是设定种子生长准则,将满足这一准则的点合并到种子像素所在的区域中。
《Object Partitioning using Local Convexity》一文的作者从以往的研究中找到了一种基于凹凸性的分割方法。对于二维图像而言其凹凸性较难描述,但对于三维图像而言,凹凸性几乎是与生俱来的。Stein提出局部凸连接生长方法Locally Convex Connected Patches(LCCP),首先根据点云数据的法向量夹角和空间距离将点云划分为超体素,记录超体素邻接图,判定邻接超体素的连接关系,最后将所有凸连接超体素合并形成最终分割结果。该方法在超体素基础上做生长算法,减少了计算量且使用凹凸性作为生长准则,可以准确分割不同物体的边界,在堆叠物体中有较好的效果。SONG用平面图像轮廓检测方法做预处理,结合 LCCP方法完成对堆叠国际象棋棋子和堆叠管道的分割,改善了欠分割情况。LCCP算法通过选取种子点,以法向量夹角或曲率作为阈值进行区域生长,能够保持良好的边界性与区域连通性,分割效率高,但算法性能依赖于种子选取,且生长时易跨越物体边界,从而造成欠分割的问题。
参考1
一、代码结构
实现代码 | QT窗口 |
lccp_segmentation.h | mainwindow.h |
ccp_segmentation.hpp | mainwindow.cpp |
lccp_segmentation.cpp |
二、默认参数
mainwindow.cpp中默认参数;
if(lineVoxel->text()!=NULL &&
lineSeed->text()!=NULL){
voxel_resolution = this->lineVoxel->text().toFloat();
seed_resolution = this->lineSeed->text().toFloat();
color_importance = this->lineColor->text().toFloat();
}
else{
voxel_resolution = 0.0075f; //粒子距离 = 0.0075f
seed_resolution = 0.3f; //晶核距离 = 0.3f
color_importance = 0.0f; //颜色容差 = 0.0f
}
三、主要函数
void mainwindow::lccpSeg()
{
clock_t startTime,endTime;
startTime = clock();
if(lineVoxel->text()!=NULL &&
lineSeed->text()!=NULL){
voxel_resolution = this->lineVoxel->text().toFloat();
seed_resolution = this->lineSeed->text().toFloat();
color_importance = this->lineColor->text().toFloat();
}
else{
voxel_resolution = 0.0075f;
seed_resolution = 0.3f;
color_importance = 0.0f;
}
//输入点云
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 ;
}
pcl::fromPCLPointCloud2(input_pointcloud2, *input_cloud_ptr);
PCL_INFO("Done making cloud\n");
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 = 20;
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);
// Configure Visualizer
pcl::visualization::PCLVisualizer viewer = pcl::visualization::PCLVisualizer ("3D Viewer",false);
viewer.addPointCloud (lccp_labeled_cloud, "Segmented point cloud");
renderWindow = vtkSmartPointer<vtkRenderWindow>::New();
renderWindow = viewer.getRenderWindow();
qvtk->SetRenderWindow(renderWindow);
viewer.setupInteractor(qvtk->GetInteractor(),qvtk->GetRenderWindow());
qvtk->update();
qvtk->GetRenderWindow()->Render();
endTime = clock();
cout<<"The run time is :" << (double)(endTime-startTime)/CLOCKS_PER_SEC <<"s"<<endl;
}
参考2
Q:源代码得不到分割后带标记的点云!
A:解决:输出点云的label全部是0。最后发现,getlabelcloud()函数是可以得到有label且不为0的点云的,但是这个点云不可以使用pcl中的pcl::savePCDFile来保存成pcd文件,这样保存的pcd文件的label都是0,又没找到可视化的工具,所以就新建一个pcl::PointXYZRGB类型的点云,重新对其赋值,进而得到可以用pcl::savePCDFile的点云文件Q:终输出4个点云文件:
1.超体聚类后有xyz的.txt文件;
2.超体聚类后有label的.txt文件;
3.根据不同label给随机颜色的.pcd文件
4.在LCCP之后,数据类型为xyz和label的.txt文件
//超体聚类+LCCP
#include "stdafx.h"
#include <stdlib.h>
#include <cmath>
#include <limits.h>
#include <boost/format.hpp>
#include <fstream>
#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/visualization/cloud_viewer.h>
#include <pcl/filters/passthrough.h>
#include <pcl/segmentation/supervoxel_clustering.h>
#include <pcl/segmentation/lccp_segmentation.h>
#define Random(x) (rand() % x)
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("xyzrgb.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.3f;
float seed_resolution = 1.2f;
float color_importance = 0.0f;
float spatial_importance = 1.0f;
float normal_importance = 0.0f;
bool use_single_cam_transform = false;
bool use_supervoxel_refinement = false;
unsigned int k_factor = 0;
pcl::SupervoxelClustering<PointT> super(voxel_resolution, seed_resolution);
super.setUseSingleCameraTransform(use_single_cam_transform);
super.setInputCloud(input_cloud_ptr);
super.setColorImportance(color_importance);
super.setSpatialImportance(spatial_importance);
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::PointXYZL>::Ptr overseg = super.getLabeledCloud();
ofstream outFile1("过分割3.txt", std::ios_base::out);
for (int i = 0; i < overseg->size(); i++) {
outFile1 << overseg->points[i].x << "\t" << overseg->points[i].y << "\t" << overseg->points[i].z << "\t" << overseg->points[i].label << endl;
}
int label_max1 = 0;
for (int i = 0;i< overseg->size(); i++) {
if (overseg->points[i].label>label_max1)
label_max1 = overseg->points[i].label;
}
pcl::PointCloud<pcl::PointXYZRGB>::Ptr ColoredCloud1(new pcl::PointCloud<pcl::PointXYZRGB>);
ColoredCloud1->height = 1;
ColoredCloud1->width = overseg->size();
ColoredCloud1->resize(overseg->size());
for (int i = 0; i < label_max1; i++) {
int color_R = Random(255);
int color_G = Random(255);
int color_B = Random(255);
for (int j = 0; j < overseg->size(); j++) {
if (overseg->points[j].label == i) {
ColoredCloud1->points[j].x = overseg->points[j].x;
ColoredCloud1->points[j].y = overseg->points[j].y;
ColoredCloud1->points[j].z = overseg->points[j].z;
ColoredCloud1->points[j].r = color_R;
ColoredCloud1->points[j].g = color_G;
ColoredCloud1->points[j].b = color_B;
}
}
}
pcl::io::savePCDFileASCII("过分割3.pcd", *ColoredCloud1);
//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);
ofstream outFile2("分割后合并3.txt", std::ios_base::out);
for (int i = 0; i < lccp_labeled_cloud->size();i++) {
outFile2 << lccp_labeled_cloud->points[i].x << "\t" << lccp_labeled_cloud->points[i].y << "\t" << lccp_labeled_cloud->points[i].z << "\t" << lccp_labeled_cloud->points[i].label << endl;
}
int label_max2 = 0;
for (int i = 0; i< lccp_labeled_cloud->size(); i++) {
if (lccp_labeled_cloud->points[i].label>label_max2)
label_max2 = lccp_labeled_cloud->points[i].label;
}
pcl::PointCloud<pcl::PointXYZRGB>::Ptr ColoredCloud2(new pcl::PointCloud<pcl::PointXYZRGB>);
ColoredCloud2->height = 1;
ColoredCloud2->width = lccp_labeled_cloud->size();
ColoredCloud2->resize(lccp_labeled_cloud->size());
for (int i = 0; i < label_max2; i++) {
int color_R = Random(255);
int color_G = Random(255);
int color_B = Random(255);
for (int j = 0; j < lccp_labeled_cloud->size(); j++) {
if (lccp_labeled_cloud->points[j].label == i) {
ColoredCloud2->points[j].x = lccp_labeled_cloud->points[j].x;
ColoredCloud2->points[j].y = lccp_labeled_cloud->points[j].y;
ColoredCloud2->points[j].z = lccp_labeled_cloud->points[j].z;
ColoredCloud2->points[j].r = color_R;
ColoredCloud2->points[j].g = color_G;
ColoredCloud2->points[j].b = color_B;
}
}
}
pcl::io::savePCDFileASCII("分割后合并3.pcd", *ColoredCloud2);
return 0;
}