# YUV 格式与 RGB 格式的相互转换公式

RGB to YUV

YCr=VCb=U===0.257R+0.504G+0.098B+160.439R0.368G0.071B+1280.148R0.291G+0.439B+128

YUV to RGB

BGR===1.164(Y16)+2.018(U128)1.164(Y16)0.813(V128)0.391(U128)1.164(Y16)+1.596(V128)

CCIR 601 定义的转换公式是：

YCr=VCb=U===0.299R+0.587G+0.114B0.713(RY)=0.500R0.419G0.081B0.564(BY)=0.169R0.331G+0.500B

RGB===Y+1.403VY0.344U0.714VY+1.770U

RGB===Y+1.403×(V128)Y0.343×(U128)0.714×(V128)Y+1.770×(U128)

RGB 到 YUV 的转换公式变化很小，只是VU 的值做了个平移。

YCr=VCb=U===0.299R+0.587G+0.114B0.500R0.419G0.081B+1280.169R0.331G+0.500B+128

u = U - 128;
v = V - 128;
R = qRound(0, Y + v + (v * 103) >> 8, 255);
G = qRound(0, Y – (u * 88) >> 8 – (v * 183) >> 8, 255);
B = qRound(0, Y + u + (u * 198) >> 8, 255);

/**
* @brief yuv2rgb
* @param y [0, 255]
* @param u [0, 255]
* @param v [0, 255]
* @return #ABGR
*/
inline QRgb yuv2rgb(uint8_t y, uint8_t u, uint8_t v)
{
int R = qRound(y + 1.403 * (v - 128)); // 四舍五入到最近的整数
int G = qRound( - 0.343 * (u - 128) - 0.714 * (v - 128));
int B = qRound(y + 1.770 * (u - 128));

R = qBound(0, R, 255);
G = qBound(0, y + G, 255);
B = qBound(0, B, 255);

return qRgb(R, G, B);
}

/**
* @brief rgb2yuv
* @param rgb [0, 255]
* @param y [0, 255]
* @param u [0, 255]
* @param v [0, 255]
*/
inline void rgb2yuv(QRgb rgb, uint8_t &y, uint8_t &u, uint8_t &v)
{
int R = qRed(rgb);
int B = qBlue(rgb);
int G = qGreen(rgb);

int Y, U, V;
Y = 0.299 * R + 0.587 * G + 0.114 * B;
U = -0.169 * R - 0.331 * G + 0.500 * B + 128;
V = 0.500 * R - 0.419 * G - 0.081 * B + 128;

y = qBound(0, Y, 255);
u = qBound(0, U, 255);
v = qBound(0, V, 255);
}

inline QRgb yuv2rgb_fast(uint8_t y, uint8_t u, uint8_t v)
{
u = u - 128;
v = v - 128;

int r_dif = v + ((v * 103) >> 8);
int g_dif = -((u * 88) >> 8) - ((v * 183) >> 8);
int b_dif = u +((u * 198) >> 8);

int R = y + r_dif;
int G = y + g_dif;
int B = y + b_dif;

R = qBound(0, R, 255);
G = qBound(0, G, 255);
B = qBound(0, B, 255);

return qRgb(R, G, B);
}


RGB===Y+1.403×(V128)Y0.343×(U128)0.714×(V128)Y+1.770×(U128)

G 的计算稍微复杂点，0.343×(U128)0.714×(V128)$– 0.343 \times(U – 128) – 0.714 \times(V – 128)$ 的计算结果可以存在一个 256256$256 * 256$ 的表格中。这个表格中存放的数从 -135 到 134， 共有 270 个。因此，可以再用一个 256270$256*270$ 的表格来存储 Y$Y$ 和这个值的和。因此，G 的计算需要两层查表。即使这样，也比移位运算快得多。

class YUV2RGBConverter
{
public:
YUV2RGBConverter();
QRgb yuv2rgb(uint8_t y, uint8_t u, uint8_t v);
void yuy2(const uint8_t * yuy2, QRgb * rgb, size_t size);
void vyuy(const uint8_t * yuy2, QRgb * rgb, size_t size);
void yuv422(const uint8_t * yuv_y,
const uint8_t * yuv_u,
const uint8_t * yuv_v,
QRgb * rgb,
size_t size);
private:
static uint8_t RYV[256][256];
static uint8_t BYU[256][256];
static uint16_t TUV[256][256];
static uint8_t GYT[256][270];
static bool m_table_init;
};

inline QRgb YUV2RGBConverter::yuv2rgb(uint8_t y, uint8_t u, uint8_t v)
{
int R = RYV[y][v];
int G = GYT[y][TUV[u][v]];
int B = BYU[y][u];
return qRgb(R, G, B);
}

bool YUV2RGBConverter::m_table_init = false;
uint8_t YUV2RGBConverter::RYV[256][256];
uint8_t YUV2RGBConverter::BYU[256][256];
uint16_t YUV2RGBConverter::TUV[256][256];
uint8_t YUV2RGBConverter::GYT[256][270];

YUV2RGBConverter::YUV2RGBConverter()
{
int R, G, B, T;
if(!m_table_init)
{
for(int y = 0; y < 256; y++)
{
for(int v = 0; v < 256; v++)
{
int u = v;
R = qRound(y + 1.403 * (v - 128));
B = qRound(y + 1.770 * (u - 128));
RYV[y][v] = qBound(0, R, 255);
BYU[y][u] = qBound(0, B, 255);
}
}
for(int u = 0; u < 256; u++)
{
for(int v = 0; v < 256; v++)
{
T = qRound(-0.343 * (u - 128) - 0.714 * (v - 128));
// T 的范围 [-134, 135]
TUV[u][v] = qBound(0, T + 134, 269);
}
}
for(int y = 0; y < 256; y++)
{
for(int t = 0; t < 270; t++)
{
G = y + (t - 134);
GYT[y][t] = qBound(0, G, 255);
}
}

m_table_init = true;
}
}

void YUV2RGBConverter::yuy2(const uint8_t *yuy2, QRgb * rgb, size_t size)
{
size_t i = 0;
do
{
uint8_t y0 = *yuy2++;
uint8_t u = *yuy2++;
uint8_t y1 = *yuy2++;
uint8_t v = *yuy2++;

*rgb++ = yuv2rgb(y0, u, v);
*rgb++ = yuv2rgb(y1, u, v);
i += 2;
}while(i < size);
}

void YUV2RGBConverter::vyuy(const uint8_t * yuy2, QRgb * rgb, size_t size)
{
size_t i = 0;
do
{
uint8_t v = *yuy2++;
uint8_t y0 = *yuy2++;
uint8_t u = *yuy2++;
uint8_t y1 = *yuy2++;

*rgb++ = yuv2rgb(y0, u, v);
*rgb++ = yuv2rgb(y1, u, v);
i += 2;
}while(i < size);
}

void YUV2RGBConverter::yuv422(const uint8_t * yuv_y,
const uint8_t * yuv_u,
const uint8_t * yuv_v,
QRgb * rgb,
size_t size)
{
size_t i = 0;
do
{
uint8_t y0 = *yuv_y++;
uint8_t y1 = *yuv_y++;
uint8_t u = *yuv_u++;
uint8_t v = *yuv_v++;

*rgb++ = yuv2rgb(y0, u, v);
*rgb++ = yuv2rgb(y1, u, v);
i += 2;
}while(i < size);
}


YUY2是packed方式的。水平方向两个像素打包到一个DWORD，并且UV采样率只有Y的一半，这符合人的视觉特征能有效的压缩数据,具体布局为[Y0,U0,Y1,V0,Y2,U1,Y3,V1,Y4,U2,Y5,V2,…]。

vyuv 与 YUY2 很类似，只是排列顺序颠倒了：V,Y,U,Y,V,Y,U,Y,V,Y,U,Y,…..

yuv422 格式是把 YUV 这三个通道分开存储到三个数组。UV采样率还是只有Y的一半。

©️2019 CSDN 皮肤主题: 大白 设计师: CSDN官方博客