当前位置: 首页 > news >正文

将yolov5s部署到安卓上实战经验总结

最近需要在手机端实现一个目标检测的功能,于是选择了小巧又在目标检测方面表现很好的yolov5s,官网下载yolov5代码,用自己做的数据集进行了训练,然后把模型转换成torchscript格式,这些过程网上都有很多讲解,不再赘述。主要讲一下在安卓上推理的代码。

pytorch在安卓上的使用官方demo,主要代码如下:

    Bitmap bitmap = null;Module module = null;try {// creating bitmap from packaged into app android asset 'image.jpg',// app/src/main/assets/image.jpgbitmap = BitmapFactory.decodeStream(getAssets().open("image.jpg"));// loading serialized torchscript module from packaged into app android asset model.pt,// app/src/model/assets/model.ptmodule = LiteModuleLoader.load(assetFilePath(this, "model.pt"));} catch (IOException e) {Log.e("PytorchHelloWorld", "Error reading assets", e);finish();}// showing image on UIImageView imageView = findViewById(R.id.image);imageView.setImageBitmap(bitmap);// preparing input tensorfinal Tensor inputTensor = TensorImageUtils.bitmapToFloat32Tensor(bitmap,TensorImageUtils.TORCHVISION_NORM_MEAN_RGB, TensorImageUtils.TORCHVISION_NORM_STD_RGB, MemoryFormat.CHANNELS_LAST);// running the modelfinal Tensor outputTensor = module.forward(IValue.from(inputTensor)).toTensor();// getting tensor content as java array of floatsfinal float[] scores = outputTensor.getDataAsFloatArray();// searching for the index with maximum scorefloat maxScore = -Float.MAX_VALUE;int maxScoreIdx = -1;for (int i = 0; i < scores.length; i++) {if (scores[i] > maxScore) {maxScore = scores[i];maxScoreIdx = i;}}String className = ImageNetClasses.IMAGENET_CLASSES[maxScoreIdx];

但是这段代码中用的模型不是yolov5,直接用于yolov5的模型是跑不通的,首先计算outputTensor的时候直接把模型输出toTensor(),这个会报错,报错讲说期望Tensor类型但是给了个Tuple,由此可知模型的输出IValue其内置类型是Tuple,于是toTuple然后取第一个元素再toTensor()就可以了。原因是yolov5的输出在Tensor外面又包装了一层,组成了一个Tuple。

然后是结果scores的解析方法,对于yolov5,当有n个目标类别的时候,这个scores的含义是[x,y,w,h,conf,type1score,type2score,......typenscore,x,y,w,h,conf,type1score,type2score,....typenscore......],一直重复25200次,其中x,y是目标框的中心坐标,w,h是目标框的宽高,conf是框的置信度,后面分别是n个类别的得分。所以自然不能用上述代码中的方法取结果。

等我修改完这两处之后,代码可以正常运行,但奇怪的是在python上运行训练好的模型,结果是非常好的,基本95%的时候都可以获取到目标物体在图像中的最小外接矩形,其它5%也只是偏移一点点,但到了手机上,这个结果常常不准确,检测框没有包住目标物体的所有部分是很大概率的事,一开始我怀疑是模型转换的时候丢失了精度,但后来发现转换成torchscript并没有量化,并且在不量化的情况下,模型没必要把一些参数进行修改,这不是努力降精度吗?不合常理。于是仔细看了下yolov5源码中的推理部分,发现图片在进入模型之前,进行了/255的归一化操作。于是乎问题聚集到了原来代码中的这一行:

TensorImageUtils.bitmapToFloat32Tensor(bitmap,
        TensorImageUtils.TORCHVISION_NORM_MEAN_RGB, TensorImageUtils.TORCHVISION_NORM_STD_RGB, MemoryFormat.CHANNELS_LAST);
经过了多次调试,终于发现这个函数其实是对bitmap的像素值进行了/255的归一化后,再使用传入的均值数组和标准差数组对归一化过的数值进行了Z-score归一化。Z-score归一化的目的原本是为了让数据符合标准正态分布,但是进入TensorImageUtils类可以看到:

public static float[] TORCHVISION_NORM_MEAN_RGB = new float[]{0.485F, 0.456F, 0.406F};
public static float[] TORCHVISION_NORM_STD_RGB = new float[]{0.229F, 0.224F, 0.225F};

即使用了事先固定的均值和标准差,而不是传入数据的均值和标准差,所以不一定可以得到符合标准正态分布的数据。但是这不重要,因为我要的是直接不作Z-score归一化,只/255就可以了,于是我自定义了一个值为0的均值数组,和值为1的标准差数组,然后传入这个函数,就保证了结果相当于没有做Z-score归一化。原因是Z-score归一化公式如下:

x* = ( x − μ ) / σ

我的最终关键代码如下:注意处理结果的部分,因为我是图片中一定只有0或1个目标检测框,所以我没有使用NMS(非极大值抑制)算法。如果你的图片中有多个检测框,则必须用NMS。我只有两个类别,所以idcnt计算是score.length/7,也就是score.length/(4+1+类别数)。

model = Module.load(path);float[] TORCHVISION_NORM_MEAN_RGB = new float[]{0F, 0F, 0F};float[] TORCHVISION_NORM_STD_RGB = new float[]{1F, 1F, 1F};Tensor inputTensor = TensorImageUtils.bitmapToFloat32Tensor(newBitmap, TORCHVISION_NORM_MEAN_RGB, TORCHVISION_NORM_STD_RGB);// running the modelIValue value = IValue.from(inputTensor);Tensor outputTensor_ori = model.forward(value).toTuple()[0].toTensor();// getting tensor content as java array of floatsfloat[] scores = outputTensor_ori.getDataAsFloatArray();// searching for the index with maximum scorefloat maxScore = 0.85F;int maxScoreIdx = -1;int idcnt = scores.length / 7;for (int i = 0; i < idcnt; i++) {int exist = i*7+4;int j = exist+1+type;if (scores[exist] > 0.25F && scores[j] > maxScore) {maxScore = scores[j];maxScoreIdx = i;}}if (maxScoreIdx == -1) {return false;}float tx = scores[maxScoreIdx*7];float ty = scores[maxScoreIdx*7+1];float tw = scores[maxScoreIdx*7+2];float th = scores[maxScoreIdx*7+3];float ltx = (tx-tw/2);float lty = (ty-th/2);float rbx = (tx+tw/2);float rby = (ty+th/2);drawROI(newBitmap, (int)ltx, (int)lty, (int)rbx, (int)rby);

相关文章:

将yolov5s部署到安卓上实战经验总结

最近需要在手机端实现一个目标检测的功能&#xff0c;于是选择了小巧又在目标检测方面表现很好的yolov5s&#xff0c;官网下载yolov5代码&#xff0c;用自己做的数据集进行了训练&#xff0c;然后把模型转换成torchscript格式&#xff0c;这些过程网上都有很多讲解&#xff0c;…...

算法日记————对顶堆(4道题)

对顶堆的作用主要在于动态维护第k大的数字&#xff0c;考虑使用两个优先队列&#xff0c;一个大9999999999根堆一个小根堆&#xff0c;小根堆维护大于等于第k大的数字的数&#xff0c;它的堆顶就是堆内最小&#xff0c;第k大的数字&#xff0c;另外一个大根堆维护小于等于k的数…...

【I.MX6ULL移植】Ubuntu-base根文件系统移植

1.下载Ubuntu16.04根文件系统 http://cdimage.ubuntu.com/ 1 2 3 4 5 2.解压ubuntu base 根文件系统 为了存放 ubuntu base 根文件系统&#xff0c;先在 PC 的 Ubuntu 系统中的 nfs 目录下创建一个名为 ubuntu_rootfs 的目录&#xff0c;命令如下&#xff1a; 【注意&…...

unity3d for web

时光噶然 一晃好多年过去了&#xff08;干了5年的u3d游戏&#xff09;&#xff0c;记得最后一次使用的版本好像是 unity 2017。 那个是 unity3d for webgl 还需要装个插件。用起来很蛋疼。 最近做一个小项目 在选择是用 Layabox 还是 cocosCreate 的时候 我想起了老战友 Uni…...

大宋咨询(深圳问卷调研)关于消费者研究的流程

消费者研究是一项至关重要的任务&#xff0c;它有助于企业了解目标市场的需求、偏好和行为&#xff0c;从而制定更加精准的营销策略。在执行消费者研究时&#xff0c;需要遵循一定的步骤和方法&#xff0c;以确保研究的准确性和有效性。开展消费者研究需要一系列的步骤和方法。…...

STM32看似无法唤醒的一种异常现象分析

1. 引言 STM32 G0 系列产品具有丰富的外设和强大的处理性能以及良好的低功耗特性&#xff0c;被广泛用于各类工业产品中&#xff0c;包括一些需要低功耗需求的应用。 2. 问题描述 用户使用 STM32G0B1 作为汽车多媒体音响控制器的控制芯片&#xff0c;用来作为收音机频道存贮…...

iOS - Runtime-isa详解(位域、union(共用体)、位运算)

文章目录 iOS - Runtime-isa详解&#xff08;位域、union&#xff08;共用体&#xff09;、位运算&#xff09;前言1. 位域介绍1.1 思路1.2 示例 - 结构体1.3 示例 - union&#xff08;共用体&#xff09;1.3.1 说明 1.4 结构体 对比 union&#xff08;共用体&#xff09; 2. a…...

使用VSCode搭建Vue 3开发环境

使用VSCode搭建Vue 3开发环境 Vue 3是一种流行的前端JavaScript框架,它提供了响应式的数据绑定和组合式的API。Visual Studio Code(VSCode)是一个轻量级但功能强大的源代码编辑器,支持多种语言开发。本文将引导您完成使用VSCode搭建Vue 3开发环境的步骤。 1. 下载和安装V…...

深度学习中的模型蒸馏技术:实现流程、作用及实践案例

在深度学习领域&#xff0c;模型压缩与部署是一项重要的研究课题&#xff0c;而模型蒸馏便是其中一种有效的方法。 模型蒸馏&#xff08;Model Distillation&#xff09;最初由Hinton等人在2015年提出&#xff0c;其核心思想是通过知识迁移的方式&#xff0c;将一个复杂的大模型…...

Java服务运行在Linux----维护常用命令

想起来哪些再添加上去 查看Java程序进程 jps -l 查出进程后根据pid 查询程序所在目录 pwdx 31313 根据端口查找PID 根据pid杀死程序 kill -p 31313 查看目录下所有包含9527的文件 grep -rn 9527 查看磁盘空间 查找文件名"nginx"文件或模糊查找"*nginx*&quo…...

夜晚水闸3D可视化:科技魔法点亮水利新纪元

在宁静的夜晚&#xff0c;当城市的霓虹灯逐渐暗淡&#xff0c;你是否曾想过&#xff0c;那些默默守护着城市安全的水闸&#xff0c;在科技的魔力下&#xff0c;正焕发出别样的光彩&#xff1f;今天&#xff0c;就让我们一起走进夜晚水闸3D模型&#xff0c;感受科技为水利带来的…...

从零开始的软件开发实战:互联网医院APP搭建详解

今天&#xff0c;笔者将以“从零开始的软件开发实战&#xff1a;互联网医院APP搭建详解”为主题&#xff0c;深入探讨互联网医院APP的开发过程和关键技术。 第一步&#xff1a;需求分析和规划 互联网医院APP的主要功能包括在线挂号、医生预约、医疗咨询、健康档案管理等。我们…...

【深度学习】YOLO检测器的发展历程

YOLO检测器的发展历程 YOLO&#xff08;You Only Look Once&#xff09;检测器是一种流行的实时对象检测系统&#xff0c;以其速度和准确性而闻名。自2016年首次推出以来&#xff0c;YOLO已经成为计算机视觉领域的一个重要里程碑。在本博客中&#xff0c;我们将探讨YOLO检测器…...

C语言--编译和链接

1.翻译环境 计算机能够执行二进制指令&#xff0c;我们的电脑不会直接执行C语言代码&#xff0c;编译器把代码转换成二进制的指令&#xff1b; 我们在VS上面写下printf("hello world");这行代码的时候&#xff0c;经过翻译环境&#xff0c;生成可执行的exe文件&…...

实现使用C#代码完成wifi的切换和连接功能

实现使用C#代码完成wifi的切换和连接功能 代码如下&#xff1a; namespace Wifi连接器 {public partial class Form1 : Form{private List<Wlan.WlanAvailableNetwork> NetWorkList new List<Wlan.WlanAvailableNetwork>();private WlanClient.WlanInterface Wla…...

Mac添加和关闭开机应用

文章目录 mac添加和关闭开机应用添加开机应用删除/查看 mac添加和关闭开机应用 添加开机应用 删除/查看 打开&#xff1a;系统设置–》通用–》登录项–》查看登录时打开列表 选中打开项目&#xff0c;点击“-”符号...

QT QInputDialog弹出消息框用法

使用QInputDialog类的静态方法来弹出对话框获取用户输入&#xff0c;缺点是不能自定义按钮的文字&#xff0c;默认为OK和Cancel&#xff1a; int main(int argc, char *argv[]) {QApplication a(argc, argv);bool isOK;QString text QInputDialog::getText(NULL, "Input …...

Unity3d使用Jenkins自动化打包(Windows)(一)

文章目录 前言一、安装JDK二、安装Jenkins三、Jenkins插件安装和使用基础操作 实战一基础操作 实战二 四、离线安装总结 前言 本篇旨在介绍基础的安装和操作流程&#xff0c;只需完成一次即可。后面的篇章将深入探讨如何利用Jenkins为Unity项目进行打包。 一、安装JDK 1、进入…...

HarmonyOS 应用开发之Want的定义与用途

Want 是一种对象&#xff0c;用于在应用组件之间传递信息。 其中&#xff0c;一种常见的使用场景是作为 startAbility() 方法的参数。例如&#xff0c;当UIAbilityA需要启动UIAbilityB并向UIAbilityB传递一些数据时&#xff0c;可以使用Want作为一个载体&#xff0c;将数据传递…...

enscan自动化主域名信息收集

enscan下载 Releases wgpsec/ENScan_GO (github.com) 能查的分类 实操&#xff1a; 首先打开linux 的虚拟机、 然后把下面这个粘贴到虚拟机中 解压后打开命令行 初始化 ./enscan-0.0.16-linux-amd64 -v 命令参数如下 oppo信息收集 运行下面代码时 先去配置文件把coo…...

别再死记硬背了!用Kahn算法搞定LeetCode 207课程表,保姆级C++代码逐行解析

从课程表到任务调度&#xff1a;Kahn算法在LeetCode 207中的实战应用 每次打开LeetCode看到那道课程表问题&#xff0c;你是不是也感到一阵头疼&#xff1f;先修课程、依赖关系、环状检测……这些概念堆在一起&#xff0c;简直比大学选课系统还让人崩溃。但别担心&#xff0c;今…...

SemanticKITTI数据集评测:DarkNet53Seg、PointNet++等模型谁更强?附复现代码

SemanticKITTI点云语义分割实战&#xff1a;模型选型与性能优化指南 点云语义分割技术正在重塑自动驾驶、机器人导航和三维场景理解等领域的研究范式。作为该领域最具挑战性的基准之一&#xff0c;SemanticKITTI数据集凭借其大规模、高密度标注和真实场景多样性&#xff0c;已成…...

STM32智能婴儿床系统设计与实现

基于STM32的智能婴儿床系统设计1. 项目概述1.1 系统架构本智能婴儿床系统采用模块化设计架构&#xff0c;以STM32F103RCT6微控制器为核心处理单元&#xff0c;集成多种传感器模块和执行机构。系统通过蓝牙与手机APP建立双向通信&#xff0c;实现环境参数监测、异常报警和远程控…...

3步解锁数据自由:WeChatMsg让聊天记录成为数字资产

3步解锁数据自由&#xff1a;WeChatMsg让聊天记录成为数字资产 【免费下载链接】WeChatMsg 提取微信聊天记录&#xff0c;将其导出成HTML、Word、CSV文档永久保存&#xff0c;对聊天记录进行分析生成年度聊天报告 项目地址: https://gitcode.com/GitHub_Trending/we/WeChatMs…...

给嵌入式新手的Cortex-M0内核超详细图解:从寄存器到中断,一篇搞定STM32/GD32入门

给嵌入式新手的Cortex-M0内核超详细图解&#xff1a;从寄存器到中断&#xff0c;一篇搞定STM32/GD32入门 刚拿到STM32开发板时&#xff0c;看着密密麻麻的引脚和上百页的芯片手册&#xff0c;我完全不知道从哪里开始。直到导师指着原理图说&#xff1a;"把芯片想象成一个忙…...

Trae平台实战:我如何教会一个AI智能体应对动态网页和反爬虫?

Trae平台实战&#xff1a;动态网页抓取与反爬策略的智能应对之道 在数据驱动的商业环境中&#xff0c;网页抓取技术已成为企业获取竞争优势的关键能力。然而&#xff0c;随着网站防护技术的升级&#xff0c;传统爬虫在面对动态加载内容和复杂反爬机制时往往力不从心。本文将分享…...

C++ sort函数进阶指南:如何优雅地自定义结构体排序规则

C sort函数进阶指南&#xff1a;如何优雅地自定义结构体排序规则 在C开发中&#xff0c;数据排序是一个永恒的话题。当我们需要处理复杂数据结构时&#xff0c;标准库提供的默认排序方式往往无法满足需求。这时&#xff0c;掌握sort函数的高级用法就显得尤为重要。本文将深入探…...

小白也能搞定!用Docker和Halo 2.10搭建个人博客,再也不用担心公网访问问题

零基础玩转DockerHalo 2.10&#xff1a;打造高颜值个人博客全攻略 在数字内容创作爆发的时代&#xff0c;拥有一个专属博客空间已成为个人品牌建设的标配。但传统建站方案往往面临技术门槛高、维护成本大等痛点。本文将带你用Docker容器技术和Halo 2.10开源系统&#xff0c;30…...

Python内存泄漏分析实战指南(生产环境零停机排查全流程)

第一章&#xff1a;Python内存泄漏的本质与危害Python内存泄漏并非源于C语言中常见的“未释放malloc内存”&#xff0c;而是指对象被意外长期持有&#xff0c;导致垃圾回收器&#xff08;GC&#xff09;无法将其回收&#xff0c;从而持续占用堆内存。其本质是**引用关系的非预期…...

Wan2.2-I2V-A14B企业应用:品牌广告片AI辅助生成+人工精修工作流

Wan2.2-I2V-A14B企业应用&#xff1a;品牌广告片AI辅助生成人工精修工作流 1. 企业级视频创作新范式 在品牌营销领域&#xff0c;高质量视频内容的需求正呈指数级增长。传统视频制作流程面临三大痛点&#xff1a;创意实现周期长、专业团队成本高、批量生产难度大。Wan2.2-I2V…...