YUV转RGBA的操作
NV12格式
YYYYYYYY
YYYYYYYY
YYYYYYYY
YYYYYYYY
UVUVUVUV
UVUVUVUV
两个平面,UV交错
基本方案
int clamp(int value, int min, int max) {
if (value < min) {
return min;
} else if (value > max) {
return max;
} else {
return value;
}
}
void NV12_T_RGBA(unsigned char* YUV, unsigned char* rgba, unsigned int width, unsigned int height)
{
int frameSize = width * height;
for (int j = 0; j < height; j++) {
for (int i = 0; i < width; i++) {
// Y值
int Y = YUV[j * width + i];
// UV值, UV 交错排列,只占总高度的一半
int U = YUV[frameSize + (j / 2) * width + (i & ~1)];
int V = YUV[frameSize + (j / 2) * width + (i & ~1) + 1];
// 转换
int R = clamp((int)(Y + 1.13983 * (V - 128)), 0, 255);
int G = clamp((int)(Y - 0.39465 * (U - 128) - 0.5806 * (V - 128)), 0, 255);
int B = clamp((int)(Y + 2.03211 * (U - 128)), 0, 255);
// 存储RGBA数据,将Alpha通道设置为0
rgba[(j * width + i) * 4] = R;
rgba[(j * width + i) * 4 + 1] = G;
rgba[(j * width + i) * 4 + 2] = B;
rgba[(j * width + i) * 4 + 3] = 0;
}
}
}
在NV12格式的YUV数据中,UV分量是按照2x2的子采样存储的,这意味着每个UV值对应原始图像中2x2区块的四个像素。这种存储方式导致UV数据的横向分辨率只有Y数据的一半。为了从YUV数据中正确地提取UV分量,我们需要确保我们访问的UV索引对应于其在Y数据中的位置。
这里使用的表达式 i & ~1 用于确定当前像素对应的UV组件的水平位置。下面是这个操作的详细解释:
位运算详解
i:表示当前像素的横坐标。
~1:这是一个位运算符。1 在二进制中表示为 00000001,取反后变为 11111110。
i & ~1:这个运算实际上是将 i 的最低位(二进制的最右边位)清零,从而使得得到的结果总是偶数。这个偶数指向当前2x2块中第一个像素(即左上角)的位置。
例子说明
假设我们有一个宽度为8的图像,对应的行中像素横坐标 i 从0到7。我们使用 i & ~1 来查看结果:
当 i = 0(二进制 000)时,i & ~1 的结果是 0(二进制 000)。
当 i = 1(二进制 001)时,i & ~1 的结果是 0(二进制 000)。
当 i = 2(二进制 010)时,i & ~1 的结果是 2(二进制 010)。
当 i = 3(二进制 011)时,i & ~1 的结果是 2(二进制 010)。
当 i = 4(二进制 100)时,i & ~1 的结果是 4(二进制 100)。
当 i = 5(二进制 101)时,i & ~1 的结果是 4(二进制 100)。
当 i = 6(二进制 110)时,i & ~1 的结果是 6(二进制 110)。
当 i = 7(二进制 111)时,i & ~1 的结果是 6(二进制 110)。
如你看到,对于每两个相邻的 i 值(0和1, 2和3, 4和5, 6和7),i & ~1 都返回相同的偶数值。这样确保了,不管处理的是2x2块中的哪一个像素,我们总是从对应的UV坐标(同一个UV值)提取色差分量,保持了UV分量正确的对应和采样逻辑
不同的转换公式
参考:YUV与RGB转换公式(BT601、BT709、BT2020)_bt601和bt709-CSDN博客