YUV与RGB格式转换的教训与思考(一)
实际上YUV格式种类繁多,很容易让人弄混,不同的格式,在不同的设备上显示,效果也不相同。
一、标准公式:
可以参考标准网站,下面只列常用的。
1. 模拟电视PAL/NTSC制式的视频格式转换:
2. 标准清晰度电视(SDTV)的转换(基于ITU-R BT.601):
请注意,YUV要有全范围和非全范围值的区别。啥叫全范围咧? 一个像素,理论上值在0-255间,就是full-range,否则就是
nofull-range.
下面这2个矩阵就是nofull-range 转换(因为值Y值在16..235,CbCr在16..240间):
full-range的转换公式:
3. 高清电视HDTV转换(ITU-R BT.709):
二、工程优化
公式采用浮点运算,众所周知,浮点运算肯定比整型运算慢上很多,所以工程上的第一步,就是将浮点运算整数化:
namespace vpsdk
{
#define R_INDEX 2
#define G_INDEX 1
#define B_INDEX 0
#define C_BPP3 3
#define pix_clip(pix) pix<0?0:pix>255?255:pix
static void bgr_to_y_row_c(uint8_t *src,uint8_t *ybuf,int width) {
int offset = 0;
for(int y=0; y<width; y++) {
int r = src[offset+R_INDEX];
int g = src[offset+G_INDEX];
int b = src[offset+B_INDEX];
int pix = (r*5983 +20127*g+2032*b+524288)>>15;
if( pix < 16 ) pix = 16;
else if( pix > 235 ) pix = 235;
ybuf[y] = (uint8_t)pix;
offset += C_BPP3;
}
}
static void bgr_to_full_y_row_c(uint8_t *src,uint8_t *ybuf,int width) {
int offset = 0;
for(int y=0; y<width; y++) {
int r = src[offset+R_INDEX];
int g = src[offset+G_INDEX];
int b = src[offset+B_INDEX];
int pix = (r*9798 +19235*g+3736*b)>>15;
ybuf[y] = (uint8_t)pix_clip(pix);
offset += C_BPP3;
}
}
static void bgr_to_uv_row_c(uint8_t *src,uint8_t *ubuf,uint8_t *vbuf,int stride,int width) {
for(int y=0,ii=0; y<width; y+=2,ii++) {
int r[4],g[4],b[4];
r[0] = (int)src[y*C_BPP3+R_INDEX];
g[0] = (int)src[y*C_BPP3+G_INDEX];
b[0] = (int)src[y*C_BPP3+B_INDEX];
r[1] = (int)src[(y+1)*C_BPP3+R_INDEX];
g[1] = (int)src[(y+1)*C_BPP3+G_INDEX];
b[1] = (int)src[(y+1)*C_BPP3+B_INDEX];
r[2] = (int)src[y*C_BPP3+R_INDEX+stride];
g[2] = (int)src[y*C_BPP3+G_INDEX+stride];
b[2] = (int)src[y*C_BPP3+B_INDEX+stride];
r[3] = (int)src[(y+1)*C_BPP3+R_INDEX+stride];
g[3] = (int)src[(y+1)*C_BPP3+G_INDEX+stride];
b[3] = (int)src[(y+1)*C_BPP3+B_INDEX+stride];
int uu=0,vv=0;
for(int k=0; k<4; k++) {
uu += (((-3298*r[k]-11094*g[k]+14392*b[k])+4194304)>>15);
vv += (((14392*r[k]-13073*g[k]-1320*b[k])+4194304)>>15);
}
uu /= 4;
vv /= 4;
ubuf[ii] = (uint8_t)pix_clip(uu);
vbuf[ii] = (uint8_t)pix_clip(vv);
}
}
// rgb -> yuv :
// y = 0 + 0.299*r + 0.587*g + 0.114*b;
// u = 128 - 0.169*r - 0.331*g + 0.500*b ;
// v = 128 + 0.500*r - 0.419*g - 0.081*b ;
// int : *32768
// y = 0 + 9798*r + 19235*g + 3736*b;
// u = 4194304 - 5538*r - 10846*g + 16384*b ;
// v = 4194304 + 16384*r- 13730*g - 2654*b ;
//------------------------------------------------
static void bgr_to_full_uv_row_c(uint8_t *src,uint8_t *ubuf,uint8_t *vbuf,int stride,int width) {
for(int y=0,ii=0; y<width; y+=2,ii++) {
int r[4],g[4],b[4];
r[0] = (int)src[y*C_BPP3+R_INDEX];
g[0] = (int)src[y*C_BPP3+G_INDEX];
b[0] = (int)src[y*C_BPP3+B_INDEX];
r[1] = (int)src[(y+1)*C_BPP3+R_INDEX];
g[1] = (int)src[(y+1)*C_BPP3+G_INDEX];
b[1] = (int)src[(y+1)*C_BPP3+B_INDEX];
r[2] = (int)src[y*C_BPP3+R_INDEX+stride];
g[2] = (int)src[y*C_BPP3+G_INDEX+stride];
b[2] = (int)src[y*C_BPP3+B_INDEX+stride];
r[3] = (int)src[(y+1)*C_BPP3+R_INDEX+stride];
g[3] = (int)src[(y+1)*C_BPP3+G_INDEX+stride];
b[3] = (int)src[(y+1)*C_BPP3+B_INDEX+stride];
int uu=0,vv=0;
for(int k=0; k<4; k++) {
uu += (((-5538*r[k]-10846*g[k]+16384*b[k])+4194304)>>15);
vv += (((16384*r[k]-13730*g[k]-2654*b[k])+4194304)>>15);
}
uu /= 4;
vv /= 4;
if( uu < 16 ) uu = 16;
else if( uu > 240.0 ) uu = 240;
if( vv < 16 ) vv = 16;
else if( vv > 240.0 ) vv = 240;
ubuf[ii] = (uint8_t)uu;
vbuf[ii] = (uint8_t)vv;
}
}
/////////////////////////////////////////////////////////////////////////////////////////////
//For digital component video the color format YCbCr is used. For standard definition TV applications
// (SDTV) the following equation describes the color conversion from RGB to YCbCr
// (according to ITU-R BT.601):
//y = y-16 (16..235)
//u = u - 128 (16..240)
//v = v - 128 (16..240)
//r = 1.164 * y + 0.00 + 1.596 * v
//g = 1.164 * y - 0.392*u - 0.813 * v
//b = 1.164 * y + 2.017*u + 0.00 * v;
/////////////////////////////////////////////////////////////////////////////////////////////
// rgb -> yuv :
// y = 16 + 0.257*r + 0.504 *g + 0.098 * b; rgb(0..255)
// u = 128 - 0.148*r - 0.291 *g + 0.439 * b; y(16..235)
// v = 128 + 0.439*r - 0.368 *g - 0.071 * b; uv(16..240)
//
/////////////////////////////////////////////////////////////////////////////////////////////
static void yuv_to_bgr_row_c(uint8_t *ybuf,uint8_t *ubuf,uint8_t *vbuf,uint8_t *bgr,int width)
{
for(int x=0; x<width; x++) {
int y = (ybuf[x] - 16);
int u = ubuf[x/2] - 128;
int v = vbuf[x/2] - 128;
y = y * 38142;
bgr[x*3+0] = pix_clip((y + 69206*u)>>15);
bgr[x*3+1] = pix_clip((y - 6980*u - 17465*v)>>15);
bgr[x*3+2] = pix_clip((y + 58753*v)>>15);
}
}
/////////////////////////////////////////////////////////////////////////////////////////////
//o convert a full-range YCbCr color into RGB is described by the following equation
//y = y - 0 (0..255)
//u = u - 128 (0..255)
//v = v - 128 (0..255)
//r = 1.0 * y + 0.00 + 1.4 * v
//g = 1.0 * y - 0.343*u - 0.711 * v
//b = 1.0 * y + 1.765*u + 0.00 * v;
// int : 32768
//
//r = 32768 * y + 0.00 + 45875 * v
//g = 32768 * y - 11239*u - 23298 * v
//b = 32768 * y + 57836*u + 0.00 * v;
//
/////////////////////////////////////////////////////////////////////////////////////////////
// rgb -> yuv :
// y = 0 + 0.299*r + 0.587*g + 0.114*b;
// u = 128 - 0.169*r - 0.331*g + 0.500*b ;
// v = 128 + 0.500*r - 0.419*g - 0.081*b ;
// int : *32768
// y = 0 + 9798*r + 19235*g + 3736*b;
// u = 4194304 - 5538*r - 10846*g + 16384*b ;
// v = 4194304 + 16384*r- 13730*g - 2654*b ;
//------------------------------------------------
//
/////////////////////////////////////////////////////////////////////////////////////////////
static void full_yuv_to_bgr_row_c(uint8_t *ybuf,uint8_t *ubuf,uint8_t *vbuf,uint8_t *bgr,int width)
{
for(int x=0; x<width; x++) {
int y = ybuf[x];
int u = ubuf[x/2] - 128;
int v = vbuf[x/2] - 128;
y = y * 32768;
bgr[x*3+2] = pix_clip((y + 45875*v)>>15);
bgr[x*3+1] = pix_clip((y - 11239*u - 23298*v)>>15);
bgr[x*3+0] = pix_clip((y + 57836*u)>>15);
}
}
static void nv12_to_bgr_row_c(uint8_t *ybuf,uint8_t *uvbuf,uint8_t *bgr,int width)
{
for(int x=0; x<width; x++) {
int y = (ybuf[x] - 16);
int u = uvbuf[x/2] - 128;
int v = uvbuf[x/2+1] - 128;
y = y * 38142;
bgr[x*3] = pix_clip((y + 69206*u)>>15);
bgr[x*3+1] = pix_clip((y - 6980*u - 17465*v)>>15);
bgr[x*3+2] = pix_clip((y + 58753*v)>>15);
}
}
static void full_nv12_to_bgr_row_c(uint8_t *ybuf,uint8_t *uvbuf,uint8_t *bgr,int width)
{
for(int x=0; x<width; x++) {
int y = ybuf[x];
int u = uvbuf[x/2] - 128;
int v = uvbuf[x/2+1] - 128;
y = y * 32768;
bgr[x*3+2] = pix_clip((y + 45875*v)>>15);
bgr[x*3+1] = pix_clip((y - 11239*u - 23298*v)>>15);
bgr[x*3+0] = pix_clip((y + 57836*u)>>15);
}
}
typedef void (*fn_nv12_to_bgr_row)(uint8_t *ybuf,uint8_t *uvbuf,uint8_t *bgra,int width);
typedef void (*fn_yuv_to_bgr_row)(uint8_t *ybuf,uint8_t *ubuf,uint8_t *vbuf,uint8_t *bgr,int width);
typedef void (*fn_bgr_to_y_row)(uint8_t *src,uint8_t *ybuf,int width);
typedef void (*fn_bgr_to_uv_row)(uint8_t *src,uint8_t *ubuf,uint8_t *vbuf,int stride,int width);
static void bgr_to_yv12_c(uint8_t *src, int stride,
uint8_t *ybuf, int ystride,
uint8_t *ubuf, int ustride,
uint8_t *vbuf, int vstride,
int width, int height,int full)
{
int iheight = abs(height);
int istride = height<0?-stride:stride;
if( height < 0 ) src = src + (iheight-1)*stride;
fn_bgr_to_y_row func_y = full?bgr_to_full_y_row_c:bgr_to_y_row_c;
fn_bgr_to_uv_row func_uv = full?bgr_to_full_uv_row_c:bgr_to_uv_row_c;
for(int y=0; y<iheight-1; y+=2) {
func_y(src,ybuf,width);
func_y(src+istride,ybuf+ystride,width);
func_uv(src,ubuf,vbuf,istride,width);
src += (istride*2);
ybuf += (ystride*2);
ubuf += ustride;
vbuf += vstride;
}
if( iheight & 1 ) {
func_uv(src,ubuf,vbuf,0,width/2);
func_y(src,ybuf,width);
}
}
static void yv12_to_bgr_c(uint8_t *ybuf,int ystride,
uint8_t *ubuf,int ustride,
uint8_t *vbuf,int vstride,
uint8_t *bgr, int stride,
int width, int height,int full)
{
int iheight = abs(height);
int istride = stride;
if( height < 0 ) {
istride = -stride;
bgr = bgr + (iheight-1)*stride;
}
fn_yuv_to_bgr_row func = full?full_yuv_to_bgr_row_c:yuv_to_bgr_row_c;
for(int y=0; y<iheight-1; y+=2) {
func(ybuf,ubuf,vbuf,bgr,width);
func(ybuf+ystride,ubuf,vbuf,bgr+istride,width);
bgr += (2*istride);
ybuf += 2*ystride;
ubuf += ustride;
vbuf += vstride;
}
if( iheight & 1 ) {
func(ybuf,ubuf,vbuf,bgr,width);
}
}
static void nv12_to_bgr_c(uint8_t *ybuf,int ystride,
uint8_t *uvbuf,int uvstride,
uint8_t *bgr, int stride,
int width, int height,int full)
{
int iheight = abs(height);
int istride = stride;
if( height < 0 ) {
istride = -stride;
bgr = bgr + (iheight-1)*stride;
}
fn_nv12_to_bgr_row func = full?full_nv12_to_bgr_row_c:nv12_to_bgr_row_c;
for(int y=0; y<iheight-1; y+=2) {
func(ybuf,uvbuf,bgr,width);
func(ybuf+ystride,uvbuf,bgr+istride,width);
bgr += (2*istride);
ybuf += 2*ystride;
uvbuf += uvstride;
}
if( iheight & 1 ) {
func(ybuf,uvbuf,bgr,width);
}
}
bool vp_convert::i420_to_bgr(int width,int height,uint8_t *src,video_frame* dst,int full) {
uint8_t *ybuf = src;
uint8_t *ubuf = src+width*height;
uint8_t *vbuf = src+width*height*5/4;
yv12_to_bgr_c(ybuf,width,ubuf,width>>1,vbuf,width>>1,dst->m_data[0],dst->m_line[0],width,height,full);
return true;
}
bool vp_convert::yv12_to_bgr(int width,int height,uint8_t *src,video_frame* dst,int full) {
uint8_t *ybuf = src;
uint8_t *vbuf = src+width*height;
uint8_t *ubuf = src+width*height*5/4;
yv12_to_bgr_c(ybuf,width,ubuf,width>>1,vbuf,width>>1,dst->m_data[0],dst->m_line[0],width,height,full);
return true;
}
bool vp_convert::nv12_to_bgr(int width,int height,uint8_t *src,video_frame* dst,int full) {
uint8_t *ybuf = src;
uint8_t *uvbuf = src+width*height;
nv12_to_bgr_c(ybuf,width,uvbuf,width,dst->m_data[0],dst->m_line[0],width,height,full);
return true;
}
bool vp_convert::bgr_to_i420(video_frame* src,video_frame *dst,int full) {
bgr_to_yv12_c(src->m_data[0],src->m_line[0],
dst->m_data[0], dst->m_line[0],
dst->m_data[1], dst->m_line[1],
dst->m_data[2], dst->m_line[2],
src->m_width,dst->m_height,full);
return true;
}
}