日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

车牌识别思路

發布時間:2023/11/30 编程问答 28 豆豆
生活随笔 收集整理的這篇文章主要介紹了 车牌识别思路 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

本文源自我之前花了2天時間做的一個簡單的車牌識別系統。那個項目,時間太緊,樣本也有限,達不到對方要求的95%識別率(主要對于車牌來說,D,0,O,I,1等等太相似了。然后,漢字的識別難度也不小),因此未被對方接受。在此放出,同時描述一下思路及算法。

全文分兩部分,第一部分講車牌識別及普通驗證碼這一類識別的普通方法,第二部分講對類似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)?/?((double)?n2);
77?????????????double?sb?=?((n1?*?n2)?*?(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)???????去噪,然后切割成一個一個的字符

?

上面這張車牌切割是比較簡單的,從左到右掃描一下,碰見空大的,咔嚓一刀,就解決了。但有一些車牌,比如這張:

?

????簡單的掃描就解決不了。因此需要一個比較通用的去噪和切割算法。這里我采用的是比較樸素的方法:

????將上面的圖片看成是一個平面。將圖片向水平方向投影,這樣有字的地方的投影值就高,沒字的地方投影得到的值就低。這樣會得到一根曲線,像一個又一個山頭。下面是我手畫示意圖:

?

?????然后,用一根掃描線(上圖中的S)從下向上掃描。這個掃描線會與圖中曲線存在交點,這些交點會將山頭分割成一個又一個區域。車牌圖片一般是7個字符,因此,當掃描線將山頭分割成七個區域時停止。然后根據這七個區域向水平線的投影的坐標就可以將圖片中的七個字符分割出來。

?????但是,現實是復雜的。比如,“川”字,它的水平投影是三個山頭。按上面這種掃描方法會將它切開。因此,對于上面的切割,需要加上約束條件:每個山頭有一個中心線,山頭與山頭的中心線的距離必需在某一個值之上,否則,則需要將這兩個山頭進行合并。加上這個約束之后,便可以有效的切割了。

以上是水平投影。然后還需要做垂直投影與切割。這里的垂直投影與切割就一個山頭,因此好處理一些。

切割結果如下:

水平投影及切割代碼:

?


?1?public?
static?IList<Bitmap>?Split(Bitmap?map,?int?count)
?2?{
?3?????if?(count?<=?0)
?4?????{
?5?????????throw?new?ArgumentOutOfRangeException("Count?必須大于0.");
?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?==?CharGrayValue)
17?????????????{
18?????????????????xNormal[i]++;
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?(xNormal[j]?>=?i)
29?????????????{
30?????????????????if?((j?==?(x?-?1))?&&?(pair.Status?==?PairStatus.Start))
31?????????????????{
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?==?PairStatus.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.Width?<?(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?=?new?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,代表掃描線與曲線的一對交點:


?1?private?
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的狀態。具體哪個狀態是什么意義,我已經忘了。


1?private?
enum?PairStatus
2?{
3?????JustCreated,
4?????Start,
5?????End
6?}
7?

?

以上這一段代碼寫的很辛苦,因為要處理很多特殊情況。那個PairStatus?也是為處理特殊情況引進的。

垂直投影與切割的代碼簡單一些,不貼了,見附后的dll的BitmapConverter.TrimHeight方法。

以上用到的是樸素的去噪與切割方法。有些圖片,尤其是驗證碼圖片,需要特別的去噪處理。具體操作方法就是,打開CxImage(http://www.codeproject.com/KB/graphics/cximage.aspx),或者Paint.Net,用上面的那些圖片處理方法,看看能否有效去噪。記住自己的操作步驟,然后翻他們的源代碼,將其中的算法提取出來。還有什么細化啊,濾波啊,這些處理可以提高圖片的質量。具體可參考ITK的代碼或圖像處理書籍。

?

(3)???????提取每一個字符的特征,生成特征矢量或特征矩陣

?

將切割出來的字符,分割成一個一個的小塊,比如3×3,5×5,或3×5,或10×8,然后統計一下每小塊的值為255的像素數量,這樣得到一個矩陣M,或者將這個矩陣簡化為矢量V。

通過以上3步,就可以將一個車牌中的字符數值化為矢量了。

(1)-(3)步具體的代碼流程如下:


?1?????????????
?2?????????????BitmapConverter.ToGrayBmp(bitmap);????//?圖片灰度化
?3?????????????BitmapConverter.Binarizate(bitmap);?//?圖片二值化
?4?????????????IList<Bitmap>?mapList?=?BitmapConverter.Split(bitmap,?DefaultCharsCount);?//?水平投影然后切割
?5?????????????Bitmap?map0?=?BitmapConverter.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?的矢量表示。

??? (4)???分類

分類的原理很簡單。用(Vij,Ci)表示一個樣本。其中,Vij是樣本圖片經過上面過程數值化后的矢量。Ci是人肉眼識別這張圖片,給出的結果。Vij表明,有多個樣本,它們的數值化后的矢量不同,但是它們的結果都是Ci。假設待識別的圖片矢量化后,得到的矢量是V’。

直觀上,我們會有這樣一個思路,就是這張待識別的圖片,最像樣本庫中的某張圖片,那么我們就將它當作那張圖片,將它識別為樣本庫中那張圖片事先指定的字符。

在我們眼睛里,判斷一張圖片和另一張圖片是否相似很簡單,但對于電腦來說,就很難判斷了。我們前面已經將圖片數值化為一個個維度一樣的矢量,電腦是怎樣判斷一個矢量與另一個矢量相似的呢?

這里需要計算一個矢量與另一個矢量間的距離。這個距離越短,則認為這兩個矢量越相似。

我用?SampleVector<T>?來代表矢量:


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


1?????
public?interface?IMetric<TElement,TReturn>
2?????{
3?????????TReturn?Compute(SampleVector<TElement>?v1,?SampleVector<TElement>?v2);
4?????}
5?

?

常用的是MinkowskiMetric

?


?1?????
///?<summary>
?2?????///?Minkowski?測度。
?3?????///?</summary>
?4?????public?class?MinkowskiMetric<TElement>?:?IMetric<TElement,?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?!=?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(v1[i])?-?Convert.ToDouble(v2[i])),?Scale);
18?????????????}
19?????????????return?Math.Pow(result,?1.0?/?Scale);
20?????????}
21?}
22?
23?MetricFactory?負責生產各種維度的MinkowskiMetric:
24?
25?????public?class?MetricFactory
26?????{
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>?CreateEuclideanMetric<TElement>()
33?????????{
34?????????????return?CreateMinkowskiMetric<TElement>(2);
35?????????}
36?????}
37?

?

MinkowskiMetric是普遍使用的測度。但不一定是最有效的量。因為它對于矢量V中的每一個點都一視同仁。而在圖像識別中,每一個點的重要性卻并不一樣,例如,Q和O的識別,特征在下半部分,下半部分的權重應該大于上半部分。對于這些易混淆的字符,需要設計特殊的測量方法。在車牌識別中,其它易混淆的有D和0,0和O,I和1。Minkowski Metric識別這些字符,效果很差。因此,當碰到這些字符時,需要進行特別的處理。由于當時時間緊,我就只用了Minkowski Metric。

我的代碼中,只實現了哪個最近,就選哪個。更好的方案是用K近鄰分類器或神經網絡分類器。K近鄰的原理是,找出和待識別的圖片(矢量)距離最近的K個樣本,然后讓這K個樣本使用某種規則計算(投票),這個新圖片屬于哪個類別(C);神經網絡則將測量的過程和投票判決的過程參數化,使它可以隨著樣本的增加而改變,是這樣的一種學習機。有興趣的可以去看《模式分類》一書的第三章和第四章。

?

二、?變態字符的識別

?

有些字符變形很嚴重,有的字符連在一起互相交叉,有的字符被掩蓋在一堆噪音海之中。對這類字符的識別需要用上特殊的手段。

下面介紹幾種幾個經典的處理方法,這些方法都是被證實對某些問題很有效的方法:

(1)???切線距離?(Tangent Distance):可用于處理字符的各種變形,OCR的核心技術之一。

(2)???霍夫變換(Hough Transform):對噪音極其不敏感,常用于從圖片中提取各種形狀。圖像識別中最基本的方法之一。

(3)???形狀上下文(Shape Context):將特征高維化,對形變不很敏感,對噪音也不很敏感。新世紀出現的新方法。

?

因為這幾種方法我均未編碼實現過,因此只簡單介紹下原理及主要應用場景。

?

(1)???切線距離

?

前面介紹了MinkowskiMetric。這里我們看看下面這張圖:一個正寫的1與一個歪著的1.

?

?

用MinkowskiMetric計算的話,兩者的MinkowskiMetric很大。

然而,在圖像識別中,形狀形變是常事。理論上,為了更好地識別,我們需要對每一種形變都采足夠的樣,這樣一來,會發現樣本數幾乎無窮無盡,計算量越來越大。

怎么辦呢?那就是通過計算切線距離,來代替直接距離。切線距離比較抽象,我們將問題簡化為二維空間,以便以理解。

?

?

上圖有兩條曲線。分別是兩個字符經過某一形變后所產生的軌跡。V1和V2是2個樣本。V’是待識別圖片。如果用樣本之間的直接距離,比較哪個樣本離V’最近,就將V’當作哪一類,這樣的話,就要把V’分給V1了。理論上,如果我們無限取樣的話,下面那一條曲線上的某個樣本離V’最近,V’應該歸類為V2。不過,無限取樣不現實,于是就引出了切線距離:在樣本V1,V2處做切線,然后計算V’離這兩條切線的距離,哪個最近就算哪一類。這樣一來,每一個樣本,就可以代表它附近的一個樣本區域,不需要海量的樣本,也能有效的計算不同形狀間的相似性。

深入了解切線距離,可參考這篇文章。Transformation invariance in pattern recognition – tangent distance and tangent propagation (http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.32.9482)這篇文章。

?

(2)???霍夫變換

?

霍夫變換出自1962年的一篇專利。它的原理非常簡單:就是坐標變換的問題。

?

如,上圖中左圖中的直線,對應著有圖中k-b坐標系中的一個點。通過坐標變換,可以將直線的識別轉換為點的識別。點的識別就比直線識別簡單的多。為了避免無限大無限小問題,常用的是如下變換公式:

?

?

下面這張圖是wikipedia上一張霍夫變換的示意圖。左圖中的兩條直線變換后正對應著右圖中的兩個亮點。

?

?

?

通過霍夫變換原理可以看出,它的抗干擾性極強極強:如果直線不是連續的,是斷斷續續的,變換之后仍然是一個點,只是這個點的強度要低一些。如果一個直線被一個矩形遮蓋住了,同樣不影響識別。因為這個特征,它的應用性非常廣泛。

?

對于直線,圓這樣容易被參數化的圖像,霍夫變換是最擅長處理的。對于一般的曲線,可通過廣義霍夫變換進行處理。感興趣的可以google之,全是數學公式,看的人頭疼。

?

(3)???形狀上下文

?

圖像中的像素點不是孤立的,每個像素點,處于一個形狀背景之下,因此,在提取特征時,需要將像素點的背景也作為該像素點的特征提取出來,數值化。

形狀上下文(Shape Context,形狀背景)就是這樣一種方法:假定要提取像素點O的特征,采用上圖(c)中的坐標系,以O點作為坐標系的圓心。這個坐標系將O點的上下左右切割成了12×5=60小塊,然后統計這60小塊之內的像素的特征,將其數值化為12×5的矩陣,上圖中的(d),(e),(f)便分別是三個像素點的Shape Context數值化后的結果。如此一來,提取的每一個點的特征便包括了形狀特征,加以計算,威力甚大。來看看Shape Context的威力:

上圖中的驗證碼,對Shape Context來說只是小Case。

?

看看這幾張圖。嘿嘿,硬是給識別出來了。

?

Shape Context是新出現的方法,其威力到底有多大目前還未見底。這篇文章是Shape context的必讀文章:Shape Matching and Object Recognitiom using shape contexts(http://www.cs.berkeley.edu/~malik/papers/BMP-shape.pdf)。最后那兩張驗證碼識別圖出自Greg Mori,Jitendra Malik的《Recognizing Objects in Adversarial Clutter:Breaking a Visual CAPTCHA》一文。

?

===========================================================?

?

附件:第一部分的代碼(vcr.zip). 3個dll文件,反編譯看的很清晰。源代碼反而沒dll好看,我就不放了。其中,Orc.Generics.dll是幾個泛型類,Orc.ImageProcess.Common.dll 對圖像進行處理和分割,Orc.PatternRecognition.dll?是識別部分。

?

??? 這三個dll可以直接用在車牌識別上。用于車牌識別,對易混淆的那幾個字符識別率較差,需要補充幾個分類器,現有分類器識別結果為D ,O,0,I,1等時,用新分類器識別。用于識別驗證碼需要改一改。

??? 有個asp.net的調用例子可實現在線上傳圖片識別,因為其中包含多張車牌信息,不方便放出來。我貼部分代碼出來:

?


Global.asax:

????
void?Application_Start(object?sender,?EventArgs?e)?
????{
????????log4net.Config.XmlConfigurator.Configure();
????????Orc.Spider.Vcr.DaoConfig.Init();
????????Classifier.Update(Server);
????}

DaoConfig:

using?System;
using?Castle.ActiveRecord;
using?Castle.ActiveRecord.Framework;
using?Castle.ActiveRecord.Framework.Config;

namespace?Orc.Spider.Vcr
{
????public?static?class?DaoConfig
????{
????????private?static?Boolean?Inited?=?false;

????????public?static?void?Init()
????????{
????????????if?(!Inited)
????????????{
????????????????
????????????????Inited?=?true;
????????????????XmlConfigurationSource?con?=?new?XmlConfigurationSource(AppDomain.CurrentDomain.BaseDirectory?+?@"\ActiveRecord.config");

????????????????ActiveRecordStarter.Initialize
????????????????????(con,
????????????????????????typeof(TrainPattern)?
????????????????????????);
????????????}
????????}
????}
}

TrainPattern://?TrainPattern存在數據庫里

????[ActiveRecord("TrainPattern")]
????public?class?TrainPattern?:?ActiveRecordBase<TrainPattern>
????{
????????[PrimaryKey(PrimaryKeyType.Native,?"Id")]
????????public?Int32?Id?{?get;?set;?}

????????[Property("FileName")]
????????public?String?FileName?{?get;?set;?}

????????[Property("Category")]
????????public?String?Category?{?get;?set;?}

????????public?static?TrainPattern[]?FindAll()
????????{
????????????String?hql?=?"from?TrainPattern?ORDER?BY?Category?DESC";
????????????SimpleQuery<TrainPattern>?query?=?new?SimpleQuery<TrainPattern>(hql);
????????????return?query.Execute();
????????}
????}

Classifier://主要調用封裝在這里

public?class?Classifier
{
????protected?static?Orc.PatternRecognition.KnnClassifier<Int32>?DefaultChineseCharClassifier;
????protected?static?Orc.PatternRecognition.KnnClassifier<Int32>?DefaultEnglishAndNumberCharClassifier;
????protected?static?Orc.PatternRecognition.KnnClassifier<Int32>?DefaultNumberCharClassifier;

????public?static?Int32?DefaultWidthSplitCount?=?3;
????public?static?Int32?DefaultHeightSplitCount?=?3;
????public?static?Int32?DefaultCharsCount?=?7;?//?一張圖片中包含的字符個數
????public?static?Int32?DefaultHeightTrimThresholdValue?=?4;

????public?static?ILog?Log?=?LogManager.GetLogger("Vcr");

????public?static?void?Update(HttpServerUtility?server)
????{
????????TrainPattern[]?TPList?=?TrainPattern.FindAll();
????????
????????if?(TPList?==?null)?return;

????????DefaultChineseCharClassifier?=?new?KnnClassifier<Int32>(DefaultWidthSplitCount?*?DefaultHeightSplitCount);
????????DefaultEnglishAndNumberCharClassifier?=?new?KnnClassifier<Int32>(DefaultWidthSplitCount?*?DefaultHeightSplitCount);
????????DefaultNumberCharClassifier?=?new?KnnClassifier<Int32>(DefaultWidthSplitCount?*?DefaultHeightSplitCount);

????????foreach?(TrainPattern?tp?in?TPList)
????????{
????????????String?path?=?server.MapPath(".")?+?"/VcrImage/"?+?tp.FileName;
????????????using?(Bitmap?bitmap?=?new?Bitmap(path))
????????????{
????????????????TrainPattern<Int32>?tpv?=?CreateTainPatternVector(bitmap,?tp.Category.Substring(0,?1));
????????????????Char?c?=?tpv.Category[0];
????????????????if?(c?>=?'0'?&&?c?<=?'9')
????????????????{
????????????????????DefaultEnglishAndNumberCharClassifier.AddTrainPattern(tpv);
????????????????????DefaultNumberCharClassifier.AddTrainPattern(tpv);
????????????????}
????????????????else?if?(c?>=?'a'?&&?c?<=?'z')
????????????????????DefaultEnglishAndNumberCharClassifier.AddTrainPattern(tpv);
????????????????else?if?(c?>=?'A'?&&?c?<=?'Z')
????????????????????DefaultEnglishAndNumberCharClassifier.AddTrainPattern(tpv);
????????????????else
????????????????????DefaultChineseCharClassifier.AddTrainPattern(tpv);
????????????}
????????}
????}

????protected?static?TrainPattern<Int32>?CreateTainPatternVector(Bitmap?bitmap,?String?categoryChars)
????{
????????TrainPattern<int>?tpv?=?new?TrainPattern<int>(?CreateSampleVector(bitmap),?categoryChars);
????????tpv.XNormalSample?=?CreateXNormalSampleVector(bitmap);
????????tpv.YNormalSample?=?CreateYNormalSampleVector(bitmap);
????????return?tpv;
????}

????protected?static?SampleVector<Int32>?CreateSampleVector(Bitmap?bitmap)
????{
????????????ImageSpliter?spliter?=?new?ImageSpliter(bitmap);
????????????spliter.WidthSplitCount?=?DefaultWidthSplitCount;
????????????spliter.HeightSplitCount?=?DefaultHeightSplitCount;
????????????spliter.Init();
????????????return?new?SampleVector<Int32>(spliter.ValueList);
????}

????protected?static?SampleVector<Int32>?CreateYNormalSampleVector(Bitmap?bitmap)
????{
????????ImageSpliter?spliter?=?new?ImageSpliter(bitmap);
????????spliter.WidthSplitCount?=?1;
????????spliter.HeightSplitCount?=?DefaultHeightSplitCount;
????????spliter.Init();
????????return?new?SampleVector<Int32>(spliter.ValueList);
????}

????protected?static?SampleVector<Int32>?CreateXNormalSampleVector(Bitmap?bitmap)
????{
????????ImageSpliter?spliter?=?new?ImageSpliter(bitmap);
????????spliter.WidthSplitCount?=?DefaultWidthSplitCount;
????????spliter.HeightSplitCount?=?1;
????????spliter.Init();
????????return?new?SampleVector<Int32>(spliter.ValueList);
????}

????public?static?String?Classify(String?imageFileName)
????{
????????Log.Debug("識別文件:"?+?imageFileName);

????????String?result?=?String.Empty;
????????if?(DefaultChineseCharClassifier?==?null?||?DefaultEnglishAndNumberCharClassifier?==?null)?throw?new?Exception("識別器未初始化.");
????????using?(Bitmap?bitmap?=?new?Bitmap(imageFileName))
????????{
????????????BitmapConverter.ToGrayBmp(bitmap);
????????????BitmapConverter.Binarizate(bitmap);
????????????IList<Bitmap>?mapList?=?BitmapConverter.Split(bitmap,?DefaultCharsCount);

????????????if?(mapList.Count?==?DefaultCharsCount)
????????????{
????????????????Bitmap?map0?=?BitmapConverter.TrimHeight(mapList[0],?DefaultHeightTrimThresholdValue);
????????????????TrainPattern<Int32>?tp0?=?CreateTainPatternVector(map0,?"?");
????????????????String?sv0Result?=?DefaultChineseCharClassifier.Classify(tp0);
????????????????Console.WriteLine("識別樣本:?"?+?tp0.Sample.ToString());
????????????????result?+=?sv0Result;
????????????????for?(int?i?=?1;?i?<?mapList.Count;?i++)
????????????????{
????????????????????Bitmap?mapi?=?BitmapConverter.TrimHeight(mapList[i],?DefaultHeightTrimThresholdValue);
????????????????????TrainPattern<Int32>?tpi?=?CreateTainPatternVector(mapi,?"?");
????????????????????Console.WriteLine("識別樣本:?"?+?tpi.Sample.ToString());

????????????????????if?(i?<?mapList.Count?-?3)
????????????????????????result?+=?DefaultEnglishAndNumberCharClassifier.Classify(tpi);
????????????????????else
????????????????????????result?+=?DefaultNumberCharClassifier.Classify(tpi);
????????????????}
????????????}

????????????return?result;
????????}
????}

????/*
????public?static?IList<Tuple<Double,String>>?ComputeDistance(String?imageFileName)
????{
????????if?(DefaultChineseCharClassifier?==?null)?throw?new?Exception("識別器未初始化.");
????????using?(Bitmap?bitmap?=?new?Bitmap(imageFileName))
????????{
????????????ImageSpliter?spliter?=?new?ImageSpliter(bitmap);
????????????spliter.WidthSplitCount?=?DefaultWidthSplitCount;
????????????spliter.HeightSplitCount?=?DefaultHeightSplitCount;
????????????spliter.Init();

????????????SampleVector<Int32>?sv?=?new?SampleVector<Int32>(spliter.ValueList);
????????????return?DefaultChineseCharClassifier.ComputeDistance(sv);
????????}
????}
*/
}

總結

以上是生活随笔為你收集整理的车牌识别思路的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。