聊聊颜色的技术实现(四)—— 从色觉到量化后的YUV

物理世界中的量变成编码流程的颜色空间YUV历程:

  1. 从物理世界中的量开始

物理世界中的一个绝对颜色,通过对比参照ICC(<u>International Color Consortium</u>)中RGB属性的色彩配置文件,得到一个RGB值;或 摄像机根据其CCD/CMOS上每个单色像素点的电荷量(物理量),生成相应的Raw文件,raw通过转换矩阵(拜耳矩阵),转换成标准的RGB格式。

  1. RGB经伽马校正形成 R'G'B';

伽马校正:由于显示器(阴极射线管显示器CRT)显示图像的时候,电压增加一倍,亮度并不跟着增加一倍,即输出亮度和电压并不是成线性关系的,而是按幂函数变化,通常gamma值为2.2(2.2通常是是大多数显示设备的大概平均gamma值。基于gamma2.2的颜色空间叫做sRGB颜色空间。每个监视器的gamma曲线都有所不同,但是gamma2.2在大多数监视器上表现都不错),这样当电压为50%时,亮度如果是线性的那么应该也是50%的亮度,由于gamma的存在,亮度为50%^2.2 = 21.8%,亮度要低很多;Gamma校正(Gamma Correction)的思路是在最终的颜色输出上应用监视器Gamma的倒数。

而在物理世界中,如果光的强度增加一倍,那么亮度也会增加一倍;为了使显示器显示的颜色与人眼观察世界看到的物理世界一样,我们在显示器输入之前,做一个操作把显示器的Gamma2.2影响平衡掉(人们并没有去调节CRT本身,也许太难搞了,或者问题发现时已经有太多的CRT),这个叫做Gamma校正:本来相机采集到的亮度是线性的,但存储的时候采用了gamma编码,采用gamma值的倒数的幂函数处理颜色,也就是说,我们的jpeg文件里的颜色值已经进行了gamma校正,都是非线性空间的值,位于gamma空间,记为R'G'B',常见的sRGB标准中颜色值也是gamma空间的。而在图形渲染时,使用的颜色是按照线性空间考虑的,要在线性空间计算颜色,所以颜色计算前需要将颜色重新转回线性空间,后续会讲。
(不过CRT已经不主流了,但是gamma还是被保留了下来,可能是为了兼容各类输入设备吧。The gamma value has no effect on alpha samples, which are always a linear fraction of full opacity.)

Gamma Correction相关总的流程如下:



  1. R'G'B'==>Y'U'V'==>Y'Pb'Pr'==>Y'Cb'Cr'

YUV comes from R'G'B'

前文提到:

红、绿、蓝三个颜色通道每种色各分为255阶亮度,在0时“灯”最弱——是关掉的,而在255时“灯”最亮。当三色数值相同时为无色彩的灰度色,而三色都为255时为最亮的白色,都为0时为黑色。

由于人眼对亮度更敏感,我们就从R'G'B'把亮度提取出来,用Y分量表示

Y' = kr R' + (1-kb-kr)G' + kb B'

kr,kb的取值大小取决于色彩空间标准,一般绿色G'前面系数(1-kb-kr)最大了,因为人眼对绿色的亮度程度更敏感。UV是由B'-Y'与R'-Y'限幅而来的,限幅的目的是防止色域越限,同时确保编码后的复合电视信号电平在VHF/UHF电视发射机的要求范围内一个RGB图像可以在捕捉之后转换为 YCbCr格式用来减少存储和传输负担。

U' = B'-Y' = -kr R' -  (1-kb-kr)G' + (1 - kb)B' //亮度与蓝色分量的差值
V' = R'-Y' = (1-kr)R' - (1-kb-kr)G' - kb B'  //亮度与红色分量的差值

以上变换可以经过矩阵变换实现。

在显示图象之前,再转回为RGB。G可以从YCbCr中解压出来,这说明不需要存储和传输Cg参数。

R' = Y' + (1-kr)/0.5 * V'
G' = Y' - 2kb(1-kb)/(1-kb-kr) * U' - 2kr(1-kr)/(1-kb-kr) * V'
B' = Y' + (1-kb)/0.5 * U'

而后根据使用场景需要对进行YUV进一步处理,获得Y'U'V'、Y'Cb'Cr'、Y'Pb'Pr'等格式,后面统称为Y'U'V';

Y'U'V'、Y'Cb'Cr'、Y'Pb'Pr',他们有什么区别吗?各自什么来历呢?

我觉得还是很有必要分辨它们的,不然当你R'G'B'<=>Y'U'V'转换时选用哪一个矩阵都可能混淆,导致色差。

首先Y'U'V'、Y'Cb'Cr'、Y'Pb'Pr'都是YUV格式,都是用亮度、色差来存储颜色信息。其中Y'Pb'Pr'是用于模拟系统播放,Y'Cb'Cr'适用于数字系统播放。实际上,Y'Pb'Pr' 是Y'U'V'基于偏移缩放产生的格式;Y'Cb'Cr'是基于Y'Pb'Pr'信号进行量化,从模拟信号转化成数字信号的而来;

为了更形象具体的解释上述内容,我们具体到SDTV with BT.601中来看:

Y'Pb'Pr' 
kr = 0.229
kb = 0.114
kg = 1 - kr - kb = 0.587
Umax = 0.5
Vmax = 0.5

所以

Y' = 0.299R' + 0.587G' + 0.114B'

Pb =( Umax /(1- Wb))* (B' - Y' ) = 0.564 * (B' - Y' )

Pr= ( Vmax /(1- Wr))* (R' - Y' ) = 0.713 * (R' - Y' )

Y~ [0,1] U,V~[-0.5,0.5] R', G', B' in [0; 1]



为了解决浮点型数的精度问题,Y'CbCr comes out~,Y'、Cb、Cr分量将会 scaled 为8bits 的整数Y:[ 0, 255 ],Cb'\Cr' :[-128,128]; 为了解决滤波后的过冲现象,Y分量将会 shift 到 [16, 235] ,Cb\Cr分量将会 shift 到[16 , 240],为了在数字系统中使用YUV格式来表示颜色,模拟信号将会被量化成数字信号。
具体步骤:

  1. 根据值域范围进行缩放,成整数格式,整型运算代替浮点运算
    Limited Y~(16,235) U/V ~(16,240) RGB取值范围均为0-255
    Y(scaled before shift) = Y * (235 - 16) = (0.299R' + 0.587G' + 0.114B')* (235 - 16) = 66 R' + 129 G' + 25 B'
    U (scaled before shift) = Pb * (240 - 16) = (-0.168736R' + 0.331264G' + 0.5B')* (240 - 16)
    V 就不赘述了......

    Full range Y~(0,256) U/V ~(0,256)
    Y = (77R + 150G + 29B)>>8;
    U = ((-44
    R - 87G + 131B)>>8) + 128;
    V = ((131R - 110G - 21*B)>>8) + 128 ;

  2. 根据值域范围进行平移,为了使各个分量不会出现负数或者超出值域的值



    [16, 235] Y′ values are conventionally shifted and scaled to the range [16, 235] (referred to as studio swing or "TV levels") rather than using the full range of [0, 255] (referred to as full swing or "PC levels"). This practice was standardized in SMPTE-125M in order to accommodate signal overshoots ("ringing") due to filtering.

  3. 各个分量进行量化处理

  1. 为了方便运算 将分母转化成256(相当于<<8),所以上式分子与分母应该都乘以256/255
  1. 对应逆转公式如下:

ITU-R BT.601 recommendation帮我们整理了一下上述公式

于是 我们得到了我们会直接拿来用的矩阵公式

// BT.601, which is the standard for SDTV.
const GLfloat sColorConversion601[] = {
        1.164,  1.164, 1.164,
        0.0, -0.392, 2.017,
        1.596, -0.813,   0.0,
};

所以我们可以知道 这个公式对应的是 601 值域范围为[16, 235]、[16 , 240]的 digital的YUV ,Y'Cb'Cr',转换成R'G'B'的矩阵公式。
Y'=0.5时,第一个图是Y'U'V',第二个图是Y'Cb'Cr'(坐标值归一化后),


之所以展示这些图是为了说明,U分量决定色度,Y分量决定亮度。

色差空间标准的加权值krkb不同。

BT 709

BT 2020

我们常用的JPEG使用Y'Cr'Cb'的修订后的BT 609 标准,CB and CR have the full 8-bit range of [0...255], 就是 Full range,也就是·说上述第二步中的Y'分量不需要加16,其他步骤保持不变,从而

在YUV 家族中,Y'CbCr 是在计算机系统中应用最多的成员,我们绝大多数的场景中遇到的YUV格式,就是YCbCr。YCbCr其中Y是指亮度分量,Cb指蓝色色度分量,而Cr指红色色度分量,可以从下图中主观感知一下Cb、Cr的效果:

!](https://upload-images.jianshu.io/upload_images/6638024-27f4491bd2ab2de7?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

That the white snow is represented as a middle value in both Cr and Cb;

that the brown barn is represented by weak Cb and strong Cr;

that the green grass is represented by weak Cb and weak Cr;

and that the blue sky is represented by strong Cb and weak Cr.

  1. 编码:输入是Y'U'V'格式 输出是文件;
  2. 解码:输入是文件 软解码输出是Y'U'V'格式;硬解码输出是R'G'B'(Surface 纹理)
  3. OpenGL渲染图形时gamma correction

https://learnopengl.com/Advanced-Lighting/Gamma-Correction

由于OpenGL渲染时会在线性RGB空间进行计算处理,所以颜色被发送到帧缓冲中已经完成了gamma空间的R'G'B'到线性空间RGB的转换。

float gamma = 2.2;
vec3 diffuseColor = pow(texture(diffuse, texCoords).rgb, vec3(gamma));

对于移动端,这个转换是默认进行的(后来实验证明是错的),默认执行glEnable(GL_FRAMEBUFFER_SRGB),以下说明均是以该开关enable的前提下。

在OpenGL中,shader以及texture的filtering 过程中,处理的颜色都要求是线性的,而屏幕显示是非线性的sRGB。所以存在一个转换过程,即sRGB -> RGB, RGB -> sRGB。最终会在读取时进行sRGB到线性的转换,并在写入时进行线性到sRGB的转换。
在OpenGL上,图片可以用纹理表示,也可以用framebuffer表示,它们都是一块buffer。sRGB图片在OpenGL下会有以下几种处理情况。
通过使用glTexImage2D、glTexSubImage2D等等,通过传入的图片数据,生成使用sRGB颜色空间的图片。因此,OpenGL 会认为传入的数据所用的颜色空间是sRGB colorspace,如果传入的图片不在此颜色空间,可能会发生图片变色的情况。 shader通过sampler读取数据(也就是texel),OpenGL会认为传入图片的texel处于sRGB颜色空间,(依然前提是glEnable(GL_FRAMEBUFFER_SRGB))但是shader处理的时候需要的是线性的颜色空间,这个时候,OpenGL会自动执行一次颜色空间的转换,sRGB -> RGB。
通过Framebuffer处理图片,需要明确告诉OpenGL是否启用伽玛校正。通过调用glEnable(GL_FRAMEBUFFER_SRGB) 启用framebuffer的伽玛校正,OpenGL会把输出的颜色从线性的颜色空间转换成非线性的sRGB,否则,图片输出到屏幕的时候,是没有伽玛校正,图片会较暗。这一类的framebuffer叫作:sRGB Frame Buffer。它是由硬件支持的,在不支持GL_FRAMEBUFFER_SRGB的情况下(OpenGL ES 3.0 之后才支持),数据的转换可以通过shader来实现,这里暂时不深究。但是sRGB Frame Buffer的转换速度要比它快,毕竟是硬件支持的。另外需要注意的是alpha的数据是不做转换的,而且只能支持每位8位的格式数据。
可以通过glGetFramebufferAttachmentParameter来查询该framebuffer是否开启GL_FRAMEBUFFER_SRGB。

glGetFramebufferAttachmentParameter(GL_FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING)

写:当fragment shader 向sRGB Frame Buffer写入颜色的时候,颜色会被编码成sRGB格式的数据,然后存储到framebuffer中。 读:当从sRGB Frame Buffer读取数据的时候,颜色以sRGB的格式读取,然后转换成线性的颜色数据返回
其中sRGB就是R'G'B'中的一种,其对应的gamma值为 2.2。
总结:
我们的图像资源解码Y'CrCb转换成R'G'B',framebuffer里要存放的是sRGB的值,我们从fragment shader采样的时候,将纹理 sRGB空间的值转换成线性空间的颜色值,然后线性空间的颜色值在传入这个sRGB framebuffer的时候,framebuffer相关内部操作会将颜色再转成sRGB存入framebuffer中(sRGB framebuffer 会认为传入的值都是线性空间的值);当OpenGL需要信息一些渲染处理,尤其是物理光模型,当硬件支持 GL 3.x+时,从framebuffer中取出的值会自动转换成 线性空间值,然后对这个线性空间值做相应的filtering操作,或者把它跟其他framebuffer取出来的线性空间值进行blending,最后framebuffer相关内部操作将blending后的线性空间值转换成sRGB空间的值存入framebuffer;最后 用于显示时,从framebuffer取出sRGB值来显示。

大致先这些吧==

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,968评论 6 482
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,601评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 153,220评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,416评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,425评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,144评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,432评论 3 401
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,088评论 0 261
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,586评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,028评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,137评论 1 334
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,783评论 4 324
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,343评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,333评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,559评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,595评论 2 355
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,901评论 2 345

推荐阅读更多精彩内容