C++开发学习笔记(二)

啃cpp感觉还是太痛苦了,啃得甚至感觉有些动摇了,我最近一直在看Rust,但可惜秋招市场还是cpp为主,咬咬牙还是接着学吧。

我打算把目前CPU做的一些YUUV -> BGR 这一部分迁移到使用瑞芯微提供的RGA进行硬件加速,熟悉这个Demo。

编译

首先需要把RGA的库加入编译。

1
2
3
4
5
6
7
8
9
10
11
12
add_library(rga_runtime SHARED IMPORTED)
set_target_properties(rga_runtime PROPERTIES
    IMPORTED_LOCATION "${THIRD_PARTY}/librga/lib/librga.so"
)
target_include_directories(yolo_camera_demo PRIVATE
    "${THIRD_PARTY}/librga/include"
)
target_link_libraries(yolo_camera_demo PRIVATE rga_runtime)
set_target_properties(yolo_camera_demo PROPERTIES
    BUILD_WITH_INSTALL_RPATH TRUE
    INSTALL_RPATH "$ORIGIN/../3rdparty/librga/lib"
)
  1. 声明导入库(add_library): SHARED表示动态库.so,IMPORTED表示它不是由项目编译生成,而是外部库;
  2. 指定导入库的实际路径(set_target_properties):把刚刚导入的库和实际路径连接;
  3. 给项目添加头文件目录(target_include_directories):让项目在编译阶段能搜索到库的头文件,PRIVATE表示只对项目yolo_camera_demo生效;
  4. 链接静态库(target_link_libraries):在链接阶段让库参与链接;
  5. 设置安装后的rpath(set_target_properties):在运行阶段让库参与运行。

NV12 -> BGR

这一部分说起来没有那么难,主要参考官方文档和官方例程。不过在啃代码的地方看到一些AI的雷霆写法:

1
2
3
4
5
6
7
8
9
bt709_ = enc == V4L2_YCBCR_ENC_709;
//由于==的优先级更高,因此等效为:
bt709_ = (enc == V4L2_YCBCR_ENC_709);
//等效为:
if (enc == V4L2_YCBCR_ENC_709) {
bt709_ = true;
} else {
bt709_ = false;
}
像素格式

扯远了,接下来回到正题。
原程序中的图像格式转换是在core.hpp中,做了一些固定权重的转换。在主程序中:

1
2
3
4
5
6
7
8
    for (unsigned row = 0; row < height_; ++row) {
        auto* dst = frame.bgr.ptr<cv::Vec3b>(row);
        const auto* chroma = uv + size_t(row / 2) * stride_;
        for (unsigned col = 0; col < width_; ++col) {
            const auto c = yuvToBgr(y[size_t(row) * stride_ + col], chroma[col & ~1u], chroma[(col & ~1u) + 1], full_, bt709_);
            dst[col] = {c.b, c.g, c.r};
        }
    }

首先要理解NV12的像素格式,是一种半压缩的视频流格式,Y表示亮度,U表示蓝色差,V表示红色差。形如:

1
2
3
4
5
6
7
8
9
Y:
Y00 Y01 Y02 Y03
Y10 Y11 Y12 Y13
Y20 Y21 Y22 Y23
Y30 Y31 Y32 Y33

UV:
U00 V00 U01 V01
U10 V10 U11 V11

其次还有一个色域的标准BT.709,他定义了一套色度转换系数,如下所示:
$$
\begin{bmatrix} R \ G \ B \end{bmatrix} =
\begin{bmatrix}
1.16438 & 0.00000 & 1.79274 \
1.16438 & -0.21325 & -0.53291 \
1.16438 & 2.11240 & 0.00000
\end{bmatrix}
\begin{bmatrix} Y-16 \ U-128 \ V-128 \end{bmatrix}
$$
除此之外,还有其他的BT.601,色域更窄一些,如果使用错误的标准,会导致颜色被压缩,画面暗淡。

wrapbuffer_virtualaddr_t

知道标准之后,那么最核心的一句就可以确定下来了:

1
2
3
4
5
6
7
    const IM_STATUS status = imcvtcolor(
        src,// input image
        dst,// output image
        RK_FORMAT_YCbCr_420_SP,// source image format
        RK_FORMAT_BGR_888,// destination image format
        IM_YUV_TO_RGB_BT601_FULL// color space mode
    );

但其中,src和dst要提前定义内存空间:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
    auto* nv12 = static_cast<uint8_t*>(mem.ptr)
        + latest_plane.data_offset;
    const auto src = wrapbuffer_virtualaddr_t(
        nv12,
        width_,
        height_,
        stride_,height_,
        RK_FORMAT_YCbCr_420_SP
    );

    const auto dst = wrapbuffer_virtualaddr_t(
        frame.bgr.data,
        frame.bgr.cols,
        frame.bgr.rows,
        static_cast<int>(frame.bgr.step / frame.bgr.elemSize()),
        frame.bgr.rows,
        RK_FORMAT_BGR_888
    );

这里填充的参数就不过多解释了,主要是wrapbuffer_virtualaddr_t,他的主要作用是申请图像的缓冲区,我们这里是从V4L2拿的数据,目前还需要拷贝一下。

图像内存

MMAP

我有点讨厌cpp这种事无巨细的管理,不过这也是高性能的来源。相机的数据先被搬到内存中后,再由内核建立联系,这个桥梁就是MMAP。组合使用 ioctl 和 mmap,才能访问到V4L2的视频流。

bytesused/offset/stride
  • bytesused 可以理解为申请视频帧缓存的有效内容。
  • offset 表示有效数据的起点。
  • stride表示一行数据这内存中占用的字节数。
1
有效数据 = buffer[offset, offset + bytesused)

参考资料


C++开发学习笔记(二)
http://blog.mingxuan.xin/2026/10/02/20261002/
作者
Obscure
发布于
2026年10月2日
许可协议