转自:http://www.latelee.org/my-study/yuv-learning-yuv420p-to-rgb24.html
对于YUV420的格式,网上有一大堆资料,这里就不说了。直奔主题,给出如何转换的函数,一如既往,只用代码说事。
YUV420有打包格式(Packed),一如前文所述。同时还有平面格式(Planar),即Y、U、V是分开存储的,每个分量占一块地方,其中Y为 width*height,而U、V合占Y的一半,该种格式每个像素占12比特。根据U、V的顺序,分出2种格式,U前V后即YUV420P,也叫 I420,V前U后,叫YV12(YV表示Y后面跟着V,12表示12bit)。另外,还有一种半平面格式(Semi-planar),即Y单独占一块地 方,但其后U、V又紧挨着排在一起,根据U、V的顺序,又有2种,U前V后叫NV12,在国内好像很多人叫它为YUV420SP格式;V前U后叫 NV21。这种格式似乎比NV16稍受欢迎。
首先给出转换查询表的生成函数,代码是在网上找来的,如下:
static long int crv_tab[256]; static long int cbu_tab[256]; static long int cgu_tab[256]; static long int cgv_tab[256]; static long int tab_76309[256]; static unsigned char clp[1024]; //for clip in CCIR601 void init_yuv420p_table() { long int crv,cbu,cgu,cgv; int i,ind; static int init = 0; if (init == 1) return; crv = 104597; cbu = 132201; /* fra matrise i global.h */ cgu = 25675; cgv = 53279; for (i = 0; i < 256; i++) { crv_tab[i] = (i-128) * crv; cbu_tab[i] = (i-128) * cbu; cgu_tab[i] = (i-128) * cgu; cgv_tab[i] = (i-128) * cgv; tab_76309[i] = 76309*(i-16); } for (i = 0; i < 384; i++) clp[i] = 0; ind = 384; for (i = 0;i < 256; i++) clp[ind++] = i; ind = 640; for (i = 0;i < 384; i++) clp[ind++] = 255; init = 1; }
下面给出YUV420平面格式的转换函数,如下:
/** 内存分布 w +--------------------+ |Y0Y1Y2Y3... | |... | h |... | | | +--------------------+ |U0U1 | |... | h/2 |... | | | +----------+ |V0V1 | |... | h/2 |... | | | +----------+ w/2 */ void yuv420p_to_rgb24(YUV_TYPE type, unsigned char* yuvbuffer,unsigned char* rgbbuffer, int width,int height) { int y1, y2, u, v; unsigned char *py1, *py2; int i, j, c1, c2, c3, c4; unsigned char *d1, *d2; unsigned char *src_u, *src_v; static int init_yuv420p = 0; src_u = yuvbuffer + width * height; // u src_v = src_u + width * height / 4; // v if (type == FMT_YV12) { src_v = yuvbuffer + width * height; // v src_u = src_u + width * height / 4; // u } py1 = yuvbuffer; // y py2 = py1 + width; d1 = rgbbuffer; d2 = d1 + 3 * width; init_yuv420p_table(); for (j = 0; j < height; j += 2) { for (i = 0; i < width; i += 2) { u = *src_u++; v = *src_v++; c1 = crv_tab[v]; c2 = cgu_tab[u]; c3 = cgv_tab[v]; c4 = cbu_tab[u]; //up-left y1 = tab_76309[*py1++]; *d1++ = clp[384+((y1 + c1)>>16)]; *d1++ = clp[384+((y1 - c2 - c3)>>16)]; *d1++ = clp[384+((y1 + c4)>>16)]; //down-left y2 = tab_76309[*py2++]; *d2++ = clp[384+((y2 + c1)>>16)]; *d2++ = clp[384+((y2 - c2 - c3)>>16)]; *d2++ = clp[384+((y2 + c4)>>16)]; //up-right y1 = tab_76309[*py1++]; *d1++ = clp[384+((y1 + c1)>>16)]; *d1++ = clp[384+((y1 - c2 - c3)>>16)]; *d1++ = clp[384+((y1 + c4)>>16)]; //down-right y2 = tab_76309[*py2++]; *d2++ = clp[384+((y2 + c1)>>16)]; *d2++ = clp[384+((y2 - c2 - c3)>>16)]; *d2++ = clp[384+((y2 + c4)>>16)]; } d1 += 3*width; d2 += 3*width; py1 += width; py2 += width; } }
再给出YUV420SP转换的函数,如下:
void yuv420sp_to_rgb24(YUV_TYPE type, unsigned char* yuvbuffer,unsigned char* rgbbuffer, int width,int height) { int y1, y2, u, v; unsigned char *py1, *py2; int i, j, c1, c2, c3, c4; unsigned char *d1, *d2; unsigned char *src_u; static int init_yuv420p = 0; src_u = yuvbuffer + width * height; // u py1 = yuvbuffer; // y py2 = py1 + width; d1 = rgbbuffer; d2 = d1 + 3 * width; init_yuv420p_table(); for (j = 0; j < height; j += 2) { for (i = 0; i < width; i += 2) { if (type == FMT_NV12) { u = *src_u++; v = *src_u++; // v紧跟u,在u的下一个位置 } if (type == FMT_NV21) { v = *src_u++; u = *src_u++; // u紧跟v,在v的下一个位置 } c1 = crv_tab[v]; c2 = cgu_tab[u]; c3 = cgv_tab[v]; c4 = cbu_tab[u]; //up-left y1 = tab_76309[*py1++]; *d1++ = clp[384+((y1 + c1)>>16)]; *d1++ = clp[384+((y1 - c2 - c3)>>16)]; *d1++ = clp[384+((y1 + c4)>>16)]; //down-left y2 = tab_76309[*py2++]; *d2++ = clp[384+((y2 + c1)>>16)]; *d2++ = clp[384+((y2 - c2 - c3)>>16)]; *d2++ = clp[384+((y2 + c4)>>16)]; //up-right y1 = tab_76309[*py1++]; *d1++ = clp[384+((y1 + c1)>>16)]; *d1++ = clp[384+((y1 - c2 - c3)>>16)]; *d1++ = clp[384+((y1 + c4)>>16)]; //down-right y2 = tab_76309[*py2++]; *d2++ = clp[384+((y2 + c1)>>16)]; *d2++ = clp[384+((y2 - c2 - c3)>>16)]; *d2++ = clp[384+((y2 + c4)>>16)]; } d1 += 3*width; d2 += 3*width; py1 += width; py2 += width; } }
参考资料:
http://www.fourcc.org/yuv.php
https://wiki.videolan.org/YUV/
李迟 2015.8.5 晚上
本文固定链接: http://www.latelee.org/my-study/yuv-learning-yuv420p-to-rgb24.html