车牌识别及验证码识别的一般思路_第1页
车牌识别及验证码识别的一般思路_第2页
车牌识别及验证码识别的一般思路_第3页
车牌识别及验证码识别的一般思路_第4页
车牌识别及验证码识别的一般思路_第5页
已阅读5页,还剩19页未读 继续免费阅读

下载本文档

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

文档简介

1、车牌识别及验证码识别的一般思路 本文源自我之前花了2天时间做的一个简单的车牌识别系统。那个项目,时间太紧,样本也有限,达不到对方要求的95%识别率(主要对于车牌来说,D,0,O,I,1等等太相似了。然后,汉字的识别难度也不小),因此未被对方接受。在此放出,同时描述一下思路及算法。全文分两部分,第一部分讲车牌识别及普通验证码这一类识别的普通方法,第二部分讲对类似QQ验证码,Gmail验证码这一类变态验证码的识别方法和思路。一、车牌/验证码识别的普通方法车牌、验证码识别的普通方法为:(1) 将图片灰度化与二值化(2) 去噪,然后切割成一个一个的字符(3) 提取每一个字符的特征,生成特征矢量或特征矩

2、阵(4) 分类与学习。将特征矢量或特征矩阵与样本库进行比对,挑选出相似的那类样本,将这类样本的值作为输出结果。下面借着代码,描述一下上述过程。因为更新SVN Server,我以前以bdb储存的代码访问不了,因此部分代码是用Reflector反编译过来的,望见谅。(1) 图片的灰度化与二值化这样做的目的是将图片的每一个象素变成0或者255,以便以计算。同时,也可以去除部分噪音。图片的灰度化与二值化的前提是bmp图片,如果不是,则需要首先转换为bmp图片。用代码说话,我的将图片灰度化的代码(算法是在网上搜到的):Code1protectedstaticColorGray(Colorc)23intr

3、gb=Convert.ToInt32(double)(0.3*c.R)+(0.59*c.G)+(0.11*c.B);4returnColor.FromArgb(rgb,rgb,rgb);56通过将图片灰度化,每一个象素就变成了一个0-255的灰度值。然后是将灰度值二值化为 0 或255。一般的处理方法是设定一个区间,比如,a,b,将a,b之间的灰度全部变成255,其它的变成0。这里我采用的是网上广为流行的自适应二值化算法。Code1publicstaticvoidBinarizate(Bitmapmap)23inttv=ComputeThresholdValue(map);4intx=map.

4、Width;5inty=map.Height;6for(inti=0;ix;i+)78for(intj=0;j=tv)1112map.SetPixel(i,j,Color.FromArgb(0xff,0xff,0xff);1314else1516map.SetPixel(i,j,Color.FromArgb(0,0,0);171819202122privatestaticintComputeThresholdValue(Bitmapimg)2324inti;25intk;26doublecsum;27intthresholdValue=1;28intihist=newint0x100;29fo

5、r(i=0;i0x100;i+)3031ihisti=0;3233intgmin=0xff;34intgmax=0;35for(i=1;i(img.Width-1);i+)3637for(intj=1;jgmax)4243gmax=cn;4445if(cngmin)4647gmin=cn;48495051doublesum=csum=0.0;52intn=0;53for(k=0;k=0xff;k+)5455sum+=k*ihistk;56n+=ihistk;5758if(n=0)5960return60;6162doublefmax=-1.0;63intn1=0;64for(k=0;kfmax

6、)7980fmax=sb;81thresholdValue=k;82838485returnthresholdValue;868788灰度化与二值化之前的图片:灰度化与二值化之后的图片:注:对于车牌识别来说,这个算法还不错。对于验证码识别,可能需要针对特定的网站设计特殊的二值化算法,以过滤杂色。(2) 去噪,然后切割成一个一个的字符上面这张车牌切割是比较简单的,从左到右扫描一下,碰见空大的,咔嚓一刀,就解决了。但有一些车牌,比如这张:简单的扫描就解决不了。因此需要一个比较通用的去噪和切割算法。这里我采用的是比较朴素的方法:将上面的图片看成是一个平面。将图片向水平方向投影,这样有字的地方的投影值

7、就高,没字的地方投影得到的值就低。这样会得到一根曲线,像一个又一个山头。下面是我手画示意图:然后,用一根扫描线(上图中的S)从下向上扫描。这个扫描线会与图中曲线存在交点,这些交点会将山头分割成一个又一个区域。车牌图片一般是7个字符,因此,当扫描线将山头分割成七个区域时停止。然后根据这七个区域向水平线的投影的坐标就可以将图片中的七个字符分割出来。但是,现实是复杂的。比如,“川”字,它的水平投影是三个山头。按上面这种扫描方法会将它切开。因此,对于上面的切割,需要加上约束条件:每个山头有一个中心线,山头与山头的中心线的距离必需在某一个值之上,否则,则需要将这两个山头进行合并。加上这个约束之后,便可以

8、有效的切割了。以上是水平投影。然后还需要做垂直投影与切割。这里的垂直投影与切割就一个山头,因此好处理一些。切割结果如下:水平投影及切割代码:Code1publicstaticIListSplit(Bitmapmap,intcount)23if(count=0)45thrownewArgumentOutOfRangeException(Count必须大于0.);67IListresultList=newList();8intx=map.Width;9inty=map.Height;10intsplitBitmapMinWidth=4;11intxNormal=newintx;12for(inti

9、=0;ix;i+)1314for(intj=0;jy;j+)1516if(map.GetPixel(i,j).R=CharGrayValue)1718xNormali+;19202122Pairpair=newPair();23for(inti=0;iy;i+)2425IListpairList=newList(count+1);26for(intj=0;j=i)2930if(j=(x-1)&(pair.Status=PairStatus.Start)3132pair.End=j;33pair.Status=PairStatus.End;34if(pair.End-pair.Start)=sp

10、litBitmapMinWidth)3536pairList.Add(pair);3738pair=newPair();3940elseif(pair.Status=PairStatus.JustCreated)4142pair.Start=j;43pair.Status=PairStatus.Start;444546elseif(pair.Status=PairStatus.Start)4748pair.End=j;49pair.Status=PairStatus.End;50if(pair.End-pair.Start)=splitBitmapMinWidth)5152pairList.A

11、dd(pair);5354pair=newPair();5556if(pairList.Countcount)5758break;596061if(pairList.Count=count)6263foreach(PairpinpairList)6465if(p.Width(map.Width/10)6667intwidth=(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);707172foreach(Pai

12、rpinpairList)7374intnewMapWidth=(p.End-p.Start)+1;75BitmapnewMap=newBitmap(newMapWidth,y);76for(intni=p.Start;ni=p.End;ni+)7778for(intnj=0;njy;nj+)7980newMap.SetPixel(ni-p.Start,nj,map.GetPixel(ni,nj);818283resultList.Add(newMap);8485returnresultList;868788returnresultList;8990代码中的 Pair,代表扫描线与曲线的一对交

13、点:Code1privateclassPair23publicPair();4publicintCharPixelCountget;set;5publicintCharPixelXDensityget;6publicintEndget;set;7publicintStartget;set;8publicBitmapConverter.PairStatusStatusget;set;9publicintWidthget;1011PairStatus代表Pair的状态。具体哪个状态是什么意义,我已经忘了。Code1privateenumPairStatus23JustCreated,4Start,

14、5End67以上这一段代码写的很辛苦,因为要处理很多特殊情况。那个PairStatus 也是为处理特殊情况引进的。垂直投影与切割的代码简单一些,不贴了,见附后的dll的BitmapConverter.TrimHeight方法。以上用到的是朴素的去噪与切割方法。有些图片,尤其是验证码图片,需要特别的去噪处理。具体操作方法就是,打开CxImage(),或者Paint.Net,用上面的那些图片处理方法,看看能否有效去噪。记住自己的操作步骤,然后翻他们的源代码,将其中的算法提取出来。还有什么细化啊,滤波啊,这些处理可以提高图片的质量。具体可参考ITK的代码或图像处理书籍。(3) 提取每一个字符的特征,

15、生成特征矢量或特征矩阵将切割出来的字符,分割成一个一个的小块,比如33,55,或35,或108,然后统计一下每小块的值为255的像素数量,这样得到一个矩阵M,或者将这个矩阵简化为矢量V。通过以上3步,就可以将一个车牌中的字符数值化为矢量了。(1)-(3)步具体的代码流程如下:Code12BitmapConverter.ToGrayBmp(bitmap);/图片灰度化3BitmapConverter.Binarizate(bitmap);/图片二值化4IListmapList=BitmapConverter.Split(bitmap,DefaultCharsCount);/水平投影然后切割5Bi

16、tmapmap0=BitmapConverter.TrimHeight(mapList0,DefaultHeightTrimThresholdValue);/垂直投影然后切割6ImageSpliterspliter=newImageSpliter(map0);7spliter.WidthSplitCount=DefaultWidthSplitCount;8spliter.HeightSplitCount=DefaultHeightSplitCount;9spliter.Init();10然后,通过spliter.ValueList就可以获得 Bitmap map0 的矢量表示。 (4) 分类分

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

18、个矢量间的距离。这个距离越短,则认为这两个矢量越相似。我用 SampleVector 来代表矢量:Code1publicclassSampleVector23protectedTVectorget;set;4publicInt32DimensiongetreturnVector.Length;567T代表数据类型,可以为Int32,也可以为Double等更精确的类型。测量距离的公共接口为:IMetricCode1publicinterfaceIMetric23TReturnCompute(SampleVectorv1,SampleVectorv2);45常用的是MinkowskiMetric。

19、Code1/2/Minkowski测度。3/4publicclassMinkowskiMetric:IMetric56publicInt32Scaleget;privateset;7publicMinkowskiMetric(Int32scale)8Scale=scale;910publicDoubleCompute(SampleVectorv1,SampleVectorv2)1112if(v1=null|v2=null)thrownewArgumentNullException();13if(v1.Dimension!=v2.Dimension)thrownewArgumentExcepti

20、on(v1和v2的维度不等.);14Doubleresult=0;15for(inti=0;iv1.Dimension;i+)1617result+=Math.Pow(Math.Abs(Convert.ToDouble(v1i)-Convert.ToDouble(v2i),Scale);1819returnMath.Pow(result,1.0/Scale);20212223MetricFactory负责生产各种维度的MinkowskiMetric:2425publicclassMetricFactory2627publicstaticIMetricCreateMinkowskiMetric(

21、Int32scale)2829returnnewMinkowskiMetric(scale);303132publicstaticIMetricCreateEuclideanMetric()3334returnCreateMinkowskiMetric(2);353637MinkowskiMetric是普遍使用的测度。但不一定是最有效的量。因为它对于矢量V中的每一个点都一视同仁。而在图像识别中,每一个点的重要性却并不一样,例如,Q和O的识别,特征在下半部分,下半部分的权重应该大于上半部分。对于这些易混淆的字符,需要设计特殊的测量方法。在车牌识别中,其它易混淆的有D和0,0和O,I和1。Mink

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

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

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

25、话,下面那一条曲线上的某个样本离V最近,V应该归类为V2。不过,无限取样不现实,于是就引出了切线距离:在样本V1,V2处做切线,然后计算V离这两条切线的距离,哪个最近就算哪一类。这样一来,每一个样本,就可以代表它附近的一个样本区域,不需要海量的样本,也能有效的计算不同形状间的相似性。深入了解切线距离,可参考这篇文章。这篇文章。(2) 霍夫变换霍夫变换出自1962年的一篇专利。它的原理非常简单:就是坐标变换的问题。如,上图中左图中的直线,对应着有图中k-b坐标系中的一个点。通过坐标变换,可以将直线的识别转换为点的识别。点的识别就比直线识别简单的多。为了避免无限大无限小问题,常用的是如下变换公式:

26、下面这张图是wikipedia上一张霍夫变换的示意图。左图中的两条直线变换后正对应着右图中的两个亮点。通过霍夫变换原理可以看出,它的抗干扰性极强极强:如果直线不是连续的,是断断续续的,变换之后仍然是一个点,只是这个点的强度要低一些。如果一个直线被一个矩形遮盖住了,同样不影响识别。因为这个特征,它的应用性非常广泛。对于直线,圆这样容易被参数化的图像,霍夫变换是最擅长处理的。对于一般的曲线,可通过广义霍夫变换进行处理。感兴趣的可以google之,全是数学公式,看的人头疼。(3) 形状上下文图像中的像素点不是孤立的,每个像素点,处于一个形状背景之下,因此,在提取特征时,需要将像素点的背景也作为该像素

27、点的特征提取出来,数值化。形状上下文(Shape Context,形状背景)就是这样一种方法:假定要提取像素点O的特征,采用上图(c)中的坐标系,以O点作为坐标系的圆心。这个坐标系将O点的上下左右切割成了125=60小块,然后统计这60小块之内的像素的特征,将其数值化为125的矩阵,上图中的(d),(e),(f)便分别是三个像素点的Shape Context数值化后的结果。如此一来,提取的每一个点的特征便包括了形状特征,加以计算,威力甚大。来看看Shape Context的威力:上图中的验证码,对Shape Context来说只是小Case。看看这几张图。嘿嘿,硬是给识别出来了。Shape C

28、ontext是新出现的方法,其威力到底有多大目前还未见底。这篇文章是Shape context的必读文章:Shape Matching and Object Recognitiom using shape contexts()。最后那两张验证码识别图出自Greg Mori,Jitendra Malik的Recognizing Objects in Adversarial Clutter:Breaking a Visual CAPTCHA一文。=附件:第一部分的代码(vcr.zip是识别部分。 这三个dll可以直接用在车牌识别上。用于车牌识别,对易混淆的那几个字符识别率较差,需要补充几个分类器,

29、现有分类器识别结果为D ,O,0,I,1等时,用新分类器识别。用于识别验证码需要改一改。 有个的调用例子可实现在线上传图片识别,因为其中包含多张车牌信息,不方便放出来。我贴部分代码出来:CodeGlobal.asax:voidApplication_Start(objectusingSystem;usingCastle.ActiveRecord;usingusingnamespacepublicstaticclassDaoConfigprivatestaticBooleanInited=false;publicstaticvoidInit()if(!Inited)Inited=true;Xml

30、ConfigurationSourcecon=newActiveRecord.config);ActiveRecordStarter.Initialize(con,typeof(TrainPattern);TrainPattern:/TrainPattern存在数据库里ActiveRecord(TrainPattern)publicclassTrainPattern:ActiveRecordBasePrimaryKey(PrimaryKeyType.Native,Id)publicInt32Idget;set;Property(FileName)publicStringFileNameget;

31、set;Property(Category)publicStringCategoryget;set;publicstaticTrainPatternFindAll()Stringhql=fromTrainPatternORDERBYCategoryDESC;SimpleQueryquery=newSimpleQuery(hql);returnquery.Execute();Classifier:/主要调用封装在这里publicclassClassifierprotectedstaticprotectedstaticprotectedstaticpublicstaticInt32DefaultW

32、idthSplitCount=3;publicstaticInt32DefaultHeightSplitCount=3;publicstaticInt32DefaultCharsCount=7;/一张图片中包含的字符个数publicstaticInt32DefaultHeightTrimThresholdValue=4;publicstaticILogLog=LogManager.GetLogger(Vcr);publicstaticvoidUpdate(HttpServerUtilityserver)TrainPatternTPList=TrainPattern.FindAll();if(T

33、PList=null)return;DefaultChineseCharClassifier=newKnnClassifier(DefaultWidthSplitCount*DefaultHeightSplitCount);DefaultEnglishAndNumberCharClassifier=newKnnClassifier(DefaultWidthSplitCount*DefaultHeightSplitCount);DefaultNumberCharClassifier=newKnnClassifier(DefaultWidthSplitCount*DefaultHeightSpli

34、tCount);foreach(TrainPatterntpinTPList)Stringpath=server.MapPath(.)+/VcrImage/+tp.FileName;using(Bitmapbitmap=new0,1);Charc=tpv.Category0;if(c=0&c=a&c=A&c=Z)DefaultEnglishAndNumberCharClassifier.AddTrainPattern(tpv);elseDefaultChineseCharClassifier.AddTrainPattern(tpv);protectedstaticTrainPatternCre

35、ateTainPatternVector(Bitmapbitmap,StringcategoryChars)TrainPatterntpv=newTrainPattern(CreateSampleVector(bitmap),categoryChars);tpv.XNormalSample=CreateXNormalSampleVector(bitmap);tpv.YNormalSample=CreateYNormalSampleVector(bitmap);returntpv;protectedstaticSampleVectorCreateSampleVector(Bitmapbitmap

36、)ImageSpliterspliter=newImageSpliter(bitmap);spliter.WidthSplitCount=DefaultWidthSplitCount;spliter.HeightSplitCount=DefaultHeightSplitCount;spliter.Init();returnnewSampleVector(spliter.ValueList);protectedstaticSampleVectorCreateYNormalSampleVector(Bitmapbitmap)ImageSpliterspliter=newImageSpliter(b

37、itmap);spliter.WidthSplitCount=1;spliter.HeightSplitCount=DefaultHeightSplitCount;spliter.Init();returnnewSampleVector(spliter.ValueList);protectedstaticSampleVectorCreateXNormalSampleVector(Bitmapbitmap)ImageSpliterspliter=newImageSpliter(bitmap);spliter.WidthSplitCount=DefaultWidthSplitCount;spliter.HeightSplitCount=1;spliter.Init();returnnewSampleVector(spliter.ValueList);publicstaticStringClassify(StringimageFileName)Log.Debug(识别文件:+imageFileName);

温馨提示

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

评论

0/150

提交评论