基于静态镜头的高效网球场边线检测及多边形拟合技术问询
基于OpenCV实现网球场外部多边形拟合
OpenCV完全支持你要的拟合需求,结合你已经用HoughLinesP实现的边线检测,我们可以通过以下步骤完成网球场外框多边形的拟合:
核心思路
- 分类存储有效边线:把你代码中筛选出的纵向(红色)、横向(绿色)边线单独存储,这些是构成外框的关键直线
- 计算有效交点:网球场外框为四边形,由纵向和横向直线相交得到四个顶点,筛选出符合球场位置逻辑的交点
- 多边形规整拟合:用OpenCV的
approxPolyDP对交点集合进行拟合,得到平滑的外框多边形
修改后的代码实现
import math import numpy as np import cv2 # 计算两条线段的有效交点(仅返回线段范围内的交点) def line_intersection(line1, line2): x1, y1, x2, y2 = line1 x3, y3, x4, y4 = line2 denom = (x1 - x2)*(y3 - y4) - (y1 - y2)*(x3 - x4) if denom == 0: return None # 平行线段无交点 t_num = (x1 - x3)*(y3 - y4) - (y1 - y3)*(x3 - x4) u_num = (x1 - x3)*(y1 - y2) - (y1 - y3)*(x1 - x2) t = t_num / denom u = -u_num / denom if 0 <= t <= 1 and 0 <= u <= 1: x = x1 + t*(x2 - x1) y = y1 + t*(y2 - y1) return (int(x), int(y)) return None # 加载图像 img = cv2.imread("assets/game-frames/clay-m-2012-71-870.jpg") gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) edges = cv2.Canny(gray, 50, 100, apertureSize=3) lines = cv2.HoughLinesP(edges, 2, np.pi/180, 100, minLineLength=20, maxLineGap=10) vertical_lines = [] # 存储纵向边线(红色) horizontal_lines = [] # 存储横向边线(绿色) # 筛选并分类边线 for line in lines: x1, y1, x2, y2 = line[0] length = math.sqrt((x1 - x2)**2 + (y1 - y2)**2) if x1 == x2 or x1 < 10 or y1 < 100: continue # 筛选纵向边线 elif abs((y2 - y1)/(x2 - x1)) > 1.5 and abs((y2 - y1)/(x2 - x1)) < 3 and length > 200: vertical_lines.append((x1, y1, x2, y2)) cv2.line(img, (x1, y1), (x2, y2), (0, 0, 255), 1) # 筛选横向边线 elif abs((y2 - y1)/(x2 - x1)) < 0.02 and ((x1 > 44 and x1 < 685) and (x2 < 685 and x2 > 44)) and ((y1 > 110 and y1 < 120) or (y1 > 400 and y1 < 420)): horizontal_lines.append((x1, y1, x2, y2)) cv2.line(img, (x1, y1), (x2, y2), (0, 255, 0), 1) else: continue # 计算所有纵向与横向线段的交点 intersections = [] for v_line in vertical_lines: for h_line in horizontal_lines: pt = line_intersection(v_line, h_line) if pt is not None: intersections.append(pt) # 去重处理,避免重复交点干扰拟合 intersections = list(set(intersections)) if len(intersections) >=4: # 转换为OpenCV可处理的数组格式 pts = np.array(intersections, dtype=np.int32) # 拟合多边形,epsilon控制拟合精度(取周长的1%) perimeter = cv2.arcLength(pts, True) approx = cv2.approxPolyDP(pts, 0.01*perimeter, True) # 绘制拟合后的蓝色外框多边形 if len(approx) ==4: cv2.polylines(img, [approx], True, (255,0,0), 2) cv2.imshow("Detected Lines & Court Outline", img) cv2.waitKey(0) cv2.destroyAllWindows()
关键说明
- 静态机位下,
approxPolyDP的epsilon参数可以固定,保证低算力消耗 - 交点去重处理避免了重复计算的点影响拟合结果
- 线段交点计算逻辑只保留线段范围内的有效交点,过滤掉延长线的无效交叉
内容的提问来源于stack exchange,提问作者spazznolo
相关产品推荐
相关产品推荐

