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

如何分离倾斜不均的文本行优化OCR?霍夫变换问题及方案咨询

解决每行倾斜不同的文本OCR去歪与分行问题

问题分析

你要识别的文本块存在每行倾斜程度不一致的情况,整块去歪无法解决部分行的识别问题;尝试用霍夫变换检测行间隔的水平线,结果却只检测到垂直线——核心原因在于霍夫参数未针对水平线优化,且行间隔未必是连续的直线,换用更可靠的分行方法会更高效。

方案一:调整霍夫变换参数,精准检测水平线

你的霍夫变换当前未限定角度范围,导致垂直线被优先检测,修改参数聚焦水平区域即可:

  • 限定检测角度为接近水平的区间(85°-95°,对应弧度np.pi*85/180到np.pi*95/180),过滤垂直线候选
  • 调整minLineLength和maxLineGap:行间隔可能不是完全连续的,适当调大maxLineGap(比如5-10)、减小minLineLength(比如100),适配行间隔的实际宽度
  • 可选:先对原图做水平膨胀,强化行间隔的空白区域,让霍夫更容易检测到连续线

修改后的代码示例:

import cv2
import numpy as np
import imutils

def hough_lines_horizontal(cvImage):
    img = cvImage.copy()
    edges = cv2.Canny(img, 50, 150, apertureSize=3)
    inv = np.invert(edges)
    
    # 限定角度在85-95度(接近水平)
    angle_min = np.pi * 85 / 180
    angle_max = np.pi * 95 / 180
    # 调整参数适配行间隔特征
    linesP = cv2.HoughLinesP(inv, 1, np.pi/180, threshold=100, minLineLength=100, maxLineGap=5)
    
    img2 = cv2.cvtColor(inv, cv2.COLOR_GRAY2BGR)
    if linesP is not None:
        for l in linesP[:, 0]:
            cv2.line(img2, (l[0], l[1]), (l[2], l[3]), (0, 255, 0), 2)
    
    cv2.namedWindow('Resized', cv2.WINDOW_NORMAL)
    cv2.resizeWindow('Resized', 600, 900)
    cv2.imshow("Resized", imutils.resize(img2, width=500))
    cv2.waitKey(0)
    cv2.destroyAllWindows()
    return linesP

方案二:用水平投影法分行(更稳定可靠)

霍夫检测水平线依赖连续空白,而水平投影法直接基于每行的像素分布,更适合分割不规则倾斜的文本行:

  1. 对二值化图像计算水平方向像素投影:统计每一行的白色像素总数
  2. 设定阈值(比如投影值低于总行像素数的5%),找出投影值极低的行,即为行间隔
  3. 根据这些间隔位置,切割出每行文本

代码示例:

def split_lines_by_projection(cvImage):
    img = cvImage.copy()
    # 确保是灰度图,已预处理可跳过
    if len(img.shape) == 3:
        img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    _, binary = cv2.threshold(img, 127, 255, cv2.THRESH_BINARY_INV)
    
    # 计算水平投影
    h, w = binary.shape
    horizontal_proj = np.sum(binary, axis=1)
    # 设定阈值筛选行间隔
    threshold = np.max(horizontal_proj) * 0.05
    line_gaps = np.where(horizontal_proj < threshold)[0]
    
    # 合并连续间隔行,确定分割点
    split_points = []
    start = line_gaps[0]
    for i in range(1, len(line_gaps)):
        if line_gaps[i] - line_gaps[i-1] > 1:
            split_points.append((start + line_gaps[i-1])//2)
            start = line_gaps[i]
    split_points.append((start + line_gaps[-1])//2)
    
    # 切割每行文本,过滤过窄行
    lines = []
    prev = 0
    for point in split_points:
        if point - prev > 5:
            line_img = binary[prev:point, :]
            lines.append(line_img)
        prev = point
    # 处理最后一行
    if h - prev > 5:
        lines.append(binary[prev:h, :])
    
    # 展示分割结果
    for idx, line in enumerate(lines):
        cv2.imshow(f"Line {idx}", imutils.resize(line, width=500))
        cv2.waitKey(0)
    cv2.destroyAllWindows()
    return lines

方案三:单行文独立去歪方法

分割出每行后,用以下方法校正单行文的倾斜:

  1. 最小外接矩形法:找到行文本的轮廓,计算最小外接矩形的旋转角度,旋转校正
  2. 主轴方向法:计算文本轮廓的主轴角度,以此为基准旋转

代码示例(单行文去歪):

def deskew_single_line(line_img):
    # 找到文本轮廓
    contours, _ = cv2.findContours(line_img, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
    if not contours:
        return line_img
    # 合并所有轮廓的外接矩形
    rect = cv2.minAreaRect(np.concatenate(contours))
    angle = rect[2]
    
    # 调整角度:minAreaRect返回角度范围为[-90,0),转换为实际旋转角度
    if angle < -45:
        angle = 90 + angle
    # 旋转图像校正倾斜
    (h, w) = line_img.shape[:2]
    center = (w // 2, h // 2)
    M = cv2.getRotationMatrix2D(center, angle, 1.0)
    deskewed = cv2.warpAffine(line_img, M, (w, h), flags=cv2.INTER_CUBIC, borderMode=cv2.BORDER_REPLICATE)
    return deskewed

整体流程建议

  1. 用水平投影法分割每行文本(比霍夫变换更稳定)
  2. 对每行文本分别用最小外接矩形法去歪
  3. 对校正后的单行文本执行OCR识别

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.16 09:53:13