色域规定了显示颜色范围(上一节以灰度举例,但是对于彩色显示设备,不同颜色显示范围并不一样)。比如BT.709就是一种色域。每种色域主要影响的是RGB与YUV/YCbCr两种颜色的转换系数。
以BT.709为例,标准规定的转换是:
Y = 0.2126 R + 0.7152 G + 0.0722 B
Cb = (B - Y) / 1.8556 = -0.1146 R - 0.3854 G + 0.5 B + 128
Cr = (R - Y) / 1.5748 = 0.5 R - 0.4542 G - 0.0490 B + 128
而BT.601的转换是
Y = 0.299 R + 0.587 G + 0.114 B
Cb = (B - Y) / 1.772 = -0.1687 R - 0.3313 G + 0.5 B + 128
Cr = (R - Y) / 1.402 = 0.5 R - 0.4197 G - 0.0813 B + 128
int sws_setColorspaceDetails(struct SwsContext *c,
const int inv_table[4],
int srcRange,
const int table[4],
int dstRange,
int brightness,
int contrast,
int saturation
)
Parameters
dstRange flag indicating the while-black range of the output (1=jpeg / 0=mpeg)
srcRange flag indicating the while-black range of the input (1=jpeg / 0=mpeg)
table the yuv2rgb coefficients describing the output yuv space, normally ff_yuv2rgb_coeffs[x]
inv_table the yuv2rgb coefficients describing the input yuv space, normally ff_yuv2rgb_coeffs[x]
brightness 16.16 fixed point brightness correction
contrast 16.16 fixed point contrast correction
saturation 16.16 fixed point saturation correction
什么是颜色空间
颜色空间是定义颜色显示范围。
早期(目前也是)的显示设备由于技术限制,无法显示出所有颜色。以灰阶为例,电视显示的范围是16-235,超出这个范围的值只能做削波处理,导致某些颜色显示不出来。因此在编码时,我们可以把源信号从[0-255],转换到[16-235]内,相当于把溢出的部分均匀分摊到中间位置。虽然这样中间部分的颜色显示不准,但起码显示颜色的范围是完整的。但是具体到不同设备,比如电视,虽然BT.709色彩空间规范定义显示范围是16-235,但实际不同显示器仍然有色差。有的显示器颜色范围更大,同一个文件往往能看到更多细节。
色域
色域规定了显示颜色范围(上一节以灰度举例,但是对于彩色显示设备,不同颜色显示范围并不一样)。比如BT.709就是一种色域。每种色域主要影响的是RGB与YUV/YCbCr两种颜色的转换系数。 以BT.709为例,标准规定的转换是: Y = 0.2126 R + 0.7152 G + 0.0722 B Cb = (B - Y) / 1.8556 = -0.1146 R - 0.3854 G + 0.5 B + 128 Cr = (R - Y) / 1.5748 = 0.5 R - 0.4542 G - 0.0490 B + 128 而BT.601的转换是 Y = 0.299 R + 0.587 G + 0.114 B Cb = (B - Y) / 1.772 = -0.1687 R - 0.3313 G + 0.5 B + 128 Cr = (R - Y) / 1.402 = 0.5 R - 0.4197 G - 0.0813 B + 128
反过来也有应于公式,实际在计算时是通过矩阵已经逆矩阵完成运算,这里不再赘述。 由于转换都是浮点计算,会出现取整、溢出等。所以转换并不是无损的,每个颜色的位数也对结果有影响。默认是8bit,有些格式是10bit,转化的精度自然就高一些。
如何查看颜色空间
事实上,在生成文件后,都会嵌入对应的颜色空间信息。可以通过ffprobe工具打印。(推荐mediainfo,更专业)
图片也有
括号中的是颜色矩阵和颜色转换空间。比如
rgb24(pc, gbr/unknown/unknown)
这个描述中的gbr
表示颜色转换矩阵使用的是gbr(Green-Blue-Red)颜色空间。而unknown
表示没有指定具体的色彩标准或转换矩阵参数。rgb24(pc, gbr/bt709/bt709)
,表示图片的颜色空间是RGB,像素格式是RGB 24位(每个通道8位),并且使用了gbr/bt709/bt709的颜色转换矩阵。广色域图片
系统播放器在显示这些文件时,通常都会根据文件信息中的颜色空间,结合硬件(显示器)的显示范围(色域)进行自动调整。 代码处理相对繁琐一些,通常需要借助系统API实现。
Instagram 如何在新 iPhone 的广色域屏幕中显示广色域图像
使用mediainfo查看图片intro_1
颜色空间转换(CSC)
ffmpeg可以通过
libswscale
从一个颜色格式转换到另一个颜色格式。YUV420P对应的是(tv, bt709),YUVJ420P对应的是(pc, bt601). AV_PIX_FMT_YUV420P与AV_PIX_FMT_YUVJ420P
下面是一个示例代码:
如果是yuv和rgb的转换,还需要设置色域等信息
函数说明
sRGB颜色空间 vs 线性颜色空间 vs Gamma颜色空间
老式的CRT显示器,颜色输入和输出并不是线性显示的,而是一条曲线 用公式来表示就是
Y = X ^ G
,X = 输入,Y = 输出,G = Gamma系数。默认这个是2.2。现代的液晶显示器是可以达到线性输出的,但有些模式为了兼容老式CRT,也内置了Gamma校准。这当然不是我们期望的效果,我们期望输入0.5,实际显示的也是0.5。但是在CRT显示器中,大部分输出都比输入暗,因此画面整体会偏暗。
为了补偿CRT显示问题,人为的对图片进行反向提亮操作,即sRGB空间。提亮的系数一般为0.45,这样刚好抵消显示器Gamma,达到理想中的线性输出。但是如果输出的是线性颜色空间,会出现过曝的现象,这时必须对每个颜色pow(x, 2.2)还原到线性空间。