【開(kāi)源項(xiàng)目】特斯拉+樹(shù)莓派實(shí)現(xiàn)車牌識(shí)別檢測(cè)系統(tǒng)
來(lái)源:機(jī)器之心 |?作者:Robert Lucian Chiriac
參與:王子嘉、思、一鳴
怎樣在不換車的前提下打造一個(gè)智能車系統(tǒng)呢?一段時(shí)間以來(lái),本文作者 Robert Lucian Chiriac 一直在思考讓車擁有探測(cè)和識(shí)別物體的能力。這個(gè)想法非常有意思,因?yàn)槲覀円呀?jīng)見(jiàn)識(shí)過(guò)特斯拉的能力,雖然沒(méi)法馬上買一輛特斯拉(不得不提一下,Model 3 現(xiàn)在看起來(lái)越來(lái)越有吸引力了),但他有了一個(gè)主意,可以努力實(shí)現(xiàn)這一夢(mèng)想。
所以,作者用樹(shù)莓派做到了,它放到車上可以實(shí)時(shí)檢測(cè)車牌。
在接下來(lái)的內(nèi)容里,我們將介紹項(xiàng)目中的每個(gè)步驟,并提供 GitHub 項(xiàng)目地址,其中項(xiàng)目地址只是客戶端工具,還其它數(shù)據(jù)集與預(yù)訓(xùn)練模型都可以在原博客結(jié)尾處找到。
項(xiàng)目地址:
https://github.com/RobertLucian/cortex-license-plate-reader-client
下面,讓我們看看作者 Robert Lucian Chiriac 是如何一步步打造一個(gè)好用的車載檢測(cè)識(shí)別系統(tǒng)。
放一張成品圖鎮(zhèn)樓。
第一步:確定項(xiàng)目范圍
開(kāi)始之前,我腦海里出現(xiàn)的第一個(gè)問(wèn)題是這樣一個(gè)系統(tǒng)應(yīng)該能夠做到什么。如果說(shuō)我活到現(xiàn)在學(xué)到了什么,那就是循序漸進(jìn)——從小處著手永遠(yuǎn)是最好的策略。所以,除了基本的視覺(jué)任務(wù),我需要的只是在開(kāi)車時(shí)能清楚地識(shí)別車牌。這個(gè)識(shí)別過(guò)程包括兩個(gè)步驟:
檢測(cè)到車牌。
識(shí)別每個(gè)車牌邊界框內(nèi)的文本。
我覺(jué)得如果我能完成這些任務(wù),再做其他類似的任務(wù)(如確定碰撞風(fēng)險(xiǎn)、距離等)就會(huì)容易得多。我甚至可能可以創(chuàng)建一個(gè)向量空間來(lái)表示周圍的環(huán)境——想想都覺(jué)得酷。
在確定這些細(xì)節(jié)之前,我知道我得先做到:
一個(gè)機(jī)器學(xué)習(xí)模型,以未標(biāo)記的圖像作為輸入,從而檢測(cè)到車牌;
某種硬件。簡(jiǎn)單地說(shuō),我需要連接了一個(gè)或多個(gè)攝像頭的計(jì)算機(jī)系統(tǒng)來(lái)調(diào)用我的模型。
那就先從第一件事開(kāi)始吧——構(gòu)建對(duì)象檢測(cè)模型。
第二步:選擇正確的模型
經(jīng)過(guò)仔細(xì)研究,我決定用這些機(jī)器學(xué)習(xí)模型:
YOLOv3- 這是當(dāng)下最快的模型之一,而且跟其他 SOTA 模型的 mAP 相當(dāng)。我們用這個(gè)模型來(lái)檢測(cè)物體;
CRAFT 文本檢測(cè)器 - 我們用它來(lái)檢測(cè)圖像中的文本;
CRNN - 簡(jiǎn)單來(lái)說(shuō),它就是一個(gè)循環(huán)卷積神經(jīng)網(wǎng)絡(luò)模型。為了將檢測(cè)到的字符按照正確的順序排成單詞,它必須是時(shí)序數(shù)據(jù);
這三個(gè)模型是怎么通力合作的呢?下面說(shuō)的就是操作流程了:
首先,YOLOv3 模型從攝像機(jī)處接收到一幀幀圖像,然后在每個(gè)幀中找到車牌的邊界框。這里不建議使用非常精確的預(yù)測(cè)邊界框——邊界框比實(shí)際檢測(cè)對(duì)象大一些會(huì)更好。如果太擠,可能會(huì)影響到后續(xù)進(jìn)程的性能;
文本檢測(cè)器接收 YOLOv3 裁剪過(guò)的車牌。這時(shí),如果邊界框太小,那么很有可能車牌文本的一部分也被裁掉了,這樣預(yù)測(cè)結(jié)果會(huì)慘不忍睹。但是當(dāng)邊界框變大時(shí),我們可以讓 CRAFT 模型檢測(cè)字母的位置,這樣每個(gè)字母的位置就可以非常精確;
最后,我們可以將每個(gè)單詞的邊界框從 CRAFT 傳遞到 CRNN 模型,以預(yù)測(cè)出實(shí)際單詞。
有了我的基本模型架構(gòu)草圖,我可以開(kāi)始轉(zhuǎn)戰(zhàn)硬件了。
第三步:設(shè)計(jì)硬件
當(dāng)我發(fā)現(xiàn)我需要的是一種低功耗的硬件時(shí),我想起了我的舊愛(ài):樹(shù)莓派。因?yàn)樗袑傧鄼C(jī) Pi Camera,也有足夠的計(jì)算能力在不錯(cuò)的幀率下預(yù)處理各個(gè)幀。Pi Camera 是樹(shù)莓派(Raspberry Pi)的實(shí)體攝像機(jī),而且有其成熟完整的庫(kù)。
為了接入互聯(lián)網(wǎng),我可以通過(guò) EC25-E 的 4G 接入,我以前的一個(gè)項(xiàng)目里也用過(guò)它的一個(gè) GPS 模塊,詳情可見(jiàn):
博客地址:https://www.robertlucian.com/2018/08/29/mobile-network-access-rpi/
然后我要開(kāi)始設(shè)計(jì)外殼了——把它掛在汽車的后視鏡上應(yīng)該沒(méi)問(wèn)題,所以我最終設(shè)計(jì)了一個(gè)分為兩部分的支撐結(jié)構(gòu):
在后視鏡的方向上,樹(shù)莓派+ GPS 模塊+ 4G 模塊將保留下來(lái)。關(guān)于我使用的 GPS 和 4G 天線,你可以去看一下我關(guān)于 EC25-E 模塊的文章;
在另一側(cè),我用一個(gè)利用球關(guān)節(jié)活動(dòng)的手臂來(lái)支撐 Pi Camera
我會(huì)用我可靠的 Prusa i3 MK3S 3D 打印機(jī)來(lái)打印這些零件,在原文文末也提供了 3D 打印參數(shù)。
圖 1 :樹(shù)莓派+4G/GPS 殼的外形
圖 2:利用球關(guān)節(jié)活動(dòng)臂支撐 Pi Camera
圖 1 和圖 2 就是它們渲染時(shí)候的樣子。注意這里的 c 型支架是可插拔的,所以樹(shù)莓派的附件和 Pi Camera 的支撐物沒(méi)有和支架一起打印出來(lái)。他們共享一個(gè)插座,插座上插著支架。如果某位讀者想要復(fù)現(xiàn)這個(gè)項(xiàng)目,這是非常有用的。他們只需要調(diào)整后視鏡上的支架就可以了。目前,這個(gè)底座在我的車(路虎 Freelander)上工作得很好。
圖 3:Pi Camera 支撐結(jié)構(gòu)的側(cè)視圖
圖 4:Pi Camera 支撐結(jié)構(gòu)和 RPi 底座的正視圖
圖 5:預(yù)計(jì)的相機(jī)視野
圖 6:內(nèi)置 4G/GPS 模塊、Pi Camera,樹(shù)莓派的嵌入式系統(tǒng)近照
顯然,這些東西需要一些時(shí)間來(lái)建模,我需要做幾次才能得到堅(jiān)固的結(jié)構(gòu)。我使用的 PETG 材料每層高度為 200 微米。PETG 在 80-90 攝氏度下可以很好地工作,并且對(duì)紫外線輻射的抵抗力很強(qiáng)——雖然沒(méi)有 ASA 好,但是也很強(qiáng)。
這是在 SolidWorks 中設(shè)計(jì)的,所以我所有的 SLDPRT/SLDASM 文件以及所有的 STLs 和 gcode 都可以在原文末找到。你也可以用這些東西來(lái)打印你自己的版本。
第四步:訓(xùn)練模型
既然硬件解決了,就該開(kāi)始訓(xùn)練模型了。大家應(yīng)該都知道,盡可能站在巨人的肩膀上工作。這就是遷移學(xué)習(xí)的核心內(nèi)容了——先用非常大的數(shù)據(jù)集來(lái)學(xué)習(xí),然后再利用這里面學(xué)到的知識(shí)。
YOLOv3
我在網(wǎng)上找了很多預(yù)先訓(xùn)練過(guò)的車牌模型,并沒(méi)有我最初預(yù)期的那么多,但我找到了一個(gè)在 3600 張車牌圖上訓(xùn)練過(guò)的。這個(gè)訓(xùn)練集并不大,但也比什么都沒(méi)有強(qiáng)。除此之外,它也是在 Darknet 的預(yù)訓(xùn)練模型的基礎(chǔ)上進(jìn)行訓(xùn)練的,所以我可以直接用。
模型地址:https://github.com/ThorPham/License-plate-detection
因?yàn)槲乙呀?jīng)有了一個(gè)可以記錄的硬件系統(tǒng),所以我決定用我的系統(tǒng)在鎮(zhèn)上轉(zhuǎn)上幾個(gè)小時(shí),收集新的視頻幀數(shù)據(jù)來(lái)對(duì)前面的模型進(jìn)行微調(diào)。
我使用 VOTT 來(lái)對(duì)那些含有車牌的幀進(jìn)行標(biāo)注,最終創(chuàng)建了一個(gè)包含 534 張圖像的小數(shù)據(jù)集,這些圖像中的車牌都有標(biāo)記好的邊界框。
數(shù)據(jù)集地址:https://github.com/RobertLucian/license-plate-dataset
然后我又找到利用 Keras 實(shí)現(xiàn) YOLOv3 網(wǎng)絡(luò)的代碼,并用它來(lái)訓(xùn)練我的數(shù)據(jù)集,然后將我的模型提交到這個(gè) repo,這樣別人也能用它。我最終在測(cè)試集上得到的 mAP 是 90%,考慮到我的數(shù)據(jù)集非常小,這個(gè)結(jié)果已經(jīng)很好了。
Keras 實(shí)現(xiàn):https://github.com/experiencor/keras-yolo3
提交合并請(qǐng)求:https://github.com/experiencor/keras-yolo3/pull/244
CRAFT & CRNN
為了找到一個(gè)合適的網(wǎng)絡(luò)來(lái)識(shí)別文本,我經(jīng)過(guò)了無(wú)數(shù)次的嘗試。最后我偶然發(fā)現(xiàn)了 keras-ocr,它打包了 CRAFT 和 CRNN,非常靈活,而且有預(yù)訓(xùn)練過(guò)的模型,這太棒了。我決定不對(duì)模型進(jìn)行微調(diào),讓它們保持原樣。
keras-ocr 地址:https://github.com/faustomorales/keras-ocr
最重要的是,用 keras-ocr 預(yù)測(cè)文本非常簡(jiǎn)單。基本上就是幾行代碼。你可以去該項(xiàng)目主頁(yè)看看這是如何做到的。
第五步:部署我的車牌檢測(cè)模型
模型部署主要有兩種方法:
在本地進(jìn)行所有的推理;
在云中進(jìn)行推理。
這兩種方法都有其挑戰(zhàn)。第一個(gè)意味著有一個(gè)中心「大腦」計(jì)算機(jī)系統(tǒng),這很復(fù)雜,而且很貴。第二個(gè)面臨的則是延遲和基礎(chǔ)設(shè)施方面的挑戰(zhàn),特別是使用 gpu 進(jìn)行推理。
在我的研究中,我偶然發(fā)現(xiàn)了一個(gè)名為 cortex 的開(kāi)源項(xiàng)目。它是 AI 領(lǐng)域的新人,但作為 AI 開(kāi)發(fā)工具的下一個(gè)發(fā)展方向,這無(wú)疑是有意義的。
cortex 項(xiàng)目地址:https://github.com/cortexlabs/cortex
基本上,cortex 是一個(gè)將機(jī)器學(xué)習(xí)模型部署為生產(chǎn)網(wǎng)絡(luò)服務(wù)的平臺(tái)。這意味著我可以專注于我的應(yīng)用程序,而把其余的事情留給 cortex 去處理。它在 AWS 上完成所有準(zhǔn)備工作,而我唯一需要做的就是使用模板模型來(lái)編寫預(yù)測(cè)器。更棒的是,我只需為每個(gè)模型編寫幾十行代碼。
如下是從 GitHub repo 獲取的 cortex 運(yùn)行時(shí)的終端。如果這都稱不上優(yōu)美簡(jiǎn)潔,那我就不知道該用什么詞來(lái)形容它了:
因?yàn)檫@個(gè)計(jì)算機(jī)視覺(jué)系統(tǒng)不是為了實(shí)現(xiàn)自動(dòng)駕駛而設(shè)計(jì)的,所以延遲對(duì)我來(lái)說(shuō)不那么重要,我可以用 cortex 來(lái)解決這個(gè)問(wèn)題。如果它是自動(dòng)駕駛系統(tǒng)的一部分,那么使用云提供商提供的服務(wù)就不是一個(gè)好主意,至少現(xiàn)在不是。
部署帶有 cortex 的 ML 模型只需:
定義 cortex.yaml 文件,它是我們的 api 的配置文件。每個(gè) API 將處理一種類型的任務(wù)。我給 yolov3 的 API 分配的任務(wù)是檢測(cè)給定幀上的車牌邊界框,而 crnn API 則是在 CRAFT 文本檢測(cè)器和 crnn 的幫助下預(yù)測(cè)車牌號(hào)碼;
定義每個(gè) API 的預(yù)測(cè)器。基本上你要做的就是在 cortex 中定義一個(gè)特定類的 predict 方法來(lái)接收一個(gè)有效負(fù)載(所有的 servy part 都已經(jīng)被平臺(tái)解決了),這個(gè)有效負(fù)載來(lái)可以來(lái)預(yù)測(cè)結(jié)果,然后返回預(yù)測(cè)結(jié)果。就這么簡(jiǎn)單!
這里有一個(gè)經(jīng)典 iris 數(shù)據(jù)集的預(yù)測(cè)器示例,但因?yàn)槲恼缕?,具體細(xì)節(jié)在此就不做贅述了。項(xiàng)目鏈接里可以找到 cortex 使用這兩個(gè) api 的方法——這個(gè)項(xiàng)目的所有其他資源都在本文的最后。
#?predictor.pyimport?boto3
import?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]
為了做預(yù)測(cè),你只需要像下面這樣使用 curl 就行了:
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}'
然后你會(huì)收到類似setosa這樣的反饋,非常簡(jiǎn)單!
第六步:開(kāi)發(fā)客戶端
有了 cortex 來(lái)幫我進(jìn)行部署之后,我就可以開(kāi)始設(shè)計(jì)客戶端了——這算是比較棘手的部分。
我想到了以下架構(gòu):
從 Pi Camera 以可接受的分辨率(800x450 或 480x270)收集幀速率為 30 FPS 的幀,并將每個(gè)幀推入一個(gè)公共隊(duì)列;
在一個(gè)單獨(dú)的進(jìn)程中,我將從隊(duì)列中取出幀,并將它們分發(fā)給不同線程上的多個(gè)工作站;
每個(gè)工作線程(或者我稱之為推斷線程)都會(huì)向我的 cortex API 發(fā)出 API 請(qǐng)求。首先,一個(gè)請(qǐng)求到我的 yolov3API,然后,如果有任何車牌檢測(cè)到,另一個(gè)請(qǐng)求會(huì)帶著一批裁剪的車牌發(fā)到我的 crnn API。預(yù)測(cè)的車牌號(hào)碼將以文本格式返回;
將每個(gè)檢測(cè)到的牌照(帶不帶識(shí)別后的文本都可以)推到另一個(gè)隊(duì)列,最終將其廣播到瀏覽器頁(yè)面。同時(shí),還將車牌號(hào)碼預(yù)測(cè)推到另一個(gè)隊(duì)列,以便稍后將其以 csv 格式保存到磁盤;
廣播隊(duì)列將接收一組無(wú)序的幀。consumer 的任務(wù)是先把它們放在一個(gè)非常小的緩沖區(qū)(幾個(gè)幀的大小),每次廣播一個(gè)新的幀給 client 重新排序。這個(gè) consumer 在另一個(gè)進(jìn)程上單獨(dú)運(yùn)行,它還必須嘗試保持隊(duì)列的大小固定為指定值,以便以一致的幀速率顯示幀。顯然,如果隊(duì)列大小下降,那么幀率的下降是成比例的,反之亦然;
與此同時(shí),在主進(jìn)程中還會(huì)運(yùn)行另一個(gè)線程,從另一個(gè)隊(duì)列獲取預(yù)測(cè)和 GPS 數(shù)據(jù)。當(dāng)客戶端收到終止信號(hào)時(shí),預(yù)測(cè)、GPS 數(shù)據(jù)和時(shí)間也會(huì)被轉(zhuǎn)存到 csv 文件中。
下圖是客戶端與 AWS 上的云 api 之間的關(guān)系流程圖。
圖 7:基于 cortex 提供的云 api 與客戶端流程圖
在我們的例子中,客戶端是樹(shù)莓派,推理請(qǐng)求發(fā)送到的云 api 由 AWS 上的 cortex 提供。
客戶端的源代碼也可以在其 GitHub 中找到:https://github.com/robertlucian/cortex-licens-plate-reader-client
我必須克服的一個(gè)挑戰(zhàn)是 4G 的帶寬。最好減少此應(yīng)用程序所需的帶寬,以減少可能的 hangups 或?qū)捎脭?shù)據(jù)的過(guò)度使用。我決定讓 Pi Camera 使用一個(gè)非常低的分辨率:480x270(我們這里可以用一個(gè)小分辨率,因?yàn)?Pi Camera 的視野非常窄,所以我們?nèi)匀豢梢院苋菀椎刈R(shí)別車牌)。
不過(guò),即使是在這個(gè)分辨率下,每一幀的 JPEG 大小也是大約 100KB(0.8MBits)。乘以每秒 30 幀就得到 3000KB,也就是 24mb /s,這還是在沒(méi)有 HTTP 開(kāi)銷的情況下,這是很多的。
因此,我用了一些小技巧:
將寬度減少到 416 像素,也就是 YOLOv3 模型所需要的大小,而且尺度顯然是完好無(wú)損的;
將圖像轉(zhuǎn)換為灰度圖;
移除圖片頂部 45% 的部分。這里的想法是車牌不會(huì)出現(xiàn)在車架的頂部,因?yàn)槠嚥粫?huì)飛,對(duì)吧?據(jù)我所知,刪除 45% 的圖像并不影響預(yù)測(cè)器的性能;
再次轉(zhuǎn)換圖像為 JPEG,但此時(shí)的質(zhì)量變低了很多。
最終得到的幀的大小大約是 7-10KB,這是非常好的。這相當(dāng)于 2.8Mb/s。但是考慮到響應(yīng)等所有的開(kāi)銷,它大約是 3.5Mb/s。對(duì)于 crnn API,裁剪過(guò)的車牌根本不需要太多空間,即使沒(méi)有進(jìn)行壓縮,它們的大小也就是 2-3KB 左右一個(gè)。
總而言之,要以 30FPS 的速度運(yùn)行,推理 api 所需的帶寬大約是 6Mb/s,這個(gè)數(shù)字我可以接受。
結(jié)果
成功了!
上面這個(gè)是通過(guò) cortex 進(jìn)行實(shí)時(shí)推理的例子。我需要大約 20 個(gè)裝備了 gpu 的實(shí)例才能順暢地運(yùn)行它。根據(jù)這一組 gpu 的延遲,你可能需要更多的 gpu 或是更少的實(shí)例。從捕獲幀到向?yàn)g覽器窗口廣播幀之間的平均延遲約為 0.9 秒,考慮到推斷發(fā)生在很遠(yuǎn)的地方,這真是太神奇了——到現(xiàn)在我還是覺(jué)得驚訝。
文本識(shí)別部分可能不是最好的,但它至少證明了一點(diǎn)——它可以通過(guò)增加視頻的分辨率或通過(guò)減少攝像機(jī)的視場(chǎng)或通過(guò)微調(diào)來(lái)更精確。
至于 GPU 需求數(shù)太高的問(wèn)題,這可以通過(guò)優(yōu)化來(lái)解決。例如,在模型中使用混合精度/全半精度 (FP16/BFP16)。一般來(lái)說(shuō),讓模型使用混合精度對(duì)精度的影響很小,所以我們并沒(méi)有做太多的權(quán)衡。
總而言之,如果所有的優(yōu)化都到位,那么將 gpu 的數(shù)量從 20 個(gè)減少到一個(gè)實(shí)際上是可行的。如果進(jìn)行了適當(dāng)?shù)膬?yōu)化,甚至一個(gè) gpu 的資源都用不完。
原文地址:https://towardsdatascience.com/i-built-a-diy-license-plate-reader-with-a-raspberry-pi-and-machine-learning-7e428d3c7401
-END-
猜你喜歡
?最 后
??
?
請(qǐng)戳右下角,給我一點(diǎn)在看!
免責(zé)聲明:本文內(nèi)容由21ic獲得授權(quán)后發(fā)布,版權(quán)歸原作者所有,本平臺(tái)僅提供信息存儲(chǔ)服務(wù)。文章僅代表作者個(gè)人觀點(diǎn),不代表本平臺(tái)立場(chǎng),如有問(wèn)題,請(qǐng)聯(lián)系我們,謝謝!