香橙派5(RK3588)使用npu加速yolov5推理的部署过程

news2024/10/25 22:53:47

香橙派5使用npu加速yolov5推理的部署过程

硬件环境

image-20241025093331088

部署过程

模型训练(x86主机)

在带nvidia显卡(最好)的主机上进行yolo的配置与训练, 获取最终的best.pt模型文件, 详见另一篇文档

模型转换(x86主机)

下载airockchip提供的yolov5(从pt到onnx)

一定要下这个版本的yolov5, 用于将pt模型转换成onnx模型, 这里的模型转换做了优化, 砍掉了最后一层, 只有这个onnx转换成rknn后, 可以正常推理

https://github.com/airockchip/yolov5.git
# 以下操作需要在虚拟环境中进行, 如果不是在虚拟环境 请先激活
cd yolov5
python3 export.py --rknpu --weight yolov5s.pt #请把这里的pt文件换成自己的文件
# 执行结束后, 应该会在当前目录下, 看到一个同名的 .onnx文件, 例如yolov5s.onnx

将该模型放到在线模型可视化网站, 检查模型的末端是否为下图类似的结构, 在sigmoid函数后直接就是输出, 而不是另一个检测层

image-20241025102804763

如果是下面这种, 说明转换的onnx模型有问题, 用的export.py应该不是瑞芯微的, yolo官方支持的转换模型, 在最后多了一层检测, 正常是没有下面红框里的内容的

image-20241025103004096

下载rknn-toolkit2(从onnx到rknn)
git clone https://github.com/airockchip/rknn-toolkit2.git
cd rknn-toolkit2/rknn-toolkit2/packages
# 需要确定是在虚拟环境下执行下面的指令, 如果不是在虚拟环境下, 请先使用下面的命令激活虚拟环境
# conda activate yolo
pip install -r requirements_cp38-2.2.0.txt #这里要对应python版本, 以及可能需要走清华镜像源, 否则很卡, 如果需要走镜像源, 请执行下面这段进行替代
# pip install -r requirements_cp38-2.2.0.txt -i https://pypi.tuna.tsinghua.edu.cn/simple
pip install rknn_toolkit2-2.2.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl

如果有其他依赖不满足, 需要手动下载, 最后应该是这样的结果, 说明安装成功了

image-20241025101042639

下载rknn_model_zoo
git clone https://github.com/airockchip/rknn_model_zoo.git
cd rknn_model_zoo/examples/yolov5/python
# 需要确定是在虚拟环境下执行下面的指令, 如果不是在虚拟环境下, 请先使用下面的命令激活虚拟环境
# conda activate yolo
python convert.py ../model/yolov5s_relu.onnx rk3588 i8 ../model/yolov5s_relu.rknn # 注意将前面的onnx模型路径修改成上面best.pt转换成的best.onnx路径, 后面那个是输出的路径, 可以随便改

香橙派部署(arm64)

下载虚拟环境管理工具 miniforge3

安装过程与在amd64上安装anaconda类似, 可以一路敲回车, 输yes

wget https://github.com/conda-forge/miniforge/releases/latest/download/Miniforge3-Linux-aarch64.sh
# 如果上一步卡很久, 可以考虑在自己电脑上挂梯子下好传过去, 或者走中转, 使用
# wget https://mirror.ghproxy.com/https://github.com/conda-forge/miniforge/releases/latest/download/Miniforge3-Linux-aarch64.sh 进行偷渡
chmod +x Miniforge3-Linux-aarch64.sh
./Miniforge3-Linux-aarch64.sh
# 一路回车就好, 默认安装路径为 ~/miniforge3
使用miniforge3创建虚拟环境
source ~/miniforge3/bin/activate #有可能在安装过程中, 就已经把这一步写进了环境变量, 看是否有(base)前缀, 有的话就不需要执行这一步

image-20241025095818043

# 创建虚拟环境
conda create -n yolo python=3.8 #这里python版本可以指定不同的, 以3.8为例, -n 后面跟的是环境名
# 激活环境, 如果环境名不是yolo, 就把yolo这个名字换掉
conda activate yolo
安装rknn-toolkit-lite
git clone https://github.com/airockchip/rknn-toolkit2.git
# 以下命令需要在虚拟环境中进行
cd rknn-toolkit2/rknn-toolkit-lite2/packages
pip install rknn_toolkit_lite2-2.2.0-cp38-cp38-linux_aarch64.whl # 这一步可能会缺少相关依赖? 也许需要
# cd ../../rknn-toolkit2/packages
# pip install -r requirements_cp38-2.2.0.txt

测试推理(香橙派)

# 下载rknn_model_zoo
git clone https://github.com/airockchip/rknn_model_zoo.git
cd rknn_model_zoo/examples/yolov5/python
# 需要确定是在虚拟环境下执行下面的指令, 如果不是在虚拟环境下, 请先使用下面的命令激活虚拟环境
# conda activate yolo
修改香橙派自带的yolo代码

rknn-toolkit只支持amd64, 在香橙派上部署运行时需要arm架构的库, 即rknn-toolkit-lite, 因此相应的要修改该包的代码, 使用rknnlite进行推理

import os
import cv2
import sys
import argparse
import numpy as np
import platform
from rknnlite.api import RKNNLite

# 添加路径
realpath = os.path.abspath(__file__)
_sep = os.path.sep
realpath = realpath.split(_sep)
sys.path.append(os.path.join(realpath[0]+_sep, *realpath[1:realpath.index('rknn_model_zoo')+1]))

from py_utils.coco_utils import COCO_test_helper

OBJ_THRESH = 0.25
NMS_THRESH = 0.45
IMG_SIZE = (640, 640)  # (width, height), 例如 (1280, 736), 需要和训练时的size匹配

CLASSES = ("pallet",)  # 这里的类型需要匹配模型中检测的类型个数, 名称以及顺序, 否则会出现检测的是对的, 但是标签打错了的情况

DEVICE_COMPATIBLE_NODE = '/proc/device-tree/compatible'

def get_host():
    # 获取平台和设备类型
    system = platform.system()
    machine = platform.machine()
    os_machine = system + '-' + machine
    if os_machine == 'Linux-aarch64':
        try:
            with open(DEVICE_COMPATIBLE_NODE) as f:
                device_compatible_str = f.read()
                if 'rk3588' in device_compatible_str:
                    host = 'RK3588'
                else:
                    host = 'RK356x'
        except IOError:
            print('Read device node {} failed.'.format(DEVICE_COMPATIBLE_NODE))
            exit(-1)
    else:
        host = os_machine
    return host


def filter_boxes(boxes, box_confidences, box_class_probs):
    box_confidences = box_confidences.reshape(-1)
    class_max_score = np.max(box_class_probs, axis=-1)
    classes = np.argmax(box_class_probs, axis=-1)

    _class_pos = np.where(class_max_score * box_confidences >= OBJ_THRESH)
    scores = (class_max_score * box_confidences)[_class_pos]
    boxes = boxes[_class_pos]
    classes = classes[_class_pos]

    return boxes, classes, scores


def nms_boxes(boxes, scores):
    x = boxes[:, 0]
    y = boxes[:, 1]
    w = boxes[:, 2] - boxes[:, 0]
    h = boxes[:, 3] - boxes[:, 1]

    areas = w * h
    order = scores.argsort()[::-1]

    keep = []
    while order.size > 0:
        i = order[0]
        keep.append(i)

        xx1 = np.maximum(x[i], x[order[1:]])
        yy1 = np.maximum(y[i], y[order[1:]])
        xx2 = np.minimum(x[i] + w[i], x[order[1:]] + w[order[1:]])
        yy2 = np.minimum(y[i] + h[i], y[order[1:]] + h[order[1:]])

        w1 = np.maximum(0.0, xx2 - xx1 + 0.00001)
        h1 = np.maximum(0.0, yy2 - yy1 + 0.00001)
        inter = w1 * h1

        ovr = inter / (areas[i] + areas[order[1:]] - inter)
        inds = np.where(ovr <= NMS_THRESH)[0]
        order = order[inds + 1]
    keep = np.array(keep)
    return keep


def post_process(input_data, anchors):
    boxes, scores, classes_conf = [], [], []

    # 调试输出 - 查看 input_data 和 anchors 的形状
    for i in range(len(input_data)):
        print(f"Debug: input_data[{i}].shape = {input_data[i].shape}")
        print(f"Debug: anchors[{i}] = {anchors[i]}")

    # 调整 reshape 逻辑以匹配实际输入尺寸
    for i in range(len(input_data)):
        # 根据输出形状和锚点数调整 reshape 逻辑
        num_anchors = len(anchors[i])
        grid_h, grid_w = input_data[i].shape[-2], input_data[i].shape[-1]
        expected_channels = num_anchors * (5 + len(CLASSES))

        if input_data[i].shape[1] != expected_channels:
            print(f"Error: input_data[{i}] channels {input_data[i].shape[1]} does not match expected channels {expected_channels}")
            continue

        # Reshape to (num_anchors, 5 + num_classes, grid_h, grid_w)
        input_data[i] = input_data[i].reshape((num_anchors, 5 + len(CLASSES), grid_h, grid_w))

        boxes.append(box_process(input_data[i][:, :4, :, :], anchors[i]))
        scores.append(input_data[i][:, 4:5, :, :])
        classes_conf.append(input_data[i][:, 5:, :, :])

    def sp_flatten(_in):
        ch = _in.shape[1]
        _in = _in.transpose(0, 2, 3, 1)
        return _in.reshape(-1, ch)

    boxes = [sp_flatten(_v) for _v in boxes]
    classes_conf = [sp_flatten(_v) for _v in classes_conf]
    scores = [sp_flatten(_v) for _v in scores]

    boxes = np.concatenate(boxes) if boxes else np.array([])
    classes_conf = np.concatenate(classes_conf) if classes_conf else np.array([])
    scores = np.concatenate(scores) if scores else np.array([])

    if len(boxes) == 0 or len(classes_conf) == 0 or len(scores) == 0:
        return None, None, None

    boxes, classes, scores = filter_boxes(boxes, scores, classes_conf)

    nboxes, nclasses, nscores = [], [], []
    for c in set(classes):
        inds = np.where(classes == c)
        b = boxes[inds]
        c = classes[inds]
        s = scores[inds]
        keep = nms_boxes(b, s)

        if len(keep) != 0:
            nboxes.append(b[keep])
            nclasses.append(c[keep])
            nscores.append(s[keep])

    if not nclasses and not nscores:
        return None, None, None

    boxes = np.concatenate(nboxes)
    classes = np.concatenate(nclasses)
    scores = np.concatenate(nscores)

    return boxes, classes, scores


def box_process(position, anchors):
    grid_h, grid_w = position.shape[2:4]
    anchors = np.array(anchors)
    num_anchors = len(anchors)
    anchors = anchors.reshape(num_anchors, 2, 1, 1)

    # 生成网格
    col, row = np.meshgrid(np.arange(0, grid_w), np.arange(0, grid_h))
    grid = np.stack((col, row), axis=0).astype(np.float32)
    grid = grid[np.newaxis, :, :, :]  # shape: (1, 2, grid_h, grid_w)

    # 位置解码
    box_xy = (position[:, :2, :, :] * 2.0 - 0.5 + grid) * (IMG_SIZE[0] / grid_w)
    box_wh = (position[:, 2:4, :, :] * 2.0) ** 2 * anchors

    # 将 [c_x, c_y, w, h] 转换为 [x1, y1, x2, y2]
    box = np.concatenate((box_xy - box_wh / 2.0, box_xy + box_wh / 2.0), axis=1)
    return box


def draw(image, boxes, scores, classes):
    for box, score, cl in zip(boxes, scores, classes):
        left, top, right, bottom = [int(_b) for _b in box]
        print("%s @ (%d %d %d %d) %.3f" % (CLASSES[cl], left, top, right, bottom, score))
        cv2.rectangle(image, (left, top), (right, bottom), (255, 0, 0), 2)
        cv2.putText(image, '{0} {1:.2f}'.format(CLASSES[cl], score),
                    (left, top - 6), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 0, 255), 2)


def setup_model(args):
    # 根据设备类型选择模型
    if args.model_path:
        rknn_model = args.model_path
    else:
        rknn_model = 'pallet_detection.rknn'  # Replace with your model path

    rknn_lite = RKNNLite()
    ret = rknn_lite.load_rknn(rknn_model)
    if ret != 0:
        print('Load RKNN model failed')
        exit(ret)
    print('done')
    return rknn_lite


if __name__ == '__main__':
    parser = argparse.ArgumentParser(description='RKNNLite inference example')
    parser.add_argument('--model_path', type=str, required=False, help='model path, could be .rknn file')
    parser.add_argument('--target', type=str, required=False, default='rk3566', help='target RKNPU platform')
    parser.add_argument('--img_folder', type=str, default='../model', help='img folder path')
    parser.add_argument('--img_show', action='store_true', default=False, help='draw the result and show')
    parser.add_argument('--img_save', action='store_true', default=False, help='save the result')
    parser.add_argument('--coco_map_test', action='store_true', help='enable coco map test')
    args = parser.parse_args()

    # 初始化模型
    rknn_lite = setup_model(args)
    print('--> Init runtime environment')
    ret = rknn_lite.init_runtime()
    if ret != 0:
        print('Init runtime environment failed')
        exit(ret)
    print('done')

    # 加载图片
    file_list = sorted(os.listdir(args.img_folder))
    img_list = [f for f in file_list if f.lower().endswith(('.jpg', '.jpeg', '.png', '.bmp'))]
    co_helper = COCO_test_helper(enable_letter_box=True)

    # 定义锚点(这里用的是yolo默认的锚点, 可以使用自定义训练后生成的)
    anchors = [
        [[10, 13], [16, 30], [33, 23]],     # 第一个尺度的锚点
        [[30, 61], [62, 45], [59, 119]],    # 第二个尺度的锚点
        [[116, 90], [156, 198], [373, 326]] # 第三个尺度的锚点
    ]

    # 运行推理
    for i, img_name in enumerate(img_list):
        print('infer {}/{}'.format(i + 1, len(img_list)), end='\r')

        img_path = os.path.join(args.img_folder, img_name)
        img_src = cv2.imread(img_path)
        if img_src is None:
            continue

        img = co_helper.letter_box(im=img_src.copy(), new_shape=(IMG_SIZE[1], IMG_SIZE[0]), pad_color=(0, 0, 0))
        img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
        img = img[np.newaxis, ...]  # 增加批次维度以匹配4D输入要求

        # 推理
        outputs = rknn_lite.inference(inputs=[img])

        # 输出调试信息
        for idx, output in enumerate(outputs):
            print(f"Output[{idx}] shape: {output.shape}")

        boxes, classes, scores = post_process(outputs, anchors=anchors)

        if args.img_show or args.img_save:
            print('\n\nIMG: {}'.format(img_name))
            img_p = img_src.copy()
            if boxes is not None:
                draw(img_p, co_helper.get_real_box(boxes), scores, classes)

            if args.img_save:
                result_path = os.path.expanduser(f'~/result_{i+1}.jpg')
                cv2.imwrite(result_path, img_p)
                print('Detection result save to {}'.format(result_path))
            if args.img_show:
                cv2.imshow("full post process result", img_p)
                cv2.waitKeyEx(0)

    rknn_lite.release()

运行推理
# 以下命令需要在虚拟环境中进行, 当前应该在以下目录 rknn_model_zoo/examples/yolov5/python
python3 pallet_detect.py --model_path ~/new.rknn --img_folder images/ --img_save
# 修改model_path为新模型的实际目录
# 修改img_folder为实际要测试的图像集
# 如果需要保存图像, 则加上--img_save, 会将图片保存到当前路径下, 以result_*jpg为名

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

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

相关文章

docker集成Nginx和Mysql (教程)

文章目录 前言一、Docker 集成Nginx步骤 1&#xff1a;安装 Docker步骤 2&#xff1a;拉取官方的 Nginx Docker 镜像1.可以先搜索nginx镜像(查看nginx镜像)2.拉取nginx镜像步骤 3&#xff1a;运行 Nginx 容器 二、Docker 集成Mysql步骤 1&#xff1a;拉取mysql镜像步骤2、运行 …

Vulnhub打靶-DC-1

基本信息 靶机下载&#xff1a;https://download.vulnhub.com/dc/DC-1.zip 攻击机器&#xff1a;192.168.20.128&#xff08;Windows操作系统&#xff09;& 192.168.20.138&#xff08;kali&#xff09; 靶机&#xff1a;192.168.20.0/24 目标&#xff1a;获取2个flag…

SQL 干货 | SQL 半连接

大多数数据库开发人员和管理员都熟悉标准的内、外、左和右连接类型。虽然可以使用 ANSI SQL 编写这些连接类型&#xff0c;但还有一些连接类型是基于关系代数运算符的&#xff0c;在 SQL 中没有语法表示。今天我们将学习一种这样的连接类型&#xff1a;半连接&#xff08;Semi …

tensorflow案例3--运动鞋识别(学习tensorflow动态加载学习率、如何设置早停等方法)

&#x1f368; 本文为&#x1f517;365天深度学习训练营 中的学习记录博客&#x1f356; 原作者&#xff1a;K同学啊 前言 这个案例还是以学习API为主&#xff0c;学习了tensorflow如何动态加载学习率、如何设置早停等方法&#xff1b;这个案例主要学习为主&#xff0c;由于模…

SpringBoot 集成RabbitMQ 实现钉钉日报定时发送功能

文章目录 一、RabbitMq 下载安装二、开发步骤&#xff1a;1.MAVEN 配置2. RabbitMqConfig 配置3. RabbitMqUtil 工具类4. DailyDelaySendConsumer 消费者监听5. 测试延迟发送 一、RabbitMq 下载安装 官网&#xff1a;https://www.rabbitmq.com/docs 二、开发步骤&#xff1a;…

Python 实现彩票抽奖系统详解(双色球)

目录 一、系统功能概述 二、代码实现详解 &#xff08;一&#xff09;自选功能实现&#xff08;ziXuanCaiPiao函数&#xff09; &#xff08;二&#xff09;机选功能实现&#xff08;autoChoiceCaiPiao函数&#xff09; &#xff08;三&#xff09;彩票展示功能实现&#x…

如何解决 IDEA 的 pom.xml 文件中,依赖警告问题

原因 在升级高版本的Idea后&#xff0c;我的是&#xff08;2024.2&#xff09;版本。Idea默认引入了插件 Package Checker 插件&#xff0c;用于检查 Maven 的 pom.xml 引入的 jar 包是否有漏洞风险。如果有漏洞风险则直接在引入的 pom.xml 下画黄线警告。 虽然不是错误&…

Tkinter -- python GUI学习与使用

前言 python GUI 目前pythonGUI有很多&#xff0c;哪一个最好&#xff1f; 先说说我选择的思路&#xff0c;我的目的是开发一个易用的软件&#xff0c;最重要的是稳定&#xff0c;并且碰到问题能够解决&#xff0c;因此&#xff0c;我的目标很明确&#xff0c;有比较大的用户群…

杂项笔记

1 这个好像如果如果分配空间就会执行 这个扩容好像会进行拷贝 2 3 4 没懂 5

【数据结构与算法】走进数据结构的“时间胶囊”——栈

大家好&#xff0c;我是小卡皮巴拉 文章目录 目录 引言 一.栈的基本概念 1.1 定义 1.2 特性 1.3 基本操作 二.栈的实现方式 2.1 顺序栈 2.2 链栈 三.顺序栈的实现 定义顺序栈的结构 初始化 入栈 检查栈是否为空 出栈 销毁 四.链栈的实现 定义链栈的结构 初始…

未来汽车驾驶还会有趣吗?车辆动力学系统简史

未来汽车驾驶还会有趣吗&#xff1f;车辆动力学系统简史 本篇文章来源&#xff1a;Schmidt, F., Knig, L. (2020). Will driving still be fun in the future? Vehicle dynamics systems through the ages. In: Pfeffer, P. (eds) 10th International Munich Chassis Symposiu…

数字图像处理的概念(一)

一 何谓数字图像处理 1 图像的概念 图像是对客观存在的物体的一种相似性的、生动的写真或描述。 2 图像的类别 可见光成像和不可见光成像 单波段、多波段和超波段图像 伽马射线成像 主要用途包括核 医学和天文观测 等 。 核医学 a)同位素注射 骨骼扫描图像 b)正电子放射( …

【Docker】安装、镜像、容器

什么是Docker&#xff1f; Docker&#xff1a;是基于Go语言实现的开源项目。 Docker 是一个用于开发、交付和运行应用程序的开放平台。它允许开发人员将应用程序及其依赖包打包到一个可移植的容器中&#xff0c; 然后在任何流行的 Linux 机器上运行。Docker 容器是完全隔离的&…

Openpyxl--学习记录

1.工作表的基本操作 1.1 工作表的新建打开与保存 1.1.1 创建工作簿 from openpyxl import Workbook from pathlib import Pathfile_path Path.home() / "Desktop" / "123.xlsx"# 1.创建工作簿 wb Workbook() # 2.访问默认工作簿 ws wb.active # 3.填充…

pytorh学习笔记——cifar10(六)MobileNet V1网络结构

基础知识储备&#xff1a; 一、深度可分离卷积&#xff08;Depthwise Separable Convolution&#xff09; MobileNet的核心是深度可分离卷积&#xff08;Depthwise Separable Convolution&#xff09;&#xff0c;深度可分离卷积是卷积神经网络&#xff08;CNN&#xf…

低代码开发详解与行业应用指南

低代码开发简化软件开发&#xff0c;助力企业数字化转型。ZohoCreator应用于零售、制造、教育、IT、医疗、房地产等行业&#xff0c;提升效率、降低成本。灵活定价&#xff0c;支持免费试用&#xff0c;助力企业快速实现数字化。 一、低代码开发是什么&#xff1f; 低代码开发…

CSS3 动画相关属性实例大全(三)(columns、filter、flex、flex-basis 、flex-grow、flex-shrink属性)

CSS3 动画相关属性实例大全&#xff08;三) &#xff08;columns、filter、flex、flex-basis 、flex-grow、flex-shrink属性&#xff09; 本文目录&#xff1a; 一、columns属性&#xff08;设置元素的列宽和列数&#xff09; 二、filter属性&#xff08;调整图像、背景和边…

打造充电场站:场地选择与合规运营详解

建设一座充电站需要六步流程&#xff1a;准备工作 → 备案 → 土地审核 → 规划审核 → 电力申请 → 验收确认 一、准备工作 在确定建设前&#xff0c;要考察待选的场地&#xff0c;例如空地、停车场等&#xff0c;与场地所有方签订充电站建设合作协议。根据场地和车流量等实际…

Docker 部署 EMQX 一分钟极速部署

部署 EMQX ( Docker ) [Step 1] : 拉取 EMQX 镜像 docker pull emqx/emqx:latest[Step 2] : 创建目录 ➡️ 创建容器 ➡️ 拷贝文件 ➡️ 授权文件 ➡️ 删除容器 # 创建目录 mkdir -p /data/emqx/{etc,data,log}# 创建容器 docker run -d --name emqx -p 1883:1883 -p 1808…

H7-TOOL的LUA小程序教程第15期:电压,电流,NTC热敏电阻以及4-20mA输入(2024-10-21,已经发布)

LUA脚本的好处是用户可以根据自己注册的一批API&#xff08;当前TOOL已经提供了几百个函数供大家使用&#xff09;&#xff0c;实现各种小程序&#xff0c;不再限制Flash里面已经下载的程序&#xff0c;就跟手机安装APP差不多&#xff0c;所以在H7-TOOL里面被广泛使用&#xff…