一、颜色编码

① RGB 颜色编码

  • RGB 三个字母分别代表了 红、绿、蓝,这三种颜色作为三个基底颜色,将它们以不同的比例相加,可以产生多种多样的颜色。
  • RGB 图像中,每个像素点都有红、绿、蓝三个基底颜色,其中每种原色都占用 8 bit,也就是一个字节(0-255),那么一个像素点也就占用 24 bit,也就是三个字节。

  • 在图像显示中,一张 1280 * 720 大小的图片,就代表着它有 1280 * 720 个像素点。其中每一个像素点的颜色显示都采用 RGB 编码方法,将 RGB 分别取不同的值,就会展示不同的颜色,就占用 1280 * 720 * 3 / 1024 / 1024 = 2.63 MB 存储空间。

② YUV 颜色编码

  • YUV 是指亮度参量色度参量分开表示的像素格式,而这样分开的好处就是不但可以避免相互干扰,还可以降低色度的采样率而不会对图像质量影响太大。
  • YUV 颜色编码采用的是明亮度色度来指定像素的颜色。
  • Y表示明亮度(Luminance、Luma) U 和 V 表示色度(Chrominance、Chroma)而色度又定义了颜色的两个方面:色调和饱和度。
    • Y 通道数值越高,图片则越亮;
    • U 通道数值越高,颜色就越接近蓝色;
    • V 通道数值越高,颜色就越接近红色。
  • YUV 颜色编码格式在默认情况下是图像和视频压缩的标准。
  • 和 RGB 表示图像类似,每个像素点都包含 Y、U、V 分量。但是它的 Y 和 UV 分量是可以分离的,如果没有 UV 分量一样可以显示完整的图像,只不过是黑白的,如下图所示:

  • 对于 YUV 图像来说,并不是每个像素点都需要包含了 Y、U、V 三个分量,根据不同的采样格式,可以每个 Y 分量都对应自己的 UV 分量,也可以几个 Y 分量共用 UV 分量。

二、RGB 与 YUV 的相互转换

  • 对于图像显示器来说,它是通过 RGB 模型来显示图像的,而在传输图像数据时又是使用 YUV 模型,这是因为 YUV 模型可以节省带宽。因此就需要采集图像时将 RGB 模型转换到 YUV 模型,显示时再将 YUV 模型转换为 RGB 模型。
  • RGB 到 YUV 的转换,就是将图像所有像素点的 R、G、B 分量转换到 Y、U、V 分量。公式如下:
    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 到 RGB 的转换如下:
    R = Y + 1.14 * V
    G = Y - 0.39 * U - 0.58 * V
    B = Y + 2.03 * U
  • 此时的转换结束后,每个像素点都有完整的 Y、U、V 分量。而之前提到 Y 和 UV 分量是可以分离的,接下来通过不同的采样方式,可以将图像的 Y、U、V 分量重新组合。
  • 不同采样格式都是在一张图像所有像素的 RGB 转换到 YUV 基础上进行的。

三、采样方式

  • YUV 的优点之一是,色度频道的采样率可比 Y 频道低,同时不会明显降低视觉质量。有一种表示法可用来描述 U 和 V 与 Y 的采样频率比例,这个表示法称为 A:B:C 表示法。

① YUV 4:4:4 采样

  • 表示色度频道没有下采样, 每个 Y 分量对应自己的 UV 分量。
  • 其中的 Y、U、V 三个分量的采样比例是相同的,所以每个像素点的分量信息都是完整的,每个分量占用 8bit,一个像素点占用 1 个字节。与 RGB 颜色编码相比,并没有节省带宽,占用的存储空间也没有减少。

  • 假设原始图像的像素为(一对[]表示一个像素点):
    [Y0, U0, V0]; [Y1, U1, V1]; [Y2, U2, V2]; [Y3, U3, V3];
  • 将原始图像像素按照YUV4:4:4采样的码流为(相对原始像素是原样输出):
    Y0, U0, V0, Y1, U1, V1, Y2, U2, V2, Y3, U3, V3
  • 最后映射还原的像素点 = 原始图像的像素,为:
    [Y0, U0, V0]; [Y1, U1, V1]; [Y2, U2, V2]; [Y3, U3, V3];
  • 可以看到这种采样方式的图像和 RGB 颜色模型的图像大小是一样,并没有达到节省带宽的目的,当将 RGB 图像转换为 YUV 图像时,也是先转换为 YUV 4:4:4 采样的图像。

② YUV 4:2:2 采样

  • 表示 2:1 的水平下采样,没有垂直下采样。
  • 对于每两个 U 样例或 V 样例,每个扫描行都包含四个 Y 样例。
  • 两个 Y 分量共用一套 UV 分量,意味着 UV 分量是 Y 分量采样的一半,Y 分量和 UV 分量按照 2 : 1 的比例采样。如果水平方向有 4 个像素点,那么采样了 4 个 Y 分量,而只采样了 2 个 UV 分量。

  • 每采样一个像素点,都会采样 Y 分量,而 U、V 分量则会间隔一个采集一个;
  • 假设原始图像的像素为(一对[]表示一个像素点):
    [Y0, U0, V0]; [Y1, U1, V1]; [Y2, U2, V2]; [Y3, U3, V3];
  • 将原始图像像素按照 YUV4:2:2 采样的码流为:
    Y0, U0, Y1, V1, Y2, U2, Y3, V3
  • 最后映射还原的像素点为:
    [Y0, U0, V1]; [Y1, U0, V1]; [Y2, U2, V3]; [Y3, U2, V3];
  • 采样的码流映射为像素点,还是要满足每个像素点有 Y、U、V 三个分量。但是可以看到,第一和第二像素点公用了 U0、V1 分量,第三和第四个像素点公用了 U2、V3 分量,这样就节省了图像空间。

③ YUV 4:2:0 采样

  • 表示 2:1 的水平下采样,2:1 的垂直下采样。 4个Y分量共用一套UV分量。
  • YUV 4:2:0 采样,并不是指只采样 U 分量而不采样 V 分量。而是指,在每一行扫描时,只扫描一种色度分量(U 或者 V),和 Y 分量按照 2 : 1 的方式采样。
  • 第一行扫描时,YU 按照 2 : 1 的方式采样,那么第二行扫描时,YV 分量按照 2:1 的方式采样。
  • 对于每个色度分量来说,它的水平方向和竖直方向的采样和 Y 分量相比都是 2:1 。
  • 在田字格的 4 个像素点中,4 个 Y 分量共用了一套 UV 分量,如图所示

  • 假设原始图像的像素为(一对[]表示一个像素点):
    [Y0, U0, V0]; [Y1, U1, V1]; [Y2, U2, V2]; [Y3, U3, V3];
    [Y5, U5, V5]; [Y6, U6, V6]; [Y7, U7, V7]; [Y8, U8, V8];
  • 将原始图像像素按照YUV4:2:0采样的码流为:
    Y0, U0, Y1, Y2, U2, Y3,
    Y5, V5, Y6, Y7, V7, Y8,
  • 最后映射还原的像素点为:
    [Y0, U0, V5]; [Y1, U0, V5]; [Y2, U2, V7]; [Y3, U2, V7];
    [Y5, U0, V5]; [Y6, U0, V5]; [Y7, U2, V7]; [Y8, U2, V7];
  • 从映射出的像素点中可以看到,四个 Y 分量是共用了一套 UV 分量,而且是按照 2*2 的小方格的形式分布的,相比 YUV 4:2:2 采样中两个 Y 分量共用一套 UV 分量,这样更能够节省空间。

④ YUV 4:1:1 采样

  • 表示 4:1 的水平下采样,没有垂直下采样。对于每个 U 样例或 V 样例,每个扫描行都包含四个 Y 样例。
  • 与其他格式相比,4:1:1 采样不太常用,本文不对其进行详细讨论

四、YUV 存储格式

① 平面格式与打包格式

YUV 格式可以分为打包格式和平面格式。在打包格式中,Y、U 和 V 组件存储在一个数组中。像素被组织到了一些巨像素组中,巨像素组的布局取决于格式。在平面格式中,Y、U 和 V 组件作为三个单独的平面进行存储。

  • planar 平面格式:
    指先连续存储所有像素点的 Y 分量,然后存储 U 分量,最后是 V 分量。
  • packed 打包模式:
    指每个像素点的 Y、U、V 分量是连续交替存储的。

② 基于 YUV 4:2:2 采样的格式

常见的基于 YUV 4:2:2 采样的格式如下:YUYV 格式、UYVY 格式、YUV 422P 格式。

  • YUYV 格式
    YUYV 格式是采用打包格式进行存储的,指每个像素点都采用 Y 分量,但是每隔一个像素采样它的 UV 分量,排列顺序如下:
    Y0 UO Y1 V0 Y2 U2 Y3 V2
    (Y0 和 Y1 公用 U0 V0 分量,Y2 和 Y3 公用 U2 V2 分量….)
  • UYVY 格式
    UYVY 格式也是采用打包格式进行存储,它的顺序和 YUYV 相反,先采用 U 分量再采样 Y 分量,排列顺序如下:
    U0 Y0 V0 Y1 U2 Y2 V2 Y3
    (Y0 和 Y1 公用 U0 V0 分量,Y2 和 Y3 公用 U2 V2 分量….)
  • YUV 422P 格式
    YUV 422P 格式,又叫做 I422,采用的是平面格式进行存储,先存储所有的 Y 分量,再存储所有的 U 分量,再存储所有的 V 分量。

③ 基于 YUV 4:2:0 采样的格式

  • 基于 YUV 4:2:0 采样的格式主要有 YUV 420P 和 YUV 420SP 两种类型,每个类型又对应其他具体格式。
YUV 420P 类型 YUV 420SP 类型
YV12 格式 NV12 格式
YU12 格式 NV21 格式
  • YUV 420P 和 YUV 420SP 都是基于 Planar 平面模式 进行存储的,先存储所有的 Y 分量后, YUV420P 类型就会先存储所有的 U 分量或者 V 分量,而 YUV420SP 则是按照 UV 或者 VU 的交替顺序进行存储了,具体查看看下图:

  • YUV420P 的格式:

五、RGB 与YUV 转换矩阵的几何含义

  • YUV 与 RGB 的转换公式不止一种,主要原因是具体格式下,标准不同,这里采用苹果 Demo 中给出的转换矩阵,其它转换公式中,具体数值可能不同:
let ycbcrToRGBTransform = float4x4(simd_float4(+1.0000, +1.0000, +1.0000, +0.0000),simd_float4(+0.0000, -0.3441, +1.7720, +0.0000),simd_float4(+1.4020, -0.7141, +0.0000, +0.0000),simd_float4(-0.7010, +0.5291, -0.8860, +1.0000));
  • 将上面向量与矩阵乘法写成行列式形式,可能更符合大家的直觉:
R = Y + 1.402*V - 0.701
G = Y - 0.3441*U - 0.7141*V + 0.5291
B = Y + 1.772*U - 0.886
  • 可以发现,这个 YUV 转 RGB 的公式其实是个线性变换,用几何的方式表达就是说:
    • 将一个 RGB 的颜色用 xyz 坐标表示,那么将这个坐标(旋转、缩放、平移)之后,新的 xyz 坐标就可以表示 YUV 颜色值;
    • 反之也是,将一个 YUV 颜色分量当做 xyz 坐标,那么将这个坐标逆向(旋转、缩放、平移)之后,新的 xyz 坐标就可以表示 RGB 颜色值;
  • 于是,可以在 3D 空间中画一个边长为 1 的正方体,后方左下角(0, 0, 0) 就代表黑色,前方右上角(1, 1, 1) 就代表白色,如下图右下角立方体。同样复制一个,并将其坐标用矩阵转换到 YUV 空间,如下图左上角倾斜的长方体。

  • 对于 RGB 的立方体,比较简单:它的 x 坐标越大,越往右方,颜色越红;y 坐标越大,越往上方,颜色越绿;z 坐标越大,越往前方,颜色越蓝。
  • 而 YUV 的长方体,它的 x 坐标越大,越往右方,亮度越大;y 坐标越大,越往上方,颜色从黄到蓝;z 坐标越大,越往前方,颜色从青绿到红。

let box1 = scene.rootNode.childNode(withName: "box", recursively: true)!
let box2 = scene.rootNode.childNode(withName: "box2", recursively: true)!
simpleProgram(node: box1)
simpleProgram(node: box2)// YUV 到 RGB
let ycbcrToRGBTransform = float4x4(simd_float4(+1.0000, +1.0000, +1.0000, +0.0000),simd_float4(+0.0000, -0.3441, +1.7720, +0.0000),simd_float4(+1.4020, -0.7141, +0.0000, +0.0000),simd_float4(-0.7010, +0.5291, -0.8860, +1.0000)
);
let p = ycbcrToRGBTransform.inverse//RGB 到 YUV
box1.simdTransform = p// box2.simdTransform = box2.simdTransform * ycbcrToRGBTransform.inverse * box2.simdTransform.inverse
// 用 shader 进行可视化显示
func simpleProgram(node:SCNNode) {let program = SCNProgram()program.vertexFunctionName = "vertexShader"program.fragmentFunctionName = "fragmentShader"// 赋值给**SCNGeometry**或者**SCNMaterial**guard let material = node.geometry?.materials.first else { fatalError() }material.program = program
}
// 默认的头文件
#include <metal_stdlib>
using namespace metal;
// 与 SceneKit 配合使用时,需要的头文件
#include <SceneKit/scn_metal>struct VertexInput {float3 position [[attribute(SCNVertexSemanticPosition)]];
};struct ColorInOut {float4 position [[position]];float4 color;
};struct MyNodeData {float4x4 modelViewProjectionTransform;
};// 顶点着色器函数,输出为 ColorInOut 类型,输入为 VertexInput 类型的变量 in,和 MyNodeData 类型的变量指针 scn_node
vertex ColorInOut vertexShader(VertexInput in [[stage_in]], constant MyNodeData& scn_node [[buffer(0)]]) {ColorInOut out;// 将模型空间的顶点补全为 float4 类型,进行 MVP 变换out.position = scn_node.modelViewProjectionTransform * float4(in.position, 1.0);// 加 0.5,将坐标从[-0.5~0.5],转换到[0~1] 以代表颜色out.color = float4(in.position + 0.5, 1);return out;
}// 片元着色器函数,输出为 half4,输入为 ColorInOut 类型的变量 in
fragment half4 fragmentShader(ColorInOut in [[stage_in]]) {return half4(in.color);
}

六、总结

  • YUV4:4:4 中 Y、U、V 分量的采样比例相同,既可以理解为原始图像像素点原样输出,存储空间没有任何变化。
  • YUV4:2:2 采样格式,是指每采样一个像素点,都会采样 Y 分量,而 U、V 分量则会间隔一个采集一个,本质是通过左右相邻像素点共用 U/V 分量。相比 RGB 颜色编码格式,节省了 1/3 的存储空间,同时节约了在传输时的带宽。
  • YUV4:2:0 采样格式,是实际开发中最常用的颜色编码格式,相比 YUV4:2:2 采样格式,更能节省空间。是指在 2*2 的田字格中有 4 个像素点,其中 4 个 Y 分量共用一套 UV 分量,其本质是通过田字格的上下左右像素点共用 U/V 分量。

Metal之探究理解视频渲染RGB与YUV颜色编码相关推荐

  1. Android平台RTMP推送模块如何对接NV21、YV12、RGB、YUV等编码前数据

    前言 我们在对接Android平台摄像头或者屏幕采集.编码打包推送场景的时候,随着采集设备的不同,出来的数据也是多样化的,比如NV21.YV12.RGB.YUV等,更有图像数据甚至是翻转或者倒置的,如 ...

  2. FFmpeg学习之二 (yuv视频渲染)

    FFmpeg学习之二 (yuv视频渲染) yuv简介 1.yuv是什么 2.yuv采集方式 3.yuv存储方式 4.yuv格式 yuv视频渲染 1. iOS YUV视频渲染 1.1 IOS利用open ...

  3. 像素格式RGB与YUV

    1.RGB像素格式 RGB彩色模式是一种颜色标准,是通过对红(R).绿(G).蓝(B)三个颜色通道的变化以及它们相互之间的叠加来得到各式各样的颜色的,这个标准几乎包括了人类视力所能感知的所有颜色,是运 ...

  4. iOS音视频开发十三:视频渲染,用 Metal 渲染

    本系列文章通过拆解采集 → 编码 → 封装 → 解封装 → 解码 → 渲染流程并实现 Demo 来向大家介绍如何在 iOS/Android 平台上手音视频开发. 这里是第十三篇:iOS 视频渲染 De ...

  5. Metal(六) 案例之视频文件的渲染

    本案例实现使用Metal读取视频文件,并渲染到屏幕上.(此时显示是没有声音的) 思路: 使用AVFundation中的AVAssetReaderTrackOutput方法,并将原始数据传入到CMSem ...

  6. 【从零开始】理解视频编解码技术

    [从零开始]理解视频编解码技术 auxten ​ CovenantSQL 联合创始人 ​关注他 1,263 人赞同了该文章 转载自: https://github.com/leandromoreira ...

  7. Android VR Player(全景视频播放器) [10]: VR全景视频渲染播放的实现(exoplayer,glsurfaceview,opengl es)

    前言 此博客的大部分内容来自我的毕业设计论文,因此语言上会偏正式一点,如果您有任何问题或建议,欢迎留言.在此感谢实验室的聂师兄,全景视频render部分的代码设计主要参考了他所编写的代码来完成,他对视 ...

  8. webrtc 渲染_WebRTC 开发(六)摄像头采集与视频渲染分析

    在上一篇文章 WebRTC 开发(五)编译与运行 Mac 工程 中,我们编译了 WebRTC 的工程 AppRTCMobile,也看到了 App 启动后的初始界面.本文基于 WebRTC M76 ,将 ...

  9. 视频产生的本质及色彩空间:RGB 和 YUV

    一.前言 在前面几篇文章中,我们完成了音频相关基础知识的学习,从今天开始,我们要暂别音频,继续学习视频相关基础内容. 虽说声音在我们日常的生活.工作.娱乐过程中,发挥着不可替代的作用,但人们常说,百闻 ...

最新文章

  1. 苏黎世联邦理工学院SML课题组招收统计机器学习全奖博士生
  2. UTF-8 GBK 联系和区别
  3. 记录一次Socket异常:java.net.SocketException: Connection reset
  4. 三、Vue组件化开发学习笔记——组件化的基本步骤、全局组件和局部组件、父组件和子组件、注册组件的语法糖、模板分离写法、组件的数据存放
  5. mysql 8.0认证失败_Node.js无法对MySQL 8.0进行身份验证
  6. win10右键一直转圈_Win10总是自动更新?教你如何关掉自动更新
  7. Java日志性能那些事(转)
  8. linux启动管理,Linux启动管理 详述
  9. 矩阵论及其应用_数值分析篇——向量和矩阵的范数
  10. ubuntu 输入空白How to cd to folder name with spaces blank names (white space)
  11. 四阶龙格库塔法的基本思想_四阶龙格—库塔法的原理及其应用
  12. Kafka 概念、单机搭建与使用
  13. L2TP更改网络运营商后导致连接不了
  14. 搬砖: web音频流转发之音视频直播
  15. NAND flash基本概念整理
  16. 嵌入式软件之应用调试
  17. wps在线预览接口_开发文档 - WPS在线预览 - view.wps.cn
  18. 2021年福建高考成绩排名查询,福建高考排名对应学校-福建高考位次查询(2021年文科参考)...
  19. 大疆笔试——机器学习提前批
  20. WAP1.0 前端开发经验(转)

热门文章

  1. MySql cmd下的学习笔记 —— 有关建立数据库的操作(连接Mysql,建立数据库,删除数据库等等)...
  2. tomcat高并发配置
  3. 第六次作业——团队作业
  4. 文本模式下的分辨率对照表
  5. 一个容易忽略的问题—Javascript文件加载的顺序
  6. concat合并的数组会有顺序么_JS数组 Array
  7. 输入十个数,输出其中最大数、下标,用函数实现
  8. android 绘制分割线,Android EditText在其drawable和它的文本之间绘制一个分隔线
  9. java正则表达式爬虫_Java简单爬虫系列(3)---正则表达式和Java正则API的使用
  10. 布局技巧3:创建高效布局