一、常见图像色彩储存模式

RGB、RGBA、ARGB、yuv444、yuv422、yuv420

二、RGB

  RGB一般都比较熟悉,分表表示红、绿、蓝,图像的每个像素都可以用一组RGB值来表示, RGBA和GBRA与RGB大致相同,不同之处在于颜色排列不同和多出一个A(alpha值,用来表示RGB的色彩空间,即通俗的讲,透明度),大部分图片色彩空间都是用RGB表示的。

  用16进制表示RGB或GBRA和ARGB即 #000000或#00000000,每两位代表一个颜色的色值。

  例: 如果图像是ARGB,16进制色值为#99887765.
  99代表alpha,为 (916+9)/255 = 0.6
  88代表Red,为 8
16+8 = 136
  77代表Green,为 716+7 = 119
  65代表Blue,为 6
16+5 = 101

三、yuv

  与RGB不同,yuv多用于流媒体图像色彩空间的表示。其优势在于可压缩性高,减少传输带宽,缩小媒体文件体积。
  yuv分为yuv444、yuv422、yuv420,yuv与RGB的色彩储存方式完全不一样。
  y:亮度
  u(Cb):表示蓝色去掉亮度的色度
  v(Cr):表示红色去掉亮度的色度

  由于人眼对亮度的敏感度远大于对色度的敏感度,所以诞生了yuv422,yuv420。以前的黑白电视播放的画面,可以理解为没有uv分量只有y。

  以下展示yuv图像的色彩表现方式。

yuv444:
avatar

yuv422: avatar yuv420: avatar

YUV 4:4:4采样,每一个Y对应一组UV分量,一个YUV占8+8+8 = 24bits 3个字节。
YUV 4:2:2采样,每两个Y共用一组UV分量,一个YUV占8+4+4 = 16bits 2个字节。
YUV 4:2:0采样,每四个Y共用一组UV分量,一个YUV占8+2+2 = 12bits 1.5个字节。

YUV420根据uv分量存储形式的不同,又分为不同的格式,对应YUV转RGB的方式也不同。几种常见格式以及YUV分量的排布如下:
  I420: YYYYYYYY UU VV =>YUV420P
  YV12: YYYYYYYY VV UU =>YUV420P
  NV12: YYYYYYYY UVUV =>YUV420SP
  NV21: YYYYYYYY VUVU =>YUV420SP

注:移动端视频采集编码大多使用yuv420

四、yuv与RGB的转换

  开发中无法避免RGB与yuv的转换,比如使用GPUImage处理合成视频,并添加滤镜,必须把视频的CVPixelBuffer转位RGB,再由GPUImage去处理。。

一、三方库,推荐,libyuv:

https://github.com/yarrcc/libyuv-ios
https://github.com/illuspas/libyuv-android

二、使用shader

https://github.com/Alienchang/YUVConversion

三、自己写

void NV212RGBorBGR(const uint8_t *input, int width, int height, uint8_t *output,bool isRGB) {
	int nv_off = width * height;
	int  i, j, y_index = 0;
	int y, u, v;
	int r, g, b, nv_index = 0;
	for (i = 0; i < height; i++) {
		for (j = 0; j < width; j++,++y_index) {
			nv_index = i / 2 * width + j - j % 2;

			y = input[y_index];
			u = input[nv_off + nv_index];
			v = input[nv_off + nv_index + 1];

			r = y + ((351 * (v - 128)) >> 8);  //r
			g = y - ((179 * (v - 128) + 86 * (u - 128)) >> 8); //g
			b = y + ((443 * (u - 128)) >> 8); //b

			r = ((r>255) ? 255 : (r<0) ? 0 : r);
			g = ((g>255) ? 255 : (g<0) ? 0 : g);
			b = ((b>255) ? 255 : (b<0) ? 0 : b);
			if (isRGB) {
				output[y_index * 3 + 0] = (uint8_t)b;
				output[y_index * 3 + 1] = (uint8_t)g;
				output[y_index * 3 + 2] = (uint8_t)r;
			}
			else {
				output[y_index * 3 + 0] = (uint8_t)r;
				output[y_index * 3 + 1] = (uint8_t)g;
				output[y_index * 3 + 2] = (uint8_t)b;
			}
		}
	}
}

参考:
https://blog.csdn.net/byhook/article/details/84037338 https://blog.csdn.net/qq_37833413/article/details/123940366