OpenCV HoughLines检测直线与足球场实际白线不平行问题求解
问题核心原因
- Canny边缘检测阈值设置不合理,提取的边缘存在断裂、杂点过多的问题,给霍夫变换的输入引入了误差
- 霍夫变换的角度步长
theta = np.pi/50(约3.6度)精度不足,拟合得到的直线角度本身就存在偏差 - 同组直线未做角度对齐:Kmeans聚类只是将线按角度分为两组,没有统一组内所有线的角度,导致同组线角度不一致、不平行
- 代码存在语法错误:
img_with_all_lines = np.copy(2)为无效代码,会直接触发运行报错 - 手动删除、调整交点的硬编码逻辑会进一步放大坐标误差
修复方案
按以下步骤修改代码即可实现直线平行、交点坐标精准的需求:
- 新增图像预处理步骤:对原图做高斯模糊降噪,优化Canny边缘检测参数,保证白线边缘完整连续
- 调优霍夫变换参数:提高角度检测精度,调整阈值平衡检测召回率和准确率
- 新增同组线角度校准逻辑:聚类完成后,将每组内所有线的角度统一替换为组平均角度,保证同组所有线角度完全一致
- 移除硬编码操作交点的逻辑,避免人为误差
修正后可运行代码
import numpy as np import cv2 from collections import defaultdict import sys import math def segment_by_angle_kmeans(lines, k=2, **kwargs): # Define criteria = (type, max_iter, epsilon) default_criteria_type = cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER criteria = kwargs.get('criteria', (default_criteria_type, 10, 1.0)) flags = kwargs.get('flags', cv2.KMEANS_RANDOM_CENTERS) attempts = kwargs.get('attempts', 10) # Get angles in [0, pi] radians angles = np.array([line[0][1] for line in lines]) # Multiply the angles by two and find coordinates of that angle on the Unit Circle pts = np.array([[np.cos(2*angle), np.sin(2*angle)] for angle in angles], dtype=np.float32) # Run k-means if sys.version_info[0] == 2: # python 2.x ret, labels, centers = cv2.kmeans(pts, k, criteria, attempts, flags) else: # python 3.x, syntax has changed. labels, centers = cv2.kmeans(pts, k, None, criteria, attempts, flags)[1:] labels = labels.reshape(-1) # Transpose to row vector # Segment lines based on their label of 0 or 1 segmented = defaultdict(list) for i, line in zip(range(len(lines)), lines): segmented[labels[i]].append(line) segmented = list(segmented.values()) print("Segmented lines into two groups: %d, %d" % (len(segmented[0]), len(segmented[1]))) return segmented def intersection(line1, line2): """ Find the intersection of two lines specified in Hesse normal form. Returns closest integer pixel locations. """ rho1, theta1 = line1[0] rho2, theta2 = line2[0] A = np.array([[np.cos(theta1), np.sin(theta1)], [np.cos(theta2), np.sin(theta2)]]) b = np.array([[rho1], [rho2]]) x0, y0 = np.linalg.solve(A, b) x0, y0 = int(np.round(x0)), int(np.round(y0)) return [[x0, y0]] def segmented_intersections(lines): """ Find the intersection between groups of lines. """ intersections = [] for i, group in enumerate(lines[:-1]): for next_group in lines[i+1:]: for line1 in group: for line2 in next_group: intersections.append(intersection(line1, line2)) return intersections def drawLines(img, lines, color=(0,0,255)): """ Draw lines on an image """ for line in lines: for rho,theta in line: a = np.cos(theta) b = np.sin(theta) x0 = a*rho y0 = b*rho x1 = int(x0 + 1000*(-b)) y1 = int(y0 + 1000*(a)) x2 = int(x0 - 1000*(-b)) y2 = int(y0 - 1000*(a)) cv2.line(img, (x1,y1), (x2,y2), color, 2) if __name__ == '__main__': # 读取图像 img2 = cv2.imread("e:/d.jpg") # 预处理:高斯模糊降噪 blurred = cv2.GaussianBlur(img2, (3,3), 0) # 调整Canny参数,提取完整白线边缘 edges2 = cv2.Canny(blurred, 50, 150) # 优化霍夫线参数,提高角度精度 rho = 1 theta = np.pi/180 thresh = 200 lines = cv2.HoughLines(edges2, rho, theta, thresh) print("Found lines: %d" % (len(lines))) # 聚类线角度为两组 segmented = segment_by_angle_kmeans(lines, 2) # 新增:同组线角度校准,统一为组平均角度,保证平行 for group in segmented: # 计算组内平均角度 avg_theta = np.mean([line[0][1] for line in group]) # 替换组内所有线的角度为平均角度 for line in group: line[0][1] = avg_theta # 计算交点 intersections = segmented_intersections(segmented) # 交点去重 intersections = np.unique(np.array(intersections).reshape(-1,2), axis=0).tolist() img_with_segmented_lines = np.copy(img2) # 绘制两组线 drawLines(img_with_segmented_lines, segmented[0], (255,255,0)) drawLines(img_with_segmented_lines, segmented[1], (0,255,255)) # 绘制交点 for pt in intersections: cv2.circle(img_with_segmented_lines, (pt[0], pt[1]), 5 , (255, 0, 255), -1) cv2.imshow("Result", img_with_segmented_lines) cv2.waitKey() cv2.destroyAllWindows()
内容的提问来源于stack exchange,提问作者payam
相关产品推荐
相关产品推荐

