走进音视频的世界——RGB与YUV格式

2023-11-09

在图像的世界里,一般使用RGB作为存储格式。而在视频的世界里,一般使用YUV作为压缩存储格式。有时候面试官会问:为什么视频使用YUV来压缩存储,而不用RGB?YUV与RGB有什么区别,两者如何转换的?常见的RGB格式有哪些,常见的YUV格式又有哪些?手机摄像头的预览格式是什么,如何转换为YUV420P的?我们带着这些问题,来揭开RGB与YUV格式的面纱。 

 

目录

一、RGB格式

1、RGBA8888

2、RGB565

3、图像的像素阵列

二、YUV格式

1、YUV420p

2、YUV420sp

3、NV21

三、RGB与YUV转换

四、NV21转换为YUV420p

五、YUV旋转


一、RGB格式

RGB是一种图像存储格式,也是三原色,取值范围[0, 255]。R代表Red红色,G代表Green绿色,B代表Blue蓝色。在openCV中,一般使用BGR格式。在图像中,一般使用32位色的ARGB(或RGBA)代表一个像素,其中A代表Alpha透明度。常见的RGB格式有RGB888、RGBA8888、RGB565等。

1、RGBA8888

关于RGBA8888格式,每个通道占8位,即一个字节。四个通道构成一个像素,总共占32位。排列顺序如下图所示:

2、RGB565

关于RGB565格式,其中R占5位,G占6位,B占5位。三个通道构成一个像素,总共占16位。排列顺序如下图所示:

3、图像的像素阵列

一张图像由宽x高的像素阵列构成,为了内存对齐,会使用stride来填充。如下图所示,由4x3构成的像素阵列,其中P代表pixel:

二、YUV格式

YUV是一种视频压缩存储格式。其中Y代表Luma亮度,U代表Chroma色度,V代表Contrast对比度。常见的YUV采样比例如下:

  • 4:4:4 表示完全采样
  • 4:2:2 表示水平2:1采样,垂直完全采样
  • 4:2:0 表示水平2:1采样,垂直2:1采样
  • 4:1:1 表示水平4:1采样,垂直完全采样

常见的YUV格式有:YUV420p、YUV420sp、NV21等。由于U和V分量都是Y分量的1/4,而RGB888的所有分量占比都是1。进一步可得,YUV整体占比是3/2,RGB整体占比是6/2,YUV所占存储空间比RGB少了3/2。因此,默认采用YUV作为视频压缩存储格式。

1、YUV420p

YUV420p属于平面存储,YUV分量占比为4:1:1,即每4个Y共享一组UV。先是Y分量,然后是U分量,最后是V分量。排列如下图所示:

2、YUV420sp

YUV420sp属于交错存储,YUV分量占比为4:1:1,即每4个Y共享一组UV。先是Y分量,然后是UV分量交错存储。排列如下图所示:

3、NV21

NV21属于交错存储,YUV分量占比为4:1:1,即每4个Y共享一组UV。Android手机摄像头预览数据默认是NV21格式。和YUV420sp的区别是,NV21是VUVU这样排列,如下图所示:

三、RGB与YUV转换

关于YUV与RGB的转换公式,可参考ITU标准:https://www.itu.int/rec/R-REC-BT.601。也可以参考维基百科:https://zh.wikipedia.org/wiki/YUV。咱们来看下转换公式:

以rgb转yuv为例,示例代码如下:

void rgb_to_yuv(int8_t *yuv, int *rgb, int width, int height) {
    int rgbIndex = 0;
    int yIndex = 0;
    int uIndex = width * height;
    int vIndex = width * height * 5 / 4;
    int R, G, B;
    float Y, U, V;

    // 遍历图像,获取所有像素点 
    for (int i = 0; i < height; i++) {
        for (int j = 0; j < width; j++) {
            // 从像素点获取R、G、B分量
            R = (rgb[rgbIndex] & 0xFF0000) >> 16;
            G = (rgb[rgbIndex] & 0xFF00) >> 8;
            B = (rgb[rgbIndex] & 0xFF);
            // 使用公式把RGB转成YUV
            Y = 0.299 * R + 0.587 * G + 0.114 * B;
            U = -0.147 * R - 0.289 * G + 0.436 * B;
            V = 0.615 * R - 0.515 * G - 0.100 * B;
            // YUV分量赋值给yuv数组
            yuv[yIndex++] = (int8_t)Y;
            if (i % 2 == 0 && j % 2 == 0) {
                yuv[uIndex++] = (int8_t) U;
                yuv[vIndex++] = (int8_t) V;
            }
            rgbIndex++;
        }
        
    } 
}

四、NV21转换为YUV420p

由于NV21是交错存储,4个Y共享一组UV,而且是VUVU这样排列。所以,我们需要把偶数的V分量、奇数的U分量读出来,然后赋值给YUV420p。代码如下:

static void nv21_to_yuv420p(int8_t *dst, int8_t *src, int len) {
    memcpy(dst, src, len); // y
    for (int i = 0; i < len / 4; ++i) {
        *(dst + len + i) = *(src + len + i * 2 + 1);  // u
        *(dst + len * 5 / 4 + i) = *(src + len + i * 2); // v
    }
}

五、YUV旋转

YUV的存储是有旋转角度的存在。在手机拍摄时,按照逆时针来看,横屏向左是0度,竖屏向下是90度,横屏向右是180度,竖屏向上是270度。既然有旋转角度,我们就需要对YUV进行旋转处理,代码如下:

static void yuv420p_rotate90(int8_t *dst, const int8_t *src, int width, int height) {
    int n = 0;
    int wh = width * height;
    int half_width = width / 2;
    int half_height = height / 2;
    // y
    for (int j = 0; j < width; j++) {
        for (int i = height - 1; i >= 0; i--) {
            dst[n++] = src[width * i + j];
        }
    }
    // u
    for (int i = 0; i < half_width; i++) {
        for (int j = 1; j <= half_height; j++) {
            dst[n++] = src[wh + ((half_height - j) * half_width + i)];
        }
    }
    // v
    for (int i = 0; i < half_width; i++) {
        for (int j = 1; j <= half_height; j++) {
            dst[n++] = src[wh + wh / 4 + ((half_height - j) * half_width + i)];
        }
    }
}

static void yuv420p_rotate180(int8_t *dst, const int8_t *src, int width, int height) {
    int n = 0;
    int half_width = width / 2;
    int half_height = height / 2;
    // y
    for (int j = height - 1; j >= 0; j--) {
        for (int i = width; i > 0; i--) {
            dst[n++] = src[width * j + i - 1];
        }
    }
    // u
    int offset = width * height;
    for (int j = half_height - 1; j >= 0; j--) {
        for (int i = half_width; i > 0; i--) {
            dst[n++] = src[offset + half_width * j + i - 1];
        }
    }
    // v
    offset += half_width * half_height;
    for (int j = half_height - 1; j >= 0; j--) {
        for (int i = half_width; i > 0; i--) {
            dst[n++] = src[offset + half_width * j + i - 1];
        }
    }
}

static void yuv420p_rotate270(int8_t *dst, const int8_t *src, int width, int height) {

    for (int j = 0; j < width; j++) {
        for (int i = 1; i <= height; i++) {
            *dst++ = *(src + i * width - j);
        }
    }

    auto *src_u = const_cast<int8_t *>(src + width * height);
    for (int j = 0; j < width / 2; j++) {
        for (int i = 1; i <= height / 2; i++) {
            *dst++ = *(src_u + i * width / 2 - j);
        }
    }

    auto *src_v = const_cast<int8_t *>(src + width * height * 5 / 4);
    for (int j = 0; j < width / 2; j++) {
        for (int i = 1; i <= height / 2; i++) {
            *dst++ = *(src_v + i * width / 2 - j);
        }
    }
}

static void yuv420p_rotate(int8_t *dst, int8_t *src, int width, int height, int degree) {
    switch(degree) {
        case 0:
            memcpy(dst, src, width * height * 3 / 2);
            break;
        case 90:
            yuv420p_rotate90(dst, src, width, height);
            break;
        case 180:
            yuv420p_rotate180(dst, src, width, height);
            break;
        case 270:
            yuv420p_rotate270(dst, src, width, height);
            break;
        default:
            break;
    }
}

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

走进音视频的世界——RGB与YUV格式 的相关文章

随机推荐

  • ESP32 之 ESP-IDF 教学(十四)——虚拟文件系统(VFS)

    本文章 来自原创专栏 ESP32教学专栏 基于ESP IDF 讲解如何使用 ESP IDF 构建 ESP32 程序 发布文章并会持续为已发布文章添加新内容 每篇文章都经过了精打细磨 通过下方对话框进入专栏目录页 CSDN 请求进入目录 O
  • 安卓上基于透明代理抓包

    前言 在安卓上基于透明代理对特定APP抓包中使用的是redsocks2 本文演示如何使用clash实现同样的效果 你以为是Clash For Android 错 这里使用的是Core版本 就原理上来说 和前一篇文章并无区别 Clash的优势
  • Linux网络协议栈(五) -- 数据包的发送(based in 2.6.32)

    一 关键数据结构 对于输出封包 设备的数据结构主要包括两个 输出队列 queue 和输出队列规则 queue discipline 我们首先来看输出队列 2 6 18内核中无该结构体 struct netdev queue structne
  • 谈谈App混合开发

    混合开发的App Hybrid App 就是在一个App中内嵌一个轻量级的浏览器 一部分原生的功能改为Html 5来开发 这部分功能不仅能够在不升级App的情况下动态更新 而且可以在Android或iOS的App上同时运行 让用户的体验更好
  • 技术人修炼之道阅读笔记(六)解决对抗性思维方法

    技术人修炼之道阅读笔记 六 解决对抗性思维方法 网上经常看见产品经理和开发人员的段子 例如 杀死一个开发人员不用枪 只需要改三次需求 技术人开会 产品经理能安然无恙坐在这里 是因为门外有保安 等 这些都说明了产品经理和开发人员的矛盾 那么问
  • qt检查文件夹是否有写权限

    Qt 使用如下函数能够判断路径或者文件是否可写 bool QFileInfo isWritable const 对于win10系统实测 结果不准确 继续排查 官方文档描述 a 如果未启用 NTFS 权限检查 Windows 上的结果将仅反映
  • IDEA 程序包不存在,找不到符号但是明明存在对应的jar包 的解决方案

    注 本人刚开始是使用 Settings gt Build gt Build Tools gt Maven gt Runner gt 勾选上Delegagte IDE build run actions to Maven 这种办法 成功解决了
  • 内核管理-之进程虚拟内存-基于linux3.10

    关于启动过程内存管理见 内存管理 之启动 关于内核空间内存管理见 内存管理 之内核内存管理 如果需要 内存管理五章整理成pdf了 下载地址http download csdn net detail shichaog 8662135 进程的虚
  • hashmap 允许key重复吗_搞懂 HashMap,这一篇就够了

    HashMap 概述 如果你没有时间细抠本文 可以直接看 HashMap 概述 能让你对 HashMap 有个大致的了解 HashMap 是 Map 接口的实现 HashMap 允许空的 key value 键值对 HashMap 被认为是
  • Flutter页面关闭时Crash

    Flutter页面关闭时Crash 问题描述 项目中使用了Flutter的WebView组件来加载H5 关闭Activity时 如果FLutter页面中包含 WebView 就会导致Crash 页面中如果没有WebView 则正常关闭 Fl
  • 分布式一致性算法--Paxos

    分布式一致性算法 Paxos 2017 08 31 10 15 0人阅读 评论 0 收藏 编辑 删除 分类 Paxos 1 分布式一致性算法 Paxos Paxos算法是莱斯利 兰伯特 Leslie Lamport 1990年提出的一种基于
  • 深入探索Android稳定性优化

    前言 成为一名优秀的Android开发 需要一份完备的知识体系 在这里 让我们一起成长为自己所想的那样 众所周知 移动开发已经来到了后半场 为了能够在众多开发者中脱颖而出 我们需要对某一个领域有深入地研究与心得 对于Android开发者来说
  • Caffe解惑:为什么Caffe里头有mutable_cpu_data和cpu_data

    一开始看代码的时候会纳闷 为啥caffe里头又一个 cpu data 还要有一个mutable cpu data 其实从字面就可以看出来这个mutable就是易变的意思啦 二话不说翻出源代码看看究竟有啥区别 const void Synce
  • C# 入门教程

    C 简介 C 是一个简单的 现代的 通用的 面向对象的编程语言 它是由微软 Microsoft 开发的 下面列出了C 成为一种广泛应用的专业语言的原因 现代的 通用的编程语言 面向对象 面向组件 容易学习 结构化语言 它产生高效率的程序 它
  • clickhouse-jdbc代码优化实现无感知故障转移及负载均衡

    clickhouse数据库一直以单点功能强悍而闻名 在单点性能上 无人出其右 但是也需要指出的是 clickhouse集群的集群管理功能却是非常薄弱的 比如说无集群统一管理功能 无集群统一读写接口 仅依靠分布式表和副本表 实现了多节点之间数
  • cas cvm服务器虚拟化安装虚拟机,CAS系统如何安装虚拟机

    1 在CVM的云平台中选择一台物理服务器右击 在弹出的选项中选择 增加虚拟机 给虚拟机配置名称 选择操作系统版本 这一步一定要注意 这里选择的操作系统版本指的是会安装对应操作系统版本的驱动 如果这里选择的版本和实际安装的操作系统版本不一致会
  • 使用Python,OpenCV和Hough圆检测图像中的圆

    使用Python OpenCV和Hough圆检测图像中的圆 1 效果图 2 cv2 HoughCircles image method dp minDist 3 源码 参考 前几篇博客中有介绍 使用OpenCV检测图形中的三角形 正方形 矩
  • Oracle sys_guid()函数

    简介 SYS GUID 是Oracle 8i 后提供的函数 产生并返回一个全球唯一的标识符 类似于Java的UUID 使用方法 select sys guid from dual 运行效果 解决乱码 select RAWTOHEX sys
  • JAVA介绍

    1 Java的简单介绍 Java作为C语言和C 的衍生物 其有很多的功能与C语言及C 的功能相似 但是又不同与C语言及C 在Java的领域中 其相对于C语言及C 所具有的特征为没有指针 没有内存管理 具有真正的可移植性 及可以跨平台进行创作
  • 走进音视频的世界——RGB与YUV格式

    在图像的世界里 一般使用RGB作为存储格式 而在视频的世界里 一般使用YUV作为压缩存储格式 有时候面试官会问 为什么视频使用YUV来压缩存储 而不用RGB YUV与RGB有什么区别 两者如何转换的 常见的RGB格式有哪些 常见的YUV格式