物理世界中的量變成編碼流程的顏色空間YUV歷程:
-
從物理世界中的量開始
物理世界中的一個絕對顏色,通過對比參照ICC(<u>International Color Consortium</u>)中RGB屬性的色彩配置文件酗钞,得到一個RGB值腹忽;或 攝像機根據(jù)其CCD/CMOS上每個單色像素點的電荷量(物理量)来累,生成相應的Raw文件,raw通過轉(zhuǎn)換矩陣(拜耳矩陣)窘奏,轉(zhuǎn)換成標準的RGB格式嘹锁。
-
RGB經(jīng)伽馬校正形成 R'G'B';
伽馬校正:由于顯示器(陰極射線管顯示器CRT)顯示圖像的時候着裹,電壓增加一倍领猾,亮度并不跟著增加一倍,即輸出亮度和電壓并不是成線性關(guān)系的求冷,而是按冪函數(shù)變化瘤运,通常gamma值為2.2(2.2通常是是大多數(shù)顯示設(shè)備的大概平均gamma值〗程猓基于gamma2.2的顏色空間叫做sRGB顏色空間拯坟。每個監(jiān)視器的gamma曲線都有所不同,但是gamma2.2在大多數(shù)監(jiān)視器上表現(xiàn)都不錯)韭山,這樣當電壓為50%時郁季,亮度如果是線性的那么應該也是50%的亮度,由于gamma的存在钱磅,亮度為50%^2.2 = 21.8%梦裂,亮度要低很多;Gamma校正(Gamma Correction)的思路是在最終的顏色輸出上應用監(jiān)視器Gamma的倒數(shù)盖淡。
而在物理世界中年柠,如果光的強度增加一倍,那么亮度也會增加一倍褪迟;為了使顯示器顯示的顏色與人眼觀察世界看到的物理世界一樣冗恨,我們在顯示器輸入之前,做一個操作把顯示器的Gamma2.2影響平衡掉(人們并沒有去調(diào)節(jié)CRT本身味赃,也許太難搞了掀抹,或者問題發(fā)現(xiàn)時已經(jīng)有太多的CRT),這個叫做Gamma校正:本來相機采集到的亮度是線性的心俗,但存儲的時候采用了gamma編碼傲武,采用gamma值的倒數(shù)的冪函數(shù)處理顏色,也就是說城榛,我們的jpeg文件里的顏色值已經(jīng)進行了gamma校正揪利,都是非線性空間的值,位于gamma空間吠谢,記為R'G'B'土童,常見的sRGB標準中顏色值也是gamma空間的。而在圖形渲染時工坊,使用的顏色是按照線性空間考慮的献汗,要在線性空間計算顏色,所以顏色計算前需要將顏色重新轉(zhuǎn)回線性空間王污,后續(xù)會講罢吃。
(不過CRT已經(jīng)不主流了,但是gamma還是被保留了下來昭齐,可能是為了兼容各類輸入設(shè)備吧尿招。The gamma value has no effect on alpha samples, which are always a linear fraction of full opacity.)
Gamma Correction相關(guān)總的流程如下:
-
R'G'B'==>Y'U'V'==>Y'Pb'Pr'==>Y'Cb'Cr'
YUV comes from R'G'B'
前文提到:
紅、綠阱驾、藍三個顏色通道每種色各分為255階亮度就谜,在0時“燈”最弱——是關(guān)掉的,而在255時“燈”最亮里覆。當三色數(shù)值相同時為無色彩的灰度色丧荐,而三色都為255時為最亮的白色,都為0時為黑色喧枷。
由于人眼對亮度更敏感虹统,我們就從R'G'B'把亮度提取出來,用Y分量表示
Y' = kr R' + (1-kb-kr)G' + kb B'
kr隧甚,kb的取值大小取決于色彩空間標準车荔,一般綠色G'前面系數(shù)(1-kb-kr)最大了,因為人眼對綠色的亮度程度更敏感戚扳。UV是由B'-Y'與R'-Y'限幅而來的忧便,限幅的目的是防止色域越限,同時確保編碼后的復合電視信號電平在VHF/UHF電視發(fā)射機的要求范圍內(nèi)一個RGB圖像可以在捕捉之后轉(zhuǎn)換為 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' //亮度與紅色分量的差值
以上變換可以經(jīng)過矩陣變換實現(xiàn)珠增。
在顯示圖象之前,再轉(zhuǎn)回為RGB宜雀。G可以從YCbCr中解壓出來切平,這說明不需要存儲和傳輸Cg參數(shù)。
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'
而后根據(jù)使用場景需要對進行YUV進一步處理,獲得Y'U'V'、Y'Cb'Cr'咧栗、Y'Pb'Pr'等格式蜒程,后面統(tǒng)稱為Y'U'V';
Y'U'V'际度、Y'Cb'Cr'、Y'Pb'Pr',他們有什么區(qū)別嗎届氢?各自什么來歷呢?
我覺得還是很有必要分辨它們的覆旭,不然當你R'G'B'<=>Y'U'V'轉(zhuǎn)換時選用哪一個矩陣都可能混淆退子,導致色差岖妄。
首先Y'U'V'、Y'Cb'Cr'寂祥、Y'Pb'Pr'都是YUV格式荐虐,都是用亮度、色差來存儲顏色信息丸凭。其中Y'Pb'Pr'是用于模擬系統(tǒng)播放福扬,Y'Cb'Cr'適用于數(shù)字系統(tǒng)播放。實際上惜犀,Y'Pb'Pr' 是Y'U'V'基于偏移縮放產(chǎn)生的格式铛碑;Y'Cb'Cr'是基于Y'Pb'Pr'信號進行量化,從模擬信號轉(zhuǎn)化成數(shù)字信號的而來虽界;
為了更形象具體的解釋上述內(nèi)容汽烦,我們具體到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]
為了解決浮點型數(shù)的精度問題,Y'CbCr comes out~,Y'浓恳、Cb刹缝、Cr分量將會 scaled 為8bits 的整數(shù)Y:[ 0, 255 ],Cb'\Cr' :[-128,128]; 為了解決濾波后的過沖現(xiàn)象,Y分量將會 shift 到 [16, 235] 颈将,Cb\Cr分量將會 shift 到[16 , 240]梢夯,為了在數(shù)字系統(tǒng)中使用YUV格式來表示顏色,模擬信號將會被量化成數(shù)字信號晴圾。
具體步驟:
-
根據(jù)值域范圍進行縮放,成整數(shù)格式颂砸,整型運算代替浮點運算
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 = ((-44R - 87G + 131B)>>8) + 128;
V = ((131R - 110G - 21*B)>>8) + 128 ; -
根據(jù)值域范圍進行平移,為了使各個分量不會出現(xiàn)負數(shù)或者超出值域的值
[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.
各個分量進行量化處理
- 為了方便運算 將分母轉(zhuǎn)化成256(相當于<<8),所以上式分子與分母應該都乘以256/255
- 對應逆轉(zhuǎn)公式如下:
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',轉(zhuǎn)換成R'G'B'的矩陣公式都毒。
Y'=0.5時,第一個圖是Y'U'V'色罚,第二個圖是Y'Cb'Cr'(坐標值歸一化后),
之所以展示這些圖是為了說明账劲,U分量決定色度戳护,Y分量決定亮度。
色差空間標準的加權(quán)值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 是在計算機系統(tǒng)中應用最多的成員禀晓,我們絕大多數(shù)的場景中遇到的YUV格式精续,就是YCbCr坝锰。YCbCr其中Y是指亮度分量,Cb指藍色色度分量驻右,而Cr指紅色色度分量,可以從下圖中主觀感知一下Cb什黑、Cr的效果:
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.
-
編碼:輸入是Y'U'V'格式 輸出是文件崎淳;
-
解碼:輸入是文件 軟解碼輸出是Y'U'V'格式堪夭;硬解碼輸出是R'G'B'(Surface 紋理)
-
OpenGL渲染圖形時gamma correction
https://learnopengl.com/Advanced-Lighting/Gamma-Correction
由于OpenGL渲染時會在線性RGB空間進行計算處理,所以顏色被發(fā)送到幀緩沖中已經(jīng)完成了gamma空間的R'G'B'到線性空間RGB的轉(zhuǎn)換拣凹。
float gamma = 2.2;
vec3 diffuseColor = pow(texture(diffuse, texCoords).rgb, vec3(gamma));
對于移動端森爽,這個轉(zhuǎn)換是默認進行的(后來實驗證明是錯的),默認執(zhí)行g(shù)lEnable(GL_FRAMEBUFFER_SRGB)嚣镜,以下說明均是以該開關(guān)enable的前提下爬迟。
在OpenGL中,shader以及texture的filtering 過程中菊匿,處理的顏色都要求是線性的付呕,而屏幕顯示是非線性的sRGB。所以存在一個轉(zhuǎn)換過程跌捆,即sRGB -> RGB, RGB -> sRGB徽职。最終會在讀取時進行sRGB到線性的轉(zhuǎn)換,并在寫入時進行線性到sRGB的轉(zhuǎn)換佩厚。
在OpenGL上姆钉,圖片可以用紋理表示,也可以用framebuffer表示抄瓦,它們都是一塊buffer潮瓶。sRGB圖片在OpenGL下會有以下幾種處理情況。
通過使用glTexImage2D钙姊、glTexSubImage2D等等毯辅,通過傳入的圖片數(shù)據(jù),生成使用sRGB顏色空間的圖片煞额。因此思恐,OpenGL 會認為傳入的數(shù)據(jù)所用的顏色空間是sRGB colorspace,如果傳入的圖片不在此顏色空間立镶,可能會發(fā)生圖片變色的情況壁袄。 shader通過sampler讀取數(shù)據(jù)(也就是texel),OpenGL會認為傳入圖片的texel處于sRGB顏色空間,(依然前提是glEnable(GL_FRAMEBUFFER_SRGB))但是shader處理的時候需要的是線性的顏色空間媚媒,這個時候嗜逻,OpenGL會自動執(zhí)行一次顏色空間的轉(zhuǎn)換,sRGB -> RGB缭召。
通過Framebuffer處理圖片栈顷,需要明確告訴OpenGL是否啟用伽瑪校正逆日。通過調(diào)用glEnable(GL_FRAMEBUFFER_SRGB) 啟用framebuffer的伽瑪校正,OpenGL會把輸出的顏色從線性的顏色空間轉(zhuǎn)換成非線性的sRGB萄凤,否則室抽,圖片輸出到屏幕的時候,是沒有伽瑪校正靡努,圖片會較暗坪圾。這一類的framebuffer叫作:sRGB Frame Buffer。它是由硬件支持的惑朦,在不支持GL_FRAMEBUFFER_SRGB的情況下(OpenGL ES 3.0 之后才支持)兽泄,數(shù)據(jù)的轉(zhuǎn)換可以通過shader來實現(xiàn),這里暫時不深究漾月。但是sRGB Frame Buffer的轉(zhuǎn)換速度要比它快病梢,畢竟是硬件支持的。另外需要注意的是alpha的數(shù)據(jù)是不做轉(zhuǎn)換的梁肿,而且只能支持每位8位的格式數(shù)據(jù)蜓陌。
可以通過glGetFramebufferAttachmentParameter來查詢該framebuffer是否開啟GL_FRAMEBUFFER_SRGB。glGetFramebufferAttachmentParameter(GL_FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING)
寫:當fragment shader 向sRGB Frame Buffer寫入顏色的時候吩蔑,顏色會被編碼成sRGB格式的數(shù)據(jù)钮热,然后存儲到framebuffer中。 讀:當從sRGB Frame Buffer讀取數(shù)據(jù)的時候哥纫,顏色以sRGB的格式讀取霉旗,然后轉(zhuǎn)換成線性的顏色數(shù)據(jù)返回
其中sRGB就是R'G'B'中的一種,其對應的gamma值為 2.2蛀骇。
總結(jié):
我們的圖像資源解碼Y'CrCb轉(zhuǎn)換成R'G'B'厌秒,framebuffer里要存放的是sRGB的值,我們從fragment shader采樣的時候擅憔,將紋理 sRGB空間的值轉(zhuǎn)換成線性空間的顏色值鸵闪,然后線性空間的顏色值在傳入這個sRGB framebuffer的時候,framebuffer相關(guān)內(nèi)部操作會將顏色再轉(zhuǎn)成sRGB存入framebuffer中(sRGB framebuffer 會認為傳入的值都是線性空間的值)暑诸;當OpenGL需要信息一些渲染處理蚌讼,尤其是物理光模型,當硬件支持 GL 3.x+時个榕,從framebuffer中取出的值會自動轉(zhuǎn)換成 線性空間值篡石,然后對這個線性空間值做相應的filtering操作,或者把它跟其他framebuffer取出來的線性空間值進行blending西采,最后framebuffer相關(guān)內(nèi)部操作將blending后的線性空間值轉(zhuǎn)換成sRGB空間的值存入framebuffer凰萨;最后 用于顯示時,從framebuffer取出sRGB值來顯示。
大致先這些吧==