使用opencv-dnn移植caffe-model到安卓手機上
opencv3.3.0+添加了dnn模組,支援常用的深度學習框架,如caffe、tf、torch、mxnet等,本部落格主要是使用opencv的dnn模組,移植訓練好的caffe model到安卓手機上,主要參照官放教程,以及自己的一些改動,https://docs.opencv.org/3.4.0/d0/d6c/tutorial_dnn_android.html。
在進行移植之前,需要做幾個準備工作:
1.選擇一個移動端的深度學習框架,如ncnn、mace、tf-lite等,這裡我使用的是opencv-android,需要下載opencv-android-sdk,
https://github.com/opencv/opencv/releases
3.ubuntu下安卓的開發環境配置。
實現步驟如下:
1.開啟Android Studio,新建一個工程,Let's call it opencv_mobilenet,預設設定;
2.
新增opencv庫,File->New->Import module,路徑unpacked_OpenCV_package/sdk/java,
-
開啟如下兩個檔案:
AndroidStudioProjects/opencv_mobilenet/app/build.gradle
AndroidStudioProjects/opencv_mobilenet/openCVLibrary330/build.gradle
修改
compileSdkVersion
和buildToolsVersion
(根據自己的環境修改)compileSdkVersion 14
->compileSdkVersion 26
buildToolsVersion "25.0.0"
->buildToolsVersion "26.0.1"
-
File->Project Structure
. 新增 OpenCV module .
至此,安卓上的opencv環境就配置好了。
3.Make a sample
- 修改
app/src/main/res/layout/activity_main.xml;
- 將下載好的caffe model,
MobileNetSSD_deploy.prototxt
和MobileNetSSD_deploy.caffemodel
複製到路徑app/build/intermediates/assets/debug
下; - 修改
/app/src/main/AndroidManifest.xml;
修改java主程式,app/src/main/java/org/opencv/samples/opencv_mobilenet/MainActivity.java
。
4.編譯工程,生成app,就可以安裝到手機上了。
以上是官方的教程,如果直接按照以上步驟,會發生,除了自己編譯生成的app外,還會在手機上安裝opencv package的app,開啟自己編譯生成的app,會提示找不到opencv package,即使已經安裝了opencv package的app,因此,我這裡將opencv-android的庫檔案一起打包到app中,但是這樣會導致一個問題,編譯生成的app檔案會比較大。
-
在安卓工程
/app/src/main/
下建立一個jniLibs
資料夾,再將 OpenCV Android SDK 中sdk/native/libs
下的所有資料夾複製到建立的jniLibs
目錄下。( armeabi-v7a,是目前大多數手機支援的架構,app/src/main/jniLibs
是 Android Studio 存放jni庫的預設目錄,可以在app的build.gradle
檔案中通過jniLibs.srcDir
指定其他目錄 ) -
同步Gradle, 完成配置。
現在我們回頭看一下java主程式中的
關鍵程式碼,MainActivity.java下的
public Mat onCameraFrame(CvCameraViewFrame inputFrame)函式,用opencv部署caffe model主要用到了三個主要的函式,Dnn.readNetFromCaffe(proto, weights),讀取網路
Dnn.blobFromImage(frame, IN_SCALE_FACTOR,new Size(IN_WIDTH, IN_HEIGHT),new Scalar(MEAN_VAL, MEAN_VAL, MEAN_VAL), false),mat格式的image轉blob,net.forward(),前向計算,然後再解析前向計算的結果就ok了。
如果使用的是opencv-3.3.0,會發現結果只識別影象的中間的一部分或者是迴歸框位置有偏移,左邊是pc上的,右邊是手機上的,由於手機攝像頭有問題,因此就把實時檢測改成了檢測一張影象,
看下程式碼,
因為ssd-mobilenet網路的輸入是300*300,所以原始碼中是從中間裁剪出一個300*300的區域進行檢測,因此可以先將原始影象縮放到300*300,net.forward()後,解析到檢測結果後將座標框縮放到原來的大小,進行顯示就ok了。
當然,如果使用的是opencv-3.4.2,就可以直接對原圖進行檢測,現在去看一下opencv dnn的原始碼,
先看下opencv-3.3.0的原始碼,路徑opencv-3.3.0/modules/dnn/src下的dnn.cpp檔案,
函式宣告
Mat blobFromImages(const std::vector<Mat>& images_, double scalefactor, Size size,
const Scalar& mean_, bool swapRB)
實現:
再看下opencv-3.4.2的原始碼,路徑opencv-3.4.2/modules/dnn/src下的dnn.cpp檔案,
函式宣告
void blobFromImages(InputArrayOfArrays images_, OutputArray blob_, double scalefactor,
Size size, const Scalar& mean_, bool swapRB, bool crop)
實現:
對比3.3.0和3.4.2可以看到blobFromImages函式增加了bool crop引數,因此使用3.4.2的時候直接設定bool crop=false。
這裡是將影象縮放到300*300後進行檢測的結果。
當然,也可以把ssd-mobilenet換成如ssd-vgg16網路,但是速度會很慢,或者其他的一些目標檢測網路。
後期可以考慮使用更小的移動端框架,如騰訊的ncnn、小米的mace等。