python tesseract验证码识别(验证码未粘连)

本文介绍了Python中使用tesseract、PIL和OpenCV库进行验证码识别的步骤,包括图片灰度处理、二值化、去除边框、降噪等预处理,以及识别过程。代码示例展示了对清晰度较高的识图验证码和定位元素截图验证码的处理方法。
摘要由CSDN通过智能技术生成

需要的库和工具及安装

其中会用到以下几个库和工具:

工具:

tesseract

tesseract下载地址:https://digi.bib.uni-mannheim.de/tesseract/

1.下载适合自己版本(32或者64位)的非dev的exe文件,然后一路安装下去。经过多次测试,不同版本的tesseract识别的结果会不同,我试了五六个版本,最后使用tesseract-ocr-w64-setup-v5.0.0-alpha.20210506.exe 版本识别成功率最高。

:如果需要支持多国语言,到这个步骤的时候它勾选上,默认支持英文:

0

2.配置环境变量 

path 里面配置tesseract的安装路径:

 3.验证安装是否成功:

cmd,然后输入验证tesseract和验证tesseract -v 正常显示即可

 pillow 和 pytesseract 两个库

pip install pillow -i http://pypi.douban.com/simple --trusted-host pypi.douban.com
pip install pytesseract -i http://pypi.douban.com/simple --trusted-host pypi.douban.com

配置pytesseract库:

找到pytesseract 库的pytesseract.py文件,打开该.py文件,找到 tesseract_cmd,改变它的值为刚才安装 tesseract.exe 的路径。

0

安装cv2库:

pip3 install opencv-python

正文

在python爬虫爬取某些网站的验证码的时候可能会遇到验证码识别的问题,现在的验证码大多分为四类:

1、识图的验证码   2、需要计算的验证码   3、通过滑动滑块的验证码   4、语音的验证码

本文针对最简单的识图验证码,识别验证码中的英文数字。

识别验证码通常是这几个步骤:

 1、灰度处理:把彩色的验证码图片转为灰色的图片

 2、二值化:将图片处理为只有黑白两色的图片

 3、去除边框:去除验证码图片的边框,去除边框就是遍历像素点,找到四个边框上的所有点,把他们都改为白色,如果没有边框可以不用。

 4、降噪:去除掉验证码图片内的干扰条件,比如西线和点。

 5、切割字符或者倾斜度矫正:切割字符对验证码内的内容进行单个的切割,一般用于验证码粘连或者计算验证码。

 6、训练字体库

 7、识别

 如果验证码图片清晰度很高,是直接图片保存下来的。(不是截图)可以用以下代码

import re

import numpy
from PIL import Image
from pytesseract import *
from fnmatch import fnmatch
from queue import Queue
import cv2
import os


'''
需要建两个文件夹,code_dir文件夹存放验证码图片,
out_img文件夹 存放处理后的验证码图片
'''


class LdenVerifiCode:
    filedir = 'D:/logs/code_dir'  # 存放验证码的文件夹
    code_out_folder = 'D:/logs/out_img'

    def clear_border(self, img, img_name):
        '''去除边框
        '''

        # filename = self.code_out_folder + img_name.split('.')[0] + '-clearBorder.jpg'
        filename = os.path.join(self.code_out_folder, img_name.split('.')[0] + '-clearBorder.png')
        h, w = img.shape[:2]
        for y in range(0, w):
            for x in range(0, h):
                # if y ==0 or y == w -1 or y == w - 2:
                if y < 4 or y > w - 4:
                    img[x, y] = 255
                # if x == 0 or x == h - 1 or x == h - 2:
                if x < 4 or x > h - 4:
                    img[x, y] = 255

        cv2.imwrite(filename, img)
        return img

    def interference_line(self, img, img_name):
        '''
        干扰线降噪
        '''

        # filename =  self.code_out_folder + img_name.split('.')[0] + '-interferenceline.jpg'
        filename = os.path.join(self.code_out_folder, img_name.split('.')[0] + '-interferenceline.png')
        h, w = img.shape[:2]
        # !!!opencv矩阵点是反的
        # img[1,2] 1:图片的高度,2:图片的宽度
        for y in range(1, w - 1):
            for x in range(1, h - 1):
                count = 0
                if img[x, y - 1] > 245:
                    count = count + 1
                if img[x, y + 1] > 245:
                    count = count + 1
                if img[x - 1, y] > 245:
                    count = count + 1
                if img[x + 1, y] > 245:
                    count = count + 1
                if count > 2:
                    img[x, y] = 255
        cv2.imwrite(filename, img)
        return img

    def interference_point(self, img, img_name, x=0, y=0):
        """点降噪
        9邻域框,以当前点为中心的田字框,黑点个数
        :param x:
        :param y:
        :return:
        """
        # filename =  './out_img/' + img_name.split('.')[0] + '-interferencePoint.jpg'
        filename = os.path.join(self.code_out_folder, img_name.split('.')[0] + '-cutting.png')
        # todo 判断图片的长宽度下限
        cur_pixel = img[x, y]  # 当前像素点的值
        height, width = img.shape[:2]

        for y in range(0, width - 1):
            for x in range(0, height - 1):
                if y == 0:  # 第一行
                    if x == 0:  # 左上顶点,4邻域
                        # 中心点旁边3个点
                        sum = int(cur_pixel) \
                              + int(img[x, y + 1]) \
                              + int(img[x + 1, y]) \
                              + int(img[x + 1, y + 1])
                        if sum <= 2 * 245:
                            img[x, y] = 0
                    elif x == height - 1:  # 右上顶点
                        sum = int(cur_pixel) \
                              + int(img[x, y + 1]) \
                              + int(img[x - 1, y]) \
                              + int(img[x - 1, y + 1])
                        if sum <= 2 * 245:
                            img[x, y] = 0
                    else:  # 最上非顶点,6邻域
                        sum = int(img[x - 1, y]) \
                              + int(img[x - 1, y + 1]) \
                              + int(cur_pixel) \
                              + int(img[x, y + 1]) \
                              + int(img[x + 1, y]) \
                              + int(img[x + 1, y + 1])
                        if sum <= 3 * 245:
                            img[x, y] = 0
                elif y == width - 1:  # 最下面一行
                    if x == 0:  # 左下顶点
                        # 中心点旁边3个点
                        sum = int(cur_pixel) \
                              + int(img[x + 1, y]) \
                              + int(img[x + 1, y - 1]) \
                              + int(img[x, y - 1])
                        if sum <= 2 * 245:
                            img[x, y] = 0
                    elif x == height - 1:  # 右下顶点
                        sum = int(cur_pixel) \
                              + int(img[x, y - 1]) \
                              + int(img[x - 1, y]) \
                              + int(img[x - 1, y - 1])

                        if sum <= 2 * 245:
                            img[x, y] = 0
                    else:  # 最下非顶点,6邻域
                        sum = int(cur_pixel) \
                              + int(img[x - 1, y]) \
                              + int(img[x + 1, y]) \
                              + int(img[x, y - 1]) \
                              + int(img[x - 1, y - 1]) \
                              + int(img[x + 1, y - 1])
                        if sum <= 3 * 245:
                            img[x, y] = 0
                else:  # y不在边界
                    if x == 0:  # 左边非顶点
                        sum = int(img[x, y - 1]) \
                              + int(cur_pixel) \
                              + int(img[x, y + 1]) \
                              + int(img[x + 1, y - 1]) \
                              + int(img[x + 1, y]) \
                              + int(img[x + 1, y + 1])

                        if sum <= 3 * 245:
                            img[x, y] = 0
                    elif x == height - 1:  # 右边非顶点
                        sum = int(img[x, y - 1]) \
                              + int(cur_pixel) \
                              + int(img[x, y + 1]) \
                              + int(img[x - 1, y - 1]) \
                              + int(img[x - 1, y]) \
                              + int(img[x - 1, y + 1])

                        if sum <= 3 * 245:
                            img[x, y] = 0
                    else:  # 具备9领域条件的
                        sum = int(img[x - 1, y - 1]) \
                              + int(img[x - 1, y]) \
                              + int(img[x - 1, y + 1]) \
                              + int(img[x, y - 1]) \
                              + int(cur_pixel) \
                              + int(img[x, y + 1]) \
                              + int(img[x + 1, y - 1]) \
                              + int(img[x + 1, y]) \
                              + int(img[x + 1, y + 1])
                        if sum <= 4 * 245:
                            img[x, y] = 0
        cv2.imwrite(filename, img)
        return img

    def _get_dynamic_binary_image(self, filedir, img_name):
        '''
        自适应阀值二值化
        '''

        # filename = self.code_out_folder + img_name.split('.')[0] + '-binary.jpg'
        filename = os.path.join(self.code_out_folder, img_name.split('.')[0] + '-binary.png')
        img_name = filedir + '/' + img_name
        print('开始识别验证码:' + img_name)
        im = cv2.imread(img_name)
        im = cv2.cvtColor(im, cv2.COLOR_BGR2GRAY)
        th1 = cv2.adaptiveThreshold(im, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY, 21, 1)
        cv2.imwrite(filename, th1)
        return th1

    def _get_static_binary_image(self, img, threshold=140):
        '''
        手动二值化
        '''

        img = Image.open(img)
        img = img.convert('L')
        pixdata = img.load()
        w, h = img.size
        for y in range(h):
            for x in range(w):
                if pixdata[x, y] < threshold:
                    pixdata[x, y] = 0
                else:
                    pixdata[x, y] = 255

        return img

    def main(self, img_name):

        # 自适应阈值二值化
        im = self._get_dynamic_binary_image(self.filedir, img_name)

        # 去除边框
        im = self.clear_border(im, img_name)

        # 对图片进行干扰线降噪
        im = self.interference_line(im, img_name)

        # 对图片进行点降噪
        self.interference_point(im, img_name)

        # code_file ='./out_img/%s-cutting.jpg' % img_name.split('.')[0]
        code_file = os.path.join(self.code_out_folder, '%s-cutting.png' % img_name.split('.')[0])
        str_img = image_to_string(Image.open(code_file))  # 图片转文字
        cop = re.compile("[^a-z^A-Z^0-9]")  # 匹配不是英文大小写、数字的其他字符,去掉特殊字符
        code_results = cop.sub('', str_img)  # 将string1中匹配到的字符替换成空字符
        print('识别为:%s' % code_results)
        return code_results

if __name__ == '__main__':
    lvc = LdenVerifiCode()
    lvc.main('191704-ele.png')

如果是通过定位元素坐标,然后截图下来的验证码可以用以下代码:

import re

import numpy
from PIL import Image
from pytesseract import *
from fnmatch import fnmatch
from queue import Queue
import cv2
import os

'''
需要建两个文件夹,code_dir文件夹存放验证码图片,
out_img文件夹 存放处理后的验证码图片
'''


class LdenVerifiCode:
    filedir = 'E:/logs/code_dir'  # 存放验证码的文件夹
    code_out_folder = 'E:/logs/out_img'

    def clear_border(self, filedir, img_name):
        '''去除边框
        '''
        img_name = filedir + '/' + img_name
        img = cv2.imread(img_name)
        filename = os.path.join(self.code_out_folder, img_name.split('.')[0] + '-clearBorder.png')
        h, w = img.shape[:2]
        for y in range(0, w):
            for x in range(0, h):
                # if y ==0 or y == w -1 or y == w - 2:
                if y < 4 or y > w - 4:
                    img[x, y] = 255
                # if x == 0 or x == h - 1 or x == h - 2:
                if x < 4 or x > h - 4:
                    img[x, y] = 255

        cv2.imwrite(filename, img)
        return filename

    def clearNoise(self,imageFile,img_name, x=0, y=0):
        if os.path.exists(imageFile):
            image = Image.open(imageFile)
            image = image.convert('L')
            image = numpy.asarray(image)
            image = (image > 135) * 255
            image = Image.fromarray(image).convert('RGB')
            filename = os.path.join(self.code_out_folder, img_name.split('.')[0] + '-cutting.png')
            image.save(filename)
            return image

    def main(self, img_name):
        # 去除边框
        filename = self.clear_border(self.filedir, img_name)
        self.clearNoise(filename,img_name)
        code_file = os.path.join(self.code_out_folder, '%s-cutting.png' % img_name.split('.')[0])
        str_img = image_to_string(Image.open(code_file))  # 图片转文字
        cop = re.compile("[^a-z^A-Z^0-9]")  # 匹配不是英文大小写、数字的其他字符,去掉特殊字符
        code_results = cop.sub('', str_img)  # 将string1中匹配到的字符替换成空字符
        print('识别为:%s' % code_results)
        return code_results

if __name__ == '__main__':
    lvc = LdenVerifiCode()
    lvc.main('145512-ele.png')

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值