最近因为项目的缘故,用到了一些图片识别的应用,去网上找了一下关于验证码识别的源代码
发现有一个较为简单的代码:http://www.cnblogs.com/yuanbao/archive/2007/09/25/905322.html
仔细看了一下他的代码,发现这只是针对于特定的网站的,非常规则的验证码识别,
限制条件:
1)验证码必须是特定的个数
2)验证码必须非常规则
3)不可以有扰乱的线条
而我需要用到的是去搞定商城里面的价格,因为价格的话需要给消费者看,所以没有加入扰乱的线条,并且他们的数字也比较规则,这样的话,2,3条就不用考虑了,而价格的数字的个数是不定的,所以我们需要改写一下他的源代码就可以了!
这个是我改写之后的代码,说明的话都在里面做了!
using System;
using System.Collections.Generic;
using System.Text;
using System.Drawing;
namespace Pmars
{
class MyImage
{
//需要进行分析的图片
private Bitmap bmpobj;
//需要继承并且重写的数字比对串,在子类里面需要用到这个去比对数字
public Dictionary<string, char> numDic = new Dictionary<string, char>();
//得到图片中某一点的灰度数值
private int GetGrayNumColor(System.Drawing.Color posClr)
{
return (posClr.R * 19595 + posClr.G * 38469 + posClr.B * 7472) >> 16;
}
//进行灰度处理
private void GrayByPixels()
{
for (int i = 0; i < bmpobj.Height; i++)
{
for (int j = 0; j < bmpobj.Width; j++)
{
int tmpValue = GetGrayNumColor(bmpobj.GetPixel(j, i));
bmpobj.SetPixel(j, i, Color.FromArgb(tmpValue, tmpValue, tmpValue));
}
}
}
//得到一个图片的01代码序列
private string GetSingleBmpCode(Bitmap singlepic, int dgGrayValue)
{
Color piexl;
string code = "";
for (int posy = 0; posy < singlepic.Height; posy++)
for (int posx = 0; posx < singlepic.Width; posx++)
{
piexl = singlepic.GetPixel(posx, posy);
if (piexl.R < dgGrayValue) // Color.Black )
code = code + "1";
else
code = code + "0";
}
return code;
}
//从一个图片里面得到几个分开的数字小图片
private Bitmap[] GetPicValidByValue(int dgGrayValue)
{
List<Bitmap> PicList = new List<Bitmap>();
Rectangle cloneRect;
int posx1 = bmpobj.Width, posy1 = bmpobj.Height, posx2 = 0, posy2 = 0;
bool cut = false; int last = -1, lastx = 0;
for (int j = 0; j < bmpobj.Width; j++) //找有效区
{
cut = false;
for (int i = 0; i < bmpobj.Height; i++)
{
int pixelValue = bmpobj.GetPixel(j, i).R;
if (pixelValue < dgGrayValue) //根据灰度值
{
if (posx1 > j) posx1 = j;
if (posy1 > i) posy1 = i;
if (posx2 < j) posx2 = j;
if (posy2 < i) posy2 = i;
cut = true;
}
};
if (cut)
continue;
if (last + 1 == j)
{
last++;
continue;
}
cloneRect = new Rectangle(posx1, posy1, posx2 - posx1 + 1, posy2 - posy1 + 1);
lastx = j; last = j; posx1 = bmpobj.Width; posy1 = bmpobj.Height; posx2 = 0; posy2 = 0;
PicList.Add(bmpobj.Clone(cloneRect, bmpobj.PixelFormat));//复制小块图
};
return PicList.ToArray();
}
//得到一个图片的数字串
private string GetPicNumber()
{
GrayByPixels(); //灰度处理
Bitmap[] pics = GetPicValidByValue(128); //得到有效值
StringBuilder sb = new StringBuilder();
char c;
for (int i = 0; i < pics.Length; ++i)
{
string code = GetSingleBmpCode(pics[i], 128); //得到代码串
if (numDic.TryGetValue(code, out c))
{
sb.Append(c);
}
}
return sb.ToString();
}
//外部调用,得到一个图片的数字串
public string GetPicNum(Bitmap pic)
{
bmpobj = new Bitmap(pic); //转换为Format32bppRgb
return GetPicNumber();
}
//外部调用,得到一个图片的数字串(重载)
public string GetPicNum(string fileName)
{
bmpobj = new Bitmap(fileName);
return GetPicNumber();
}
//输出在一幅图里面找到的数字,测试或者找到数字比对串时用
private void TestNumber()
{
GrayByPixels(); //灰度处理
Bitmap[] pics = GetPicValidByValue(128); //得到有效值
for (int i = 0; i < pics.Length; ++i)
{
string code = GetSingleBmpCode(pics[i], 128); //得到代码串
Console.WriteLine(i);
Console.WriteLine(code);
}
}
//外部调用,输出在一幅图里面找到的数字,测试或者找到数字比对串时用
public void TestNum(Bitmap pic)
{
bmpobj = new Bitmap(pic); //转换为Format32bppRgb
TestNumber();
}
//外部调用,输出在一幅图里面找到的数字,测试或者找到数字比对串时用(重载)
public void TestNum(string fileName)
{
bmpobj = new Bitmap(fileName);
TestNumber();
}
}
}
基类建好之后我们只需要针对某一特定的网站去分析其图片中数字的对比字符串就可以了,利用基类里面的TestNum就可以了!
在此,为了说明这段代码的用法,我用京东商城的价格图片作为试验品(PS:工作中,我可没有做这些^v^)
首先我手动的下载了几张价格图片,这个在实际应用中可以直接用代码去下载,并且测试了其数字串,代码和结果显示在下面
DirectoryInfo dInfo = new DirectoryInfo(@"D:/Images");
MyImage test = new MyImage();
foreach(var file in dInfo.GetFiles())
{
Console.WriteLine(file.FullName);
test.TestNum(file.FullName);
}
这里面的01代码就是我所谓的 对比的数字串 了!
将他们提取出来,去给基类里面的numDic赋值就可以了
下面是京东商城的类的代码,其中给numDic赋值了
class JingdongImage : MyImage
{
public JingdongImage()
{
numDic.Add("111011111101111110111011111000111001111111001110000111000111110", '¥');
numDic.Add("011110110011110011110011110011110011110011011110", '0');
numDic.Add("01101110011001100110011001101111", '1');
numDic.Add("011110110011000011000110001100011000110000111111", '2');
numDic.Add("011110110011000011001110000011000011110011011110", '3');
numDic.Add("000010000110001110010110100110111111000110000110", '4');
numDic.Add("011111011000011000011110000011000011110011011110", '5');
numDic.Add("001110011000110000111110110011110011110011011110", '6');
numDic.Add("111111000011000110000110001100001100011000011000", '7');
numDic.Add("011110110011110011011110110011110011110011011110", '8');
numDic.Add("011110110011110011110011011111000011000110011100", '9');
numDic.Add("1111", '.');
}
}
并且最终测试的代码和结果如下:
DirectoryInfo dInfo = new DirectoryInfo(@"D:/Images");
JingdongImage test = new JingdongImage();
foreach(var file in dInfo.GetFiles())
{
Console.WriteLine(file.FullName);
Console.WriteLine(test.GetPicNum(file.FullName));
}
需要提到的是,在这里,图片的名称就是下载时候的价格,这个不是我假写的(- -)
总结:这个方法去找价格,或者说是识别简单的图片是可以的,前提是他们不要做过多的改变。。。。
写在这里,记录一下!