视频播放过程中,由于涉及到不同的色域和色彩描述,到底如何做才是最科学的了。
而且颜色的转换涉及到播放效率问题,是播放器开发过程中非常重要的环节。
更好的理解颜色转换需要对编码和播放有较深入的理解才能保证其正确性。
1)输入:Blu-ray节目源:其编码采用的颜色空间是YUV420
2)输出:显示设备:RGB PCLevel模式
目前由于蓝光节目源的高码流特性,现在节目源的颜色空间都是YUV420,我们解码出NV12或YV12就能保证在解码端不损失信号精度。即可以理解为无损解码。
如果我们解码完成后,要对颜色进行各种后处理,可以在yuv空间下进行,也可以转rgb空间进行,但无论在什么空间一定要从uint转到float下进行处理,避免精度丢失。
从yuv空间转到rgb是有信号丢失的,如果你在处理图像的过程中在yuv和rgb来回转换就存在着每转一次就损失一次精度问题。
现在的投影或电视为了兼容pc,其都是默认pc level rgb模式即8bit的时候,灰阶为【0,255】,而不是【16,235】
所以我们在输出的时候,yuv转rgb的时候注意转换系数,即转换矩阵。
广播电视对与节目制作有特定的标准,yuv转rgb,bt.601标清 bt.709高清1080P bt.2020高清4k,这些标准都是针对yuv来说,
每种标准对应的yuv转rgb系数也是不同的。
我们大部分电视目前实际上都是sRGB空间的电视,即标准rgb空间的电视,即要求输入内容为gamma2.2,灰阶为0-255的rgb信号,现在最新的电视开始支持hdr,hdr要求更高的颜色分辨率,最低10bit/12bit/16bit,10bit描述的颜色0-1024,16bit:0-65535
下面给出yuv转rgb矩阵生成函数:
该转换实际为yuv转rgb(tv level 16-235)在转rgb pc level
/*
M : bit num 8bit 10bit 12bit 16bit
Z: RGB的zero点位置 正常的RGB是0,
S:RGB的跨度,对于8bit的RGB是255
*/
void get_matrix(int colorspace, int M, int Z, int S)
{
float Kr = 0.2627f;
float Kb = 0.0593f;
switch (colorspace)
{
case CS_601:
Kr = 0.299f;
Kb = 0.114f;
break;
case CS_709:
Kr = 0.2126f;
Kb = 0.0722f;
break;
case CS_2020:
Kr = 0.2627f;
Kb = 0.0593f;
break;
default:;
}
float Kg = 1.0f - Kr - Kb;
int R = 0;
int G = 0;
int B = 0;
int L = Kr*R + Kb*B + Kg*G;
int Y = pow(2, M - 8) * (219 * (L - Z) / S + 16) + 0.5;
Y = (pow(2, M - 8) * 219 * Kr / S) *R + (pow(2, M - 8) * 219 * Kb / S)*B + (pow(2, M - 8) * 219 * Kg / S)*G - pow(2, M - 8) * (219 * Z / S - 16) + 0.5;
//int U = clip3(0, pow(2, M) - 1, floor(pow(2, (M - 8)) * (112 * (B - L) / ((1 - Kb)*S) + 128) + 0.5));
int U = pow(2, (M - 8)) * (112 * (B - L) / ((1 - Kb)*S) + 128) + 0.5;
U = -(pow(2, (M - 8)) * 112 * Kr / ((1 - Kb)*S))*R - (pow(2, (M - 8)) * 112 * (Kb - 1) / ((1 - Kb)*S))*B - (pow(2, (M - 8)) * 112 * Kg / ((1 - Kb)*S))*G + pow(2, (M - 8)) * 128 + 0.5;
//int V = clip3(0, pow(2, M) - 1, floor(pow(2, (M - 8)) * (112 * (R - L) / ((1 - Kr)*S) + 128) + 0.5));
int V = -(pow(2, (M - 8)) * 112 * (Kr - 1) / ((1 - Kr)*S))*R - (pow(2, (M - 8)) * 112 * Kb / ((1 - Kr)*S))*B - (pow(2, (M - 8)) * 112 * Kg / ((1 - Kr)*S))*G + pow(2, (M - 8)) * 128 + 0.5;
float a[3][4] = {
{ (pow(2, M - 8) * 219 * Kr / S) , (pow(2, M - 8) * 219 * Kg / S) , (pow(2, M - 8) * 219 * Kb / S) , -pow(2, M - 8) * (219 * Z / S - 16) + 0.5 },
{ -(pow(2, (M - 8)) * 112 * Kr / ((1 - Kb)*S)) ,-(pow(2, (M - 8)) * 112 * Kg / ((1 - Kb)*S)) ,-(pow(2, (M - 8)) * 112 * (Kb - 1) / ((1 - Kb)*S)) , pow(2, (M - 8)) * 128 + 0.5 },
{ -(pow(2, (M - 8)) * 112 * (Kr - 1) / ((1 - Kr)*S)) , -(pow(2, (M - 8)) * 112 * Kg / ((1 - Kr)*S)) , -(pow(2, (M - 8)) * 112 * Kb / ((1 - Kr)*S)) , pow(2, (M - 8)) * 128 + 0.5 }
};
//RGB系数
_debugTrace("|%.6ff, %.6ff, %.6ff, %.6ff|\n", a[0][0], a[0][1], a[0][2], a[0][3]);
_debugTrace("|%.6ff, %.6ff, %.6ff, %.6ff|\n", a[1][0], a[1][1], a[1][2], a[1][3]);
_debugTrace("|%.6ff, %.6ff, %.6ff, %.6ff|\n", a[2][0], a[2][1], a[2][2], a[2][3]);
//YUV系数
float D = a[0][0] * a[1][1] * a[2][2] + a[0][1] * a[1][2] * a[2][0] + a[0][2] * a[1][0] * a[2][1] - a[0][0] * a[1][2] * a[2][1] - a[0][1] * a[1][0] * a[2][2] - a[0][2] * a[1][1] * a[2][0];
//float b[3] = {
// Y - a[0][3],
// U - a[1][3],
// V - a[2][3]
//};
float D1 = (Y - a[0][3]) * a[1][1] * a[2][2] +
a[0][1] * a[1][2] * (V - a[2][3]) +
a[0][2] * (U - a[1][3]) * a[2][1] -
(Y - a[0][3]) * a[1][2] * a[2][1] -
a[0][1] * (U - a[1][3]) * a[2][2] -
a[0][2] * a[1][1] * (V - a[2][3]);
D1 = a[1][1] * a[2][2] * Y - a[1][1] * a[2][2] * a[0][3] +
a[0][1] * a[1][2] * V - a[0][1] * a[1][2] * a[2][3] +
a[0][2] * a[2][1] * U - a[0][2] * a[1][3] * a[2][1] -
a[1][2] * a[2][1] * Y + a[1][2] * a[2][1] * a[0][3] -
a[0][1] * a[2][2] * U + a[0][1] * a[1][3] * a[2][2] -
a[0][2] * a[1][1] * V + a[0][2] * a[1][1] * a[2][3];
D1 = Y*(a[1][1] * a[2][2] - a[1][2] * a[2][1]) +
U*(a[0][2] * a[2][1] - a[0][1] * a[2][2]) +
V*(a[0][1] * a[1][2] - a[0][2] * a[1][1]) +
(-a[1][1] * a[2][2] * a[0][3] - a[0][1] * a[1][2] * a[2][3] - a[0][2] * a[1][3] * a[2][1] + a[1][2] * a[2][1] * a[0][3] + a[0][1] * a[1][3] * a[2][2] + a[0][2] * a[1][1] * a[2][3]);
float D2 = a[0][0] * (U - a[1][3]) * a[2][2] +
(Y - a[0][3]) * a[1][2] * a[2][0] +
a[0][2] * a[1][0] * (V - a[2][3]) -
a[0][0] * a[1][2] * (V - a[2][3]) -
(Y - a[0][3]) * a[1][0] * a[2][2] -
a[0][2] * (U - a[1][3]) * a[2][0];
D2 = Y*(a[1][2] * a[2][0] - a[1][0] * a[2][2]) +
U*(a[0][0] * a[2][2] - a[0][2] * a[2][0]) +
V*(a[0][2] * a[1][0] - a[0][0] * a[1][2]) +
(a[0][0] * (-a[1][3])* a[2][2]) - a[0][3] * a[1][2] * a[2][0] - a[0][2] * a[1][0] * a[2][3] + a[0][0] * a[1][2] * a[2][3] + a[0][3] * a[1][0] * a[2][2] + a[0][2] * a[1][3] * a[2][0];
float D3 = a[0][0] * a[1][1] * (V - a[2][3]) +
a[0][1] * (U - a[1][3]) * a[2][0] +
(Y - a[0][3]) * a[1][0] * a[2][1] -
a[0][0] * (U - a[1][3]) * a[2][1] -
a[0][1] * a[1][0] * (V - a[2][3]) -
(Y - a[0][3]) * a[1][1] * a[2][0];
D3 = Y*(a[1][0] * a[2][1] - a[1][1] * a[2][0]) +
U*(a[0][1] * a[2][0] - a[0][0] * a[2][1]) +
V*(a[0][0] * a[1][1] - a[0][1] * a[1][0]) +
a[0][0] * a[1][1] * (-a[2][3]) + a[0][1] * (-a[1][3]) * a[2][0] + (-a[0][3]) * a[1][0] * a[2][1] - a[0][0] * (-a[1][3]) * a[2][1] - a[0][1] * a[1][0] * (-a[2][3]) - (-a[0][3]) * a[1][1] * a[2][0];
R = D1 / D;
G = D2 / D;
B = D3 / D;
float b[3][4] = {
{ (a[1][1] * a[2][2] - a[1][2] * a[2][1]) / D, (a[0][2] * a[2][1] - a[0][1] * a[2][2]) / D, (a[0][1] * a[1][2] - a[0][2] * a[1][1]) / D, ((-a[1][1] * a[2][2] * a[0][3] - a[0][1] * a[1][2] * a[2][3] - a[0][2] * a[1][3] * a[2][1] + a[1][2] * a[2][1] * a[0][3] + a[0][1] * a[1][3] * a[2][2] + a[0][2] * a[1][1] * a[2][3])) / D },
{ (a[1][2] * a[2][0] - a[1][0] * a[2][2]) / D, (a[0][0] * a[2][2] - a[0][2] * a[2][0]) / D, (a[0][2] * a[1][0] - a[0][0] * a[1][2]) / D, ((a[0][0] * (-a[1][3])* a[2][2]) - a[0][3] * a[1][2] * a[2][0] - a[0][2] * a[1][0] * a[2][3] + a[0][0] * a[1][2] * a[2][3] + a[0][3] * a[1][0] * a[2][2] + a[0][2] * a[1][3] * a[2][0]) / D },
{ (a[1][0] * a[2][1] - a[1][1] * a[2][0]) / D, (a[0][1] * a[2][0] - a[0][0] * a[2][1]) / D, (a[0][0] * a[1][1] - a[0][1] * a[1][0]) / D,(a[0][0] * a[1][1] * (-a[2][3]) + a[0][1] * (-a[1][3]) * a[2][0] + (-a[0][3]) * a[1][0] * a[2][1] - a[0][0] * (-a[1][3]) * a[2][1] - a[0][1] * a[1][0] * (-a[2][3]) - (-a[0][3]) * a[1][1] * a[2][0]) / D }
};
//RGB系数
_debugTrace("YUV coef matrix :\n");
_debugTrace("|%.6ff, %.6ff, %.6ff, %.6ff|\n", b[0][0], b[0][1], b[0][2], b[0][3] / 255);
_debugTrace("|%.6ff, %.6ff, %.6ff, %.6ff|\n", b[1][0], b[1][1], b[1][2], b[1][3] / 255);
_debugTrace("|%.6ff, %.6ff, %.6ff, %.6ff|\n", b[2][0], b[2][1], b[2][2], b[2][3] / 255);
/*
|1.164384, -0.000000, 1.792741, -249.579559|
|1.164383, -0.213249, -0.532909, 76.668961|
|1.164383, 2.112402, -0.000000, -290.655945|
*/
Y = 82;
U = 90;
V = 240;
R = 1.164384*Y + 1.792741*V - 249.579559;
G = 1.164384*Y - 0.213249 *U - 0.532909*V + 76.668961;
B = 1.164384*Y + 2.112402*U - 290.655945;
_debugTrace("R = %d G = %d B = %d\n", R, G, B);
}