python

关注公众号 jb51net

关闭
首页 > 脚本专栏 > python > Python OpenCV验证码字符分割

Python基于OpenCV实现验证码字符分割的方案

作者:.笑对人生.

在自动化验证码识别流程中,字符分割是连接图片预处理与字符识别的关键环节,本文将详细解析一段基于 OpenCV 的验证码分割代码,该代码通过灰度处理、边缘优化、轮廓检测等步骤,精准提取验证码中的单个字符,需要的朋友可以参考下

一、引言

在自动化验证码识别流程中,字符分割是连接图片预处理与字符识别的关键环节。本文将详细解析一段基于 OpenCV 的验证码分割代码,该代码通过灰度处理、边缘优化、轮廓检测等步骤,精准提取验证码中的单个字符,同时兼顾灵活性与细节保留,适用于多数简单印刷体验证码场景。

本文用到验证码的图片

样例:

二、核心功能拆解

该代码的核心目标是将一张包含多个字符的验证码图片,分割为单个字符图片并保存。整体流程遵循 “预处理→特征提取→筛选→分割” 的技术路径,具体包含 6 个关键步骤:

三、代码逐段深度解析

1. 函数入口与图片读取

def split_picture(imagepath):
    # 读取图片:以灰度模式(0)读取,减少色彩干扰
    gray = cv2.imread(imagepath, 0)
    if gray is None:
        print(f"错误:无法读取图片,请检查路径 '{imagepath}' 是否正确")
        return

2. 边缘噪声优化

# 边缘处理:仅轻微处理边缘(避免过度裁剪字符)
height, width = gray.shape
# 只处理最外层1像素,避免大面积修改边缘导致字符丢失
for i in range(width):
    gray[0, i] = 255
    gray[height - 1, i] = 255
for j in range(height):
    gray[j, 0] = 255
    gray[j, width - 1] = 255

3. 中值滤波去噪

# 中值滤波:减小模板(避免模糊字符细节)
blur = cv2.medianBlur(gray, 3)  # 3*3模板,保留更多字符细节

4. 二值化处理

# 二值化:降低阈值(确保字符被正确识别为黑色)
# 尝试更低的阈值(150),避免字符因亮度问题被误判为背景
ret, thresh1 = cv2.threshold(blur, 150, 255, cv2.THRESH_BINARY)

5. 轮廓检测与信息打印

# 查找轮廓:保留所有轮廓(包括内部结构,避免漏检)
contours, hierarchy = cv2.findContours(thresh1, cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)
 
# 打印所有轮廓信息(方便调试)
print("所有轮廓信息(x, y, 宽, 高, 面积):")
for cnt in contours:
    x, y, w, h = cv2.boundingRect(cnt)
    area = w * h
    print(f"轮廓:x={x}, y={y}, 宽={w}, 高={h}, 面积={area}, 宽高比={w / h:.2f}")

6. 有效轮廓筛选

# 大幅放宽过滤条件(优先确保目标轮廓被保留)
valid_contours = []
for cnt in contours:
    x, y, w, h = cv2.boundingRect(cnt)
    area = w * h
    # 边缘排除:几乎不排除(只排除紧贴边缘的1像素)
    if x < 1 or y < 1 or x + w > width - 1 or y + h > height - 1:
        continue
    # 面积范围:覆盖之前的大轮廓(上限提高到40000,下限200)
    if area < 200 or area > 40000:
        continue
    # 宽高比:允许更宽的字符(如M)
    if w / h < 0.3 or w / h > 3:
        continue
    valid_contours.append((x, y, w, h))

7. 排序与分割保存

# 按x坐标排序(确保字符顺序与验证码一致)
valid_contours.sort(key=lambda c: c[0])
 
# 输出结果并保存
if not valid_contours:
    print("\n未找到有效字符区域!请根据上面的轮廓信息进一步放宽条件。")
else:
    print(f"\n共找到 {len(valid_contours)} 个有效区域:")
    for i, (x, y, w, h) in enumerate(valid_contours, 1):
        print(f"有效区域 {i}:(x={x}, y={y}, 宽={w}, 高={h})")
        # 裁剪并保存单个字符
        cv2.imwrite(f'char{i}.jpg', thresh1[y:y + h, x:x + w])

8. 主函数调用

def main():
    imagepath = 'Verification_Code.png'
    split_picture(imagepath)
 
if __name__ == "__main__":
    main()

四、关键优化点总结

相比传统验证码分割代码,该实现的核心优势在于 “保留细节 + 灵活可调”,具体优化点如下:

  1. 最小化边缘处理:仅处理最外层 1 像素,避免字符裁剪,适配边缘字符场景。
  2. 小模板滤波:3*3 中值滤波在去噪的同时,最大程度保留字符细节(如细线条、拐角)。
  3. 低阈值二值化:150 的阈值适配亮度不均的验证码,减少字符误判。
  4. 宽松筛选条件:大面积范围(200-40000)和宽高比(0.3-3),兼容多数验证码类型,降低用户调试成本。
  5. 调试友好:打印所有轮廓信息,方便用户根据实际场景调整参数(如某类验证码字符面积偏小,可降低面积下限)。

五、使用说明与调试建议

1. 基础使用步骤

  1. 安装 OpenCV 库:执行pip install opencv-python
  2. 将验证码图片命名为Verification_Code.png,与代码放在同一目录;或修改main()中的imagepath为实际路径。
  3. 运行代码,分割后的字符图片会以char1.jpgchar2.jpg等名称保存在当前目录。

2. 常见问题调试

六、完整Python代码展示

import cv2
 
 
def split_picture(imagepath):
    # 读取图片
    gray = cv2.imread(imagepath, 0)
    if gray is None:
        print(f"错误:无法读取图片,请检查路径 '{imagepath}' 是否正确")
        return
 
    # 边缘处理:仅轻微处理边缘(避免过度裁剪字符)
    height, width = gray.shape
    # 只处理最外层1像素,避免大面积修改边缘导致字符丢失
    for i in range(width):
        gray[0, i] = 255
        gray[height - 1, i] = 255
    for j in range(height):
        gray[j, 0] = 255
        gray[j, width - 1] = 255
 
    # 中值滤波:减小模板(避免模糊字符细节)
    blur = cv2.medianBlur(gray, 3)  # 3*3模板,保留更多字符细节
 
    # 二值化:降低阈值(确保字符被正确识别为黑色)
    # 尝试更低的阈值(150),避免字符因亮度问题被误判为背景
    ret, thresh1 = cv2.threshold(blur, 150, 255, cv2.THRESH_BINARY)
 
    # 查找轮廓:保留所有轮廓(包括内部结构,避免漏检)
    contours, hierarchy = cv2.findContours(thresh1, cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)
 
    # 打印所有轮廓信息(方便调试)
    print("所有轮廓信息(x, y, 宽, 高, 面积):")
    for cnt in contours:
        x, y, w, h = cv2.boundingRect(cnt)
        area = w * h
        print(f"轮廓:x={x}, y={y}, 宽={w}, 高={h}, 面积={area}, 宽高比={w / h:.2f}")
 
    # 大幅放宽过滤条件(优先确保目标轮廓被保留)
    valid_contours = []
    for cnt in contours:
        x, y, w, h = cv2.boundingRect(cnt)
        area = w * h
        # 边缘排除:几乎不排除(只排除紧贴边缘的1像素)
        if x < 1 or y < 1 or x + w > width - 1 or y + h > height - 1:
            continue
        # 面积范围:覆盖之前的大轮廓(上限提高到40000,下限200)
        if area < 200 or area > 40000:
            continue
        # 宽高比:允许更宽的字符(如M)
        if w / h < 0.3 or w / h > 3:
            continue
        valid_contours.append((x, y, w, h))
 
    # 按x坐标排序
    valid_contours.sort(key=lambda c: c[0])
 
    # 输出结果
    if not valid_contours:
        print("\n未找到有效字符区域!请根据上面的轮廓信息进一步放宽条件。")
    else:
        print(f"\n共找到 {len(valid_contours)} 个有效区域:")
        for i, (x, y, w, h) in enumerate(valid_contours, 1):
            print(f"有效区域 {i}:(x={x}, y={y}, 宽={w}, 高={h})")
            cv2.imwrite(f'char{i}.jpg', thresh1[y:y + h, x:x + w])
 
 
def main():
    imagepath = 'Verification_Code.png'
    split_picture(imagepath)
 
 
if __name__ == "__main__":
    main()

程序运行截图如下:

七、总结

本文详细解析了一个基于OpenCV的验证码字符分割方案。该方案通过灰度转换、边缘处理、中值滤波、二值化、轮廓检测和筛选等步骤,实现验证码字符的精准分割。核心特点包括:仅处理1像素边缘保护字符完整性、3×3中值滤波保留细节、150低阈值二值化适应亮度不均、宽松筛选条件(面积200-40000,宽高比0.3-3)兼容多种字符类型。代码提供调试信息输出功能,便于参数调整,适用于多数简单验证码场景,最终按x坐标排序输出分割后的字符图片。

以上就是Python基于OpenCV实现验证码字符分割的方案的详细内容,更多关于Python OpenCV验证码字符分割的资料请关注脚本之家其它相关文章!

您可能感兴趣的文章:
阅读全文