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;
2.pc上训练好的网络模型,这里我使用的是caffe版的ssd_mobilenet_v1,下载地址https://github.com/chuanqi305/MobileNet-SSD;
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;
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文件会比较大。
具体过程,参照https://blog.csdn.net/tobacco5648/article/details/51615434:
在安卓工程 /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
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等。