libyuv 入门指南:高性能图像处理库

libyuv 是 Google 开源的高性能跨平台图像处理库,提供 YUV/RGB 转换、缩放、旋转等功能,广泛应用于 WebRTC、Chromium 等项目。


一、简介

libyuv 是一个开源跨平台 C/C++ 库,专注于图像和视频的处理加速。它提供了丰富的函数接口,可实现颜色空间转换、图像旋转、缩放、镜像等操作。

项目地址:https://chromium.googlesource.com/libyuv/libyuv/

主要特点

特性 说明
高性能 优化算法 + SIMD 指令集(SSE/AVX/NEON)
跨平台 Windows、Linux、macOS、Android、iOS
跨架构 x86、x64、ARM、ARM64
易集成 简单 API,无外部依赖
开源免费 Apache 2.0 协议

二、核心功能

2.1 颜色空间转换

支持多种格式之间的相互转换:

YUV ↔ RGB

  • I420、I422、I444 ↔ RGB24、RGBA、ARGB
  • NV12、NV21 ↔ RGB 系列

YUV 格式互转

  • I420 ↔ NV12/NV21
  • I420 ↔ I422/I444

2.2 图像缩放

支持多种缩放算法:

算法 特点 适用场景
kFilterNone 最近邻插值 速度快,质量一般
kFilterLinear 双线性插值 平衡速度与质量
kFilterBilinear 双线性插值 同上
kFilterBox 盒滤波 缩小时效果好

2.3 图像旋转

支持任意角度旋转:

  • 0°、90°、180°、270° 快速旋转
  • 镜像翻转(水平/垂直)

2.4 其他功能

  • 图像裁剪
  • 图像复制
  • 原始数据转换

三、编译与集成

3.1 CMake 编译

git clone https://chromium.googlesource.com/libyuv/libyuv
cd libyuv
mkdir build && cd build
cmake ..
make -j4
make install

3.2 Windows 编译

使用 Visual Studio 打开 CMakeLists.txt 或直接使用 CMake GUI。

3.3 集成到项目

# CMakeLists.txt
find_package(libyuv REQUIRED)
target_link_libraries(your_target libyuv::yuv)

四、常用 API 示例

4.1 I420 → RGB24 转换

#include "libyuv.h"

int i420_to_rgb24(const uint8_t* src_y,
                   const uint8_t* src_u,
                   const uint8_t* src_v,
                   uint8_t* dst_rgb24,
                   int width,
                   int height) {
    int src_stride_y = width;
    int src_stride_uv = width / 2;
    int dst_stride_rgb24 = width * 3;
    
    return I420ToRGB24(src_y, src_stride_y,
                       src_u, src_stride_uv,
                       src_v, src_stride_uv,
                       dst_rgb24, dst_stride_rgb24,
                       width, height);
}

4.2 图像缩放

#include "libyuv.h"

int scale_i420(const uint8_t* src_y,
               const uint8_t* src_u,
               const uint8_t* src_v,
               uint8_t* dst_y,
               uint8_t* dst_u,
               uint8_t* dst_v,
               int src_width, int src_height,
               int dst_width, int dst_height) {
    int src_stride_y = src_width;
    int src_stride_uv = src_width / 2;
    int dst_stride_y = dst_width;
    int dst_stride_uv = dst_width / 2;
    
    return I420Scale(src_y, src_stride_y,
                     src_u, src_stride_uv,
                     src_v, src_stride_uv,
                     src_width, src_height,
                     dst_y, dst_stride_y,
                     dst_u, dst_stride_uv,
                     dst_v, dst_stride_uv,
                     dst_width, dst_height,
                     kFilterBilinear);
}

4.3 图像旋转

#include "libyuv.h"

int rotate_i420(const uint8_t* src_y,
                const uint8_t* src_u,
                const uint8_t* src_v,
                uint8_t* dst_y,
                uint8_t* dst_u,
                uint8_t* dst_v,
                int width, int height,
                int rotation) {  // 0, 90, 180, 270
    int src_stride_y = width;
    int src_stride_uv = width / 2;
    
    // 旋转后宽高可能互换
    int dst_width = (rotation == 90 || rotation == 270) ? height : width;
    int dst_height = (rotation == 90 || rotation == 270) ? width : height;
    int dst_stride_y = dst_width;
    int dst_stride_uv = dst_width / 2;
    
    return I420Rotate(src_y, src_stride_y,
                      src_u, src_stride_uv,
                      src_v, src_stride_uv,
                      dst_y, dst_stride_y,
                      dst_u, dst_stride_uv,
                      dst_v, dst_stride_uv,
                      width, height,
                      (enum RotationMode)rotation);
}

4.4 NV12 → I420 转换

#include "libyuv.h"

int nv12_to_i420(const uint8_t* src_y,
                 const uint8_t* src_uv,
                 uint8_t* dst_y,
                 uint8_t* dst_u,
                 uint8_t* dst_v,
                 int width, int height) {
    int src_stride_y = width;
    int src_stride_uv = width;
    int dst_stride_y = width;
    int dst_stride_uv = width / 2;
    
    return NV12ToI420(src_y, src_stride_y,
                      src_uv, src_stride_uv,
                      dst_y, dst_stride_y,
                      dst_u, dst_stride_uv,
                      dst_v, dst_stride_uv,
                      width, height);
}

五、libyuv vs libswscale 性能对比

5.1 性能测试数据

操作 图像尺寸 libyuv libswscale 性能比
RGB → YUV 转换 4096×4096 11:1
缩放 4096×4096 → 2048×2048 6:1

数据来源:https://www.nxrte.com/jishu/50785.html

5.2 功能对比

特性 libyuv libswscale
性能 极高(SIMD 优化) 较高
缩放算法 较少 丰富
颜色空间支持 主流格式 非常全面
多线程 需自行实现 内置支持
依赖 依赖 FFmpeg
授权 Apache 2.0 LGPL/GPL

5.3 选型建议

选择 libyuv

  • 实时视频处理(WebRTC、直播)
  • 嵌入式设备(资源受限)
  • 对性能要求极高的场景
  • 只需常见格式转换

选择 libswscale

  • 需要支持冷门格式
  • 需要多种缩放算法
  • 已集成 FFmpeg 的项目
  • 对性能不敏感的场景

注意

  • libswscale 可通过多线程达到与 libyuv 相近的性能,但 CPU 占用更高
  • 实际场景建议分别测试,根据具体需求选择

六、性能优化建议

6.1 内存对齐

// 分配对齐内存,提升 SIMD 访问效率
uint8_t* aligned_buffer = (uint8_t*)aligned_malloc(width * height * 3 / 2, 16);

6.2 批量处理

// 批量处理多帧,减少函数调用开销
for (int i = 0; i < frame_count; i++) {
    I420ToRGB24(frames[i].y, frames[i].stride_y, ...);
}

6.3 多线程处理

// 多线程处理不同区域
#pragma omp parallel for
for (int row = 0; row < height; row += 16) {
    // 处理 16 行
}

七、常见问题

Q1:libyuv 支持哪些 YUV 格式?

主要支持:

  • Planar:I420、I422、I444、YV12、YV16、YV24
  • Semi-Planar:NV12、NV21、NV16、NV24
  • Packed:YUY2、UYVY、UYVY

Q2:如何判断编译时是否启用了 SIMD?

#ifdef LIBYUV_ENABLE_NEON
    printf("NEON enabled\n");
#endif
#ifdef LIBYUV_ENABLE_SSE2
    printf("SSE2 enabled\n");
#endif
#ifdef LIBYUV_ENABLE_AVX2
    printf("AVX2 enabled\n");
#endif

Q3:libyuv 线程安全吗?

大部分函数是线程安全的,但不要同时操作同一块内存区域。


参考资源

官方资源

  • 源码:https://chromium.googlesource.com/libyuv/libyuv/
  • API 文档:https://chromium.googlesource.com/libyuv/libyuv/+/main/include/libyuv.h

推荐阅读

  • libyuv 详解:https://blog.csdn.net/gitblog_00062/article/details/136704290
  • 使用示例:https://www.cnblogs.com/eastgeneral/p/16590622.html
  • libyuv vs libswscale:https://www.nxrte.com/jishu/50785.html

[本文整理自学习笔记,如有疏漏欢迎指正]

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐