YUV简介
YUV,是一种颜色编码方法。常使用在各个视频处理组件中。 YUV在对照片或视频编码时,考虑到人类的感知能力,允许降低色度的带宽。
YUV是编译true-color
颜色空间(color space)的种类,Y'UV, YUV, YCbCr,YPbPr等专有名词都可以称为YUV,彼此有重叠。“Y”表示明亮度(Luminance、Luma),“U”和“V”则是色度、浓度(Chrominance、Chroma),
格式
YUV Formats分成两个格式:
- 紧缩格式(packed formats):将Y、U、V值存储成Macro Pixels数组,和RGB的存放方式类似。
- 平面格式(planar formats):将Y、U、V的三个分量分别存放在不同的矩阵中。
紧缩格式(packed format)中的YUV是混合在一起的,对于YUV4:4:4格式而言,用紧缩格式很合适的,因此就有了UYVY、YUYV等。平面格式(planar formats)是指每Y分量,U分量和V分量都是以独立的平面组织的,也就是说所有的U分量必须在Y分量后面,而V分量在所有的U分量后面,此一格式适用于采样(subsample)。平面格式(planar format)有I420(4:2:0)、YV12、IYUV等。
简介总结
- 为了兼容黑白电视而诞生。Y表示灰度。
- 对比RBG,需要的带宽小。RGB每一帧需要
3*w*h
的大小,YUV420
这种形式的话,需要w*h*3/2
的大小。
YUV420格式了解
在Android上通过Camera可以取到 NV21 与 YV12.YUV数据
YUV420P
image.pngYV12:
yv12.pngNV21:
这种格式(NV21)是Android相机预览的标准图片格式。YUV 4:2:0平面图像,8位Y采样,接着是具有8位2x2二次采样色度采样的交织V / U平面。
YUV像素处理
准备
-
下载好测试图
Lenna.png - 通过FFmpeg命令,转成NV21
ffmpeg -i Lenna.png -pix_fmt nv21 LennaNv21.yuv
- 下载YUV Player Deluxe作为图片的预览
http://www.yuvplayer.com/
开始
NV21 To YUV420P
- 代码
//nv21 YYYYYYYY VU VU => yyyy yyyy uuuu vvvv
public static void nv21ToYuv420(int size, int frameLength, byte[] frame) {
//将Y复制过去
byte[] frameY = new byte[frameLength];
// Arrays.fill(frameY, (byte) 128);
System.arraycopy(frame, 0, frameY, 0, size);
int uvSize = (frameLength - size) / 2;
byte[] uFrame = new byte[uvSize];
byte[] vFrame = new byte[uvSize];
for (int i = 0; i < uvSize * 2; i++) {
int result = i % 2;
int resultIndex = i / 2;
if (result == 0) {
vFrame[resultIndex] = frame[size + i];
// System.out.println("uFrame resultIndex="+resultIndex+", i="+(size+i));
} else {
uFrame[resultIndex] = frame[size + i];
// System.out.println("vFrame resultIndex="+resultIndex+", i="+(size+i));
}
}
System.arraycopy(uFrame, 0, frameY, size, uvSize);
System.arraycopy(vFrame, 0, frameY, size + uvSize, uvSize);
}
-
结果对比(yuv420格式 512x512预览)
结果1.png
yuv 420p顺时针 90
- 代码
public static void yuv420Rotation90(int srcH, int srcW, int frameLength, byte[] frame) {
//将Y复制过去
byte[] frameY = new byte[frameLength];
int size = srcH * srcW;
int uvSize = (frameLength - size) / 2;
//翻转Y
//记录旋转的h w
int rH = 0;
int rW = 0;
for (int oH = 0; oH < srcH; oH++) {
for (int oW = 0; oW < srcW; oW++) {
//第一行->最后一列
//旋转之后,现在的行数等于原来的横向的index
rH = oW;
//现在的w= 原来行数的总和-H
rW = srcH - oH - 1;
frameY[srcW * rH + rW] = frame[srcW * oH + oW];
}
}
//翻转U
//记录旋转的h srcW
srcH = srcH / 2;
srcW = srcW / 2;
for (int oH = 0; oH < srcH; oH++) {
for (int oW = 0; oW < srcW; oW++) {
//第一行->最后一列
//旋转之后,现在的行数等于原来的横向的index
rH = oW;
//现在的w= 原来行数的总和-H
rW = srcH - oH - 1;
frameY[size + srcW * rH + rW] = frame[size + srcW * oH + oW];
}
}
//翻转V
//记录旋转的h srcW
for (int oH = 0; oH < srcH; oH++) {
for (int oW = 0; oW < srcW; oW++) {
//第一行->最后一列
//旋转之后,现在的行数等于原来的横向的index
rH = oW;
//现在的w= 原来行数的总和-H
rW = srcH - oH - 1;
int rP = size + uvSize + srcW * rH + rW;
int oP = size + uvSize + srcW * oH + oW;
frameY[rP] = frame[oP];
}
}
}
-
结果对比(yuv420格式 512x512预览)
image.png
翻转
public static void yuv420Flip(int srcH, int srcW, int frameLength, byte[] frame) {
//将Y复制过去
int size = srcW * srcH;
byte[] frameY = new byte[frameLength];
int uvSize = (frameLength - size) / 2;
//翻转Y
//就是讲下面的复制到上面来
for (int i = 0; i < srcH; i++) {
System.arraycopy(frame, (srcH - i - 1) * srcW, frameY, i * srcW, srcW);
}
srcH = srcH / 2;
srcH = srcH / 2;
for (int i = 0; i < srcH; i++) {
System.arraycopy(frame, size + (srcH - i - 1) * srcW, frameY, size + i * srcW, srcW);
}
for (int i = 0; i < srcH; i++) {
System.arraycopy(frame, size + uvSize + (srcH - i - 1) * srcW, frameY, size + uvSize + i * srcW, srcW);
}
}
-
结果对比(yuv420格式 512x512预览)
image.png
裁剪
public static void yuv420Clip(int srcH, int srcW, int clipH, int clipW, int startW, int startH, byte[] frame) {
int srcSize = srcH * srcW;
int clipSize = clipH * clipW;
int clipFrameLength = clipH * clipW * 3 / 2;
byte[] frameY2 = new byte[clipFrameLength];
int totalClipH = clipH;
int totalClipW = clipW;
int totalW = srcW;
int totalH = srcH;
//复制Y
for (int i = 0; i < totalClipH; i++) {
//在原来数组中的H
int oH = startH + i;
int srcPos = startW + totalW * oH;
System.arraycopy(frame, srcPos, frameY2, totalClipW * i, totalClipW);
}
totalClipH = clipH / 2;
totalClipW = clipW / 2;
totalW = srcW / 2;
totalH = srcH / 2;
// //复制UV
for (int i = 0; i < totalClipH; i++) {
//在原来数组中的H
int oH = startH / 2 + i;
int srcPos = srcSize + startW / 2 + totalW * oH;
int desPos = clipSize + totalClipW * i;
System.arraycopy(frame, srcPos, frameY2, desPos, totalClipW);
}
for (int i = 0; i < totalClipH; i++) {
//在原来数组中的H
int oH = startH / 2 + i;
int srcPos = srcSize + srcSize / 4 + startW / 2 + totalW * oH;
int desPos = clipSize + clipSize / 4 + totalClipW * i;
System.arraycopy(frame, srcPos, frameY2, desPos, totalClipW);
}
}
-
结果对比(yuv420格式 左300x300 右512x512预览)
image.png
网友评论