上一个教程 :利用 OpenVINO 使用 OpenCV
下一个教程 :YOLO DNNs
原作者 | Dmitry Kurtaev |
---|---|
兼容性 | OpenCV >= 3.3 |
简介
在本教程中,你将了解如何使用 OpenCV 深度学习模块在安卓设备上运行深度学习网络。
本教程针对以下版本的相应软件编写:
- Android Studio 2.3.3
- OpenCV 3.3.0+
软件要求
- 从 https://developer.android.com/studio 下载并安装 Android Studio。
- 从 https://github.com/opencv/opencv/releases 获取最新的预编译 OpenCV for Android 版本并解压(例如,
opencv-4.X.Y-android-sdk.zip
)。 - 从 https://github.com/chuanqi305/MobileNet-SSD 下载 MobileNet 对象检测模型。我们需要配置文件
MobileNetSSD_deploy.prototxt
和权重文件MobileNetSSD_deploy.caffemodel
。
创建一个空的 Android Studio 项目
-
打开 Android Studio。启动一个新项目。我们将其称为
opencv_mobilenet
。
-
保留默认目标设置。
-
使用 "Empty Template "模板。将活动命名为
MainActivity
,并使用相应的布局activity_main
。
- 等待项目创建完成。转到
运行->编辑配置
。选择USB 设备
作为运行的目标设备。
插入设备并运行项目。在我们进行下一步之前,它应已安装并成功启动。
注意
如遇到问题,请阅读《Android 开发入门》。
添加 OpenCV 依赖关系
- 进入
File->New->Import module
(文件->新建->导入模块),并提供unpacked_OpenCV_package/sdk/java
的路径。模块名称会自动检测。禁用 Android Studio 在下一个窗口中建议的所有功能。
- 打开两个文件:
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 模块依赖。
- 从
unpacked_OpenCV_package/apk
中安装一个合适的 OpenCV 管理器到目标设备。
adb install OpenCV_3.3.0_Manager_3.30_armeabi-v7a.apk
恭喜您 现在我们可以使用 OpenCV 制作一个示例了。
制作样本
我们的示例将从摄像头获取图片,将其转发到深度网络,并接收一组矩形、类别标识符和置信度值(范围为 [0, 1]
)。
- 首先,我们需要添加一个必要的部件,用于显示处理过的图片。修改
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="org.opencv.samples.opencv_mobilenet.MainActivity">
<org.opencv.android.JavaCameraView
android:id="@+id/CameraView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="visible" />
</FrameLayout>
- 将下载的
MobileNetSSD_deploy.prototxt
和MobileNetSSD_deploy.caffemodel
放入app/build/intermediates/assets/debug
文件夹。 - 修改
/app/src/main/AndroidManifest.xml
以启用全屏模式、设置正确的屏幕方向并允许使用摄像头。
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="org.opencv.samples.opencv_mobilenet"
>
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.AppCompat.NoActionBar"> <!--Full screen mode-->
<activity
android:exported="true"
android:name=".MainActivity"
android:screenOrientation="landscape"> <!--Screen orientation-->
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
<!--Allow to use a camera-->
<uses-permission android:name="android.permission.CAMERA"/>
<uses-feature android:name="android.hardware.camera" android:required="false"/>
<uses-feature android:name="android.hardware.camera.autofocus" android:required="false"/>
<uses-feature android:name="android.hardware.camera.front" android:required="false"/>
<uses-feature android:name="android.hardware.camera.front.autofocus" android:required="false"/>
</manifest>
- 替换
app/src/main/java/org/opencv/samples/opencv_mobilenet/MainActivity.java
的内容:
package org.opencv.samples.opencv_mobilenet;
import android.content.Context;
import android.content.res.AssetManager;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import org.opencv.android.BaseLoaderCallback;
import org.opencv.android.CameraBridgeViewBase;
import org.opencv.android.CameraBridgeViewBase.CvCameraViewFrame;
import org.opencv.android.CameraBridgeViewBase.CvCameraViewListener2;
import org.opencv.android.LoaderCallbackInterface;
import org.opencv.android.OpenCVLoader;
import org.opencv.core.Core;
import org.opencv.core.Mat;
import org.opencv.core.Point;
import org.opencv.core.Scalar;
import org.opencv.core.Size;
import org.opencv.dnn.Net;
import org.opencv.dnn.Dnn;
import org.opencv.imgproc.Imgproc;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
public class MainActivity extends AppCompatActivity implements CvCameraViewListener2 {
// 初始化 OpenCV 管理器。
private BaseLoaderCallback mLoaderCallback = new BaseLoaderCallback(this) {
@Override
public void onManagerConnected(int status) {
switch (status) {
case LoaderCallbackInterface.SUCCESS: {
Log.i(TAG, "OpenCV loaded successfully");
mOpenCvCameraView.enableView();
break;
}
default: {
super.onManagerConnected(status);
break;
}
}
}
};
@Override
public void onResume() {
super.onResume();
OpenCVLoader.initAsync(OpenCVLoader.OPENCV_VERSION, this, mLoaderCallback);
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// 设置相机监听器。
mOpenCvCameraView = (CameraBridgeViewBase)findViewById(R.id.CameraView);
mOpenCvCameraView.setVisibility(CameraBridgeViewBase.VISIBLE);
mOpenCvCameraView.setCvCameraViewListener(this);
}
// 加载网络。
public void onCameraViewStarted(int width, int height) {
String proto = getPath("MobileNetSSD_deploy.prototxt", this);
String weights = getPath("MobileNetSSD_deploy.caffemodel", this);
net = Dnn.readNetFromCaffe(proto, weights);
Log.i(TAG, "Network loaded successfully");
}
public Mat onCameraFrame(CvCameraViewFrame inputFrame) {
final int IN_WIDTH = 300;
final int IN_HEIGHT = 300;
final float WH_RATIO = (float)IN_WIDTH / IN_HEIGHT;
final double IN_SCALE_FACTOR = 0.007843;
final double MEAN_VAL = 127.5;
final double THRESHOLD = 0.2;
// 获取新帧
Mat frame = inputFrame.rgba();
Imgproc.cvtColor(frame, frame, Imgproc.COLOR_RGBA2RGB);
// 通过网络转发图像。
Mat blob = Dnn.blobFromImage(frame, IN_SCALE_FACTOR,
new Size(IN_WIDTH, IN_HEIGHT),
new Scalar(MEAN_VAL, MEAN_VAL, MEAN_VAL), /*swapRB*/false, /*crop*/false);
net.setInput(blob);
Mat detections = net.forward();
int cols = frame.cols();
int rows = frame.rows();
detections = detections.reshape(1, (int)detections.total() / 7);
for (int i = 0; i < detections.rows(); ++i) {
double confidence = detections.get(i, 2)[0];
if (confidence > THRESHOLD) {
int classId = (int)detections.get(i, 1)[0];
int left = (int)(detections.get(i, 3)[0] * cols);
int top = (int)(detections.get(i, 4)[0] * rows);
int right = (int)(detections.get(i, 5)[0] * cols);
int bottom = (int)(detections.get(i, 6)[0] * rows);
// 在检测对象周围绘制矩形。
Imgproc.rectangle(frame, new Point(left, top), new Point(right, bottom),
new Scalar(0, 255, 0));
String label = classNames[classId] + ": " + confidence;
int[] baseLine = new int[1];
Size labelSize = Imgproc.getTextSize(label, Imgproc.FONT_HERSHEY_SIMPLEX, 0.5, 1, baseLine);
// 绘制标签背景。
Imgproc.rectangle(frame, new Point(left, top - labelSize.height),
new Point(left + labelSize.width, top + baseLine[0]),
new Scalar(255, 255, 255), Imgproc.FILLED);
// 写入类名和置信度。
Imgproc.putText(frame, label, new Point(left, top),
Imgproc.FONT_HERSHEY_SIMPLEX, 0.5, new Scalar(0, 0, 0));
}
}
return frame;
}
public void onCameraViewStopped() {}
// 将文件上传到存储空间并返回路径。
private static String getPath(String file, Context context) {
AssetManager assetManager = context.getAssets();
BufferedInputStream inputStream = null;
try {
// Read data from assets.
inputStream = new BufferedInputStream(assetManager.open(file));
byte[] data = new byte[inputStream.available()];
inputStream.read(data);
inputStream.close();
// 在存储器中创建副本文件。
File outFile = new File(context.getFilesDir(), file);
FileOutputStream os = new FileOutputStream(outFile);
os.write(data);
os.close();
// 返回可通过普通方式读取的文件路径。
return outFile.getAbsolutePath();
} catch (IOException ex) {
Log.i(TAG, "Failed to upload a file");
}
return "";
}
private static final String TAG = "OpenCV/Sample/MobileNet";
private static final String[] classNames = {"background",
"aeroplane", "bicycle", "bird", "boat",
"bottle", "bus", "car", "cat", "chair",
"cow", "diningtable", "dog", "horse",
"motorbike", "person", "pottedplant",
"sheep", "sofa", "train", "tvmonitor"};
private Net net;
private CameraBridgeViewBase mOpenCvCameraView;
}
- Launch an application and make a fun!