前言
目的:
本文主要记录如何判断点是否位于ROI区域内部。
常见判断方法:
面积和判断法;
夹角和判断法;
凸多边形判断法;
引射线法;
各判断方法适用性:
面积和判断法、夹角和判断法:
【优势】实现较为简单,效率高,不需要循环所有的roi的边,主要参与计算的是多边形的几个顶点;
【劣势】需要多边形是规则的直边,对弧形的多边形则无法处理;
凸多边形判断法:
【优势】实现不难,就是逆时针判断目标点是否一直在各个相邻点形成的边的左侧;
【劣势】需要循环所有的roi的边;另外对于凹多边形,需要进行补边成凸多边形;
引射线法:
【优势】对roi的形状没有任何限制;
【劣势】需要循环所有的roi的边;
小结:
因为我自己的roi是有弧度的,因此选择引射线法,本文也仅对该方法进行程序实现。
一、“引射线法”是什么?
定义:
从任意位置画一条到目标点的水平线,计算该水平线进出平面边界的次数:
如果两边的进出次数都是偶数(包括0),则点在平面外;
如果是奇数,则点在平面内。点正好落在定点或边界时会出现特殊的判断。
——————————————————————————————————————————————————————
在实际使用时,只需要判断一侧的进出次数即可。
二、实现
1.方法一
设置无穷远处的一个点(横坐标无穷大——大于所有的此时的点即可,纵坐标与目标点相同),作为目标点与远处点的线段端点,查看该线段有roi邻居点连成的线段之间是否相交:
struct Point{
int Point_id;
double x,y;
};
//判断两个线段是否交点
double determinant(double v1, double v2, double v3, double v4){
return (v1*v3-v2*v4);
}
bool intersect(Point aa, Point bb, Point cc, Point dd){
double delta = determinant(bb.x-aa.x, cc.x-dd.x, bb.y-aa.y, cc.y-dd.y);
if ( delta<=(1e-6) && delta>=-(1e-6) ){// delta=0,表示两线段重合或平行
return false;
}
double namenda = determinant(cc.x-aa.x, cc.x-dd.x, cc.y-aa.y, cc.y-dd.y) / delta;
if ( namenda>1 || namenda<0 ){
return false;
}
double miu = determinant(bb.x-aa.x, cc.x-aa.x, bb.y-aa.y, cc.y-aa.y) / delta;
if ( miu>1 || miu<0 ){
return false;
}
return true;
}
int main(){
int m=Polygon.size();//roi边界点数组,假设为eigen库函数
bool flag=false;
//目标点和无穷远构成的线段的两个端点
point_object.x=1;
point_object.y=10;
point_long.x=INT_MAX/2000;
point_long.y=10;
//求解横纵坐标最值
x_map_max=Polygon.row(0).maxCoeff();
x_map_min=Polygon.row(0).minCoeff();
y_map_max=Polygon.row(1).maxCoeff();
y_map_min=Polygon.row(1).minCoeff();
//判断目标点是否在大的roi区域内部
if(point_object.x > x_map_max||point_object.x <x_map_min||point_object.y >y_map_max||point_object.y<y_map_min){
cout<<"点不在大的区域内部"<<endl;
}
else{
for(int i=0,j=m-1; i<m; j=i,i++){
//目标点需在两个坐标点纵坐标之间,提高判断效率;
if( Polygon(1,i)<=point_object.y && point_object.y<Polygon(1,j) || Polygon(1,j)<=point_object.y&& point_object.y<Polygon(1,i) ){
//roi的邻居点构成的线段的两个端点
point_A.x=Polygon(0,i);
point_A.y=Polygon(1,i);
point_B.x=Polygon(0,j);
point_B.y=Polygon(1,j);
//判断两条线段是否相交
if(intersect(point_object,point_long,point_A,point_B)){
flag=!flag;
}//if
}//if
}//for
}
if(flag){
cout<<"点在区域内部..."<<endl;
}
else{
cout<<"点不在区域内部..."<<endl;
}
}
2.方法二
求取与目标点相同的y在roi相邻点的线段L1上的横坐标x,若目标点的x大于上述x,则交点数+1;此方法的思想本质上与方法一相同,只是不需要设置无穷远处的点作为目标点;
只判断一侧的交点数,且只判断y2<y<y1时的x坐标关系。
//部分核心函数
//roi的极值
double x_inside_map_max=inside_map.row(0).maxCoeff();
double x_inside_map_min=inside_map.row(0).minCoeff();
double y_inside_map_max=inside_map.row(1).maxCoeff();
double y_inside_map_min=inside_map.row(1).minCoeff();
bool flag=false;
if(point_object[b].x > x_inside_map_max||point_object[b].x <x_inside_map_min||point_object[b].y >y_inside_map_max||point_object[b].y<y_inside_map_min){
cout<<"点不在inside_map的区域内部..."<<endl;
}else{
for(int i=0,j=m-1; i<m; j=i,i++){
double slope=0;
if(inside_map(1,j)!=inside_map(1,i)){ //确保分母非0
slope =(inside_map(0,j)-inside_map(0,i))/(inside_map(1,j)-inside_map(1,i));
}
else{
slope =(inside_map(0,j)-inside_map(0,i))/(0.00001);
}
bool cond1 = (inside_map(1,j)<point_object[b].y && point_object[b].y<=inside_map(1,i) ) || (inside_map(1,i)<point_object[b].y && point_object[b].y<=inside_map(1,j)); //y在两个点之间
bool cond2 = (inside_map(0,j)<=point_object[b].x)||(inside_map(0,i)<=point_object[b].x); //x在右侧
if( cond1 && cond2 ){
bool cond3 = ( point_object[b].x > (inside_map(0,i) + slope * (point_object[b].y-inside_map(1,i))) ); //两点式直线方程
if(cond3){
flag=!flag; //有交点
}//if
}//if
}//for
if(flag){//交点数标志位变化,证明经过奇次变换,有奇数个交点,点在内部;
cout<<setprecision(15)<<"点在inside_map区域内部..."<<endl;
}else{
cout<<"点不在inside_map区域内部..."<<endl;
}