Form 徐新华
色彩是可见光所展现的,不同颜色的物体吸收和反射的波长的光不同,所产生不同的颜色。
色彩做为现实世界固有的属性是存在和不变的。
我们利用数学知识对色彩进行编码,便是色彩空间的来源,不同的编码其所指向的颜色都是唯一存在的,因此色彩空间都是可以互相转换的。其只是不同的数学变现形式而已。
例如:我用0-255表现红色,也可以用0.0-1.0表现红色,他们之间是可以互转的。
不同精度表现的颜色数量也不一样,如果你只用1个bit位表现颜色,那么这个世界就只有两种颜色0和1即非黑即白,如果你用8个bit位表示颜色,就有256种颜色,呵呵就是最开始所说的256色。
现实世界是连续的,我们的数学表征是离散的。我们用有限的数字表示无限的颜色,这样数字精度越大所表现的颜色就越多,就越接近真实的世界,如果色彩还原系统如电视能支持这么大的精度,则越接近真实。
那么是否是精度越高越好了,这时候我们就需要和现实我们能做到的进行妥协。
1)显示设备能够显示的精度
2)我们传输的带宽和cpu的处理能力,处理32bit和处理8bit对带宽和处理器要求有天大的区别。
如何解决这个问题?
科学家们对人眼就行测量和观察得出人眼的一些特性。
1)人眼有视锥细胞视杆细胞来区分颜色,
2)人眼有视觉暂留作用:这个作用我们就可以用dither抖动来模拟更多的颜色,通过快速交换不同的颜色来欺骗眼睛。
3)人眼对图像的认知是非均匀和非线性的:实际上压缩算法充分利用这一点。很多颜色人眼是区分不出差别的。呵呵举个简单的例子,1.74m和1.75m高的两个人站在一起感觉两个人差不多看不出差别,1.73和1.74也看不出差别,但1.73和1.75放到一起就有差别了。类似对于颜色采样的精度在一定区间,人眼就认为是连续的,准确的。
4)人眼对380nm-780nm不同波长的颜色光有不同的敏感度,有色光初中我们就学到用三棱镜可以分离为红橙黄绿青蓝紫,在光谱上人类对绿光感觉最灵敏,向两边递减,根据这个特性科学家发明了yuv色彩空间进行编码,数字量化的表示是ycbcr 亮度+蓝色偏差+红色偏差来表示颜色。
如何看CIE1931色域图:
从自然界看色彩是由不同波长的光所发出的,但我们可以通过混合不同比例的光来产生其他颜色,譬如rgb是最常用的基准色,当然也可以采用其他颜色组合。
我们的色域空间CIE1931也是基于RGB三原色定义的,被成为XYZ色彩空间,X代表R,Y代表G,Z代表B,通过不同比例混合这几种色光来产生所有颜色。
我们定义X+Y+Z=1即三种颜色的比例之和为一。
那么基于XYZ构建的几何平面上表示了所有的颜色。由于3维图形不好看。我们从X+Y+Z=1数学等式可以看到只要知道xy就能得出z,故我们直接在二维平面上构建我们的色域图如上图所示。
从图上观察可以看出x比例加大即R色加大,y比例加大,代表绿色加大,xy较小即z变大blue变大。
色域图边界我们看到的数字是纯色可见光的波长。下面的直连线外为不可见光,红外线和紫外线。
白色点即xyz比例相当的点即0.33左右,就在D65位置。即x和y坐标为0.33左右的位置。
这是标准的线性空间定义的颜色,随着采样精度的提高,所能表达的颜色数量就越多。
我们提到的现实科学中使用的各种的色彩空间是由于我们无法用更高的精度来表达颜色,能表达的颜色的个数非常有限,所以很多标准都是为了不同的领域定义了一个颜色子集,这样就形成了不同的色彩空间标准。
譬如:
计算机使用的RGB色彩空间,他使用8bit位描述每种基色,他所能描述的色彩空间相对较小。
基于电视的YUV色彩空间601,709 2020,2020空间用10bit到16bit来描述颜色所覆盖的XYZ色彩空间的色彩就更多。电视黑白电视只要亮度为了兼容,同时对色度敏感度小,可以减少色度的数量便于传输。
基于印刷行业的CMYK色彩空间由于颜料调色,这种方式更方便显示和印刷才能一致。
sRGB色彩空间,其代表的数值已经做过gamma校正了等等不同目的的色彩空间。
现在的显示设备大部分都是非线性的,即给出的电压激发荧光粉产生亮度不是线性比例y=kx的,而是指数形式的关系 y=x^ϒ ϒ(gamma)通常取2.2这也是sRGB空间的gamma取值,实际上每个显示设备gamma值可以不同(跟显示器实现方式有关)。
y=x^2.2(显示器的gamma曲线)横坐标是电信号,纵坐标是亮度信号。
例如,我们想显示0.2的亮度,如果是线性的,我需要给0.2的电压(我们给的电平pcm),这样我们预期的给定值和实际值相符,就能正常显示对的图像了,可惜你给的0.2v的电压在物理特性上只能才生0.05的亮度,这样对于这个像素点就和我们的预期有差距了,这时候怎么办了,聪明的科学家就想到了我这个像素要0.2的亮度,给0.2v不行,我就给0.4v
这样就修改我们的图像像素数据值对于0.2的亮度我们给0.4v的数据,这样就是gamma校正。
我们要理解gamma是为了补偿显示器还原色彩不准确所引入的,显示器用来激发显示亮度的电压和亮度不是线性正比例关系,故显示器必须做gamma修正,使得显示器能正确的显示信号源给定的颜色值。
实际上显示器做了gamma修正后基本上就能正确反应我们信号源给定的颜色值了,但为什么有些视频播放器还要做gamma校正了。这是因为为了讨好人的眼睛,根据人的眼睛对亮度信号的不同敏感度来修改每个像素值,对于yuv色彩空间来说,gamma只修改的是y分量即亮度值,而不修改色度的uv分量。
人眼睛对于亮信号更敏感,调整gamma,如果小于1图像曲线是x的0.n次方,从图像看会导致压缩了暗部,拉大了亮部,如用255个数字表示灰阶,如果正常是16个数字表示暗部,那现在可能就只有8个了,导致暗部的区分度过低,暗部细节丢失,反而表达亮部的数字变多了,亮部信号显示更多。
通常我们不应该在软件上调整gamma,这个是显示器的固有属性,而且每个人对于颜色的感知并不相同,有的人认为好,有的人认为失真了,所以正常情况下,应该由用户自己调整。
DXGI_FORMAT_R8G8B8A8_TYPELESS = 27,
DXGI_FORMAT_R8G8B8A8_UNORM = 28,
DXGI_FORMAT_R8G8B8A8_UNORM_SRGB = 29,
DXGI_FORMAT_R8G8B8A8_UINT = 30,
DXGI_FORMAT_R8G8B8A8_SNORM = 31,
DXGI_FORMAT_R8G8B8A8_SINT = 32,
DXGI_FORMAT_R10G10B10A2_UNORM = 24,
DXGI_FORMAT_R16G16B16A16_UNORM = 11,
这些纹理都有些不同的后缀其含义说明如下:
SNORM: S代表signed 有符号 NORM:代表归一化
UINT: U代表unsigned 无符号 INT代表正常的正说没有归一化,如果是8bit就代表0-255
SRGB:代表非线性的色彩空间,实际上就是标准色彩空间,这个空间是正常rgb描述的空间做了gamma=2.2的变换。我们基本不用,除非我们加载的纹理是sRGB色彩空间的图片。
有符号的归一化就是【-1.0, 1.0】无符号归一化就是【0,1.0f】
SINT ==>[-128,127]
UINT ==>[0,255]
UNORM ==>[0.0, 1.0]
SNORM ==>[-1.0, 1.0]
FLOAT :如果是16位的浮点其表示的值含义是1bit的符号位,5bit的指数位,10bit的小数位。
32位的浮点数,1位符号位,8位指数位,23位小数位
DXGI_FORMAT_R8G8B8A8_UNORM:这种格式的纹理资源在资源中被解释成无符号的整数即[0,255],但在shader中被解释为[0.0,1.0],所以我们写着色器shader程序的时候注意他的取值范围。
对于带有归一化后缀的纹理他们在着色器和资源中被解读成不同的数值。
DXGI_FORMAT_P016:每通道16位平面YUV 4:2:0视频资源格式。该视频资源格式的有效亮度数据视图格式为DXGI_FORMAT_R16_UNORM和DXGI_FORMAT_R16_UINT。该视频资源格式的有效色度数据视图格式(宽度和高度均为亮度视图的1/2)是DXGI_FORMAT_R16G16_UNORM和DXGI_FORMAT_R16G16_UINT
DXGI_FORMAT_P010:每通道10比特平面YUV 4:2:0视频资源格式。该视频资源格式的有效亮度数据视图格式为DXGI_FORMAT_R16_UNORM和DXGI_FORMAT_R16_UINT。运行时不强制最低6位是否为0(假设该视频资源格式是使用16位的10位格式)。如果需要,应用程序着色器代码必须手动强制执行此操作。从运行时的角度来看,DXGI_FORMAT_P010与DXGI_FORMAT_P016没什么区别。
P10/P12/P16没有本质区别都是用16bit表示颜色值,只是精度不同10bit低位的6bit没有意义,即显示设备没有那么高的精度,前面10bit相同后面的不同,对于显示器也认为是同一种颜色。
BT:broadcast Television
这些标准都是国际电信无线电通信部门为电视制定的图像色彩编码标准。用以确保电视从节目制作到播放能够正常的显示正确的颜色和图像。
每种色彩空间标准所覆盖的颜色数量不同其都是CIE 1931基于XYZ色彩空间所表示的颜色的子集。
这几种色彩空间都是基于YUV的其和RGB色彩空间存在着转换关系,而且最接近人的真实感知。人眼对亮度更敏感对色度相对不敏感,哈哈更有人是色盲,区分不了颜色。
YUV420 容量只有444的一半,由于人眼敏感度的原因并不能看出颜色有什么差别,这样就为传输和存储省了很大的空间。
对于8bit位的YUV,Y区间为[16,235] , UV[16,240] 为什么不是0–255了,是因为crt电视的时候是无法到达0度黑的。
对于10bit的yuv,y[64, 940], uv[64, 960],没多一个数达标所能表达颜色的阶数就多一个,颜色分辨率就更大,如果只有2bit我们只能表达4中颜色,8bit就可以256种了。颜色细分不够就会出现条带banding。
Dither的出现原因,由于想表达更多的颜色,譬如想表达10bit灰阶的颜色,但我们只有8bit的数据,这时候就可以通过抖动来模拟更多的颜色了,带来的后果是降低了分辨率,譬如用2个像素来表达原来的一个像素,2个像素相当于用16bit来描述颜色了,色彩所覆盖的就更多,但分辨率减少了一半。实际电视是显示不了这么多颜色的,但通过这种方式欺骗眼睛,这种情况在显示设别能力不足,但节目源有更多的bit深度的情况下有用。实际上没有卵用,这种情况并不多。
区别:
601/709/2020他们用来转变到rgb的比例系数并不相同,这也是根据显示设备的能力由于能够更多的表达颜色,这样调整系数使得颜色分布更合理。
601标准用8bit位深。
709通常为8bit也可以更多位深。720P 1080P
2020 10bit/12bit/16bit,并且分辨率有要求必须是4K或8K分辨率。并且还只能是逐行。
HDR的时候需要创建P010的纹理,即显示设备要支持非RGB输出
我们在使用YUV色彩空间的时候最终我们要转换成RGB纹理输出,我们的电视在不支持10bit的情况下通常只能是8bit的rgb,我们经常看到有rgb输出pclevel[0,255], tvlevel[16-235],现在的电视,投影为了兼容pc基本上都是pclevel了,而且表达的颜色也更多。只要老的一些电视才有tv level这个。
所以我们的视频输出rgb都是pc level给投影和电视。
10bit的内容要在8bit的显示设备输出,如果要有更好的效果可以考虑dither,特别是针对4k电视,但节目源是1080P的情况。
作者:徐新华
原文:https://blog.csdn.net/xuxinhua/article/details/82502836