RKNN YOLOv5 推理直接使用 NV12 视频帧可行性分析

RKNN YOLOv5 推理直接使用 NV12 视频帧可行性分析

问题

从摄像头拉取的原始视频帧格式为 NV12 (YUV420SP),当前流程中需要先通过 nv12_to_rgb() 将 NV12 转换为 RGB888 后再送入 inference_yolov5_model() 做推理,造成不必要的 CPU 资源消耗。


核心结论

不能直接将 NV12 原始数据送入 YOLOv5 模型推理。

YOLOv5 模型的第一层卷积层期望输入为 3 通道 RGB 数据 (shape [1, 640, 640, 3]),而 NV12 是 YUV 色彩空间的 1.5 通道平面格式(Y 平面 + UV 交错平面),两者的数据布局和语义完全不同。即使使用 rknn_input.pass_through = 1 将原始字节直接传递给 NPU,模型也会输出无意义的结果。

最优方案:使用 Rockchip RGA(2D 硬件加速器)替代 CPU 完成 NV12→RGB888 转换。

该代码库的 image_utils.c 已原生支持此方案------通过 RGA 的 improcess()一次硬件调用 内完成 NV12→RGB888 色彩空间转换 + 缩放 + letterbox 填充,彻底消除 CPU 逐像素转换的开销。


代码库已有支持

关键结构体和函数已就位:

组件 位置 说明
IMAGE_FORMAT_YUV420SP_NV12 utils/common.h:13 image_buffer_t 支持的 NV12 格式枚举
get_rga_fmt() returns RK_FORMAT_YCbCr_420_SP utils/image_utils.c:468-469 NV12 到 RGA 格式的映射
convert_image_rga() utils/image_utils.c:498 使用 RGA 硬件转换,支持不同 src/dst 格式
convert_image_with_letterbox() utils/image_utils.c:699 NV12→RGB888 + resize + letterbox 一次完成
librga.so install/.../lib/librga.so 已部署的 RGA 运行时库

代码修改方案

1. main.cc --- 替换 CPU 端 NV12→RGB 转换

删除以下代码:

cpp 复制代码
// main.cc:1113  --- 分配 RGB 缓冲区
stream->rgb_buffer = (unsigned char *)malloc(WIDTH * HEIGHT * 3);

// main.cc:1123-1125 --- 设置 RGB888 格式
img.width = WIDTH;
img.height = HEIGHT;
img.format = IMAGE_FORMAT_RGB888;

// main.cc:1146-1147 --- CPU 转换并赋值
nv12_to_rgb((unsigned char *)frame, stream->rgb_buffer, WIDTH, HEIGHT);
img.virt_addr = stream->rgb_buffer;

替换为以下逻辑:

cpp 复制代码
// 设置源图像:直接指向 NV12 帧
image_buffer_t src_img;
memset(&src_img, 0, sizeof(src_img));
src_img.virt_addr = (unsigned char *)frame;
src_img.width = WIDTH;
src_img.height = HEIGHT;
src_img.format = IMAGE_FORMAT_YUV420SP_NV12;
src_img.size = WIDTH * HEIGHT * 3 / 2;  // NV12 size = w*h*1.5

// 设置目标图像:模型输入尺寸的 RGB888
image_buffer_t dst_img;
memset(&dst_img, 0, sizeof(dst_img));
dst_img.width = stream->model_ctx.model_width;
dst_img.height = stream->model_ctx.model_height;
dst_img.format = IMAGE_FORMAT_RGB888;
dst_img.size = stream->model_ctx.model_width * stream->model_ctx.model_height * 3;
dst_img.virt_addr = (unsigned char *)malloc(dst_img.size);

// RGA 硬件完成 NV12→RGB888 + resize + letterbox(一次调用)
letterbox_t letterbox;
convert_image_with_letterbox(&src_img, &dst_img, &letterbox, 114);

// 传给推理函数(格式 RGB888,尺寸匹配模型输入)
img = dst_img;

注意dst_img.virt_addr 需要在整个流生命周期内可复用(只需分配一次),建议将其存储在 stream_runtime_t 中,替换原有的 rgb_buffer

2. inference_yolov5_model() (rknpu2/yolov5.cc)

无需修改。

当前函数第 182-184 行会检查:

cpp 复制代码
if (img->width == app_ctx->model_width &&
    img->height == app_ctx->model_height &&
    img->format == IMAGE_FORMAT_RGB888)
{
    input_buf = img->virt_addr;  // 走快路径,直接使用
}

由于 main.cc 已通过 RGA 将图像预处理为尺寸匹配的 RGB888,此快路径条件满足,函数会直接将缓冲区送入 NPU,无需任何额外的 CPU 转换。

3. convert.py --- 模型转换

无需修改。

RGA 硬件输出的 RGB888 数据与 ONNX/RKNN 模型期望的输入格式完全一致。


方案对比

方案 CPU 负载 硬件加速 延迟 代码改动量
当前:CPU nv12_to_rgb() + 软件 resize 高(逐像素)
RGA 硬件 NV12→RGB888 + resize 极低 RGA 硬件 极低(~1ms) main.cc 少量修改
pass_through=1 直接送 NV12 NPU 需修改 ONNX 图,侵入性强
修改模型首层接受 NV12 NPU 需重新训练/修改模型,复杂度高

推荐方案:RGA 硬件加速转换------这是 Rockchip 平台的标准做法,代码改动最小,性能收益最大。


RGA 转换关键代码路径

复制代码
main.cc
  |
  v
convert_image_with_letterbox(src=NV12, dst=RGB888)
  |
  v
convert_image()
  |-- try: convert_image_rga()        ← RGA 硬件 `improcess()`
  |      成功 → NV12→RGB888 + resize + letterbox 一次完成
  |
  |-- fail fallback: convert_image_cpu()
          `src->format != dst->format` → 返回 -1
          (CPU 路径不支持跨格式转换,RGA 不可用时 NV12 路线不可用)

当前平台的 RGA 硬件至关重要。如果 RGA 不可用(如 RGA 驱动未加载),建议保持原有的 CPU nv12_to_rgb() 路径作为回退。

相关推荐
byte轻骑兵8 小时前
【HID】规范精讲[19]: 蓝牙HID设备SDP交互实战——从服务搜索到属性解析的全流程拆解
人工智能·人机交互·键盘·hid·蓝牙遥控
wenzhangli78 小时前
OoderAI V3.5.0 技术白皮书——NLP 驱动的 AI 原生开发平台
人工智能·自然语言处理
INosdfgs8 小时前
nICEnnnnnnnLeeBilibiliDown:B站视频下载工具
其他·音视频
Tech_D8 小时前
微米级的精准魔法:激光微加工,解锁高端制造新可能
人工智能·单片机·机器人·自动化·制造
EasyGBS8 小时前
平安校园安防升级,国标GB28181视频平台EasyGBS实现全区域视频无死角合规管控
音视频
布吉岛的石头8 小时前
Java 程序员第 22 阶段:Function Call 工具调用实战,Java 封装大模型外部能力
java·人工智能·python
GlobalInfo8 小时前
十五五规划开启,人工智能操控无人机市场走向何方?2026-2032年市场前景深度分析
人工智能·无人机
zhangxingchao8 小时前
AI 大模型核心四:工程体系化思维
前端·人工智能·后端
英辰朗迪AI获客8 小时前
AI动态简报之技术前沿篇(2026.05.25)
人工智能