1. 程式人生 > >使用opencv-dnn移植caffe-model到安卓手機上

使用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,

  • 開啟如下兩個檔案:

    1. AndroidStudioProjects/opencv_mobilenet/app/build.gradle
    2. AndroidStudioProjects/opencv_mobilenet/openCVLibrary330/build.gradle

    修改 compileSdkVersionbuildToolsVersion (根據自己的環境修改)

    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.prototxtMobileNetSSD_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 SDKsdk/native/libs 下的所有資料夾複製到建立的 jniLibs 目錄下。( armeabi-v7a,是目前大多數手機支援的架構,app/src/main/jniLibsAndroid 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等。