车牌识别及验证码识别_第1页
车牌识别及验证码识别_第2页
车牌识别及验证码识别_第3页
车牌识别及验证码识别_第4页
车牌识别及验证码识别_第5页
已阅读5页,还剩8页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

1、车牌识别及验证码识别引言:本文主要对于车牌识别系统来说, D,0, O, I, 1等等太相似了。然后,汉字的识别难度也不小,因此很难达到分辨率在95%以上,但是可以作为图像处理技术的小论文,在此放出,同时描述一下思路及算法。 一、车牌 /验证码识别的普通方法 车牌、验证码识别的普通方法为: (1) 将图片灰度化与二值化 (2) 去噪,然后切割成一个一个的字符 (3) 提取每一个字符的特征,生成特征矢量或特征矩阵 (4) 分类与学习。将特征矢量或特征矩阵与样本库进行比对,挑选出相似的那类样本,将这类样本的值作为输出结果。 下面借着代码,描述一下上述过程。因为更新 SVN Server,我以前以

2、bdb储存的代码访问不了,因此部分代码是用 Reflector反编译过来的,望见谅。 (1) 图片的灰度化与二值化 这样做的目的是将图片的每一个象素变成 0或者255 ,以便以计算。同时,也可以去除部分噪音。 图片的灰度化与二值化的前提是 bmp图片,如果不是,则需要首先转换为 bmp图片。 用代码说话,我的将图片灰度化的代码(算法是在网上搜到的): Code 1 protected static Color Gray(Color c)2 3 int rgb = Convert.ToInt32( double ) ( 0.3 * c.R) + ( 0.59 * c.G) + ( 0.11 *

3、c.B);4 return Color.FromArgb(rgb, rgb, rgb);5 6 通过将图片灰度化,每一个象素就变成了一个 0-255的灰度值。 然后是将灰度值二值化为 0 或 255。一般的处理方法是设定一个区间,比如, a,b,将 a,b之间的灰度全部变成 255,其它的变成 0。这里我采用的是网上广为流行的自适应二值化算法。 Code 1 public static void Binarizate(Bitmap map) 2 3 int tv = ComputeThresholdValue(map); 4 int x = map.Width; 5 int y = map.H

4、eight; 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 ComputeThresholdV

5、alue(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 ihisti = 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

6、 + )38 39 int cn = img.GetPixel(i, j).R;40 ihistcn + ;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 * ihistk;56 n += ihistk;57 58 if (n = 0 )59 60 return 60 ;61 62 doubl

7、e fmax = - 1.0 ;63 int n1 = 0 ;64 for (k = 0 ; k < 0xff ; k + )65 66 n1 += ihistk;67 if (n1 != 0 )68 69 int n2 = n - n1;70 if (n2 = 0 )71 72 return thresholdValue;73 74 csum += k * ihistk;75 double m1 = csum / ( double ) n1);76 double m2 = (sum - csum) / ( double ) n2);77 double sb = (n1 * n2) *

8、(m1 - m2) * (m1 - m2);78 if (sb > fmax)79 80 fmax = sb;81 thresholdValue = k;82 83 84 85 return thresholdValue;86 87 88 灰度化与二值化之前的图片: 灰度化与二值化之后的图片: 注:对于车牌识别来说,这个算法还不错。对于验证码识别,可能需要针对特定的网站设计特殊的二值化算法,以过滤杂色。 (2) 去噪,然后切割成一个一个的字符 上面这张车牌切割是比较简单的,从左到右扫描一下,碰见空大的,咔嚓一刀,就解决了。但有一些车牌,比如这张: 简单的扫描就解决不了。因此需要一个比较通

9、用的去噪和切割算法。 这里我采用的是比较朴素的方法: 将上面的图片看成是一个平面。将图片向水平方向投影,这样有字的地方的投影值就高,没字的地方投影得到的值就低。这样会得到一根曲线,像一个又一个山头。下面是我手画示意图: 然后,用一根扫描线(上图中的 S)从下向上扫描。这个扫描线会与图中曲线存在交点,这些交点会将山头分割成一个又一个区域。车牌图片一般是 7个字符,因此,当扫描线将山头分割成七个区域时停止。然后根据这七个区域向水平线的投影的坐标就可以将图片中的七个字符分割出来。 但是,现实是复杂的。比如,“川”字,它的水平投影是三个山头。按上面这种扫描方法会将它切开。因此,对于上面的切割,需要加上

10、约束条件:每个山头有一个中心线,山头与山头的中心线的距离必需在某一个值之上,否则,则需要将这两个山头进行合并。加上这个约束之后,便可以有效的切割了。 以上是水平投影。然后还需要做垂直投影与切割。这里的垂直投影与切割就一个山头,因此好处理一些。 切割结果如下: 水平投影及切割代码: Code 1 public static IList < Bitmap > Split(Bitmap map, int count) 2 3 if (count <= 0 ) 4 5 throw new ArgumentOutOfRangeException( " Count 必须大于0.

11、 " ); 6 7 IList < Bitmap > resultList = new List < Bitmap > (); 8 int x = map.Width; 9 int y = map.Height;10 int splitBitmapMinWidth = 4 ;11 int xNormal = new int x;12 for ( int i = 0 ; i < x; i + )13 14 for ( int j = 0 ; j < y; j + )15 16 if (map.GetPixel(i, j).R = CharGrayVa

12、lue)17 18 xNormali + ;19 20 21 22 Pair pair = new Pair();23 for ( int i = 0 ; i < y; i + )24 25 IList < Pair > pairList = new List < Pair > (count + 1 );26 for ( int j = 0 ; j < x; j + )27 28 if (xNormalj >= i)29 30 if (j = (x - 1 ) && (pair.Status = PairStatus.Start)31

13、32 pair.End = j;33 pair.Status = PairStatus.End;34 if (pair.End - pair.Start) >= splitBitmapMinWidth)35 36 pairList.Add(pair);37 38 pair = new Pair();39 40 else if (pair.Status = PairStatus.JustCreated)41 42 pair.Start = j;43 pair.Status = PairStatus.Start;44 45 46 else if (pair.Status = PairStat

14、us.Start)47 48 pair.End = j;49 pair.Status = PairStatus.End;50 if (pair.End - pair.Start) >= splitBitmapMinWidth)51 52 pairList.Add(pair);53 54 pair = new Pair();55 56 if (pairList.Count > count)57 58 break ;59 60 61 if (pairList.Count = count)62 63 foreach (Pair p in pairList)64 65 if (p.Widt

15、h < (map.Width / 10 )66 67 int width = (map.Width / 10 ) - p.Width;68 p.Start = Math.Max( 0 , p.Start - (width / 2 );69 p.End = Math.Min( int ) (p.End + (width / 2 ), ( int ) (map.Width - 1 );70 71 72 foreach (Pair p in pairList)73 74 int newMapWidth = (p.End - p.Start) + 1 ;75 Bitmap newMap = ne

16、w Bitmap(newMapWidth, y);76 for ( int ni = p.Start; ni <= p.End; ni + )77 78 for ( int nj = 0 ; nj < y; nj + )79 80 newMap.SetPixel(ni - p.Start, nj, map.GetPixel(ni, nj);81 82 83 resultList.Add(newMap);84 85 return resultList;86 87 88 return resultList;89 90 代码中的 Pair,代表扫描线与曲线的一对交点: Code 1 pr

17、ivate class Pair 2 3 public Pair(); 4 public int CharPixelCount get ; set ; 5 public int CharPixelXDensity get ; 6 public int End get ; set ; 7 public int Start get ; set ; 8 public BitmapConverter.PairStatus Status get ; set ; 9 public int Width get ; 10 11 PairStatus 代表 Pair 的状态。具体哪个状态是什么意义,我已经忘了。

18、 Code 1 private enum PairStatus2 3 JustCreated,4 Start,5 End6 7 以上这一段代码写的很辛苦,因为要处理很多特殊情况。那个 PairStatus 也是为处理特殊情况引进的。 垂直投影与切割的代码简单一些,不贴了,见附后的 dll 的 BitmapConverter.TrimHeight 方法。 (3)提取每一个字符的特征,生成特征矢量或特征矩阵 将切割出来的字符,分割成一个一个的小块,比如 3 × 3 , 5 × 5 ,或 3 × 5 ,或 10 × 8 ,然后统计一下每小块的值为 255 的像

19、素数量,这样得到一个矩阵 M ,或者将这个矩阵简化为矢量 V 。 通过以上 3 步,就可以将一个车牌中的字符数值化为矢量了。 ( 1 ) - ( 3 )步具体的代码流程如下: Code 1 2 BitmapConverter.ToGrayBmp(bitmap); / 图片灰度化 3 BitmapConverter.Binarizate(bitmap); / 图片二值化 4 IList < Bitmap > mapList = BitmapConverter.Split(bitmap, DefaultCharsCount); / 水平投影然后切割 5 Bitmap map0 = Bi

20、tmapConverter.TrimHeight(mapList 0 , DefaultHeightTrimThresholdValue); / 垂直投影然后切割 6 ImageSpliter spliter = new ImageSpliter(map0); 7 spliter.WidthSplitCount = DefaultWidthSplitCount; 8 spliter.HeightSplitCount = DefaultHeightSplitCount; 9 spliter.Init();10 然后,通过spliter.ValueList就可以获得 Bitmap map0 的矢量

21、表示。 (4) 分类 分类的原理很简单。用 (Vij,Ci) 表示一个样本。其中, Vij 是样本图片经过上面过程数值化后的矢量。 Ci 是人肉眼识别这张图片,给出的结果。 Vij 表明,有多个样本,它们的数值化后的矢量不同,但是它们的结果都是 Ci 。假设待识别的图片矢量化后,得到的矢量是 V 。 直观上,我们会有这样一个思路,就是这张待识别的图片,最像样本库中的某张图片,那么我们就将它当作那张图片,将它识别为样本库中那张图片事先指定的字符。 在我们眼睛里,判断一张图片和另一张图片是否相似很简单,但对于电脑来说,就很难判断了。我们前面已经将图片数值化为一个个维度一样的矢量,电脑是怎样判断一个

22、矢量与另一个矢量相似的呢? 这里需要计算一个矢量与另一个矢量间的距离。这个距离越短,则认为这两个矢量越相似。 我用 SampleVector <T> 来代表矢量: Code 1 public class SampleVector < T > 2 3 protected T Vector get ; set ; 4 public Int32 Dimension get return Vector.Length; 5 6 7 T 代表数据类型,可以为 Int32 ,也可以为 Double 等更精确的类型。 测量距离的公共接口为: IMetric Code 1 public

23、interface IMetric < TElement,TReturn > 2 3 TReturn Compute(SampleVector < TElement > v1, SampleVector < TElement > v2);4 5 常用的是 MinkowskiMetric 。 Code 1 / <summary> 2 / Minkowski 测度。 3 / </summary> 4 public class MinkowskiMetric < TElement > : IMetric < TElemen

24、t, Double > 5 6 public Int32 Scale get ; private set ; 7 public MinkowskiMetric(Int32 scale) 8 Scale = scale; 9 10 public Double Compute(SampleVector < TElement > v1, SampleVector < TElement > v2)11 12 if (v1 = null | v2 = null ) throw new ArgumentNullException();13 if (v1.Dimension !

25、= v2.Dimension) throw new ArgumentException( " v1 和 v2 的维度不等. " );14 Double result = 0 ;15 for ( int i = 0 ; i < v1.Dimension; i + )16 17 result += Math.Pow(Math.Abs(Convert.ToDouble(v1i) - Convert.ToDouble(v2i), Scale);18 19 return Math.Pow(result, 1.0 / Scale);20 21 22 23 MetricFactor

26、y 负责生产各种维度的MinkowskiMetric:24 25 public class MetricFactory26 27 public static IMetric < TElement, Double > CreateMinkowskiMetric < TElement > (Int32 scale)28 29 return new MinkowskiMetric < TElement > (scale);30 31 32 public static IMetric < TElement, Double > CreateEuclidea

27、nMetric < TElement > ()33 34 return CreateMinkowskiMetric < TElement > ( 2 );35 36 37 MinkowskiMetric 是普遍使用的测度。但不一定是最有效的量。因为它对于矢量 V 中的每一个点都一视同仁。而在图像识别中,每一个点的重要性却并不一样,例如, Q 和 O 的识别,特征在下半部分,下半部分的权重应该大于上半部分。对于这些易混淆的字符,需要设计特殊的测量方法。在车牌识别中,其它易混淆的有 D 和 0 , 0 和 O , I 和 1 。 Minkowski Metric 识别这些字

28、符,效果很差。因此,当碰到这些字符时,需要进行特别的处理。由于当时时间紧,我就只用了 Minkowski Metric 。 我的代码中,只实现了哪个最近,就选哪个。更好的方案是用 K 近邻分类器或神经网络分类器。 K 近邻的原理是,找出和待识别的图片(矢量)距离最近的 K 个样本,然后让这 K 个样本使用某种规则计算(投票),这个新图片属于哪个类别( C );神经网络则将测量的过程和投票判决的过程参数化,使它可以随着样本的增加而改变,是这样的一种学习机。有兴趣的可以去看模式分类一书的第三章和第四章。 二、 变态字符的识别 有些字符变形很严重,有的字符连在一起互相交叉,有的字符被掩盖在一堆噪音海

29、之中。对这类字符的识别需要用上特殊的手段。 下面介绍几种几个经典的处理方法,这些方法都是被证实对某些问题很有效的方法: (1) 切线距离 ( Tangent Distance ):可用于处理字符的各种变形, OCR 的核心技术之一。 (2) 霍夫变换( Hough Transform ):对噪音极其不敏感,常用于从图片中提取各种形状。图像识别中最基本的方法之一。 (3) 形状上下文( Shape Context ):将特征高维化,对形变不很敏感,对噪音也不很敏感。新世纪出现的新方法。 因为这几种方法我均未编码实现过,因此只简单介绍下原理及主要应用场景。 (1) 切线距离 前面介绍了 Minko

30、wskiMetric 。这里我们看看下面这张图:一个正写的 1 与一个歪着的 1. 用 MinkowskiMetric 计算的话,两者的 MinkowskiMetric 很大。 然而,在图像识别中,形状形变是常事。理论上,为了更好地识别, 我们需要对每一种形变都采足够的样,这样一来,会发现样本数几乎无穷无尽,计算量越来越大。 怎么办呢?那就是通过计算切线距离,来代替直接距离。切线距离比较抽象,我们将问题简化为二维空间,以便以理解。 上图有两条曲线。分别是两个字符经过某一形变后所产生的轨迹。 V1 和 V2 是 2 个样本。 V 是待识别图片。如果用样本之间的直接距离,比较哪个样本离 V 最近,就将 V 当作哪一类,这样的话,就要把 V 分给 V1 了。理论上,如果我们无限取样的话,下面那一条曲线上的某个样本离 V 最近,V 应该归类为V2。不过,无限取样不现实,于是就引出了切线距离:在样本 V1 , V2 处做切线,然后计算 V 离这两条切线的距离,哪个最近就算哪

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论