很早之前接觸到了飛槳(PaddlePaddle)以及PaddleDetection工具,被他們的簡單易用吸引,同時,這些工具極大降低了訓練模型的門檻並減少了所需時間,非常適合新手入門。在很多實際應用場景也有不俗的表現。
在端側部署方面,Paddle Lite是飛槳產品棧中用於端側高效能輕量化AI應用部署的推理引擎,給了移動端等場景更多可能。這款引擎允許我們在很多硬體平臺上實現輕量化的高效預測,進行一次預測耗時較短,也不需要太多的計算資源。
那麼如果我們想開發一款既能在本地進行預測又能在Android和iOS上面有一致體驗的App的話,Flutter無疑是一個好選擇。其作為開源移動UI框架已然成為跨平臺移動開發一大趨勢,在開發時可以保留狀態進行熱過載,內建許多令人眼前一亮的元件和漂亮的動畫,同時還能保證效能達到和原生應用一樣。也正因為這樣,不少公司開始把自己的應用向Flutter遷移,有許多我們耳熟能詳的App其實已經是基於Flutter開發。假如你已經對安卓原生開發十分熟悉的話,不妨去試試。
這次我們就基於Flutter來開發一個實時目標檢測程式,這也得益於Flutter支援訪問iOS和Android上的原生系統功能和系統SDK。
我們專案的GitHub地址:
https://github.com/KernelErr/realtime-object-detector
其中內建了水果識別模型,下載下來就能直接編譯體驗。約定:
Flutter端:Flutter專案主目錄。
Android端:專案的Android子目錄,原生安卓。
開發環境
我們在開發的時候環境如下:
Flutter version 1.12.13+hotfix.8Dart version 2.7.0Android Studio (version 3.6)Android toolchain - develop for Android devices (Android SDK version 29.0.3)Flutter在更新的同時加入了越來越多新的特性,網上一些基於老版本的實現方法已經不太實用,我們需要進行一些修改。
準備模型
Paddle Lite需要通過opt工具生成其支援的輕量化模型,如果你手上已經有PaddleDetection訓練出來的模型,那麼你需要先在PaddleDetection匯出模型,然後通過opt工具進行轉換。
如果你有其他框架訓練出來的模型,如caffe、tensorflow、onnx等,可以利用X2Paddle來轉換。
假設我們已經得到了兩個檔案:
model.nb - 基於Yolov3 Tiny訓練且已經通過opt優化好的模型label - 模型預測一一對應的標籤如何在Flutter中支援Paddle Lite?
我們只需要通過Android Studio建立一個新的Flutter專案,這裡我們假設名字是realtime_od。
準備Paddle Lite的預測庫和模型檔案
由於我們使用的是安卓原生程式碼,所以我們需要在Android端進行開發,而不是Flutter端。我們在Paddle Lite提供的預編譯預測庫裡面下載需要的預編譯庫,放到Android端的相應資料夾內,和原生安卓的目錄類似。之後我們繼續在android資料夾內放置模型檔案,在realtime_od/android/app/src/main/下面新建assets資料夾,並分別把模型和標籤放到models和labels子資料夾內。這時候你的目錄結果應該是這樣:
我們使用口罩模型作為樣例,模型位置是:models/mask/model.nb,標籤位置是:labels/mask_label_list。因此你需要在MainActivity裡面賦值:
禁用壓縮
在生成APK的時候,我們的模型會被壓縮,所以我們需要修改build.gradle配置檔案來禁用assets資料夾的壓縮。
提供原生安卓支援
如果為了Flutter的支援,給Paddle Lite專門寫一套Dart呼叫程式碼是工作巨大的,所以我們不妨直接基於官方的Demo進行修改。在Android端,我們直接使用了官方Demo中的程式碼,並在MainActivity內註冊了Channel。
由於Flutter中加入了程序安全機制,我們使用了一個MethodResultWrapper保證在主程序裡面返回result。新版Flutter中你需要使用configureFlutterEngine而不是onCreate來註冊元件。
使用實時影像
讓我們來給Flutter提供來自攝像頭的實時影像!新增一下Flutter的camera外掛,Dart 已經有很多現成的包給我們使用:
同時需要確保專案的最低Android SDK版本在21以上。在官方提供的Demo中,圖片輸入使用的是Bitmap圖片,但是我們從外掛得到的格式是android.graphics.ImageFormat.YUV_420_888,在Predictor類的最下面我們進行了相應的轉換,轉換程式碼的使用已經聯絡原作者獲得授權。我們在其中使用了RenderScript進行高效計算,避免延遲過高。
顯示實時影象並標註
大量的工作都花在了Android端上面,下面讓我們來Flutter端做些工作。
在main.dart和object_detector.dart裡面你可以發現我們呼叫Android端提供的方法,即loadModel以及detectObject。同時在DrawObjects類裡面提供了標註目標的功能,程式碼相對比較簡單,利用得到的預測結果進行畫圖。
Let’s run it!
這裡使用的是群友提供的口罩模型,label檔案裡面只有兩行,分別是戴口罩和未帶口罩。我們在Android 9裝置上面用PaddlePaddle官方示例圖片測試一下。
從日誌上面可以看出Paddle Lite預測的時間是接近700 ms。
更改模型和優化方案
如何使用其他模型
我們是參考群友的解決方案(參考連結裡面給出)適配的YOLO v3,主要的修改在Predictor內的模型輸入以及MainActivity的初始化。因為官方使用的是其他模型,輸入的Shape和我們不一樣,我們的是320。
如果你需要使用其他模型,請同步修改輸入處的:
以及輸出處的:
標註函式處也需要做相應修改,修改main.dart:
怎麼更快
實際上我們的模型還不夠快,選擇合適的模型,可以把預測時間縮短到更短。具體還是看自己的需要,Paddle Lite支援許多主流的模型,大家可以進行選用。
Trouble Shooting
記錄的問題包括Flutter開發過程中遇到的和Paddle Lite使用中遇到的:
1. Methods marked with @UiThread must be executed on the main thread.
這是因為Flutter引入了程序安全,不能直接在子程序裡面返回result,需要在主程序裡面返回,網上現在有很多解決辦法,我們的也是來自GitHub。
2. 錯誤: 不相容的型別: MainActivity無法轉換為FlutterEngine
很可能你看的教程是舊版本,請直接參考官方文件寫原生安卓。我們在原生安卓開發的時候指定了v2。
3. Paddle Lite出現庫錯誤
一開始以為是官方的問題,但是自己手動編譯一次庫就能解決。我已經內建了arm64的無問題的庫。
4. 其他問題
官方文件 -> GitHub -> 搜尋引擎,一般都能解決。