用Python实现china-pub登录验证码的识别

文/图 luxijun
自己一直想写一个简单的验证码识别程序,能明白识别过程的原理就好,于是近日试验了一下。常去china-pub买书,发现它的验证码色彩清新数字格式工整,很适合初学者拿来练习,就从它入手吧。 
我使用的是Python 2.5.1,查看其文档发现没有可以处理图像的模块,上网搜索Python Imaging Library(PIL)并下载了PIL-1.1.6.win32-py2.5.exe和pil-handbook.pdf,最终解决了问题。 

图像处理 
查看验证码的属性,大小为40×10,也就是说四个字符每个数字占的大小为10×10,图片生成地址asp">http://www.china-pub.com/edition06/imgchk/validatecode.asp。在识别过程中,大家可以在本地保存几张做测试。在china-pub主页多刷几次,尽量选取重复数字少的验证码图片保存到本地计算机,这样只要处理几张图片就可以得到0~9的全部数字,然后分析数字的特征和它们之间的差别。 

import Image 
def img2four(image): 
width = 10 
height = 10 
left=0 
upper=0 
right=10 
lower=10 

c = 4 
while(c): 
box = (left, upper, right, lower) 
im = Image.open(image) 
region = im.crop(box) 
region.convert(L).save(crop_+str(4-c)+.bmp) 
left = left + 10 
right = right + 10 
c = c – 1 

这段代码可以将验证码图像分割和去色,并分别保存为crop_0.bmp~crop_3.bmp,如将验证码3519分割为3、5、1、9并保存。也可以自己将其重命名为3.bmp、5.bmp等,方便后面代码测试时直接用for循环就可以测试10张图片。代码中的crop()用于分割图像,其参数给出分割的形状。convert(L)是把图像转化成8bit灰度图,0代表黑,255代表白,这样每个数字图片中只包含两种颜色值,字符色和背景色,方便比较和识别。在PIL中,坐标系是图片的左上角为(0,0)的。得到0~9的单个点阵图片后,我们用VC6.0全部打开,方便观察和比较,如图1所示。 
 
图1 

验证码识别 
  由于所有的点阵图片的背景色都是相同的,而数字颜色则是不同的,所以要想看某个数字的全部pixel值(或者你认为眼睛看的颜色不是很准的话),可以写如下程序。 

def printPixel(image): 
img = Image.open(image) 

for y in range(0, 10): 
for x in range(0, 10): 
print img.getpixel((x,y)), 
print 

  printPixel()的参数image为图片路径。通过这个函数的调用,我们可以按图的格式打印出各个像素的值,细看是可以看出相同的数值连在一起构成的“数字”的,而且可以知道背景色的值为238。 
开始我是一点点用眼睛去找各个数字之间的相同和不同的,比如只有5和7经过(0,0)点,然后再找一个它俩之间的不同点就可以判断出是5还是7,然后再找其他数字的特点与差别。没想到通过if语句竟然可以识别出全部数字(太顺利倒让我不太适应了)。用if语句的好处是不用找多个关键点,只要一个一个找就好了,数字也能被一个一个的识别出来。 

def cross(color): 
if color !=238: 
return True 
else: 
return False 
def recognize(image): 
bgcolor = 238 
img = Image.open(image) 

p = img.getpixel((1,8)) 
if cross(p): 
return 7 

p = img.getpixel((0,0)) 
if cross(p): 
return 5 

p = img.getpixel((2,1)) 
if cross(p): 
return 1 

p = img.getpixel((3,1)) 
if cross(p): 
return 4 

p = img.getpixel((1,1)) 
if cross(p): 
# not 1, must be 6 
return 6 

p = img.getpixel((1,7)) 
if cross(p): 
return 2 

p = img.getpixel((2,5)) 
if cross(p): 
return 9 

p = img.getpixel((5,4)) 
if cross(p): 
# not 9, must be 0 
return 0 

p = img.getpixel((1,4)) 
if cross(p): 
return 8 
else: 
return 3 

def getCode(image): 
img2four(image) 
n0 = recognize(crop_0.bmp) 
n1 = recognize(crop_1.bmp) 
n2 = recognize(crop_2.bmp) 
n3 = recognize(crop_3.bmp) 

# remove crop files 
import os 
#import time 
#time.sleep(3) 
#if you want to see the temp images, sleep 3 secs 
if os.path.exists(crop_0.bmp): 
os.remove(crop_0.bmp) 

if os.path.exists(crop_1.bmp): 
os.remove(crop_1.bmp) 

if os.path.exists(crop_2.bmp): 
os.remove(crop_2.bmp) 

if os.path.exists(crop_3.bmp): 
os.remove(crop_3.bmp) 

return str(n0) + str(n1) + str(n2) + str(n3) 

函数getCode()完成调用图片分割函数、数字识别、删除临时图片文件和返回识别处的结果的工作。识别过程是很快的,所以分割后的临时图片文件在文件夹下根本看不见,可以去掉代码中的注释符号,让线程睡眠3秒钟来查看。 
总觉得用眼睛一点一点找不够智能,所以我又写了个compare.py来比较和统计从(0,0)到(9,9)这100个点都有哪些数字通过,然后打印出通过的比较少的(0~2个)那些坐标来做判断。如果某点只有一个数字经过,那么一下就可以判断出是这个数字,比如(1,8)只有7经过。根据这个统计结果,其他数字的判断点的选取也就变得非常简单了,最多两个点就可以判断一个数字。compare.py的代码如下。

import Image 
def printPixel(image): 
# get a bmp files all pixel 
img = Image.open(image) 
l=[] 
for y in range(0, 10): 
for x in range(0, 10): 
l.append(img.getpixel((x,y))) 

return l 

def getxy(number): 
#convert lists index to the tuple of pixel 
x = number % 10 
y = number / 10 
return (x, y) 

def findDiff(): 
# find different point between 0 and 9 
list = [] 
for i in range(0, 10): 
list.append(printPixel(str(i)+.bmp)) 

for j in range(0,100): 
count = 0 
num = [] 
for k in range(0, 10): 
if list[k][j] != 238: 
num.append(k) 
count = count + 1 
if count < 3 and count > 0: 
print count = , count, , pixel is , getxy(j),, num is ,num 

if __name__ == "__main__": 
findDiff() 

其中的printPixel()用来以list的形式返回其参数所传递进去的图像像素值,list长度为100,是以行为主的存储方式存储的。getxy()用来将printPixel()所返回的list的索引值转化为坐标值的形式,以便打印输出给用户观看。findDiff()用来对0.bmp到9.bmp这10个图像进行统计和打印结果。 

GUI界面 
单击“Get bmp”按钮从网上获取验证码图片文件,“Get code”调用getCode()函数获取验证码的数值并显示。初始化时使用的图片是python的图片。因为图像很简单,所以识别是可以做到100%正确的。初始化的界面如图2所示,识别后的界面如图3所示,具体的代码实现,大家可以看随文提供的源代码,这里就不贴出来了。 
   
图2 
 
图3 

后记 
第一次听说验证码识别时让我十分吃惊,不明白高手是怎么做到的。上网找资料明白是通过关键点的判断来实现的,所以花了一个下午终于完成了编写,希望能给其他想写验证码识别程序的朋友提供一点思路

posted @ 2012-05-19 14:46  icamel  阅读(736)  评论(0编辑  收藏  举报