yolov5 7.0版本部署手机端。通过pnnx导出ncnn。

news2025/1/20 12:00:36

yolov5 7.0版本部署手机端。通过pnnx导出ncnn。

  • 流程
  • 配置ncnn android yolov5
  • 导出自己模型的ncnn
    • 修改yolo.py文件
    • 导出TorchScript文件
    • pnnx转torchscript为ncnn
  • 安卓运行
      • 权重路径
      • 输入输出
      • anchors 大小
      • 类别名
      • generate_proposals方法修改
    • 结果

流程

网络yolov5 的部署已经有很多了,但是他们很多都是老版本,2023.12.03最新的版本是7.0。导致现在部署碰到各种问题。如下:

  1. (根源) yolov5 export.py导出onnx时添加train参数。但是train参数在最新的7.0版本已经被去掉了。导致问题。
  2. 没有train参数后,使用export.py 导出onnx,再将onnx转ncnn时报错。修改onnx模型麻烦且容易出问题。

本文使用pnnx代码库https://github.com/pnnx/pnnx将torchscript转为ncnn.避免上述问题。流程如下:
在这里插入图片描述

配置ncnn android yolov5

代码库:https://github.com/nihui/ncnn-android-yolov5。先使用代码库中提供的yolov5s ncnn权重。手机端能正常运行并产生输出。
在这里插入图片描述

导出自己模型的ncnn

修改yolo.py文件

老版本的export.py 中,通过添加train参数,去除模型中的后处理。但是新版本中,这个参数没了,所以我们需要将模型中的后处理去掉。
找到yolov5代码中的models->yolo.py文件,将Detect类下面的forward函数替换(大概是56-80行),修改为下面的forward

    def forward(self, x):
        z = []  # inference output
        for i in range(self.nl):
            feat = self.m[i](x[i])  # conv
            # x(bs,255,20,20) -> x(bs,20,20,255)
            feat = feat.permute(0, 2, 3, 1).contiguous()
            z.append(feat.sigmoid())
        return tuple(z)

导出TorchScript文件

直接导出即可

python export.py --weights yolov5s.pt  --include torchscript

pnnx转torchscript为ncnn

代码库:https://github.com/pnnx/pnnx.直接使用releases中的可执行文件即可。使用下面的命令转。需要注意的是zsh不支持官网的[]命令,需要用""包裹

'./pnnx'  'yolov5s.torchscript'    "inputshape=[1,3,640,640]"

正常情况下,在yolov5s.torchscript的文件中已经产生了yolov5s.ncnn.bin 和yolov5s.ncnn.param。这就是我们要的ncnn文件。

安卓运行

将上面的yolov5s.ncnn.bin 和yolov5s.ncnn.param都放入ncnn-android项目文件夹。路径是ncnn-android-yolov5/app/src/main/assets/,这里面应该有一个yolov5s.bin和yolov5s.param。我们将我们转的模型也放进去。如下图。
在这里插入图片描述

然后我们修改yolov5ncnn_jni.cpp文件(上图中的绿色框)。修改模型权重路径,输入输出、anchors大小和类别名。

权重路径

全局搜索yolov5.load_param,将后面的yolov5s.param修改为自己的param名。就在这个代码附近有bin的加载.同理修改

输入输出

打开https://netron.app/,然后将param拖进去, 最上面的这个名字是in0,将in0填写到ex.input中。 模型有三个输出,分别对应stride 8,stride 16和stride 32.将这个输出的名字也填写到对应位置。一般情况下,stride 8对应out0,stride 16对应out1,stride 32对应out2.

最上面的模型输出,以及对应的名字最上面的模型输出,以及对应的名字
下面是应该填写的位置。红色是input,绿色是output.
在这里插入图片描述
模型的第一个头。同理可找另外两个头。
在这里插入图片描述

anchors 大小

anchors的大小就在ex.extract的下方。一共有3个地方需要填写,对应stride 8(小物体),stride 16和stride 32(大物体)。如果自己的网络anchors大小没变则不用改。下图是stride 8 的修改。
在这里插入图片描述

类别名

类别名。全局搜索static const char* class_names。改成自己的就好了。
在这里插入图片描述

generate_proposals方法修改

把整个generate_proposals方法的代码用下面的代码替换。大概在yolov5ncnn_jni.cpp文件的185行。


static void generate_proposals(const ncnn::Mat& anchors, int stride, const ncnn::Mat& in_pad, const ncnn::Mat& feat_blob, float prob_threshold, std::vector<Object>& objects)
{
    const int num_w = feat_blob.w;
    const int num_grid_y = feat_blob.c;
    const int num_grid_x = feat_blob.h;

    const int num_anchors = anchors.w / 2;
    const int walk = num_w / num_anchors;
    const int num_class = walk - 5;

    for (int i = 0; i < num_grid_y; i++)
    {
        for (int j = 0; j < num_grid_x; j++)
        {

            const float* matat = feat_blob.channel(i).row(j);

            for (int k = 0; k < num_anchors; k++)
            {
                const float anchor_w = anchors[k * 2];
                const float anchor_h = anchors[k * 2 + 1];
                const float* ptr = matat + k * walk;
                float box_confidence = ptr[4];
                if (box_confidence >= prob_threshold)
                {
                    // find class index with max class score
                    int class_index = 0;
                    float class_score = -FLT_MAX;
                    for (int c = 0; c < num_class; c++)
                    {
                        float score = ptr[5 + c];
                        if (score > class_score)
                        {
                            class_index = c;
                            class_score = score;
                        }
                        float confidence = box_confidence * class_score;

                        if (confidence >= prob_threshold)
                        {
                            float dx = ptr[0];
                            float dy = ptr[1];
                            float dw = ptr[2];
                            float dh = ptr[3];

                            float pb_cx = (dx * 2.f - 0.5f + j) * stride;
                            float pb_cy = (dy * 2.f - 0.5f + i) * stride;

                            float pb_w = powf(dw * 2.f, 2) * anchor_w;
                            float pb_h = powf(dh * 2.f, 2) * anchor_h;

                            float x0 = pb_cx - pb_w * 0.5f;
                            float y0 = pb_cy - pb_h * 0.5f;
                            float x1 = pb_cx + pb_w * 0.5f;
                            float y1 = pb_cy + pb_h * 0.5f;

                            Object obj;
                            obj.x = x0;
                            obj.y = y0;
                            obj.w = x1 - x0;
                            obj.h = y1 - y0;
                            obj.label = class_index;
                            obj.prob = confidence;

                            objects.push_back(obj);

                        }
                    }
                }
            }
        }
    }
}

结果

点击运行。
在这里插入图片描述

参考:https://zhuanlan.zhihu.com/p/606440867

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

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

相关文章

STM32串口接收不定长数据(接收中断+超时判断)

玩转 STM32 单片机&#xff0c;肯定离不开串口。串口使用一个称为串行通信协议的协议来管理数据传输&#xff0c;该协议在数据传输期间控制数据流&#xff0c;包括数据位数、波特率、校验位和停止位等。由于串口简单易用&#xff0c;在各种产品交互中都有广泛应用。 但在使用串…

基础组件总结(以Element UI组件库为例)

一般对一个组件的使用方式&#xff1a; 每一个组件都有被绑定的数据&#xff0c; &#xff08;1&#xff09;首先会对组件的数据初始化&#xff08;a.data中赋初值 b. 在生命周期函数created或mounted中为变量赋予初值&#xff09; &#xff08;2&#xff09;由于不同组件的…

CSS新手入门笔记整理:CSS边框样式

边框宽度&#xff1a;boder-width 语法 boder-width:像素值&#xff1b; 边框样式&#xff1a;boder-style 语法 boder-style:取值&#xff1b; 属性值 说明 none 无样式 dashed 虚线 solid 实线 边框颜色&#xff1a;boder-color 语法 boder-color:色值&#xf…

MFC 绘制单一颜色圆形、渐变颜色边框圆形、渐变填充圆形以及绘制三角函数正弦函数曲线.

MFC 绘制三种不同圆形以及绘制正弦函数曲线 本文使用visual Studio MFC 平台实现绘制单一颜色圆形、渐变颜色边框圆形、渐变填充圆形以及绘制三角函数正弦函数曲线. 关于基础工程的创建请参考 01-Visual Studio 使用MFC 单文档工程绘制单一颜色直线和绘制渐变颜色的直线 02-vis…

Hisat-Trinity-PASA等组学分析流程

一边学习&#xff0c;一边总结&#xff0c;一边分享&#xff01; 详细教程请访问&#xff1a; 组学分析流程 本期分析流程 Hisat2-SamtoolsTrinity_GG_denovoPASA … 本期教程文章 题目&#xff1a;Genomic insights into local adaptation and future climate-induced vu…

全面的.NET微信网页开发之JS-SDK使用步骤、配置信息和接口请求签名生成详解

JSSDK使用步骤 步骤一:绑定安全域名&#xff1a; 先登录微信公众平台进入“公众号设置”的“功能设置”里填写“JS接口安全域名”。 步骤二:引入JS文件&#xff1a; 在需要调用JS接口的页面引入如下JS文件&#xff0c;&#xff08;支持https&#xff09;&#xff1a;http://…

分治—快速选择算法

文章目录 &#x1f347;215.数组中的第K个最大元素&#x1f348;1. 题目&#x1f349;2. 算法原理&#x1f34a;3. 代码实现 &#x1f34b;LCR 159. 库存管理 III&#x1f34c;1. 题目&#x1f34d;2. 算法原理&#x1f96d;代码实现 &#x1f347;215.数组中的第K个最大元素 …

〖大前端 - 基础入门三大核心之JS篇㊺〗- 定时器和延时器

说明&#xff1a;该文属于 大前端全栈架构白宝书专栏&#xff0c;目前阶段免费&#xff0c;如需要项目实战或者是体系化资源&#xff0c;文末名片加V&#xff01;作者&#xff1a;不渴望力量的哈士奇(哈哥)&#xff0c;十余年工作经验, 从事过全栈研发、产品经理等工作&#xf…

MathType公式编辑器安装教程

一、下载 MathType7是一款可以帮助用户快速完成数学公式编辑的应用软件&#xff0c;这款软件适合在进行教育教学、科研机构、论文写作的时候使用。我们可以直接通过这款软件来获取到大量数学上使用到的函数、数学符号等内容&#xff0c;然后使用这些内容来完成公式编辑。 …

ROS第一个程序——helloworld

目录 一、工作空间的创建 1.创建工作空间并初始化 2.进入 src 创建 ros 包并添加依赖 二、C实现helloworld C源码实现 编辑 ros 包下的 Cmakelist.txt文件 进入工作空间目录并编译 执行 三、python实现helloworld 进入 ros 包添加 scripts 目录并编辑 python 文件 …

【设计模式-4.1】行为型——观察者模式

说明&#xff1a;本文介绍设计模式中行为型设计模式中的&#xff0c;观察者模式&#xff1b; 商家与顾客 观察者模式属于行为型设计模式&#xff0c;关注对象的行为。以商家与顾客为例&#xff0c;商家有商品&#xff0c;顾客来购买商品&#xff0c;如果商家商品卖完了&#…

【Leetcode题单】(01 数组篇)刷题关键点总结02【统计数组中的元素】

【Leetcode题单】&#xff08;01 数组篇&#xff09;刷题关键点总结02【统计数组中的元素】&#xff08;6题&#xff09; 统计数组中的元素645. 错误的集合 Easy697. 数组的度 Easy448. 找到所有数组中消失的数字 Easy442. 数组中重复的数据 Medium41. 缺失的第一个正数 Hard27…

【问题解决】Linux内核编译安装后磁盘空间已满问题

Linux内核编译安装后磁盘空间已满问题解决过程 【注】本文为个人遇到Linux内核经过make&#xff0c;make modules……乃至最后install以后VMware磁盘空间爆炸的情况后&#xff0c;而又不想重装虚拟机&#xff0c;自己找资料实现解决的&#xff0c;文章中很多链接是来自别的博主…

python动态圣诞下雪图

运行图片 代码 import pygame import random# 初始化Pygame pygame.init()# 创建窗口 width, height 800, 600 screen pygame.display.set_mode((width, height)) pygame.display.set_caption(Christmas Tree)# 定义颜色 GREEN (34, 139, 34) RED (255, 0, 0) WHITE (255…

GAMES101:作业2记录

总览 在上次作业中&#xff0c;虽然我们在屏幕上画出一个线框三角形&#xff0c;但这看起来并不是那么的有趣。所以这一次我们继续推进一步——在屏幕上画出一个实心三角形&#xff0c;换言之&#xff0c;栅格化一个三角形。上一次作业中&#xff0c;在视口变化之后&#xff0…

Redis--13--缓存一致性问题

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 缓存一致性问题1、先更新缓存&#xff0c;再更新DB方案二&#xff1a;先更新DB&#xff0c;再更新缓存方案三&#xff1a;先删缓存&#xff0c;再写数据库推荐1&…

Python ctypes:揭秘高级Python与底层交互秘籍

更多资料获取 &#x1f4da; 个人网站&#xff1a;ipengtao.com ctypes是Python标准库中的外部函数库&#xff0c;允许Python调用动态链接库中的函数。它提供了与C兼容的数据类型和允许Python调用共享库中的函数&#xff0c;对系统级编程和与硬件交互非常有用。 基本用法 加…

基于Django框架搭建的协同过滤算法电影推荐网站-爬取的豆瓣电影数据

欢迎大家点赞、收藏、关注、评论啦 &#xff0c;由于篇幅有限&#xff0c;只展示了部分核心代码。 文章目录 一项目简介概述技术栈实现流程 二、功能三、系统四. 总结 一项目简介 # 电影推荐网站介绍 概述 该电影推荐网站是基于Django框架搭建的&#xff0c;旨在为用户提供个…

jdk1.8 hashmap源码阅读

目录 hashmap 成员变量 hashmap支持null键吗&#xff1f;为什么&#xff1f; 当扩容的时候&#xff0c;所有元素都会重新计算hash值吗&#xff1f; 怎么减少扩容次数 为什么node数组的大小是2的n次&#xff1f; 1.8和1.7的区别 1.8为啥要用红黑树&#xff1f; 扩容机制…

12.3_黑马MybatisPlus笔记(上)

目录 02 03 04 05 06 07 ​编辑 thinking:system.out::println?​编辑 thinking&#xff1a;list.of? 08 thinking&#xff1a;RequestParam和 ApiParam注解使用&#xff1f; thinking&#xff1a;RequestParam 和PathVariable的区别&#xff1f; ​编辑 ​编…