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

如何用Python+OpenCV分离手写文本与点阵/实线书写线?

问题:手写文本与点阵线条分离的优化方案

我用以下Python脚本尝试分离手写文本和其书写的线条,处理实线时效果不错,但点阵线条识别困难:尝试用膨胀操作把点阵转成实线,但会导致文本粘连,被误识别为水平线。批量处理数千张图时,逐图调整kernel不现实,求解决办法,以及是否有更合适的线条识别方法?

示例图片

点阵线条示例1
点阵线条示例2
点阵线条示例3

原代码

import cv2
file_path = r'image.jpg'
image = cv2.imread(file_path)

# resize image if image is bigger then screen size
print('before Dimensions : ', image.shape)
if image.shape[0] > 1200:
    image = cv2.resize(image, None, fx=0.2, fy=0.2)
print('after Dimensions : ', image.shape)

result = image.copy()
gray = cv2.cvtColor(image,cv2.COLOR_BGR2GRAY)
thresh = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)[1]

# Applying dilation to make lines solid
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (3,3))
dilation = cv2.dilate(thresh, kernel, iterations = 1)

# Detect horizontal lines
horizontal_kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (40,1))
detect_horizontal = cv2.morphologyEx(dilation, cv2.MORPH_OPEN, horizontal_kernel, iterations=2)
cnts = cv2.findContours(detect_horizontal, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if len(cnts) == 2 else cnts[1]
for c in cnts:
    cv2.drawContours(result, [c], -1, (36,255,12), 2)

cv2.imshow('1- gray', gray)
cv2.imshow("2- thresh", thresh)
cv2.imshow("3- detect_horizontal", detect_horizontal)
cv2.imshow("4- result", result)
cv2.waitKey(0)
cv2.destroyAllWindows()

解决方案

针对点阵线条识别+避免文本粘连的问题,给出三个实用优化方向:

1. 用水平定向膨胀替代全方向膨胀

原代码用3x3方形kernel膨胀,会向四个方向扩张导致文本粘连。改用水平细长kernel,只在水平方向连接点阵,不破坏文本的垂直结构:

# 替换原膨胀步骤
horizontal_dilate_kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (5, 1))  # 水平长、垂直窄的kernel
dilation = cv2.dilate(thresh, horizontal_dilate_kernel, iterations=2)

这种方式只会把水平排列的点阵连成线,手写文本的笔画不会被过度粘连,后续水平线检测时误判概率大幅降低。

2. 霍夫直线检测直接识别点阵线条

霍夫直线检测不需要线条连续,天生适配离散点阵的直线识别,完全避开膨胀操作带来的文本干扰:

import numpy as np  # 需要导入numpy

# 替换原水平线检测部分
edges = cv2.Canny(gray, 50, 150)
# 霍夫概率直线检测,参数可根据图片调整
lines = cv2.HoughLinesP(edges, 1, np.pi/180, threshold=30, minLineLength=100, maxLineGap=20)
if lines is not None:
    for line in lines:
        x1, y1, x2, y2 = line[0]
        # 筛选水平线:y坐标差小于阈值,排除垂直线/斜线
        if abs(y1 - y2) < 5:
            cv2.line(result, (x1, y1), (x2, y2), (36,255,12), 2)

参数说明:

  • minLineLength:过滤短线条,排除单文本笔画
  • maxLineGap:允许点阵间的间隙,把离散点连成完整直线
  • abs(y1-y2) <5:确保只保留接近水平的线条

3. 先移除文本区域再处理线条

利用文本和线条的尺寸差异,先从二值图中剔除文本,再处理点阵线条:

import numpy as np  # 需要导入numpy

# 在thresh生成后添加以下代码
# 寻找所有连通区域
cnts = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if len(cnts) == 2 else cnts[1]
# 创建掩码,移除小连通区域(文本)
mask = np.ones(thresh.shape, dtype=np.uint8) * 255
for c in cnts:
    area = cv2.contourArea(c)
    if area < 200:  # 阈值根据实际文本大小调整
        cv2.drawContours(mask, [c], -1, 0, -1)
# 得到移除文本后的二值图
no_text_thresh = cv2.bitwise_and(thresh, thresh, mask=mask)
# 后续对no_text_thresh做膨胀和水平线检测即可

这种方式彻底消除文本干扰,膨胀操作只会作用于线条点阵,完全避免误识别。

方案选择建议

  • 若线条严格水平、文本与线条方向差异大:水平定向膨胀+形态学检测速度最快,适合批量处理;
  • 若点阵间隙大、线条非严格水平:霍夫直线检测鲁棒性最强;
  • 若文本与线条尺寸差异明显:先移除文本的方案最彻底。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.24 19:27:30