简单验证码是指验证码图片里的字符,固定不变,或者变化很小的验证码,比如:字符上会有一些感扰点感扰线等情况,或者只有少数几种字体变化和字体大小变化。
这里给出源代码都是非常简单的源代码,学过《C程序设计》的都可以看懂,不会超出书本范围,而且不使用指针,这样VB,Delphi,JAVA 等语言都可以照此写出相应的源代码。
验证码识别可以分为三个大的步骤:预处理,分割,识别。
预处理又可细分为读取图片,二值化,去感扰点,去感扰线等等步骤。
读取图片很简单就是把要识别的验证码图片读取到我们的程序里,网络上常见的验证码图片格式有BMP,JPG,GIF,PNG,其中BMP的图片格式种类很多,但都不复杂,24位BMP格式最简单,除去54字节的文件头,只需要顺序读取BMP文件的字节信息就可以了,256色的也就是8位的BMP格式也很常见,256色BMP图片在文件头后多一个索引表,后面也是顺序存储图片信息的。JPG,GIF,PNG的格式都复杂一点,不过网上都有这些公司或组织发布的source code。
下面以24位BMP格式的源代码进行说明:
#define W 40 //定义验证码图片的宽度 #define H 20 //定义验证码图片的宽度 #define N 4 //定义字符位数,一般是4位 #define BMP_filename "c:\\test.bmp" int x,y,i,s,temp; int YZM[W][H]={0}; int YZM_red[W][H]={0}; int YZM_green[W][H]={0}; int YZM_blue[W][H]={0}; int rec[W]={0}; int lf[N],rt[N],up[N],dw[N]; char result[N]; FILE *fp=fopen(BMP_filename,"rb");//以二进制只读方式打开BMP文件 for(i=0;i<54;i++)temp=fgetc(fp);//跳过54个字节的文件头数据 for(y=H-1;y>=0;y--)//BMP图片数据是倒序存储的,据说是为了下载显示方便,呵呵 { for(x=0;x<W;x++){YZM_red[x][y]=fgetc(fp);YZM_green[x][y]=fgetc(fp);YZM_blue[x][y]=fgetc(fp);} //依次读取每个像素的RGB值 } fclose(fp);//关闭文件
好的,通过以上几个语句就可以把图片信息读取到程序的数组里了,然后对其进行二值化,去感扰等工作。
二值化就是把刚才读取的RGB信息,转换成01数据,0表示空白背景,1表示字符。
for(x=0;x<W;x++)for(y=0;y<H;y++) { if(YZM_red[x][y]*0.3+YZM_green[x][y]*0.6+YZM_blue[x][y]*0.1<200)YZM[x][y]=1;else YZM[x][y]=0; //red*0.3+green*0.6+blue*0.1 是计算亮度的公式 } //如果图片里有感扰点,可以用下面的语句去除。 for(x=1;x<W-1;x++)for(y=1;y<H-1;y++) { if(YZM[x][y]==1 && YZM[x-1][y-1]==0 && YZM[x][y-1]==0 && YZM[x+1][y-1]==0 && YZM[x-1][y]==0 && YZM[x+1][y]==0 && YZM[x-1][y+1]==0 && YZM[x][y+1]==0 && YZM[x+1][y+1]==0) YZM[x][y]=0; //如果一个点的值是1,而且它的周围8个点的值都是0,那么这个点就是感扰点 }
经过这些预处理工作就可以得到一个二进制数组数据了。我们以一组普通的验证码图片为例,
图片缺失
for(y=0;y<H;y++) { for(x=0;x<W;x++) printf(" %c",YZM[x][y]+'0'); printf("\n"); }
我们可以用上面的输出语句进行输出,输出语句在最终的程序中可以删除或者注释掉。
输出结果如下:
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 0 0 0 0 0 1 1 0 0 0 0 0 0 1 1 1 1 0 0 0 1 1 1 1 1 1 1 0 0 0 0 0 0 1 1 0 0 0 0 0 0 0 0 0 1 1 1 0 0 0 0 0 1 1 0 0 1 1 0 0 1 1 0 0 0 0 0 0 0 0 0 0 0 1 1 0 0 0 0 0 0 0 0 1 1 1 1 0 0 0 0 1 1 0 0 0 0 1 0 0 1 1 0 0 0 0 0 0 0 0 0 0 0 1 1 0 1 1 1 0 0 0 0 0 0 1 1 0 0 0 0 1 1 0 0 0 0 0 0 0 1 1 0 1 1 1 0 0 0 0 0 0 0 1 1 1 0 0 1 1 0 0 0 0 0 1 1 0 0 0 0 1 1 0 1 1 1 0 0 0 1 1 1 0 0 1 1 0 0 0 0 0 0 0 0 0 0 0 0 1 1 0 0 0 0 1 1 0 0 0 0 1 1 1 0 0 1 1 0 0 0 0 0 0 0 0 1 1 0 0 0 0 0 0 0 0 0 0 0 1 1 0 0 0 0 1 1 0 0 0 0 1 1 0 0 0 0 1 1 0 0 0 0 0 0 0 1 1 0 0 0 0 0 1 1 0 0 0 0 1 1 0 0 0 0 1 1 0 0 0 0 1 1 0 0 0 0 1 1 0 1 1 0 0 0 0 1 1 0 0 0 0 0 0 1 1 0 0 1 1 0 0 0 0 0 1 1 0 0 0 0 0 1 1 0 0 1 1 0 0 0 1 1 0 0 1 1 0 0 0 0 0 0 0 0 1 1 1 1 0 0 0 0 1 1 1 1 1 1 0 0 0 0 1 1 1 1 0 0 0 0 0 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
for(x=0;x<W;x++) { s=0; for(y=0;y<H;y++)s=s+YZM[x][y]; if(s>0)rec[x]=1;else rec[x]=0;//rec记录数组的断连情况 } for(i=0,x=1;x<W-1;x++)if(rec[x-1]==0 && rec[x]==1 && rec[x+1]==1){i++;lf[i]=x;}//计算每个字符的左边界 for(i=0,x=1;x<W-1;x++)if(rec[x-1]==1 && rec[x]==1 && rec[x+1]==0){i++;rt[i]=x;}//计算每个字符的右边界 for(i=1;i<=N;i++)for(x=0;x<W;x++)for(y=0;y<H;y++) if(x>=lf[i] && x<=rt[i] && YZM[x][y]==1)YZM[x][y]=i; for(y=H-1;y>=0;y--)for(x=0;x<W;x++)for(i=1;i<=N;i++)if(YZM[x][y]==i)up[i]=y;//计算每个字符的上边界
经过上面的分割语句,我们就完成了分割工作,我们可以用输出语句进行输出,输出结果如下:
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 0 0 0 0 0 2 2 0 0 0 0 0 0 3 3 3 3 0 0 0 4 4 4 4 4 4 4 0 0 0 0 0 0 1 1 0 0 0 0 0 0 0 0 0 2 2 2 0 0 0 0 0 3 3 0 0 3 3 0 0 4 4 0 0 0 0 0 0 0 0 0 0 0 1 1 0 0 0 0 0 0 0 0 2 2 2 2 0 0 0 0 3 3 0 0 0 0 3 0 0 4 4 0 0 0 0 0 0 0 0 0 0 0 1 1 0 1 1 1 0 0 0 0 0 0 2 2 0 0 0 0 3 3 0 0 0 0 0 0 0 4 4 0 4 4 4 0 0 0 0 0 0 0 1 1 1 0 0 1 1 0 0 0 0 0 2 2 0 0 0 0 3 3 0 3 3 3 0 0 0 4 4 4 0 0 4 4 0 0 0 0 0 0 0 0 0 0 0 0 1 1 0 0 0 0 2 2 0 0 0 0 3 3 3 0 0 3 3 0 0 0 0 0 0 0 0 4 4 0 0 0 0 0 0 0 0 0 0 0 1 1 0 0 0 0 2 2 0 0 0 0 3 3 0 0 0 0 3 3 0 0 0 0 0 0 0 4 4 0 0 0 0 0 1 1 0 0 0 0 1 1 0 0 0 0 2 2 0 0 0 0 3 3 0 0 0 0 3 3 0 4 4 0 0 0 0 4 4 0 0 0 0 0 0 1 1 0 0 1 1 0 0 0 0 0 2 2 0 0 0 0 0 3 3 0 0 3 3 0 0 0 4 4 0 0 4 4 0 0 0 0 0 0 0 0 1 1 1 1 0 0 0 0 2 2 2 2 2 2 0 0 0 0 3 3 3 3 0 0 0 0 0 4 4 4 4 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
最后我们就可以进行识别工作,识别的方法有很多,可以进行逐点的精确识别,也可以进行模糊识别等等,甚至也可以不进行去感扰和分割工作直接进行识别(主要用于粘连的验证码),这都要根据具体情况来具体分析用什么方法最有效,简单验证码的识别则没有那么复杂,用很多方法都可以识别,这里使用的是从每个字符的左上边界开始,精确判断5行3列15个点来进行识别。
for(i=1;i<=N;i++) { if( YZM[lf[i]+0][up[i]+0]==0 && YZM[lf[i]+1][up[i]+0]==0 && YZM[lf[i]+2][up[i]+0]==0 && YZM[lf[i]+0][up[i]+1]==0 && YZM[lf[i]+1][up[i]+1]==0 && YZM[lf[i]+2][up[i]+1]==i && YZM[lf[i]+0][up[i]+2]==0 && YZM[lf[i]+1][up[i]+2]==i && YZM[lf[i]+2][up[i]+2]==i && YZM[lf[i]+0][up[i]+3]==i && YZM[lf[i]+1][up[i]+3]==i && YZM[lf[i]+2][up[i]+3]==0 && YZM[lf[i]+0][up[i]+4]==i && YZM[lf[i]+1][up[i]+4]==i && YZM[lf[i]+2][up[i]+4]==0 )result[i-1]='0'; if( YZM[lf[i]+0][up[i]+0]==0 && YZM[lf[i]+1][up[i]+0]==0 && YZM[lf[i]+2][up[i]+0]==i && YZM[lf[i]+0][up[i]+1]==0 && YZM[lf[i]+1][up[i]+1]==i && YZM[lf[i]+2][up[i]+1]==i && YZM[lf[i]+0][up[i]+2]==i && YZM[lf[i]+1][up[i]+2]==i && YZM[lf[i]+2][up[i]+2]==i && YZM[lf[i]+0][up[i]+3]==0 && YZM[lf[i]+1][up[i]+3]==0 && YZM[lf[i]+2][up[i]+3]==i && YZM[lf[i]+0][up[i]+4]==0 && YZM[lf[i]+1][up[i]+4]==0 && YZM[lf[i]+2][up[i]+4]==i )result[i-1]='1'; if( YZM[lf[i]+0][up[i]+0]==0 && YZM[lf[i]+1][up[i]+0]==0 && YZM[lf[i]+2][up[i]+0]==i && YZM[lf[i]+0][up[i]+1]==0 && YZM[lf[i]+1][up[i]+1]==i && YZM[lf[i]+2][up[i]+1]==i && YZM[lf[i]+0][up[i]+2]==i && YZM[lf[i]+1][up[i]+2]==i && YZM[lf[i]+2][up[i]+2]==0 && YZM[lf[i]+0][up[i]+3]==0 && YZM[lf[i]+1][up[i]+3]==0 && YZM[lf[i]+2][up[i]+3]==0 && YZM[lf[i]+0][up[i]+4]==0 && YZM[lf[i]+1][up[i]+4]==0 && YZM[lf[i]+2][up[i]+4]==0 )result[i-1]='2'; if( YZM[lf[i]+0][up[i]+0]==0 && YZM[lf[i]+1][up[i]+0]==i && YZM[lf[i]+2][up[i]+0]==i && YZM[lf[i]+0][up[i]+1]==i && YZM[lf[i]+1][up[i]+1]==i && YZM[lf[i]+2][up[i]+1]==0 && YZM[lf[i]+0][up[i]+2]==0 && YZM[lf[i]+1][up[i]+2]==0 && YZM[lf[i]+2][up[i]+2]==0 && YZM[lf[i]+0][up[i]+3]==0 && YZM[lf[i]+1][up[i]+3]==0 && YZM[lf[i]+2][up[i]+3]==0 && YZM[lf[i]+0][up[i]+4]==0 && YZM[lf[i]+1][up[i]+4]==0 && YZM[lf[i]+2][up[i]+4]==0 )result[i-1]='3'; if( YZM[lf[i]+0][up[i]+0]==0 && YZM[lf[i]+1][up[i]+0]==0 && YZM[lf[i]+2][up[i]+0]==0 && YZM[lf[i]+0][up[i]+1]==0 && YZM[lf[i]+1][up[i]+1]==0 && YZM[lf[i]+2][up[i]+1]==0 && YZM[lf[i]+0][up[i]+2]==0 && YZM[lf[i]+1][up[i]+2]==0 && YZM[lf[i]+2][up[i]+2]==0 && YZM[lf[i]+0][up[i]+3]==0 && YZM[lf[i]+1][up[i]+3]==0 && YZM[lf[i]+2][up[i]+3]==i && YZM[lf[i]+0][up[i]+4]==0 && YZM[lf[i]+1][up[i]+4]==i && YZM[lf[i]+2][up[i]+4]==i )result[i-1]='4'; if( YZM[lf[i]+0][up[i]+0]==i && YZM[lf[i]+1][up[i]+0]==i && YZM[lf[i]+2][up[i]+0]==i && YZM[lf[i]+0][up[i]+1]==i && YZM[lf[i]+1][up[i]+1]==i && YZM[lf[i]+2][up[i]+1]==0 && YZM[lf[i]+0][up[i]+2]==i && YZM[lf[i]+1][up[i]+2]==i && YZM[lf[i]+2][up[i]+2]==0 && YZM[lf[i]+0][up[i]+3]==i && YZM[lf[i]+1][up[i]+3]==i && YZM[lf[i]+2][up[i]+3]==0 && YZM[lf[i]+0][up[i]+4]==i && YZM[lf[i]+1][up[i]+4]==i && YZM[lf[i]+2][up[i]+4]==i )result[i-1]='5'; if( YZM[lf[i]+0][up[i]+0]==0 && YZM[lf[i]+1][up[i]+0]==0 && YZM[lf[i]+2][up[i]+0]==i && YZM[lf[i]+0][up[i]+1]==0 && YZM[lf[i]+1][up[i]+1]==i && YZM[lf[i]+2][up[i]+1]==i && YZM[lf[i]+0][up[i]+2]==i && YZM[lf[i]+1][up[i]+2]==i && YZM[lf[i]+2][up[i]+2]==0 && YZM[lf[i]+0][up[i]+3]==i && YZM[lf[i]+1][up[i]+3]==i && YZM[lf[i]+2][up[i]+3]==0 && YZM[lf[i]+0][up[i]+4]==i && YZM[lf[i]+1][up[i]+4]==i && YZM[lf[i]+2][up[i]+4]==0 )result[i-1]='6'; if( YZM[lf[i]+0][up[i]+0]==i && YZM[lf[i]+1][up[i]+0]==i && YZM[lf[i]+2][up[i]+0]==i && YZM[lf[i]+0][up[i]+1]==0 && YZM[lf[i]+1][up[i]+1]==0 && YZM[lf[i]+2][up[i]+1]==0 && YZM[lf[i]+0][up[i]+2]==0 && YZM[lf[i]+1][up[i]+2]==0 && YZM[lf[i]+2][up[i]+2]==0 && YZM[lf[i]+0][up[i]+3]==0 && YZM[lf[i]+1][up[i]+3]==0 && YZM[lf[i]+2][up[i]+3]==0 && YZM[lf[i]+0][up[i]+4]==0 && YZM[lf[i]+1][up[i]+4]==0 && YZM[lf[i]+2][up[i]+4]==0 )result[i-1]='7'; if( YZM[lf[i]+0][up[i]+0]==0 && YZM[lf[i]+1][up[i]+0]==0 && YZM[lf[i]+2][up[i]+0]==i && YZM[lf[i]+0][up[i]+1]==0 && YZM[lf[i]+1][up[i]+1]==i && YZM[lf[i]+2][up[i]+1]==i && YZM[lf[i]+0][up[i]+2]==i && YZM[lf[i]+1][up[i]+2]==i && YZM[lf[i]+2][up[i]+2]==0 && YZM[lf[i]+0][up[i]+3]==0 && YZM[lf[i]+1][up[i]+3]==i && YZM[lf[i]+2][up[i]+3]==i && YZM[lf[i]+0][up[i]+4]==0 && YZM[lf[i]+1][up[i]+4]==0 && YZM[lf[i]+2][up[i]+4]==i )result[i-1]='8'; if( YZM[lf[i]+0][up[i]+0]==0 && YZM[lf[i]+1][up[i]+0]==0 && YZM[lf[i]+2][up[i]+0]==i && YZM[lf[i]+0][up[i]+1]==0 && YZM[lf[i]+1][up[i]+1]==i && YZM[lf[i]+2][up[i]+1]==i && YZM[lf[i]+0][up[i]+2]==i && YZM[lf[i]+1][up[i]+2]==i && YZM[lf[i]+2][up[i]+2]==0 && YZM[lf[i]+0][up[i]+3]==i && YZM[lf[i]+1][up[i]+3]==i && YZM[lf[i]+2][up[i]+3]==0 && YZM[lf[i]+0][up[i]+4]==0 && YZM[lf[i]+1][up[i]+4]==i && YZM[lf[i]+2][up[i]+4]==i )result[i-1]='9'; } printf("%s",result);//输出识别结果