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

如何在OpenCV Python中合并单条直线的HoughLinesP坐标?

解决HoughLinesP检测直线重复分段的合并问题

HoughLinesP会将同一条连续直线拆分成多段检测出来(比如文档里的直线被识别成上下两段),要把这些分段合并成单条直线,核心是通过角度一致性和位置距离判断线段是否属于同一直线,再合并端点得到完整直线。

关键判断条件

要判定两条线段属于同一条直线,需要满足:

  • 两条线段的角度(斜率)几乎一致:角度差小于设定阈值(比如4度,即π/45弧度)
  • 两条线段的距离足够近:取其中一条线段的端点,计算到另一条线段的距离,小于像素阈值(比如5像素)
  • 线段的延伸范围重叠:两条线段的x/y坐标范围有重叠,不是完全分离的

实现代码

在你现有代码的基础上,添加合并逻辑:

import cv2
from google.colab.patches import cv2_imshow
import numpy as np

# 辅助函数:计算线段的角度(弧度)
def get_line_angle(x1, y1, x2, y2):
    return np.arctan2(y2 - y1, x2 - x1)

# 辅助函数:计算点到线段的距离
def point_to_line_distance(px, py, x1, y1, x2, y2):
    # 直线的一般式:Ax + By + C = 0
    A = y2 - y1
    B = x1 - x2
    C = x2*y1 - x1*y2
    return abs(A*px + B*py + C) / np.sqrt(A**2 + B**2)

# 辅助函数:合并两条线段的端点,得到覆盖范围最大的线段
def merge_lines(line1, line2):
    x1, y1, x2, y2 = line1[0]
    x3, y3, x4, y4 = line2[0]
    # 取所有x中的最小和最大值,y同理
    min_x = min(x1, x2, x3, x4)
    max_x = max(x1, x2, x3, x4)
    min_y = min(y1, y2, y3, y4)
    max_y = max(y1, y2, y3, y4)
    return np.array([[min_x, min_y, max_x, max_y]])

# 核心合并函数
def merge_duplicate_lines(lines, angle_threshold=np.pi/45, distance_threshold=5):
    if lines is None or len(lines) == 0:
        return []
    
    merged_lines = [lines[0]]
    
    for line in lines[1:]:
        x1, y1, x2, y2 = line[0]
        current_angle = get_line_angle(x1, y1, x2, y2)
        merged = False
        
        # 和已合并的每条直线对比
        for i in range(len(merged_lines)):
            mx1, my1, mx2, my2 = merged_lines[i][0]
            merged_angle = get_line_angle(mx1, my1, mx2, my2)
            
            # 角度差在阈值内,同时考虑反向直线的情况
            if abs(current_angle - merged_angle) < angle_threshold or \
               abs(current_angle - merged_angle - np.pi) < angle_threshold:
                # 计算当前线段端点到合并直线的距离
                dist1 = point_to_line_distance(x1, y1, mx1, my1, mx2, my2)
                dist2 = point_to_line_distance(x2, y2, mx1, my1, mx2, my2)
                
                if dist1 < distance_threshold and dist2 < distance_threshold:
                    # 合并两条线段
                    merged_lines[i] = merge_lines(merged_lines[i], line)
                    merged = True
                    break
        
        if not merged:
            merged_lines.append(line)
    
    return np.array(merged_lines)

# --- 原有预处理代码 ---
img= cv2.imread('page9.png')
gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)

kernel_size = 5
blur_gray = cv2.GaussianBlur(gray,(kernel_size, kernel_size),0)

low_threshold = 50
high_threshold = 150
edges = cv2.Canny(blur_gray, low_threshold, high_threshold)

rho = 1
theta = np.pi / 180
threshold = 25
min_line_length = 150
max_line_gap = 3
line_image = np.copy(img) * 0

lines = cv2.HoughLinesP(edges, rho, theta, threshold, np.array([]),
                    min_line_length, max_line_gap)

# --- 执行合并 ---
merged_lines = merge_duplicate_lines(lines)
print(f"原始检测线段数:{len(lines)},合并后线段数:{len(merged_lines)}")

# 绘制合并后的直线
for line in merged_lines:
    x1, y1, x2, y2 = line[0]
    cv2.line(line_image, (x1, y1), (x2, y2), (0, 255, 0), 2)

# 叠加到原图
result = cv2.addWeighted(img, 0.8, line_image, 1, 0)
cv2_imshow(result)

参数调整说明

  • angle_threshold:角度差阈值,单位弧度,默认4度(π/45),如果直线有轻微倾斜可适当调大
  • distance_threshold:点到直线的距离阈值,单位像素,默认5,文档直线可根据清晰度调整
  • 如果合并效果不理想,可以先调整HoughLinesP的参数(比如增大max_line_gap,让算法先尽量检测连续线段),再用合并逻辑兜底

内容的提问来源于stack exchange,提问作者Zahab

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.21 07:39:55