深度神经网络——如何在 Android 设备上运行深度网络 OpenCV v4.8.0

上一个教程利用 OpenVINO 使用 OpenCV

下一个教程YOLO DNNs

原作者Dmitry Kurtaev
兼容性OpenCV >= 3.3

简介

在本教程中,你将了解如何使用 OpenCV 深度学习模块在安卓设备上运行深度学习网络。

本教程针对以下版本的相应软件编写:

  • Android Studio 2.3.3
  • OpenCV 3.3.0+

软件要求

创建一个空的 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 在下一个窗口中建议的所有功能。

在这里插入图片描述

在这里插入图片描述

  • 打开两个文件:
  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 模块依赖。

在这里插入图片描述

在这里插入图片描述

  • 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.prototxtMobileNetSSD_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!

在这里插入图片描述

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值