​用樹莓派(Raspberry Pi)和機器學習製作一個DIY車牌閱讀器

幾個月前,筆者有一些想法,想讓自己的車能檢測和識別物體。筆者之所以有如此想法,主要是因為已經見識了特斯拉的能力,雖然並不想馬上買一輛特斯拉(不得不說,Model 3看起來一天比一天誘人),但筆者認為應該嘗試實現自己的夢想。

所以,筆者實施了這個想法。

​用樹莓派(Raspberry Pi)和機器學習製作一個DIY車牌閱讀器

動圖來自結果的預測視頻。


下文記錄了項目中的每個步驟。如果只想看到檢測器運行的視頻或者GitHub鏈接,請跳到底部。

​用樹莓派(Raspberry Pi)和機器學習製作一個DIY車牌閱讀器

第一步,確定項目範圍

起初,筆者思考了這樣一個系統應該有什麼能力。如果說筆者這輩子學到了什麼,那就是從小範圍開始永遠是最好的策略:小步走。所以,除了車道保持任務(每個人都已經做過),筆者只是想在駕駛時簡單地識別車牌。此識別過程包括兩個步驟:

1、檢測車牌。

2、識別每個車牌邊框內的文字。

筆者認為,如果能做到這一點,那麼繼續進行其他任務應該是相當容易的(比如確定碰撞風險、距離等)。甚至可以創建一個向量空間來表示環境,這就十分炫酷了。

在過於擔心細節之前,筆者需要:

· 一個機器學習模型,將未標記圖像作為輸入並檢測車牌。

· 一些硬件。粗略地說,需要一個連接到一個或多個攝像機的計算機系統來查詢模型。

首先著手構建對象檢測的正確模型。

​用樹莓派(Raspberry Pi)和機器學習製作一個DIY車牌閱讀器

第二步,選擇正確模型

經過仔細研究,決定採用以下機器學習模型:

1. YOLOv3——這是迄今為止速度最快的模型,具有可與其他先進模型相媲美的映射。這個模型是用來檢測對象的。

2. CRAFT文本檢測器——用於檢測圖像中的文本。

3. CRNN ——一個循環的CNN(卷積神經網絡)模型。它必須進行循環,因為它需要將檢測到的字符按正確的順序組成單詞。

這三個模型如何協同?操作流程如下:

1. 首先,YOLOv3模型在攝像頭接收到的每一幀中檢測每個車牌的邊框。不建議預測的邊框非常精確,超過檢測到的對象的範圍較為合適。如果範圍太狹窄,那麼可能會影響後續進程的性能。這與下面的模型是並行不悖的。

2. CRAFT文本檢測器從YOLOv3接收裁剪後的車牌。如果裁剪的幀太狹窄,那麼很有可能會漏掉車牌的部分文字,導致預測失敗。但當邊框較大時,可以讓CRAFT模型檢測字的位置。這可以非常精確地預測每個字的位置。

3. 最後,可以將CRAFT中每個單詞的邊框輸入CRNN模型來預測實際單詞。

有了基本的模型架構,接下來可以解決硬件了。

​用樹莓派(Raspberry Pi)和機器學習製作一個DIY車牌閱讀器

第三步,設計硬件

當筆者知道自己需要一些簡單的東西時,便想起了舊愛:樹莓派。它有足夠的計算能力來用合適的幀數對幀進行預處理,它也有Pi Camera。Pi Camera是樹莓派的攝像機系統。它有一個非常棒的存儲庫,而且非常成熟。

至於互聯網接入,可以只用一個EC25-E來實現4G接入,也有來自筆者曾經的項目的一個GPS模塊嵌入。

筆者決定從外殼開始。把它掛在汽車的後視鏡上應該會很好用,所以最後設計了一個雙組件支撐結構:

1. 後視鏡一側,樹莓派+GPS模塊+4G模塊。可以查看筆者關於EC25-E模塊的文章,看看筆者對GPS和4G天線的選擇。

2. 在另一側,將用一個球關節臂來支撐Pi Camera進行定位。

這些支架/外殼將用筆者信任的Prusa i3 MK3S 3D打印機打印。

​用樹莓派(Raspberry Pi)和機器學習製作一個DIY車牌閱讀器


圖1—樹莓派+4G/GPS防護外殼


​用樹莓派(Raspberry Pi)和機器學習製作一個DIY車牌閱讀器


圖2—用球關節支撐Pi Camera進行定位

圖1和圖2顯示了實施時的結構。請注意,C型夾持器是可插拔的,所以已經打印的支架不附帶樹莓派的外殼和Pi Camera的支架。它們有一個插座,可將支架插入其中。如某一個讀者決定複製該項目,這是非常有用的。只需調整支架,使其適應汽車後視鏡。目前,筆者車上(是一臺路虎神行者)的夾持器功能良好。

​用樹莓派(Raspberry Pi)和機器學習製作一個DIY車牌閱讀器


圖3—PiCamera支架結構的側視圖

​用樹莓派(Raspberry Pi)和機器學習製作一個DIY車牌閱讀器


圖4—Pi Camera支架結構以及樹莓派夾持器的正視圖

​用樹莓派(Raspberry Pi)和機器學習製作一個DIY車牌閱讀器


圖5—對攝像機視野的預測

​用樹莓派(Raspberry Pi)和機器學習製作一個DIY車牌閱讀器


圖6—4G/GPS模塊、Pi Camera和樹莓派的嵌入式系統的特寫照片

顯然,這些需要一些時間來建模。筆者需要幾次迭代來使結構更加堅固。筆者在200微米的層高使用了PETG材料。PETG能夠在80-90攝氏度中運行,並且抗紫外線輻射性很強,雖然不如ASA。

​用樹莓派(Raspberry Pi)和機器學習製作一個DIY車牌閱讀器

第四步,訓練模型

一旦有了硬件,就開始訓練模型。

不出所料,最好不要自己重新創造,而是儘可能重複使用別人的。這就是遷移學習的全部內容——利用來自其他非常龐大的數據集的見解。筆者幾天前在這篇文章中讀到了關於遷移學習的一個例子。文中談到了一個哈佛醫學院的附屬團隊,該團隊能夠微調一個模型,“從胸片中預測長期死亡率,包括非癌症死亡”。他們只有5萬張標記圖像的小數據集,但是使用的預訓練模型(Inception-v4)是在大約1400萬張圖像上訓練的。他們只花了不到一小部分的原始模型所需的訓練成本(時間和金錢方面),而且已經達到了相當高的精確度。

筆者也打算這麼做。

YOLOv3

筆者在網上尋找預先訓練過的車牌模型,沒有最初預期的那麼多,但找到了一個在大約3600張車牌圖像上訓練過的模型。它並不多,但也比什麼都沒有強,而且除此之外,它也是在Darknet的預訓練模型上訓練的,可以使用。

而且因為筆者已經有了一個可以錄製的硬件系統,所以筆者決定在鎮上駕駛幾個小時,收集幀,來對上述模型進行微調。

筆者使用VOTT對收集到的幀進行註釋(當然還有車牌)。最後創建了一個由534幅圖像組成的小數據集,其中帶有標記的車牌邊框。

然後筆者找到了YOLOv3網絡在Keras上的應用。用它來訓練數據集,然後把筆者的模型再放到這個庫中,這樣其他人也可以使用它。筆者在測試集上得到的映射是90%,在如此小的數據集上能有如此表現已經很不錯了。

CRAFT以及CRNN

在無數次嘗試尋找一種好的網絡來識別文本之後,筆者偶然發現了keras-ocr,它是CRAFT和CRNN包的一個靈活版本。而且它還附帶了他們預先訓練好的模型。那太棒了。筆者決定不對模型進行微調,讓它們保持原樣。

最重要的是,用keras-ocr預測文本非常簡單。基本上只是幾行代碼。可以去他們的主頁看看是怎麼做的。

​用樹莓派(Raspberry Pi)和機器學習製作一個DIY車牌閱讀器

第五步,將筆者的車牌部署到檢測器模型

可以採用兩種主要的模型部署方法:

1、在本地處理所有推理。

2、在雲端處理所有推理。

這兩種方法都有一些困難。第一種方法意味著要有一個龐大的“大腦”計算機系統,這很複雜而且昂貴。第二個挑戰涉及延時和基礎設施,特別是用GPU進行推理。

筆者在研究中,偶然發現了一個叫做cortex的開源項目。這是相當新鮮的,但它作為人工智能開發工具下一步的進化肯定是有一定意義的。

從根本上說,cortex是一個用於將機器學習模型部署為一鍵切換的生產web服務的平臺。這意味著筆者可以專注於應用程序,剩下的事情交給cortex來管理。在本例中,它完成了AWS上的所有配置,而筆者要做的唯一一件事就是使用模板模型來編寫預測器。更牛的是,每個模型只要寫幾十行即可。

這是GitHub庫中cortex在終端中的作用。除了美麗和簡單,筆者想不出還能用什麼詞來形容:

​用樹莓派(Raspberry Pi)和機器學習製作一個DIY車牌閱讀器

圖源: Cortex GitHub

由於這個計算機視覺系統不是自動駕駛使用的,延遲對筆者來說就不那麼重要了,筆者可以用cortex來解決這個問題。如果它是自動駕駛系統的一部分,那麼使用通過雲提供商提供的服務就不是一個好主意,至少目前不是。

使用cortex部署機器學習模型僅需以下步驟:

1. 確定cortex.yaml文件,這是API的配置文件。每個API將處理一種類型的任務。筆者指定的yolov3 API用於檢測指定幀上車牌的邊框,而crnn API用於通過CRAFT文本檢測器和CRNN來預測車牌。

2. 確定每個API的預測器。基本上,就是在cortex中定義一個特定類的預測法以接收一個有效負載(所有檢測的部分已經由平臺處理),使用有效負載預測結果,然後返回預測。就這麼簡單!

在不深入討論筆者是如何完成這一工作的具體細節的情況下(為了保持文章的適當長度),這裡有一個經典虹膜數據集的預測器示例。cortex實現這兩個API的鏈接可以在這裡的存儲庫中找到,這個項目的所有其他資源都在本文末尾。















<code>#predictor.pyimport boto3import picklelabels = ["setosa", "versicolor","virginica"]class PythonPredictor:    def __init__(self, config):        s3 = boto3.client("s3")       s3.download_file(config["bucket"], config["key"],"model.pkl")        self.model =pickle.load(open("model.pkl", "rb"))    def predict(self, payload):        measurements = [            payload["sepal_length"],           payload["sepal_width"],           payload["petal_length"],           payload["petal_width"],        ]        label_id =self.model.predict([measurements])[0]        return labels[label_id]/<code>

然後做一個預測,只需使用curl,就像這樣




<code>curl http://***.amazonaws.com/iris-classifier     -X POST -H "Content-Type:application/json"     -d  {"sepal_length": 5.2,"sepal_width": 3.6, "petal_length": 1.4,"petal_width": 0.3}/<code>

做出的預測就像這個“Setosa”。非常簡單!

​用樹莓派(Raspberry Pi)和機器學習製作一個DIY車牌閱讀器

第六步,開發客戶端

通過cortex完成部署,筆者可以繼續設計客戶端——這是比較棘手的部分。

筆者想出了以下結構:

1. 以30幀每秒的速度從Pi Camera以相當高的分辨率(800x450或480x270)收集幀,並將每個幀推入公用隊列。

2. 在一個單獨的進程中,筆者將從隊列中取出幀,並將它們分發給不同工作線程。

3. 每個工作線程(或筆者所稱的推理線程)都將向cortex API發出API請求。首先,向yolov3API發出一個請求,然後,如果檢測到任何車牌,則向crnn API發出另一個帶有一批裁剪過的車牌的請求。作出的響應將包含文本格式的預測車牌。

4. 將每個檢測到的車牌(有或無識別的文本)推送到另一個隊列,最終將其廣播到瀏覽器頁面。同時,還將車牌預測推送到另一個隊列,以便稍後將它們保存到磁盤(csv格式)。

5. 廣播隊列將接收一串無序幀。它的用戶的任務是在每次向客戶端廣播新幀時將它們放在一個非常小的緩衝區中(只有幾個幀大小),從而重新排序它們。這個用戶正在另一個進程上單獨運行。該用戶還必須嘗試將隊列上的大小保持在指定值,以便幀能夠以一致的幀數(即30幀每秒)顯示。顯然,如果隊列大小減少,那麼幀數就會成比例減少,反之亦然,當隊列大小增加時,幀數也會成比例增加。最初,筆者想實現一個滯後函數,但筆者認為它會給工作流造成極大影響。

6. 同時,主進程中會有另一個線程運行,從另一個隊列和GPS數據中提取預測。當客戶端接收到終止信號時,預測、GPS數據和時間也轉存到csv文件中。

下圖是客戶端與AWS上的雲API相關的流程圖。

​用樹莓派(Raspberry Pi)和機器學習製作一個DIY車牌閱讀器


圖7——cortex提供的雲API相關的客戶端流程圖

在本例中,客戶端是樹莓派,推理請求發送到的雲API是由AWS(Amazon Web Services)上的cortex提供的。

客戶端的源代碼也可以在其GitHub 存儲庫中進行查看。

必須克服的一個困難是4G的帶寬。最好降低此應用程序所需的帶寬,以減少可能的問題或對可用數據的過度使用。筆者決定在Pi Camera上使用非常低的分辨率:480x270(可以使用小分辨率,因為Pi Camera的視野非常窄,所以仍然可以很容易地識別車牌)。然而,即使在該分辨率下,幀的JPEG在10兆比特處為約100千字節。乘以30幀/秒,得到3000千字節,大約是24兆字節/秒,這還不包括HTTPoverhead——這是一個很大的數目。

筆者採用了以下策略:

· 將寬度降至416像素,這正是YOLOv3模型調整圖像大小的結果。尺寸保持完好。

· 將圖像轉換為灰度。

· 移除圖像頂部45%的部分。一般認為車牌不會出現在車架的頂部,因為汽車不會飛起來,對吧?從筆者觀察到的情況來看,剪掉45%的圖像並不會影響預測器的性能。

· 再次將圖像轉換為JPEG,但質量較低。

得到的框架大小約為7-10KB,非常好。這相當於2.8 兆字節/秒。但是算上所有的overhead,它大約是3.5兆字節/秒(也包括響應)。

對於crnn API,裁剪後的車牌根本不需要太多,即使不用壓縮技巧。每一部分大約為2-3千字節。

總而言之,要以30幀/秒的速度運行,推理API所需的帶寬約為6兆字節/秒。這個數字可以接受。

​用樹莓派(Raspberry Pi)和機器學習製作一個DIY車牌閱讀器

結論


這確實行之有效!

上述是通過cortex運行推理的實時示例。筆者需要大約20個配備GPU的實例才能順利運行。根據集群的延遲,可能需要更多或更少的實例。從捕獲幀到廣播到瀏覽器窗口之間的平均延遲約為0.9秒,而且推理還可以更深入,這真是太神奇了。

文本識別部分可能不是最好的,但它至少證明了這一點——通過提高視頻的分辨率,或者通過縮小攝像機的視野,或者通過微調,它可以精確得多。

高GPU可以通過優化來減少。例如,轉換模型以使用混合/全半精度(FP16/BFP16)。一般來說,使用混合精度的模型對精度的影響最小,因此並不需要進行太多的權衡。

T4和V100 GPU具有特殊的張量核心,用於在半精度類型上進行超快的矩陣乘法。在T4上,半精度運算比單精度運算的加速比約為8倍,在V100上,半精度運算比單精度運算的加速比約為10倍。這是一個數量級的差異,意味著一個已轉換為使用單精度/混合精度的模型可以少花多達8倍的時間來進行推理,而在V100上則少花十分之一的時間。

筆者還沒有轉換模型以使用單精度/混合精度,因為這超出了這個項目的範圍。就筆者而言,這只是一個優化問題。筆者最有可能在cortex 0.14版本發佈時完成這項工作(如果有真正的多進程支持和基於隊列的自動縮放),這樣筆者也可以利用多進程網絡服務器。

總而言之,如果所有優化都到位,將集群的規模從20個配備GPU的實例減少到只有一個實際上是可行的。如果優化得當,甚至連一個配備GPU的實例都不用。

為了更加節約成本,在AWS上使用Elastic Inference可以減少高達75%的成本,這是一個很大的數目!形象地說,可以花一毛錢就擁有一個用於實時處理流的管道。不幸的是,目前cortex還無法支持Elastic Inference,但可以看到這一點在不久的將來就能實現,因為它已經進入了他們的視野中。參見cortexlabs/cortex/issues/618。

注意:YOLOv3和CRNN模型,可以通過在更大的數據集(大約50-100K樣本)上對其進行微調來改進。此處,幀的尺寸還可以進一步減小,以減少數據的使用,而不對精確度造成很大影響:“補償某處,以便從其他地方取走”。這與轉換所有這些模型以使用半精度類型(以及可能的Elastic Inference)相結合,可以形成一種非常有效或降低成本的推理機器。


分享到:


相關文章: