序言
基于RV1106平台开展YOLOv5模型移植过程中,发现RKNN官方参考代码存在两大技术瓶颈:其一,算法实现深度依赖RK底层硬件加速模块,导致代码架构与硬件强耦合;其二,原始实现仅支持单帧图像推理模式,无法满足实际应用场景需求。尽管参考了幸狐LockFox团队的视频流推理方案,但其数据采集端仍固守mipi摄像头专用传输协议,在非标准硬件环境下缺乏可移植性。
针对上述技术痛点,本研究提出基于OpenCV通用框架的端到端视频流推理方案。通过将摄像头数据采集模块重构为OpenCV标准接口实现,成功解耦硬件依赖关系。在保证算法完整性的前提下,将预处理、后处理等环节全部替换为OpenCV标准函数库操作。经实验验证,在输入分辨率320×320条件下,系统达到20FPS的推理性能。性能分析显示,OpenCV的图像操作消耗的性能较大,成为当前系统主要瓶颈。通过将输入分辨率再次降低,推理帧率可再次提升。
YOLOV5目标检测
本章节基于 Lockzhiner Vision Module 和 YOLOv5 目标检测模型,实现实时目标检测功能。
1. 基本知识简介
1.1 目标检测简介
目标检测是计算机视觉领域中的一个关键任务,它不仅需要识别图像中存在哪些对象,还需要定位这些对象的位置。具体来说,目标检测算法会输出每个检测到的对象的边界框(Bounding Box)以及其所属类别的概率或置信度得分。
- 应用场景:目标检测技术广泛应用于多个领域,包括但不限于安全监控、自动驾驶汽车、智能零售和医疗影像分析。
1.2 YOLOv5简介
YOLOv5 是 Ultralytics 在 2020 年基于 PyTorch 开发的一种高效目标检测模型,属于 YOLO 系列。它通过一次前向传播即可预测目标的类别和位置,适用于实时检测任务。YOLOv5 提供了多种模型大小(如 YOLOv5s、m、l、x),适应不同硬件条件。其结构包括骨干网络 CSPDarknet、特征融合层和检测头,支持多尺度预测和自定义训练,广泛用于各种检测场景。
2. API 文档
2.1 YOLOv5模型类
2.1.1 头文件
#include "yolov5.h"
2.1.2 模型初始化函数
int init_yolov5_model(const char* model_path, rknn_app_context_t* ctx);
- 作用:加载YOLOv5 RKNN模型并初始化推理上下文
- 参数
- model_path:RKNN模型文件路径
- ctx:模型上下文指针
- 返回值:
- 0:初始化成功
- -1:初始化失败
2.1.3 模型推理函数
int inference_yolov5_model(rknn_app_context_t* ctx,
object_detect_result_list* od_results);
- 作用:执行模型推理并获取检测结果
- 参数:
- ctx:已初始化的模型上下文
- od_results:检测结果存储结构体指针
- 返回值:
- 0:推理成功
- -1 :推理失败
2.1.4 模型释放函数
void release_yolov5_model(rknn_app_context_t* ctx);
- 作用:释放模型相关资源
- 参数:
- ctx:待释放的模型上下文
- 返回值:无
2.2 图像处理函数
2.2.1 Letterbox处理
cv::Mat letterbox(cv::Mat& image);
- 作用:保持图像比例进行缩放,添加灰边填充
- 参数:
-image:输入图像(RGB格式) - 返回值:
- 返回预处理图像
2.2.2 坐标映射函数
void mapCoordinates(float& x, float& y);
- 作用:将模型输出坐标映射回原始图像坐标系
- 参数:
- x/y:模型输出坐标(输入输出参数)
- 返回值:无
2.3 结果处理函数
2.3.1 后处理初始化
void init_post_process();
- 作用:加载类别标签文件
- 参数:无
- 返回值:无
2.3.2 结果绘制函数
void draw_detections(int count,
object_detect_result* results,
cv::Mat& frame,
void (*mapFunc)(float&, float&));
- 作用:在图像上绘制检测框和标签
- 参数:
- count:检测结果数量
- results:检测结果数组
- frame:目标图像帧
- mapFunc:坐标映射函数指针
- 返回值:无
3. 代码解析
3.1 流程图
main函数
├─ 参数检查与提示
│ └─ 检查参数数量是否为4,否则打印用法
├─ 初始化YOLOv5模型
│ ├─ 创建rknn_app_context结构体
│ ├─ 调用init_yolov5_model()加载RKNN模型
│ └─ 错误处理:模型加载失败时退出
├─ 加载标签文件
│ └─ 调用init_post_process()
├─ 初始化视频输入
│ ├─ 创建Edit对象并建立连接
│ ├─ 设置摄像头参数(640x480)
│ └─ 打开摄像头设备
├─ 主处理循环
│ ├─ 帧计时开始
│ ├─ 读取视频帧
│ │ └─ 空帧检查
│ ├─ 图像预处理
│ │ ├─ 调整尺寸至320x320
│ │ └─ 执行letterbox处理
│ ├─ 模型推理
│ │ ├─ 将数据复制到模型输入内存
│ │ ├─ 调用inference_yolov5_model()执行推理
│ │ └─ 获取检测结果列表
│ ├─ 结果可视化
│ │ ├─ 调用draw_detections()绘制检测框
│ │ └─ 使用mapCoordinates坐标映射
│ ├─ 显示输出
│ │ └─ 调用edit.Print()显示处理后的帧
│ ├─ 性能计算
│ │ ├─ 记录结束时间
│ │ └─ 计算并打印帧处理时间
│ └─ 循环继续条件
└─ 资源释放
├─ 调用release_yolov5_model()释放模型
├─ 调用deinit_post_process()释放后处理
└─ 释放摄像头资源
3.2核心代码解析
- 模型初始化
rknn_app_context_t rknn_app_ctx;
init_yolov5_model("yolov5s.rknn", &rknn_app_ctx);
- 图像预处理
cv::Mat letterboxImage = letterbox(frame); // 保持比例缩放
memcpy(rknn_app_ctx.input_mems[0]->virt_addr,
letterboxImage.data,
model_width * model_height * 3);
- 模型推理
object_detect_result_list od_results;
inference_yolov5_model(&rknn_app_ctx, &od_results);
- 结果可视化
draw_detections(od_results.count,
od_results.results,
frame,
mapCoordinates);
3.3 完整代码实现
👉 点击获取完整源码
4. 编译过程
4.1 编译环境搭建
- 请确保你已经按照 开发环境搭建指南 正确配置了开发环境。
- 同时以正确连接开发板。
4.2 Cmake介绍
# CMake最低版本要求
cmake_minimum_required(VERSION 3.10)
project(test-find-blobs)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
# 定义项目根目录路径
set(PROJECT_ROOT_PATH "${CMAKE_CURRENT_SOURCE_DIR}/../..")
message("PROJECT_ROOT_PATH = " ${PROJECT_ROOT_PATH})
include("${PROJECT_ROOT_PATH}/toolchains/arm-rockchip830-linux-uclibcgnueabihf.toolchain.cmake")
# 定义 OpenCV SDK 路径
set(OpenCV_ROOT_PATH "${PROJECT_ROOT_PATH}/third_party/opencv-mobile-4.10.0-lockzhiner-vision-module")
set(OpenCV_DIR "${OpenCV_ROOT_PATH}/lib/cmake/opencv4")
find_package(OpenCV REQUIRED)
set(OPENCV_LIBRARIES "${OpenCV_LIBS}")
# 定义 LockzhinerVisionModule SDK 路径
set(LockzhinerVisionModule_ROOT_PATH "${PROJECT_ROOT_PATH}/third_party/lockzhiner_vision_module_sdk")
set(LockzhinerVisionModule_DIR "${LockzhinerVisionModule_ROOT_PATH}/lib/cmake/lockzhiner_vision_module")
find_package(LockzhinerVisionModule REQUIRED)
set(RKNPU2_BACKEND_BASE_DIR "${LockzhinerVisionModule_ROOT_PATH}/include/lockzhiner_vision_module/vision/deep_learning/runtime")
if(NOT EXISTS ${RKNPU2_BACKEND_BASE_DIR})
message(FATAL_ERROR "RKNPU2 backend base dir missing: ${RKNPU2_BACKEND_BASE_DIR}")
endif()
add_executable(yolov5_main
main.cc
postprocess.cc
yolov5.cc
yolov5.h
postprocess.h
)
target_include_directories(yolov5_main PRIVATE ${LOCKZHINER_VISION_MODULE_INCLUDE_DIRS} ${rknpu2_INCLUDE_DIRS} ${RKNPU2_BACKEND_BASE_DIR})
target_link_libraries(yolov5_main PRIVATE ${OPENCV_LIBRARIES} ${LOCKZHINER_VISION_MODULE_LIBRARIES})
install(
TARGETS yolov5_main
RUNTIME DESTINATION .
)
4.3 编译项目
使用 Docker Destop 打开 LockzhinerVisionModule 容器并执行以下命令来编译项目
# 进入Demo所在目录
cd /LockzhinerVisionModuleWorkSpace/LockzhinerVisionModule/Cpp_example/D10_yolov5
# 创建编译目录
rm -rf build && mkdir build && cd build
# 配置交叉编译工具链
export TOOLCHAIN_ROOT_PATH="/LockzhinerVisionModuleWorkSpace/arm-rockchip830-linux-uclibcgnueabihf"
# 使用cmake配置项目
cmake ..
# 执行编译项目
make -j8 && make install
在执行完上述命令后,会在build目录下生成可执行文件。
5. 例程运行示例
5.1 运行
chmod 777 yolov5_main
# 在实际应用的过程中LZ-Picodet需要替换为下载的或者你的rknn模型
./yolov5_main ./voc320.rknn 20 label
5.2 结果展示
- 可以看到我们可以正确识别多种类别的
🚀 开发者生态:
- 订阅 更新日志 获取新模型支持
🔧 问题反馈:
遇到技术问题?请到凌智视觉模块官方仓库**凌智视觉模块** 提交Issue