根据上一篇博客介绍的超体素分割原理,在PCL中该功能以将其实现,并将其进行可视化。但是存在如下问题:(1)其将所有的块同时显示,对于很多其他应用,比如后续处理需要对每一块点单独进行操作,那么就需要将每个块点云单独抽取出来处理;(2)基于超体素分割,分割得到的点云块数据是远少于原始点的数据,其中与初始体素大小密切相关,初始体素体积越小,损失会小一点。这有点类似降采样,但实际一些分类中,是要保证在超体素处理前后,其总的点数是要保持不变的,因此这些没有化成超体素的点也需要单独抽取出,进行后续处理;(3)块与体素之间拓扑关系的构建。在一些若干处理中,如分类中,点之间的上下文信息与点之间的关系距离密切相关,而有些文献,如《An efficient global energy optimization approach for robust 3D plane segmentation of point clouds》中使用体素中心(或体素所有坐标的平均值)来表示体素,中心坐标一般位于体素中心位置,其与相邻体素之间的距离较远,在条件随机场中,根据距离计算而言,实际相邻的2个体素本身属于同一物体,但实际中可能由于距离太远而被错划分成不同地物。因此体素间相邻关系如何有效构建,也是值得研究的。
针对问题(1),即单独将分出的点云簇分开,代码如下:(PCL1.8)
#include <pcl/console/parse.h>
#include <pcl/point_cloud.h>
#include <pcl/point_types.h>
#include <pcl/visualization/pcl_visualizer.h>
#include <pcl/segmentation/supervoxel_clustering.h>
#include <pcl/filters/statistical_outlier_removal.h>
#include<iostream>
//VTK include needed for drawing graph lines
#include <vtkPolyLine.h>
#include"IO.h"
using namespace pcl;
using namespace std;
typedef PointXYZ PointT;
typedef PointXYZL PointTL;
int main()
{
IO IOExample;
//--------------------加载点云--------------------------
PointCloud<PointT>::Ptr cloud(new PointCloud<PointT>);
char *inputpath = "..//测试数据//desk.txt";
vector<pcl::PointXYZ> allpoints;
allpoints = IOExample.ReadPointXYZIntoVector(inputpath);
cloud->width = allpoints.size();
cloud->height = 1;
cloud->is_dense = false;
cloud->resize(cloud->width*cloud->height);
for (int i = 0; i < allpoints.size(); i++)
{
cloud->points[i].x = allpoints[i].x;
cloud->points[i].y = allpoints[i].y;
cloud->points[i].z = allpoints[i].z;
}
//------------------构建超体素--------------------------
float voxel_resultion = 0.005f;
float seed_resultion = 0.15f;
float color_importance = 0.0f;
float spatial_importance = 0.4f;
float normal_importance = 5.0f;
SupervoxelClustering<PointT> super(voxel_resultion, seed_resultion);
super.setInputCloud(cloud);
super.setNormalImportance(normal_importance);
super.setColorImportance(color_importance);
super.setSpatialImportance(spatial_importance);
std::map<uint32_t, Supervoxel<PointT>::Ptr >supervoxl_clustering;
super.extract(supervoxl_clustering);
cout << "supervoxel number is " << supervoxl_clustering.size() << endl;
ofstream outfile("..//测试数据//supervoxel_缺点.txt", ios::out);
srand((int)time(0));
for (auto label_itr = supervoxl_clustering.cbegin(); label_itr != supervoxl_clustering.cend(); label_itr++)
{
double r = rand() % 255;
double g = rand() % 255;
double b = rand() % 255;
int super_label = label_itr->first;
Supervoxel<PointT>::Ptr super_cloud = supervoxl_clustering.at(super_label);
for (int j = 0; j < super_cloud->voxels_->points.size(); j++)
{
outfile << fixed << setprecision(3) << super_cloud->voxels_->points[j].x << "\t" << super_cloud->voxels_->points[j].y << "\t" << super_cloud->voxels_->points[j].z <<
"\t" << fixed << setprecision(0) << r << "\t" << g << "\t" << b << endl;
}
}
outfile.close();
return 0;
}
可以发现,其实有相当部分的点没有划分到超体素中
(2)第二个问题,PCL中其实是有将没有划分到体素中的点进行单独保存的情况,只是很少有博主对其仔细进行研究。将所有点进行保存,其代码如下:
#include <pcl/console/parse.h>
#include <pcl/point_cloud.h>
#include <pcl/point_types.h>
#include <pcl/visualization/pcl_visualizer.h>
#include <pcl/segmentation/supervoxel_clustering.h>
#include <pcl/filters/statistical_outlier_removal.h>
#include<iostream>
//VTK include needed for drawing graph lines
#include <vtkPolyLine.h>
#include"IO.h"
using namespace pcl;
using namespace std;
typedef PointXYZ PointT;
typedef PointXYZL PointTL;
void main()
{
IO IOExample;
//--------------------加载点云--------------------------
PointCloud<PointT>::Ptr cloud(new PointCloud<PointT>);
char strpath[] = "..//测试数据//desk.txt";
char* path = strpath;
vector<pcl::PointXYZ> allpoints;
allpoints = IOExample.ReadPointXYZIntoVector(path);
cloud->width = allpoints.size();
cloud->height = 1;
cloud->is_dense = false;
cloud->resize(cloud->width * cloud->height);
for (int i = 0; i < allpoints.size(); i++)
{
cloud->points[i].x = allpoints[i].x;
cloud->points[i].y = allpoints[i].y;
cloud->points[i].z = allpoints[i].z;
}
//------------------构建超体素--------------------------
float voxel_resultion = 0.005f;
float seed_resultion = 0.15f;
float color_importance = 0.0f;
float spatial_importance = 0.4f;
float normal_importance = 5.0f;
SupervoxelClustering<PointT> super(voxel_resultion, seed_resultion);
super.setInputCloud(cloud);
super.setNormalImportance(normal_importance);
super.setColorImportance(color_importance);
super.setSpatialImportance(spatial_importance);
std::map<uint32_t, Supervoxel<PointT>::Ptr >supervoxl_clustering;
super.extract(supervoxl_clustering);
pcl::PointCloud<pcl::PointXYZL>::Ptr supervoxel_cloud = super.getLabeledCloud();
int begin = 0;
//遍历体素获取体素内点的索引,根据索引提取分割后的点云
ofstream outfile("..//测试数据//supervoxel_不缺点.txt", ios::out);
struct segpoints
{
vector<pcl::PointXYZ> points;
};
vector<segpoints> allsegs;
allsegs.resize(supervoxl_clustering.size());
vector<pcl::PointXYZ> nonsegs;
for (int i = 0; i < supervoxel_cloud->points.size(); i++)
{
double idlabel = supervoxel_cloud->points[i].label;
pcl::PointXYZ tempoint;
tempoint.x = supervoxel_cloud->points[i].x;
tempoint.y = supervoxel_cloud->points[i].y;
tempoint.z = supervoxel_cloud->points[i].z;
if (idlabel > 0 && idlabel <= supervoxl_clustering.size())
{
allsegs[idlabel - 1].points.push_back(tempoint);
}
else
{
nonsegs.push_back(tempoint);
}
}
//进行保存
for (int i = 0; i < nonsegs.size(); i++)
{
outfile << fixed << setprecision(3) << nonsegs[i].x << "\t" << nonsegs[i].y << "\t" << nonsegs[i].z << "\t" << fixed << setprecision(0) << 255 << "\t" << 255 << "\t" << 255 << endl;
}
for (int i = 0; i < allsegs.size(); i++)
{
double r = rand() % 255;
double g = rand() % 255;
double b = rand() % 255;
for (int j = 0; j < allsegs[i].points.size(); j++)
{
outfile << fixed << setprecision(3) << allsegs[i].points[j].x << "\t" << allsegs[i].points[j].y << "\t" << allsegs[i].points[j].z <<
"\t" << fixed << setprecision(0) << r << "\t" << g << "\t" << b << endl;
}
}
outfile.close();
cout << "结束" << endl;
system("pause");
}
其实可以发现,这样进行超体素划分,体素块之间存在一些将不属于同一面片的点,划分到同一体素当中,这存在明显的。从同一平面上看(左一),相邻面片之间的点全部划分到对应的体素中去了
对于(3)个问题,目前还没有好的解决方案,按照之前博客来说,其包括如下方法,使用近邻点构建体素与体素、体素与单个点之间的关系;或者使用中心点来表示。