验证码识别思想
原文发布时间为:2010-08-07 —— 来源于本人的百度文章 [由搬家工具导入]
读后总结:其实就几个步骤:
1:用程序先把所有验证码(已知的,单个的)的灰度值放入一个数组
2:分割未知验证码,把未知验证码的一个一个数字或字母分割出来
3:分别取分割验证码的 灰度值
4:将分割验证码的灰度值与数组中的灰度值进行匹配,匹配程度最大的,即可能就是该码
类:
#region 获取纯数字验证码
/// <summary>
/// 获取纯数字验证码
/// </summary>
/// <param name="arrNum">0~9数字 的已知灰度值数组</param>
/// <param name="iNum">几个验证码</param>
/// <returns>验证码</returns>
public string GetSimpleCode(string[] arrNum, int iNum)
{
string strCode = string.Empty;//得到的码
Bitmap[] arrBmp =GetSplitPics(iNum, 1);//分割验证码
for (int k = 0; k < arrBmp.Length; k++)
{
string strValue = GetSingleBmpCode(arrBmp[k], 128);//获取验证码灰度值
int[] intTemp = new int[10];//灰度值匹配度
//获取匹配度
int iMax = 0, iMaxIndex = -1;
for (int i = 0; i < 10; i++)
{
int iCount = 0;
if (arrNum[i].Length == strValue.Length) //长度相等,才有可能是这个验证码
{
for (int j = 0; j < strValue.Length; j++)
{
if (arrNum[i][j].ToString() == strValue[j].ToString())//相等,则匹配度增加
{
iCount++;
}
}
}
if (iCount > iMax)//获取最大匹配度的码
{
iMax = iCount;
iMaxIndex = i;
}
}
if (iMaxIndex != -1)
{
strCode += iMaxIndex.ToString();
}
else//没有匹配任何一个
{
strCode += "?";
}
}
return strCode;
}
#endregion
==================================
第一部份 图片的组成及特征码的定义
做为验证码的识别,那我们首先需要了解的就是基本的原理,这里就是先说清楚这个原理,再往后大家看代码就比较容易明白了(其实本来就比较简单,我说这么多就是为了大家看完不说我的水平次罢了。再次爆汗~!)
正题了,其实整个验证码的识别的话我们先得分解一个图,每一个图形其实都是由每一个点和每一个点来组成的,说到每个点其实就是一个色块,当每一个色块拼接起来就成了一个图形,这个虽然说有点废话,但我还是提一下。
需要更好的理解我们来看图:
图一 图二
通过上面的两幅图片我们就能很清楚的知道色块情况了,每一个小块就 是一个色块,有些用白色来表示,而整个图的点则也是通过坐标来识别的,X,Y两个点,这样我们就不难分清行和列了,当我们取色块X1,Y1就是指取出第一 行,第一列,如果我们取色块为X2,Y5则是指第五行,第二个色块。呵呵!
色块了解了,我们可以通过取色块中的颜色来做为一个比较和判断的识别标准。我们对一个图中的数字进行取色,当颜色的值为黑色(0)时,我们将点记录为1,当所取的点值为白色(255)时,我们将点记录为0。这样整张图取下来,我们就会得到一个字符串,格式如下:
0000000000000000000000000000000000000000000000000000000000000000000000000000000000111111111111111100001111111111111111001100000000000000001111000000000000000011110000000000000000111100000000000000001111000000000000000011110000000000000000111100000000000000001111000000000000000011001111111111111111000011111111111111110000000000000000000000000000000000000000000000000000000000000000000000000000000000
上面的这段类似二进制的字符串就是我们从图一中提出来的东西了,我们称这一个字符串为这张图片的特征码。这个特征的解读我们利用前面所说的知识来自己解读一下,以图一为参考,图一中的前面的四列都是白色,所以我们的特征码中开始好多个零。>_<
从第五列开始,除了前面的两格是白色,接下来就是黑色了,这样我们开始数一下,就是应该有82个零,然后开始是1,嘿嘿~~~大家可以数一下。接着就开 始到了黑色部份了,这里应该有11个黑色块,在我们的特征码中有11个1。这样的话大家就很清楚,这个特征码的结构了。
好,现在对图片的解读我们已经很清楚了,接着就是用代码来实现了。
我们这一章中将使用C#写一个Winform的程序来提取图片中的特征码。
在这一章里我们的重点部份有3点
1、
Bitmap中的Bitmap.GetPixel(x,y) //这里是读取图片中的点中的颜色
注意:需要使用Bitmap的话我们则需要引用两个命名空间,分别为:
using System.Drawing;
using System.Drawing.Imaging;
2、图片中取了第个点,但一个点中的色彩是由R.G.B三个色组成,所以我们需要知道R.G.B的颜色,这里我们例程中的图片比较单一,而且文字是黑色 的文字,黑色的RGB的色值为(255.255.255)所以,我们只需要取R值就行了,如果是文字中是多种颜色组成的话,最好用photoshop改成 灰度,再进行取值,这样得到的特征码会比较准确。
3、取色值的时候使用到了循环嵌套,一个取行值,一个取列值,图片中每一个点都需要扫描到,最后就是注意“环保”,用完了要把图片关闭。(*_*)
好了,知道了需要使用什么东西我们就开始写代码了。
代码比较多,所以我就只在这里放重要部份的代码,而多余部份的代码大家自己下源程看吧,源程我会放在附件中,源程中尽量使用简单的代码,没有使用类,没有使用工厂模式,这样容易看得懂,但是却不是好的代码,所以大家如果自己做的话注意这个问题。
//载入图片
Bitmap bmp = new Bitmap(drawing);
//每行每列扫描获取图片数字编码字符
string CodeNumber = "";
//定义一个字符串变量用于存储特征码
//对图进行逐点扫描,当R值不等于255时则将CodeNumber记为0,否则记为1
for (int x = 0; x < bmp.Width; x++) //行扫描,由x.0至x.图片宽度
{
for (int y = 0; y < bmp.Height; y++) //列扫描,由y.0至图片高度
{
if (bmp.GetPixel(x, y).R == 0) //对图片中的点进行判断,当x,y点中的R色为0的时候
{
CodeNumber = CodeNumber + "1"; //记录为1
}
else //否则
{
CodeNumber = CodeNumber + "0"; //记录为0
}
}
}
//关闭图片
bmp.Dispose();
//将特征码显示在richTextBox1控件中显示出来
richTextBox1.Text = CodeNumber;
每行都有注释,多余的我就不说了。程序完成以后,我们将特征码记录下来。在后面我们制作验证码识别器的时候需要使用。(未完待续 下一章,使用特征码制作验证码识别器)
上 一章我们说了特征码及特征码的提取,现在我们所需要的就是通过特征码来实现验证码的识别,其实聪明的朋友已经猜到了,这个验证码的识别到了这里就很明白 了,没什么特别的就是将第每个色块提到的特征码进行对比,识别过程就是一个对比的过程。呵呵~~~我其实上就是为了再混个精华贴。好了,和上章一样,我这 里给的代码比较少,需要详细代码的朋友直接下源程看吧,我的源程中注释都不少,应该比较容易能看懂,在这个例子中我的注释就不那么多了,因为重点和上个例 子差不多。
本章重点:
1、使用上一章所制作的提取器,提取无杂情况下图片的特征码,并将0~9,单独数字的特征码保存,在这里程序开始的时候使用一个字符串数字,将每个特征码都做保存。格式如下:
string[] Features;
Features = new string[10];
Features[0] = "0000000000000000000000000000000000000000000000000000000000000000000000000000000000111111111111111100001111111111111111001100000000000000001111000000000000000011110000000000000000111100000000000000001111000000000000000011110000000000000000111100000000000000001111000000000000000011001111111111111111000011111111111111110000000000000000000000000000000000000000000000000000000000000000000000000000000000";
Features[1] = "0000000000000000000000000000000000000000000000000000000000000000000000000000000000110000000000000011001100000000000000110011000000000000001100110000000000000011111111111111111111111111111111111111111100000000000000000011000000000000000000110000000000000000001100000000000000000011000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000";…………
2、注意图片中色块的RGB的色值。如果需要读取的验证码的图形的背景色中的色块的R值为多少及图片中文字中的色块的R值是多少,这个可以通过PHOTOSHOP或是其它的屏幕取色软件来实现。
3、注意图片的长度和高度,上面提取的验证码是每一个单数字的长度及高度,而在这里是需要这整张图片的长度是多少,高度是多少,必需切成一个一个的数字进 行验证。现在我们示列中的图片的总长度是120,其中包涵着6个数字,我们所提取的特征码为20*20,也就是说总长度必需分为6份,每单数字的高度为 20,长度为20,循环6次就能得到整张图片。
4、注意你图片中的误差,如果无杂点的情况下那么你的误差点可以为0,如果有杂点的情况下就需要判断你的数字的误差点与特征码中的误差数量是多少。这样才能更好的做出判断。也能提高准确率。
好了,上面就是这一章的重点,现在我们开始继续我们的程序。
首先我们要做就是先将特征码做做成一个字符串数组,在上面已经给出了,这里就不重复给出了,接着我们需要的就是载入图片,这里所载入的图片是需要识别的验 证码的图片。然后我们对这个图片进行读取,而这里所读取的不是整张图片,而是将图片分成20*20的范围来读取,每读出一个20*20的范围就进行一次判 断。使用一个循环就可以弄好了。如果不清楚的话一会自己看附件中的源程就能明白。
做好读取图片中的特征码以后就是对我们图片中的特征码进行对比,首先是字符串的长度对比,当字符串的长度不相等的时候就不用判断了,因为这是不可能正确的,跳过,不能识别。-_-!!!
当验证码的长度相同,就开始判断两个字符串是否完全相等,如果完全相等的情况下就直接输出数字,说明这个数字无杂,除了背景以外,其它的都完全相同。
如果字符串的长度相等,而字符串又不相等的时候我们就将字符串拆成字符串数组,一个一个的特征码进行比较,出现不相同的情况的时候就将错误点进行记录,这 些错误点就是我们所说的误差点,最后做完整个循环判断以后如果错误点小于我们所设定的误差点的情况下,则等于此数,否则进入下一次的循环,当所有的特征码 都进行对比,没有得到我们所需要的结果时,这个点将不能误别,继续跳过。
最后做完了所有的判断以后记住一定要关闭图片就可以了。