车牌识别及验证码识别.docx
- 文档编号:24612530
- 上传时间:2023-05-29
- 格式:DOCX
- 页数:20
- 大小:213.54KB
车牌识别及验证码识别.docx
《车牌识别及验证码识别.docx》由会员分享,可在线阅读,更多相关《车牌识别及验证码识别.docx(20页珍藏版)》请在冰豆网上搜索。
车牌识别及验证码识别
车牌识别及验证码识别
引言:
本文主要对于车牌识别系统来说,D,0,O,I,1等等太相似了。
然后,汉字的识别难度也不小,因此很难达到分辨率在95%以上,但是可以作为图像处理技术的小论文,在此放出,同时描述一下思路及算法。
一、车牌/验证码识别的普通方法
车牌、验证码识别的普通方法为:
(1)将图片灰度化与二值化
(2)去噪,然后切割成一个一个的字符
(3)提取每一个字符的特征,生成特征矢量或特征矩阵
(4)分类与学习。
将特征矢量或特征矩阵与样本库进行比对,挑选出相似的那类样本,将这类样本的值作为输出结果。
下面借着代码,描述一下上述过程。
因为更新SVNServer,我以前以bdb储存的代码访问不了,因此部分代码是用Reflector反编译过来的,望见谅。
(1)图片的灰度化与二值化
这样做的目的是将图片的每一个象素变成0或者255,以便以计算。
同时,也可以去除部分噪音。
图片的灰度化与二值化的前提是bmp图片,如果不是,则需要首先转换为bmp图片。
用代码说话,我的将图片灰度化的代码(算法是在网上搜到的):
Code
1protectedstaticColorGray(Colorc)
2{
3intrgb=Convert.ToInt32((double)(((0.3*c.R)+(0.59*c.G))+(0.11*c.B)));
4returnColor.FromArgb(rgb,rgb,rgb);
5}
6
通过将图片灰度化,每一个象素就变成了一个0-255的灰度值。
然后是将灰度值二值化为0或255。
一般的处理方法是设定一个区间,比如,[a,b],将[a,b]之间的灰度全部变成255,其它的变成0。
这里我采用的是网上广为流行的自适应二值化算法。
Code
1publicstaticvoidBinarizate(Bitmapmap)
2{
3inttv=ComputeThresholdValue(map);
4intx=map.Width;
5inty=map.Height;
6for(inti=0;i 7{ 8for(intj=0;j 9{ 10if(map.GetPixel(i,j).R>=tv) 11{ 12map.SetPixel(i,j,Color.FromArgb(0xff,0xff,0xff)); 13} 14else 15{ 16map.SetPixel(i,j,Color.FromArgb(0,0,0)); 17} 18} 19} 20} 21 22privatestaticintComputeThresholdValue(Bitmapimg) 23{ 24inti; 25intk; 26doublecsum; 27intthresholdValue=1; 28int[]ihist=newint[0x100]; 29for(i=0;i<0x100;i++) 30{ 31ihist[i]=0; 32} 33intgmin=0xff; 34intgmax=0; 35for(i=1;i<(img.Width-1);i++) 36{ 37for(intj=1;j<(img.Height-1);j++) 38{ 39intcn=img.GetPixel(i,j).R; 40ihist[cn]++; 41if(cn>gmax) 42{ 43gmax=cn; 44} 45if(cn 46{ 47gmin=cn; 48} 49} 50} 51doublesum=csum=0.0; 52intn=0; 53for(k=0;k<=0xff;k++) 54{ 55sum+=k*ihist[k]; 56n+=ihist[k]; 57} 58if(n==0) 59{ 60return60; 61} 62doublefmax=-1.0; 63intn1=0; 64for(k=0;k<0xff;k++) 65{ 66n1+=ihist[k]; 67if(n1! =0) 68{ 69intn2=n-n1; 70if(n2==0) 71{ 72returnthresholdValue; 73} 74csum+=k*ihist[k]; 75doublem1=csum/((double)n1); 76doublem2=(sum-csum)/((double)n2); 77doublesb=((n1*n2)*(m1-m2))*(m1-m2); 78if(sb>fmax) 79{ 80fmax=sb; 81thresholdValue=k; 82} 83} 84} 85returnthresholdValue; 86} 87 88 灰度化与二值化之前的图片: 灰度化与二值化之后的图片: 注: 对于车牌识别来说,这个算法还不错。 对于验证码识别,可能需要针对特定的网站设计特殊的二值化算法,以过滤杂色。 (2)去噪,然后切割成一个一个的字符 上面这张车牌切割是比较简单的,从左到右扫描一下,碰见空大的,咔嚓一刀,就解决了。 但有一些车牌,比如这张: 简单的扫描就解决不了。 因此需要一个比较通用的去噪和切割算法。 这里我采用的是比较朴素的方法: 将上面的图片看成是一个平面。 将图片向水平方向投影,这样有字的地方的投影值就高,没字的地方投影得到的值就低。 这样会得到一根曲线,像一个又一个山头。 下面是我手画示意图: 然后,用一根扫描线(上图中的S)从下向上扫描。 这个扫描线会与图中曲线存在交点,这些交点会将山头分割成一个又一个区域。 车牌图片一般是7个字符,因此,当扫描线将山头分割成七个区域时停止。 然后根据这七个区域向水平线的投影的坐标就可以将图片中的七个字符分割出来。 但是,现实是复杂的。 比如,“川”字,它的水平投影是三个山头。 按上面这种扫描方法会将它切开。 因此,对于上面的切割,需要加上约束条件: 每个山头有一个中心线,山头与山头的中心线的距离必需在某一个值之上,否则,则需要将这两个山头进行合并。 加上这个约束之后,便可以有效的切割了。 以上是水平投影。 然后还需要做垂直投影与切割。 这里的垂直投影与切割就一个山头,因此好处理一些。 切割结果如下: 水平投影及切割代码: Code 1publicstaticIList 2{ 3if(count<=0) 4{ 5thrownewArgumentOutOfRangeException("Count必须大于0."); 6} 7IList 8intx=map.Width; 9inty=map.Height; 10intsplitBitmapMinWidth=4; 11int[]xNormal=newint[x]; 12for(inti=0;i 13{ 14for(intj=0;j 15{ 16if(map.GetPixel(i,j).R==CharGrayValue) 17{ 18xNormal[i]++; 19} 20} 21} 22Pairpair=newPair(); 23for(inti=0;i 24{ 25IList 26for(intj=0;j 27{ 28if(xNormal[j]>=i) 29{ 30if((j==(x-1))&&(pair.Status==PairStatus.Start)) 31{ 32pair.End=j; 33pair.Status=PairStatus.End; 34if((pair.End-pair.Start)>=splitBitmapMinWidth) 35{ 36pairList.Add(pair); 37} 38pair=newPair(); 39} 40elseif(pair.Status==PairStatus.JustCreated) 41{ 42pair.Start=j; 43pair.Status=PairStatus.Start; 44} 45} 46elseif(pair.Status==PairStatus.Start) 47{ 48pair.End=j; 49pair.Status=PairStatus.End; 50if((pair.End-pair.Start)>=splitBitmapMinWidth) 51{ 52pairList.Add(pair); 53} 54pair=newPair(); 55} 56if(pairList.Count>count) 57{ 58break; 59} 60} 61if(pairList.Count==count) 62{ 63foreach(PairpinpairList) 64{ 65if(p.Width<(map.Width/10)) 66{ 67intwidth=(map.Width/10)-p.Width; 68p.Start=Math.Max(0,p.Start-(width/2)); 69p.End=Math.Min((int)(p.End+(width/2)),(int)(map.Width-1)); 70} 71} 72foreach(PairpinpairList) 73{ 74intnewMapWidth=(p.End-p.Start)+1; 75BitmapnewMap=newBitmap(newMapWidth,y); 76for(intni=p.Start;ni<=p.End;ni++) 77{ 78for(intnj=0;nj 79{ 80newMap.SetPixel(ni-p.Start,nj,map.GetPixel(ni,nj)); 81} 82} 83resultList.Add(newMap); 84} 85returnresultList; 86} 87} 88returnresultList; 89} 90 代码中的Pair,代表扫描线与曲线的一对交点: Code 1privateclassPair 2{ 3publicPair(); 4publicintCharPixelCount{get;set;} 5publicintCharPixelXDensity{get;} 6publicintEnd{get;set;} 7publicintStart{get;set;} 8publicBitmapConverter.PairStatusStatus{get;set;} 9publicintWidth{get;} 10} 11 PairStatus代表Pair的状态。 具体哪个状态是什么意义,我已经忘了。 Code 1privateenumPairStatus 2{ 3JustCreated, 4Start, 5End 6} 7 以上这一段代码写的很辛苦,因为要处理很多特殊情况。 那个PairStatus也是为处理特殊情况引进的。 垂直投影与切割的代码简单一些,不贴了,见附后的dll的BitmapConverter.TrimHeight方法。 以上用到的是朴素的去噪与切割方法。 有些图片,尤其是验证码图片,需要特别的去噪处理。 具体操作方法就是,打开CxImage(),或者Paint.Net,用上面的那些图片处理方法,看看能否有效去噪。 记住自己的操作步骤,然后翻他们的源代码,将其中的算法提取出来。 还有什么细化啊,滤波啊,这些处理可以提高图片的质量。 具体可参考ITK的代码或图像处理书籍。 (3)提取每一个字符的特征,生成特征矢量或特征矩阵 将切割出来的字符,分割成一个一个的小块,比如3×3,5×5,或3×5,或10×8,然后统计一下每小块的值为255的像素数量,这样得到一个矩阵M,或者将这个矩阵简化为矢量V。 通过以上3步,就可以将一个车牌中的字符数值化为矢量了。 (1)-(3)步具体的代码流程如下: Code 1 2BitmapConverter.ToGrayBmp(bitmap);//图片灰度化 3BitmapConverter.Binarizate(bitmap);//图片二值化 4IList 5Bitmapmap0=BitmapConverter.TrimHeight(mapList[0],DefaultHeightTrimThresholdValue);//垂直投影然后切割 6ImageSpliterspliter=newImageSpliter(map0); 7spliter.WidthSplitCount=DefaultWidthSplitCount; 8spliter.HeightSplitCount=DefaultHeightSplitCount; 9spliter.Init(); 10 然后,通过spliter.ValueList就可以获得Bitmapmap0的矢量表示。 (4)分类 分类的原理很简单。 用(Vij,Ci)表示一个样本。 其中,Vij是样本图片经过上面过程数值化后的矢量。 Ci是人肉眼识别这张图片,给出的结果。 Vij表明,有多个样本,它们的数值化后的矢量不同,但是它们的结果都是Ci。 假设待识别的图片矢量化后,得到的矢量是V’。 直观上,我们会有这样一个思路,就是这张待识别的图片,最像样本库中的某张图片,那么我们就将它当作那张图片,将它识别为样本库中那张图片事先指定的字符。 在我们眼睛里,判断一张图片和另一张图片是否相似很简单,但对于电脑来说,就很难判断了。 我们前面已经将图片数值化为一个个维度一样的矢量,电脑是怎样判断一个矢量与另一个矢量相似的呢? 这里需要计算一个矢量与另一个矢量间的距离。 这个距离越短,则认为这两个矢量越相似。 我用SampleVector Code 1publicclassSampleVector 2{ 3protectedT[]Vector{get;set;} 4publicInt32Dimension{get{returnVector.Length;}} 5…… 6} 7 T代表数据类型,可以为Int32,也可以为Double等更精确的类型。 测量距离的公共接口为: IMetric Code 1publicinterfaceIMetric 2{ 3TReturnCompute(SampleVector 4} 5 常用的是MinkowskiMetric。 Code 1/// 2///Minkowski测度。 3/// 4publicclassMinkowskiMetric IMetric 5{ 6publicInt32Scale{get;privateset;} 7publicMinkowskiMetric(Int32scale) 8{Scale=scale;} 9 10publicDoubleCompute(SampleVector 11{ 12if(v1==null||v2==null)thrownewArgumentNullException(); 13if(v1.Dimension! =v2.Dimension)thrownewArgumentException("v1和v2的维度不等."); 14Doubleresult=0; 15for(inti=0;i 16{ 17result+=Math.Pow(Math.Abs(Convert.ToDouble(v1[i])-Convert.ToDouble(v2[i])),Scale); 18} 19returnMath.Pow(result,1.0/Scale); 20} 21} 22 23MetricFactory负责生产各种维度的MinkowskiMetric: 24 25publicclassMetricFactory 26{ 27publicstaticIMetric 28{ 29returnnewMinkowskiMetric 30} 31 32publicstaticIMetric 33{ 34returnCreateMinkowskiMetric (2); 35} 36} 37 MinkowskiMetric是普遍使用的测度。 但不一定是最有效的量。 因为它对于矢量V中的每一个点都一视同仁。 而在图像识别中,每一个点的重要性却并不一样,例如,Q和O的识别,特征在下半部分,下半部分的权重应该大于上半部分。 对于这些易混淆的字符,需要设计特殊的测量方法。 在车牌识别中,其它易混淆的有D和0,0和O,I和1。 MinkowskiMetric识别这些字符,效果很差。 因此,当碰到这些字符时,需要进行特别的处理。 由于当时时间紧,我就只用了MinkowskiMetric。 我的代码中,只实现了哪个最近,就选哪个。 更好的方案是用K近邻分类器或神经网络分类器。 K近邻的原理是,找出和待识别的图片(矢量)距离最近的K个样本,然后让这K个样本使用某种规则计算(投票),这个新图片属于哪个类别(C);神经网络则将测量的过程和投票判决的过程参数化,使它可以随着样本的增加而改变,是这样的一种学习机。 有兴趣的可以去看《模式分类》一书的第三章和第四章。 二、变态字符的识别 有些字符变形很严重,有的字符连在一起互相交叉,有的字符被掩盖在一堆噪音海之中。 对这类字符的识别需要用上特殊的手段。 下面介绍几种几个经典的处理方法,这些方法都是被证实对某些问题很有效的方法: (1)切线距离(TangentDistance): 可用于处理字符的各种变形,OCR的核心技术之一。 (2)霍夫变换(HoughTransform): 对噪音极其不敏感,常用于从图片中提取各种形状。 图像识别中最基本的方法之一。 (3)形状上下文(ShapeContext): 将特征高维化,对形变不很敏感,对噪音也不很敏感。 新世纪出现的新方法。 因为这几种方法我均未编码实现过,因此只简单介绍下原理及主要应用场景。 (1)切线距离 前面介绍了MinkowskiMetric。 这里我们看看下面这张图: 一个正写的1与一个歪着的1. 用MinkowskiMetric计算的话,两者的MinkowskiMetric很大。 然而,在图像识别中,形状形变是常事。 理论上,为了更好地识别,我们需要对每一种形变都采足够的样,这样一来,会发现样本数几乎无穷无尽,计算量越来越大。 怎么办呢? 那就是通过计算切线距离,来代替直接距离。 切线距离比较抽象,我们将问题简化为二维空间,以便以理解。 上图有两条曲线。 分别是两个字符经过某一形变后所产生的轨迹。 V1和V2是2个样本。 V’是待识别图片。 如果用样本之间的直接距离,比较哪个样本离V’最近,就将V’当作哪一类,这样的话,就要把V’分给V1了。 理论上,如果我们无限取
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 车牌 识别 验证
![提示](https://static.bdocx.com/images/bang_tan.gif)