【整理】验证码识别

本文最后修改于 235 天前,部分内容可能已经过时!

为了实现完全自动化的抢课效果,我当初尝试了 Tesseract-OCR,但对粘合和变形较为严重的数字识别效果比较差。Tesseract-OCR 提供了学习工具,输入特定的字体模板,不过工具依赖 java 环境,我懒得装。

先来说一下大体的思路:

验证码样本的抓取:

import urllib.request    
from urllib.error import URLError
import time
from retry import retry

@retry(tries=50, delay=2)
    def getcap():
        CaptchaUrl = "http://xxx.xxx.xxx.xxx/academic/getCaptcha.do"
        f1 = open('cap.txt', 'r', encoding='utf-8')
        x = int(f1.read())
        while(x<=250000):
                if (x % 10) == 0:
                        time.sleep(2)                        
                urllib.request.urlretrieve(CaptchaUrl,'C:\\Users\\Gaz\\Desktop\\Captcha\\%s.jpg' % x)
                time.sleep(0.3)
                print(x)
                x+=1
                with open("cap.txt","w") as f2:
                        f2.write("%s" %x)
    getcap()

Captcha

验证码的大体规格:

  1. 不定长,最少四位最多六位
  2. 像素比较低,相比于其他网站的验证码确实低很多
  3. 粘连性强,两个字符基本上糊在一块而儿

下图是最理想的情况:四个数字无粘连并且不弯曲,这类验证码占总验证码的一小部分。大部分的验证码会包含一个两个数字粘连的情况,差一点的会出现三个粘连的情况。

Captchagood

跑满 10k 个样本,如果数字出现的概率是平均的话,每个数字就约有 40k 个样本,这个样本量已经十分可观了。

还有这种最差情况的,弯曲粘连都出现,人眼识别姑且不能正确的读出数字,何况机器呢。

Captchabad

值得庆幸的是,图片几乎是没有噪点的,这就可以省了滤波的一步,也避免了去完噪点后引起的数字周边出现毛刺。

首先进行二值化,这些基本操作不再赘述了。然后我们将二值化后的图像转化为 numpy 矩阵,

[00000011110000000011111110000000000001000010000000000000000000000000000000000000]
[00000011110000000011100000001111000001000010000000000000000000000000000000000000]
[00000000110000000111000000011000000001000010111110000000000000000000000000000000]
[00000000110000000110111000110000000000100100000010000000000000000000000000000000]
[00000000110000001111111100111110000000011000000100000000000000000000000000000000]
[00000000110000001110011101110011000001100110000100000000000000000000000000000000]
[00000000110000001110011101100011000010000001001000000000000000000000000000000000]
[00000000110000001110011101100011000010000001001000000000000000000000000000000000]
[00000000110000001110011101100011000010000001001000000000000000000000000000000000]
[00000000110000000111111001100110000001000010010000000000000000000000000000000000]
[00000011111100000011110000111100000000111100010000000000000000000000000000000000]
[00000000000000000000000000000000000000000000000000000000000000000000000000000000]

利用竖直投影的办法,找到那些空像素的竖线,将它们作为图片的切割线进行粗分,这样就可以把那些离散的数字给切出来。这种竖直投影切割的方法,大部分验证码识别都是这么做的。

import cv2
import numpy as np
from matplotlib import pyplot as plt
np.set_printoptions(threshold=np.inf)
for z in range(0,100):
    img=cv2.imread('%s.jpg' %z)
    GrayImage=cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
    ret,thresh1=cv2.threshold(GrayImage,127,255,cv2.THRESH_BINARY)
    images = thresh1
    Listx = []
    Listy = []
    mtr=np.array(images)
    for j in range(0,80):
        for i in range(0,25):
            if(mtr[i,j]==0):
                Listx.append(j)
                break
    print(Listx)
    Listy.append(Listx[0]-1)
    for k in range (1,len(Listx)):
        if(Listx[k]!=Listx[k-1]+1):
            Listy.append(Listx[k-1])
            Listy.append(Listx[k])
    Listy.append(Listx[-1])
    print(Listy)
    m=0
    while(m<len(Listy)):
        roi = images[0:,Listy[m]:Listy[m+1]]
        if(Listy[m+1]-Listy[m]>=11):
            cv2.imwrite('C:\\Users\\Gaz\\Desktop\\Cappp\\mul\\%s%s.png' %(z,m),roi)
        else:
            cv2.imwrite('C:\\Users\\Gaz\\Desktop\\Cappp\\sin\\%s%s.png' %(z,m),roi)            
        m=m+2

完成粗分后,需要对图片进行进一步的细分。

常见的图像处理方法有很多种,


下面的步骤就是分类和学习的过程了,于是这个项目算是断头了。
这么弄来,基于SVM向量机的思路很明显了,而且识别率应该也会在90%以上。但实际上,直接把样本扔进CNN里,由于分割完了平均能有40k的样本数据量,足够多,不用担心收敛的问题。关于CNN的代码有机会写好补上。

下面给两篇很好的参考文献:
[1]简献忠, 曹树建, 郭强. SOM聚类与Voronoi图在验证码字符分割中的应用[J]. 计算机应用研究, 2015(9):2857-2861.
[2]An Intuitive Explanation of Convolutional Neural Networks
https://ujjwalkarn.me/2016/08/11/intuitive-explanation-convnets/

Tags:验证码Python
上一篇
下一篇

添加新评论