流媒体之色彩转换——RGB(X)与YUV之间转换

本文详细介绍了RGB32到RGB24的转换方法,以及RGB与YUV格式相互转换的多种算法,包括常规转换、去浮点转换、去浮点去乘法转换和查表转换,并提供了C++代码实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


 

一:RGB32转RGB24

RGB32与RGB24相比多个Alpha分量,因此在转化的时候可以选择直接丢弃Alpha分量,也可以选择将r、g和b分别乘以归一化的Alpha因子。例如RGBA转RGB:

void rgba_to_rgb(const uint8_t *src, uint8_t *dst, uint32_t width, uint32_t height, bool alpha)
{
	uint8_t *src_ptr = const_cast<uint8_t *>(src);
	uint8_t *dst_ptr = dst;

	float a = 1.f;
	int src_pos = 0;
	int dst_pos = 0;
	if (alpha) {
		for (int i = 0; i < width; i++) {
			for (int j = 0; j < height; j++) {
				src_pos = i * 4 + j;
				dst_pos = i * 3 + j;
				a = (*(src_ptr + src_pos + 3)) / 255.f;
				*(dst_ptr + dst_pos) = *(src_ptr + src_pos) * a;
				*(dst_ptr + dst_pos + 1) = *(src_ptr + src_pos + 1) * a;
				*(dst_ptr + dst_pos + 2) = *(src_ptr + src_pos + 2) * a;
			}
		}
	}
	else {
		for (int i = 0; i < width; i++) {
			for (int j = 0; j < height; j++) {
				src_pos = i * 4 + j;
				dst_pos = i * 3 + j;
				*(dst_ptr + dst_pos) = *(src_ptr + src_pos);
				*(dst_ptr + dst_pos + 1) = *(src_ptr + src_pos + 1);
				*(dst_ptr + dst_pos + 2) = *(src_ptr + src_pos + 2);
			}
		}
	}
}

 
 

二:YUV与RGB相互转换

Keith Jack’s的书《Video Demystified》(ISBN 1-878707-09-4) 给出的转换公式:

RGB->YUV:

Y = 0.257 R + 0.504 G + 0.098 B + 16 C r = V = 0.439 R − 0.368 G − 0.071 B + 128 C b = U = − 0.148 R − 0.291 G + 0.439 B + 128 Y=0.257R+0.504G+0.098B+16\\ Cr=V=0.439R-0.368G-0.071B+128\\ Cb=U=-0.148R-0.291G+0.439B+128 Y=0.257R+0.504G+0.098B+16Cr=V=0.439R0.368G0.071B+128Cb=U=0.148R0.291G+0.439B+128

YUV->RGB:

B = 1.164 ( Y − 16 ) + 2.018 ( U − 128 ) G = 1.164 ( Y − 16 ) − 0.813 ( V − 128 ) − 0.391 ( U − 128 ) R = 1.164 ( Y − 16 ) + 1.596 ( V − 128 ) B=1.164(Y-16)+2.018(U-128)\\ G=1.164(Y-16)-0.813(V-128)-0.391(U-128)\\ R=1.164(Y-16)+1.596(V-128) B=1.164(Y16)+2.018(U128)G=1.164(Y16)0.813(V128)0.391(U128)R=1.164(Y16)+1.596(V128)

上述式子RGB范围为 [ 0 , 255 ] [0,255] [0,255] ;Y范围为 [ 16 , 235 ] [16, 235] [16,235] ;UV范围为 [ 16 , 239 ] [16, 239] [16,239] 。计算超出范围后需要截断处理。

CCIR 601定义的转换公式为:

RGB->YUV:

Y = 0.299 R + 0.587 G + 0.114 B C r = V = 0.713 ( R − Y ) = 0.500 R − 0.419 G − 0.081 B C b = U = 0.564 ( B − Y ) = − 0.169 R − 0.331 G + 0.500 B Y=0.299R+0.587G+0.114B\\ Cr=V=0.713(R-Y)=0.500R-0.419G-0.081B\\ Cb=U=0.564(B-Y)=-0.169R-0.331G+0.500B Y=0.299R+0.587G+0.114BCr=V=0.713(RY)=0.500R0.419G0.081BCb=U=0.564(BY)=0.169R0.331G+0.500B

YUV->RGB:

R = Y + 1.403 V G = Y − 0.344 U − 0.714 V B = Y + 1.770 U R=Y+1.403V\\ G=Y-0.344U-0.714V\\ B=Y+1.770U R=Y+1.403VG=Y0.344U0.714VB=Y+1.770U

上述式子RGB范围为 [ 0 , 1 ] [0, 1] [0,1] ;Y范围为 [ 0 , 1 ] [0, 1] [0,1] ;Cr和Cb范围为 [ − 0.5 , 0.5 ] [-0.5, 0.5] [0.5,0.5]

如果把RGB和YUV的范围都缩放到 [ 0 , 255 ] [0, 255] [0,255] ,常用的转换公式为:

RGB->YUV:

Y = 0.299 R + 0.587 G + 0.114 B C r = V = 0.500 R − 0.419 G − 0.081 B + 128 C b = U = − 0.169 R − 0.331 G + 0.500 B + 128 Y=0.299R+0.587G+0.114B\\ Cr=V=0.500R-0.419G-0.081B+128\\ Cb=U=-0.169R-0.331G+0.500B+128 Y=0.299R+0.587G+0.114BCr=V=0.500R0.419G0.081B+128Cb=U=0.169R0.331G+0.500B+128

YUV->RGB:

R = Y + 1.403 ∗ ( V − 128 ) G = Y − 0.343 ∗ ( U − 128 ) − 0.714 ∗ ( V − 128 ) B = Y + 1.770 ∗ ( U − 128 ) R=Y+1.403*(V-128)\\ G=Y-0.343*(U-128)-0.714*(V-128)\\ B=Y+1.770*(U-128) R=Y+1.403(V128)G=Y0.343(U128)0.714(V128)B=Y+1.770(U128)

2.1 YUV->RGB

2.1.1 常规转换

按照公式进行常规运算,包含浮点运算和乘法运算。

void yuv_to_rgb(uint8_t y, uint8_t u, uint8_t v, uint8_t *r, uint8_t *g, uint8_t *b)
{
	int _r = y + 1.403 * (v - 128);
	int _g = y - 0.343 * (u - 128) - 0.714 * (v - 128);
	int _b = y + 1.770 * (u - 128);

	*r = _r > 255 ? 255 : (_r < 0 ? 0 : (uint8_t)_r);
	*g = _g > 255 ? 255 : (_g < 0 ? 0 : (uint8_t)_g);
	*b = _b > 255 ? 255 : (_b < 0 ? 0 : (uint8_t)_b);
}
2.1.2 去浮点转换

去除转换公式中的浮点运算,但会有精度损失:
R = Y + 1.403 ∗ ( V − 128 ) G = Y − 0.343 ∗ ( U − 128 ) − 0.714 ∗ ( V − 128 ) B = Y + 1.770 ∗ ( U − 128 ) R=Y+1.403*(V-128)\\ G=Y-0.343*(U-128)-0.714*(V-128)\\ B=Y+1.770*(U-128) R=Y+1.403(V128)G=Y0.343(U128)0.714(V128)B=Y+1.770(U128)
转换为:
V ′ = V − 128 U ′ = U − 128 R = Y + 1.403 ∗ V ′ = Y + V ′ + 0.403 ∗ V ′ ≈ Y + V ′ + ( ( V ′ ∗ 103 ) &gt; &gt; 8 ) G = Y − 0.343 ∗ U ′ − 0.714 ∗ V ′ ≈ Y − ( ( U ′ ∗ 88 ) &gt; &gt; 8 ) − ( ( V ′ ∗ 183 ) &gt; &gt; 8 ) B = Y + 1.770 ∗ U ′ ≈ Y + U ′ + ( ( U ′ ∗ 198 ) &gt; &gt; 8 ) V&#x27;=V-128\\ U&#x27;=U-128\\ R=Y+1.403*V&#x27;=Y+V&#x27;+0.403*V&#x27;≈Y+V&#x27;+((V&#x27;*103)&gt;&gt;8)\\ G=Y-0.343*U&#x27;-0.714*V&#x27;≈Y-((U&#x27;*88)&gt;&gt;8)-((V&#x27;*183)&gt;&gt;8)\\ B=Y+1.770*U&#x27;≈Y+U&#x27;+((U&#x27;*198)&gt;&gt;8) V=V128U=U128R=Y+1.403V=Y+V+0.403VY+V+((V103)>>8)G=Y0.343U0.714VY((U88)>>8)((V183)>>8)B=Y+1.770UY+U+((U198)>>8)

void yuv_to_rgb_faster(uint8_t y, uint8_t u, uint8_t v,
                       uint8_t *r, uint8_t *g, uint8_t *b)
{
	int _u = u - 128;
	int _v = v - 128;

	int _r = y + _v + ((_v * 103) >> 8);
	int _g = y - ((_u * 88) >> 8) - ((_v * 183) >> 8);
	int _b = y + _u + ((_u * 198) >> 8);

	*r = _r > 255 ? 255 : (_r < 0 ? 0 : (uint8_t)_r);
	*g = _g > 255 ? 255 : (_g < 0 ? 0 : (uint8_t)_g);
	*b = _b > 255 ? 255 : (_b < 0 ? 0 : (uint8_t)_b);
}
2.1.3 去浮点去乘法转换

接上面优化,去除乘法操作(改乘法为右移操作):
R ≈ Y + V ′ + ( ( V ′ ∗ ( 64 + 32 + 4 + 2 + 1 ) ) &gt; &gt; 8 ) = Y + V ′ + ( ( ( V ′ &lt; &lt; 6 ) + ( V ′ &lt; &lt; 5 ) + ( V ′ &lt; &lt; 2 ) + ( V ′ &lt; &lt; 1 ) + V ′ ) &gt; &gt; 8 ) G ≈ Y − ( ( U ′ ∗ ( 64 + 16 + 8 ) ) &gt; &gt; 8 ) − ( ( V ′ ∗ ( 128 + 32 + 16 + 4 + 2 + 1 ) ) &gt; &gt; 8 ) = Y − ( ( ( U ′ &lt; &lt; 6 ) + ( U ′ &lt; &lt; 4 ) + ( U ′ &lt; &lt; 3 ) &gt; &gt; 8 ) − ( ( ( V ′ &lt; &lt; 7 ) + ( V ′ &lt; &lt; 5 ) + ( V ′ &lt; &lt; 4 ) + ( V ′ &lt; &lt; 2 ) + ( V ′ &lt; &lt; 1 ) + V ′ ) &gt; &gt; 8 ) B ≈ Y + U ′ + ( ( U ′ ∗ ( 128 + 64 + 4 + 2 ) ) &gt; &gt; 8 ) = Y + U ′ + ( ( ( U ′ &lt; &lt; 7 ) + ( U ′ &lt; &lt; 6 ) + ( U ′ &lt; &lt; 2 ) + ( U ′ &lt; &lt; 1 ) ) &gt; &gt; 8 ) R≈Y+V&#x27;+((V&#x27;*(64 + 32 + 4 + 2 + 1))&gt;&gt;8)\\ =Y+V&#x27;+(((V&#x27;&lt;&lt;6)+(V&#x27;&lt;&lt;5)+(V&#x27;&lt;&lt;2)+(V&#x27;&lt;&lt;1)+V&#x27;)&gt;&gt;8)\\ G≈Y-((U&#x27;*(64 + 16 + 8))&gt;&gt;8)-((V&#x27;*(128+32+16+4+2+1))&gt;&gt;8)\\ =Y-(((U&#x27;&lt;&lt;6)+(U&#x27;&lt;&lt;4)+(U&#x27;&lt;&lt;3)&gt;&gt;8)-(((V&#x27;&lt;&lt;7)+(V&#x27;&lt;&lt;5)+(V&#x27;&lt;&lt;4)+(V&#x27;&lt;&lt;2)+(V&#x27;&lt;&lt;1)+V&#x27;)&gt;&gt;8)\\ B≈Y+U&#x27;+((U&#x27;*(128+64+4+2))&gt;&gt;8)\\ =Y+U&#x27;+(((U&#x27;&lt;&lt;7)+(U&#x27;&lt;&lt;6)+(U&#x27;&lt;&lt;2)+(U&#x27;&lt;&lt;1))&gt;&gt;8) RY+V+((V(64+32+4+2+1))>>8)=Y+V+(((V<<6)+(V<<5)+(V<<2)+(V<<1)+V)>>8)GY((U(64+16+8))>>8)((V(128+32+16+4+2+1))>>8)=Y(((U<<6)+(U<<4)+(U<<3)>>8)(((V<<7)+(V<<5)+(V<<4)+(V<<2)+(V<<1)+V)>>8)BY+U+((U(128+64+4+2))>>8)=Y+U+(((U<<7)+(U<<6)+(U<<2)+(U<<1))>>8)

void yuv_to_rgb_speed(uint8_t y, uint8_t u, uint8_t v,
                      uint8_t *r, uint8_t *g, uint8_t *b)
{
	int _u = u - 128;
	int _v = v - 128;

	int _r = y + _v + (((_v << 6) + (_v << 5) + (_v << 2) + (_v << 1) + _v) >> 8);
	int _g = y - (((_u << 6) + (_u << 4) + (_u << 3)) >> 8) - (((_v << 7) + (_v << 5)
		+ (_v << 4) + (_v << 2) + (_v << 1) + _v) >> 8);
	int _b = y + _u + (((_u << 7) + (_u << 6) + (_u << 2) + (_u << 1)) >> 8);

	*r = _r > 255 ? 255 : (_r < 0 ? 0 : (uint8_t)_r);
	*g = _g > 255 ? 255 : (_g < 0 ? 0 : (uint8_t)_g);
	*b = _b > 255 ? 255 : (_b < 0 ? 0 : (uint8_t)_b);
}
2.1.4 查表转换

查表法为事先将计算结果存储在表中,之后在表中查找。由于Y、U和V分量为 [ 0 , 255 ] [0, 255] [0,255] 之间的离散值,所以可以采用查表法:

uint8_t g_r[256][256];
uint8_t g_g[256][256][256];
uint8_t g_b[256][256];
void setup_yuv_to_rgb_table()
{
	// R = Y + 1.403 * (V - 128);
	// g_r[Y][V]
	for (int i = 0; i < 256; i++) {
		for (int j = 0; j < 256; j++) {
			int r = i + 1.403 * (j - 128);
			g_r[i][j] = r > 255 ? 255 : (r < 0 ? 0 : (uint8_t)r);
		}
	}

	// G = Y - 0.343 * (U - 128) - 0.714 * (V - 128);
	// g_g[Y][U][V]
	for (int i = 0; i < 256; i++) {
		for (int j = 0; j < 256; j++) {
			for (int k = 0; k < 256; k++) {
				int g = i - 0.343 * (j - 128) - 0.714 * (k - 128);
				g_g[i][j][k] = g > 255 ? 255 : (g < 0 ? 0 : (uint8_t)g);
			}
		}
	}
	
	// B = Y + 1.770 * (U - 128);
	// g_b[Y][U]
	for (int i = 0; i < 256; i++) {
		for (int j = 0; j < 256; j++) {
			int b = i + 1.770 * (j - 128);
			g_b[i][j] = b > 255 ? 255 : (b < 0 ? 0 : (uint8_t)b);
		}
	}
}

void yuv_to_rgb_table(uint8_t y, uint8_t u, uint8_t v,
                      uint8_t *r, uint8_t *g, uint8_t *b)
{
	*r = g_r[y][v];
	*g = g_g[y][u][v];
	*b = g_b[y][u];
}
2.1.5 测试
void test_yuv_to_rgb()
{
	uint8_t y = 100, u = 100, v = 100;
	uint8_t r = 0, g = 0, b = 0;
	uint64_t loop = 1000000000;

	// Normal
	auto time_start = std::chrono::system_clock::now();
	for (uint64_t i = 0; i < loop; i++) {
		yuv_to_rgb(y, u, v, &r, &g, &b);
	}
	std::cout << std::to_string(r) << ", " << std::to_string(g) << " ," << std::to_string(b) << std::endl;
	std::chrono::duration<double> time_spend = std::chrono::system_clock::now() - time_start;
	double time_cost = time_spend.count() * 1000;
	std::cout << "Normal Time: " << time_cost << " ms" << std::endl;

	// Faster
	time_start = std::chrono::system_clock::now();
	for (uint64_t i = 0; i < loop; i++) {
		yuv_to_rgb_faster(y, u, v, &r, &g, &b);
	}
	std::cout << std::to_string(r) << ", " << std::to_string(g) << " ," << std::to_string(b) << std::endl;
	time_spend = std::chrono::system_clock::now() - time_start;
	time_cost = time_spend.count() * 1000;
	std::cout << "Faster Time: " << time_cost << " ms" << std::endl;

	// Speed
	time_start = std::chrono::system_clock::now();
	for (uint64_t i = 0; i < loop; i++) {
		yuv_to_rgb_speed(y, u, v, &r, &g, &b);
	}
	std::cout << std::to_string(r) << ", " << std::to_string(g) << " ," << std::to_string(b) << std::endl;
	time_spend = std::chrono::system_clock::now() - time_start;
	time_cost = time_spend.count() * 1000;
	std::cout << "Speed Time: " << time_cost << " ms" << std::endl;

	// Table
	setup_yuv_to_rgb_table();
	time_start = std::chrono::system_clock::now();
	for (uint64_t i = 0; i < loop; i++) {
		yuv_to_rgb_table(y, u, v, &r, &g, &b);
	}
	std::cout << std::to_string(r) << ", " << std::to_string(g) << " ," << std::to_string(b) << std::endl;
	time_spend = std::chrono::system_clock::now() - time_start;
	time_cost = time_spend.count() * 1000;
	std::cout << "Table Time: " << time_cost << " ms" << std::endl;
}

测试结果:
在这里插入图片描述
移位操作替代乘法操作的优化实际运行结果并不一定好于乘法算法,另外Release模式下要去除全程序优化,否则时间测量无效。

2.2 RGB->YUV

2.2.1 常规转换

按照公式进行常规运算,包含浮点运算和乘法运算。

void rgb_to_yuv(uint8_t r, uint8_t g, uint8_t b, uint8_t *y, uint8_t *u, uint8_t *v)
{
	int _y = 0.299 * r + 0.587 * g + 0.114 * b;
	int _u = -0.169 * r - 0.331 * g + 0.500 * b + 128;
	int _v = 0.500 * r - 0.419 * g - 0.081 * b + 128;

	*y = _y > 255 ? 255 : (_y < 0 ? 0 : (uint8_t)_y);
	*u = _u > 255 ? 255 : (_u < 0 ? 0 : (uint8_t)_u);
	*v = _v > 255 ? 255 : (_v < 0 ? 0 : (uint8_t)_v);
}
2.2.2 去浮点转换

Y = 0.299 R + 0.587 G + 0.114 B C r = V = 0.500 R − 0.419 G − 0.081 B + 128 C b = U = − 0.169 R − 0.331 G + 0.500 B + 128 Y=0.299R+0.587G+0.114B\\ Cr=V=0.500R-0.419G-0.081B+128\\ Cb=U=-0.169R-0.331G+0.500B+128 Y=0.299R+0.587G+0.114BCr=V=0.500R0.419G0.081B+128Cb=U=0.169R0.331G+0.500B+128
转化为:
Y = 0.299 R + 0.587 G + 0.114 B = ( 77 R + 150 G + 29 B ) &gt; &gt; 8 C r = V = 0.500 R − 0.419 G − 0.081 B + 128 = ( 128 R − 107 G − 21 B + 32768 ) &gt; &gt; 8 C b = U = − 0.169 R − 0.331 G + 0.500 B + 128 = ( − 43 R − 85 G + 128 B + 32768 ) &gt; &gt; 8 Y=0.299R+0.587G+0.114B=(77R+150G+29B)&gt;&gt;8\\ Cr=V=0.500R-0.419G-0.081B+128=(128R-107G-21B+32768)&gt;&gt;8\\ Cb=U=-0.169R-0.331G+0.500B+128=(-43R-85G+128B+32768)&gt;&gt;8 Y=0.299R+0.587G+0.114B=(77R+150G+29B)>>8Cr=V=0.500R0.419G0.081B+128=(128R107G21B+32768)>>8Cb=U=0.169R0.331G+0.500B+128=(43R85G+128B+32768)>>8

void rgb_to_yuv_faster(uint8_t r, uint8_t g, uint8_t b, uint8_t *y, uint8_t *u, uint8_t *v)
{
	int _y = ((77 * r + 150 * g + 29 * b) >> 8);
	int _u = ((128 * r - 107 * g - 21 * b + 32768) >> 8);
	int _v = ((-43 * r - 85 * g + 128 * b + 32768) >> 8);

	*y = _y > 255 ? 255 : (_y < 0 ? 0 : (uint8_t)_y);
	*u = _u > 255 ? 255 : (_u < 0 ? 0 : (uint8_t)_u);
	*v = _v > 255 ? 255 : (_v < 0 ? 0 : (uint8_t)_v);
}
2.2.3 去浮点去乘法转换

Y = ( R &lt; &lt; 6 + R &lt; &lt; 3 + R &lt; &lt; 2 + R + G &lt; &lt; 7 + G &lt; &lt; 4 + G &lt; &lt; 2 + G &lt; &lt; 1 + B &lt; &lt; 4 + B &lt; &lt; 3 + B &lt; &lt; 2 + B ) &gt; &gt; 8 U = ( R &lt; &lt; 7 − G &lt; &lt; 6 − G &lt; &lt; 5 − G &lt; &lt; 3 − G &lt; &lt; 1 − G − B &lt; &lt; 4 − B &lt; &lt; 2 − B + 32768 ) &gt; &gt; 8 V = ( − R &lt; &lt; 5 − R &lt; &lt; 3 − R &lt; &lt; 1 − R − G &lt; &lt; 6 − G &lt; &lt; 4 − G &lt; &lt; 2 − G + B &lt; &lt; 7 + 32768 ) &gt; &gt; 8 Y=(R&lt;&lt;6+R&lt;&lt;3+R&lt;&lt;2+R+G&lt;&lt;7+G&lt;&lt;4+G&lt;&lt;2+G&lt;&lt;1+B&lt;&lt;4+B&lt;&lt;3+B&lt;&lt;2+B)&gt;&gt;8\\ U=(R&lt;&lt;7-G&lt;&lt;6-G&lt;&lt;5-G&lt;&lt;3-G&lt;&lt;1-G-B&lt;&lt;4-B&lt;&lt;2-B+32768)&gt;&gt;8\\ V=(-R&lt;&lt;5-R&lt;&lt;3-R&lt;&lt;1-R-G&lt;&lt;6-G&lt;&lt;4-G&lt;&lt;2-G+B&lt;&lt;7+32768)&gt;&gt;8 Y=(R<<6+R<<3+R<<2+R+G<<7+G<<4+G<<2+G<<1+B<<4+B<<3+B<<2+B)>>8U=(R<<7G<<6G<<5G<<3G<<1GB<<4B<<2B+32768)>>8V=(R<<5R<<3R<<1RG<<6G<<4G<<2G+B<<7+32768)>>8

void rgb_to_yuv_speed(uint8_t r, uint8_t g, uint8_t b, uint8_t *y, uint8_t *u, uint8_t *v)
{
	int _y = (((r << 6) + (r << 3) + (r << 2) + r + (g << 7) + (g << 4) + (g << 2) + (g << 1)
		+ (b << 4) + (b << 3) + (b << 2) + b) >> 8);
	int _u = (((r << 7) - (g << 6) - (g << 5) - (g << 3) - (g << 1) - g
		- (b << 4) - (b << 2) - b + 32768) >> 8);
	int _v = ((-(r << 5) - (r << 3) - (r << 1) - r - (g << 6) - (g << 4) - (g << 2) - g
		+ (b << 7) + 32768) >> 8);

	*y = _y > 255 ? 255 : (_y < 0 ? 0 : (uint8_t)_y);
	*u = _u > 255 ? 255 : (_u < 0 ? 0 : (uint8_t)_u);
	*v = _v > 255 ? 255 : (_v < 0 ? 0 : (uint8_t)_v);
}
2.2.4 查表转换

事先存储计算结果,转换时直接查表:

uint8_t g_y[256][256][256];
uint8_t g_u[256][256][256];
uint8_t g_v[256][256][256];
void setup_rgb_to_yuv_table()
{
	// Y = 0.299 * R + 0.587 * G + 0.114 * B;
	// g_y[r][g][b]
	for (int i = 0; i < 256; i++) {
		for (int j = 0; j < 256; j++) {
			for (int k = 0; k < 256; k++) {
				int y = 0.299 * i + 0.587 * j + 0.114 * k;
				g_y[i][j][k] = y > 255 ? 255 : (y < 0 ? 0 : (uint8_t)y);
			}
		}
	}

	// U = -0.169 * R - 0.331 * G + 0.500 * B + 128;
	// g_u[r][g][b]
	for (int i = 0; i < 256; i++) {
		for (int j = 0; j < 256; j++) {
			for (int k = 0; k < 256; k++) {
				int u = -0.169 * i - 0.331 * j + 0.500 * k + 128;
				g_u[i][j][k] = u > 255 ? 255 : (u < 0 ? 0 : (uint8_t)u);
			}
		}
	}

	// V = 0.500 * R - 0.419 * G - 0.081 * B + 128;
	// g_v[r][g][b]
	for (int i = 0; i < 256; i++) {
		for (int j = 0; j < 256; j++) {
			for (int k = 0; k < 256; k++) {
				int v = 0.500 * i - 0.419 * j - 0.081 * k + 128;
				g_v[i][j][k] = v > 255 ? 255 : (v < 0 ? 0 : (uint8_t)v);
			}
		}
	}
}

void rgb_to_yuv_table(uint8_t r, uint8_t g, uint8_t b, uint8_t *y, uint8_t *u, uint8_t *v)
{
	*y = g_y[r][g][b];
	*u = g_u[r][g][b];
	*v = g_v[r][g][b];
}
2.2.5 测试
void test_rgb_to_yuv()
{
	uint8_t r = 100, g = 100, b = 100;
	uint8_t y = 0, u = 0, v = 0;
	uint64_t loop = 1000000000;

	// Normal
	auto time_start = std::chrono::system_clock::now();
	for (uint64_t i = 0; i < loop; i++) {
		rgb_to_yuv(r, g, b, &y, &u, &v);
	}
	std::cout << std::to_string(y) << ", " << std::to_string(u) << " ," << std::to_string(v) << std::endl;
	std::chrono::duration<double> time_spend = std::chrono::system_clock::now() - time_start;
	double time_cost = time_spend.count() * 1000;
	std::cout << "Normal Time: " << time_cost << " ms" << std::endl;

	// Faster
	time_start = std::chrono::system_clock::now();
	for (uint64_t i = 0; i < loop; i++) {
		rgb_to_yuv_faster(r, g, b, &y, &u, &v);
	}
	std::cout << std::to_string(y) << ", " << std::to_string(u) << " ," << std::to_string(v) << std::endl;
	time_spend = std::chrono::system_clock::now() - time_start;
	time_cost = time_spend.count() * 1000;
	std::cout << "Faster Time: " << time_cost << " ms" << std::endl;

	// Speed
	time_start = std::chrono::system_clock::now();
	for (uint64_t i = 0; i < loop; i++) {
		rgb_to_yuv_speed(r, g, b, &y, &u, &v);
	}
	std::cout << std::to_string(y) << ", " << std::to_string(u) << " ," << std::to_string(v) << std::endl;
	time_spend = std::chrono::system_clock::now() - time_start;
	time_cost = time_spend.count() * 1000;
	std::cout << "Speed Time: " << time_cost << " ms" << std::endl;

	// Table
	setup_rgb_to_yuv_table();
	time_start = std::chrono::system_clock::now();
	for (uint64_t i = 0; i < loop; i++) {
		rgb_to_yuv_table(r, g, b, &y, &u, &v);
	}
	std::cout << std::to_string(y) << ", " << std::to_string(u) << " ," << std::to_string(v) << std::endl;
	time_spend = std::chrono::system_clock::now() - time_start;
	time_cost = time_spend.count() * 1000;
	std::cout << "Table Time: " << time_cost << " ms" << std::endl;
}

测试结果:
在这里插入图片描述
移位操作替代乘法操作的优化实际运行结果并不一定好于乘法算法,但在移动设备上效果可能有较大提升。查表法依然最快。
 
 
reference:
YUV 格式与 RGB 格式的相互转换公式及C++ 代码
色彩转换系列之RGB格式与YUV格式互转原理及实现

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值