YUV与RGB的相互转换一直以来都是非常常用的基础算法,如何才能最高效的转换,成为一个难点问题,尤其是目前视频直播火热的时候,这些算法的优化也越发重要。
今天我结合自己的一些经验,给大家介绍一个简单的优化算法。
首先,我们看下最常用的YUV与RGB相互转换的算法公式,如下所示:
注意,RGB取值范围均为0-255:
1,RGB转YUV
Y = 0.299R + 0.587G + 0.114B
U = -0.147R - 0.289G + 0.436B
V = 0.615R - 0.515G - 0.100B
2,YUV转RGB
R = Y + 1.14V
G = Y - 0.39U - 0.58V
B = Y + 2.03U
我的优化方案如下:
优化1:看到上述算法,从算法优化角度来看,算法计算中,最好不要出现浮点运算,浮点运算比较耗时;
基于这一点,我们做如下操作:
Y * 256 = 0.299 * 256R + 0.587 * 256G + 0.114 * 256B
U * 256 = -0.147 * 256R - 0.289 * 256G + 0.436 * 256B
V * 256 = 0.615 * 256R - 0.515 * 256G - 0.100 * 256B
R * 256 = Y * 256 + 1.14 * 256V
G * 256 = Y * 256 - 0.39 * 256U - 0.58 * 256V
B * 256 = Y * 256 + 2.03 * 256U
简化上面的公式如下:
256Y = 76.544R + 150.272G + 29.184B
256U = -37.632R - 73.984G + 111.616B
256V = 157.44R - 131.84G - 25.6B
256R = 256Y + 291.84V
256G = 256Y - 99.84U - 148.48V
256B = 256Y + 519.68U
做到这一步,我这里要说明一下:我们这里的转换是有损的,适用于追求速度,而对效果要求不是100%准确的情况。
然后,我们就可以对上述公式进一步优化,彻底干掉小数:
256Y = 77R + 150G + 29B
256U = -38R - 74G + 112B
256V = 158R - 132G - 26B
256R = 256Y + 292V
256G = 256Y - 100U - 149V
256B = 256Y + 520U
实际上就是四舍五入,为什么要乘以256,这是实际上是为了缩小误差,当然你这个地方乘数越大,误差越小。
优化2:干掉所有乘法,用移位运算表示;
上述公式,我们可以用移位进行简单优化:
Y = (77R + 150G + 29B) >> 8
U = (-38R - 74G + 112B) >> 8
V = (158R - 132G - 26B) >> 8
R = (256Y + 292V) >> 8
G = (256Y - 100U - 149V) >> 8
B = (256Y + 520U) >> 8
做到此处,已经没有了浮点运算量了,但是我们发现虽然采用了移位运算,但是,公式中还有很多乘法运算,乘法跟移位运算相比,还是效率太低了,因此,我们将把所有乘法都改成移位运算。
如何将常数乘法改成移位运算?这里给个例子:
Y=Y*9可以改为:Y=(Y<<3)+Y
因此,我们可以讲YUV的公式继续改为最简:
RGB转YUV:
Y = ((R << 6) + (R << 3) + (R << 2) + R + (G << 7) + (G << 4) + (G << 2) + (G << 1) + (B << 4) + (B << 3) + (B << 2) + B) >> 8;
U = (-((R << 5) + (R << 2) + (R << 1)) - ((G << 6) + (G << 3) + (G << 1)) + ((B << 6) + (B << 5) + (B << 4))) >> 8;
V = ((R << 7) + (R << 4) + (R << 3) + (R << 2) + (R << 1) - ((G << 7) + (G << 2)) - ((B << 4) + (B << 3) + (B << 1))) >> 8;
YUV转RGB:
R = ((Y << 8) + ((V << 8) + (V << 5) + (V << 2))) >> 8;
G = ((Y << 8) - ((U << 6) + (U << 5) + (U << 2)) - ((V << 7) + (V << 4) + (V << 2) + V)) >> 8;
B = ((Y << 8) + (U << 9) + (U << 3)) >> 8;
static void RGBToYUV(int Red, int Green, int Blue, int* Y,int* U,int* V)
{
*Y = ((Red << 6) + (Red << 3) + (Red << 2) + Red + (Green << 7) + (Green << 4) + (Green << 2) + (Green << 1) + (Blue << 4) + (Blue << 3) + (Blue << 2) + Blue) >> 8;
*U = (-((Red << 5) + (Red << 2) + (Red << 1)) - ((Green << 6) + (Green << 3) + (Green << 1)) + ((Blue << 6) + (Blue << 5) + (Blue << 4))) >> 8;
*V = ((Red << 7) + (Red << 4) + (Red << 3) + (Red << 2) + (Red << 1) - ((Green << 7) + (Green << 2)) - ((Blue << 4) + (Blue << 3) + (Blue << 1))) >> 8;
};
static void YUVToRGB(int Y, int U, int V, int* Red, int* Green, int* Blue)
{
*Red = ((Y << 8) + ((V << 8) + (V << 5) + (V << 2))) >> 8;
*Green = ((Y << 8) - ((U << 6) + (U << 5) + (U << 2)) - ((V << 7) + (V << 4) + (V << 2) + V)) >> 8;
*Blue = ((Y << 8) + (U << 9) + (U << 3)) >> 8;
};
效果图:
原图
使用浮点型RGB-YUV-RGB结果图
使用优化版RGB-YUV-RGB结果图
在说一下,这个优化纯粹是追求速度,当然,效果差异很小,如果你需要的是100%的准确,最好还是要使用浮点计算。有什么问题,联系我,QQ1358009172