OpenCV验证码图像轮廓绘制:紧密数字分割难题求助
解决紧密数字的分割问题
针对紧密排列数字导致轮廓粘连的问题,提供两种可靠的分割方案:
方法1:形态学腐蚀(适用于数字间有细微间隙的场景)
通过横向腐蚀缩小数字宽度,断开粘连部分后再提取轮廓:
import cv2 import numpy as np # 读取图像 image = cv2.imread("622174.png") gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) # 二值化(黑底白字用THRESH_BINARY_INV,白底黑字改用THRESH_BINARY) thresh = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY_INV)[1] # 创建1x3的横向腐蚀核,控制腐蚀程度 kernel = np.ones((1,3), np.uint8) eroded = cv2.erode(thresh, kernel, iterations=1) # 提取最外层轮廓 contours, hierarchy = cv2.findContours(eroded, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) # 绘制结果 result = image.copy() cv2.drawContours(result, contours, -1, (0,255,0), 1) cv2.imwrite("split_result.png", result)
方法2:垂直投影分割(不受粘连程度影响的通用方案)
通过计算每列的像素密度,找到数字间的空白列作为分割边界:
import cv2 import numpy as np image = cv2.imread("622174.png") gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) thresh = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY_INV)[1] # 计算垂直投影:统计每列的白色像素数量 vertical_proj = np.sum(thresh, axis=0) # 定位空白列(投影值为0的列) split_points = np.where(vertical_proj == 0)[0] # 提取连续的非空白列区间,对应单个数字 digit_intervals = [] start_col = None for col in range(len(vertical_proj)): if vertical_proj[col] > 0 and start_col is None: start_col = col elif vertical_proj[col] == 0 and start_col is not None: digit_intervals.append((start_col, col)) start_col = None # 处理最后一个数字的收尾 if start_col is not None: digit_intervals.append((start_col, len(vertical_proj))) # 分割并保存每个数字,同时在原图绘制分割框 for idx, (x1, x2) in enumerate(digit_intervals): single_digit = thresh[:, x1:x2] cv2.imwrite(f"digit_{idx}.png", single_digit) cv2.rectangle(image, (x1, 0), (x2, image.shape[0]), (0,255,0), 1) cv2.imwrite("split_with_boxes.png", image)
关键注意点
- 二值化模式要匹配图像底色:黑底白字用
THRESH_BINARY_INV,白底黑字用THRESH_BINARY,同时垂直投影需对应计算黑色像素数量(即np.sum(255 - thresh, axis=0))。 - 腐蚀核的大小需根据粘连程度调整,过大的核会破坏数字结构。
- 垂直投影方法只要数字间存在垂直空白区域(哪怕极窄),就能实现精准分割。
内容的提问来源于stack exchange,提问作者Alex Key
相关产品推荐
相关产品推荐

