YUV格式学习:YUV444转换RGB24

YUV格式有很多种,按其采样方式,有444、422、420,还有411(但不常见)。针对数据的排序,又有平面格式和打包格式,还有“踢啊”特有的半平面格式——这些排列组合,就显得YUV格式多种多样,初看起来杂乱无章,但只要用心,就能理出头绪。像我,也是花了很多时间去慢慢搜索才真正理解。有时间会系统整理几篇文章出来,也会实践一下。

YUV444格式没有进行压缩,占用空间为with*height*3,与RGB占用空间相同。因此在转换上也很方便,但很多编码器似乎不太支持该格式。或许和其占用空间有莫大的关联吧。
首先给出生成查询表格的函数,代码是在网上找来的,如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#ifndef MAX
#define MAX(a, b) ((a) > (b) ? (a) : (b))
#endif
#ifndef MIN
#define MIN(a, b) ((a) < (b) ? (a) : (b))
#endif

static long U[256], V[256], Y1[256], Y2[256];

void init_yuv422p_table(void)
{
int i;
static int init = 0;
if (init == 1) return;
// Initialize table
for (i = 0; i < 256; i++)
{
V[i] = 15938 * i - 2221300;
U[i] = 20238 * i - 2771300;
Y1[i] = 11644 * i;
Y2[i] = 19837 * i - 311710;
}

init = 1;
}

在转换成RGB之前,要调用上述函数。下面是YUV444转换成RGB24的函数实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
void yuv4444_to_rgb24(unsigned char *yuv, unsigned char *rgb, int width, int height)
{
int y, cb, cr;
int r, g, b;
int i = 0;
unsigned char* p_y;
unsigned char* p_u;
unsigned char* p_v;
unsigned char* p_rgb;

p_y = yuv;
p_u = yuv+width*height;
p_v = yuv+2*width*height;
p_rgb = rgb;

init_yuv422p_table();

for (i = 0; i < width * height; i++)
{
y = p_y[0];
cb = p_u[0];
cr = p_v[0];
//yuv2rgb(y, cb, cr, &r, &g, &b);
//yuv2rgb_1(y, cb, cr, &r, &g, &b);
r = MAX (0, MIN (255, (V[cr] + Y1[y])/10000)); //R value
b = MAX (0, MIN (255, (U[cb] + Y1[y])/10000)); //B value
g = MAX (0, MIN (255, (Y2[y] - 5094*(r) - 1942*(b))/10000)); //G value

p_rgb[0] = r;
p_rgb[1] = g;
p_rgb[2] = b;;
p_rgb += 3;
p_y++;
p_u++;
p_v++;
}
}

从代码看到,实际上就是扫描YUV444的内存,得到Y、U、V的分量,然后查表得到对应用R、G、B值,再赋值给RGB内存指针。如前所述,YUV444与RGB占用空间相同,故在代码并无太多的技巧。

李迟 2015.8.5 晚上