python简单验证码识别

本文是使用PIL+pytesseract识别简单验证码,目的是要识别红色字体,思路如下:
话不多说,直接上代码
#! -*- encoding:utf-8 -*-
"""
# author: ****
# date  : 2018.7.10
# desc  : 图片验证码识别
"""
import requests
from PIL import Image
import pytesseract
import os

img_type = ".png"  # 验证码图片类型
default_name = "verifying_code" + img_type  # 默认验证码名称
default_out_name = "test_" + "verifying_code" + img_type  # 默认处理干扰像素后的验证码名称
fp = "image/"  # 默认验证码图片路径


# 下载验证码
def downloads_pic(pic_name=default_name):
    url = 'http://www.bhi.com.cn/Public/Isvalid.ashx'
    res = requests.get(url, stream=True)
    with open("image/" + pic_name, 'wb') as f:
        for chunk in res.iter_content(chunk_size=1024):
            if chunk:  # filter out keep-alive new chunks
                f.write(chunk)
                f.flush()
        f.close()


# 二值化的图片识别,返回字符串
def img_to_str(image_name=default_out_name):
    """
    二值化的图片识别,返回字符串
    :param image_name: 图片名称
    :return: 验证码字符串
    """
    bname = fp + 'b' + image_name  # + img_type
    # 识别
    pytesseract.pytesseract.tesseract_cmd = 'D:\\Program Files\\Tesseract-OCR\\tesseract.exe'
    tessdata_dir_config = '--tessdata-dir "D:\\Program Files\\Tesseract-OCR\\tessdata"'
    text = pytesseract.image_to_string(Image.open(bname), config=tessdata_dir_config)
    r_text = []
    for t in list(text):
        if t.strip():
            r_text.append(t.strip())
    return "".join(r_text)


# 去除非红色干扰像素,将非红色的点替换成白色
def noise_reduction(image_name=default_name):
    """
    去除非红色干扰像素,将非红色的点替换成白色
    :param image_name: 验证码图片名称
    :return: 无
    """
    fp_img = fp + image_name  # + img_type  # 待处理的图片
    img = Image.open(fp_img)  # 读取系统的内照片
    width = img.size[0]  # 宽度
    height = img.size[1]  # 高度
    for i in range(0, height):  # 遍历所有宽度的点
        # datas = []
        for j in range(0, width):  # 遍历所有高度的点
            data = img.getpixel((j, i))  # 打印该图片的所有点
            if data < 210 or data > 230:  # 筛选非红色像素值
                img.putpixel((j, i), 251)
            # datas.append(data)  # 打印每个像素点的颜色像素值
        # print(datas)  # 查看干扰像素
    img = img.convert("RGB")  # 把图片强制转成RGB
    img.save(fp + default_out_name)  # 保存修改像素点后的图片


# 根据噪点的位置信息,消除二值图片的黑点噪声
def remove_noise_pixel(img, noise_point_list):
    """
    根据噪点的位置信息,消除二值图片的黑点噪声
    :type img:Image
    :param img:
    :param noise_point_list:
    :return:
    """
    for item in noise_point_list:
        img.putpixel((item[0], item[1]), 1)


# 获取干净的二值化的图片
def get_clear_bin_image(image_name=default_out_name):
    """
    获取干净的二值化的图片。
    图像的预处理:
    1. 先转化为灰度
    2. 再二值化
    3. 然后清除噪点
    参考:http://python.jobbole.com/84625/
    :type image_name:图片名称
    :return: 无
    """
    bname = fp + 'b' + image_name  # + img_type
    im = Image.open(fp + image_name)  # + img_type
    imgry = im.convert('L')  # 转化为灰度图

    table = get_bin_table()
    out = imgry.point(table, '1')  # 变成二值图片:0表示黑色,1表示白色
    # out.save(bname.replace("btest_", "atest_"))  # 去除噪点前的图片
    noise_point_list = []  # 通过算法找出噪声点,第一步比较严格,可能会有些误删除的噪点
    for x in range(out.width):
        for y in range(out.height):
            res_9 = sum_9_region(out, x, y)
            if (0 < res_9 < 3) and out.getpixel((x, y)) == 0:  # 找到孤立点
                pos = (x, y)  #
                noise_point_list.append(pos)
    # 根据噪点的位置信息,消除二值图片的黑点噪声
    for item in noise_point_list:
        out.putpixel((item[0], item[1]), 1)
    out.save(bname)


# 获取灰度转二值的映射table
def get_bin_table(threshold=140):
    """
    获取灰度转二值的映射table
    :param threshold:
    :return:
    """
    table = []
    for i in range(256):
        if i < threshold:
            table.append(0)
        else:
            table.append(1)
    return table


# 9邻域框,以当前点为中心的田字框,黑点个数
def sum_9_region(img, x, y):
    """
    9邻域框,以当前点为中心的田字框,黑点个数
    :param x: 像素x位置
    :param y: 像素y位置
    :return:
    """
    # todo 判断图片的长宽度下限
    cur_pixel = img.getpixel((x, y))  # 当前像素点的值
    width = img.width
    height = img.height

    if cur_pixel == 1:  # 如果当前点为白色区域,则不统计邻域值
        return 0

    if y == 0:  # 第一行
        if x == 0:  # 左上顶点,4邻域
            # 中心点旁边3个点
            sum = cur_pixel \
                  + img.getpixel((x, y + 1)) \
                  + img.getpixel((x + 1, y)) \
                  + img.getpixel((x + 1, y + 1))
            return 4 - sum
        elif x == width - 1:  # 右上顶点
            sum = cur_pixel \
                  + img.getpixel((x, y + 1)) \
                  + img.getpixel((x - 1, y)) \
                  + img.getpixel((x - 1, y + 1))

            return 4 - sum
        else:  # 最上非顶点,6邻域
            sum = img.getpixel((x - 1, y)) \
                  + img.getpixel((x - 1, y + 1)) \
                  + cur_pixel \
                  + img.getpixel((x, y + 1)) \
                  + img.getpixel((x + 1, y)) \
                  + img.getpixel((x + 1, y + 1))
            return 6 - sum
    elif y == height - 1:  # 最下面一行
        if x == 0:  # 左下顶点
            # 中心点旁边3个点
            sum = cur_pixel \
                  + img.getpixel((x + 1, y)) \
                  + img.getpixel((x + 1, y - 1)) \
                  + img.getpixel((x, y - 1))
            return 4 - sum
        elif x == width - 1:  # 右下顶点
            sum = cur_pixel \
                  + img.getpixel((x, y - 1)) \
                  + img.getpixel((x - 1, y)) \
                  + img.getpixel((x - 1, y - 1))

            return 4 - sum
        else:  # 最下非顶点,6邻域
            sum = cur_pixel \
                  + img.getpixel((x - 1, y)) \
                  + img.getpixel((x + 1, y)) \
                  + img.getpixel((x, y - 1)) \
                  + img.getpixel((x - 1, y - 1)) \
                  + img.getpixel((x + 1, y - 1))
            return 6 - sum
    else:  # y不在边界
        if x == 0:  # 左边非顶点
            sum = img.getpixel((x, y - 1)) \
                  + cur_pixel \
                  + img.getpixel((x, y + 1)) \
                  + img.getpixel((x + 1, y - 1)) \
                  + img.getpixel((x + 1, y)) \
                  + img.getpixel((x + 1, y + 1))

            return 6 - sum
        elif x == width - 1:  # 右边非顶点
            # print('%s,%s' % (x, y))
            sum = img.getpixel((x, y - 1)) \
                  + cur_pixel \
                  + img.getpixel((x, y + 1)) \
                  + img.getpixel((x - 1, y - 1)) \
                  + img.getpixel((x - 1, y)) \
                  + img.getpixel((x - 1, y + 1))

            return 6 - sum
        else:  # 具备9领域条件的
            sum = img.getpixel((x - 1, y - 1)) \
                  + img.getpixel((x - 1, y)) \
                  + img.getpixel((x - 1, y + 1)) \
                  + img.getpixel((x, y - 1)) \
                  + cur_pixel \
                  + img.getpixel((x, y + 1)) \
                  + img.getpixel((x + 1, y - 1)) \
                  + img.getpixel((x + 1, y)) \
                  + img.getpixel((x + 1, y + 1))
            return 9 - sum


# 获取验证码结果
def get_verifying_code():
    """
    获取验证码结果
    :return: 验证码字符串
    """
    # 清楚之前的验证码
    for file in os.listdir(fp):
        os.remove(fp + file)
    # 获取验证码图片
    downloads_pic()
    # 处理验证码,去除干扰像素
    noise_reduction()
    # 降噪
    get_clear_bin_image()
    # 识别并返回验证码字符串
    result = img_to_str()
    return result


if __name__ == '__main__':
    # 批量获取验证码图片
    # for i in range(20):
    #     downloads_pic(pic_name=str(i))

    # 目标工具方法
    print(get_verifying_code())
    pass

猜你喜欢

转载自blog.csdn.net/Febe_Tien/article/details/81706186
今日推荐