如何在Android设备上运行深度网络

介绍

在本教程中,您将了解如何使用 OpenCV 深度学习模块在 Android 设备上运行深度学习网络。教程是为 Android Studio 2022.2.1 编写的。

要求

创建一个空的 Android Studio 项目并添加 OpenCV 依赖项

使用 Android Development with OpenCV 教程初始化项目并添加 OpenCV。

制作应用

我们的示例将从相机中获取照片,将其转发到深度网络中,并接收 [0, 1] 范围内的一组矩形、类标识符和置信度值。

  • 首先,我们需要添加一个必要的小部件来显示处理后的帧。修改:app/src/main/res/layout/activity_main.xml
    <?xml version=“1.0encoding=“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”
    安卓:layout_width=“match_parent”
    安卓:layout_height=“match_parent”
    tools:context=“org.opencv.samples.opencv_mobilenet。主活动”>
    <org.opencv.android.JavaCameraView
    android:id=“@+id/CameraView”
    安卓:layout_width=“match_parent”
    安卓:layout_height=“match_parent”
    android:visibility=“可见” />
    </FrameLayout(框架布局)>
  • 修改以启用全屏模式,设置正确的屏幕方向并允许使用相机。/app/src/main/AndroidManifest.xml
    <?xml version=“1.0encoding=“utf-8”?>
    <清单 xmlns:android=“http://schemas.android.com/apk/res/android”>
    <应用
    android:label=“@string/app_name”>
    <活动
    android:exported=“true”
    android:name=“。主活动”
    android:screenOrientation=“landscape”> <!--屏幕方向-->
    <意图过滤器>
    <操作 android:name=“android.intent.action.MAIN” />
    <类别 android:name=“android.intent.category.LAUNCHER” />
    </意图过滤器>
    </活动>
    </应用>
    <!--允许使用相机-->
    <使用权限 android:name=“android.permission.CAMERA”/>
    <使用功能 android:name=“android.hardware.cameraandroid:required=“false”/>
    <使用功能 android:name=“android.hardware.camera.autofocusandroid:required=“false”/>
    <使用功能 android:name=“android.hardware.camera.frontandroid:required=“false”/>
    <使用功能 android:name=“android.hardware.camera.front.autofocusandroid:required=“false”/>
    </清单>
  • 如有必要,替换以下内容并设置自定义包名称:app/src/main/java/com/example/myapplication/MainActivity.java
软件包 com.example.myapplication;
导入 android.content.Context;
导入 android.content.res.AssetManager;
导入 android.os.Bundle;
导入 android.util.Log;
导入 android.widget.Toast;
导入 org.opencv.android.CameraActivity;
导入 org.opencv.android.CameraBridgeViewBase;
导入 org.opencv.android.CameraBridgeViewBase.CvCameraViewFrame;
导入 org.opencv.android.CameraBridgeViewBase.CvCameraViewListener2;
导入 org.opencv.android.OpenCVLoader;
导入 org.opencv.core.Core;
导入 org.opencv.core.Mat;
导入 org.opencv.core.MatOfByte;
导入 org.opencv.core.Point;
导入 org.opencv.core.Scalar;
导入 org.opencv.core.Size;
进口 org.opencv.dnn.Net;
导入 org.opencv.dnn.Dnn;
导入 org.opencv.imgproc.Imgproc;
导入 java.io.InputStream;
导入 java.io.IOException;
导入 java.util.Collections;
导入 java.util.List;
公共MainActivity 扩展 CameraActivity 实现 CvCameraViewListener2 {
@Override
公共无效 onResume() {
super.onResume();
if (mOpenCvCameraView != 空)
mOpenCvCameraView.enableView();
}
@Override
受保护的 void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
如果 (OpenCVLoader.initLocal()) {
Log.i(TAG, “OpenCV 加载成功”);
} {
Log.e(TAG, “OpenCV 初始化失败!”);
(Toast.makeText(this“OpenCV 初始化失败!”, Toast.LENGTH_LONG)).show();
返回;
}
mModelBuffer = loadFileFromResource(R.raw.mobilenet_iter_73000);
mConfigBuffer = loadFileFromResource(R.raw.deploy);
if (mModelBuffer == null || mConfigBuffer == null) {
Log.e(TAG,“无法从资源加载模型”);
}
Log.i(TAG, “模型文件加载成功”);
net = Dnn.readNet(“caffe”, mModelBuffer, mConfigBuffer);
Log.i(TAG, “网络加载成功”);
setContentView(R.layout.activity_main);
设置摄像头侦听器。
mOpenCvCameraView = (CameraBridgeViewBase)findViewById(R.id.CameraView);
mOpenCvCameraView.setVisibility(CameraBridgeViewBase.VISIBLE);
mOpenCvCameraView.setCvCameraViewListener(这个);
}
@Override
public void onPause()
{
super.onPause();
if (mOpenCvCameraView != 空)
mOpenCvCameraView.disableView();
}
@Override
保护列表<?扩展 CameraBridgeViewBase> getCameraViewList() {
返回集合.singletonList(mOpenCvCameraView);
}
公共无效 onDestroy() {
super.onDestroy();
if (mOpenCvCameraView != 空)
mOpenCvCameraView.disableView();
mModelBuffer.release();
mConfigBuffer.release();
}
加载网络。
public void onCameraViewStarted(int width, int height) {
}
公共垫 onCameraFrame(CvCameraViewFrame inputFrame) {
最终 int IN_WIDTH = 300;
最终 int IN_HEIGHT = 300;
最终浮点WH_RATIO=(浮点)IN_WIDTH/IN_HEIGHT;
最终双IN_SCALE_FACTOR = 0.007843;
最终双MEAN_VAL = 127.5;
最终双精度阈值 = 0.2;
获取新框架
Log.d(TAG, “处理新帧!”);
垫子框架 = inputFrame.rgba();
Imgproc.cvtColor(帧,帧,Imgproc.COLOR_RGBA2RGB);
通过网络转发图像。
Mat blob = Dnn.blobFromImage(frame, IN_SCALE_FACTOR,
new Size(IN_WIDTH, IN_HEIGHT),
new 标量(MEAN_VAL, MEAN_VAL, MEAN_VAL), /*swapRB*/false, /*裁剪*/false);
net.setInput(blob);
垫子检测 = net.forward();
int cols = frame.cols();
int rows = frame.rows();
检测 = detections.reshape(1, (int)detections.total() / 7);
forint i = 0; i < detections.rows(); ++i) {
倍置信度 = detections.get(i, 2)[0];
if (置信度>阈值) {
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] * 行);
int right = (int)(detections.get(i, 5)[0] * cols);
int bottom = (int)(detections.get(i, 6)[0] * 行);
在检测到的物体周围绘制矩形。
Imgproc.rectangle(frame, new Point(left, top), new Point(right, bottom),
标量(0, 255, 0));
字符串标签 = classNames[classId] + “: ” + 置信度;
int[] baseLine = int[1];
大小 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]),
标量(255, 255, 255), Imgproc.FILLED);
写下类名和置信度。
Imgproc.putText(frame, label, new Point(left, top),
Imgproc.FONT_HERSHEY_SIMPLEX, 0.5, 标量(0, 0, 0));
}
}
返回帧;
}
public void onCameraViewStopped() {}
私人MatOfByte loadFileFromResource(int id) {
byte[] 缓冲区;
尝试 {
从应用程序资源加载级联文件
InputStream 是 = getResources().openRawResource(id);
int 大小 = is.available();
缓冲区 = 字节 [大小];
int bytesRead = is.read(缓冲区);
is.close();
} catch (IOException e) {
e.printStackTrace();
Log.e(TAG,“无法从资源ONNX模型!抛出异常:“ + e);
(Toast.makeText(this“无法从资源ONNX模型!”, Toast.LENGTH_LONG)).show();
返回 null;
}
返回 new MatOfByte(buffer);
}
private static final 字符串 TAG = “OpenCV-MobileNet”;
private static final String[] classNames = {“background”
“飞机”, “自行车”, “鸟”“船”
“瓶子”, “公共汽车”, “汽车”, “猫”, “椅子”
“牛”, “餐桌”, “狗”“马”
“摩托车”, “人”“盆栽”
“绵羊”, “沙发”, “火车”“TVMONITOR”};
私人MatOfByte mConfigBuffer;
私人MatOfByte mModelBuffer;
私人净净值;
私人CameraBridgeViewBase mOpenCvCameraView;
}
  • 将下载并放入文件夹中。OpenCV DNN 模型主要用于从文件加载 ML 和 DNN 模型。现代 Android 不允许在没有额外权限的情况下使用它,但提供了 Java API 来从资源中加载字节。此示例使用替代 DNN API,该 API 从内存中缓冲区而不是文件初始化模型。以下函数从资源中读取模型文件,并将其转换为(在 C++ 世界中模拟)适合 OpenCV Java API 的对象:deploy.prototxtmobilenet_iter_73000.caffemodelapp/src/main/res/rawMatOfBytesstd::vector<char>
私人MatOfByte loadFileFromResource(int id) {
byte[] 缓冲区;
尝试 {
从应用程序资源加载级联文件
InputStream 是 = getResources().openRawResource(id);
int 大小 = is.available();
缓冲区 = 字节 [大小];
int bytesRead = is.read(缓冲区);
is.close();
} catch (IOException e) {
e.printStackTrace();
Log.e(TAG,“无法从资源ONNX模型!抛出异常:“ + e);
(Toast.makeText(this“无法从资源ONNX模型!”, Toast.LENGTH_LONG)).show();
返回 null;
}
返回 new MatOfByte(buffer);
}

然后使用以下行完成网络初始化:

mModelBuffer = loadFileFromResource(R.raw.mobilenet_iter_73000);
mConfigBuffer = loadFileFromResource(R.raw.deploy);
if (mModelBuffer == null || mConfigBuffer == null) {
Log.e(TAG,“无法从资源加载模型”);
}
Log.i(TAG, “模型文件加载成功”);
net = Dnn.readNet(“caffe”, mModelBuffer, mConfigBuffer);
Log.i(TAG, “网络加载成功”);

另请参阅有关资源的 Android 文档

  • 看看 DNN 模型输入是如何准备的,推理结果是如何解释的:
Mat blob = Dnn.blobFromImage(frame, IN_SCALE_FACTOR,
new Size(IN_WIDTH, IN_HEIGHT),
new 标量(MEAN_VAL, MEAN_VAL, MEAN_VAL), /*swapRB*/false, /*裁剪*/false);
net.setInput(blob);
垫子检测 = net.forward();
int cols = frame.cols();
int rows = frame.rows();
检测 = detections.reshape(1, (int)detections.total() / 7);
forint i = 0; i < detections.rows(); ++i) {
倍置信度 = detections.get(i, 2)[0];
if (置信度>阈值) {
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] * 行);
int right = (int)(detections.get(i, 5)[0] * cols);
int bottom = (int)(detections.get(i, 6)[0] * 行);
在检测到的物体周围绘制矩形。
Imgproc.rectangle(frame, new Point(left, top), new Point(right, bottom),
标量(0, 255, 0));
字符串标签 = classNames[classId] + “: ” + 置信度;
int[] baseLine = int[1];
大小 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]),
标量(255, 255, 255), Imgproc.FILLED);
写下类名和置信度。
Imgproc.putText(frame, label, new Point(left, top),
Imgproc.FONT_HERSHEY_SIMPLEX, 0.5, 标量(0, 0, 0));
}
}

Dnn.blobFromImage将相机帧转换为神经网络输入张量。应用调整大小和统计归一化。网络输出张量的每一行都包含有关一个检测到对象的信息,顺序如下:范围 [0, 1] 的置信度、类 ID、左、上、右、下框坐标。所有坐标都在 [0, 1] 范围内,应在渲染前缩放到图像大小。

  • 启动应用程序并带来乐趣!

    11_demo.jpg

   在线教程

有需要的小伙伴,可以点击下方链接免费领取或者V扫描下方二维码免费领取🆓

请添加图片描述

人工智能书籍

第一阶段:零基础入门(3-6个月)

新手应首先通过少而精的学习,看到全景图,建立大局观。 通过完成小实验,建立信心,才能避免“从入门到放弃”的尴尬。因此,第一阶段只推荐4本最必要的书(而且这些书到了第二、三阶段也能继续用),入门以后,在后续学习中再“哪里不会补哪里”即可。

第二阶段:基础进阶(3-6个月)

熟读《机器学习算法的数学解析与Python实现》并动手实践后,你已经对机器学习有了基本的了解,不再是小白了。这时可以开始触类旁通,学习热门技术,加强实践水平。在深入学习的同时,也可以探索自己感兴趣的方向,为求职面试打好基础。

第三阶段:工作应用

这一阶段你已经不再需要引导,只需要一些推荐书目。如果你从入门时就确认了未来的工作方向,可以在第二阶段就提前阅读相关入门书籍(对应“商业落地五大方向”中的前两本),然后再“哪里不会补哪里”。

 有需要的小伙伴,可以点击下方链接免费领取或者V扫描下方二维码免费领取🆓

在这里插入图片描述

 

  • 20
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值