跳到主要内容

RKNN 推理测试

1. RKNPU 简介

NPU(Nerual Processing Unit)是一种专门用于加速神经网络计算的处理器。为了满足人工智能的需求,瑞芯微逐渐将NPU集成到其处理器中,这种内置于瑞芯微处理器的NPU被称为RKNPU。LuckFox Pico 系列开发板了搭载瑞芯微 RV1103/RV1106 芯片,内置瑞芯微自研第4代NPU。该NPU具有高运算精度,支持int4、int8、int16混合量化。RKNPU4.0被划分为RKNPU2,因此要使用RKNPU2的SDK和工具套件。

2. RKNN-Toolkit2 简介

RKNN-Toolkit2 工具在 PC 平台上提供 C 或 Python 接口,简化模型的部署和运行。用户可以通过该工具轻松完成以下功能:模型转换、量化、推理、性能和内存评估、量化精度分析以及模型加密。RKNN 软件栈可以帮助用户快速的将 AI 模型部署到 Rockchip 芯片。整体的框架如下:

为了使用 RKNPU,用户需要首先在计算机上运行 RKNN-Toolkit2 工具,将训练好的模型转换为 RKNN 格式模型,之后使用 RKNN C API 或 Python API 在开发板上进行部署。本节介绍用户如何快速在Luckfox Pico 系列板子上使用 RKNPU。

3 RKNN-Toolkit2 安装(PC ubuntu22.04)

3.1 本地安装

  1. 环境要求

    操作系统版本Ubuntu18.04(x64)Ubuntu20.04(x64)Ubuntu22.04(x64)
    Python版本3.6/3.73.8/3.93.10/3.11
  2. 下载rknn-toolkit2

    git clone https://github.com/rockchip-linux/rknn-toolkit2
  3. 安装python环境

    sudo apt-get update
    sudo apt-get install python3 python3-dev python3-pip
    sudo apt-get install libxslt1-dev zlib1g zlib1g-dev libglib2.0-0 libsm6 libgl1-mesa-glx libprotobuf-dev gcc
  4. 安装RKNN-ToolKit2依赖包

    pip3 install -r rknn-toolkit2/packages/requirements_cpxx-1.6.0.txt

    # such as:
    pip3 install -r rknn-toolkit2/packages/requirements_cp310-1.6.0.txt

    根据不同的Python版本,选择安装对应的依赖包:

    Python版本RKNN-Toolkit2依赖包
    3.6requirements_cp36-1.6.0.txt
    3.7requirements_cp37-1.6.0.txt
    3.8requirements_cp38-1.6.0.txt
    3.9requirements_cp39-1.6.0.txt
    3.10requirements_cp310-1.6.0.txt
    3.11requirements_cp311-1.6.0.txt
  5. 安装RKNN-ToolKit2

    pip3 install rknn-toolkit2/packages/rknn_toolkit2-x.x.x+xxxxxxxx-cpxx-cpxx-linux_x86_64.whl

    # such as:
    pip3 install rknn-toolkit2/packages/rknn_toolkit2-1.6.0+81f21f4d-cp310-cp310-linux_x86_64.whl

    包名格式为:rknn_toolkit2-{版本号}+{commit 号}-cp{Python 版本}-cp{Python 版本}-linux_x86_64.whl,根据不同的Python版本,选择安装对应的安装包:

    Python版本RKNN-Toolkit2安装包
    3.6rknn_toolkit2-{版本号}+{commit 号}-cp36-cp36m-linux_x86_64.whl
    3.7rknn_toolkit2-{版本号}+{commit 号}-cp36-cp37m-linux_x86_64.whl
    3.8rknn_toolkit2-{版本号}+{commit 号}-cp36-cp38m-linux_x86_64.whl
    3.9rknn_toolkit2-{版本号}+{commit 号}-cp36-cp39m-linux_x86_64.whl
    3.10rknn_toolkit2-{版本号}+{commit 号}-cp36-cp310m-linux_x86_64.whl
    3.11rknn_toolkit2-{版本号}+{commit 号}-cp36-cp311m-linux_x86_64.whl

    若执行以下命令没有报错,则安装成功:

    python3
    from rknn.api import RKNN

3.2 Conda 安装

推荐使用 Conda 创建 python 虚拟环境,可以在多个应用场景下灵活切换,避免出现版本不匹配而无法运行等问题。实例在 AI 模型的训练和模型的转换过程中需要使用不同的 python 虚拟环境。

3.2.1 安装 miniconda 工具

  1. 检查是否安装了miniconda 或其他 conda 工具,如果有输出版本号说明已经安装。

    conda --version
  2. 下载安装包

    wget https://mirrors.tuna.tsinghua.edu.cn/anaconda/miniconda/Miniconda3-4.6.14-Linux-x86_64.sh
  3. 安装miniconda

    chmod 777 Miniconda3-4.6.14-Linux-x86_64.sh
    bash Miniconda3-4.6.14-Linux-x86_64.sh
    • 注意:miniconda 的安装包必须使用 chmod 777 来设置权限。

    • 执行安装后输入回车阅读许可条款,输入 yes 接受许可继续安装,再次输入 回车 会在家目录下创建 miniconda 文件夹,后续创建的虚拟环境会放置在此处。最后再输入一次 yes 进行 Conda 初始化 。

  4. 在计算机的终端窗口,进入 Conda base环境

    source ~/miniconda3/bin/activate # miniconda3 安装的目录(自定义安装根据实际情况进行修改)
    # 成功后,命令行提示符会变成以下形式:
    # (base) xxx@xxx:~$
  5. 如果你想在每次打开终端时都自动激活 Miniconda 环境,你可以将激活命令添加到你的 shell 配置文件中:

    vim nano ~/.bashrc

    #在文件末尾添加以下行:
    source ~/miniconda3/bin/activate

    #退出conda环境
    conda deactivate

3.2.2 创建 RKNN-Toolkit2 Conda 环境

  1. 创建 RKNN-Toolkit2 开发 Conda 环境, -n 参数表示环境名称,指定 python 版本为3.8(建议版本)

    conda create -n RKNN-Toolkit2 python=3.8
    • 输入 y 确认安装默认的安装包。
  2. 进入 RKNN-Toolkit2 Conda 环境

    conda activate RKNN-Toolkit2
  3. 验证 Python 版本是否使用正确

    python --version
    • 注意:部分开发环境在创建 Conda 环境后 Python 版本没有正常切换,重新启动终端可以解决。
  4. 获取 RKNN-Toolkit2 安装包

    git clone https://github.com/rockchip-linux/rknn-toolkit2.git
  5. 进入文件夹

    cd rknn-toolkit2
  6. 安装 RKNN-Toolkit2 相关的依赖库,cp38 为对应的 Conda 环境 python 版本,实验使用的版本为 3.8 所以使用后缀为 cp38 的依赖项

    pip install tf-estimator-nightly==2.8.0.dev2021122109 
    pip install -r rknn-toolkit2/packages/requirements_cp38-1.6.0.txt -i https://pypi.mirrors.ustc.edu.cn/simple/
    • 不换源下载速度太慢会导致安装失败。如果更换源,有时你选择的镜像源可能暂时不可用或者受到访问限制,这可能会导致下载失败。你可以尝试切换到其他可用的镜像源。

      #常用pip镜像源:
      阿里云 http://mirrors.aliyun.com/pypi/simple/
      中国科技大学 https://pypi.mirrors.ustc.edu.cn/simple/
      豆瓣(douban) http://pypi.douban.com/simple/
      清华大学 https://pypi.tuna.tsinghua.edu.cn/simple/
      中国科学技术大学 http://pypi.mirrors.ustc.edu.cn/simple/
  7. 安装 RKNN-Toolkit2

    pip install rknn-toolkit2/packages/rknn_toolkit2-1.6.0+81f21f4d-cp38-cp38-linux_x86_64.whl
    • 根据 python 版本,选择 packages 文件夹下的安装包文件,其中的 81f21f4d 为 commit 号,根据实际情况进行选择。使用 python3.8 对应带有 cp38 后缀的安装包。
  8. 验证是否安装成功,如果没有报错说明安装成功。

    python
    >>> from rknn.api import RKNN
    • 效果:

4. 模型部署

4.1 初识 ONNX

ONNX(Open Neural Network Exchange),开放神经网络交换,是一种模型IR,用于在各种深度学习训练和推理框架转换的一个中间表示格式,支持多种深度学习框架,包括 TensorFlow、PyTorch、Caffe 等。将模型转换为 ONNX 格式后,可以更容易地在不同的深度学习框架中进行部署和推理,而无需重新训练模型。

在实际业务中,可以使用Pytorch或者TensorFlow训练模型,导出成ONNX格式,然后在转换成目标设备上支撑的模型格式,比如TensorRT Engine、NCNN、MNN、RKNN等格式。ONNX定义了一组和环境,平台均无关的标准格式,来增强各种AI模型的可交互性,开放性较强。

4.2 获取 ONNX 模型

ONNX 文件不仅储存了神经网络模型的权重,也储存了模型的结构信息以及网络中每一层的输入输出等信息,可以使用 Netron 中进行查看,方便后续开发工作的模型调整。根据模型的不同获取源码,实例使用的模型训练推理源码 GitHub 地址如下:

模型地址
Retianfacehttps://github.com/bubbliiiing/retinaface-pytorch
Facenethttps://github.com/bubbliiiing/facenet-pytorch
Yolov5https://github.com/airockchip/rknn_model_zoo.git

4.3 人脸检测

人脸检测使用的模型为 retinaface,可以提取出人脸的边界框坐标、置信度和人脸五个特征点的坐标。通过人脸的边界框截取图像作为人脸特征提取的输入图像,可以提高人脸特征值获取的可靠性。

  1. 获取 retinaface 源码

    git clone https://github.com/bubbliiiing/retinaface-pytorch.git
  2. 进入源码目录

    cd retinaface-pytorch
  3. 搭建模型训练环境

    conda create -n retinaface python=3.6
    • 输入 y 同意安装基础 python 工具包。
  4. 进入 Conda 虚拟环境并安装运行的依赖库

    conda activate retinaface
    pip install -r requirements.txt
    • model_data 文件夹下存放有训练好的 .pth 权重文件,选择以 mobilenet 作为骨干网络的权重文件导出为 .onnx 格式。
  5. 在工程文件夹下创建导出 ONNX 文件的python脚本export_onnx.py

    from nets.retinaface import RetinaFace
    from utils.config import cfg_mnet
    import torch

    model_path='model_data/Retinaface_mobilenet0.25.pth' #模型路径
    model=RetinaFace(cfg=cfg_mnet,pretrained = False) #模型初始化
    device = torch.device('cpu')
    model.load_state_dict(torch.load(model_path,map_location=device),strict=False) #模型加载
    net=model.eval()
    example=torch.rand(1,3,640,640) #给定输入
    torch.onnx.export(model,(example),'model_data/retinaface.onnx',verbose=True,opset_version=9) #导出
  6. 执行脚本获取 ONNX 文件(retinaface conda 环境下)

    python export_onnx.py
    • <工程文件夹>/model_data 可以获取到转换后的 ONNX 文件。

4.4 人脸特征提取

人脸特征提取可以根据输入的人脸图像提取出 128 维的特征值,可以与其他人脸的特征值计算欧氏距离来衡量匹配程度。

  1. 获取 facenet 源码

    git clone https://github.com/bubbliiiing/facenet-pytorch.git
  2. 进入源码目录

    cd facenet-pytorch
  3. 搭建模型训练环境

    conda create -n facenet python=3.6
    • 输入 y 同意安装基础 python 工具包。
  4. 进入 Conda 虚拟环境并安装运行的依赖库

    conda activate facenet
    pip install -r requirements.txt
    • model_data 文件夹下存放有训练好的 .pth 权重文件。
  5. 在工程文件夹下创建导出 ONNX 文件的python脚本export_onnx.py

    from nets.facenet import Facenet
    from torch import onnx
    import torch

    model_path='model_data/facenet_mobilenet.pth' #模型路径
    model = Facenet(backbone="mobilenet",mode="predict",pretrained=True) #模型初始化
    device = torch.device('cpu')
    model.load_state_dict(torch.load(model_path, map_location=device), strict=False)
    example=torch.rand(1,3,160,160) #给定一个输入
    torch.onnx.export(model,example,'model_data/facenet.onnx',verbose=True,opset_version=9) #导出
  6. 执行脚本获取 ONNX 文件(facenet conda 环境下)

    python export_onnx.py 
    • <工程文件夹>/model_data 可以获取到装换后的 ONNX 文件。

4.5 物体识别

Yolov5 是由 Ultralytics 公司开源的 Yolo 算法版本,它是一种单阶段目标检测算法,完全基于 PyTorch 实现,并由 Python 编写。

Yolov5有五种模型,列举如下。

  1. Yolov5n:这是最小和最快的模型。它也被称为纳米模型,由于其尺寸,在移动设备中找到应用。
  2. Yolov5s:这也是一个相对较小的模型。它用于在CPU上运行结果。
  3. Yolov5m:正如 "m "所示,这是一个中等大小的模型。
  4. Yolov5l:这是一个大型模型。
  5. Yolov5x:这是各种变体中的顶级型号。然而,尺寸导致速度上的妥协。

Yolov5 的基本原理是:通过卷积神经网络提取图像特征,并在网格划分的基础上对每个网格单元进行目标检测预测,预测边界框位置和类别,并分配置信度分数。最后,通过非极大值抑制(NMS)筛选和合并重叠较大的边界框,得到最终的目标检测结果。

  1. 获取 Yolov5 源码

    git clone https://github.com/airockchip/yolov5.git
  2. 进入 Yolov5 源码目录

    cd yolov5
  3. 搭建模型训练环境

    conda create -n yolov5 python=3.9
    • 输入 y 同意安装基础 python 工具包。
  4. 进入 Conda 虚拟环境并安装运行的依赖库

    conda activate yolov5
    pip install -r requirements.txt
  • 注意:如果无法完整下载依赖的软件包请尝试设置 pip 的镜像源。
  1. 从默认文件中导出 ONNX 文件(yolov5 conda 环境下)

     python export.py --rknpu --weight yolov5s.pt
    • 如果工程目录下没有 yolov5s.pt 的权重文件,会自动拉取文件并转换。在工程文件夹下会生成yolov5s.onnxRK_anchors.txt文件,在物体识别的源码post_process 函数会使用到 RK_anchors.txt 中的参数。生成的 yolov5s.onnx 模型相比标准的 Yolov5 模型去除了 RKNPU 不兼容的结果提取部分,在应用源码中使用 CPU 来实现,如果使用自己训练的模型需要注意执行 export.py 程序需要添加 --rknpu 参数,在 RKNPU 的应用中才能获取到真正需要的数据。
    python export.py --rknpu --weight xxx.pt
    • xxx.pt 替换为训练获取到的.pt权重文件,训练自己需要的模型参考 yolov5官方教程

4.6 调整 ONNX 模型

使用 Netron 工具查看 ONNX 模型的结构,确定是否存在 RV1103/RV1106 暂时无法支持的操作符(如Layer Normalization),参考手册 RKNN支持操作待列表(https://github.com/rockchip-linux/rknn-toolkit2/blob/master/doc/05_RKNN_Compiler_Support_Operator_List_v1.6.0.pdf) 。如果不支持的操作符位于模型的最后几层,可以考虑使用 CPU 进行实现。使用 Netron 工具观察 facenet 源码的模型,可以发现模型在输出前使用了 RKNPU 无法解析的 ReduceL2 算子。

参考源码路径 facenet-pytorch/nets/facenet.py 中的 forward 函数源码可以获知在模型推理阶段输出 128 维特征向量前进行了一个标准化的过程。

def forward(self, x, mode = "predict"):
if mode == 'predict':
x = self.backbone(x)
x = self.avg(x)
x = x.view(x.size(0), -1)
x = self.Dropout(x)
x = self.Bottleneck(x)
x = self.last_bn(x)
x = F.normalize(x, p=2, dim=1) #输出前的标准化
return x
x = self.backbone(x)
x = self.avg(x)
x = x.view(x.size(0), -1)
x = self.Dropout(x)
x = self.Bottleneck(x)
before_normalize = self.last_bn(x)

x = F.normalize(before_normalize, p=2, dim=1)
cls = self.classifier(before_normalize)
return x, cls

可以将这部分分配给 CPU 来运行,将对应代码注释后,重新导出 ONNX 模型。

注意:实例中只有 facenet 需要在源码上进行了调整。

5. Luckfox RKNN 应用示例

5.1 平台支持

DemoSystemCameraScreen
luckfox_pico_retinaface_facenetBuildrootsc3336Pico-1.3-LCD LF40-480480-ARK
luckfox_pico_retinaface_facenet_spidevBuildrootsc3336Pico-ResTouch-LCD-2.8 Pico-ResTouch-LCD-3.5
luckfox_pico_yolov5Buildrootsc3336Pico-1.3-LCD LF40-480480-ARK

注意:Luckfox Pico 对屏幕的支持不同,可以参考兼容性清单,如果没有适配的屏幕也可以使用终端查看推理结果。

5.2 验证

测试前需要开启启Framebuffer支持,开启方法详见《luckfox-config 配置》章节

  1. 测试花屏

    cat /dev/urandom > /dev/fb0
  2. 测试清屏

    cat /dev/zero > /dev/fb0
    • 如果花屏、清屏功能正常,则说明驱动可以正常加载。

5.3 转换为 RKNN 模型

5.3.1 模型转换

  1. 源码获取

    git clone https://github.com/LuckfoxTECH/luckfox_pico_rknn_example.git
  2. 进入 scripts/luckfox_onnx_to_rknn 目录

    cd luckfox_pico_rknn_example/scripts/luckfox_onnx_to_rknn
  3. 文件结构

    luckfox_onnx_to_rknn
    ├── convert--------------------------------------模型转换 python 脚本
    ├── dataset--------------------------------------模型转换参考数据集
    │ └── pic
    │ ├── facenet
    │ │ └── face.jpg
    │ ├── retinaface
    │ │ └── face.jpg
    │ └── yolov5
    │ └── bus.jpg
    └── model----------------------------------------onnx 模型和 rknn 模型
  4. 进入RKNN-Toolkit2 Conda 开发环境

    conda activate RKNN-Toolkit2
    • 注意:如果没有进入正确的 Conda 虚拟环境会导致模型转换失败。
  5. 模型转换

    cd convert
    python convert.py <onnx模型地址> <训练集地址> <导出模型地址> <模型类型(Retinaface等)>
  6. 示例

    convert.py ../model/retinaface.onnx ../dataset/retinaface_dataset.txt ../model/retinaface.rknn Retinaface
    • onnx 模型地址:训练导出的 onnx ,例程提供的模型位置在luckfox_onnx_to_rknn/model
    • 训练集地址:只需要提供少量的图片作为模型转换的参考,将图片的储存地址写在 txt 文件中作为参数导入到转换文件中。
    • 导出模型地址:导出的 rknn 模型名称与地址,注意要以 .rknn 为后缀
    • 模型类型:根据转换模型的类型会提供不同的 rknn 预处理设置,在实例根据模型种类输入 Retinaface、Facenet 和 Yolov5。

5.3.2 自定义配置

进行模型转换时需要注意预处理设置需要通过查看模型训练源码的预测部分进行修改,以 retinaface 为例,在图片导入阶段源码对图片的三通道各减去均值,在调用 RKNN.Config 接口时就需要配置 mean_values 参数。

  1. retinaface 模型源码输入预处理

    def preprocess_input(image):
    image -= np.array((104, 117, 123),np.float32)
    return image
  2. RKNN-Toolkit2 转换模型配置

    rknn.config(mean_values=[[104, 117, 123]], std_values=[[1, 1, 1]], target_platform=platform,
    quantized_algorithm="normal", quant_img_RGB2BGR=True)

5.4 模型验证

5.4.1 模型初步分析

输出的 rknn 模型内部结构无法被 Netron 工具解析,但是可以查看输入输出的基本信息进行初步判断。

  • luckfox-pico 仅支持 int8 类型输入和输出

      如果模型转换后出现了 `float` 类型的输入输出 tensor 结构会导致数据无法正确获取,需要重新调整模型结构。
  • luckfox-pico 仅支持 4 维输入维度和 4 维度输出维度

5.4.2 软件模拟器模型验证

luckfox-pico 目前仅支持使用 C-API 在板端进行模型推理,但在模型的验证阶段可以使用 RKNN-Toolkit2 的 python 接口在软件模拟器上试运行,借助 python 丰富的第三方库编写测试程序。 使用 RKNN-Toolkit2 进行模型验证的流程

  1. 创建 RKNN 对象

    rknn = RKNN()
  2. 预处理设置。根据模型训练源码的图像预处理源码进行设置

    rknn.config(mean_values=[[0, 0, 0]], std_values=[[128, 128, 128]],
    target_platform='rv1103',
    quantized_algorithm="normal")
  3. 载入模型

    rknn.load_onnx(model = model_path)
  4. 创建模型

    rknn.build(do_quantization=do_quant, dataset=DATASET_PATH)
  5. 初始化运行环境.传入target参数会利用 adb 工具远程控制板端进行模型推理,默认不传入参数使用软件模拟器进行模型推理。

    rknn.init_runtime()
  6. 输入输出处理。根据使用第三方库有不同的处理方式,可以参考模型训练源码的处理方式。

  7. 释放 RKNN 对象

    rknn.release()
    • 软件模拟器的模型推理输入输出数据可以与板端的模型推理输入输出数据作比对,判断模型部署环境是否正确。

    • 注意: 软件模拟器的模型输出的数据是没有经过量化的,输出数据保留原 ONNX 模型的输出格式(大部分是浮点类型),而 luckfox-pico 的 RKNPU 只支持 int8 类型的输出,如果要进行输出数据比对需要对 luckfox-pico 的输出数据装换为未量化前的格式。

5.5 应用设计

进行AI模型推理的应用流程

  1. rknn 初始化

    • 在进行rknn初始化时根据模型占用的大小可以选择系统分配内存或者自定义分配内存。对于一般的模型推荐使用系统分配内存。
    rknn_init(&ctx, mode_path, 0, 0, NULL);
    • 如果一次需要串行使用多个模型,在内存资源较为紧张的情况下可以使用自定义分配内存的形式,让两个模型的中间tensor内存复用,使用这种方式需要及时释放申请的内存。
    rknn_context ctx_a, ctx_b;

    rknn_init(&ctx_a, model_path_a, 0, RKNN_FLAG_MEM_ALLOC_OUTSIDE, NULL);
    rknn_query(ctx_a, RKNN_QUERY_MEM_SIZE, &mem_size_a, sizeof(mem_size_a));

    rknn_init(&ctx_b, model_path_b, 0, RKNN_FLAG_MEM_ALLOC_OUTSIDE, NULL);
    rknn_query(ctx_b, RKNN_QUERY_MEM_SIZE, &mem_size_b, sizeof(mem_size_b));

    max_internal_size = MAX(mem_size_a.total_internal_size, mem_size_b.total_internal_size);
    internal_mem_max = rknn_create_mem(ctx_a, max_internal_size);

    internal_mem_a = rknn_create_mem_from_fd(ctx_a, internal_mem_max->fd, internal_mem_max->virt_addr, mem_size_a.total_internal_size, 0);
    rknn_set_internal_mem(ctx_a, internal_mem_a);
    internal_mem_b = rknn_create_mem_from_fd(ctx_b, internal_mem_max->fd, internal_mem_max->virt_addr, mem_size_b.total_internal_size, 0);
    rknn_set_internal_mem(ctx_b, internal_mem_b);
  2. rknn 模型输入输出参数获取

    // 获取输入输出的通道树
    rknn_query(ctx, RKNN_QUERY_IN_OUT_NUM, &io_num, sizeof(io_num));

    // 获取各个输入通道的参数
    rknn_query(ctx, RKNN_QUERY_NATIVE_INPUT_ATTR, &(input_attrs[i]), sizeof(rknn_tensor_attr));

    // 获取各个输出通道的参数
    rknn_query(ctx, RKNN_QUERY_NATIVE_OUTPUT_ATTR, &(output_attrs[i]), sizeof(rknn_tensor_attr));
  3. rknn 输出输出内存申请和设置

    // 申请各个输入通道的内存
    rknn_tensor_mem* input_mems[i] = rknn_create_mem(ctx_retinaface, input_attrs[i].size_with_stride);
    // 设置各个输入通道的内存
    rknn_set_io_mem(ctx_retinaface, app_retinaface_ctx->input_mems[i], &input_attrs[0]);


    // 申请各个输出通道的内存
    rknn_tensor_mem* output_mems[i] = rknn_create_mem(ctx_retinaface, output_attrs[i].size_with_stride);
    // 设置各个输出通道的内存
    rknn_set_io_mem(ctx_retinaface, app_retinaface_ctx->output_mems[i], &output_attrs[i]);
  4. 输入图像

    • 实例使用 opencv-mobile 获取摄像头数据,默认获取的颜色通道顺序为 B-G-R ,储存格式为 cv::Mat,需要将图像数据转换为模型需要的输入形式。
    // 获取摄像头图像
    cap >> bgr;
    // 装换为模型需要的大小
    cv::resize(bgr, bgr, cv::Size(width,height), 0, 0, cv::INTER_LINEAR);
    for (int y = 0; y < height; ++y) {
    for (int x = 0; x < width; ++x) {
    cv::Vec3b pixel = bgr.at<cv::Vec3b>(y, x);
    src_image[(y * width + x) * channels + 0] = pixel[2]; // Red
    src_image[(y * width + x) * channels + 1] = pixel[1]; // Green
    src_image[(y * width + x) * channels + 2] = pixel[0]; // Blue
    }
    }
    // 将数据复制到输入tensor的虚拟地址
    memcpy(input_mems[0]->virt_addr, src_image,width*height*channels);
  5. rknn 模型推理

    • luckfox-pico 使用零拷贝API接口,调用 rknn_run 接口后会将输出数据同步到设置的输出内存虚拟地址处。
    rknn_run(ctx,nullptr);
  6. 输出数据分析处理

    • luckfox-pico 的 RKNPU 只支持整数类型的输出,RKNN 模型对输入输出数据都进行了不对齐量化,所以在进行数据处理前需要将 luckfox-pico 的输出数据转换为模型原本的格式。
    float deqnt = ((float)qnt - (float)zp) * scale;
    • 如果在进行模型训练时对模型的最后几层结构进行了裁剪,分配给 CPU 进行运算时要使用格式转换后的输出数据。以 facenet 为例,为了让 RKNPU 兼容模型,在 ONNX 模型导出时去除了最后的标准化流程,需要在输出数据进行格式转换后添加标准化运行。
    float sum = 0;
    for(int i = 0; i < 128; i++)
    sum += out_fp32[i] * out_fp32[i];
    float norm = sqrt(sum);
    for(int i = 0; i < 128; i++)
    out_fp32[i] /= norm;
  7. 显示图像

    • 对输出数据进行分析处理后的结果可以使用 opencv-mobile 进行图像处理,使显示结果能够被直观地观察出来。如果使用的屏幕 Pico-LCD-1.3 分辨率为 240 x 240 ,显示格式为 RGB565 ,需要对 opencv-mobile 处理后的图像数据进行转换才能正常显示
    cv::resize(bgr, bgr, cv::Size(240,240), 0, 0, cv::INTER_LINEAR);
    for (int i = 0; i < bgr.rows; ++i) {
    for (int j = 0; j < bgr.cols; ++j) {
    uint16_t b = (bgr.at<cv::Vec3b>(i, j)[0] >> 3);
    uint16_t g = (bgr.at<cv::Vec3b>(i, j)[1] >> 2) << 5;
    uint16_t r = (bgr.at<cv::Vec3b>(i, j)[2] >> 3) << 11;

    rgb565Image.at<uint16_t>(i, j) = r | g | b;
    framebuffer[i * FB_HIGHT + j] = rgb565Image.at<uint16_t>(i,j);
    }
    }

5.6 编译运行

5.6.1 编译(在 PC 上执行)

  1. 仓库地址(获取源码参考5.3转换为 RKNN 模型部分)

    https://github.com/LuckfoxTECH/luckfox_pico_rknn_example.git
  2. 设置环境变量

    export LUCKFOX_SDK_PATH=< luckfox-pico Sdk 地址>

    注意: 使用绝对地址

  3. 执行 ./build.sh 后选择编译的例程

    1) luckfox_pico_retinaface_facenet
    2) luckfox_pico_retinaface_facenet_spidev
    3) luckfox_pico_yolov5
    Enter your choice [1-3]:
  4. luckfox_pico_retinaface_facenet_spidev 选项是专为 Pico-ResTouch-LCD 适配的例程,需要选择使用的 Luckfox Pico 型号来确定引脚

    1) LUCKFOX_PICO_PLUS
    2) LUCKFOX_PICO_PRO_MAX
    Enter your choice [1-2]:

5.6.2 运行(在 Luckfox Pico 板端上执行)

  1. 编译完成后会在luckfox_pico_rknn_example/install文件夹下生成对应的部署文件夹(后续用< Demo Dir >表示)

    luckfox_pico_retinaface_facenet_demo
    luckfox_pico_retinaface_facenet_spidev_pro_max_demo
    luckfox_pico_retinaface_facenet_spidev_plus_demo
    luckfox_pico_yolov5_demo
  2. 将生成的< Demo Dir >完整上传到 Luckfox Pico 上 (可使用adb ssh等方式) ,执行

    #在 Luckfox Pico 板端运行,<Demo Target> 是部署文件夹中的可执行程序
    cd <Demo Dir>
    chmod a+x <Demo Target>
  3. luckfox_pico_retinaface_facenet

    ./luckfox_pico_retinaface_facenet <retinaface模型> <facenet模型> <参考图像> 
    #示例:./luckfox_pico_retinaface_facenet ./model/RetinaFace.rknn ./model/mobilefacenet.rknn ./model/test.jpg
  4. luckfox_pico_retinaface_facenet_spidev

    ./luckfox_pico_retinaface_facenet_spidev <retinaface模型> <facenet模型> <参考图像>
    #示例:./luckfox_pico_retinaface_facenet_spidev ./model/RetinaFace.rknn ./model/mobilefacenet.rknn ./model/test.jpg
  5. luckfox_pico_yolov5

    ./luckfox_pico_yolov5 <yolov5模型> 
    #示例:./luckfox_pico_yolov5 ./model/yolov5.rknn

    注意:

    • 在运行demo前请执行 RkLunch-stop.sh 关闭 Luckofox Pico 开机默认开启的后台程序 rkicp ,解除对摄像头的占用
    • 例程相关的 RKNN 模型和相关配置文件放置在 < Demo Dir >/model 中,可以快速进行验证

5.6.3 示例效果

示例提供了物体识别和人脸识别的源码,可以作为参考进行其他 AI 模型的部署。两个示例利用 opencv-mobile 驱动 sc3335 摄像头捕获图像,将图像处理后的结果显示在屏幕上。

  1. 人脸识别的示例使用 retinaface 模型进行人脸检测,使用 facenet 模型进行人脸特征值提取,可以与传入的参考人脸对比计算 欧氏距离(特征值的差异度,越小说明匹配度越高)。

  2. 物体识别的示例使用 Yolov5 卷积神经网络模型,可以识别 80 种物体,并显示置信度。

6. rknn_model_zoo 应用示例

rknn_model_zoo 是瑞芯微官方提供的 RKNPU 支持的各种主流算法的部署示例,目前对 Luckfox Pico 所使用的 RV1103/RV1106 有限支持,最新的示例支持 mobilenet 模型部署和 yolo 模型部署,本章以部署 yolov5 为例介绍 rknn_model_zoo 示例的使用。

6.1 导出 RKNN 模型

  1. 下载 rknn_model_zoo

    git clone https://github.com/airockchip/rknn_model_zoo.git
  2. 获取 Yolov5 ONNX模型文件

    cd <rknn_model_zoo Path>/rknn_model_zoo/examples/yolov5/model
    chmod a+x download_model.sh
    ./download_model.sh
  3. 执行 rknn_model_zoo/examples/yolov5/python 目录下的模型转换程序 convert.py,使用方法:

    conda activate RKNN-Toolkit2
    cd <rknn_model_zoo Path>/rknn_model_zoo/examples/yolov5/python
    python3 convert.py ../model/yolov5s.onnx rv1106
    # output model will be saved as ../model/yolov5.rknn
    python3 convert.py <onnx_model> <TARGET_PLATFORM> <dtype(optional)> <output_rknn_path(optional)>

    参数介绍:

    • <onnx_model>:ONNX 模型路径。
    • <TARGET_PLATFORM>:指定NPU平台名称。例如“rv1106”。
    • <quant_dtype> :可选项,可以指定为i8fpi8表示进行量化,fp表示不量化,默认为i8
    • <output_rknn_path>:可选项,用于指定 RKNN 模型的保存路径,默认保存在 ONNX 模型同一目录下,名称为 'yolov5.rknn'

6.2 编译和构建

  1. 成功将 ONNX 模型转换成 RKNN 模型后,现在对 rknn_model_zoo/examples/yolov5 目录下的例程进行交叉编译,编译例程前需要设置如下环境变量:

    export GCC_COMPILER=<SDK目录>/tools/linux/toolchain/arm-rockchip830-linux-uclibcgnueabihf/bin/arm-rockchip830-linux-uclibcgnueabihf
  2. 执行 rknn_model_zoo 目录下的 build-linux.sh 脚本。该脚本将编译例程:

    chmod +x ./build-linux.sh
    ./build-linux.sh -t rv1106 -a armv7l -d yolov5
    • 编译过程:

      (RKNN-Toolkit2) luckfox@luckfox:~/rknn_model_zoo$ ./build-linux.sh -t rv1106 -a armv7l -d yolov5
      ./build-linux.sh -t rv1106 -a armv7l -d yolov5
      /home/luckfox-pico/tools/linux/toolchain/arm-rockchip830-linux-uclibcgnueabihf/bin/arm-rockchip830-linux-uclibcgnueabihf
      ===================================
      BUILD_DEMO_NAME=yolov5
      BUILD_DEMO_PATH=examples/yolov5/cpp
      TARGET_SOC=rv1106
      TARGET_ARCH=armv7l
      BUILD_TYPE=Release
      ENABLE_ASAN=OFF
      INSTALL_DIR=/home/rknn_model_zoo/install/rv1106_linux_armv7l/rknn_yolov5_demo
      BUILD_DIR=/home/rknn_model_zoo/build/build_rknn_yolov5_demo_rv1106_linux_armv7l_Release
      CC=/home/luckfox-pico/tools/linux/toolchain/arm-rockchip830-linux-uclibcgnueabihf/bin/arm-rockchip830-linux-uclibcgnueabihf-gcc
      CXX=/home/luckfox-pico/tools/linux/toolchain/arm-rockchip830-linux-uclibcgnueabihf/bin/arm-rockchip830-linux-uclibcgnueabihf-g++
      ===================================
      -- Configuring done
      -- Generating done
      -- Build files have been written to: /home/rknn_model_zoo/build/build_rknn_yolov5_demo_rv1106_linux_armv7l_Release
      Consolidate compiler generated dependencies of target imagedrawing
      Consolidate compiler generated dependencies of target imageutils
      Consolidate compiler generated dependencies of target fileutils
      [ 40%] Built target fileutils
      [ 40%] Built target imagedrawing
      [ 60%] Built target imageutils
      Consolidate compiler generated dependencies of target rknn_yolov5_demo
      [100%] Built target rknn_yolov5_demo
      [ 20%] Built target imageutils
      [ 40%] Built target fileutils
      [ 60%] Built target imagedrawing
      [100%] Built target rknn_yolov5_demo
      Install the project...
      -- Install configuration: "Release"
      -- Installing: /home/rknn_model_zoo/install/rv1106_linux_armv7l/rknn_yolov5_demo/./rknn_yolov5_demo
      -- Set runtime path of "/home/rknn_model_zoo/install/rv1106_linux_armv7l/rknn_yolov5_demo/./rknn_yolov5_demo" to "$ORIGIN/lib"
      -- Installing: /home/rknn_model_zoo/install/rv1106_linux_armv7l/rknn_yolov5_demo/./model/bus.jpg
      -- Installing: /home/rknn_model_zoo/install/rv1106_linux_armv7l/rknn_yolov5_demo/./model/coco_80_labels_list.txt
      -- Installing: /home/rknn_model_zoo/install/rv1106_linux_armv7l/rknn_yolov5_demo/model/yolov5.rknn
      -- Installing: /home/rknn_model_zoo/install/rv1106_linux_armv7l/rknn_yolov5_demo/lib/librknnmrt.so
      -- Installing: /home/rknn_model_zoo/install/rv1106_linux_armv7l/rknn_yolov5_demo/lib/librga.so
  3. 交叉编译完成后在 rknn_model_zoo 目录下会生成一个 install 目录,包含编译出来的程序和库文件。

    (RKNN-Toolkit2) luckfox@luckfox:~/rknn_model_zoo/install/rv1106_linux_armv7l/rknn_yolov5_demo$ ls
    lib model rknn_yolov5_demo

6.3 运行程序

  1. 先将整个 rknn_yolov5_demo 目录传输至开发板,然后执行下面指令运行程序:

    cd /root/rknn_yolov5_demo/
    ./rknn_yolov5_demo model/yolov5.rknn model/bus.jpg
  2. 推理完成后生成图片 out.png

    # ls
    lib model out.png rknn_yolov5_demo

6.4 实验结果

  • 水果推理测试

  • 多物体场景推理测试

7. FAQ

  1. 开发板上运行yolov5_demo_test时遇到找不到文件报错。

    答:尝试执行 killall rkipc 或 RkLunch-stop.sh 关闭系统默认rkipc 程序,解除对摄像头的占用后再运行 demo