文件组成
为了实现训练以及识别的过程,我总共设计了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
由于牵涉到文件写入,而且与其他代码不存在依赖关系,所以把他独立出来方便修改。
代码语言:javascript复制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的矩阵,再转化为单行数组。数组中每一个点就是一个特征。但是在后来的评估中,为了提高识别准确率,又加入了三个特征,即:空白的联通块数、字符的边界像素个数、字符的填充像素个数。事实证明这三个特征极大的提高了验证的准确性。
代码语言:javascript复制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数据集的标准格式进行处理。