介绍
在本教程中,您将了解如何使用 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.prototxt
MobileNetSSD_deploy.caffemodel
创建一个空的 Android Studio 项目并添加 OpenCV 依赖项
使用 Android Development with OpenCV 教程初始化项目并添加 OpenCV。
制作应用
我们的示例将从相机中获取照片,将其转发到深度网络中,并接收 [0, 1] 范围内的一组矩形、类标识符和置信度值。
- 首先,我们需要添加一个必要的小部件来显示处理后的帧。修改:
app/src/main/res/layout/activity_main.xml
<?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”安卓:layout_width=“match_parent”安卓:layout_height=“match_parent”tools:context=“org.opencv.samples.opencv_mobilenet。主活动”><org.opencv.android.JavaCameraViewandroid:id=“@+id/CameraView”安卓:layout_width=“match_parent”安卓:layout_height=“match_parent”android:visibility=“可见” /></FrameLayout(框架布局)>- 修改以启用全屏模式,设置正确的屏幕方向并允许使用相机。
/app/src/main/AndroidManifest.xml
<?xml version=“1.0” encoding=“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.camera” android:required=“false”/><使用功能 android:name=“android.hardware.camera.autofocus” android:required=“false”/><使用功能 android:name=“android.hardware.camera.front” android:required=“false”/><使用功能 android:name=“android.hardware.camera.front.autofocus” android: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(这个);}@Overridepublic 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);for (int 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] * 行);在检测到的物体周围绘制矩形。新标量(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.prototxt
mobilenet_iter_73000.caffemodel
app/src/main/res/raw
MatOfBytes
std::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);for (int 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] * 行);在检测到的物体周围绘制矩形。新标量(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] 范围内,应在渲染前缩放到图像大小。
- 启动应用程序并带来乐趣!
在线教程
- 麻省理工学院人工智能视频教程 – 麻省理工人工智能课程
- 人工智能入门 – 人工智能基础学习。Peter Norvig举办的课程
- EdX 人工智能 – 此课程讲授人工智能计算机系统设计的基本概念和技术。
- 人工智能中的计划 – 计划是人工智能系统的基础部分之一。在这个课程中,你将会学习到让机器人执行一系列动作所需要的基本算法。
- 机器人人工智能 – 这个课程将会教授你实现人工智能的基本方法,包括:概率推算,计划和搜索,本地化,跟踪和控制,全部都是围绕有关机器人设计。
- 机器学习 – 有指导和无指导情况下的基本机器学习算法
- 机器学习中的神经网络 – 智能神经网络上的算法和实践经验
- 斯坦福统计学习
有需要的小伙伴,可以点击下方链接免费领取或者V扫描下方二维码免费领取🆓
人工智能书籍
- OpenCV(中文版).(布拉德斯基等)
- OpenCV+3计算机视觉++Python语言实现+第二版
- OpenCV3编程入门 毛星云编著
- 数字图像处理_第三版
- 人工智能:一种现代的方法
- 深度学习面试宝典
- 深度学习之PyTorch物体检测实战
- 吴恩达DeepLearning.ai中文版笔记
- 计算机视觉中的多视图几何
- PyTorch-官方推荐教程-英文版
- 《神经网络与深度学习》(邱锡鹏-20191121)
- …
第一阶段:零基础入门(3-6个月)
新手应首先通过少而精的学习,看到全景图,建立大局观。 通过完成小实验,建立信心,才能避免“从入门到放弃”的尴尬。因此,第一阶段只推荐4本最必要的书(而且这些书到了第二、三阶段也能继续用),入门以后,在后续学习中再“哪里不会补哪里”即可。
第二阶段:基础进阶(3-6个月)
熟读《机器学习算法的数学解析与Python实现》并动手实践后,你已经对机器学习有了基本的了解,不再是小白了。这时可以开始触类旁通,学习热门技术,加强实践水平。在深入学习的同时,也可以探索自己感兴趣的方向,为求职面试打好基础。
第三阶段:工作应用
这一阶段你已经不再需要引导,只需要一些推荐书目。如果你从入门时就确认了未来的工作方向,可以在第二阶段就提前阅读相关入门书籍(对应“商业落地五大方向”中的前两本),然后再“哪里不会补哪里”。
有需要的小伙伴,可以点击下方链接免费领取或者V扫描下方二维码免费领取🆓