這篇文章是一個系列中的第三篇。前兩篇的地址貼下:介紹、詳解1。我撰寫這系列文章的目的是:1、普及車牌識別中相關的技術與知識點;2、幫助開發者了解EasyPR的實現細節;3、增進溝通。
EasyPR的項目地址在這:GitHub。要想運行EasyPR的程序,首先必須配置好openCV,具體可以參照這篇文章。
在前兩篇文章中,我們已經初步了解了EasyPR的大概內容,在本篇內容中我們開始深入EasyRP的程序細節。了解EasyPR是如何一步一步實現一個車牌的識別過程的。根據EasyPR的結構,我們把它分為六個部分,前三個部分統稱為“Plate Detect”過程。主要目的是在一副圖片中發現僅包含車牌的圖塊,以此提高整體識別的准確率與速度。這個過程非常重要,如果這步失敗了,后面的字符識別過程就別想了。而“Plate Detect”過程中的三個部分又分別稱之為“Plate Locate” ,“SVM train”,“Plate judge”,其中最重要的部分是第一步“Plate Locate”過程。本篇文章中就是主要介紹“Plate Locate”過程,並且回答以下三個問題:
1.此過程的作用是什么,為什么重要?
2.此過程是如何實現車牌定位這個功能的?
3.此過程中的細節是什么,如何進行調優?
1.“Plate Locate”的作用與重要性
在說明“Plate Locate”的作用與重要性之前,請看下面這兩幅圖片。
圖1 兩幅包含車牌的不同形式圖片
左邊的圖片是作者訓練的圖片(作者大部分的訓練與測試都是基於此類交通抓拍圖片),右邊的圖片則是在百度圖片中“車牌”獲得(這個圖片也可以稱之為生活照片)。右邊圖片的問題是一個網友評論時問的。他說EasyPR在處理百度圖片時的識別率不高。確實如此,由於工業與生活應用目的不同,拍攝的車牌的大小,角度,色澤,清晰度不一樣。而對圖像處理技術而言,一些算法對於圖像的形式以及結構都有一定的要求或者假設。因此在一個場景下適應的算法並不適用其他場景。目前EasyPR所有的功能都是基於交通抓拍場景的圖片制作的,因此也就導致了其無法處理生活場景中這些車牌照片。
那么是否可以用一致的“Plate Locate”過程中去處理它?答案是也許可以,但是很難,而且最后即便處理成功,效率也許也不盡如人意。我的推薦是:對於不同的場景要做不同的適配。盡管“Plate Locate”過程無法處理生活照片的定位,但是在后面的字符識別過程中兩者是通用的。可以對EasyPR的“Plate Locate”做改造,同時仍然使用整體架構,這樣或許可以處理。
有一點事實值得了解到是,在生產環境中,你所面對的圖片形式是固定的,例如左邊的圖片。你可以根據特定的圖片形式來調優你的車牌程序,使你的程序對這類圖片足夠健壯,效率也夠高。在上線以后,也有很好的效果。但當圖片形式調整時,就必須要調整你的算法了。在“Plate Locate”過程中,有一些參數可以調整。如果通過調整這些參數就可以使程序良好工作,那最好不過。當這些參數也不能夠滿足需求時,就需要完全修改EasyPR的實現代碼,因此需要開發者了解EasyPR是如何實現plateLocate這一過程的。
在EasyPR中,“Plate Locate”過程被封裝成了一個“CPlateLocate”類,通過“plate_locate.h”聲明,在“plate_locate.cpp”中實現。
CPlateLocate包含三個方法以及數個變量。方法提供了車牌定位的主要功能,變量則提供了可定制的參數,有些參數對於車牌定位的效果有非常明顯的影響,例如高斯模糊半徑、Sobel算子的水平與垂直方向權值、閉操作的矩形寬度。CPlateLocate類的聲明如下:
class CPlateLocate { public: CPlateLocate(); //! 車牌定位 int plateLocate(Mat, vector<Mat>& ); //! 車牌的尺寸驗證 bool verifySizes(RotatedRect mr); //! 結果車牌顯示 Mat showResultMat(Mat src, Size rect_size, Point2f center); //! 設置與讀取變量 //... protected: //! 高斯模糊所用變量 int m_GaussianBlurSize; //! 連接操作所用變量 int m_MorphSizeWidth; int m_MorphSizeHeight; //! verifySize所用變量 float m_error; float m_aspect; int m_verifyMin; int m_verifyMax; //! 角度判斷所用變量 int m_angle; //! 是否開啟調試模式,0關閉,非0開啟 int m_debug; };
注意,所有EasyPR中的類都聲明在命名空間easypr內,這里沒有列出。CPlateLocate中最核心的方法是plateLocate方法。它的聲明如下:
//! 車牌定位 int plateLocate(Mat, vector<Mat>& );
方法有兩個參數,第一個參數代表輸入的源圖像,第二個參數是輸出數組,代表所有檢索到的車牌圖塊。返回值為int型,0代表成功,其他代表失敗。plateLocate內部是如何實現的,讓我們再深入下看看。
2.“Plate Locate”的實現過程
plateLocate過程基本參考了taotao1233的博客的處理流程,但略有不同。
plateLocate的總體識別思路是:如果我們的車牌沒有大的旋轉或變形,那么其中必然包括很多垂直邊緣(這些垂直邊緣往往緣由車牌中的字符),如果能夠找到一個包含很多垂直邊緣的矩形塊,那么有很大的可能性它就是車牌。
依照這個思路我們可以設計一個車牌定位的流程。設計好后,再根據實際效果進行調優。下面的流程是經過多次調整與嘗試后得出的,包含了數月來作者針對測試圖片集的一個最佳過程(這個流程並不一定適用所有情況)。plateLocate的實現代碼在這里不貼了,Git上有所有源碼。plateLocate主要處理流程圖如下:
圖2 plateLocate流程圖
下面會一步一步參照上面的流程圖,給出每個步驟的中間臨時圖片。這些圖片可以在1.01版的CPlateLocate中設置如下代碼開啟調試模式。
CPlateLocate plate; plate.setDebug(1);
臨時圖片會生成在tmp文件夾下。對多個車牌圖片處理的結果僅會保留最后一個車牌圖片的臨時圖片。
1、原始圖片。
2、經過高斯模糊后的圖片。經過這步處理,可以看出圖像變的模糊了。這步的作用是為接下來的Sobel算子去除干擾的噪聲。
3、將圖像進行灰度化。這個步驟是一個分水嶺,意味着后面的所有操作都不能基於色彩信息了。此步驟是利是弊,后面再做分析。
4、對圖像進行Sobel運算,得到的是圖像的一階水平方向導數。這步過后,車牌被明顯的區分出來。
5、對圖像進行二值化。將灰度圖像(每個像素點有256個取值可能)轉化為二值圖像(每個像素點僅有1和0兩個取值可能)。
6、使用閉操作。對圖像進行閉操作以后,可以看到車牌區域被連接成一個矩形裝的區域。
7、求輪廓。求出圖中所有的輪廓。這個算法會把全圖的輪廓都計算出來,因此要進行篩選。
8、篩選。對輪廓求最小外接矩形,然后驗證,不滿足條件的淘汰。經過這步,僅僅只有六個黃色邊框的矩形通過了篩選。
8、角度判斷與旋轉。把傾斜角度大於閾值(如正負30度)的矩形舍棄。左邊第一、二、四個矩形被舍棄了。余下的矩形進行微小的旋轉,使其水平。
10、統一尺寸。上步得到的圖塊尺寸是不一樣的。為了進入機器學習模型,需要統一尺寸。統一尺寸的標准寬度是136,長度是36。這個標准是對千個測試車牌平均后得出的通用值。下圖為最終的三個候選”車牌“圖塊。
這些“車牌”有兩個作用:一、積累下來作為支持向量機(SVM)模型的訓練集,以此訓練出一個車牌判斷模型;二、在實際的車牌檢測過程中,將這些候選“車牌”交由訓練好的車牌判斷模型進行判斷。如果車牌判斷模型認為這是車牌的話就進入下一步即字符識別過程,如果不是,則舍棄。
3.“Plate Locate”的深入討論與調優策略
好了,說了這么多,讀者想必對整個“Plate Locate”過程已經有了一個完整的認識。那么讓我們一步步審核一下處理流程中的每一個步驟。回答下面三個問題:這個步驟的作用是什么?省略這步或者替換這步可不可以?這個步驟中是否有參數可以調優的?通過這幾個問題可以幫助我們更好的理解車牌定位功能,並且便於自己做修改、定制。
由於篇幅關系,下面的深入討論放在下期。
版權說明:
本文中的所有文字,圖片,代碼的版權都是屬於作者和博客園共同所有。歡迎轉載,但是務必注明作者與引用來源。任何未經允許的剽竊以及爬蟲抓取都屬於侵權,作者和博客園保留所有權利。
參考文獻:
1.http://my.phirobot.com/blog/2014-02-opencv_configuration_in_vs.html
2.http://blog.csdn.net/jinshengtao/article/details/17883075
3.http://www.ruanyifeng.com/blog/2012/11/gaussian_blur.html
4.http://blog.csdn.net/xiaowei_cqu/article/details/7829481