一个简单的验证码识别

来源:互联网 发布:mac os 10.7软件 编辑:程序博客网 时间:2024/05/22 12:18

作为一名刚接触编程一年的同学,第一次接触图片处理,所以处理办法什么的比较简单无脑。

各位大神如果看到了请轻喷……   

说到验证码识别,想必大家都知道基本的步骤:1.预处理 2.灰度化 3.二值化 4.去噪 5.分割 6.识别

起初做这个的时候,组长给我们的验证码图片全部是jpeg的,我在很多博客以及文库中看到的都是对bmp进行处理的,那对于我这种超级新手来说,当然是拿bmp来进行处理了。

因为我们这个识别需要在linux下来进行,所以必然第一步就是将 jpeg格式的验证码转换为 bmp了。

我在网上找了很多方法,这里就贴出一种好了

将图片格式弄好之后,接下来就是对 bmp 做一个大致的了解了。

1:BMP文件组成

  BMP文件由文件头、位图信息头、颜色信息和图形数据四部分组成。
  2:BMP文件头(14字节)
  BMP文件头数据结构含有BMP文件的类型、文件大小和位图起始位置等信息。
  其结构定义如下:
  typedef struct tagBITMAPFILEHEADER
  {
  WORD bfType; // 位图文件的类型,必须为BM(1-2字节)
  DWORD bfSize; // 位图文件的大小,以字节为单位(3-6字节)
  WORD bfReserved1; // 位图文件保留字,必须为0(7-8字节)
  WORD bfReserved2; // 位图文件保留字,必须为0(9-10字节)
  DWORD bfOffBits; // 位图数据的起始位置,以相对于位图(11-14字节)
  // 文件头的偏移量表示,以字节为单位
  } BITMAPFILEHEADER;
  3:位图信息头(40字节)
  BMP位图信息头数据用于说明位图的尺寸等信息。
  typedef struct tagBITMAPINFOHEADER{
  DWORD biSize; // 本结构所占用字节数(15-18字节)
  LONG biWidth; // 位图的宽度,以像素为单位(19-22字节)
  LONG biHeight; // 位图的高度,以像素为单位(23-26字节)
  WORD biPlanes; // 目标设备的级别,必须为1(27-28字节)
  WORD biBitCount;// 每个像素所需的位数,必须是1(双色),(29-30字节)
  // 4(16色),8(256色)或24(真彩色)之一
  DWORD biCompression; // 位图压缩类型,必须是 0(不压缩),(31-34字节)
  // 1(BI_RLE8压缩类型)或2(BI_RLE4压缩类型)之一
  DWORD biSizeImage; // 位图的大小,以字节为单位(35-38字节)
  LONG biXPelsPerMeter; // 位图水平分辨率,每米像素数(39-42字节)
  LONG biYPelsPerMeter; // 位图垂直分辨率,每米像素数(43-46字节)
  DWORD biClrUsed;// 位图实际使用的颜色表中的颜色数(47-50字节)
  DWORD biClrImportant;// 位图显示过程中重要的颜色数(51-54字节)
  } BITMAPINFOHEADER;
  4:颜色表
  颜色表用于说明位图中的颜色,它有若干个表项,每一个表项是一个RGBQUAD类型的结构,定义一种颜色。RGBQUAD结构的定义如下:
  typedef struct tagRGBQUAD {
  BYTE rgbBlue;// 蓝色的亮度(值范围为0-255)
  BYTE rgbGreen; // 绿色的亮度(值范围为0-255)
  BYTE rgbRed; // 红色的亮度(值范围为0-255)
  BYTE rgbReserved;// 保留,必须为0
  } RGBQUAD;
  颜色表中RGBQUAD结构数据的个数有biBitCount来确定:
  当biBitCount=1,4,8时,分别有2,16,256个表项;
  当biBitCount=24时,没有颜色表项。
  位图信息头和颜色表组成位图信息,BITMAPINFO结构定义如下:
  typedef struct tagBITMAPINFO {
  BITMAPINFOHEADER bmiHeader; // 位图信息头
  RGBQUAD bmiColors[1]; // 颜色表
  } BITMAPINFO;
  5:位图数据
  位图数据记录了位图的每一个像素值,记录顺序是在扫描行内是从左到右,扫描行之间是从下到上。位图的一个像素值所占的字节数:
  当biBitCount=1时,8个像素占1个字节;
  当biBitCount=4时,2个像素占1个字节;
  当biBitCount=8时,1个像素占1个字节;
  当biBitCount=24时,1个像素占3个字节;
  Windows规定一个扫描行所占的字节数必须是
  4的倍数(即以long为单位),不足的以0填充,
  biSizeImage = ((((bi.biWidth * bi.biBitCount) + 31) & ~31) / 8) * bi.biHeight;

这个在百度百科大家可以做个详细的了解,这里就不多说了。

验证码图片:        

1.预处理:

接下来就是对图片进行读取,

2.灰度化:

对其各个像素的GBR值进行灰度化处理,我用的公式是求平均值的方法,比较方便,效果也还不错。,将求得的平均值存入一个二维数组中,方便后面的处理。

3.二值化

二值化有很多种算法,我试了试大津法(最大方差法)还有自适应阈值法,不知道什么原因,二值化出来的图片简直把图片就给毁了。。。。最后还是将图片的GBR信息打印出来,自己找了个临界值,效果还不错,二值化之后的噪点也只有几个。后面的处理就方便多了。将大于这个临界值的像素点设为255,小于的设为0.出来的图像就完全黑白二值了。

4.去噪

去噪,看了看几张验证码,发现字母组成最少的就是i,j上面的点了,4个像素点组成,于是将图片中一个像素点周围的8个点进行判断,如果这个点周围有不超过三个黑点,则证明这个点为噪点,需要去除,需要注意的是边界周围只有5个点,需要进行判断是否为边界。

5.分割

因为我们的BMP图片是有间据的,所以分割就用最简单的一种了

先将去噪完后的BMP像素数组在按自左向右的基础上从上往下找,找到的第一个黑点即为字符的左边界,再向右,找到的一列全不为黑点的话,则上一列即为字符的右边界,再在左边界和右边界的区间下,从下往上按行遍历,找到字符的下边界,再从上往下找到上边界,其他字符同理。就可以将图片分割出来了。

6.识别

我用的识别方法还是比较麻烦的,先是对验证码的字符进行输出(0,1组成),然后再找他们之间的不同相似之处,所以不是很方便,这里也就不细提了,看代码就应该理解了。

下面是代码:

#include <iostream>#include <cstdlib>#include <cstdio>#include <cstring>using namespace std;void OtsuThreshold(int *p_data){int i,j;int nWidth = 70;int nHeight = 30;for(j = 0;j < nHeight; j++){//二值化实现for(i = 0; i < nWidth ; i++){if(p_data[j * nWidth +i] < 38)p_data[j * nWidth + i] = 0;else p_data[j * nWidth + i] = 255;}}}void Print(int graph[][70]){ int i,j;for(i = 0; i < 30; i++){ for(j = 0; j < 70; j++){if(graph[i][j] == 255){// printf(" ");graph[i][j] = 1;}// else printf("*");}// printf("\n");}// printf("\n\n\n\n");}void duibi(int z[][20][20],int bl1,int br1,int bl2,int br2,int bl3,int br3,int bl4,int br4){int jianju[4];int i;jianju[0] = br1-bl1+1;jianju[1] = br2-bl2+1;jianju[2] = br3-bl3+1;jianju[3] = br4-bl4+1;for(i = 0; i < 4; i++){if(jianju[i] <= 5)//<=5                              字符占  小于等于5列的{if(jianju[i] == 5){if(z[i][0][0] == 0)printf("r");else if(z[i][0][1] == 1)printf("i");else printf("t");}else printf("i");}else if(jianju[i] == 6)//6                     字符占   6列{if(z[i][0][1] == 0){if(z[i][0][4] == 1)printf("t");else printf("s");}else if(z[i][0][2] == 1)printf("j");else if(z[i][0][4] == 1)printf("t");else printf("f");}else if(jianju[i] == 7)//7{if(z[i][0][0] == 1 && z[i][0][1] == 1 && z[i][0][2] == 1){if(z[i][0][6] == 1)printf("e");else if(z[i][0][3] == 0 && z[i][0][4] == 0 && z[i][0][5] == 0 && z[i][0][6] == 0)printf("c");else printf("f");}else if(z[i][0][0] == 1 && z[i][0][1] == 1){if(z[i][0][5] == 1){if(z[i][1][1] == 1)printf("t");else printf("e");}else if(z[i][0][4] == 1)printf("r");else printf("f");}else if(z[i][0][0] == 1){printf("3"); }else if(z[i][0][2] == 1 && z[i][0][3] == 0)printf("p");else if(z[i][0][0] == 0 && z[i][0][1] == 0 && z[i][0][2] == 0)printf("z");else if(z[i][0][0] == 0 && z[i][0][1] == 0 && z[i][0][2] == 1 && z[i][0][3] == 1) printf("h");else printf("r");}else if(jianju[i] == 8)//8{if(z[i][0][0] == 1 && z[i][1][1] == 1 && z[i][2][2] == 1 && z[i][3][3] == 1)printf("j");else if(z[i][0][0] == 1 && z[i][1][1] == 1 && z[i][2][2] == 1)printf("d");else if(z[i][0][0] == 1 && z[i][1][1] == 1){if(z[i][3][3] == 1 &&z[i][4][4] == 1)printf("c");else if(z[i][3][3] == 1 && z[i][4][4] ==0)printf("a");else printf("f");}else if(z[i][0][0] == 1 && z[i][2][2] == 1){if(z[i][1][0] == 0) printf("3");else if(z[i][4][4] == 1 && z[i][0][1] == 0)printf("2");else printf("a");}else if(z[i][0][0] == 1 && z[i][6][6] == 1)printf("c");else if(z[i][0][0] == 1 && z[i][3][3] == 1)printf("g");else if(z[i][0][0] == 1) printf("s");else if(z[i][1][1] == 1)printf("2");else if(z[i][2][2] != 1)printf("n");else if(z[i][4][4] == 1){if(z[i][5][5] == 1) printf("u");else printf("7");}else if(z[i][7][7] == 1) printf("z");else printf("h");}else if(jianju[i] == 9) //9{if(z[i][0][0] == 1 && z[i][3][3] == 1 && z[i][4][4] == 0 && z[i][5][5] == 0 && z[i][6][6] == 0 && z[i][7][7] ==0 && z[i][8][8] == 0)printf("a");else if(z[i][2][2] == 0 && z[i][3][3] ==1 && z[i][4][4] == 0 && z[i][5][5] == 0 && z[i][6][6] == 1 && z[i][7][7] == 1)printf("e");else if(z[i][0][0] == 1 && z[i][1][1] == 1 &&z[i][2][2]== 1)printf("d");else if(z[i][1][1] == 0 && z[i][2][2] == 0 && z[i][4][4] ==1 && z[i][5][5] == 1 && z[i][7][7] == 0){if(z[i][0][5] == 1) printf("g");else if(z[i][0][7] == 0) printf("n");else printf("p");}else if(z[i][0][0] ==0 && z[i][1][1] == 0 && z[i][3][3] == 1 && z[i][4][4] == 1 && z[i][5][5] == 0 && z[i][7][7] ==1 && z[i][8][8] ==1){if(z[i][0][3] == 1 && z[i][10][0] == 0)printf("v");else if(z[i][0][3] == 1 && z[i][10][0] == 1) printf("y");else printf("7");}else if(z[i][0][0] == 0 && z[i][1][1] ==0 && z[i][2][2] ==0 && z[i][3][3] ==1 &&z[i][4][4] == 1){printf("y");}else if(z[i][0][0] == 1 && z[i][1][1] ==1 && z[i][2][2] == 0){if(z[i][3][3] == 1)printf("6");else if(z[i][4][4] == 1)printf("k");else if(z[i][5][5] == 1 && z[i][12][4] == 1)printf("h");else if(z[i][12][4] == 0 && z[i][12][2] == 1) printf("b");else printf("s");}else if(z[i][0][0] == 1 && z[i][1][1] == 0){if(z[i][2][2] == 1){if(z[i][4][4] ==1 ) printf("2");else printf("z");}else if(z[i][3][3] == 0)printf("r");else printf("8");}else printf("b");}else if(jianju[i] == 10) //10{if(z[i][0][0] == 1 && z[i][1][1] == 1 && z[i][2][2] == 1){if(z[i][3][3] == 1) printf("d");else printf("x");}else if(z[i][0][0] == 1 && z[i][1][1] == 1 && z[i][2][2] == 0){if(z[i][3][3] == 1 && z[i][10][0] == 0)printf("3");else if(z[i][3][3] == 1 && z[i][10][0] == 1) printf("2");else if(z[i][4][4] == 1 && z[i][5][5] == 1 && z[i][6][6] ==1)printf("p");else if(z[i][6][6] == 1) printf("b");else if(z[i][5][5] == 1) printf("k");else if(z[i][9][9] == 1) printf("5");else if(z[i][4][4] == 1) printf("6");else printf("8");}else if(z[i][0][0] == 1 && z[i][1][1] == 0){if(z[i][2][2] == 0)printf("n");else if(z[i][4][4] == 1) printf("7");else printf("z");}      else if(z[i][5][5] == 0) printf("x");else if(z[i][12][0] == 1) printf("y");else printf("v");}else if(jianju[i] == 11)//11{if(z[i][0][2] == 1){ if(z[i][0][3] == 1)printf("4");else printf("2");}else if(z[i][0][8] == 1)printf("x");else printf("u");}else if(jianju[i] >=13)//>=13{if(jianju[i] == 13||jianju[i] == 15){ if(jianju[i] == 15)if(z[i][0][0] == 1) printf("m");else printf("w");}else if(jianju[i] == 14)printf("m");else if(z[i][0][0] == 1)printf("m");else printf("w");}}}int main(int argc,char *argv[]){FILE *fp;int rgb[30*70];int gbr[30 * (70*3)];int graph[30][70] = {0};int g=0,b=0,r=0;int i,j,n,a,d,k;int Height,Width;int bl1=0,br1=0,bg1=0,bd1=0,bl2=0,br2=0,bg2=0,bd2=0;int bl3=0,br3=0,bg3=0,bd3=0,bl4=0,br4=0,bg4=0,bd4=0;int z[4][20][20] = {0};// memset(z,0,sizeof(z));fp = fopen(argv[1],"rb");if(fp == NULL)cout<<"read error!";fseek(fp,18,0);fread(&Width,4,1,fp);fread(&Height,4,1,fp);fseek(fp,54,0);for(i = 0; i < 30 ; i++)//读取图片进行灰度化并保存到rgb数组{ for(j = 0; j < 70; j++){fread(&g,1,1,fp);fread(&b,1,1,fp);fread(&r,1,1,fp);rgb[i * 70 + j]=(g+b+r)/3;//灰度化}fseek(fp,2,1);}OtsuThreshold(rgb);//二值化for(i = 0; i < Height; i++){for(j = 0; j < Width; j++)graph[30-1-i][j] = rgb[i * 70 + j];}//输出图片Print(graph);//去噪for(i = 0; i < 30; i++){for(j = 0; j < 70; j++){ n = 0;if(i == 29){if(graph[i][j] == 0 && graph[i][j-1] != 0 && graph[i][j+1] != 0 && graph[i-1][j] != 0)graph[i][j] = 1;if(graph[i][j] == 0){ n = 0;if(graph[i-1][j-1] == 0 )n++;if(graph[i-1][j] == 0)n++;if(graph[i-1][j+1] == 0)n++;if(graph[i][j-1] == 0)n++;if(graph[i][j+1] == 0)n++;if(n < 2)graph[i][j] = 1;}}/* if(graph[i][j] == 0 && graph[i-1][j-1] != 0 && graph[i-1][j] != 0 && graph[i-1][j+1] != 0 && graph[i][j-1] != 0 && graph[i][j+1] != 0 && graph[i+1][j-1] != 0 && graph[i+1][j] != 0 && graph[i+1][j+1] != 0)graph[i][j] = 1;*/if(graph[i][j] == 0){ n = 0;if(graph[i-1][j-1] == 0 )n++;if(graph[i-1][j] == 0)n++;if(graph[i-1][j+1] == 0)n++;if(graph[i][j-1] == 0)n++;if(graph[i][j+1] == 0)n++;if(graph[i+1][j-1] == 0)n++;if(graph[i+1][j] == 0)n++;if(graph[i+1][j+1] == 0)n++;if(n < 2)graph[i][j] = 1;}}}//第一个字符// 左边界for(i = 0; i < Width; i++){for(j = 0; j < Height; j++){if(graph[j][i] == 0){bl1 = i;break;}}if(bl1 != 0)break;}//右边界for(i = bl1; i < Width; i++){ n = 0;for(j = 0; j < Height; j++){if(graph[j][i] == 0)n++;}if(n == 0){ br1 = i-1;break;}}//上边界for(i = 0; i < Height; i++ ){ n = 0;for(j = bl1; j <= br1; j++){if(graph[i][j] == 0){ bg1 = i;n = 1;break;}}if(n != 0)break;}//下边界for(i = Height-1; i >= 0 ; i--){ n = 0;for(j = bl1; j <= br1; j++){if(graph[i][j] == 0){bd1 = i;n = 1;break;} }if(n != 0)break;}//第二个字符//左边界for(i = br1+1; i < Width; i++){for(j = 0; j < Height; j++){if(graph[j][i] == 0){bl2 = i;break;}}if(bl2 != 0)break;}//右边界for(i = bl2; i < Width; i++){n = 0;for(j = 0; j < Height; j++){if(graph[j][i] == 0)n++;}if(n == 0){br2 = i-1;break;}}//上边界for(i = 0; i < Height; i++){ n = 0;for(j = bl2; j <= br2; j++){if(graph[i][j] == 0)n++;}if(n != 0){ bg2 = i;break;}}//下边界for(i = Height-1; i >= 0; i--){ n = 0;for(j = bl2; j <= br2; j++){if(graph[i][j] == 0){ n = 1;bd2 = i;}}if(n != 0)break;}//第三个字符//左边界for(i = br2+1; i < Width; i++){for(j = 0; j < Height; j++){if(graph[j][i] == 0 ){ bl3 = i;break;}}if(bl3 != 0)break;}//右边界for(i = bl3; i < Width; i++){ n = 0;for(j = 0; j < Height; j++){if(graph[j][i] == 0){n++;break;}}if(n == 0){br3 = i-1;break;}}//上边界for(i = 0; i < Height; i++){for(j = bl3; j <= br3; j++){if(graph[i][j] == 0){bg3 = i;break;}}if(bg3 != 0)break;}//下边界for(i = Height-1; i >= 0; i--){ n = 0;for(j = bl3; j <= br3; j++){if(graph[i][j] == 0){ n = 1;bd3 = i;break;}}if(n != 0)break;}//第四个字符//左边界for(i = br3+1; i < Width; i++){for(j = 0; j < Height; j++){if(graph[j][i] == 0){bl4 = i;break;}}if(bl4 != 0)break;}//右边界for(i = bl4; i < Width; i++){ n = 0;for(j = 0; j < Height; j++){if(graph[j][i] == 0){ n++;break;}}if(n == 0){br4 = i-1;break;}}if(i == Width)br4 = Width-1;//上边界for(i = 0; i < Height; i++){for(j = bl4; j <= br4; j++){if(graph[i][j] == 0){bg4 = i;break;}}if(bg4 != 0)break;}//下边界for(i = Height-1; i >= 0; i--){ n = 0;for(j = bl4; j <= br4; j++){if(graph[i][j] == 0){n = 1;bd4 = i;break;}}if(n != 0)break;}//输出图片//Print(graph); //将图片保存到z【】【】【】数组中for(i = bg1,a = 0;i <= bd1; i++,a++){ for(j = bl1,d = 0; j <= br1; j++,d++)z[0][a][d] = graph[i][j];}for(i = bg2,a = 0;i <= bd2; i++,a++){ for(j = bl2,d = 0; j <= br2; j++,d++)z[1][a][d] = graph[i][j];}for(i = bg3,a = 0;i <= bd3; i++,a++){ for(j = bl3,d = 0; j <= br3; j++,d++)z[2][a][d] = graph[i][j];}for(i = bg4,a = 0;i <= bd4; i++,a++){ for(j = bl4,d = 0; j <= br4; j++,d++)z[3][a][d] = graph[i][j];}duibi(z,bl1,br1,bl2,br2,bl3,br3,bl4,br4);printf("\n");/* for(i = 0; i < 4; i++){for(j = 0; j < 20; j++){ for(k = 0; k < 20; k++)if(z[i][j][k] == 1)printf("1,");else printf("0,");printf("\n");}printf("\n");}*/cout<<endl;fclose(fp);return (0);}


欢迎各位给出更好的处理方法什么的,如果愿意请发自己的代码到我邮箱:346512016@qq.com  微笑
我会虚心请教的~
原创粉丝点击