八个比特

验证码识别

字数统计: 924阅读时长: 3 min
2019/04/07 Share

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

先来说一下大体的思路:

验证码样本的抓取:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
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 矩阵,

1
2
3
4
5
6
7
8
9
10
11
12
[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=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.
2An Intuitive Explanation of Convolutional Neural Networks
https://ujjwalkarn.me/2016/08/11/intuitive-explanation-convnets/

CATALOG