天天看點

使用opencv和神經網絡車牌識别

 【原文: http://blog.csdn.net/raby_gyl/article/details/11617875】

書名:《Mastering OpenCV with Practical Computer Vision Projects》

由于添加了一個*号,顯示亂碼,不曉得怎麼回事,為了不耽誤大家看可以下載下傳word版本的翻譯:http://download.csdn.net/detail/xuluhui123/6806749

不免有錯,不在本文章中修改了,以防再次出現亂碼,這裡整理好了,但是代碼沒有中文注釋,word版本中有。

《書中部分其他章節的翻譯》:http://blog.csdn.net/raby_gyl/article/details/17472271

電子書下載下傳位址:http://download.csdn.net/detail/xuluhui123/6310819

随書源代碼下載下傳位址:http://download.csdn.net/detail/xuluhui123/6310851

程式流程圖下載下傳位址:http://download.csdn.net/detail/xuluhui123/6310811

流程圖也可參考:http://blog.csdn.net/xuluhui123/article/details/11659915

推薦:采用SVM和神經網絡車牌識别詳細流程圖,和代碼中部分解釋:http://blog.csdn.net/raby_gyl/article/details/11659915

本人英語四級水準,以下翻譯隻供自己存檔和像我一樣的初學者參閱。裡面錯誤肯定很多,也有好多英語句子我不太明白的。敬請大家糾正。我最近還要好好的消化一下。還會進行修改的。最近剛看了opencv中文版的機器學習篇,opencv自帶的特征資料都是已經做好了的,對于特征資料的生成,矩陣的操作不熟悉的我,還是在腦子裡難以形成一個整體的思路。想在網上下載下傳一些執行個體項目的代碼,好像很難,即使找到了又沒有詳細的說明,對于一個初學者可能要花好久的時間才能完全整理清楚(還必須查閱相關資料)。最近有幸在opencv論壇,看到朋友們推薦的這本書,而且附有源代碼,能夠運作,看到了效果。這使我決定要看一看,對于一個不喜歡看英文的我,硬着頭皮把第五章看完了。讀英文最大的痛苦是句子長,來回的修飾,然而第二大痛苦就是,我們記憶裡不怎麼好,讀完一段話,即使每句話都好像能了解,然而将的是什麼還是雲裡霧裡。其實我感覺最好的方法,就是自己大約了解了,翻譯下來,整成中文的,然後推敲一下是不是語義上合理。

<内容>

本章向大家介紹建立一個自動車牌識别應用(ANPR)所需要的步驟。基于不同的情況,有不同的方法和技術。例如,IR camera(紅外線錄影機),固定車位置,光亮情況,等等。我們開始建構一個ANPR應用,來檢測離車2-3米拍的照片中的車牌。在模糊的光線下,并且不是平行與地面而是與車牌的有個小角度的傾斜。

本章的主要目的是向大家介紹圖像的分割和特征提取,模式識别的基礎,和兩個重要的模式識别算法:支援向量機和人工神經網絡。在這一章,我們将包含以下内容:

1、ANPR(自動車牌識别)

2、車牌檢測

3、車牌識别

ANPR介紹:

ANRP也就是衆所周知的ALPR,或者AVI,或者CPR,是一種用在光學字元識别的監視方法和其他方法,例如分割和檢測來讀取車牌号。

在ANPR系統中最好的結果是使用一個紅外線錄影機,因為檢測和OCR(光學字元識别)分割之前的分割步驟變的簡單,幹淨和錯誤最小化。這是由于光線法則,最基本的是因為入射角度等于反射角度。當我們看一個光滑的表面例如一個平面鏡,我們能看到這個基本的反射。粗糙表面的反射例如一張紙導緻的反射稱為漫射或者散射。車牌号的主要部分有一個特殊的特性叫做回複反射。車牌的表面

是用覆寫有成千上萬個細小半球的材料做成的。這樣會使光線回複反射到光線源,我們從下面的圖可以看到:

使用opencv和神經網絡車牌識别

                    角度反射             散射或者漫射          回複反射

如果我們使用一個帶有紅外線投影結構和濾波的錄影機,我們使用帶有紅外線的錄影機重新擷取,将得到一個非常高品質的照片用來分割和随後的檢測和識别車牌數字。即不依賴于任何光線環境,如下圖所示:

使用opencv和神經網絡車牌識别

在這一章,我們沒有使用紅外線攝像,我們使用正常的攝像。我們這樣做,以至于我們沒有得到最好的結果,得到的是一個更高水準的檢測錯誤和高的錯誤識别率。這與我們使用紅外錄影機所期待的結果截然相反。然而,兩者的步驟是一樣的。

每個國家車牌的大小和規格不同,為了獲得更好的結果和減少錯誤,我們知道這些規格是佷有用的。本章使用的算法意圖是闡述ANPR的基本原理和西班牙車牌,但是我們能把他們擴到任何國家或者規格車牌。

在本章,我将使用來之西班牙的車牌。在西班牙,有三種不同大小和形狀的車牌。我們将使用最普通(使用最多)的車牌,其大小是520*110mm。兩種字元(數字和字母)的間距是41mm。數字和數字之前(或者字母和字母之間)距離是14mm。第一組字元含有四個數字。另外一組含有三個字母,其中不包括元音字母:A,E,I,O,U。和N,Q。所有的字元大小為45*77。

這些資料對于字元分割很重要,因為我們能夠檢查兩個字元和空格,來核實我們得到是一個字元而沒有其它圖檔部分。如下是一個車牌圖。

使用opencv和神經網絡車牌識别

ANPR 算法

在解釋ANPR代碼之前,我們需要定義算法的主要步驟和任務。ANPR主要分為兩步:車牌的檢測和車牌的識别。車牌檢測就是檢測車牌在整個圖像幀中的位置。當一個圖像中的車牌檢測到時,車牌的分割将交給接下來的一步——車牌識别。在車牌識别中,我們用OCR算法來決定車牌上的字母數字的字元。

在下圖我們可以看到兩個主要算法的步驟,車牌檢測和車牌識别。車牌識别之後,程式将在圖像幀中畫出檢測到的車牌。這個算法能傳回壞的結果甚至沒有結果(檢測不到)。

使用opencv和神經網絡車牌識别

每個步驟都展示在上邊的圖中,我們來定義另外三個步驟。他們通常用在模式識别算法中。

1、分割。該步檢測和移動圖像中每個感興趣的區域。

2、特征提取。該步提取每個塊的一系列的特征。

3、分類。該步從車牌識别步驟或者把圖像部分分為有車牌和無車牌的車牌檢測的步驟,(上述兩個步驟中)提取每個字元。

下圖向我們展示了在整個算法中模式識别的步驟。

使用opencv和神經網絡車牌識别

抛開主要的應用,即該應用的目的是檢測和識别一個車牌數字,我們來簡單介紹兩個不經常被介紹的任務:

1、怎麼樣訓練一個模式識别系統?

2、怎麼樣來評估這樣的一個系統?

然而,這些任務通常比主要應用本身更重要。因為,如果我們不能正确的訓練模式識别系統,我們的系統就會失敗并且不能正确的工作。不同的模式需要不同類型的訓練和評估。我們需要在不同的環境,條件,帶有不同特征,來評估我們的系統,進而得到最好的效果。這兩個任務有時一起使用,因為不同的特征能産生不同的結果,這種情況我們會在評估部分看到。

車牌檢測

在這一步中,我們需要檢測在一個圖像幀中所有的車牌。為了做這個任務。我們分為兩個主要的步驟:分割和分割分類。特征步驟不在闡述,是因為我們用圖像部分作為一個特征矢量。

第一步(分割),我們應用不同的濾波器,形态學操作,輪廓算法,和确認擷取圖像的這些部分可能有一個車牌。

第二步(分類),我們采用支援向量機(SVM)分類出每個圖像部分——我們的特征。在建立主程式之前,我們訓練兩個不同的類别——有車牌和無車牌。我們采用前向_水準視覺的彩色圖像,寬度為800像素,從離車的2到4米處擷取的。這樣要求對確定正确的分割很重要。如果我們建立了一個多尺度圖像算法,我們能夠展示檢測。

在下面的圖像中,我們展示了車牌檢測所包含的所有處理:

1、Sobel濾波

2、門檻值操作

3、閉操作

4、填充區域的掩膜

5、把可能檢測到車牌标記為紅色(特征圖像)

6、SVM分類後,檢測到的車牌

使用opencv和神經網絡車牌識别

分割

分割是把一幅圖像分割成許多部分的過程。這個過程簡化圖像分析,使特征提取更容易。

車牌分割的一個重要特征是在車牌中的高數量的垂直邊緣(就是垂直邊緣比較多)(假定照片是從前面拍的,車牌沒有旋轉,并且沒有視覺上的扭曲。這個特征可以用來在分割的第一步(sobel濾波),來排除那些沒有垂直邊緣的區域。

在尋找垂直邊緣之前,我們需要把彩色圖像轉換為灰階圖像,因為彩色在我們的任務中沒有幫助,并且移除來之相機或者外界的噪聲。如果我們不應用去噪方法,我們将得到許多的垂直邊緣,将會産生檢測失敗。

[cpp]  view plain copy

使用opencv和神經網絡車牌識别
使用opencv和神經網絡車牌識别
  1. //convert image to gray  
  2. Mat img_gray;  
  3. cvtColor(input,img_gray,CV_BGR2GRAY);  
  4. blur(img_gray,img_gray,Size(5,5));  
使用opencv和神經網絡車牌識别
使用opencv和神經網絡車牌識别

為了尋找垂直邊緣,我們采用sobel濾波并且找到一階垂直方向導數。這個導數是個數學函數,允許我們找到圖像上的垂直邊緣。Opencv中Sobel函數的定義如下:

[cpp]  view plain copy

使用opencv和神經網絡車牌識别
使用opencv和神經網絡車牌識别
  1. void Sobel(InputArray src,OutputArray dst,int ddepth,int xorder,int yoder,int ksize=3,double scale=1,double delta=0,int borderType=BORDER_DEFAULT)  
使用opencv和神經網絡車牌識别
使用opencv和神經網絡車牌識别

這裡,ddepth是目的圖像的深度,xorder是x導數的次序(即x的order階導數),yorder為y導數的次序。ksize核的大小要麼是1,3,5要麼是7。scale用在計算導數值,是個可選項。delta是一個加到結果的可選項。bordertype是像素的插值方法。

在本程式中,我們使用xorder=1,yorder=0,ksize=3;

[cpp]  view plain copy

使用opencv和神經網絡車牌識别
使用opencv和神經網絡車牌識别
  1. //尋找垂直方向的線,車牌還很的垂直線密度  
  2. Mat img_sobel;  
  3. Sobel(img_gray,img_sobel,CV_8U,1,0,3,1,0);  
使用opencv和神經網絡車牌識别
使用opencv和神經網絡車牌識别

Sobel濾波後,我們應用一個門檻值濾波器來獲得一個二值圖像,門檻值的通過otsu方法獲得。Ostu算法需要一個8位圖像作為輸入,該方法自動的決定最佳的門檻值。

[cpp]  view plain copy

使用opencv和神經網絡車牌識别
使用opencv和神經網絡車牌識别
  1. Mat img_threshold;  
  2. threshold(img_sobel,img_threhold,0,255,CV_THRESH_OTSU+CV_THRESH_BINARY);  
使用opencv和神經網絡車牌識别
使用opencv和神經網絡車牌識别

為了在阈threshold函數中定義ostus方法,我們使用CV_THRESH_OTST值混合參數。則門檻值參數被忽略。

(小心:當CV_THRSH_OTST被定義,threshold函數會通過ostus算法傳回最優門檻值)

通過應用一個閉操作,我們能夠去掉每個垂直邊緣線的空白部分。并且連接配接有含有邊緣數量很多的所有區域。在這一步,我們得到可能的含有車牌的區域。

[cpp]  view plain copy

使用opencv和神經網絡車牌識别
使用opencv和神經網絡車牌識别
  1. Mat element = getStructuringElement(MORPH_RECT, Size(17, 3));  
使用opencv和神經網絡車牌識别
使用opencv和神經網絡車牌識别

在morphologEx函數中使用上述定義的結構元。

[cpp]  view plain copy

使用opencv和神經網絡車牌識别
使用opencv和神經網絡車牌識别
  1. morphologyEx(img_threshold, img_threshold, CV_MOP_CLOSE, element);  
使用opencv和神經網絡車牌識别
使用opencv和神經網絡車牌識别

應用完這些操作之後,我們的得到了可能含有車牌的區域,大部分的這些區域将沒有包含車牌。這些區域用連通部分分析(opencv 中文版319頁)或者使用findContours函數來分開。最後一個函數用不同的方法和結果來獲得一個二值圖像的輪廓。我們隻需要用任何分層關系和任何多邊形近似結果來獲得外輪廓。

[cpp]  view plain copy

使用opencv和神經網絡車牌識别
使用opencv和神經網絡車牌識别
  1. //Find contours of possibles plates  
  2. vector< vector< Point> > contours;  
  3. findContours(img_threshold,  
  4.             contours,           // a vector of contours  
  5.             CV_RETR_EXTERNAL,   // retrieve the external contours  
  6.             CV_CHAIN_APPROX_NONE); // all pixels of each contour  
使用opencv和神經網絡車牌識别
使用opencv和神經網絡車牌識别

為了檢測每個輪廓,提取輪廓的最小矩形邊界框。OpenCV采用minAreaRect函數來完成這個任務。這個函數傳回一個旋轉矩形類對象:RotatedRect。我們使用vector容器疊代器通路每一個輪廓,我們可以得到旋轉的矩行,在分類前做一些初步的确認。

[cpp]  view plain copy

使用opencv和神經網絡車牌識别
使用opencv和神經網絡車牌識别
  1. //Start to iterate to each contour found  
  2. vector<vector<Point> >::iterator itc= contours.begin();  
  3. vector<RotatedRect> rects;  
  4. //Remove patch that has  no inside limits of aspect ratio and area.   
  5. while (itc!=contours.end()) {  
  6. //Create bounding rect of object  
  7.   RotatedRect mr= minAreaRect(Mat(*itc));  
  8.   if( !verifySizes(mr)){  
  9.     itc= contours.erase(itc);  
  10.   }else{  
  11.   ++itc;  
  12.   rects.push_back(mr);  
  13.   }  
  14. }  
使用opencv和神經網絡車牌識别
使用opencv和神經網絡車牌識别

我們基于面積和寬高比,對于檢查到的區域做一下确認。如果寬高比大于為 520/110=4.727272 (車牌寬除以車牌高)(允許帶有 40% 的誤差)和邊界在 15 像素和 125 像素高的區域,我們才認為是一個車牌區域。這些值根據圖像的大小和相機的位置進行計算。

[cpp]  view plain copy

使用opencv和神經網絡車牌識别
使用opencv和神經網絡車牌識别
  1. bool DetectRegions::verifySizes(RotatedRect candidate ){  
  2.   float error=0.4;  
  3. //Spain car plate size: 52x11 aspect 4,7272  
  4.   const float aspect=4.7272;  
  5. //Set a min and max area. All other patches are discarded  
  6.   int min= 15*aspect*15; // minimum area  
  7.   int max= 125*aspect*125; // maximum area  
  8. //Get only patches that match to a respect ratio.  
  9.   float rmin= aspect-aspect*error;  
  10.   float rmax= aspect+aspect*error;  
  11.   int area= candidate.size.height * candidate.size.width;  
  12.   float r= (float)candidate.size.width / (float)candidate.size.height;  
  13.   if(r<1)  
  14. r= 1/r;  
  15.   if(( area < min || area > max ) || ( r < rmin || r > rmax )){  
  16.     return false;  
  17.   }else{  
  18.   return true;  
  19.   }  
  20. }  
使用opencv和神經網絡車牌識别
使用opencv和神經網絡車牌識别

我們利用車牌的白色背景屬性可以進一步改善。所有的車牌都有統一的背景顔色。我們可以使用漫水填充算法來擷取旋轉矩陣的精确修剪。

剪切車牌的第一步是在最後一個旋轉矩陣中心的附近得到一些種子,在寬度和高度中得到最小的車牌,用它來産生離中心近的種子。

我們想要選擇白色區域,我們需要一些種子,至少有一個種子接觸到白色區域。接着對每一個種子,我們使用floodFill函數來得到一個掩碼圖像,用來存儲新的最接近的修剪區域。

[cpp]  view plain copy

使用opencv和神經網絡車牌識别
使用opencv和神經網絡車牌識别
  1. for(int i=0; i< rects.size(); i++){  
  2. //For better rect cropping for each possible box  
  3. //Make floodfill algorithm because the plate has white background  
  4. //And then we can retrieve more clearly the contour box  
  5. circle(result, rects[i].center, 3, Scalar(0,255,0), -1);  
  6. //get the min size between width and height  
  7. float minSize=(rects[i].size.width < rects[i].size.height)?rects[i].  
  8. size.width:rects[i].size.height;  
  9. minSize=minSize-minSize*0.5;  
  10. //initialize rand and get 5 points around center for floodfill   
  11. algorithm  
  12. srand ( time(NULL) );  
  13. //Initialize floodfill parameters and variables  
  14. Mat mask;  
  15. mask.create(input.rows + 2, input.cols + 2, CV_8UC1);  
  16. mask= Scalar::all(0);  
  17. int loDiff = 30;  
  18. int upDiff = 30;  
  19. int connectivity = 4;  
  20. int newMaskVal = 255;  
  21. int NumSeeds = 10;  
  22. Rect ccomp;  
  23. int flags = connectivity + (newMaskVal << 8 ) + CV_FLOODFILL_FIXED_  
  24. RANGE + CV_FLOODFILL_MASK_ONLY;  
  25. for(int j=0; j<NumSeeds; j++){  
  26.   Point seed;  
  27.   seed.x=rects[i].center.x+rand()%(int)minSize-(minSize/2);  
  28.   seed.y=rects[i].center.y+rand()%(int)minSize-(minSize/2);  
  29.   circle(result, seed, 1, Scalar(0,255,255), -1);  
  30.   int area = floodFill(input, mask, seed, Scalar(255,0,0), &ccomp,     
  31. Scalar(loDiff, loDiff, loDiff), Scalar(upDiff, upDiff, upDiff),   
  32. flags);  
  33.   }  
使用opencv和神經網絡車牌識别
使用opencv和神經網絡車牌識别

漫水填充函數用顔色把連通區域填充到掩碼圖像,填充從種子開始。設定與相鄰像素或者種子像素之間差異的最下界和最上界(如果設定了 CV_FLOODFILL_FIXED_RANGE ,則填充的像素點都是與種子點進行比較。就是如果像素值為 x,seed-low<=x<=seed+up ,則該位置将被填充)

[cpp]  view plain copy

使用opencv和神經網絡車牌識别
使用opencv和神經網絡車牌識别
  1. int floodFill(InputOutputArray image, InputOutputArray mask, Point   
  2. seed, Scalar newVal, Rect* rect=0, Scalar loDiff=Scalar(), Scalar   
  3. upDiff=Scalar(), int flags=4 )  
使用opencv和神經網絡車牌識别
使用opencv和神經網絡車牌識别

參數 newVal 是填充到圖像的新值 . 參數 loDiff 和 upDiff 就是上邊描述的。

參數 flag 由以下組成:

1、低位:包含連通的值,預設4連通,或者8連通。連通決定了像素的哪個鄰居像素被考慮進來。

2、高位:可以為0,也可以是下邊值的組合:CV_FLOODFILL_FIXED_RANGE 和CV_FLOODFILL_MASK_ONLY.

CV_FLOODFILL_FIXED_RANGE用來設定目前像素和種子像素之間的差異。

CV_FLOODFILL_MASK_ONLY,将填充掩碼圖像,而不是圖像本身。

一旦我們得到了用來剪切的掩碼圖像,我們進而得到掩碼圖像點的最小外接矩形,再次檢查矩形大小。對于每一個掩碼,一個白色像素獲得位置用minAreaRect函數重新得到最相近的修剪區域。

//檢查新城的漫水填充掩碼是不是一個正确的塊。(因為使用車牌,車牌有邊界,漫水填充不會超過車牌的邊界,而對于其他區域(檢查出來的矩形)漫水填充會占據很多區域,形成的矩形也很大,再進入verifySizes函數時,可能就會被丢棄,得到更可能是車牌的區域)

//得到所有的點為最小旋轉矩形

[cpp]  view plain copy

使用opencv和神經網絡車牌識别
使用opencv和神經網絡車牌識别
  1. //Check new floodfill mask match for a correct patch.  
  2. //Get all points detected for minimal rotated Rect  
  3. vector<Point> pointsInterest;  
  4. Mat_<uchar>::iterator itMask= mask.begin<uchar>();  
  5. Mat_<uchar>::iterator end= mask.end<uchar>();  
  6. for( ; itMask!=end; ++itMask)  
  7.   if(*itMask==255)  
  8.   pointsInterest.push_back(itMask.pos());  
  9.   RotatedRect minRect = minAreaRect(pointsInterest);  
  10.   if(verifySizes(minRect)){  
  11. …  
使用opencv和神經網絡車牌識别
使用opencv和神經網絡車牌識别

既然分割過程已經完成并且我們得到了有效的區域。我們能夠修剪每一個檢測到的區域,去掉那些可能存在的旋轉,修剪圖像區域,重新設定圖像的大小,并且均衡化修剪過的區域。

首先,我們需要通過函數 getRotationMatrix2D 來獲得轉換矩陣,用來去掉那些檢測區域的旋轉。我們需要注意高度,因為 RectatedRect 類能夠被傳回并且旋轉了 90 度。是以我們必須檢查矩形的寬高比,如果它小于 1 ,則進行 90 度的旋轉。

[cpp]  view plain copy

使用opencv和神經網絡車牌識别
使用opencv和神經網絡車牌識别
  1. //Get rotation matrix  
  2. float r= (float)minRect.size.width / (float)minRect.size.height;  
  3. float angle=minRect.angle;  
  4. if(r<1)  
  5.   angle=90+angle;  
  6.   Mat rotmat= getRotationMatrix2D(minRect.center, angle,1);  
使用opencv和神經網絡車牌識别
使用opencv和神經網絡車牌識别

用轉換矩陣,我們現在能通過仿射變換旋轉輸入圖像了(幾何中的仿射變換是平行線到平行線(可以參考opencv中文版 186頁,仿射變換和透視變換的差別)。在warpAffine函數中,我們設定輸入輸出圖像,轉換矩陣,輸出圖像的大小(在我們的程式中,我們使用和輸入圖像一樣的大小),插值方法。如果我們需要的話,我們可以定義邊界方法和邊界值。

[cpp]  view plain copy

使用opencv和神經網絡車牌識别
使用opencv和神經網絡車牌識别
  1. //Create and rotate image  
  2. Mat img_rotated;  
  3. warpAffine(input, img_rotated, rotmat, input.size(), CV_INTER_CUBIC);  
使用opencv和神經網絡車牌識别
使用opencv和神經網絡車牌識别

我們旋轉圖像之後,我們用getRectSubPix函數來修剪圖像,該函數修剪拷貝給定長度和寬度,以及中心點的圖像部分。如果圖像旋轉了,我們需要使用C++swap函數來改變寬和高的大小。

[cpp]  view plain copy

使用opencv和神經網絡車牌識别
使用opencv和神經網絡車牌識别
  1. //Crop image  
  2. Size rect_size=minRect.size;  
  3. if(r < 1)  
  4. swap(rect_size.width, rect_size.height);  
  5. Mat img_crop;  
  6. getRectSubPix(img_rotated, rect_size, minRect.center, img_crop);  
使用opencv和神經網絡車牌識别
使用opencv和神經網絡車牌識别

修剪的圖像不能很好的在訓練和分類中使用,因為他們沒有相同的大小。并且,每個圖像包含不同的光照條件,增加了他們之間的差别。為了解決這個問題。我們把所有的圖像調整為統一大的大小,采用直方圖均衡化。

[cpp]  view plain copy

使用opencv和神經網絡車牌識别
使用opencv和神經網絡車牌識别
  1. Mat resultResized;  
  2. resultResized.create(33,144, CV_8UC3);  
  3. resize(img_crop, resultResized, resultResized.size(), 0, 0, INTER_  
  4. CUBIC);  
  5. //Equalize cropped image  
  6. Mat grayResult;  
  7. cvtColor(resultResized, grayResult, CV_BGR2GRAY);  
  8. blur(grayResult, grayResult, Size(3,3));  
  9. equalizeHist(grayResult, grayResult);  
使用opencv和神經網絡車牌識别
使用opencv和神經網絡車牌識别

對于每一個檢測到區域,我們存儲修剪過的圖像。把他們的位置存儲在vector中.

[cpp]  view plain copy

使用opencv和神經網絡車牌識别
使用opencv和神經網絡車牌識别
  1. output.push_back(Plate(grayResult,minRect.boundingRect()));  
使用opencv和神經網絡車牌識别
使用opencv和神經網絡車牌識别

分類

我們預處理和分割圖像的所有可能部分之後,我們現在需要判别每一個分割是不是一個車牌。為這樣做,我們使用SVM算法。

支援向量機是一個模式識别算法,它是監督學習算法的一份子,最初是建立是為了二值分類的。有監督的學習是一種機器學習算法,它通過标簽資料的使用進行學習。我們需要一些帶有标簽的資料來訓練這個算法。每一個資料集需要有一個類别。

SVM建立一個或多個超平面,用來區分每類資料。

一個典型的例子是2維點集,它定義了兩個類。SVM尋找最優線來區分每個類。

使用opencv和神經網絡車牌識别

在任一分類之前的第一個任務是訓練我們的分類器。這項工作的完成優先于開始主要的應用程式。它被稱為離線訓練。這不是一個簡單的工作,因為它需要充足的資料來訓練這個系統。但是大的資料集并不總是暗示最好的結果。在我們的例子中,我們沒有充足的資料,是因為沒有一個公共的車牌資料的事實。正因為如此,我們需要拍數百張車照,然後預處理和分割所有的照片。

我們用大小為144*33的75張車牌和35非車牌來訓練我們的系統。我們在下面的圖中能看到資料的一個樣本。這并不是個大的資料集,對我們的需求來說,它已經可以充足的得到一個體面的結果。在實際的應用中,我們需要訓練更多的資料。

使用opencv和神經網絡車牌識别

很容易了解機器學習是怎樣工作的,我們使用分類器算法的圖像像素特征(想一下,有很多更好的方法和特征來訓練一個SVM,比如主成分分析,傅裡葉變換,紋理分析,等等)

我們需要通過DectectRegions類建立圖像和訓練我們的系統。把變量savingRegions設定為真用來儲存圖像。我們可以通過segementAllFiles.sh bash腳本檔案把檔案夾下的所有圖像檔案上重複這個過程。該檔案可以從書的源代碼中獲得。

為了使這個更簡單,我們存儲了已經處理好和準備好是所有圖像的資料,放在了xml檔案裡,之間使用SVM函數調用。trainSVM.cpp程式用一些檔案夾下的數張圖檔檔案建立的xml檔案。

小心:為機器學習的Opencv算法訓練的資料存儲在一個N*M的矩陣中,N表示樣本數,M表示特征數。每個資料集作為一行存儲在訓練矩陣中(就是N*M個像素點,展開成一行,作為訓練矩陣的N*M個特征。詳細可以參考trainSVM.cpp代碼中)

類别存儲在另外一個大小為N*1的矩陣中。每一個類通過一個浮點數來識别。(這裡代碼中是不是int?)

opencv有個簡單的方式來管理xml或者Json個數的資料檔案,即FileStorage類。這個類使我們存儲和讀取opencv變量和結構體或者我們傳統的變量。使用這個函數,我們嫩而過讀取訓練的資料矩陣和訓練的類别,并且把他們儲存在SVM_TrainingData 和SVM_Classes中:

[cpp]  view plain copy

使用opencv和神經網絡車牌識别
使用opencv和神經網絡車牌識别
  1. FileStorage fs;  
  2. fs.open(”SVM.xml”, FileStorage::READ);  
  3. Mat SVM_TrainingData;  
  4. Mat SVM_Classes;  
  5. fs[”TrainingData”] >> SVM_TrainingData;  
  6. fs[”classes”] >> SVM_Classes;  
使用opencv和神經網絡車牌識别
使用opencv和神經網絡車牌識别

現在我們需要設定SVM參數,定義最基本的參數來供SVM算法的使用。我們使用CvSVMParam結構來定義它。它是一個映射,用來把訓練的資料提升到一個線性可分的資料集合。這種映射包括資料維數的增加,通過一個核函數可以有效的得到。我們在這裡選用CvSVM::LINEAR類型,這就是意味着沒有映射。

[cpp]  view plain copy

使用opencv和神經網絡車牌識别
使用opencv和神經網絡車牌識别
  1. //Set SVM params  
  2. CvSVMParams SVM_params;  
  3. SVM_params.kernel_type = CvSVM::LINEAR;  
使用opencv和神經網絡車牌識别
使用opencv和神經網絡車牌識别

這時我們建立和訓練我們的分離器。Opencv為支援向量機算法定義了CvSVM類。我們用訓練的資料來,類别和參數資料來初始化它。

[cpp]  view plain copy

使用opencv和神經網絡車牌識别
使用opencv和神經網絡車牌識别
  1. CvSVM svmClassifier(SVM_TrainingData, SVM_Classes, Mat(), Mat(), SVM_  
  2. params);  
使用opencv和神經網絡車牌識别
使用opencv和神經網絡車牌識别

我們的分離器準備好了,我們可以使用SVM類的predict函數來預測一個可能的修剪圖像。這個函數傳回類别i。在我們的執行個體中,我們标記每一個車牌類别為1,非車牌類别标記為0。對于每個檢測到的區域,我們使用SVM來分出它是車牌還是非車牌,并且隻儲存正确的響應。下面的代碼是主程式的一部分,成為線上處理:

[cpp]  view plain copy

使用opencv和神經網絡車牌識别
使用opencv和神經網絡車牌識别
  1. vector<Plate> plates;  
  2. for(int i=0; i< possible_regions.size(); i++)  
  3. {  
  4.   Mat img=possible_regions[i].plateImg;  
  5.   Mat p= img.reshape(1, 1);//convert img to 1 row m features  
  6.   p.convertTo(p, CV_32FC1);  
  7.   int response = (int)svmClassifier.predict( p );  
  8.   if(response==1)  
  9.     plates.push_back(possible_regions[i]);  
  10. }  
使用opencv和神經網絡車牌識别
使用opencv和神經網絡車牌識别

車牌識别

車牌識别目标的第二步就是用光符字元識别來擷取車牌上的字元。對于每個檢測到的車牌,我開始分割車牌得到每個字元,并且使用人工神經網絡機器學習算法來識别字元。同時在這一部分我們也将學習怎麼樣評估一個分類算法。

OCR分割

首先,我們獲得車牌圖像的部分作為OCR分割函數是輸入(已經均衡化直方圖的圖像)。我們應用一個門檻值濾波器濾波,并把濾波後的門檻值圖像作為尋找輪廓算法的輸入。我們可以通過下圖看到過程:

使用opencv和神經網絡車牌識别

分割處理的代碼如下;

[cpp]  view plain copy

使用opencv和神經網絡車牌識别
使用opencv和神經網絡車牌識别
  1. Mat img_threshold;  
  2. threshold(input, img_threshold, 60, 255, CV_THRESH_BINARY_INV);  
  3. if(DEBUG)  
  4.   imshow(”Threshold plate”, img_threshold);  
  5. Mat img_contours;  
  6. img_threshold.copyTo(img_contours);  
  7. //Find contours of possibles characters  
  8. vector< vector< Point> > contours;  
  9. findContours(img_contours,  
  10.             contours,            // a vector of contours  
  11.             CV_RETR_EXTERNAL,    // retrieve the external contours  
  12.             CV_CHAIN_APPROX_NONE); // all pixels of each contour  
使用opencv和神經網絡車牌識别
使用opencv和神經網絡車牌識别

我們使用CV_THRESH_BINARY參數通過把白色值變為黑色,黑色值變為白色來實作門檻值輸出的反轉。因為我們需要擷取字元的輪廓,而輪廓的算法尋找的是白色像素。

對于每一個檢測到的輪廓,我們核實一下大小,去除那些規格太小的或者寬高比不正确的區域。字元是45/77的寬高比。我們允許用于選擇或者扭曲帶來的百分之35的誤差。如果一個區域面積高于80%(就是像素大于0的超過80%),則我們認為這個區域是一個黑色塊.(因為白變黑,黑變白處理了),不是字元。為了計算面積我們使用countNonZero函數來計算高于0值的像素值。

[cpp]  view plain copy

使用opencv和神經網絡車牌識别
使用opencv和神經網絡車牌識别
  1. bool OCR::verifySizes(Mat r)  
  2. {  
  3.   //Char sizes 45x77  
  4.   float aspect=45.0f/77.0f;  
  5.   float charAspect= (float)r.cols/(float)r.rows;  
  6.   float error=0.35;  
  7.   float minHeight=15;  
  8.   float maxHeight=28;  
  9.   //We have a different aspect ratio for number 1, and it can be    
  10.   //~0.2  
  11.   float minAspect=0.2;  
  12.   float maxAspect=aspect+aspect*error;  
  13.   //area of pixels  
  14.   float area=countNonZero(r);  
  15.   //bb area  
  16.   float bbArea=r.cols*r.rows;  
  17.   //% of pixel in area  
  18.   float percPixels=area/bbArea;  
  19.   if(percPixels < 0.8 && charAspect > minAspect && charAspect <  
  20.   maxAspect && r.rows >= minHeight && r.rows < maxHeight)  
  21.   return true;  
  22.   else  
  23.   return false;  
  24. }  
使用opencv和神經網絡車牌識别
使用opencv和神經網絡車牌識别

如果分割字元被證明了,我們必須對它進行預處理,設定同樣的大小和所有字元的位置,把它存儲在一個附加的charsegment類的對象中。這個類儲存了字元的圖像和位置,我們需要給字元排序,因為尋找輪廓的算法傳回的輪廓并不是規定的順序。

特征提取

下一步是對每個分割的字元進行特征提取用神經網絡算法訓練和分類。不像車牌檢測特征提取的步驟那樣使用的是SVM,我們不用圖像的所有像素。我們使用一個更加通用的特征在光符字元識别中,其中包括水準和垂直累加直方圖,一個低分辨率樣本。我們在下圖可以看到更加形象的特征,每個圖像有一個5*5的低分辨率和直方圖的累加。

使用opencv和神經網絡車牌識别

對于每一個字元,我們用countNonZero計算每一行或者每一列的非零的個數,把他們存儲在一個新的資料矩陣mhist中。我們使用minMaxLoc函數找到資料矩陣的最大值,并用這個值來歸一化,即采用convertTo函數将mhist的所有元素都除以這個最大的值。我們建立一個ProjecteddHistotram函數來建立累積直方圖,這個函數帶有兩個輸入的參數,一個是一個二值圖像,一個是我們需要的直方圖的類型即水準或者垂直。

[cpp]  view plain copy

使用opencv和神經網絡車牌識别
使用opencv和神經網絡車牌識别
  1. Mat OCR::ProjectedHistogram(Mat img, int t)  
  2. {  
  3.   int sz=(t)?img.rows:img.cols;  
  4.   Mat mhist=Mat::zeros(1,sz,CV_32F);  
  5.   for(int j=0; j<sz; j++){  
  6.     Mat data=(t)?img.row(j):img.col(j);  
  7.     mhist.at<float>(j)=countNonZero(data);  
  8.   }  
  9. //Normalize histogram  
  10. double min, max;  
  11. minMaxLoc(mhist, &min, &max);  
  12. if(max>0)  
  13. mhist.convertTo(mhist,-1 , 1.0f/max, 0);  
  14.   return mhist;  
  15. }  
使用opencv和神經網絡車牌識别
使用opencv和神經網絡車牌識别

其他特征使用一個低分辨率的樣本圖像。替代使用整個字元圖像,我們建立一個低分辨率字元,例如5*5。我們用5*5,10*10,20*20大小的字元來訓練我們的系統(本程式中Charsize=20)。然後評估哪一個傳回的是最好的結果,然後我們在系統中使用它。一旦我們擁有了特征,我們建立一個M列的矩陣,矩陣的每一行的每一列都是特征值。

[cpp]  view plain copy

使用opencv和神經網絡車牌識别
使用opencv和神經網絡車牌識别
  1. Mat OCR::features(Mat in, int sizeData)  
  2. {  
  3. //Histogram features  
  4.   Mat vhist=ProjectedHistogram(in,VERTICAL);  
  5.   Mat hhist=ProjectedHistogram(in,HORIZONTAL);  
  6. //Low data feature  
  7.   Mat lowData;  
  8.   resize(in, lowData, Size(sizeData, sizeData) );  
  9.   int numCols=vhist.cols + hhist.cols + lowData.cols *   
  10.   lowData.cols;  
  11.   Mat out=Mat::zeros(1,numCols,CV_32F);  
  12.   //Assign values to feature  
  13.   int j=0;  
  14.   for(int i=0; i<vhist.cols; i++)  
  15.   {  
  16.     out.at<float>(j)=vhist.at<float>(i);   
  17.     j++;  
  18.   }  
  19.   for(int i=0; i<hhist.cols; i++)  
  20.   {  
  21.   out.at<float>(j)=hhist.at<float>(i);  
  22.   j++;  
  23.   }  
  24.   for(int x=0; x<lowData.cols; x++)  
  25.   {  
  26.     for(int y=0; y<lowData.rows; y++)  
  27.     {  
  28.       out.at<float>(j)=(float)lowData.at<unsigned char>(x,y);  
  29.       j++;  
  30.     }  
  31.   }  
  32.   return out;  
  33. }  
使用opencv和神經網絡車牌識别
使用opencv和神經網絡車牌識别

OCR分類

在分類這一部,我們使用人工神經網絡機器學習算法。更具體一點,多層感覺器(MLP),一個廣泛使用的人工神經網絡算法。

MLP神經網絡有一個輸入層,輸出層和一個或多個隐層。每一層有一個活多個神經元連接配接着前向和後向層。

下面的例子表示一個3層感覺器(它是一個二值分類器,它映射輸入的一個實值向量,輸出單一的二值),它帶有三個輸入,兩個輸出和一個含有5個神經元的隐層。

使用opencv和神經網絡車牌識别

MLP中所有的神經元,都相似,每個神經元有幾個輸出(前向連接配接神經元)和幾個輸出連接配接着相同的值(後向連接配接神經元)。每個神經元用帶有權重的輸入的和加上一個偏移量再經過一個選擇的激活函數轉換後得到輸出結果。

使用opencv和神經網絡車牌識别

有三個廣泛使用的激活函數:Identiy,S函數,高斯函數。最常用的預設的激活函數是S函數。其中alpha和beta設定為1:

使用opencv和神經網絡車牌識别

一個訓練人工神經網絡的輸入是一個特征矢量。它傳輸值到隐層。用權重和激活函數來計算結果。它進一步的把輸出結果往下傳輸直到到達含有一定數量的神經元類别時。

每一層的權重,突觸,和神經元通過訓練神經網絡算法來計算和學習。為了訓練我們的分類器。在SVM訓練時,我們建立兩個資料矩陣,但是訓練的标簽有點不同。替代N*1的矩陣(這裡N代表訓練資料的行數,1是矩陣的列),那裡我們使用了數字作為表示符。我們必須建立一個N*M大小的矩陣,這裡的N是訓練/樣本的資料,M是類别(我們的示例中是10個數字和20個字母)。如果資料行i歸宿與類别j,則我們将位置(i,j)的設定為1.

使用opencv和神經網絡車牌識别

我們建立OCR::train函數來建立我們所需要的矩陣并且用訓練資料矩陣,類别矩陣和隐層神經元的數目來訓練我們的系統。訓練資料從xml檔案導入,就像我們為SVM訓練做的那樣。

我們必須定義每一層的神經元的數目來初始化ANN類。在我們的例子中,我們隻隻用了一個隐層,是以我們定義一個1行3列的矩陣。第一類位置是特征的數目,第二列位置是隐層所含隐藏的神經元的數目,第三列的位置是類的數目。

Opencv為ANN定義了一個CvANN_MLP類。通過定義的層的數目和原子數,激活函數和alpha和beta參數用建立函數來初始化類。

[cpp]  view plain copy

使用opencv和神經網絡車牌識别
使用opencv和神經網絡車牌識别
  1. void OCR::train(Mat TrainData, Mat classes, int nlayers)  
  2. {  
  3.   Mat layerSizes(1,3,CV_32SC1);  
  4.   layerSizes.at<int>(0)= TrainData.cols;  
  5.   layerSizes.at<int>(1)= nlayers;  
  6.   layerSizes.at<int>(2)= numCharacters;  
  7.   ann.create(layerSizes, CvANN_MLP::SIGMOID_SYM, 1, 1); //ann is    
  8.   global class variable  
  9.   //Prepare trainClasses  
  10.   //Create a mat with n trained data by m classes  
  11.   Mat trainClasses;  
  12.   trainClasses.create( TrainData.rows, numCharacters, CV_32FC1 );  
  13.   for( int i = 0; i <  trainClasses.rows; i++ )  
  14.   {  
  15.     for( int k = 0; k < trainClasses.cols; k++ )  
  16.     {  
  17.     //If class of data i is same than a k class  
  18.     if( k == classes.at<int>(i) )  
  19.     trainClasses.at<float>(i,k) = 1;  
  20.     else  
  21.     trainClasses.at<float>(i,k) = 0;  
  22.     }  
  23.   }  
  24.   Mat weights( 1, TrainData.rows, CV_32FC1, Scalar::all(1) );  
  25.   //Learn classifier  
  26.   ann.train( TrainData, trainClasses, weights );  
  27.   trained=true;  
  28. }  
使用opencv和神經網絡車牌識别
使用opencv和神經網絡車牌識别

訓練之後,我們能使用OCR::classify函數對任何分割的特征車牌進行分類。

[cpp]  view plain copy

使用opencv和神經網絡車牌識别
使用opencv和神經網絡車牌識别
  1. int OCR::classify(Mat f)  
  2. {  
  3.   int result=-1;  
  4.   Mat output(1, numCharacters, CV_32FC1);  
  5.   ann.predict(f, output);  
  6.   Point maxLoc;  
  7.   double maxVal;  
  8.   minMaxLoc(output, 0, &maxVal, 0, &maxLoc);  
  9.   //We need to know where in output is the max val, the x (cols) is   
  10.   //the class.  
  11.   return maxLoc.x;  
  12. }  
使用opencv和神經網絡車牌識别
使用opencv和神經網絡車牌識别

CvANN_MLP類使用predict函數來為一個特征矢量分類。不想SVM的分類函數,ANN的predict函數傳回的是一個矢量,其大小和類的數目相同,帶有屬于每一個類的輸入特征的幾率。(計算矢量中的每一個值,相當于該矢量屬于機率的機率,大題可以這樣認為)

為了得到更好的效果,我們使用minMaxLoc函數來獲得響應的最大數和最小數(這裡隻用了最大數),以及在矩陣中的位置。我們字元的類通過x位置的一個高值(值大的)來指定。

使用opencv和神經網絡車牌識别

為了完成每一個車牌的檢測,我們對字元排好序,用Plate類的str()函數來傳回一個string。并且我們可以在原圖上畫出來。

[cpp]  view plain copy

使用opencv和神經網絡車牌識别
使用opencv和神經網絡車牌識别
  1. string licensePlate=plate.str();  
  2. rectangle(input_image, plate.position, Scalar(0,0,200));  
  3. putText(input_image, licensePlate, Point(plate.position.x, plate.  
  4. position.y), CV_FONT_HERSHEY_SIMPLEX, 1, Scalar(0,0,200),2);  
使用opencv和神經網絡車牌識别
使用opencv和神經網絡車牌識别

評估

我們的工程完成了,但是當我們訓練一個機器學習算法比如Ocr,例如,我們需要指定最好的特征和參數來使用,怎樣在我們的系統中正确的分類,識别和檢測錯誤。

我們需要用不同的情況和參數來評估我們的系統,評價錯誤的産生,和得到最好的參數來使這些錯誤最小化。

在這一章,我們評估OCR任務使用下面的變量:低分辨率圖像的大小和隐層含有的神經元的數目。

我們已經建立了evalOCR.cpp程式,在那裡我們将使用通過trainOCR.cpp産生的XML訓練資料檔案。OCR.xml檔案包含5*5,10*10,15*15,20*20下采樣圖像特征構成的訓練資料矩陣。

[cpp]  view plain copy

使用opencv和神經網絡車牌識别
使用opencv和神經網絡車牌識别
  1. Mat classes;  
  2. Mat trainingData;  
  3. //Read file storage.  
  4. FileStorage fs;  
  5. fs.open(”OCR.xml”, FileStorage::READ);  
  6. fs[data] >> trainingData;  
  7. fs[”classes”] >> classes;  
使用opencv和神經網絡車牌識别
使用opencv和神經網絡車牌識别

評估應用擷取每一個下采樣矩陣特征,擷取100個随機行用來訓練,和其他用來測試ANN算法的行一樣并且檢查錯誤。

在訓練系統之前,我們測試每一個随機樣本,檢查一下響應是不是正确。如果響應不正确,我們增加錯誤計算變量,接着除以樣本的數量用來評估。這表示用随機資料訓練的錯誤(該值在0到1之間)

[cpp]  view plain copy

使用opencv和神經網絡車牌識别
使用opencv和神經網絡車牌識别
  1. float test(Mat samples, Mat classes)  
  2. {  
  3.   float errors=0;  
  4.   for(int i=0; i<samples.rows; i++)  
  5.   {  
  6.     int result= ocr.classify(samples.row(i));  
  7.     if(result!= classes.at<int>(i))  
  8.     errors++;  
  9.   }  
  10.   return errors/samples.rows;  
  11. }  
使用opencv和神經網絡車牌識别
使用opencv和神經網絡車牌識别

這個程式傳回每一個樣本集的錯誤率。對于一個好的評估,我們需要用不同的随機訓練行資料來訓練我們的程式。這會産生不同的測試錯誤值。我們可以把這樣錯誤加起來,然後求平均值。為了完成這個任務。我們建立了下面的bash unix腳本來自動執行:

[html]  view plain copy

使用opencv和神經網絡車牌識别
使用opencv和神經網絡車牌識别
  1. #!/bin/bash  
  2. echo ”#ITS \t 5 \t 10 \t 15 \t 20” > data.txt  
  3. folder=$(pwd)  
  4. for numNeurons in 10 20 30 40 50 60 70 80 90 100 120 150 200 500  
  5. do  
  6.   s5=0;  
  7.   s10=0;  
  8.   s15=0;  
  9.   s20=0;  
  10.   for j  in {1..100}  
  11.   do  
  12.     echo numNeurons&nbsp;j  
  13.     a= ( folder/build/evalOCR $numNeurons TrainingDataF5)  
  14.     s5=(echo&nbsp;"</span><span class="attribute" style="margin:0px; padding:0px; border:currentColor; color:red; background-color:inherit">scale</span><span style="margin:0px; padding:0px; border:currentColor; background-color:inherit">=</span><span class="attribute-value" style="margin:0px; padding:0px; border:currentColor; color:blue; background-color:inherit">4</span><span style="margin:0px; padding:0px; border:currentColor; background-color:inherit">;&nbsp;scale=4; s5+$a” | bc -q 2>/dev/null)  
  15.     a= ( folder/build/evalOCR $numNeurons TrainingDataF10)  
  16.     s10=(echo&nbsp;"</span><span class="attribute" style="margin:0px; padding:0px; border:currentColor; color:red; background-color:inherit">scale</span><span style="margin:0px; padding:0px; border:currentColor; background-color:inherit">=</span><span class="attribute-value" style="margin:0px; padding:0px; border:currentColor; color:blue; background-color:inherit">4</span><span style="margin:0px; padding:0px; border:currentColor; background-color:inherit">;&nbsp;scale=4; s10+$a” | bc -q 2>/dev/null)  
  17.     a= ( folder/build/evalOCR $numNeurons TrainingDataF15)  
  18.     s15=(echo&nbsp;"</span><span class="attribute" style="margin:0px; padding:0px; border:currentColor; color:red; background-color:inherit">scale</span><span style="margin:0px; padding:0px; border:currentColor; background-color:inherit">=</span><span class="attribute-value" style="margin:0px; padding:0px; border:currentColor; color:blue; background-color:inherit">4</span><span style="margin:0px; padding:0px; border:currentColor; background-color:inherit">;&nbsp;scale=4; s15+$a” | bc -q 2>/dev/null)  
  19.     a= ( folder/build/evalOCR $numNeurons TrainingDataF20)  
  20.     s20=(echo&nbsp;"</span><span class="attribute" style="margin:0px; padding:0px; border:currentColor; color:red; background-color:inherit">scale</span><span style="margin:0px; padding:0px; border:currentColor; background-color:inherit">=</span><span class="attribute-value" style="margin:0px; padding:0px; border:currentColor; color:blue; background-color:inherit">4</span><span style="margin:0px; padding:0px; border:currentColor; background-color:inherit">;&nbsp;scale=4; s20+$a” | bc -q 2>/dev/null)  
  21.   done  
  22.   echo ”i&nbsp;\t&nbsp;s5 \t s10&nbsp;\t&nbsp;s15 \t $s20”  
  23.   echo ”i&nbsp;\t&nbsp;s5 \t s10&nbsp;\t&nbsp;s15 \t $s20” >> data.txt  
  24. done  
使用opencv和神經網絡車牌識别
使用opencv和神經網絡車牌識别

這個腳本儲存了一個data.text檔案,它包含了每個大學的所有結果和隐層神經元的數目。這個可以使用gnuplot畫出來。我們可以在下面的圖中看到結果:

使用opencv和神經網絡車牌識别

我們可以看到最低的錯誤在8%以下,它是使用隐層包含20個神經元和從規格是10*10d的圖像塊中提取的特征。

總結

在這一章,我們學習了自動車牌識别項目是怎麼工作的,并且它有兩個重要的步驟:

車牌定位和車牌識别。

在第一步中,我們學習了怎麼樣分割圖像來找到存在車牌的部分。和怎麼樣用一個簡單的啟發法和支援向量機來對有車牌和無車牌進行二值分類。

在第二步我們學習了怎麼樣通過輪廓算法來分割圖像。從每個字元提取特征向量,并且使用人工神經網絡把對特征進行分類,分到一個字元類中。

我們也學習了怎麼樣用訓練随機樣本來評估一個機器算法。并且使用不同的參數和特征對它進行評估。

[cpp]  view plain copy

使用opencv和神經網絡車牌識别
使用opencv和神經網絡車牌識别
  1. <pre code_snippet_id=“138968” snippet_file_name=“blog_20140102_4_5303797”></pre>  
  2. <pre></pre>  
  3. <pre></pre>  
  4. <pre></pre>  
  5. <pre></pre>  
  6. <pre></pre>  
  7. <pre></pre>  
  8. <pre></pre>  
  9. <pre></pre>  
  10. <pre></pre>  
  11. <pre></pre>  
  12. <pre></pre>  
  13. <pre></pre>  
  14. <pre></pre>  
  15. <pre></pre>  
  16. <pre></pre>  
  17. <pre></pre>  
  18. <pre></pre>  
使用opencv和神經網絡車牌識别
使用opencv和神經網絡車牌識别

繼續閱讀