天天看点

一个简单的验证码识别

作为一名刚接触编程一年的同学,第一次接触图片处理,所以处理办法什么的比较简单无脑。 各位大神如果看到了请轻喷……    说到验证码识别,想必大家都知道基本的步骤: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);
}
           

欢迎各位给出更好的处理方法什么的,如果愿意请发自己的代码到我邮箱:[email protected]  

一个简单的验证码识别

我会虚心请教的~

继续阅读