--- title: 車牌辨識:CRNN + CTC date: 2020-08-12 is_modified: false disqus: cynthiahackmd categories: - "智慧計算 › 人工智慧" tags: - "AI/ML" - "車牌辨識" - "光學字元辨識 OCR" - "電腦視覺 CV" - "CRNN" - "CTC" - "Published" --- {%hackmd @CynthiaChuang/Github-Page-Theme %} <br> 前一陣子(2020-02)的工作成果記錄。文章內容及架構是基於同事的投影片進行擴充與刪減而成,雖然同事整理過了,但畢竟自己整理過才是自己的咩 :smile:。 <!--more--> ## Optical Character Recognition, OCR OCR 為光學字元辨識(Optical Character Recognition)的縮寫,主要目的是將文字資料的圖像檔案進行分析辨識處理,取得文字及版面資訊。 ### 基本辨識流程 一般來說,一套完整的辨認系統通常包含三部分:影像掃描器(Image scanner)、光學字元辨認軟/硬體以及輸出介面(Output interface)。 ```flow input=>operation: 影像掃描器 ocr=>subroutine: 光學字元辨識 output=>operation: 輸出介面 input(right)->ocr(right)->output ``` 以超速照相為例,會先透過影像掃描器,即攝影機或是照相機拍下超速車輛,再並將圖片送往光學字元辨識軟體,也就是我們的車牌辨識系統,得到超速車輛的車牌,最後再把此車牌號碼送往罰單的系統,開出一張超速罰單,此步驟對應到就是流程中的輸出介面。 雖說整套流程包含三個部份,但在這邊我們只將目光放在光學字元辨識系統上。 <br> 細分光學字元辨識系統,可在分成兩個步驟:分別是==文字檢測==與==文字識別==。 - **文字檢測**: 顧名思義就是找出哪裡有文字以及文字範圍大小,對應到車牌辨識系統中就是找出車牌的位置並框出車牌。 - **文字辨識**: 是對定位出的文字區域進行辨識,一般狹義的 OCR 指的是這一部份,本文也是專注在這一部份。 ```flow input=>operation: 輸入 detection=>operation: 文字檢測 recognition=>operation: 文字辨識 output=>operation: 輸出 input(right)->detection(right)->recognition(right)->output ``` ### 文字辨識方法 文字辨識的方法依照實做的方式,可區分成 ==Two Stage== 或 ==End To End== 兩種。 <br> Two Stage 顧名思義會分成兩個主步驟來進行,它會先將目標區域中的字元進行分割,也就是將車牌中的字一個一個擷出來,並將擷出的字元進行正規化,最後透過特徵截取將字元一一進行辨識。 <p class="illustration"> <img src="https://i.imgur.com/irrsatm.png?1" alt="two stage flow"> 文字檢測 + 文字辨識(Two Stage) 辨識流程(圖片來源: <a href="https://sites.google.com/site/cs0991326/home/che-pai-bian-shi">Google 協作平台</a>) </p> 而另一種 end to end 的方式則不需要進行字元分割,是一步到位的辨識方法。它會直接將擷取出的整份車牌送入神經網路中學習。 兩相比較,前者多建立資料庫來進行比對,資料多寡並不會影響準確率,全倚賴特徵擷取的規則,但計算上較簡單。反之,後者的準確率受資料量影響,但計算較為複雜。 ## CRNN 說到車牌,之前[提過](/@CynthiaChuang/Taiwan-License-Plate-Rules-for-LPR)臺灣目前車牌是採兩代車牌並行使用的做法。目前流通的車牌總共有 `2-4`、 `4-2`、 `2-2`、 `3-2`、 `2-3`、 `3-3` 與 `3-4` 共 7 種格式,長度由 4 到 7 不等。 考慮到不定長度的狀況,最後決定採用 [CRNN](https://arxiv.org/pdf/1507.05717.pdf),將文字辨識轉為序列問題,透過 DCNN(深度卷積網絡)串接 RNN 的模型架構,對圖片進行文字識別。 ### 網路基本架構 <p class="illustration"> <img src="https://i.imgur.com/jLmg7u1.png" alt="CRNN OCR Network Architecture"> CRNN OCR Network Architecture (圖片來源: <a href="https://arxiv.org/pdf/1507.05717.pdf">論文</a>) </p> 根據上圖架構 CRNN 模型主要可以分成三個部分: 1. Convolutional Layers 2. Recurrent Layers 3. Transcription Layer <br> **Convolutional Layers** 這層的主要目的是使用 Convolutional Layers 的特性,去提取圖像特徵。 在將圖片餵進網路之前,需要對圖片做前處理,在原始的網路架構中,僅接受 `W x 32 (寬 x 高)` 的灰階圖片,因此輸入尺寸為 $(width, height, channel)=(W, 32, 1)$。當讀入圖片後,會經由使用 VGG 概念而採用的大量 $3 \times 3$ Conv 及 $2 \times 2$ pooling 所構成的網路,最終會得到此圖的特徵向量。 <p class="illustration"> <img src="https://i.imgur.com/EpQEZpo.png" alt="Network configuration summary"> Network configuration summary(圖片來源: <a href="https://arxiv.org/pdf/1507.05717.pdf">論文</a>) </p> 在此必須提到一件事,根據論文架構表格中,作者將最後後兩層的 pooling size 改為 ==1 X 2== pooling。有人認為這是為了盡可能不丟失寬度方向的資料,會較適合英文字母的識別(例如非等寬字體中較窄的 i 與 l)。 不過在論文作者的[原始碼](https://github.com/bgshih/crnn/blob/f5d41e3355fc4447f77cd6d5e5777b3a160c93cb/model/crnn_demo/config.lua#L68)中,在最後兩層的 pooling size 並非如論文內紅框處設計,[作者表示](https://github.com/bgshih/crnn/issues/6)論文中的 $1 \times 2$,其實是筆誤 XDDD <p class="illustration"> <img src="https://i.imgur.com/ykx0PYw.png" alt=""> (圖片來源: <a href="https://github.com/bgshih/crnn/issues/6">bgshih/crnn</a>) </p> 但根據我同事的實驗,在我們的資料集上使用 $1 \times 2$ 的 pooling size,表現確實比使用 $2 \times 2$ 好,這算是美麗的誤會嗎? 因此我們決定當作沒看到筆誤這件事,仍舊按照論文中這張表格進行實作。當過完所有的 Maxpooling 後,寬度為 $W \div 2^2$ ,高度為 $H \div 2^4$,在過最一層 $2 \times 2$ Conv,且 padding size 為 0(padding = ‘VALID’),最終輸出尺寸為 $(\frac{W}{4}, 1, 512)$。 <p class="illustration"> <img src="https://i.imgur.com/FMOuqrN.png" alt="Convolutional Layers"> Convolutional Layers(圖片來源: <a href="https://arxiv.org/pdf/1507.05717.pdf">論文</a>) </p> 但此階段的輸出無法直接最為下一階段的,需要經過一次轉換,因為 RNN 所接受的輸入格式應該為 $(timesteps, dim)$,很明顯的維度不對。 在論文中作者將這樣的轉換稱之為 ==Map-to-Sequence==,不過其實就是做 reshape 變成 $(\frac{W}{4} * 1, 512)$,恩...可以想像成依照寬度將原圖分成 $\frac{W}{4} * 1$ 等分,每等分會有 512 個特徵。因此最後產生就是,原圖從左到右按照順序生成的特徵向量,做為下一階段的輸入。 <br> **Recurrent Layers** 由於車牌長度是不定的,因此第二階段引入了雙向 RNN。不過習慣上說是 RNN,但實際上指的是 LSTM,用 GRU 也行,別真的用 RNN,不然應該會遇到梯度消失的問題。 在這一層中,網路會對上一階段傳入的特徵向量進行學習,以進行預測。由上圖可以了解,上一階段所傳入的特徵向量,其實相當於車牌中的一個矩形區域。RNN 所要學習的就是這個矩形區域所屬的字元為何,其輸出尺寸為 $(\frac{W}{4} * 1, 256*2)$。 最後再將輸出結果透過 softmax 「壓縮」成各個字元的機率分布,作為 CTC 層計算的輸入。假若你的目標字元為 A~Z 與 0~1,共 36 個,而此層的輸出為 $(\frac{W}{4} * 1, 36+1)$,多出來的一個類別保留給 CTC 層計算時所使用的 Blank。 <br> **Transcription Layer** 將從 Recurrent Layers 取的標籤通過去重複、整合等動作得到最終的辨識結果。白話的說就是將 RNN 的輸出做 Softmax 後得到各字元。 值得一提的是,這邊並不是採用常用的 Softmax cross-entropy loss,而是所謂的 ==CTC loss(Connectionist Temporal Classification,聯接時間分類)==。 ### Connectionist Temporal Classification, CTC 在這邊引入 CTC loss 是為了處理==不定長度序列的對齊問題==! 在一般使用 Softmax cross-entropy 計算 loss 時,會每一行輸出都對應到一個字元。因此你必須先標出每個字元在圖像中的位置,並調整網路以保證 Recurrent Layers 後所輸出的特徵值中,每一行會剛好對應到一個字元,才能進行訓練。 <p class="illustration"> <img src="https://i.imgur.com/fi6mMeg.png?1" alt="one column mapping to one char"> 上層輸出的特徵值必須每一行對應到一個字元,才能進行訓練(圖片來源: <a href="https://www.cnblogs.com/skyfsm/p/10335717.html">冠军的试炼</a>) </p> 但在實務上,若要額外標註每個字元位置會比單純標記字元,多出 5 倍以上的時間,耗時又費工。且由於車牌字元長度不定、照片角度不一、字寬不等、字型也有所差別,想讓每一行會剛好對應到一個字元,幾乎不可能。 但若忽略這個問題,直接對 Recurrent Layers 輸出進行序列分類,可能會出現冗餘資訊,如一個字母被連續識別兩次。遇到這情況又不可直接去除重複的字母,因為原始文字可能本來就存在連續重複字元,如:cook、geek、hello。 <p class="illustration"> <img src="https://i.imgur.com/FuIOQqa.png" alt="直接序列分類可能會出現冗餘資訊"> 直接序列分類可能會出現冗餘資訊(圖片來源: <a href="http://fancyerii.github.io/books/ctc/">李理的博客</a>) </p> 那如何針對每一幀進行對齊與識別?幸運的是,這問題在語音辨識中已有所研究。這是因為每個人語速不一,因此導致語音分幀結果和標籤對齊的挑戰。 論文中引入了語音辨識中常用的 CTC 來計算 loss,計算概念上類似 beam search,計算每條路徑的機率、並加總相同輸出的機率,最終取最高值最為輸出。 :::info 若對 CTC 的實做理論有興趣的,請詳閱這兩篇: 1. [语音识别:深入理解CTC Loss原理 | 左左左左想 | CSDN博客](https://blog.csdn.net/Left_Think/article/details/76370453) 2. [CTC理论和实战 | lili on | 李理的博客](http://fancyerii.github.io/books/ctc/) ::: <p class="illustration"> <img src="https://i.imgur.com/JTaLcpw.png" alt="直接序列分類可能會出現冗餘資訊"> 直接序列分類可能會出現冗餘資訊(圖片來源: <a href="http://fancyerii.github.io/books/ctc/">李理的博客</a>) </p> 實做過程中須注意的是,CTC 存在著 Blank 的概念,可以想像成序列剛好位於字與字之間的空白,因此在設計 Recurrent Layers 的輸出時會保留一個維度給 Blank,Blank 本身並不影響輸出結果,在輸出時 Blank 會被視為無意義字元給濾掉,但在計算每條路徑的機率時會將納入計算。 <p class="illustration"> <img src="https://i.imgur.com/JxSAKwO.png" alt="直接序列分類可能會出現冗餘資訊"> </p> ## CRNN 實作 在實做方面我們參考了 [CRNN-Keras](https://github.com/qjadud1994/CRNN-Keras) 的實做。但我們針對我們資料集,我們將輸入的圖像改成了彩色圖片,不做額外優化 baseline 分數約為 94.69%。 ### 優化與改善 我們分別針對資料本身、前處理、模型與後處理做了些嘗試與調整,這些調整約提升了 3.37%,準確度由 94.69% 來到了 98.06%。 <br> **資料本身** 在這邊我們重新檢閱了資料集本身清除訓練資料集中品質不好、非車牌與標註錯誤...等資料,以得到較好資料集本質。而針對加入非車牌資料,我們以全 Blank 作為它的標籤,一齊加入訓練,以增強模型的容錯能力。預期看到非車牌資料時能有辨識,並輸出空值,如此一來,可以在利用系統面的設計,來作為例外處理或補救。 另外依照臺灣的車牌比例更了輸入資料的尺寸,並添加更多實際場景資料作為訓練集,重新進行訓練。 <br> **前處理** 另外採用第三方 library 的 [aleju/imgaug](https://github.com/aleju/imgaug) 與 [mdbloice/Augmentor](https://github.com/mdbloice/Augmentor) ,來做 **Data Augmentation** 以增加模型的泛化能力。Augmentation 主要針對對比、變焦、偏斜、旋轉進行調整,並對高反光、耀光、雨景、日落、鏡頭模糊...等情況進行模擬。 P.S. Augmentor 的安裝與使用可以看[這篇](https://hackmd.io/@CynthiaChuang/Augmentor-Image-Augmentation-Library-in-Python)。 <br> **後處理** 後處理則是預測後校正,主要針對[維基百科](https://zh.wikipedia.org/wiki/%E8%87%BA%E7%81%A3%E8%BB%8A%E8%BC%9B%E7%89%8C%E7%85%A7)所提及的一些規則進行校正,主要是 0、1、I、O 這些規則的調整。 <br> **模型** 這邊參考語音作法,先針對不同特性的聲音訓練分類器(男生、女生、外國人...),並提取其內某一層當作特徵,當作額外輸入資訊。這邊則是這邊增加了一個車牌樣式分類器,抽取分類器 Dense 層特徵,當作 CRNN LSTM 額外的 input 資料一起訓練。 另外修改了 CTC decode 將它由 greedy search 改成了 prefix beam search。 其他嘗試 ResNet、[CRNN12](https://sci-hub.tw/10.1007/978-981-13-1733-0_9)、[STN(Spatial Transformer Networks)](https://zhuanlan.zhihu.com/p/37110107),不過最終模型實驗的改善都先擱置了。 ## 參考資料 1. PLUSTEK 公司 (2009-09-12)。[什麼是 OCR?](http://www.fuji.com.tw/posts/1889) 。檢自 蘋果網 (2020-03-17)。 2. 協同撰寫。[光學字元辨識](https://zh.wikipedia.org/wiki/%E5%85%89%E5%AD%A6%E5%AD%97%E7%AC%A6%E8%AF%86%E5%88%AB) 。檢自 維基百科 (2020-03-17)。 3. 王梅玲 (2011-04-27)。[技術服務小百科: 光學字元識別 (OCR, Optical Character Recognition)](http://techserviceslibrary.blogspot.com/2011/04/ocr-optical-character-recognition.html)。檢自 技術服務小百科 - Blogger (2020-03-17)。 4. 騰訊雲。[嘿,OCR文字識別瞭解下!](https://codertw.com/%E4%BA%BA%E5%B7%A5%E6%99%BA%E6%85%A7/320/)。檢自 程式前沿 (2020-03-17)。 5. cs0991326 (2011-03-16)。[車牌辨識](https://sites.google.com/site/cs0991326/home/che-pai-bian-shi)。檢自 cs0991326 - Google 協作平台 (2020-03-17)。 6. [車用電子系統導論_13 車牌辨識](http://140.117.156.238/course/IAE/2013/%E8%BB%8A%E7%94%A8%E9%9B%BB%E5%AD%90%E7%B3%BB%E7%B5%B1%E5%B0%8E%E8%AB%96_13.pdf)。檢自 葉家宏老師課程網頁 Courses (2020-06-03)。 7. 白裳 (2020-01-06)。[一文读懂CRNN+CTC文字识别](https://zhuanlan.zhihu.com/p/43534801)。檢自 机器学习随笔 - 知乎 (2020-03-17)。 8. AI科技園 (2019-11-01)。[大話文本識別經典模型:CRNN](https://kknews.cc/tech/pvgpqk8.html)。檢自 每日頭條 (2020-06-03)。 9. z.defying (2019-10-20)。[理解文本识别网络CRNN](https://zhuanlan.zhihu.com/p/71506131)。檢自 深度学习漫谈 知乎 (2020-06-03)。 10. Madcola (2019-01-29)。[【OCR技术系列之七】端到端不定长文字识别CRNN算法详解](https://www.cnblogs.com/skyfsm/p/10335717.html)。檢自 冠军的试炼 (2020-06-03)。 11. 左左左左想 (2017-07-30)。[语音识别:深入理解CTC Loss原理](https://blog.csdn.net/Left_Think/article/details/76370453)。檢自 CSDN博客 (2020-06-03)。 12. lili on。[CTC理论和实战](http://fancyerii.github.io/books/ctc/)。檢自 李理的博客 (2020-07-10)。 ## 更新紀錄 :::spoiler 最後更新日期:2020-08-12 - 2020-08-12 發布 - 2020-07-10 完稿 - 2020-03-17 起稿 ::: <br><br> > **本文作者**: 辛西亞.Cynthia > **本文連結**: [辛西亞的技能樹](https://cynthiachuang.github.io/License-Plate-Recognition-CRNN-CTC) / [hackmd 版本](https://hackmd.io/@CynthiaChuang/License-Plate-Recognition-CRNN-CTC) > **版權聲明**: 部落格中所有文章,均採用 [姓名標示-非商業性-相同方式分享 4.0 國際](https://creativecommons.org/licenses/by-nc-sa/4.0/deed.en) (CC BY-NC-SA 4.0) 許可協議。轉載請標明作者、連結與出處!