使用tesseract进行验证码识别

Posted by ZMY on January 20, 2020

使用tesseract进行验证码识别

项目目的

在使用python进行web爬虫过程中,验证码是一个无法绕过的问题,一些大型网站会采用比较高级的验证方式,如图片点选,滑动图片等等。
今天进行的是较低一级的验证码图片的识别,即图片上的字母和数值上有横线如何进行识别。如下图,当然现今有很多站点提供api可供大家进行识别,我们要做的是如何在本地搭建一套这样的验证码识别系统,供自己使用

对于像12306网站这种网站上的图片点验证码识别比较麻烦,应该使用的是神经网络识别系统,之后有时间会实践一下,更新一个专门关于神经网络识别验证码的博客。

软件环境

  • 系统版本centos7.4.1708
  • python版本3.6.5
  • tesseract版本4.1.0
  • leptonica版本1.78.0(tesseract包的安装依赖)
  • PIL库图片过滤
  • tesseract数据训练验证码

安装及部署

一. 安装并编译leptonica

1.安装依赖包

#yum install gcc autoconf automake libtool libjpeg-devel libpng-devel libtiff-devel zlib-devel -y

2.下载leptonica-1.78.0并编译、加载环境变量

#wget http://www.leptonica.org/source/leptonica-1.78.0.tar.gz
#tar xzvf leptonica-1.78.0.tar.gz
#cd leptonica-1.78.0

3.编译并安装

#./autogen.sh 
#./configure
#make
#make install

4.添加环境变量

#vim /etc/profile

5.最后添加如下内容

#export LD_LIBRARY_PATH=$LD_LIBRARY_PAYT:/usr/local/lib
#export LIBLEPT_HEADERSDIR=/usr/local/include
#export PKG_CONFIG_PATH=/usr/local/lib/pkgconfig 

6.执行如下命令使之立即生效

#source /etc/profile

二.下载并安装tesseract4.1.0

1.安装tesseract

#wget https://github.com/tesseract-ocr/tesseract/archive/4.1.0.tar.gz
#tar xzvf tesseract-4.1.0.tar.gz
#cd tesseract-4.1.0

2.编译并安装

#./autogen.sh 
#./configure
#make
#make install

3.查看tesseract版本

#tesseract --version
4.1.0

三.training的编译安装

1.安装依赖库

#yum install libicu-devel pango-devel cairo-devel -y

2.下载并安装libarchive

#wget http://www.libarchive.org/downloads/libarchive-3.3.3.tar.gz
#tar xzvf libarchive-3.3.3.tar.gz
#cd libarchive-3.3.3
###编译并安装
#./configure
#make
#make install

3.下载安装icu52版本,不安装这个软件包training会包icu版本较低错误

#wget http://download.icu-project.org/files/icu4c/52.1/icu4c-52_1-src.tgz
#tar -xvzf icu4c-52_1-src.tgz
#cd icu/source/
###编译并安装
#./runConfigureICU Linux --with-library-bits=64
#make -j 5
#make install

4.创建软连接

#ln -s /usr/local/lib/libicui18n.so.52 /usr/lib64/libicui18n.so.52
#ln -s /usr/local/lib/libicuio.so.52 /usr/lib64/libicuio.so.52
#ln -s /usr/local/lib/libicuuc.so.52 /usr/lib64/libicuuc.so.52
#ln -s /usr/local/lib/libicudata.so.52 /usr/lib64/libicudata.so.5

5.编译并安装traning

#./configure
#make training
#make training-install

6.有如下提示training安装成功

lstmtraining --version
4.1.0

四. 下载用于图片训练的验证码

1.去网上下载用于验证码训练的图片1000+张
我使用的验证码sample:https://www.kaggle.com/fournierp/captcha-version-2-images 有兴趣可以自己制作验证码:https://pypi.org/project/captcha/ 2.将1000+张验证码图片分成二部分,一部分用于tesseract数据训练,一部分用于测试训练后的识别准确率
这里将500+张待训练验证码放在/root/samples_training/下,将500+张待验证验证码放在/root/samples_test/下。

待训练原始验证码存储路径:/root/samples_training/
待训练降噪后验证码存储路径:/root/samples_training_convert_image/
待训练过滤后验证码存储路径:/root/samples_training_after_filter/
待验证原始验证码存储路径:/root/samples_test/
待验证降噪后验证码存储路径:/root/samples_test_convert_image/
待验证过滤后验证码存储路径:/root/samples_test_after_filter/
执行过程中convert_all_training_image.py、convert_all_test_image.py、1.bash、result.py所在路径:/root/zhai/ocr/1-17/

五. 使用PIL库进行图片过滤

#python convert_all_training_image.py

代码如下:下载地址下载

from PIL import Image,ImageFilter
import os
path="/root/samples_training" #设置sample图片路径
'''PIX函数作用是对图片进行降噪处理
通过getpixel获取每个像素所对应的二值的数值,然后根据规则进行降噪处理
降噪规则有很多种,包括4格,8格,大于5格等等,根据实际的情况进行选择'''
def pIx(data,iteration=1):
    w,h=data.size
    for x in range(1,w-1):
        if x > 1 and x != w-2:
            left = x - 1
            leftleft= x - 2
            right = x + 1
            rightright = x + 2
        for y in range(1,h-1):
            up = y - 1
            upup = y - 2
            down = y + 1
            downdown = y + 2
            if x <= 2 or x >= (w - 2):
                data.putpixel((x,y),1)
            elif y <= 2 or y >= (h - 2):
                data.putpixel((x,y),1)
            elif data.getpixel((x,y)) == 0:
                if y > 1 and y != h-1:
                    up_color = data.getpixel((x,up))
                    down_color = data.getpixel((x,down))
                    downdown_color = data.getpixel((x,downdown))
                    left_color = data.getpixel((left,y))
                    left_down_color = data.getpixel((left,down))
                    right_color = data.getpixel((right,y))
                    right_up_color = data.getpixel((right,up))
                    right_down_color = data.getpixel((right,down))
                    if (down_color == 0 and downdown_color == 1) or (left_color == 1 and right_color == 1 and up_color == 1 and down_color == 1) or (up_color == 1 and down_color == 1):
                        data.putpixel((x,y),1)
            else:
                pass
            data.save(itcp+filename_prefix+'_strip_noise.png')
    if iteration > 1:
        iteration=iteration-1
        pIx(data,iteration=iteration)
for  filename in os.listdir(path):
    image_training_path=path
    image_training_convert_path='/root/samples_training_convert_image/'
    itp=image_training_path#简化一下变量名
    itcp=image_training_convert_path
    img=Image.open(itp+filename)
    Img=img.convert('L') #转换成灰度图
    filename_prefix=filename.split(".")[0]
    Img.save(itcp+filename_prefix+'_gray.png')
######下面是二值化图片过程###########################
    threshold=143     #阈值设置成143,根据图片的情况进行设置
    table=[]
    for i in range(256):
        if i < threshold:
            table.append(0)
        else:
            table.append(1)
    photo=Img.point(table,'1')
    photo.save(itcp+filename_prefix+'_blackwhite.png')
    pIx(photo,iteration=2)      #因为图片上的横线大概是两像素宽度,所以这里迭代2次

然后执行下面代码

#chmod 777 1.bash
#sh 1.bash

代码如下:下载地址下载

#!/bin/bash
#将所有png图片拷贝到新目录下,以防误删除,因为图片过滤500张大概需要3个小时
cp /root/samples_training_convert_image/* /root/samples_training_after_filter/
cd /root/samples_training_after_filter
#去除掉中转图片
shopt -s extglob
rm -rf !(*strip*)
#将图片以结果名称重命名 如:e2d66_strip_noise.png---->e2d66.png
for i in `ls`
do
rename "_strip_noise" "" *
done
#将png格式图片转化为tif
for i in `ls`
do
k=${i%.*}
convertformat $i $k.tif
done
#将png图片删除
rm -rf *.png

python代码主要是下面三个功能
(1)将图片转换成灰度图
(2)二值化处理
(3)降噪
bash脚本主要是下面两个功能
(4)png图片转换成tif图片
(5)只保留名称包含strip的图片,并将图片重命名为与真实验证码名称相同的图片

六. 对数据进行训练

这里使用jTessBoxEditor是在win7上进行的,因此合并后的tif文件,和修改后的box文件都需要上传到centos7服务器上
1.tif文件合并
用jTessBoxEditor工具,将样本文件合并成.tif文件。
我这里引用的别人教程中的图片,因为我写这篇博客用的家里的macbook,训练环境用的公司的win7系统,现在又正好放假,忘记截图保存了,但过程是一样的。
(1)将box文件和tif文件放在同一目录下
(2)Tools -> Merge TIFF,选择文件类型为all the images,选中所有图片 -> 命名为***.tif 合并为.tif文件


因为这里用的是别的博客上的图片,和我实际中命名的图片有冲突,我的环境中图片合并后的名字为engnum.zhai.exp0.tif,将合并后的tif文件上传到centos服务器上
2.生成box文件

#tesseract engnum.zhai.exp0.tif engnum.zhai.exp0 -l eng lstmbox

上一步执行完后会生成一个叫做engnum.zhai.exp0.box的文件,将这个文件和engnum.zhai.exp0.tif文件拷后到win7环境下
3.box文件调整
使用jTessBoxEditor对box文件进行调整,将识别出错的字符进行更改,这里需要注意的是因为用到了lstm,lstm是识别一行字符,而不是单个字符。即使用jTessBoxEditor打开文件后一行数据为一个框。这里用的是别人的图片实际调整按照一行一调整进行,保存的文件名不变


将调整后的box文件拷贝到centos服务器上
4.数据训练
(1)得到.lstmf文件,为之后的数据训练做准备

#tesseract engnum.zhai.exp0.tif engnum.zhai.exp0 -l eng --psm 6 lstm.train

(2)提取.lstm文件
从 https://github.com/tesseract-ocr/tessdata_best 链接中下载eng.traineddata文件
将上一步下载的eng.traineddata文件拷贝到服务器上,然后运行下面命令

#combine_tessdata -e eng.traineddata eng.lstm

运行上述代码,会从.traineddata文件中提取出eng.lstm 文件,将该文件放在/root/zhai/ocr/1-17/下
(3)创建包含 (1)步生成的训练文件路径的txt文件

#vim engnum.training_files.txt

代码如下

/root/zhai/ocr/1-17/engnum.zhai.exp0.lstmf

(4)生成数据训练checkpoint

#time lstmtraining --model_output="./output/" --continue_from="./eng.lstm" --train_listfile="./engnum.training_files.txt" --traineddata="./eng.traineddata" --debug_interval -1 --target_error_rate 0.01

–model_output:训练数据生成目录
–continue_from:(2)中提取出来的文件
–train_listfile:(3) 步内容
–traineddata:(2)下载内容
–-debug_interval 设置为-1时,命令运行后会显示一些结果参数
–target_error_rate 设置为0.01 为了准确率,将数值设置的小一些
(5)生成最终的数据训练集合
通过以下命令生成engnum.traineddata训练数据

#lstmtraining --stop_training --continue_from="/root/zhai/ocr/1-17/output/_checkpoint" --traineddata="/root/zhai/ocr/1-17/eng.traineddata" --model_output="/root/zhai/ocr/1-17/output/engnum.traineddata"

(6)将训练的数据放到指定位置

#cp /root/zhai/ocr/1-17/output/engnum.traineddata /usr/local/share/tessdata/

六. 验证码识别验证

1.将convert_all_training_image.py中的

image_training_convert_path='/root/samples_training_convert_image/'

替换成

image_training_convert_path='/root/samples_test_convert_image/'

2.将1.bash中的

cp /root/samples_training_convert_image/* /root/samples_training_after_filter/
cd /root/samples_training_after_filter

替换成

cp /root/samples_test_convert_image/* /root/samples_test_after_filter/
cd /root/samples_test_after_filter

3.重复之前五步骤中动作 即

python convert_all_training_image.py
sh 1.bash

4.进行验证码识别
验证码识别主要是使用tesseract命令,通过在result.py中调用subprocess.getoutput()和os.system()来执行命令。

python result.py

代码内容如下:下载地址下载

import os
import subprocess
path="/root/samples_test_after_filter" #过滤后待识别的图片验证码
sum=0
for filename in os.listdir(path):
    sumnumber=len(os.listdir(path)) #待识别的文件总数
    fileindexname=filename.split(".")[0] #取文件名.前面的部分
    absolutefilename=path+'/'+filename #取绝对路径文件名
    outputfilename=os.getcwd()+'/'+'file' #tesseract识别后生成的文件名
    readoutputfile=os.getcwd()+'/'+'file.txt'#读取tesseract生成的文件名
    command1="tesseract %s %s -l engnum" %(absolutefilename,outputfilename) #执>行tesseract识别图片验证码命令参数保存在command1中
    command2="cat %s" %readoutputfile  #将识别出的结果保存在command2变量中
    os.system(command1)  #将识别结果保存到file.txt中
    value=subprocess.getoutput(command2) #取出识别结果赋值给value变量
    value_strip_blank=value.lower().strip().replace(' ','') #字母变小写后再去掉>识别过程中存在的空格
    if fileindexname == value_strip_blank:
        sum=sum+1
    print("当前图片识别结果是:%s" % value_strip_blank)
    print("图片应当别识别为:%s" % fileindexname)
print("识别率为:{:.2%}".format(sum/int(sumnumber)))

识别率如下图所示:

可以看出识别率约为83.03%,也就是10个图片能识别出8个左右,这样的识别效果我还算满意,当然想要更精确就要提供更多的训练素材

七. 总结

使用tesseract识别验证码的核心处理过程如下
1.将各种需要的软件及相关依赖安装好
2.使用PIL库对待训练的图片进行灰度、二值、降噪等处理
3.对待训练的图片进行合成、转换box文件
4.对box文件进行改错微调
5.使用lstmtraining进行数据训练
6.使用tesseract进行验证码识别
使用tesseract识别验证码,是有局限性的,而且干扰条件越多,过滤效果越不好,识别率越低,并且无法通过手工处理得到完美的模型,只能是尽量处理干扰条件,尽量完美。
之后根据需求会考虑用神经网络识别技术做验证码识别,到时候也会即时更新blog。

声明:本博客的原创文章,都是本人平时学习所做的笔记,转载请标注出处,谢谢合作。