You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

OpenCV HoughLines检测直线与足球场实际白线不平行问题求解

问题核心原因

  • Canny边缘检测阈值设置不合理,提取的边缘存在断裂、杂点过多的问题,给霍夫变换的输入引入了误差
  • 霍夫变换的角度步长theta = np.pi/50(约3.6度)精度不足,拟合得到的直线角度本身就存在偏差
  • 同组直线未做角度对齐:Kmeans聚类只是将线按角度分为两组,没有统一组内所有线的角度,导致同组线角度不一致、不平行
  • 代码存在语法错误:img_with_all_lines = np.copy(2)为无效代码,会直接触发运行报错
  • 手动删除、调整交点的硬编码逻辑会进一步放大坐标误差

修复方案

按以下步骤修改代码即可实现直线平行、交点坐标精准的需求:

  1. 新增图像预处理步骤:对原图做高斯模糊降噪,优化Canny边缘检测参数,保证白线边缘完整连续
  2. 调优霍夫变换参数:提高角度检测精度,调整阈值平衡检测召回率和准确率
  3. 新增同组线角度校准逻辑:聚类完成后,将每组内所有线的角度统一替换为组平均角度,保证同组所有线角度完全一致
  4. 移除硬编码操作交点的逻辑,避免人为误差

修正后可运行代码

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.28 10:27:06