图文验证码识别

对于大多数图文验证码,均可以使用开源OCR识别库进行处理,比如ddddocr,接下来以ddddocr库进行示范

一、ddddocr库安装和使用

  • 安装

    pip install ddddocr
  • 使用代码示例

    import ddddocr
    
    ocr = ddddocr.DdddOcr(old=True)
    
    with open("test.jpg", 'rb') as f:
        image = f.read()
    
    res = ocr.classification(image)
    print(res)

    old = True表示使用老版本模型,因为某些情况下老版本识别效果更佳

二、案例示范

  • 制作验证码

    import random
    import string
    from PIL import Image, ImageDraw, ImageFont
    def createCaptcha(imgname):
        '''
        生成5个字符长度的验证码
        :param imgname: 生成验证码图片名
        :return: 验证码字符串
        '''
    
        # 定义变量,用于画面的背景色、宽、高
        # bgcolor = (random.randrange(255), random.randrange(255), 100)
        bgcolor = 'white'
        width = 100
        height = 30
        # 创建画面对象
        im = Image.new('RGB', (width, height), bgcolor)
        # 创建画笔对象
        draw = ImageDraw.Draw(im)
        # 绘制50个噪点
        for i in range(50):
            point_position = (random.randrange(width), random.randrange(height))
            point_color = (random.randrange(255), random.randrange(255), random.randrange(255))
            draw.point(point_position, point_color)
        # 绘制2条干扰线
        for i in range(2):
            start_position = (random.randrange(width), random.randrange(height))
            end_position = (random.randrange(width), random.randrange(height))
            line_color = (random.randrange(255), random.randrange(255), random.randrange(255))
            draw.line([start_position, end_position], line_color)
        # 定义验证码的备选值
        str1 = string.ascii_letters + string.digits  # 大小写字母+数字共62个
        # 随机选取5个值作为验证码
        rand_str = random.sample(str1, 5)
        # 字体
        font_type = 'arial.ttf'  # 字体类型
        font_size = 20  # 字体大小
        # 构造字体对象
        font = ImageFont.truetype(font_type, font_size)
        # 构造字体颜色
        fontcolor = (random.randrange(255), random.randrange(255), random.randrange(255))
        # 绘制5个字
        for i in range(5):
            draw.text((20 * i + random.randint(1, 3), random.randint(0, 1)), rand_str[i], font=font, fill=fontcolor)
        im.save(imgname)  # 验证码内容命名图片,保存在当前目录下
        # 验证码字符串
        captcha_str = "".join(rand_str)
        return captcha_str
    View Code

    生成的验证码示例:

  • 验证码预处理

    import cv2
    
    def pre_handler(ori_imgname, new_imgname):
        '''
        图片预处理
        :param ori_imgname: 原验证码图片名
        :param new_imgname: 预处理后图片名
        :return:
        '''
        # 读入灰度图片
        im = cv2.imread(ori_imgname, 0)
        # im = cv2.imread(ori_imgname, cv2.IMREAD_GRAYSCALE)
    
        height, width = im.shape  # (30, 100)
    
        # 去噪线
        # opencv库中的矩阵点x, y需要反着理解
        for x in range(1, height - 1):
            for y in range(1, width - 1):
                count = 0
                if im[x, y - 1] > 245:
                    count += 1
                if im[x, y + 1] > 245:
                    count += 1
                if im[x - 1, y] > 245:
                    count += 1
                if im[x + 1, y] > 245:
                    count += 1
                if count > 2:  # 根据该点周围4个点中白点的个数,超过2个就转成白的
                    im[x, y] = 255
    
        # 去噪点
        for x in range(height):
            for y in range(width):
                # 第一排
                if x == 0:
                    # 左上角顶点
                    if y == 0:
                        num = 3
                        if im[x, y + 1] > 245:
                            num -= 1
                        if im[x + 1, y] > 245:
                            num -= 1
                        if im[x + 1, y + 1] > 245:
                            num -= 1
                    # 右上角顶点
                    elif y == width - 1:
                        num = 3
                        if im[x, y - 1] > 245:
                            num -= 1
                        if im[x + 1, y - 1] > 245:
                            num -= 1
                        if im[x + 1, y] > 245:
                            num -= 1
                    # 第一排中间点
                    else:
                        num = 5
                        if im[x, y - 1] > 245:
                            num -= 1
                        if im[x, y + 1] > 245:
                            num -= 1
                        if im[x + 1, y - 1] > 245:
                            num -= 1
                        if im[x + 1, y] > 245:
                            num -= 1
                        if im[x + 1, y + 1] > 245:
                            num -= 1
                # 最后一排
                elif x == height - 1:
                    # 左下角顶点
                    if y == 0:
                        num = 3
                        if im[x, y + 1] > 245:
                            num -= 1
                        if im[x - 1, y] > 245:
                            num -= 1
                        if im[x - 1, y + 1] > 245:
                            num -= 1
                    # 右下角顶点
                    elif y == width - 1:
                        num = 3
                        if im[x, y - 1] > 245:
                            num -= 1
                        if im[x - 1, y - 1] > 245:
                            num -= 1
                        if im[x - 1, y] > 245:
                            num -= 1
                    # 最后一排中间点
                    else:
                        num = 5
                        if im[x, y - 1] > 245:
                            num -= 1
                        if im[x, y + 1] > 245:
                            num -= 1
                        if im[x - 1, y - 1] > 245:
                            num -= 1
                        if im[x - 1, y] > 245:
                            num -= 1
                        if im[x - 1, y + 1] > 245:
                            num -= 1
                # 中间排
                else:
                    # 第一列中间点
                    if y == 0:
                        num = 5
                        if im[x - 1, y] > 245:
                            num -= 1
                        if im[x + 1, y] > 245:
                            num -= 1
                        if im[x - 1, y + 1] > 245:
                            num -= 1
                        if im[x, y + 1] > 245:
                            num -= 1
                        if im[x + 1, y + 1] > 245:
                            num -= 1
                    # 最后一列中间点
                    elif y == width - 1:
                        num = 5
                        if im[x - 1, y] > 245:
                            num -= 1
                        if im[x + 1, y] > 245:
                            num -= 1
                        if im[x - 1, y - 1] > 245:
                            num -= 1
                        if im[x, y - 1] > 245:
                            num -= 1
                        if im[x + 1, y - 1] > 245:
                            num -= 1
                    # 周围拥有8个点的点
                    else:
                        num = 8
                        if im[x - 1, y - 1] > 245:
                            num -= 1
                        if im[x - 1, y] > 245:
                            num -= 1
                        if im[x - 1, y + 1] > 245:
                            num -= 1
                        if im[x, y - 1] > 245:
                            num -= 1
                        if im[x, y + 1] > 245:
                            num -= 1
                        if im[x + 1, y - 1] > 245:
                            num -= 1
                        if im[x + 1, y] > 245:
                            num -= 1
                        if im[x + 1, y + 1] > 245:
                            num -= 1
                if num <= 1:
                    im[x, y] = 255
        cv2.imwrite(new_imgname, im)
    View Code

    如上主要进行了灰度化、去噪线、去噪点,预处理后验证码示例:

  • 验证码识别完整代码

    import cv2
    import random
    import string
    import ddddocr
    from PIL import Image, ImageDraw, ImageFont
    
    
    class CaptchaHandler:
        def main(self):
            ori_imgname = 'captcha.png'
            new_imgname = 'captcha_new.png'
            ori_str = self.createCaptcha(ori_imgname)  # 制作验证码
            self.pre_handler(ori_imgname, new_imgname)  # 图片预处理(灰度化、去噪点、去噪线)
            ocr = ddddocr.DdddOcr(old=True, show_ad=False)
            with open(ori_imgname, 'rb') as f:
                image1 = f.read()
            res1 = ocr.classification(image1)
    
            with open(new_imgname, 'rb') as f:
                image2 = f.read()
            res2 = ocr.classification(image2)
            if ori_str.lower() == res1.lower():
                result1 = True
            else:
                result1 = False
    
            if ori_str.lower() == res2.lower():
                result2 = True
            else:
                result2 = False
    
            print(f'验证码{ori_str} 原图识别结果:{res1},处理后识别结果:{res2}')
            return result1, result2
    
        def pre_handler(self, ori_imgname, new_imgname):
            '''
            图片预处理
            :param ori_imgname: 原验证码图片名
            :param new_imgname: 预处理后图片名
            :return:
            '''
            # 读入灰度图片
            im = cv2.imread(ori_imgname, 0)
            # im = cv2.imread(ori_imgname, cv2.IMREAD_GRAYSCALE)
    
            height, width = im.shape  # (30, 100)
    
            # 去噪线
            # opencv库中的矩阵点x, y需要反着理解
            for x in range(1, height - 1):
                for y in range(1, width - 1):
                    count = 0
                    if im[x, y - 1] > 245:
                        count += 1
                    if im[x, y + 1] > 245:
                        count += 1
                    if im[x - 1, y] > 245:
                        count += 1
                    if im[x + 1, y] > 245:
                        count += 1
                    if count > 2:  # 根据该点周围4个点中白点的个数,超过2个就转成白的
                        im[x, y] = 255
    
            # 去噪点
            for x in range(height):
                for y in range(width):
                    # 第一排
                    if x == 0:
                        # 左上角顶点
                        if y == 0:
                            num = 3
                            if im[x, y + 1] > 245:
                                num -= 1
                            if im[x + 1, y] > 245:
                                num -= 1
                            if im[x + 1, y + 1] > 245:
                                num -= 1
                        # 右上角顶点
                        elif y == width - 1:
                            num = 3
                            if im[x, y - 1] > 245:
                                num -= 1
                            if im[x + 1, y - 1] > 245:
                                num -= 1
                            if im[x + 1, y] > 245:
                                num -= 1
                        # 第一排中间点
                        else:
                            num = 5
                            if im[x, y - 1] > 245:
                                num -= 1
                            if im[x, y + 1] > 245:
                                num -= 1
                            if im[x + 1, y - 1] > 245:
                                num -= 1
                            if im[x + 1, y] > 245:
                                num -= 1
                            if im[x + 1, y + 1] > 245:
                                num -= 1
                    # 最后一排
                    elif x == height - 1:
                        # 左下角顶点
                        if y == 0:
                            num = 3
                            if im[x, y + 1] > 245:
                                num -= 1
                            if im[x - 1, y] > 245:
                                num -= 1
                            if im[x - 1, y + 1] > 245:
                                num -= 1
                        # 右下角顶点
                        elif y == width - 1:
                            num = 3
                            if im[x, y - 1] > 245:
                                num -= 1
                            if im[x - 1, y - 1] > 245:
                                num -= 1
                            if im[x - 1, y] > 245:
                                num -= 1
                        # 最后一排中间点
                        else:
                            num = 5
                            if im[x, y - 1] > 245:
                                num -= 1
                            if im[x, y + 1] > 245:
                                num -= 1
                            if im[x - 1, y - 1] > 245:
                                num -= 1
                            if im[x - 1, y] > 245:
                                num -= 1
                            if im[x - 1, y + 1] > 245:
                                num -= 1
                    # 中间排
                    else:
                        # 第一列中间点
                        if y == 0:
                            num = 5
                            if im[x - 1, y] > 245:
                                num -= 1
                            if im[x + 1, y] > 245:
                                num -= 1
                            if im[x - 1, y + 1] > 245:
                                num -= 1
                            if im[x, y + 1] > 245:
                                num -= 1
                            if im[x + 1, y + 1] > 245:
                                num -= 1
                        # 最后一列中间点
                        elif y == width - 1:
                            num = 5
                            if im[x - 1, y] > 245:
                                num -= 1
                            if im[x + 1, y] > 245:
                                num -= 1
                            if im[x - 1, y - 1] > 245:
                                num -= 1
                            if im[x, y - 1] > 245:
                                num -= 1
                            if im[x + 1, y - 1] > 245:
                                num -= 1
                        # 周围拥有8个点的点
                        else:
                            num = 8
                            if im[x - 1, y - 1] > 245:
                                num -= 1
                            if im[x - 1, y] > 245:
                                num -= 1
                            if im[x - 1, y + 1] > 245:
                                num -= 1
                            if im[x, y - 1] > 245:
                                num -= 1
                            if im[x, y + 1] > 245:
                                num -= 1
                            if im[x + 1, y - 1] > 245:
                                num -= 1
                            if im[x + 1, y] > 245:
                                num -= 1
                            if im[x + 1, y + 1] > 245:
                                num -= 1
                    if num <= 1:
                        im[x, y] = 255
            cv2.imwrite(new_imgname, im)
    
        def createCaptcha(self, imgname):
            '''
            生成5个字符长度的验证码
            :param imgname: 生成验证码图片名
            :return: 验证码字符串
            '''
    
            # 定义变量,用于画面的背景色、宽、高
            # bgcolor = (random.randrange(255), random.randrange(255), 100)
            bgcolor = 'white'
            width = 100
            height = 30
            # 创建画面对象
            im = Image.new('RGB', (width, height), bgcolor)
            # 创建画笔对象
            draw = ImageDraw.Draw(im)
            # 绘制50个噪点
            for i in range(50):
                point_position = (random.randrange(width), random.randrange(height))
                point_color = (random.randrange(255), random.randrange(255), random.randrange(255))
                draw.point(point_position, point_color)
            # 绘制2条干扰线
            for i in range(2):
                start_position = (random.randrange(width), random.randrange(height))
                end_position = (random.randrange(width), random.randrange(height))
                line_color = (random.randrange(255), random.randrange(255), random.randrange(255))
                draw.line([start_position, end_position], line_color)
            # 定义验证码的备选值
            str1 = string.ascii_letters + string.digits  # 大小写字母+数字共62个
            # 随机选取5个值作为验证码
            rand_str = random.sample(str1, 5)
            # 字体
            font_type = 'arial.ttf'  # 字体类型
            font_size = 20  # 字体大小
            # 构造字体对象
            font = ImageFont.truetype(font_type, font_size)
            # 构造字体颜色
            fontcolor = (random.randrange(255), random.randrange(255), random.randrange(255))
            # 绘制5个字
            for i in range(5):
                draw.text((20 * i + random.randint(1, 3), random.randint(0, 1)), rand_str[i], font=font, fill=fontcolor)
            im.save(imgname)  # 验证码内容命名图片,保存在当前目录下
            # 验证码字符串
            captcha_str = "".join(rand_str)
            return captcha_str
    
    
    if __name__ == '__main__':
        handler = CaptchaHandler()
        count1 = 0
        count2 = 0
        for i in range(200):
            result1, result2 = handler.main()
            if result1 is True:
                count1 += 1
            if result2 is True:
                count2 += 1
        print(f'识别200次,原图成功{count1}次,预处理后成功{count2}次')
    View Code

    通过200次测试,在忽略字符大小写的情况下,ddddocr库识别原图和经过预处理后验证码的成功率都很高,接近90%的成功率

posted @ 2022-12-26 02:50  eliwang  阅读(238)  评论(0编辑  收藏  举报