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

如何用OpenCV优化HoughLinesP检测手写文档的下划线与方框?

检测文档中的下划线与方框问题

问题背景

需要检测下图中的下划线与方框:
原始文档图

预期检测效果如下:
预期检测效果

已尝试的代码

import cv2
import numpy as np

# Load image
img = cv2.imread('document.jpg')

# Convert image to grayscale
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

# Apply Gaussian blur to reduce noise
blur = cv2.GaussianBlur(gray, (3, 3), 0)

# Threshold the image
ret, thresh = cv2.threshold(blur, 127, 255, cv2.THRESH_TRUNC)

# Apply Canny Edge Detection
edges = cv2.Canny(thresh, 155, 200)

# Use HoughLinesP to detect lines
lines = cv2.HoughLinesP(edges, rho=1, theta=1*np.pi/180, threshold=100, minLineLength=100, maxLineGap=50)

# Draw lines on image
for line in lines:
    x1, y1, x2, y2 = line[0]
    cv2.line(img, (x1, y1), (x2, y2), (0, 0, 255), 4)

当前不理想的结果

当前检测结果

我的思考

  • 考虑结合Otsu自适应阈值处理,为Canny边缘检测提供更合适的二值图,但当前cv2.threshold()已能较好分离文档内容与背景;
  • 若能让HoughLinesP输出平滑线条而非杂乱结果,可通过线条交点(或近交点)检测方框,通过判断线条两端y坐标范围检测下划线。

核心疑问

  1. 如何让HoughLinesP输出平滑线条,以便推进方框与下划线的检测?
  2. 我提出的检测方案是否高效?OpenCV有无更优实现方式?

解决方案建议

1. 优化HoughLinesP输出平滑线条

HoughLinesP产生杂乱线条的核心原因是边缘噪声和参数适配问题,可从以下方向调整:

  • 调整Canny参数:降低低阈值(比如cv2.Canny(thresh, 50, 150)),减少有效边缘的丢失;同时增大高斯模糊核(比如(5,5)),过滤细微噪声。
  • 优化HoughLinesP参数:
    • 提高minLineLength(比如设为150),过滤短杂线;
    • 缩小maxLineGap(比如设为20),让位置相近的线段更易合并为完整线条;
    • 微调threshold(比如设为80),平衡检测灵敏度与准确性;
  • 线条后处理:对检测到的线段进行聚类合并,将角度差小于5°、位置相邻的线段拟合为一条完整直线,剔除零散杂线。

2. 更高效的针对性检测方案

针对文档中的下划线和方框,无需依赖Hough变换,OpenCV有更直接高效的实现方式:

  • 下划线检测:
    利用形态学操作的水平结构元素,创建长条形水平核kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (50,1)),通过cv2.morphologyEx(thresh, cv2.MORPH_OPEN, kernel)提取水平线条,再通过轮廓检测筛选符合长度、宽度特征的下划线。
  • 方框检测:
    直接使用cv2.findContours()提取轮廓,配合cv2.approxPolyDP()进行轮廓近似,筛选出四边形轮廓,再通过面积、长宽比判断是否为目标方框——这种方法比Hough变换更适配文档中规则闭合方框的检测场景。

优化后的示例代码

import cv2
import numpy as np

# 加载图像
img = cv2.imread('document.jpg')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

# 自适应阈值处理(替代固定阈值,鲁棒性更强)
thresh = cv2.adaptiveThreshold(gray, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY_INV, 11, 2)

# ---------------------- 检测下划线 ----------------------
# 创建水平结构元素
horizontal_kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (60, 1))
# 提取水平线条
horizontal_lines = cv2.morphologyEx(thresh, cv2.MORPH_OPEN, horizontal_kernel, iterations=2)
# 查找轮廓并绘制下划线
cnts = cv2.findContours(horizontal_lines, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)[0]
for c in cnts:
    x, y, w, h = cv2.boundingRect(c)
    if w > 50 and h < 5:  # 筛选符合下划线尺寸的轮廓
        cv2.rectangle(img, (x, y), (x + w, y + h), (0, 0, 255), 2)

# ---------------------- 检测方框 ----------------------
# 创建垂直结构元素辅助提取边框
vertical_kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (1, 60))
vertical_lines = cv2.morphologyEx(thresh, cv2.MORPH_OPEN, vertical_kernel, iterations=2)
# 合并水平和垂直线条得到边框掩码
box_mask = cv2.bitwise_or(horizontal_lines, vertical_lines)
# 查找轮廓
cnts = cv2.findContours(box_mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)[0]
for c in cnts:
    perimeter = cv2.arcLength(c, True)
    approx = cv2.approxPolyDP(c, 0.02 * perimeter, True)
    if len(approx) == 4:  # 筛选四边形轮廓
        x, y, w, h = cv2.boundingRect(approx)
        if w > 100 and h > 50:  # 筛选符合方框尺寸的轮廓
            cv2.rectangle(img, (x, y), (x + w, y + h), (0, 255, 0), 2)

# 显示结果
cv2.imshow('Result', img)
cv2.waitKey(0)
cv2.destroyAllWindows()

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.02 20:15:55