Mythsman


努力工作,认真生活。


自制正方软件系统验证码的识别程序(2/4)

文件组成

为了实现训练以及识别的过程,我总共设计了6个文件,作用如下:

文件作用split.py用于将验证码中四个小字符分割出来,并分类保存。util.py用于保存一些常用的函数logistic_sgd.py这是官网上的样例代码,实现了softmax的分类算法,当然还要进行下修改package.py这个用来将图像数据进行处理并打包压缩成为方便使用的数据集train.py这是开始训练的接口check.py这是利用训练结果进行识别的接口

还有两个文件夹:

文件夹作用recognized/用于保存可作为训练集的图片,图片以内容的实际值命名number/用于保存分离子图后的小图片,以类似0/ 1/ a/....的文件夹的形式进行分类

运行依赖

这里我使用的是python2.7版,需要以下必要的运行库的支持:

theano:

安装$sudo pip install theano

目的:这是学习算法的运行依赖

numpy:

安装$sudo apt-get install python-numpy

目的:提供了很多必要的数学运算方法

PIL:

安装$sudo apt-get install python-pil

目的:进行图片的处理

split.py

由于牵涉到文件写入,而且与其他代码不存在依赖关系,所以把他独立出来方便修改。

import os
import numpy as np
import Image
import shutil

print '... spliting'

source_dir='recognized/'
dest_dir='number/'

if os.path.exists(dest_dir):
	shutil.rmtree(dest_dir)

os.mkdir(dest_dir)
list=os.listdir(source_dir)
directory='123456780abcdefghijklmnpqrstuvwxy'
cnt={}
for i in directory:
	if not os.path.exists(dest_dir+i):
		os.mkdir(dest_dir+i+'/')
	cnt[i]=0

for name in list:
	img=Image.open(source_dir+name)
	img.crop((5,2,17,20)).save(dest_dir+name[0]+'/'+str(cnt[name[0]]),'png')
	cnt[name[0]]+=1
	img.crop((17,2,29,20)).save(dest_dir+name[1]+'/'+str(cnt[name[1]]),'png')
	cnt[name[1]]+=1
	img.crop((29,2,41,20)).save(dest_dir+name[2]+'/'+str(cnt[name[2]]),'png')
	cnt[name[2]]+=1
	img.crop((41,2,53,20)).save(dest_dir+name[3]+'/'+str(cnt[name[3]]),'png')
	cnt[name[3]]+=1

其实也很简单,就是从recognized/中读取源图,分割成4张子图保存在number/中对应的文件夹下,并以数字序数命名。这里用了PIL进行图片分割。但其实这里的难点不是代码本身,而是要观察每张图片分隔的界限。由于普通图片查看器的并没有标尺,为了观察的更细致,我用的是GIMP(Ubuntu中的ps)进行观察。

为了方便纠正训练错误,在每次训练前我会把之前的图片删除重新写入。

package.py

这个文件用来将之前分割过的图片进行转化,也是最关键的部分。

我的思路就是将每个图片用PIL转化成灰度图,在转化为二值图,成为一个12*18的矩阵,再转化为单行数组。数组中每一个点就是一个特征。但是在后来的评估中,为了提高识别准确率,又加入了三个特征,即:空白的联通块数、字符的边界像素个数、字符的填充像素个数。事实证明这三个特征极大的提高了验证的准确性。

import cPickle,os,Image,gzip
import numpy as np
from util import *

print '... packaging'

source_dir='number/'

os.chdir(source_dir)
list=os.listdir('./')
data=[]
ans=[]

dict='012345678abcdefghijklmnpqrstuvwxy'
for num in list:
	os.chdir(num)
	list_pic=os.listdir('./')
	for pic in list_pic:
		img=Image.open(pic)
		img=blur1(img)
		cnt1=seed_fill(img)
		cnt2=count_fill(img)
		cnt2=(cnt2-50)/10.0
		cnt3=count_border(img)
		cnt3=(cnt3-50)/10.0
		img=blur2(img)
		arr=np.array(img)
		arr=arr.reshape(12*18).astype('int').astype('bool').astype('float32')
		arr=arr.tolist()
		arr.append(cnt1)
		arr.append(cnt2)
		arr.append(cnt3)
		arr=np.array(arr).astype('float32')
		data.append(arr)
		ans.append(float(dict.find(num)))
	os.chdir('../')

os.chdir('../')
data=np.array(data)
ans=np.array(ans)

size=len(data)

rand_arr=np.arange(size)
np.random.shuffle(rand_arr)

rand_data=[]
rand_ans=[]

for i in rand_arr:
	rand_data.append(data[i])
	rand_ans.append(ans[i])

rand_data=np.array(rand_data)
rand_ans=np.array(rand_ans)

len_train=size*0.6
len_valid=size*0.2
len_test=size-len_train-len_valid

output=((rand_data[0:len_train],rand_ans[0:len_train]),(rand_data[len_train:len_train+len_valid],rand_ans[len_train:len_train+len_valid]),(rand_data[size-len_test:size],rand_ans[size-len_test:size]))

outputfile=open('vericode.pkl','wb')
cPickle.dump(output,outputfile)
outputfile.close()

outputfile=open('vericode.pkl','rb')
outputgzipfile=gzip.open('vericode.pkl.gz','wb')
outputgzipfile.writelines(outputfile)
outputfile.close()
outputgzipfile.close()
os.remove('vericode.pkl')

没写注释,就列下注意点吧:

  • 在进行图像格式处理的过程中,很多小细节需要注意,特别是图片到数组的格式转化。
  • 对于新加特征值的计算,我放在了util.py中实现。
  • 对于新加的特征值还要进行特征值的优化,用以提高迭代效率。
  • 对数据需要进行训练集、评估集和测试集分类,比例为6:2:2,并且首先需要进行随机化处理。
  • 输出的验证集我是用cPickle直接输出之后再用gzip进行了压缩。
  • 尤其注意输出的格式,我是用的mnist数据集的标准格式进行处理。