YOLOv8n部署RK3588开发板全流程(PT→ONNX→RKNN模型转换、板端后处理检测)

部署运行你感兴趣的模型镜像

在这里插入图片描述

------------25.3.25更新内容如下:
已在GitHub开源与博主另一篇博客【YOLOv8部署至RK3588】模型训练→转换RKNN→开发板部署 同步的YOLOv8_RK3588_object_detect项目,地址:
https://github.com/A7bert777/YOLOv8_RK3588_object_detect

注:本文是以瑞芯微RK3588 SoC进行示例,同时也可支持瑞芯微其他系列SoC:RK3562、RK3566、RK3568、RK3576、RK3399PRO、RK1808、RV1126、RV1126B、RV1103、RV1106、RV1109等,部署流程也基本一致,如需帮助,可通过Github仓库的 README.md 沟通。

前言

更新:此训练、转换、部署流程已有新版本,可移步此篇文章:【YOLOv8n部署至RK3588】模型训练→转换rknn→部署全流程,该文是完全按照瑞芯微最新demo进行部署,效果更好,模型转换的流程步骤更精简明确。

小白博主,第一次写博客记录自己YOLOv8n部署RK3588开发板的全流程,记录下踩的所有坑,欢迎交流。
本篇主要参考博主@山水无移-ZhangQian的文章,如有需要,可自行查看参考。

博主之前有写过YOLOv8目标检测&图像分割、YOLOv10目标检测、MoblieNetv2图像分类的模型训练、转换、部署文章,感兴趣的小伙伴可以了解下:
【YOLO11-obb部署至RK3588】模型训练→转换RKNN→开发板部署
【YOLO11部署至RK3588】模型训练→转换RKNN→开发板部署
【YOLOv10部署RK3588】模型训练→转换rknn→部署流程
【YOLOv8-obb部署至RK3588】模型训练→转换RKNN→开发板部署
【YOLOv8-pose部署至RK3588】模型训练→转换RKNN→开发板部署
【YOLOv8seg部署RK3588】模型训练→转换rknn→部署全流程
【YOLOv8部署至RK3588】模型训练→转换rknn→部署全流程
【YOLOv7部署至RK3588】模型训练→转换RKNN→开发板部署
【YOLOv6部署至RK3588】模型训练→转换RKNN→开发板部署
【YOLOv5部署至RK3588】模型训练→转换RKNN→开发板部署
【MobileNetv2图像分类部署至RK3588】模型训练→转换rknn→部署流程
【ResNet50图像分类部署至RK3588】模型训练→转换RKNN→开发板部署
YOLOv8n部署RK3588开发板全流程(pt→onnx→rknn模型转换、板端后处理检测)

欢迎各位小伙伴评论交流,同时也感谢昊哥、诚哥帮助


一、模型训练

YOLOv8的模型训练参考可如下两篇文章,不做过多叙述:
一、第一篇
二、第二篇

备注:博主训练时,选用的预训练模型为YOLOv8n

二、配置用于pt模型转onnx模型的环境

  1. 在由pt模型转onnx模型过程中,最好是新创建一个环境,避免在YOLOv8下路径出错等bug问题。
  2. conda create -n newpt2onnx python=3.8 创建模型转换的环境
  3. conda activate newpt2onnx 激活该环境
  4. 在https://github.com/airockchip/ultralytics_yolov8上下载相关配置文件,操作如下,进入YOLOv8文件夹后,在终端输入:git clone https://github.com/airockchip/ultralytics_yolov8.git,clone完成后进入ultralytics_yolov8文件夹下。输入两个命令:
    第一个命令:
    pip install -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple
    完成后输入第二个命令:
    pip install -e .
    完成这两个命令后,主要是修改了源码的ultralytics/nn/modules/head.py和ultralytics/engine/exporter.py两个文件。
  5. 环境配置完成

三、pt→onnx模型转换

.pt模型转.onnx模型
此处采用博主@山水无移-ZhangQian的转换方法

第一步:对YOLOv8/ultralytics_yolov8/ultralytics/nn/modules/head.py文件进行修改:
将Detect类改成如下所示:

class Detect(nn.Module):
    """YOLOv8 Detect head for detection models."""
    dynamic = False  # force grid reconstruction
    export = False  # export mode
    shape = None
    anchors = torch.empty(0)  # init
    strides = torch.empty(0)  # init

    def __init__(self, nc=4, ch=()):  # detection layer  //这边我把nc改成了4,原来nc是80
        super().__init__()
        self.nc = nc  # number of classes
        self.nl = len(ch)  # number of detection layers
        self.reg_max = 16  # DFL channels (ch[0] // 16 to scale 4/8/12/16/20 for n/s/m/l/x)
        self.no = nc + self.reg_max * 4  # number of outputs per anchor
        self.stride = torch.zeros(self.nl)  # strides computed during build
        c2, c3 = max((16, ch[0] // 4, self.reg_max * 4)), max(ch[0], min(self.nc, 100))  # channels
        self.cv2 = nn.ModuleList(
            nn.Sequential(Conv(x, c2, 3), Conv(c2, c2, 3), nn.Conv2d(c2, 4 * self.reg_max, 1)) for x in ch)
        self.cv3 = nn.ModuleList(nn.Sequential(Conv(x, c3, 3), Conv(c3, c3, 3), nn.Conv2d(c3, self.nc, 1)) for x in ch)
        self.dfl = DFL(self.reg_max) if self.reg_max > 1 else nn.Identity()

    def forward(self, x):
        """Concatenates and returns predicted bounding boxes and class probabilities."""
        shape = x[0].shape  # BCHW

        # if self.export and self.format == 'rknn':
        #     y = []
        #     for i in range(self.nl):
        #         y.append(self.cv2[i](x[i]))
        #         cls = torch.sigmoid(self.cv3[i](x[i]))
        #         cls_sum = torch.clamp(cls.sum(1, keepdim=True), 0, 1)
        #         y.append(cls)
        #         y.append(cls_sum)
        #     return y
                # 导出 onnx 增加
        y = []
        for i in range(self.nl):
            t1 = self.cv2[i](x[i])
            t2 = self.cv3[i](x[i])
            y.append(t1)
            y.append(t2)
        return y


        for i in range(self.nl):
            x[i] = torch.cat((self.cv2[i](x[i]), self.cv3[i](x[i])), 1)
        if self.training:
            return x
        elif self.dynamic or self.shape != shape:
            self.anchors, self.strides = (x.transpose(0, 1) for x in make_anchors(x, self.stride, 0.5))
            self.shape = shape

        x_cat = torch.cat([xi.view(shape[0], self.no, -1) for xi in x], 2)
        if self.export and self.format in ('saved_model', 'pb', 'tflite', 'edgetpu', 'tfjs'):  # avoid TF FlexSplitV ops
            box = x_cat[:, :self.reg_max * 4]
            cls = x_cat[:, self.reg_max * 4:]
        else:
            box, cls = x_cat.split((self.reg_max * 4, self.nc), 1)
        dbox = dist2bbox(self.dfl(box), self.anchors.unsqueeze(0), xywh=True, dim=1) * self.strides

        if self.export and self.format in ('tflite', 'edgetpu'):
            # Normalize xywh with image size to mitigate quantization error of TFLite integer models as done in YOLOv5:
            # https://github.com/ultralytics/yolov5/blob/0c8de3fca4a702f8ff5c435e67f378d1fce70243/models/tf.py#L307-L309
            # See this PR for details: https://github.com/ultralytics/ultralytics/pull/1695
            img_h = shape[2] * self.stride[0]
            img_w = shape[3] * self.stride[0]
            img_size = torch.tensor([img_w, img_h, img_w, img_h], device=dbox.device).reshape(1, 4, 1)
            dbox /= img_size

        y = torch.cat((dbox, cls.sigmoid()), 1)
        return y if self.export else (y, x)

上面主要是对nc进行修改,初始为80类,我的检测类别有4类,所以将nc改成4,然后对forward进行如下修改。(可直接复制)

第二步:对YOLOv8/ultralytics_yolov8/ultralytics/enginn/model.py进行修改
将model.py中的 _load函数进行修改,加入模型导出功能,因为在执行后续model = YOLO(“xxx.pt”)的命令时,会从model中的_load方法构建模型,因此在此处加入模型导出功能可实现导出。

具体改动如下:

def _load(self, weights: str, task=None):
    """
    Initializes a new model and infers the task type from the model head.

    Args:
        weights (str): model checkpoint to be loaded
        task (str | None): model task
    """
    suffix = Path(weights).suffix
    if suffix == '.pt':
        self.model, self.ckpt = attempt_load_one_weight(weights)
        self.task = self.model.args['task']
        self.overrides = self.model.args = self._reset_ckpt_args(self.model.args)
        self.ckpt_path = self.model.pt_path
    else:
        weights = check_file(weights)
        self.model, self.ckpt = weights, None
        self.task = task or guess_model_task(weights)
        self.ckpt_path = weights
    self.overrides['model'] = weights
    self.overrides['task'] = self.task
    
    print("===========  onnx =========== ")
    import torch
    dummy_input = torch.randn(1, 3, 640, 640)
    input_names = ["data"]
    output_names = ["reg1", "cls1", "reg2", "cls2", "reg3", "cls3"]
    torch.onnx.export(self.model, dummy_input, "./weights/mybestyolov8_opset9.onnx", verbose=False, input_names=input_names, output_names=output_names, opset_version=9)
    print("======================== convert onnx Finished! .... ")

注意,此处 opset_version必须为9!如果≥10,也是可以成功转出onnx模型的,但是在后续的onnx转rknn模型时,会报错:Meet unsupported MaxPool attribute ‘dilations’!,此时用netron工具查看了自己转换的onnx,maxpool中有dilation属性,这个dilation属性是onnx opset10之后的新属性,因此我们在这里采用opset_version=9,避免出错。

Opset与dilation介绍

这里延伸一下,opset即Operator Set,是ONNX的操作集,定义了用于构建和表示深度学习模型的操作符(operations),定义了一组用于构建和表示深度学习模型的操作符(operations)。
同时,ONNX操作集随着ONNX规范的发展而不断更新和扩展。每个版本的操作集都可能包含新增的操作符、对现有操作符的改进或优化,以及对某些过时操作符的废弃。因此,在将模型转换为ONNX格式时,需要指定目标操作集的版本,以确保模型在目标平台上能够正确执行。
ONNX操作集包含了各种类型的操作符,涵盖了深度学习中的常见操作,如卷积(Convolution)、池化(Pooling)、激活函数(Activation Functions)、批归一化(Batch Normalization)等。这些操作符可以被组合起来构建复杂的深度学习模型。ONNX操作集具有良好的可扩展性,允许用户根据需要定义自定义操作符。

dilation参数用于控制池化窗口中元素之间的间距,即在池化过程中,不是连续地考虑窗口内的所有元素,而是根据dilation的值来跳过某些元素。当dilation=1时(默认值),池化窗口内的元素是连续考虑的;当dilation大于1时,池化窗口内的元素之间会有间隔。
举例:如果此时9×9的特征图,池化窗口大小为3×3,dilation为2,stride为1。那么,每个池化窗口仍然为3×3大小,里面共有九个参数,但实际上参与到最大值池化计算的参数只有四个,分别是左上角、右上角、左下角以及右下角的元素(是由dilation=2决定的)
opset9版本的onnx模型:
请添加图片描述

opset11版本的onnx模型:
请添加图片描述

第三步:在/YOLOv8/ultralytics_yolov8下新建tuili.py文件,内容如下:

from ultralytics import YOLO
model = YOLO("./weights/yolov8bestptmodel.pt")
results = model(task='detect', mode='predict', source='2022129_1_158.jpg', line_width=3, show=True, save=True, device='cpu')

第四步:运行tuili.py
python tuili.py
在这里插入图片描述
在这里插入图片描述
会出现各种报错,但是没关系,只要出现=========== onnx =========== 和======================== convert onnx Finished! … ,说明tuili.py中的model = YOLO(“./weights/yolov8bestptmodel.pt”)成功调用了def _load()函数,此时已成功转换出了onnx模型,如下图所示:
在这里插入图片描述

四、配置onnx转rknn模型的虚拟环境

在配置该环境前,需要先安装rknn_toolkit2,记住,如果是RK3588的开发板,最好安装1.3版本的rknn_toolkit2,否则容易出错。
其余版本的开发板可以自己多试试,看看结果。
整个rknn_toolkit2-1.3.0的安装流程按照如下博客:rknn_toolkit2-1.3.0安装
主要安装流程如下:先百度云下载RK_NPU_SDK_1.3.0,将RK_NPU_SDK_1.3.0文件下的rknn-toolkit2-1.3.0文件夹放至YOLOv8目录下
在这里插入图片描述
然后安装python3.6版本的虚拟环境:conda create -n rknn130version python=3.6
完成配置后,激活该环境
在rknn130version环境下安装rknn-toolkit2依赖:

sudo apt-get install libxslt1-dev zlib1g-dev libglib2.0 libsm6 libgl1-mesa-glx libprotobuf-dev gcc
cd rknn-toolkit2-1.3.0/doc
pip install -r requirements_cp36-1.3.0.txt -i https://pypi.tuna.tsinghua.edu.cn/simple

如果用的是云服务器,在Ubuntu下可能会显示:Linux:bash: sudo: command not found。此时可参考此篇文章
在终端输入:

apt-get update -y
apt-get install sudo

如果中途显示bfloat16安装失败,则手动安装一下numpy:pip install numpy==1.16.6
此时再重新安装rknn-toolkit2依赖
安装完依赖后,安装rknn-toolkit2,如下所示

cd rknn-toolkit2-1.3.0/packages
pip install rknn_toolkit2-1.3.0_11912b58-cp36-cp36m-linux_x86_64.whl

检查是否安装成功:

conda activate rknn130version
python
from rknn.api import RKNN

如下所示:
在这里插入图片描述
没有任何提示则安装成功。此时CTRL+Z,退出 Python 的交互式模式。

然后在YOLOv8目录下下载yolov8_rknn(此处代码为博主@山水无移-ZhangQian创作),git clone https://github.com/cqu20160901/yolov8n_onnx_tensorRT_rknn_horizon_dfl.git
将其中的yolov8_rknn放置YOLOv8目录下
在这里插入图片描述

五、onnx转rknn模型

将之前得到的onnx模型放置在yolov8_rknn文件夹下
修改onnx2rknn_demo_ZQ.py中的类别名称
并将该文件夹下的各参数进行调整
然后执行yolov8_rknn下的onnx2rknn_demo_ZQ.py
python onnx2rknn_demo_ZQ.py
结果如下即为转换rknn成功:
在这里插入图片描述
检测效果图如下:
在这里插入图片描述

六、RK3588板端部署

板端流程参考:https://github.com/cqu20160901/yolov8n_onnx_tensorRT_rknn_horizon_dfl (为博主山水无移-ZhangQian
所创)可以克隆到本地:git clone https://github.com/cqu20160901/yolov8n_rknn_Cplusplus_dfl.git
在main.cpp中修改模型地址、输入图片与输出地址后编译,再执行可执行文件,生成如下检测结果:
请添加图片描述

到此为止,完整流程已结束,所有流程包括:YOLOv8模型训练→PT转ONNX模型的环境部署→PT转ONNX→ONNX转RKNN模型的环境部署→ONNX转RKNN→在RK3588上修改main.cpp与配置参数(包括Makefile、CMakelist等等)后进行编译,生成可执行文件→执行可执行文件,生成板端检测结果。

您可能感兴趣的与本文相关的镜像

Yolo-v8.3

Yolo-v8.3

Yolo

YOLO(You Only Look Once)是一种流行的物体检测和图像分割模型,由华盛顿大学的Joseph Redmon 和Ali Farhadi 开发。 YOLO 于2015 年推出,因其高速和高精度而广受欢迎

<think>我们有一个任务:在RK3588开发板部署自定义训练的YOLOv8模型,实现摄像头实时目标检测,并将检测到的目标坐标保存到表格文件(如CSV)中。根据引用[1]和引用[2],我们知道RK3588是一款高性能的ARM处理器,适用于边缘计算。YOLOv8是一个高效的实时对象检测模型。在RK3588部署YOLOv8,我们可以参考yolov5在RK3588上的部署流程(引用[2]),因为部署流程类似。整体步骤可以分为以下几个部分:1.训练自定义的YOLOv8模型(用户已经完成,我们有一个pt模型)。2.将训练好的pt模型转换ONNX格式。3.将ONNX模型转换RK3588支持的RKNN模型。4.在RK3588开发板上编写程序,使用RKNN模型进行实时摄像头目标检测。5.在检测过程中,将检测到的目标坐标信息保存到表格文件中。下面我们逐步说明:###步骤1:将YOLOv8模型转换ONNX格式首先,我们需要将训练好的YOLOv8模型(.pt文件)转换ONNX格式。可以使用YOLOv8官方提供的导出方法。假设我们有一个训练好的模型`yolov8_custom.pt`,我们可以使用以下命令导出ONNX模型:```bashyoloexportmodel=yolov8_custom.ptformat=onnx```这将生成一个`yolov8_custom.onnx`文件。注意:在导出ONNX模型时,需要确保安装的ultralytics版本支持导出功能。另外,由于我们后续需要部署RK3588,可能需要在导出时指定动态维度(以便适应不同输入尺寸)或者固定输入尺寸。根据引用[2]中的示例,我们可能需要在导出时指定输入尺寸。例如:```bashyoloexportmodel=yolov8_custom.ptformat=onnximgsz=640,640```###步骤2:将ONNX模型转换RKNN模型接下来,我们需要在PC上(通常是x86架构的机器,使用Linux系统)使用RKNN-Toolkit2将ONNX模型转换RKNN模型。首先,确保在PC上安装了RKNN-Toolkit2(注意:RKNN-Toolkit2需要Python环境,并且有依赖库)。然后,我们可以编写一个Python脚本来转换模型。以下是一个示例脚本(参考引用[2]中yolov5的转换流程):```pythonfromrknn.apiimportRKNN#创建RKNN对象rknn=RKNN()#模型配置#配置模型,指定目标平台为RK3588rknn.config(mean_values=[[0,0,0]],std_values=[[255,255,255]],target_platform='rk3588')#加载ONNX模型ret=rknn.load_onnx(model='yolov8_custom.onnx')ifret!=0:print('Loadmodelfailed!')exit(ret)#构建RKNN模型ret=rknn.build(do_quantization=True,dataset='./dataset.txt')ifret!=0:print('Buildmodelfailed!')exit(ret)#导出RKNN模型ret=rknn.export_rknn('yolov8_custom.rknn')ifret!=0:print('Exportmodelfailed!')exit(ret)#释放RKNN对象rknn.release()```注意:在构建模型时,我们使用了量化(`do_quantization=True`),这通常需要提供一个校准数据集(通过`dataset`参数指定)。校准数据集是一个文本文件,其中包含一系列用于量化的图片路径。每行一个图片路径。例如,`dataset.txt`内容如下:```./calib_images/image1.jpg./calib_images/image2.jpg...```###步骤3:在RK3588开发板部署RKNN模型并进行实时检测转换好的`yolov8_custom.rknn`模型复制到RK3588开发板上(开发板运行安卓或Linux系统)。在RK3588开发板上,我们需要编写一个Python程序(或C++程序)来:-初始化摄像头-加载RKNN模型-循环读取摄像头帧,进行推理-解析推理结果,得到目标检测框(坐标)-将目标坐标保存到表格文件(如CSV)以下是一个Python示例代码的大致框架(假设开发板上已安装RKNN-Toolkit2的Lite版或相应SDK):```pythonimportcv2importnumpyasnpfromrknnlite.apiimportRKNNLiteimportcsvimporttime#初始化RKNNrknn_lite=RKNNLite()ret=rknn_lite.load_rknn('yolov8_custom.rknn')ret=rknn_lite.init_runtime()#打开摄像头cap=cv2.VideoCapture(0)#0表示默认摄像头#创建CSV文件并写入表头csv_file=open('detections.csv','w',newline='')csv_writer=csv.writer(csv_file)csv_writer.writerow(['timestamp','class_id','confidence','x1','y1','x2','y2'])try:whileTrue:#读取一帧ret,frame=cap.read()ifnotret:break#预处理:调整大小、归一化等(根据模型输入要求)input_img=cv2.resize(frame,(640,640))input_img=input_img.astype(np.float32)/255.0#注意:YOLOv8的输入格式可能是CHW,需要转换input_img=input_img.transpose((2,0,1))#推理outputs=rknn_lite.inference(inputs=[input_img])#后处理:解析输出,得到检测框(这里需要根据你的模型输出结构来解析)#假设outputs是模型的输出,我们需要将其转换检测框的格式(如xyxy)#注意:YOLOv8的输出格式可能和YOLOv5不同,需要根据实际情况调整后处理代码。#这里只是一个示例,具体后处理需要根据模型输出结构编写。#假设我们有一个后处理函数,返回一个列表,每个元素是一个检测框,格式为[class_id,confidence,x1,y1,x2,y2]detections=post_process(outputs)#需要自己实现post_process函数#将检测结果写入CSV(每个检测框一行)timestamp=time.time()fordetindetections:class_id,conf,x1,y1,x2,y2=detcsv_writer.writerow([timestamp,class_id,conf,x1,y1,x2,y2])#可选:在图像上绘制检测框并显示#...#按'q'退出ifcv2.waitKey(1)&0xFF==ord('q'):breakfinally:#释放资源cap.release()csv_file.close()rknn_lite.release()cv2.destroyAllWindows()```###注意事项:1.**后处理**:YOLOv8的输出格式可能与YOLOv5不同。YOLOv8检测模型输出通常是一个包含多个输出层的列表(对于不同尺度的特征图),我们需要根据模型输出的结构来解析检测框。通常,每个输出是一个形状为[1,num_classes+5,H,W]的张量,然后通过非极大值抑制(NMS)得到最终检测框。具体后处理代码需要根据模型输出结构编写。2.**性能优化**:在RK3588上,我们可以使用NPU加速推理,但后处理部分(包括NMS)通常在CPU上运行。为了达到实时性能,需要优化后处理代码。3.**坐标转换**:模型输入尺寸(如640x640)和摄像头原始帧尺寸可能不同,因此需要将检测框坐标转换回原始图像的坐标系统。###总结部署流程包括:模型转换pt->onnx->rknn)和开发板上的应用程序开发(摄像头读取、推理、后处理、结果保存)。其中,后处理部分需要根据YOLOv8的输出结构进行适配。由于YOLOv8的具体输出结构可能因版本和训练配置而异,建议先导出ONNX模型并用Netron查看输出层,以确定后处理的实现方式。以上步骤提供了一个基本的部署框架,具体实现时可能需要根据实际情况调整。§§相关问题§§1.如何将YOLOv8模型导出为ONNX格式时指定输入尺寸?2.在RKNN转换过程中,如何准备校准数据集?3.如何编写YOLOv8模型后处理代码以解析检测框?4.在RK3588开发板上如何安装RKNN-ToolkitLite?5.如何将检测框坐标转换回原始图像尺寸?</think>### 部署自定义YOLOv8模型RK3588的完整流程 #### 1. **模型转换(PC操作)** - **步骤1:PTONNX 转换** 使用YOLOv8官方导出工具(需安装`ultralytics`包): ```bash yolo export model=yolov8_custom.pt format=onnx imgsz=640 # 指定输入尺寸 ``` 生成`yolov8_custom.onnx`文件[^3]。 - **步骤2:ONNXRKNN 转换** 使用RKNN-Toolkit2(需Python环境): ```python from rknn.api import RKNN rknn = RKNN() # 配置参数(匹配训练时的预处理) rknn.config(mean_values=[[0, 0, 0]], std_values=[[255, 255, 255]], target_platform='rk3588', quantized_dtype='asymmetric_quantized-u8') # 加载ONNX转换 rknn.load_onnx(model='yolov8_custom.onnx') rknn.build(do_quantization=True, dataset='./calib_data.txt') # 校准数据集路径 rknn.export_rknn('yolov8_custom.rknn') rknn.release() ``` **校准数据集准备**: 创建`calib_data.txt`,每行包含校准图片路径(建议20-100张训练集图片)。 #### 2. **RK3588环境配置(开发板)** - **安装依赖**: ```bash sudo apt update sudo apt install python3-opencv libusb-1.0-0-dev pip install rknn-toolkit2-lite==1.7.0 opencv-python pandas ``` - **连接摄像头**: 确保`/dev/video0`设备存在(使用`v4l2-ctl --list-devices`验证)。 #### 3. **实时检测与坐标导出代码** 创建`detect_export.py`: ```python import cv2 import numpy as np from rknnlite.api import RKNNLite import pandas as pd import time # 初始化RKNN rknn = RKNNLite() rknn.load_rknn('yolov8_custom.rknn') rknn.init_runtime(core_mask=RKNNLite.NPU_CORE_0) # 指定NPU核心 # 打开摄像头 cap = cv2.VideoCapture(0) cap.set(cv2.CAP_PROP_FRAME_WIDTH, 640) cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 480) # 创建CSV记录器 df = pd.DataFrame(columns=['timestamp', 'class', 'confidence', 'x_center', 'y_center']) try: while True: ret, frame = cap.read() if not ret: break # 预处理(匹配训练参数) img = cv2.resize(frame, (640, 640)) img = img[:, :, ::-1].transpose(2, 0, 1) # BGR to RGB, HWC to CHW img = np.expand_dims(img, 0).astype('uint8') # NPU推理 outputs = rknn.inference(inputs=[img]) # 解析YOLOv8输出(示例结构,需适配自定义模型) # outputs[0]: [1, 84, 8400] 包含xywh+cls_prob predictions = np.squeeze(outputs[0]).T boxes, confs, classes = [], [], [] # 后处理(根据实际输出结构调整) for pred in predictions: if pred[4] > 0.5: # 置信度阈值 x, y, w, h = pred[:4] * [frame.shape[1], frame.shape[0]] # 还原原始尺寸 cls_id = np.argmax(pred[5:]) boxes.append([x, y, w, h]) confs.append(pred[4]) classes.append(cls_id) # 保存坐标到DataFrame timestamp = time.strftime("%Y%m%d-%H%M%S") for i in range(len(boxes)): x_center, y_center = boxes[i][0] + boxes[i][2]/2, boxes[i][1] + boxes[i][3]/2 new_row = {'timestamp':timestamp, 'class':classes[i], 'confidence':confs[i], 'x_center':x_center, 'y_center':y_center} df = pd.concat([df, pd.DataFrame([new_row])], ignore_index=True) # 实时显示(可选) cv2.imshow('Detection', frame) if cv2.waitKey(1) & 0xFF == ord('q'): break finally: cap.release() df.to_csv('detection_log.csv', index=False) # 保存CSV rknn.release() cv2.destroyAllWindows() ``` #### 4. **关键优化点** - **模型适配**: - YOLOv8旋转目标检测需调整输出解析(参考[^1]中旋转框表示) - 若使用`obb`(旋转框)模式,需修改后处理解码旋转角度 - **性能提升**: - 多线程处理:分离图像采集、推理、结果保存线程 - 降低分辨率:如使用`imgsz=480`加速推理 - 启用NPU多核:`core_mask=RKNNLite.NPU_CORE_0_1_2` - **坐标精度**: 添加摄像头标定参数,消除镜头畸变对坐标的影响: ```python # 加载标定矩阵 mtx = np.load('camera_matrix.npy') dist = np.load('dist_coeffs.npy') frame = cv2.undistort(frame, mtx, dist) # 预处理时添加 ``` #### 5. **部署验证** ```bash # 在RK3588上运行 python detect_export.py ``` 输出文件示例(detection_log.csv): ``` timestamp,class,confidence,x_center,y_center 20240510-142305,0,0.92,320.5,240.2 20240510-142305,2,0.87,150.1,380.3 ``` ### 常见问题解决 1. **模型转换失败** - 检查ONNX版本:RKNN-Toolkit2需ONNX opset≤15 - 简化模型:使用`onnx-simplifier`处理复杂算子 2. **摄像头无法打开** - 权限问题:`sudo chmod 666 /dev/video0` - 驱动支持:确认内核已加载`uvcvideo`模块 3. **推理速度不足** - 量化校准:确保校准数据覆盖多样场景 - 使用`rknn.config(batch_size=2)`启用批处理 4. **坐标偏移校正** - 在原始图像绘制检测框验证定位准确性 - 添加仿射变换矩阵修正视角偏差 ### 典型应用场景[^1] - **无人机巡检**:实时检测电力设备坐标并生成巡检报告 - **智能仓储**:记录货物位置变化到日志系统 - **工业质检**:统计缺陷产品坐标分布热力图
评论 26
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值