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

news2024/11/18 5:52:20

介绍

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

要求

  • 从 https://developer.android.com/studio 下载并安装 Android Studio。
  • 从 Releases · opencv/opencv · GitHub 获取最新的预构建的 OpenCV for Android 版本并将其解压缩(例如,需要最低版本 4.9)。opencv-4.X.Y-android-sdk.zip
  • 从 GitHub - chuanqi305/MobileNet-SSD: Caffe implementation of Google MobileNet SSD detection network, with pretrained weights on VOC0712 and mAP=0.727. 下载 MobileNet 对象检测模型。配置文件和模型权重是必需的。MobileNetSSD_deploy.prototxtMobileNetSSD_deploy.caffemodel

创建一个空的 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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1390219.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

项目压测优化实践思路

&#x1f44f;作者简介&#xff1a;大家好&#xff0c;我是爱吃芝士的土豆倪&#xff0c;24届校招生Java选手&#xff0c;很高兴认识大家&#x1f4d5;系列专栏&#xff1a;Spring原理、JUC原理、Kafka原理、分布式技术原理、数据库技术&#x1f525;如果感觉博主的文章还不错的…

一个完整的 Web 请求到底发生了什么

一、从输入一个网址开始 当我们在浏览器输入一个网址&#xff0c;然后按下回车&#xff0c;接下来浏览器显示了页面。网速好的话这之间可能就一秒&#xff0c;但在这一秒内到底发生了什么&#xff1f; 本文主要内容是试图记录一个完整 Web 请求的详细过程&#xff0c;从用户在…

计算机毕业设计 基于Java的手机销售网站的设计与实现 Java实战项目 附源码+文档+视频讲解

博主介绍&#xff1a;✌从事软件开发10年之余&#xff0c;专注于Java技术领域、Python人工智能及数据挖掘、小程序项目开发和Android项目开发等。CSDN、掘金、华为云、InfoQ、阿里云等平台优质作者✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精…

python学习笔记10(选择结构2、循环结构1)

&#xff08;一&#xff09;选择结构2 1、if……else……语句 #&#xff08;1&#xff09;基本格式 numbereval(input("请输入您的6位中奖号码&#xff1a;")) if number123456:print("恭喜您&#xff0c;中奖了") else:print("未中奖")#&…

为何我选择山海鲸可视化:五大优势解析

在众多的可视化产品中&#xff0c;我选择了山海鲸可视化&#xff0c;这并非偶然。在对比了其他同类产品后&#xff0c;我发现山海鲸可视化具有许多独特的优势和特点&#xff0c;使得它成为了我心目中的理想选择。下面我简单说一下我选择这款产品的几大原因&#xff0c;希望对在…

HBase 复制、备份、迁移

一、行业分享 分享1 阿里云 BDS-HBase 《HBase高效一键迁移的设计与实践.pdf》 https://developer.aliyun.com/live/730 https://developer.aliyun.com/article/704972 https://developer.aliyun.com/article/704977 https://blog.csdn.net/u013411339/article/details/10118…

音频和视频基础知识

声音 什么是声音&#xff1a; 声音是由物体振动产生的&#xff0c;物体发生振动&#xff0c;对周围的空气产生挤压&#xff0c;从而产生声音。声音是一种压力波&#xff0c;使周围的空气产生疏密变化&#xff0c;形成疏密相间的纵波&#xff0c;由此产生了声波。 声波三要素&…

多行SQL转成单行SQL

如下图所示 将以上多行SQL转成单行SQL 正则表达式如下 (?s)$[^a-zA-Z()0-9]*结果如下 灵活使用,也未必只能使用Sublime Text 提供了一个在线工具

SQL Server数据库镜像安装

搭建SQL Server数据库镜像&#xff08;证书&#xff09; 本次实验进行SQL server数据库镜像的搭建&#xff0c;采用证书的搭建模式 以下是搭建SQL server数据库镜像的环境设置 数据库 系统 IP 角色 计算机名 SqlServer2008R2 Server 2008R2 192.168.210.125 主体 Win-…

现代雷达车载应用——第3章 MIMO雷达技术 3.4节 自动驾驶使用的高分辨成像雷达

经典著作&#xff0c;值得一读&#xff0c;英文原版下载链接【免费】ModernRadarforAutomotiveApplications资源-CSDN文库。 3.4 自动驾驶使用的高分辨成像雷达 如今&#xff0c;许多专为ADAS功能设计的汽车雷达收发器&#xff0c;如NXP半导体的MR3003和德州仪器的AWR2243&…

网络编程day3

思维导图 下载和传输

AI嵌入式K210项目(4)-FPIOA

文章目录 前言一、FPIOA是什么&#xff1f;二、FPIOA代码分析总结 前言 磨刀不误砍柴工&#xff0c;在正式开始学习之前&#xff0c;我们先来了解下K210自带的FPIOA&#xff0c;这个概念可能与我们之前学习STM32有很多不同&#xff0c;STM32每个引脚都有特定的功能&#xff0c…

Web前端 ---- 【Vue3】computed计算属性和watch侦听属性(侦听被ref和reactive包裹的数据)

目录 前言 computed watch watch侦听ref数据 ref简单数据类型 ref复杂数据类型 watch侦听reactive数据 前言 本文介绍在vue3中的computed计算属性和watch侦听属性。介绍watch如何侦听被ref和reactive包裹的数据 computed 在vue3中&#xff0c;计算属性computed也是组合式…

Redis分布式锁--java实现

文章目录 Redis分布式锁方案&#xff1a;SETNX EXPIRE基本原理比较好的实现会产生四个问题 几种解决原子性的方案方案&#xff1a;SETNX value值是&#xff08;系统时间过期时间&#xff09;方案&#xff1a;使用Lua脚本(包含SETNX EXPIRE两条指令)方案&#xff1a;SET的扩展…

pod控制器的作用

pod控制器的作用 1、动态pv和pvc deployment是控制器 pod空气器:工作负载&#xff0c;workload用于管理pod的中间层&#xff0c;确保podi资源符合预期的状态 预期状态 1、副本数 2、容器重启策略 3、镜像拉取策略 pod、出现故障时重启等等 pod的控制器类型 1、replic…

Vue3组件库开发 之Button(1)

需求分析&#xff1a; Button 组件大部分关注样式&#xff0c;没有交互 根本分析可以得到具体的属性列表&#xff1a; type:不同的样式(Default,Primary,Danger,Info,Success,Warning) plain:样式的不同展现模式boolean round:圆角boolean circle:圆形按钮&#xff0c;适合图标…

【目标跟踪】跨相机如何匹配像素

文章目录 前言一、计算思路二、代码三、结果 前言 本本篇博客介绍一种非常简单粗暴的方法&#xff0c;做到跨相机像素匹配。已知各相机内外参&#xff0c;计算共视区域像素投影&#xff08;不需要计算图像特征&#xff09;。废话不多说&#xff0c;直接来&#xff0c;见下图。…

前端安全专题

xss (Cross Site Scripting) 跨站脚本攻击 原理 通常指黑客通过"HTML注入"篡改了网页&#xff0c;插入了恶意的脚本&#xff0c;从而在用户浏览网页时&#xff0c;控制用户浏览器的一种攻击。 常见攻击类型 存储型XSS 攻击者将恶意的 JavaScript 脚本存储在网站…

【网络工程师】NAT与动态路由

一、NAT网络地址转换 1、NAT&#xff1a;Network Address Translations 网络地址转换 2、ip地址问题&#xff1a;ipv4地址严重不够用了&#xff08;A、B、C类可以使用 D组播 E科研&#xff09; 3、解决&#xff1a;把IP地址分为了公网IP和私网IP 公网IP只能在公网上使用 私网…

手把手教你SWOT分析!建议收藏

最近&#xff0c;我一直为一件事情感到困扰。那家位于市中心的西点店生意越来越好&#xff0c;甚至已经开了两家分店&#xff0c;但是挣来的钱还不足够买房子。于是最近&#xff0c;我被这如火如荼的奶茶市场所吸引&#xff0c;想要利用已有的资源开一家奶茶店。但是我不确定这…