1、背景介绍
很多边缘提取算法提取的边缘点为无序点云,如下图所示,无序点云不利于后续各种应用,比如根据边缘计算点云面积、点云轮廓线规则化等。若对点云进行排序,则可以进行上述应用。但实际上,点云形状错综复杂,对点云进行排序难度会比较大。此次博客介绍一种针对简单形状点云的边缘点排序原理,对于复杂形状点云,仅供参考。
边缘点(红色) | 无序边缘点连接 | 有序边缘点连接 |
图 1 边缘点无序与有序示例 |
2、原理介绍
对于形状简单的点云,可以使用与某一方向的夹角,按照夹角大小进行排序,实现点的排序,如下图所示。
图 2 轮廓点与起始方向夹角示意图
对点进行排序的步骤如下:
(1)中心计算:统计所有点的平均坐标作为中心
(2)计算旋转角:以y轴为起始方向,计算每个轮廓点与y轴的夹角,得到夹角集合
(3)角度排序:对角度从小到大进行排序,按照排序后的角度对轮廓点进行排序,即实现角度排序
****在对角度排序时,使用for循环找到角度停止即可,可以提高排序效率。
for (int i = 0; i < sortedangles.size(); i++)//依据角度寻找点
{
for (int j = 0; j < allangles.size(); j++)
{
if (sortedangles[i] == allangles[j])
{
result.push_back(unorderpt[j]);
break;//终止
}
}
}
3、测试
源代码下载链接:轮廓点排序程序(c++ )
本程序在PCL配置好的环境下进行运行,只需要将配置好的环境下,将利用旋转角对点排序.cpp添加到源文件目录下运行即可。
3.1 可视化未排序前结果
测试主函数代码如下:
//未排序前的点,进行连接
void main()
{
//(1)读入边缘点
vector<pcl::PointXYZ> boundpts;
string filepath = "D:\\testdata\\testdata_01_boud.txt";
std::ifstream pointsFile(filepath, std::ios::in);
pcl::PointXYZ point;
char line[128];
double x, y, z;
int r, g, b;
while (pointsFile.getline(line, sizeof(line)))
{
std::stringstream word(line);
word >> x;
word >> y;
word >> z;
word >> r;
word >> g;
word >> b;
point.x = x;
point.y = y;
point.z = z;
if (r == 255 && g == 0 && b == 0)//边缘点(为红色点)
{
pcl::PointXYZ onept;
onept.x = x;
onept.y = y;
onept.z = z;
boundpts.push_back(onept);
}
}
pcl::visualization::PCLVisualizer viewer("point connect");
viewer.setBackgroundColor(0, 0, 0);
//(2)可视化
pcl::PointCloud<pcl::PointXYZ>::Ptr cloud(new pcl::PointCloud<pcl::PointXYZ>);
cloud->width = boundpts.size();
cloud->height = 1;
cloud->is_dense = false;
cloud->resize(cloud->width*cloud->height);
for (int i = 0; i < cloud->width; i++)
{
cloud->points[i].x = boundpts[i].x;
cloud->points[i].y = boundpts[i].y;
cloud->points[i].z = boundpts[i].z;
}
//(3)增加点
pcl::visualization::PointCloudColorHandlerCustom<pcl::PointXYZ> single_color(cloud, 255, 0, 0);
viewer.addPointCloud<pcl::PointXYZ>(cloud, single_color, "sample cloud");
viewer.setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_POINT_SIZE, 3, "sample cloud");
//(4)增加多边形
pcl::PlanarPolygon<pcl::PointXYZ> polygon;
pcl::PointCloud<pcl::PointXYZ> contour;
contour.width = boundpts.size();
contour.height = 1;
contour.is_dense = false;
contour.resize(contour.height*contour.width);
for (int i = 0; i < boundpts.size(); i++)
{
contour.points[i] = boundpts[i];
}
polygon.setContour(contour);
viewer.addPolygon(polygon, 0, 255, 0, "ploygon", 0);
while (!viewer.wasStopped())
{
viewer.spinOnce(1);
}
}
无序点连接可视化结果如下,可以发现点之间无序,其顺序与原始点云文件中点的顺序有关,影响厚后续处理。
3.2 可视化排序后点
测试主函数代码如下:
//测试排序后点,将其连接起来
void main()
{
//(1)读入边缘点
vector<pcl::PointXYZ> boundpts;
string filepath = "D:\\testdata\\testdata_01_boud.txt";
std::ifstream pointsFile(filepath, std::ios::in);
pcl::PointXYZ point;
char line[128];
double x, y, z;
int r, g, b;
while (pointsFile.getline(line, sizeof(line)))
{
std::stringstream word(line);
word >> x;
word >> y;
word >> z;
word >> r;
word >> g;
word >> b;
point.x = x;
point.y = y;
point.z = z;
if (r == 255 && g == 0 && b == 0)//边缘点(为红色点)
{
pcl::PointXYZ onept;
onept.x = x;
onept.y = y;
onept.z = z;
boundpts.push_back(onept);
}
}
//(2)对边缘点排序
vector<pcl::PointXYZ> orderpts = Orderpts(boundpts);
//(3)对点进行连接,测试点是否排序了
pcl::visualization::PCLVisualizer viewer("point connect");
viewer.setBackgroundColor(0, 0, 0);
//(3)可视化
pcl::PointCloud<pcl::PointXYZ>::Ptr cloud(new pcl::PointCloud<pcl::PointXYZ>);
cloud->width = orderpts.size();
cloud->height = 1;
cloud->is_dense = false;
cloud->resize(cloud->width*cloud->height);
for (int i = 0; i < cloud->width; i++)
{
cloud->points[i].x = orderpts[i].x;
cloud->points[i].y = orderpts[i].y;
cloud->points[i].z = orderpts[i].z;
}
//(4)增加点
pcl::visualization::PointCloudColorHandlerCustom<pcl::PointXYZ> single_color(cloud, 255, 0, 0);
viewer.addPointCloud<pcl::PointXYZ>(cloud, single_color, "sample cloud");
viewer.setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_POINT_SIZE, 3, "sample cloud");
//(5)增加多边形
pcl::PlanarPolygon<pcl::PointXYZ> polygon;
pcl::PointCloud<pcl::PointXYZ> contour;
contour.width = orderpts.size();
contour.height = 1;
contour.is_dense = false;
contour.resize(contour.height*contour.width);
for (int i = 0; i < orderpts.size(); i++)
{
contour.points[i] = orderpts[i];
}
polygon.setContour(contour);
viewer.addPolygon(polygon, 0, 255, 0, "ploygon", 0);
while (!viewer.wasStopped())
{
viewer.spinOnce(1);
}
}
可视化结果如下,可以发现点被有序进行排列,是有序形式
4、小结
基于旋转角度进行点云排序的方法,对于图形简单的点云是有效的,比如圆、长方形、三角形等,对于复杂的,如有孔洞形式、有凹凸形式,则该方法不适用。