一.算法效果
通过在PCL可视化界面上绘制2D封闭多边形来提取位于该封闭多边形内部或者外部的点,算法效果如下:
二.算法原理
PNPoly算法
2D多边形框选裁剪点云,实际上可以简化为点和多边形的位置问题,即判断点在多边形内部还是外部。 W. Randolph Franklin提出的PNPoly算法,仅用几行代码就完美的解决了上述问题。
PNPoly算法采用射线法判断点是否在多边形内部。假设待测点在多边形内部,从待测点引出一条水平向右的射线,则射线必然会与多边形有至少一个交点。该射线与多边形第一次相交时将“冲出”多边形,第二次相交将“进入”多边形,依此类推。若射线与多边形有奇数个交点,则该点在多边形内部,反之则在外部。
PNPoly算法核心代码如下:
int pnpoly(int nvert, float *vertx, float *verty, float testx, float testy)
{
int i, j, c = 0;
for (i = 0, j = nvert-1; i < nvert; j = i++) {
if ( ((verty[i]>testy) != (verty[j]>testy)) &&
(testx < (vertx[j]-vertx[i]) * (testy-verty[i]) / (verty[j]-verty[i]) + vertx[i]) )
c = !c;
}
return c;
}
- nvert 代表多边形顶点个数;
- testx, testy代表待测试点的横坐标和纵坐标;
- *vertx,*verty分别指向储存多边形横纵坐标数组的首地址;
- i,j分别代表当前多边形顶点索引;
- c表示点是否在多边形内部,默认为0,即点在多边形外部;
算法分两步判断待测点和多边形的关系:
- 排除那些不相交的边。找出被测试点的纵坐标testy在多边形两个相邻点纵坐标范围之内的边;
- 直线相交性检测。测试待测点test发出的向右的射线是否与多边形两个相邻点构成的直线相交,每相交一次,则对c值取反。判断横线穿越多边形的次数是否为奇数,如果是奇数,此时c值为1,该点在多边形内;如果是偶数,c值为0,点在在多边形外。
直线相交性判断
如图3,直线ab与过p点的水平向右射线相交于点c,可知直线ac斜率与直线ab斜率相等,即
点p发出的水平向右的射线与直线ab相交,则有p点横坐标x<x’,即可获得PNPoly中的第二个判断条件。
三.C++代码实现
1.环境配置
VS2019 + PCL1.12.1
2.C++代码
#include <iostream>
#include "vtkCamera.h"
#include <vtkRenderWindow.h>
#include <pcl/filters/project_inliers.h>
#include <pcl/visualization/mouse_event.h> //鼠标事件
#include <pcl/visualization/keyboard_event.h>//键盘事件
#include <pcl/visualization/pcl_visualizer.h>
#include "point_cloud/include/io/cloud_io.h"
//点云初始化
pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_in(new pcl::PointCloud<pcl::PointXYZ>()); //输入点云
pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_polygon(new pcl::PointCloud<pcl::PointXYZ>); //多边形点云
pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_in_plygon(new pcl::PointCloud<pcl::PointXYZ>); //多边形内部点云
pcl::PointXYZ curP, lastP; //当前点坐标,上一线段终点坐标
bool flag = false; //判断是不是第一次点击
bool isPickingMode = false; //是否为框选模式
unsigned int line_id = 0; //多边形直线索引
pcl::visualization::PCLVisualizer::Ptr interactionCustomizationVis();
//键盘事件
int PNPoly(int poly_sides, double* poly_X, double* poly_Y, double x, double y);
void projectInliers(void*);
void keyboardEventOccurred(const pcl::visualization::KeyboardEvent& event, void* viewer_void);
//鼠标事件
void getScreentPos(double* displayPos, double* world, void* viewer_void);
void mouseEventOccurred(const pcl::visualization::MouseEvent& event, void* viewer_void);