[math]判断一个点是否在多边形内的方法

本文介绍了几种用于判断点是否位于多边形内部的方法,包括向量叉乘法、面积和法、夹角和法、引射线法及PNPoly算法。探讨了各种方法的适用场景和具体实现细节。


多边形的内角是由两条相邻边形成边界之内的角;如果一个多边形的所有内角均小于180°,则该多边形为凸(convex)多边形;否则为凹(concave)多边形:

  • 凸多边形的任意两个顶点间的连线位于多边形内部或边上;
  • 按顺序计算向量叉积:凸多边形均同号,凹多边形不同号。

向量叉乘判别法

向量叉乘法,只对非凹多边形有效,对于凹多边形需要切割为凸多边形。

设多边形的顶点依次为A1, A2 … An,要判断的点为P,那么:

  • 分别计算向量PA1叉乘向量PA2,向量PA2叉乘向量PA3,…,向量PA(n-1)叉乘向量PAn,以及向量PAn叉乘向量PA1;
  • 若这些叉乘的结果都同向的话,那么这个点就在多边形的内部。

面积和判别法

判断目标点与多边形的每条边组成的三角形面积和,是否等于该多边形,相等则在多边形内部。

设多边形的顶点依次为1,2,...,n,要判断的点为P。用P点连接多边形各顶点:

  • 若P点在多边形以内,则P与各顶点组成的三角形正好填充此多边形;反之,则不能。
  • 使用多边形面积计算公式,计算面积S(根据叉乘原理):
    S = 1 2 ∣ ∑ i = 1 n ( x i ∗ y i + 1 − x i + 1 y i ) ∣ S=\frac{1}{2}\left|\sum\limits_{i=1}^n(x_i*y_{i+1} - x_{i+1}y_i)\right| S=21 i=1n(xiyi+1xi+1yi)
import sensor import time import math # 初始化摄像头 sensor.reset() sensor.set_pixformat(sensor.RGB565) sensor.set_framesize(sensor.QQVGA) sensor.skip_frames(time=2000) # 设置二值化阈值(根据实际图像调整) threshold = (0, 60) # 黑色目标 # 判断两个角度是否接近直角(90度) def is_right_angle(angle_deg, threshold=10): return abs(angle_deg - 90) < threshold # 计算两个向量之间的夹角 def angle_between_vectors(v1, v2): unit_v1 = v1 / math.hypot(v1[0], v1[1]) unit_v2 = v2 / math.hypot(v2[0], v2[1]) dot_product = sum(vi * wi for vi, wi in zip(unit_v1, unit_v2)) angle_rad = math.acos(max(-1.0, min(1.0, dot_product))) return math.degrees(angle_rad) # 查找最小的正方形 def find_smallest_square(contours): min_area = float('inf') best_square = None for cnt in contours: # 逼近多边形 approx = cnt.approx(5) if len(approx) != 4: continue # 不是四边形 # 获取四个角点 pts = [tuple(pt) for pt in approx] # 计算四条边的向量长度 edges = [ (pts[1][0] - pts[0][0], pts[1][1] - pts[0][1]), (pts[2][0] - pts[1][0], pts[2][1] - pts[1][1]), (pts[3][0] - pts[2][0], pts[3][1] - pts[2][1]), (pts[0][0] - pts[3][0], pts[0][1] - pts[3][1]) ] lengths = [math.hypot(edge[0], edge[1]) for edge in edges] # 检查四条边的长度是否相等(误差范围内) length_tolerance = 5 if all(abs(lengths[i] - lengths[(i + 1) % 4]) < length_tolerance for i in range(4)): # 检查四个角是否接近直角 angles = [ angle_between_vectors(edges[0], edges[1]), angle_between_vectors(edges[1], edges[2]), angle_between_vectors(edges[2], edges[3]), angle_between_vectors(edges[3], edges[0]) ] if all(is_right_angle(angle) for angle in angles): area = cnt.area() if area < min_area: min_area = area best_square = cnt return best_square while True: img = sensor.snapshot() binary = img.binary([threshold]) # 寻找所有轮廓 contours = img.find_contours(threshold=50) # 筛选最小的正方形 square = find_smallest_square(contours) if square: # 获取最小外接矩形 rect = square.rect() x, y, w, h = rect side_length = min(w, h) # 取最小的边作为边长 print("找到正方形!边长:", side_length, "像素") img.draw_rectangle(rect, color=(255, 0, 0), thickness=2) # 绘制红色矩形框 # 显示边长信息 img.draw_string(x, y - 10, "L: {}px".format(side_length), color=(255, 0, 0)) # 显示调试信息(可选) for i, pt in enumerate(square.approx(5)): img.draw_circle(int(pt[0]), int(pt[1]), 3, color=(0, 255, 0), fill=True)可以把边长化成厘米单位吗
08-01
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值