全文分两部分,第一部分讲车牌识别及普通验证码这一类识别的普通方法,第二部分讲对类似QQ验证码,Gmail验证码这一类变态验证码的识别方法和思路。
一、车牌/验证码识别的普通方法
车牌、验证码识别的普通方法为:
(1) 将图片灰度化与二值化
(2) 去噪,然后切割成一个一个的字符
(3) 提取每一个字符的特征,生成特征矢量或特征矩阵
(4) 分类与学习。将特征矢量或特征矩阵与样本库进行比对,挑选出相似的那类样本,将这类样本的值作为输出结果。
下面借着代码,描述一下上述过程。因为更新SVN Server,我以前以bdb储存的代码访问不了,因此部分代码是用Reflector反编译过来的,望见谅。
(1) 图片的灰度化与二值化
这样做的目的是将图片的每一个象素变成0或者255,以便以计算。同时,也可以去除部分噪音。
图片的灰度化与二值化的前提是bmp图片,如果不是,则需要首先转换为bmp图片。
用代码说话,我的将图片灰度化的代码(算法是在网上搜到的):
1 protected static Color Gray(Color c)
2 {
3 int rgb = Convert.ToInt32((double) (((0.3 * c.R) + (0.59 * c.G)) + (0.11 * c.B)));
4 return Color.FromArgb(rgb, rgb, rgb);
5 }
6
通过将图片灰度化,每一个象素就变成了一个0-255的灰度值。
然后是将灰度值二值化为 0 或255。一般的处理方法是设定一个区间,比如,[a,b],将[a,b]之间的灰度全部变成255,其它的变成0。这里我采用的是网上广为流行的自适应二值化算法。
1 public static void Binarizate(Bitmap map)
2 {
3 int tv = ComputeThresholdValue(map);
4 int x = map.Width;
5 int y = map.Height;
6 for (int i = 0; i < x; i++)
7 {
8 for (int j = 0; j < y; j++)
9 {
10 if (map.GetPixel(i, j).R >= tv)
11 {
12 map.SetPixel(i, j, Color.FromArgb(0xff, 0xff, 0xff));
13 }
14 else
15 {
16 map.SetPixel(i, j, Color.FromArgb(0, 0, 0));
17 }
18 }
19 }
20 }
21
22 private static int ComputeThresholdValue(Bitmap img)
23 {
24 int i;
25 int k;
26 double csum;
27 int thresholdValue = 1;
28 int[] ihist = new int[0x100];
29 for (i = 0; i < 0x100; i++)
30 {
31 ihist[i] = 0;
32 }
33 int gmin = 0xff;
34 int gmax = 0;
35 for (i = 1; i < (img.Width - 1); i++)
36 {
37 for (int j = 1; j < (img.Height - 1); j++)
38 {
39 int cn = img.GetPixel(i, j).R;
40 ihist[cn]++;
41 if (cn > gmax)
42 {
43 gmax = cn;
44 }
45 if (cn < gmin)
46 {
47 gmin = cn;
48 }
49 }
50 }
51 double sum = csum = 0.0;
52 int n = 0;
53 for (k = 0; k <= 0xff; k++)
54 {
55 sum += k * ihist[k];
56 n += ihist[k];
57 }
58 if (n == 0)
59 {
60 return 60;
61 }
62 double fmax = -1.0;
63 int n1 = 0;
64 for (k = 0; k < 0xff; k++)
65 {
66 n1 += ihist[k];
67 if (n1 != 0)
68 {
69 int n2 = n - n1;
70 if (n2 == 0)
71 {
72 return thresholdValue;
73 }
74 csum += k * ihist[k];
75 double m1 = csum / ((double) n1);
76 double m2 = (sum - csum) /