简说yuv
最近弄了一个读取y4m文件转成yuv的流的事情,记录一些yuv相关的细节
为什么会有yuv
因为我们目前的显示器显示的原理都是三原色,几乎所有的视频数据最后都要转为rgb格式才能渲染到显示屏上,而原始的rgb格式存储太耗费空间
rgb存储空间是每个像素点需要 rbg三个属性,每个属性八个bit来存储,也就是24bit
而yuv则是从另外一个角度描述图片,y是明亮度(灰介值),uv是则是色度,而人眼对色度没有那么敏感,可以通过减少uv的采集从而减少存储
为什么叫yuv?
查了很久,基本可以确认yuv三个字母不是某些缩写,yuv的另一个叫法是Y Cb(cut blue) Cr(Cut red),这一个可以理解的叫法,对于一个图像点(rgb),可以使用y(亮度)和cb(和蓝色的色差)以及cr(和红色的色差)来近似表达;yuv中的uv貌似跟纹理贴图中的uv坐标系有些关联;
减少采集的方式是设定采样比
- 采样比通常表示为
J:a:b
,以表示一个宽为 J 像素、高为 2 像素的采样区域内 Y Cb Cr(Y U V)的采样比:- J 表示采样区域的宽度,通常为 4;
- a 表示第一行色度采样数;
- b 表示第二行色度采样与第一行色度采样的不同样点数;(零代表Ja0合J0a依次交替,并不是uv某个维度不采样的意思)
上图来源:https://imgs.piasy.com/2018-04-27-sampling_systems_and_ratios.png
压缩比计算示例
简单算下压缩率,如上面的图,4*2 =8个像素,用rbg需要8*3*8=192bit
用yuv444需要8*8(Y)+ 4*2*8(U)+4*2*8(V)=192bit,是一样的
用yuv422需要8*8(Y)+ 2*2*8(U)+2*2*8(V)=128bit,节省了1/3,
用yuv420需要8*8(Y)+ 2*2*8(UV)=96bit,节省了一半(零代表Ja0合J0a依次交替,并不是uv某个维度不采样的意思)
YUV 存储格式
YUV 数据有两种存储格式:平面格式(planar format)和打包格式(packed format)。
- planar: 有时也称 triplanar,有三个 plane,每种分量连续存储,先存储所有的 Y 分量,再存储所有的 Cb 分量,最后存储所有的 Cr 分量(也可以 Cr 在前,Cb 在后);
- packed: 只有一个 plane,n 个样点的 Y Cb Cr 分量一起存储,接着存储下 n 个样点的分量;n 的取值、其中三种分量的存储方式,也有多种组合;
- semi planar: 有两个 plane,先存储所有的 Y 分量,后面 Cb 和 Cr 分量一起存储;
i420
I420 的采样比是 4:2:0
,它是 planar 存储方式,分量存储顺序依次是 Y, Cb, Cr
nv12(ios)
NV12 的采样比是 4:2:0
,它是 semi planar 存储方式,先存储 Y 分量,后面 Cb 和 Cr 分量一起存储,Cb 在前,Cr 在后
nv21(Android)
NV21 和 NV12 类似,采样比是 4:2:0
,也是 semi planar 存储方式,先存储 Y 分量,后面 Cb 和 Cr 分量一起存储,只不过 Cr 在前,Cb 在后
i420 转nv21
从上面的介绍可以看到,i420是yuv分开存储,y是一致的,我们只要分别拿到u和v,改变一些顺序即可从i420转到nv21
private void I420ToNv21(byte[] i420bytes, int width, int height) { byte[] nv21bytes = new byte[i420bytes.length]; int y_len = width * height; int uv_len = y_len / 4; System.arraycopy(i420bytes, 0, nv21bytes, 0, y_len); for (int i =0; i < uv_len; i++) { byte u = i420bytes[y_len + i]; byte v = i420bytes[y_len + uv_len + i]; nv21bytes[y_len + i*2] = v; nv21bytes[y_len + i*2 +1] = u; } System.arraycopy(nv21bytes, 0, i420bytes, 0, nv21bytes.length); nv21bytes = null; }
stride 或 pitch
YUV 数据在内存中存储时,每行像素的数据后面可能还有填充字节,这主要是因为有些系统/环境/操作对内存的字节对齐有要求,比如 64 字节对齐,那么宽度为 720 像素的图像,一行就不满足 64 对齐的要求,那就要填充到 768 像素。存储一行像素所需的字节数,就叫 stride,也叫 pitch。比如这里举的例子,宽为 720,stride 或 pitch 就是 64。
rgb to yuv
yuv to rgb
y4m
y4m是yuv文件的一种存储格式,上面的yuv都是整个图片或者视频的yuv,y4m文件中有明确的帧的划分,处理起来会更方便一些
相信至此,对于yuv的来历、作用以及存储使用,
p
【推荐】还在用 ECharts 开发大屏?试试这款永久免费的开源 BI 工具!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 从二进制到误差:逐行拆解C语言浮点运算中的4008175468544之谜
· .NET制作智能桌面机器人:结合BotSharp智能体框架开发语音交互
· 软件产品开发中常见的10个问题及处理方法
· .NET 原生驾驭 AI 新基建实战系列:向量数据库的应用与畅想
· 从问题排查到源码分析:ActiveMQ消费端频繁日志刷屏的秘密
· C# 13 中的新增功能实操
· Ollama本地部署大模型总结
· 【杭电多校比赛记录】2025“钉耙编程”中国大学生算法设计春季联赛(4)
· langchain0.3教程:从0到1打造一个智能聊天机器人
· 用一种新的分类方法梳理设计模式的脉络