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



原代码
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
相关产品推荐
相关产品推荐

