4. 视频流格式转换
在RKNN视频流部署中,摄像头采集到的原始数据通常不是我们熟悉的RGB格式,而是YUV家族格式(如NV12、YUYV、NV21等)。NPU推理前,图像必须经过格式转换、尺寸缩放、ROI裁剪等预处理操作,而这些操作在RK平台上通常由RGA(Rockchip Graphics Acceleration)硬件完成。如果对像素格式的理解不到位,轻则出现颜色偏绿、偏紫,重则内存越界、花屏、推理精度大幅下降。本章将系统梳理YUV、RGB、NV12等格式的存储原理、转换方法,并结合RGA的输入输出约束,给出可直接落地的工程实践方案。
上图展示了从摄像头采集到NPU推理的完整数据链路。核心要点在于:摄像头输出的是YUV格式,而RKNN模型输入通常要求RGB或BGR格式,中间的转换与预处理必须由RGA硬件完成,避免CPU参与像素级操作导致性能瓶颈。下面我们深入解析各格式的底层原理与工程转换方法。
4.1 核心原理:YUV与RGB的数学本质与存储差异
YUV色彩空间将亮度信息(Y)与色度信息(U/V)分离,其设计初衷是兼容黑白电视与彩色电视信号。人眼对亮度变化敏感,对色度变化相对迟钝,因此可以通过降低色度采样率来压缩数据量,这就是YUV 4:2:0、4:2:2等采样格式的由来。
在RKNN部署场景中,最常遇到的YUV格式是NV12(YUV420SP)和YUYV(YUV422)。NV12的存储布局为:先存储完整的Y平面(W×H字节),随后存储UV交错平面(W×H/2字节),其中U和V交替排列。YUYV则采用Y0 U0 Y1 V0的打包方式,每两个像素共享一对UV分量。
RGB与YUV之间的转换公式(BT.601标准)如下:
Y = 0.299R + 0.587G + 0.114B
U = -0.169R - 0.331G + 0.500B + 128
V = 0.500R - 0.419G - 0.081B + 128
R = Y + 1.402(V - 128)
G = Y - 0.344(U - 128) - 0.714(V - 128)
B = Y + 1.772(U - 128)
在RK3588/RK3568平台上,RGA硬件内置了BT.601、BT.709等多种转换矩阵,开发者无需手动实现浮点运算,只需正确配置RGA的输入输出格式即可。
| 格式名称 | 采样方式 | 存储布局 | 每像素字节数 | 典型来源 |
|---|---|---|---|---|
| NV12 | YUV420SP | Y平面 + UV交错 | 1.5 | RK ISP、H.264/H.265解码 |
| NV21 | YUV420SP | Y平面 + VU交错 | 1.5 | 部分Android摄像头 |
| YUYV | YUV422 | Y0 U0 Y1 V0打包 | 2.0 | UVC免驱摄像头 |
| RGB888 | RGB | R G B连续排列 | 3.0 | 模型输入标准格式 |
| BGR888 | BGR | B G R连续排列 | 3.0 | OpenCV默认格式 |
4.2 判断方法:如何识别当前视频流的像素格式
在实际开发中,摄像头输出的格式往往由驱动或V4L2配置决定。开发者需要掌握以下判断方法:
- 查看V4L2格式描述符:通过
v4l2-ctl --list-formats-ext命令查看摄像头支持的像素格式,输出中会明确标注NV12、YUYV等标识。 - 检查解码器输出:如果视频流来自H.264/H.265解码,RKMPP解码器默认输出NV12格式,可通过解码器回调的
frame_info结构体确认。 - 分析内存布局:通过打印首行像素的十六进制值,结合已知颜色(如纯红色)判断格式。例如纯红色在NV12中Y=76, U=84, V=255,在RGB中则为(255,0,0)。
- 使用RGA格式探测:调用RGA的
query_format接口,传入待检测的内存地址和大小,RGA会返回格式是否受支持。
4.3 操作流程:基于RGA的NV12到RGB888转换实战
RGA(Rockchip Graphics Accelerator)是RK平台上的2D硬件加速器,支持格式转换、缩放、旋转、裁剪等操作。以下是通过librga库完成NV12到RGB888转换的标准流程:
#include <rga/RgaApi.h>
// 1. 初始化RGA
RgaInit();
// 2. 填充源图像信息
rga_info_t src_info;
memset(&src_info, 0, sizeof(src_info));
src_info.fd = -1; // 使用虚拟地址
src_info.virAddr = (void*)nv12_buffer;
src_info.mmuFlag = 1;
src_info.rect.xoffset = 0;
src_info.rect.yoffset = 0;
src_info.rect.width = width;
src_info.rect.height = height;
src_info.rect.wstride = width; // 注意:需要16字节对齐
src_info.rect.hstride = height;
src_info.format = RK_FORMAT_YCbCr_420_SP; // NV12
// 3. 填充目标图像信息
rga_info_t dst_info;
memset(&dst_info, 0, sizeof(dst_info));
dst_info.fd = -1;
dst_info.virAddr = (void*)rgb_buffer;
dst_info.mmuFlag = 1;
dst_info.rect.xoffset = 0;
dst_info.rect.yoffset = 0;
dst_info.rect.width = width;
dst_info.rect.height = height;
dst_info.rect.wstride = width;
dst_info.rect.hstride = height;
dst_info.format = RK_FORMAT_RGB_888;
// 4. 执行转换
int ret = RgaBlit(&src_info, &dst_info, NULL);
if (ret != 0) {
printf("RGA转换失败: %s\n", strerror(errno));
}
// 5. 释放资源
RgaDeInit();
关键参数说明:wstride和hstride表示内存中实际的行宽和行高,必须与分配内存时的对齐值一致。例如,如果使用dma_buffer分配内存,通常需要按64字节对齐,此时wstride应设置为对齐后的值,而不是图像的逻辑宽度。
4.4 案例拆解:UVC摄像头实时流接入RKNN推理
背景与约束:某安防项目需要将USB摄像头采集的视频流接入RK3588平台进行实时人形检测。摄像头输出YUYV格式,分辨率1920×1080@30fps,模型输入要求RGB888格式640×640分辨率。系统CPU负载需控制在30%以内,内存带宽有限。
初始现象:直接使用OpenCV的cvtColor函数进行YUYV→BGR转换,再使用resize缩放,CPU占用率飙升至85%,帧率跌至12fps,无法满足实时性要求。
分析过程:通过性能剖析发现,CPU主要耗时在色彩转换(约18ms/帧)和缩放(约12ms/帧)。YUYV到BGR的转换涉及大量浮点运算,且OpenCV的resize在非整数倍缩放时效率较低。
优化方案:改用RGA硬件完成全部预处理,具体步骤如下:
- 使用V4L2采集YUYV数据,通过
mmap映射到用户空间。 - 调用RGA将YUYV转换为NV12(减少一次CPU拷贝,且NV12是后续处理的中间格式)。
- 再次调用RGA将NV12缩放至640×640,并同时转换为RGB888。
- 将RGB888数据直接传递给RKNN输入接口。
结果对比:优化后CPU占用率降至8%,帧率稳定在30fps,且RGA处理单帧耗时仅2.3ms(YUYV→NV12)和1.8ms(NV12→RGB888+缩放),完全满足实时性要求。
风险与代价:RGA转换的精度略低于CPU浮点转换(色差ΔE约1.5),但对人形检测任务影响可忽略。此外,RGA对输入内存有对齐要求,需要额外分配对齐缓冲区,增加了约5%的内存开销。
4.5 常见误区与总结复盘
误区一:忽视stride对齐。许多开发者直接使用图像宽度作为stride,导致RGA处理时出现花屏或内存越界。正确做法是使用dma_alloc或rockchip_mpp_alloc分配对齐内存,并获取实际stride值。
误区二:混淆NV12与NV21。两者UV排列顺序相反,如果配置错误,图像会呈现紫色/绿色色调。在调试时可通过打印U/V分量值快速判断。
误区三:在CPU上做不必要的格式转换。例如先转成BGR再转成RGB,或使用OpenCV的Mat操作处理YUV数据。这些操作应全部交给RGA完成。
误区四:忽略色彩空间标准。摄像头输出的YUV可能基于BT.601或BT.709标准,而模型训练时可能使用不同的标准。RGA默认使用BT.601,如果模型基于BT.709训练,需要额外配置RGA的色彩空间参数。
| 检查项 | 验证方法 | 预期结果 |
|---|---|---|
| 格式识别 | 打印首行像素值 | 纯红色在NV12中Y≈76, U≈84, V≈255 |
| stride对齐 | 检查RGA返回的wstride | wstride ≥ width,且为16的倍数 |
| 色彩正确性 | 显示转换后的RGB图像 | 肤色自然,无偏色 |
| 性能验证 | 使用perf工具统计 | RGA单帧处理 < 5ms |
总结而言,掌握YUV/RGB格式的本质差异和RGA的硬件特性,是构建高效RKNN视频流应用的基础。建议开发者在项目初期就明确数据链路中的每一步格式和内存布局,避免后期返工。在实际调试中,准备一张标准色卡(如Macbeth色卡)作为参照,可以快速定位色彩转换问题。