目录

第一章 绪论

1.1 验证码的概念
1.2 验证码的分类

第二章 验证码识别的研究现状

2.1 验证码识别技术的现状
2.2 几种常见的验证码识别技术
2.3 本章小结

第三章 基于理工大学教务在线验证码的特征分析

3.1 验证码的获取
3.2 验证码的图像特征
3.3 本章小结

第四章 验证码识别

4.1 图像工具简介
4.2 图片处理
4.2.1 图片的预处理
4.2.2 图片的粗分割
4.2.3 图片的再分割
4.3 图片分类
4.4 聚类与基于 SVM 支持向量机的学习
4.5 本章小结

第五章 实验结果及分析

5.1 实验结果
5.2 实验结果分析
5.3 实验的改进方法
5.4 本章小结

第六章 总结

参考文献


绪论

1.1 验证码的概念

随着网络和计算机科学的水平不断提升,人类对信息的依赖越来越深,这也引发了人们对信息安全的重视,验证码因为这一刚需应运而生。验证码 (Captcha,Completely Automated Public Turing test to tell Computers and Humans Apart,全自动区分计算机和人类的图灵测试) 是一种区分用户是计算机还是人的公共全自动程序,目前绝大多数网站都采用了验证码进行强制人机交互以隔绝恶意的信息获取和账户注册。
验证码要求用户在交互式输入验证码显示的文本字符串或者某一数学算式的结果,在一些网络要求较高的网页中往往会采用验证码来避免程序的多次登陆,以保障网站运行的正常以及其他用户的公平性。
验证码的生成原理十分简单,主要包含以下几个步骤:
1) 随机生成一个 hash 信息,通过一个不可逆或者破解难度较高的算法将 hash 信息转换成那个对应的验证码字符串,并以图片形式现实给用户;
2) 用户点击文本框输入验证码所示信息;
3) 检验文本框的输入信息与 session 中的期望值是否一致,如果验证成功,用户可以执行进一步操作,如果验证未通过,则对验证码进行更新,用户需要重新进行验证。
目前网站采用的验证码多为数字和字母组合的形式,随着 OCR(Optical Character Recognition,光学字符识别) 技术的不断完善,以字库作为模板的验证码已不具有使用价值,现多数的验证码都采取了扭曲、变形、重叠、加入噪点等图像处理作为干扰因素,这使得利用计算机进行逆向操作更加的困难。一个有效的抵制计算机自动识别的验证码,其安全性必须达到自动程序识别的成功率小于 0.01%。
1.2 验证码的分类

根据不同的网络应用情况,验证码的形式也多种多样,目前常见的验证码形式如下:
Ⅰ. 基于数字和字母的文本验证码
基于数字和字母的文本验证码是目前使用最为广泛的验证码,其优点在于生成原理简单,对用户友好度较高,可以根据网站的安全要求生成不同程度的验证码图片。在一些流量较小的论坛或个人网站上,还有许多采用四位纯数字验证码的范例。而在一些流量较大的网站中,验证码多采取不定字母 / 不定字母 + 数字的形式,同时在图像上,还采取了扭曲,粘连以及添加噪点作为干扰项。随后此类验证码还衍生出了汉字的验证码,以提高安全性。但在打码平台不断 “完善” 的今天,文本验证码的安全性正在遭受威胁。

Ⅱ. 基于问答形式的验证码
在一些对于安全性不作要求的网站上,还有基于问答形式的验证码。用户通过回答预期的问题通过网站验证,这与本文讨论的主题不相关,故不再赘述。

Ⅲ. 基于图片信息的验证码
基于图片信息的验证码用户友好度相对较高。一般形式的验证码是让用户点击相关 ROI 以通过认证,因此图片库的多少决定了验证码的实用性,目前基于图片信息的验证码使用率稳步上升,有望成为下一种通用的验证码形式。

Ⅳ. 基于声音信息的验证码
以 reCAPTCHA 为代表的基于声音信息的验证码是一种新型验证码,用户通过听取一段音频来输入期望的验证码,这一类验证码的设计主要是为了帮助视障人士进行验证码认证,

本文讨论的是第一种形式的验证码。
验证码识别的研究现状


2.1 验证码识别技术的现状

目前验证码的识别主要是基于光学识别技术 (OCR),然而目前的光学识别技术还无法对经过图像处理的验证码图像进行自动化识别,目前较为可行的方法是通过对验证码的有效分割,对逐个元素进行回归处理,再通过机器学习的数据对得到的验证码进行识别和判断,在下一章中本文会以特定的情景进行验证码识别。
国内外对于验证码的研究都起步较早,国内外学者对验证码中的字符分割问题展开了大量研究,
取得了显著进展。Chellapilla 等人最早开始了验证码识别研究。Huang 等人在 Chellapilla 的研究基础上, 改进其分割方法, 增加了对连通分量的特征垂直投影分析, 提高了分割成
功率。李兴国等人采用改进的滴水算法分割粘连字符, 但在处理字符扭曲倾斜的验证码时, 滴水算法无法准确定位起始滴落点, 造成分割字符笔画的断裂。Franc 等人提出了一种利用隐马尔可夫链 (hidden Markovmodel,HMM) 获取先验知识的粘连字符分割算法, 通过 HMM 计算粘连字符中各像素可以作为分割点的概率, 得到一个分割概率图, 据此选取合适的分割点分割图像, 可成功分割粘连字符, 但该方法依赖于先验知识的获取, 使用范围局限。而打码平台的日渐发展,对验证码的安全性造成了长足的威胁,对于一些网站的验证码,打码平台已经近乎于完美的解决了自动化注册的问题。


还真以为我会这么认认真真的写?Naive!
Naive!
其实我一开始搞验证码是因为这个:

https://github.com/Gazyip/Course_selecter-of-HRBUST

为了实现完全自动化的抢课效果,我当初尝试了 Tesseract-OCR,其实它的识别率在同类软件里算是很高的了,而且开源免费,问题是,大理工的验证码并不能很好的适应,识别结果惨不忍睹。其实 Tesseract-OCR 还有个学习库的,输入特定的字体模板,可以实现学习的效果,问题是这个学习工具要 java 环境,我并不想装 (;-P)

于是就有了今天这篇文章。

先来说一下大体的思路:

既然要做学习,必定需要大量的验证码作为样本:

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 矩阵,

[00000000010000000000111100000000000000111100000000000000000000000000000000000000]
[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/

还是比较懒啊哈哈哈哈哈
201788194159800965660810_600_0.jpg