点击下方卡片,关注“新机器视觉”公众号
重磅干货,第一时间送达
1.1基于边缘的分割方法
1.2基于区域的分割方法
1.2.1种子区域方法
1.2.2非种子区域方法
1.2.3基于图像区域增长方法
/* @brief:将点云转换为vector,俯视图栅格化
* @param [in]: in_cloud,输入点云
* @param [out]: out_cloud,转换的点云vector
* @return NONE
*/
void FrontLidarAlg::convetCloudToVector(pcl::PointCloud<pcl::PointXYZI>::Ptr in_cloud, \
cv::Mat& out_img, \
std::vector<pcl::PointCloud<pcl::PointXYZI>>* out_cloud)
{
out_img = cv::Mat::zeros(cv::Size(img_width_,img_height_), CV_8UC3);
volatile int row;
volatile int col;
for(int i=0;i<in_cloud->size();i++)
{
//将横向在检测范围之外的去掉
if(in_cloud->points[i].x <= cloud_x_min_ || in_cloud->points[i].x >= cloud_x_max_)
{
continue;
}
//将纵向在检测范围之外的去掉
if(in_cloud->points[i].y >= cloud_y_max_ || in_cloud->points[i].y <= 0)
{
continue;
}
//限制最高和最低点大小
if(in_cloud->points[i].z < min_dect_height_)//高度低于最低点,则赋值为最低点
{
in_cloud->points[i].z = min_dect_height_;
}
if(in_cloud->points[i].z > max_dect_height_)//高度高于最高点,则赋值最高点
{
in_cloud->points[i].z = max_dect_height_;
}
//计算点云所在图像的行数和列数,四舍五入
col = int((in_cloud->points[i].x - cloud_x_min_)/img_res_);
row = int(in_cloud->points[i].y/img_res_);
out_cloud->at(col+row*img_width_).points.push_back(in_cloud->points[i]);
//输入点云高度值转换到图像坐标系下的数值
int value = (int)((in_cloud->points[i].z - min_dect_height_)/dect_height_res_);
if(out_img.at<cv::Vec3b>(row,col)[0] < value)//b-最大高度
{
out_img.at<cv::Vec3b>(row,col)[0] = value;
if(0 == out_img.at<cv::Vec3b>(row,col)[1])
{
out_img.at<cv::Vec3b>(row,col)[1] = value;
}
}
else if(out_img.at<cv::Vec3b>(row,col)[1] > value)//b-最小高度
{
out_img.at<cv::Vec3b>(row,col)[1] = value;
}
}//for in_cloud->size
}
#include "object_segment.h"
namespace front_lidar {
/* @brief:点云分割构造函数,初始化参数
* @param [in]: NONE
* @param [out]: NONE
* @return NONE
*/
ObjectSegment::ObjectSegment(string config_file_str)
{
config_parser_ptr_.reset(new ConfigParser(config_file_str));
use_seg_raw_cloud_ = config_parser_ptr_->getInt("use_seg_raw_cloud");//聚类后是否输出原始数据
img_res_ = config_parser_ptr_->getDouble("img_res");//图像分辨率
cloud_x_min_ = config_parser_ptr_->getDouble("cloud_x_min");//x轴最小值
cloud_x_max_ = config_parser_ptr_->getDouble("cloud_x_max");//x轴最大值
cloud_y_min_ = config_parser_ptr_->getDouble("cloud_y_min");//y轴最小值
cloud_y_max_ = config_parser_ptr_->getDouble("cloud_y_max");//y轴最大值
min_cluster_size_ = config_parser_ptr_->getDouble("object_detect_min_cluster_size");//最小聚类个数
max_cluster_size_ = config_parser_ptr_->getDouble("object_detect_max_cluster_size");//最大聚类个数
cell_size_ = config_parser_ptr_->getDouble("cell_size");//聚类临域尺寸
min_dect_height_ = -10;//最小检测高度
max_dect_height_ = 10;//最大检测高度
dect_height_res_ = (max_dect_height_ - min_dect_height_)/255;//检测高度分辨率
region_growing_clusters_.clear();
region_growing_img_.resize(0);//区域增长后的图像
}
/* @brief:点云分割处理,将点云分割为不同障碍物点云
* @param [in]: in_img,输入图像栅格, in_vector-输入栅格化的点云
* @param [out]: cloud_clusters_ptr,输出聚类结果
* @return NONE
*/
void ObjectSegment::process(const std::vector<pcl::PointCloud<pcl::PointXYZI>>* in_vector, \
const cv::Mat& in_img, \
std::vector<pcl::PointCloud<pcl::PointXYZI>::Ptr>* cloud_clusters_ptr)
{
cloud_clusters_ptr->clear();
cluster_num_ = 0;
//区域增长,图像聚类分割
pcl::StopWatch regin_grow_timer;
reginGrowing(in_img, region_growing_img_);
LOG_Debug()<<"regin_grow time:"<<regin_grow_timer.getTime()<<"ms";
//将图像转换为点云
pcl::StopWatch vector_to_cloud_timer;
if(1 == use_seg_raw_cloud_)
{
convetVectorToCloud(in_vector, region_growing_img_, ®ion_growing_clusters_);
}
else
{
convetImageToCloud(region_growing_img_, ®ion_growing_clusters_);
}
LOG_Debug()<<"vector_to_cloud time:"<<vector_to_cloud_timer.getTime()<<"ms";
//滤除聚类点数少于设定阈值的类别
pcl::StopWatch filter_timer;
filterObjectsCloud(region_growing_clusters_, cloud_clusters_ptr);
LOG_Debug()<<"filter time:"<<filter_timer.getTime()<<"ms";
}
/* @brief:区域增长,8邻域聚类
* @param [in]: in_img-输入图像
* @param [out]: out_img,转换的图像,0-b-最大值,1-g-最小值,2-r-有无聚类标志(0-未聚类,1-255-已聚类ID)
* @return NONE
*/
void ObjectSegment::reginGrowing(const cv::Mat &in_img, cv::Mat &out_img)
{
unsigned short class_id = 0;
unsigned short element_class_id = 0;
out_img = in_img.clone();
for(int row = 1; row < out_img.rows -1; row++)
{
for(int col = 1; col < out_img.cols - 1; col++)
{
//像素的高度最大值为0,则该像素为空,无效像素
if(0 == out_img.at<cv::Vec3b>(row,col)[0])
{
continue;
}
//像素的类别标记为空,即为未标记类别的像素,则分配类别ID
if(0 == out_img.at<cv::Vec3b>(row,col)[2])
{
if(class_id > 250)//超出限定类别总数,则返回
{
return;
}
out_img.at<cv::Vec3b>(row,col)[2] = ++class_id;//给该像素赋值类别ID
}
element_class_id = out_img.at<cv::Vec3b>(row,col)[2];
//根据输入种子像素,递归区域增长
elementReginGrowing(out_img, row, col, element_class_id);
}//col
}//row
cluster_num_ = class_id;
}
/* @brief:单个元素区域增长,cell_size_邻域聚类
* @param [in]: 输入图像-in_img,当前元素的r和c,输出图像-in_img
* @param [out]: out_img,转换的图像,0-b-最大值,1-g-最小值,2-r-有无聚类标志(0-未聚类,1-255-聚类ID)
* @return NONE
*/
void ObjectSegment::elementReginGrowing(cv::Mat &in_img, int row, int col, unsigned short class_id)
{
int start_row;//起始行
int end_row;//截止行
int start_col;//起始列
int end_col;//截止列
//判断起始行 截止行 起始列 截止列,防止越界
start_row = row - cell_size_;
end_row = row + cell_size_;
start_col = col - cell_size_;
end_col = col + cell_size_;
if(start_row < 0)
start_row = 0;
if(end_row > in_img.rows - 1)
end_row = in_img.rows - 1;
if(start_col < 0)
start_col = 0;
if(end_col > in_img.cols - 1)
end_col = in_img.cols - 1;
for(int m = start_row; m <= end_row; m++)
{
for(int n = start_col; n <= end_col; n++)
{
//该像素对应最大高度为0,则为无效像素
if(0 == in_img.at<cv::Vec3b>(m,n)[0])
{
continue;
}
//该元素为初始输入,已经标记过
if(m==row && n==col)
{
continue;
}
//未标记过的元素,标记该元素,以该元素为种子,进行区域增长
if(0 == in_img.at<cv::Vec3b>(m,n)[2])
{
in_img.at<cv::Vec3b>(m,n)[2] = class_id;//标记元素
elementReginGrowing(in_img, m, n, class_id);//以该元素为初始值,进行区域增长
}
}//for col
}//for row
}
/* @brief:将vector转化为聚类结果,2D到3D
* @param [in]: in_img,输入的栅格图像
* @param [out]: out_cloud_ptr,转换的聚类结果
* @return NONE
*/
void ObjectSegment::convetImageToCloud(const cv::Mat& in_img, \
std::vector<pcl::PointCloud<pcl::PointXYZI>::Ptr>* out_cloud_ptr)
{
out_cloud_ptr->clear();
pcl::PointXYZI point;
int class_id = 0;//类ID,1开始递增
//根据类别分配向量空间
out_cloud_ptr->resize(cluster_num_);//扩充输出向量大小
//分配点云空间
for(int i=0;i<cluster_num_;i++)
{
out_cloud_ptr->at(i).reset(new pcl::PointCloud<pcl::PointXYZI>);
}
//将图像转换为点云
for(int row=0;row<in_img.rows;row++)
{
for(int col=0;col<in_img.cols;col++)
{
//如果分类标记为0,则为无效数据
if(0 == in_img.at<cv::Vec3b>(row,col)[2])
{
continue;
}
class_id = in_img.at<cv::Vec3b>(row,col)[2];//获取该区域的类别ID
//图像坐标转换为点云坐标
point.x = (col+0.5)*img_res_ + cloud_x_min_;
point.y = (row+0.5)*img_res_;
//点云坐标高度最大值
point.z = in_img.at<cv::Vec3b>(row,col)[0]*dect_height_res_ + min_dect_height_;
out_cloud_ptr->at(class_id-1)->points.push_back(point);
//点云坐标高度最小值
point.z = in_img.at<cv::Vec3b>(row,col)[1]*dect_height_res_ + min_dect_height_;
out_cloud_ptr->at(class_id-1)->points.push_back(point);
}
}
}
/* @brief:将vector转化为聚类结果,2D到3D
* @param [in]: in_vector,输入的栅格化后点云,in_img,输入的聚类标志位的图像
* @param [out]: out_cloud_ptr,转换的聚类结果
* @return NONE
*/
void ObjectSegment::convetVectorToCloud(const std::vector<pcl::PointCloud<pcl::PointXYZI>>* in_vector, \
const cv::Mat& in_img, \
std::vector<pcl::PointCloud<pcl::PointXYZI>::Ptr>* out_cloud_ptr)
{
out_cloud_ptr->clear();
int class_id = 0;//类ID,1开始递增
//根据类别分配向量空间
out_cloud_ptr->resize(cluster_num_);//扩充输出向量大小
//分配点云空间
for(int i=0;i<cluster_num_;i++)
{
out_cloud_ptr->at(i).reset(new pcl::PointCloud<pcl::PointXYZI>);
}
//将图像转换为点云
for(int row=0;row<in_img.rows;row++)
{
for(int col=0;col<in_img.cols;col++)
{
//如果分类标记为0,则为无效数据
if(0 == in_img.at<cv::Vec3b>(row,col)[2])
{
continue;
}
class_id = in_img.at<cv::Vec3b>(row,col)[2];//获取该区域的类别ID
int count = in_vector->at(col+row*in_img.cols).points.size();
if(count > 25)
count = 25;
for(int j = 0; j < count; j++)
{
out_cloud_ptr->at(class_id-1)->points.push_back(in_vector->at(col+row*in_img.cols).points[j]);
}
}
}
}
/* @brief:滤除聚类后点云数量小于设定值的类别
* @param [in]: in_objects_cloud-聚类后的目标点云
* @param [out]: out_objects_cloud_ptr-滤除类别中点数量少于设定阈值的类别
* @return NONE
*/
void ObjectSegment::filterObjectsCloud(const std::vector<pcl::PointCloud<pcl::PointXYZI>::Ptr> in_objects_cloud, \
std::vector<pcl::PointCloud<pcl::PointXYZI>::Ptr> *out_objects_cloud_ptr)
{
out_objects_cloud_ptr->clear();
for(int i=0;i<in_objects_cloud.size();i++)
{
if(in_objects_cloud[i]->size() < min_cluster_size_)
{
continue;
}
out_objects_cloud_ptr->push_back(in_objects_cloud[i]);
}
}
}//namespace front_lidar
1.2.4基于点云区域增长方法
#include <iostream>
#include <vector>
#include <pcl/point_types.h>
#include <pcl/io/pcd_io.h>
#include <pcl/search/search.h>
#include <pcl/search/kdtree.h>
#include <pcl/features/normal_3d.h>
#include <pcl/visualization/cloud_viewer.h>
#include <pcl/filters/passthrough.h>
#include <pcl/segmentation/region_growing.h>
int
main (int argc, char** argv)
{
//点云的类型
pcl::PointCloud<pcl::PointXYZ>::Ptr cloud (new pcl::PointCloud<pcl::PointXYZ>);
//打开点云
if ( pcl::io::loadPCDFile <pcl::PointXYZ> ("region_growing_tutorial.pcd", *cloud) == -1)
{
std::cout << "Cloud reading failed." << std::endl;
return (-1);
}
//设置搜索的方式或者说是结构
pcl::search::Search<pcl::PointXYZ>::Ptr tree = boost::shared_ptr<pcl::search::Search<pcl::PointXYZ> > (new pcl::search::KdTree<pcl::PointXYZ>);
//求法线
pcl::PointCloud <pcl::Normal>::Ptr normals (new pcl::PointCloud <pcl::Normal>);
pcl::NormalEstimation<pcl::PointXYZ, pcl::Normal> normal_estimator;
normal_estimator.setSearchMethod (tree);
normal_estimator.setInputCloud (cloud);
normal_estimator.setKSearch (50);
normal_estimator.compute (*normals);
//直通滤波在Z轴的0到1米之间
pcl::IndicesPtr indices (new std::vector <int>);
pcl::PassThrough<pcl::PointXYZ> pass;
pass.setInputCloud (cloud);
pass.setFilterFieldName ("z");
pass.setFilterLimits (0.0, 1.0);
pass.filter (*indices);
//聚类对象<点,法线>
pcl::RegionGrowing<pcl::PointXYZ, pcl::Normal> reg;
reg.setMinClusterSize (50); //最小的聚类的点数
reg.setMaxClusterSize (1000000); //最大的
reg.setSearchMethod (tree); //搜索方式
reg.setNumberOfNeighbours (30); //设置搜索的邻域点的个数
reg.setInputCloud (cloud); //输入点
//reg.setIndices (indices);
reg.setInputNormals (normals); //输入的法线
reg.setSmoothnessThreshold (3.0 / 180.0 * M_PI); //设置平滑度
reg.setCurvatureThreshold (1.0); //设置曲率的阀值
std::vector <pcl::PointIndices> clusters;
reg.extract (clusters);
std::cout << "Number of clusters is equal to " << clusters.size () << std::endl;
std::cout << "First cluster has " << clusters[0].indices.size () << " points." << endl;
std::cout << "These are the indices of the points of the initial" <<
std::endl << "cloud that belong to the first cluster:" << std::endl;
int counter = 0;
while (counter < clusters[0].indices.size ())
{
std::cout << clusters[0].indices[counter] << ", ";
counter++;
if (counter % 10 == 0)
std::cout << std::endl;
}
std::cout << std::endl;
//可视化聚类的结果
pcl::PointCloud <pcl::PointXYZRGB>::Ptr colored_cloud = reg.getColoredCloud ();
pcl::visualization::CloudViewer viewer ("Cluster viewer");
viewer.showCloud(colored_cloud);
while (!viewer.wasStopped ())
{
}
return (0);
}
1.2.5基于颜色区域增长方法
#include <iostream>
#include <vector>
#include <pcl/point_types.h>
#include <pcl/io/pcd_io.h>
#include <pcl/search/search.h>
#include <pcl/search/kdtree.h>
#include <pcl/visualization/cloud_viewer.h>
#include <pcl/filters/passthrough.h>
#include <pcl/segmentation/region_growing_rgb.h>
int
main (int argc, char** argv)
{
pcl::search::Search <pcl::PointXYZRGB>::Ptr tree = boost::shared_ptr<pcl::search::Search<pcl::PointXYZRGB> > (new pcl::search::KdTree<pcl::PointXYZRGB>);
pcl::PointCloud <pcl::PointXYZRGB>::Ptr cloud (new pcl::PointCloud <pcl::PointXYZRGB>);
if ( pcl::io::loadPCDFile <pcl::PointXYZRGB> ("region_growing_rgb_tutorial.pcd", *cloud) == -1 )
{
std::cout << "Cloud reading failed." << std::endl;
return (-1);
}
//存储点云的容器
pcl::IndicesPtr indices (new std::vector <int>);
//滤波
pcl::PassThrough<pcl::PointXYZRGB> pass;
pass.setInputCloud (cloud);
pass.setFilterFieldName ("z");
pass.setFilterLimits (0.0, 1.0);
pass.filter (*indices);
//基于颜色的区域生成的对象
pcl::RegionGrowingRGB<pcl::PointXYZRGB> reg;
reg.setInputCloud (cloud);
reg.setIndices (indices); //点云的索引
reg.setSearchMethod (tree);
reg.setDistanceThreshold (10); //距离的阀值
reg.setPointColorThreshold (6); //点与点之间颜色容差
reg.setRegionColorThreshold (5); //区域之间容差
reg.setMinClusterSize (600); //设置聚类的大小
std::vector <pcl::PointIndices> clusters;
reg.extract (clusters);
pcl::PointCloud <pcl::PointXYZRGB>::Ptr colored_cloud = reg.getColoredCloud ();
pcl::visualization::CloudViewer viewer ("Cluster viewer");
viewer.showCloud (colored_cloud);
while (!viewer.wasStopped ())
{
boost::this_thread::sleep (boost::posix_time::microseconds (100));
}
return (0);
}
1.3基于属性的分割方法
1.3.1欧式聚类
pcl::extractEuclideanClusters (const PointCloud<PointT> &cloud,
const typename search::Search<PointT>::Ptr &tree,
float tolerance, std::vector<PointIndices> &clusters,
unsigned int min_pts_per_cluster,
unsigned int max_pts_per_cluster)
{
if (tree->getInputCloud ()->points.size () != cloud.points.size ()) // 点数量检查
{
PCL_ERROR ("[pcl::extractEuclideanClusters] Tree built for a different point cloud dataset (%lu) than the input cloud (%lu)!\n", tree->getInputCloud ()->points.size (), cloud.points.size ());
return;
}
// Check if the tree is sorted -- if it is we don't need to check the first element
int nn_start_idx = tree->getSortedResults () ? 1 : 0;
// Create a bool vector of processed point indices, and initialize it to false
std::vector<bool> processed (cloud.points.size (), false);
std::vector<int> nn_indices;
std::vector<float> nn_distances; // 定义需要的变量
// Process all points in the indices vector
for (int i = 0; i < static_cast<int> (cloud.points.size ()); ++i) //遍历点云中的每一个点
{
if (processed[i]) //如果该点已经处理则跳过
continue;
std::vector<int> seed_queue; //定义一个种子队列
int sq_idx = 0;
seed_queue.push_back (i); //加入一个种子
processed[i] = true;
while (sq_idx < static_cast<int> (seed_queue.size ())) //遍历每一个种子
{
// Search for sq_idx kdtree 树的近邻搜索
if (!tree->radiusSearch (seed_queue[sq_idx], tolerance, nn_indices, nn_distances))
{
sq_idx++;
continue; //没找到近邻点就继续
}
for (size_t j = nn_start_idx; j < nn_indices.size (); ++j) // can't assume sorted (default isn't!)
{
if (nn_indices[j] == -1 || processed[nn_indices[j]]) // Has this point been processed before ?
continue; // 种子点的近邻点中如果已经处理就跳出此次循环继续
// Perform a simple Euclidean clustering
seed_queue.push_back (nn_indices[j]); //将此种子点的临近点作为新的种子点。入队操作
processed[nn_indices[j]] = true; // 该点已经处理,打标签
}
sq_idx++;
}
// If this queue is satisfactory, add to the clusters 最大点数和最小点数的类过滤
if (seed_queue.size () >= min_pts_per_cluster && seed_queue.size () <= max_pts_per_cluster)
{
pcl::PointIndices r;
r.indices.resize (seed_queue.size ());
for (size_t j = 0; j < seed_queue.size (); ++j)
r.indices[j] = seed_queue[j];
// These two lines should not be needed: (can anyone confirm?) -FF
std::sort (r.indices.begin (), r.indices.end ());
r.indices.erase (std::unique (r.indices.begin (), r.indices.end ()), r.indices.end ());
r.header = cloud.header;
clusters.push_back (r); // We could avoid a copy by working directly in the vector
}
}
}
// Creating the KdTree object for the search method of the extraction
pcl::search::KdTree<pcl::PointXYZ>::Ptr tree (new pcl::search::KdTree<pcl::PointXYZ>);
tree->setInputCloud (cloud_filtered);
std::vector<pcl::PointIndices> cluster_indices;
pcl::EuclideanClusterExtraction<pcl::PointXYZ> ec;
ec.setClusterTolerance (0.02); //设置近邻搜索的搜索半径为2cm
ec.setMinClusterSize (100); //设置一个聚类需要的最少点数目为100
ec.setMaxClusterSize (25000); //设置一个聚类需要的最大点数目为25000
ec.setSearchMethod (tree); //设置点云的搜索机制
ec.setInputCloud (cloud_filtered); //设置原始点云
ec.extract (cluster_indices); //从点云中提取聚类
// 可视化部分
pcl::visualization::PCLVisualizer viewer("segmention");
// 我们将要使用的颜色
float bckgr_gray_level = 0.0; // 黑色
float txt_gray_lvl = 1.0 - bckgr_gray_level;
int num = cluster_indices.size();
int j = 0;
for (std::vector<pcl::PointIndices>::const_iterator it = cluster_indices.begin (); it != cluster_indices.end (); ++it)
{
pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_cluster (new pcl::PointCloud<pcl::PointXYZ>);
for (std::vector<int>::const_iterator pit = it->indices.begin (); pit != it->indices.end (); pit++)
cloud_cluster->points.push_back (cloud_filtered->points[*pit]); //*
cloud_cluster->width = cloud_cluster->points.size ();
cloud_cluster->height = 1;
cloud_cluster->is_dense = true;
std::cout << "PointCloud representing the Cluster: " << cloud_cluster->points.size () << " data points." << std::endl;
std::stringstream ss;
ss << "cloud_cluster_" << j << ".pcd";
writer.write<pcl::PointXYZ> (ss.str (), *cloud_cluster, false); //*
pcl::visualization::PointCloudColorHandlerCustom<pcl::PointXYZ> cloud_in_color_h(cloud,
color_bar[j][0],
color_bar[j][1],
color_bar[j][2]);//赋予显示点云的颜色
viewer.addPointCloud(cloud_cluster, cloud_in_color_h, std::to_string(j));
j++;
}
1.3.2条件欧式聚类
#include <pcl/io/pcd_io.h>
#include <pcl/segmentation/conditional_euclidean_clustering.h>
#include <iostream>
// 如果这个函数返回的是真,这这个候选点将会被加入聚类中
bool
customCondition(const pcl::PointXYZ& seedPoint, const pcl::PointXYZ& candidatePoint, float squaredDistance)
{
// Do whatever you want here.做你想做的条件的筛选
if (candidatePoint.y < seedPoint.y) //如果候选点的Y的值小于种子点的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)
{
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++;
}
}
1.3.3密度聚类
#ifndef DBSCAN_H
#define DBSCAN_H
#include <pcl/point_types.h>
#define UN_PROCESSED 0
#define PROCESSING 1
#define PROCESSED 2
inline bool comparePointClusters (const pcl::PointIndices &a, const pcl::PointIndices &b) {
return (a.indices.size () < b.indices.size ());
}
template <typename PointT>
class DBSCANSimpleCluster {
public:
typedef typename pcl::PointCloud<PointT>::Ptr PointCloudPtr;
typedef typename pcl::search::KdTree<PointT>::Ptr KdTreePtr;
virtual void setInputCloud(PointCloudPtr cloud) {
input_cloud_ = cloud;
}
void setSearchMethod(KdTreePtr tree) {
search_method_ = tree;
}
void extract(std::vector<pcl::PointIndices>& cluster_indices) {
std::vector<int> nn_indices;
std::vector<float> nn_distances;
std::vector<bool> is_noise(input_cloud_->points.size(), false);
std::vector<int> types(input_cloud_->points.size(), UN_PROCESSED);
for (int i = 0; i < input_cloud_->points.size(); i++) {
if (types[i] == PROCESSED) {
continue;
}
int nn_size = radiusSearch(i, eps_, nn_indices, nn_distances);
if (nn_size < minPts_) {
is_noise[i] = true;
continue;
}
std::vector<int> seed_queue;
seed_queue.push_back(i);
types[i] = PROCESSED;
for (int j = 0; j < nn_size; j++) {
if (nn_indices[j] != i) {
seed_queue.push_back(nn_indices[j]);
types[nn_indices[j]] = PROCESSING;
}
} // for every point near the chosen core point.
int sq_idx = 1;
while (sq_idx < seed_queue.size()) {
int cloud_index = seed_queue[sq_idx];
if (is_noise[cloud_index] || types[cloud_index] == PROCESSED) {
// seed_queue.push_back(cloud_index);
types[cloud_index] = PROCESSED;
sq_idx++;
continue; // no need to check neighbors.
}
nn_size = radiusSearch(cloud_index, eps_, nn_indices, nn_distances);
if (nn_size >= minPts_) {
for (int j = 0; j < nn_size; j++) {
if (types[nn_indices[j]] == UN_PROCESSED) {
seed_queue.push_back(nn_indices[j]);
types[nn_indices[j]] = PROCESSING;
}
}
}
types[cloud_index] = PROCESSED;
sq_idx++;
}
if (seed_queue.size() >= min_pts_per_cluster_ && seed_queue.size () <= max_pts_per_cluster_) {
pcl::PointIndices r;
r.indices.resize(seed_queue.size());
for (int j = 0; j < seed_queue.size(); ++j) {
r.indices[j] = seed_queue[j];
}
// These two lines should not be needed: (can anyone confirm?) -FF
std::sort (r.indices.begin (), r.indices.end ());
r.indices.erase (std::unique (r.indices.begin (), r.indices.end ()), r.indices.end ());
r.header = input_cloud_->header;
cluster_indices.push_back (r); // We could avoid a copy by working directly in the vector
}
} // for every point in input cloud
std::sort (cluster_indices.rbegin (), cluster_indices.rend (), comparePointClusters);
}
void setClusterTolerance(double tolerance) {
eps_ = tolerance;
}
void setMinClusterSize (int min_cluster_size) {
min_pts_per_cluster_ = min_cluster_size;
}
void setMaxClusterSize (int max_cluster_size) {
max_pts_per_cluster_ = max_cluster_size;
}
void setCorePointMinPts(int core_point_min_pts) {
minPts_ = core_point_min_pts;
}
protected:
PointCloudPtr input_cloud_;
double eps_ {0.0};
int minPts_ {1}; // not including the point itself.
int min_pts_per_cluster_ {1};
int max_pts_per_cluster_ {std::numeric_limits<int>::max()};
KdTreePtr search_method_;
virtual int radiusSearch(
int index, double radius, std::vector<int> &k_indices,
std::vector<float> &k_sqr_distances) const
{
return this->search_method_->radiusSearch(index, radius, k_indices, k_sqr_distances);
}
}; // class DBSCANCluster
#endif // DBSCAN_H
pcl::search::KdTree<pcl::PointXYZ>::Ptr tree(new pcl::search::KdTree<pcl::PointXYZ>);
tree->setInputCloud(point_cloud_input);
std::vector<pcl::PointIndices> cluster_indices;
DBSCANKdtreeCluster<pcl::PointXYZ> ec;
ec.setCorePointMinPts(20);
ec.setClusterTolerance(0.05);
ec.setMinClusterSize(100);
ec.setMaxClusterSize(25000);
ec.setSearchMethod(tree);
ec.setInputCloud(point_cloud_input);
ec.extract(cluster_indices);
1.4基于模型的分割方法
1.5基于图割的分割方法
1.6关于pcl中提供的关于点云分割的模块
1.6.1最小分割算法
//生成分割器
pcl::MinCutSegmentation<pcl::PointXYZ> seg;
//分割输入分割目标
seg.setInputCloud(cloud);
//指定打击目标(目标点)
pcl::PointCloud<pcl::PointXYZ>::Ptr foreground_points(new pcl::PointCloud<pcl::PointXYZ> ());
pcl::PointXYZ point;
point.x = 68.97;
point.y = -18.55;
point.z = 0.57;
foreground_points->points.push_back(point);
seg.setForegroundPoints(foreground_points);
//指定权函数sigma
seg.setSigma(0.25);
//物体大概范围
seg.setRadius(3.0433856);
//用多少生成图
seg.setNumberOfNeighbours(14);
//和目标点相连点的权值(至少有14个)
seg.setSourceWeight(0.8);
//分割结果
std::vector <pcl::PointIndices> clusters;
seg.extract(clusters);
1.6.2超体聚类
#include <pcl/console/parse.h>
#include <pcl/point_cloud.h>
#include <pcl/point_types.h>
#include <pcl/io/pcd_io.h>
#include <pcl/visualization/pcl_visualizer.h>
#include <pcl/segmentation/supervoxel_clustering.h>
//VTK include needed for drawing graph lines
#include <vtkPolyLine.h>
// 数据类型
typedef pcl::PointXYZRGBA PointT;
typedef pcl::PointCloud<PointT> PointCloudT;
typedef pcl::PointNormal PointNT;
typedef pcl::PointCloud<PointNT> PointNCloudT;
typedef pcl::PointXYZL PointLT;
typedef pcl::PointCloud<PointLT> PointLCloudT;
//可视化
void addSupervoxelConnectionsToViewer (PointT &supervoxel_center,
PointCloudT &adjacent_supervoxel_centers,
std::string supervoxel_name,
boost::shared_ptr<pcl::visualization::PCLVisualizer> & viewer);
int
main (int argc, char ** argv)
{
//解析命令行
if (argc < 2)
{
pcl::console::print_error ("Syntax is: %s <pcd-file> \n "
"--NT Dsables the single cloud transform \n"
"-v <voxel resolution>\n-s <seed resolution>\n"
"-c <color weight> \n-z <spatial weight> \n"
"-n <normal_weight>\n", argv[0]);
return (1);
}
//打开点云
PointCloudT::Ptr cloud = boost::shared_ptr <PointCloudT> (new PointCloudT ());
pcl::console::print_highlight ("Loading point cloud...\n");
if (pcl::io::loadPCDFile<PointT> (argv[1], *cloud))
{
pcl::console::print_error ("Error loading cloud file!\n");
return (1);
}
bool disable_transform = pcl::console::find_switch (argc, argv, "--NT");
float voxel_resolution = 0.008f; //分辨率
bool voxel_res_specified = pcl::console::find_switch (argc, argv, "-v");
if (voxel_res_specified)
pcl::console::parse (argc, argv, "-v", voxel_resolution);
float seed_resolution = 0.1f;
bool seed_res_specified = pcl::console::find_switch (argc, argv, "-s");
if (seed_res_specified)
pcl::console::parse (argc, argv, "-s", seed_resolution);
float color_importance = 0.2f;
if (pcl::console::find_switch (argc, argv, "-c"))
pcl::console::parse (argc, argv, "-c", color_importance);
float spatial_importance = 0.4f;
if (pcl::console::find_switch (argc, argv, "-z"))
pcl::console::parse (argc, argv, "-z", spatial_importance);
float normal_importance = 1.0f;
if (pcl::console::find_switch (argc, argv, "-n"))
pcl::console::parse (argc, argv, "-n", normal_importance);
//如何使用SupervoxelClustering函数
pcl::SupervoxelClustering<PointT> super (voxel_resolution, seed_resolution);
if (disable_transform)//如果设置的是参数--NT 就用默认的参数
super.setUseSingleCameraTransform (false);
super.setInputCloud (cloud);
super.setColorImportance (color_importance); //0.2f
super.setSpatialImportance (spatial_importance); //0.4f
super.setNormalImportance (normal_importance); //1.0f
std::map <uint32_t, pcl::Supervoxel<PointT>::Ptr > supervoxel_clusters;
pcl::console::print_highlight ("Extracting supervoxels!\n");
super.extract (supervoxel_clusters);
pcl::console::print_info ("Found %d supervoxels\n", supervoxel_clusters.size ());
boost::shared_ptr<pcl::visualization::PCLVisualizer> viewer (new pcl::visualization::PCLVisualizer ("3D Viewer"));
viewer->setBackgroundColor (0, 0, 0);
PointCloudT::Ptr voxel_centroid_cloud = super.getVoxelCentroidCloud ();//获得体素中心的点云
viewer->addPointCloud (voxel_centroid_cloud, "voxel centroids");
viewer->setPointCloudRenderingProperties (pcl::visualization::PCL_VISUALIZER_POINT_SIZE,2.0, "voxel centroids"); //渲染点云
viewer->setPointCloudRenderingProperties (pcl::visualization::PCL_VISUALIZER_OPACITY,0.95, "voxel centroids");
PointLCloudT::Ptr labeled_voxel_cloud = super.getLabeledVoxelCloud ();
viewer->addPointCloud (labeled_voxel_cloud, "labeled voxels");
viewer->setPointCloudRenderingProperties (pcl::visualization::PCL_VISUALIZER_OPACITY,0.8, "labeled voxels");
PointNCloudT::Ptr sv_normal_cloud = super.makeSupervoxelNormalCloud (supervoxel_clusters);
//We have this disabled so graph is easy to see, uncomment to see supervoxel normals
//viewer->addPointCloudNormals<PointNormal> (sv_normal_cloud,1,0.05f, "supervoxel_normals");
pcl::console::print_highlight ("Getting supervoxel adjacency\n");
std::multimap<uint32_t, uint32_t> supervoxel_adjacency;
super.getSupervoxelAdjacency (supervoxel_adjacency);
//To make a graph of the supervoxel adjacency, we need to iterate through the supervoxel adjacency multimap
//为了使整个超体形成衣服图,我们需要遍历超体的每个临近的个体
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);
}
while (!viewer->wasStopped ())
{
viewer->spinOnce (100);
}
return (0);
}
//VTK可视化构成的聚类图
void
addSupervoxelConnectionsToViewer (PointT &supervoxel_center,
PointCloudT &adjacent_supervoxel_centers,
std::string supervoxel_name,
boost::shared_ptr<pcl::visualization::PCLVisualizer> & viewer)
{
vtkSmartPointer<vtkPoints> points = vtkSmartPointer<vtkPoints>::New ();
vtkSmartPointer<vtkCellArray> cells = vtkSmartPointer<vtkCellArray>::New ();
vtkSmartPointer<vtkPolyLine> polyLine = vtkSmartPointer<vtkPolyLine>::New ();
//Iterate through all adjacent points, and add a center point to adjacent point pair
PointCloudT::iterator adjacent_itr = adjacent_supervoxel_centers.begin ();
for ( ; adjacent_itr != adjacent_supervoxel_centers.end (); ++adjacent_itr)
{
points->InsertNextPoint (supervoxel_center.data);
points->InsertNextPoint (adjacent_itr->data);
}
// Create a polydata to store everything in
vtkSmartPointer<vtkPolyData> polyData = vtkSmartPointer<vtkPolyData>::New ();
// Add the points to the dataset
polyData->SetPoints (points);
polyLine->GetPointIds ()->SetNumberOfIds(points->GetNumberOfPoints ());
for(unsigned int i = 0; i < points->GetNumberOfPoints (); i++)
polyLine->GetPointIds ()->SetId (i,i);
cells->InsertNextCell (polyLine);
// Add the lines to the dataset
polyData->SetLines (cells);
viewer->addModelFromPolyData (polyData,supervoxel_name);
}
1.6.3基于凹凸性的点云分割
1.6.3.1lccp方法
//生成LCCP分割器
pcl::LCCPSegmentation<PointT>::LCCPSegmentation LCCPseg;
//输入超体聚类结果
seg.setInputSupervoxels(supervoxel_clusters,supervoxel_adjacency);
//CC效验beta值
seg.setConcavityToleranceThreshold (concavity_tolerance_threshold);
//CC效验的k邻点
seg.setKFactor (k_factor_arg)
//
seg.setSmoothnessCheck (bool_use_smoothness_check_arg,voxel_res_arg,seed_res_arg,smoothness_threshold_arg = 0.1);
//SC效验
seg.setSanityCheck (bool_use_sanity_criterion_arg);
//最小分割尺寸
seg.setMinSegmentSize (min_segment_size_arg)
seg.segment();
seg.relabelCloud (pcl::PointCloud<pcl::PointXYZL> &labeled_cloud_arg);
1.6.3.2cpc方法
//生成CPC分割器
pcl::CPCSegmentation<PointT>::CPCSegmentation seg;
//输入超体聚类结果
seg.setInputSupervoxels(supervoxel_clusters,supervoxel_adjacency);
//设置分割参数
setCutting (max_cuts = 20,
cutting_min_segments = 0,
cutting_min_score = 0.16,
locally_constrained = true,
directed_cutting = true,
clean_cutting = false);
seg.setRANSACIterations (ransac_iterations);
seg.segment();
seg.relabelCloud (pcl::PointCloud<pcl::PointXYZL> &labeled_cloud_arg);
1.6.4基于形态学的点云分割
//生成形态滤波器
pcl::ProgressiveMorphologicalFilter<pcl::PointXYZ> pmf;
pmf.setInputCloud (cloud);
//设置窗的大小以及切深,斜率信息
pmf.setMaxWindowSize (20);
pmf.setSlope (1.0f);
pmf.setInitialDistance (0.5f);
pmf.setMaxDistance (3.0f);
//提取地面
pmf.extract (ground->indices);
// 从标号到点云
pcl::ExtractIndices<pcl::PointXYZ> extract;
extract.setInputCloud (cloud);
extract.setIndices (ground);
extract.filter (*cloud_filtered);
1.7总结
本文仅做学术分享,如有侵权,请联系删文。