B站自研色彩空間轉換引擎
1.背景
色彩空間(Color Space)是一種數學模型,用于描述和表示顏色的方式。不同的色彩空間有不同的用途和特點,可以用于不同的應用,如圖像處理、計算機圖形、印刷、攝影等領域。它一般用于描述設備的色彩能力,或者視頻、圖片的色彩范圍。比如現在大部分人用的手機的屏幕,色彩空間大多為sRGB或者DCI-P3;網頁上常見的圖片或者視頻內容,大多屬于sRGB色彩空間;B站客戶端播放的HDR視頻,則處于BT.2020色彩空間。
目前B站UP主投稿視頻是千變萬化的,它們包含各種不同的色彩參數, 比如不同的色深,不同的色度采樣坐標,不同的色彩空間,不同的亮度傳遞函數等等(如下圖),而且每個用戶觀看這些視頻的終端也不一樣,這樣就產生了各種各樣的兼容性問題——很多時候用戶的設備并不能完全正確顯示UP主投稿的“原片”,而且老舊的設備或者瀏覽器也并不支持廣色域或者HDR視頻。為了考慮終端設備的兼容性和B站服務的完整性,我們會對用戶投稿的不同色彩空間的視頻進行統一處理。但是如何將各種不同色彩參數的視頻進行準確地統一處理,同時又能保證視頻的處理速度,這對于我們來說是一個巨大的挑戰。
色彩空間轉換中需要處理的參數
2.引擎架構
為了處理各種不同色彩參數的視頻,我們研發了一套色彩空間的轉換引擎。該引擎會對各種不同色深、色彩空間、亮度傳遞函數的普通視頻統一轉換為標準的SDR視頻。除了SDR視頻, B站也會接收很多HDR視頻投稿,對于HDR投稿,B站會統一轉出一路標準的HDR視頻和一路標準的SDR視頻。
該圖像轉換引擎分為三層,包括濾鏡層,引擎層,和設備層。其中濾鏡層包含各種濾鏡,用于處理不同的色彩參數。引擎層的主要目的是做一些預計算與計算路徑優化,比如有一些矩陣乘法只需要計算一次,那么就可以預先計算出來,而不是在像素處理的時候再去做。有一些GPU需要用到的矩陣參數,則也可以進行預拷貝,防止出現CPU與GPU之間出現頻繁拷貝的情況,計算路徑優化,則是用來去除一些不必要的步驟,來減少計算量。最后,設備層主要包含每種濾鏡在不同平臺的實現,該引擎包含一個指令集優化的X86版本和一個CUDA版本。接下來我介紹一下該濾鏡層的各種色彩轉換能力,包括坐標變換、量化、色度采樣,色調映射等等。
色彩空間處理引擎的基本架構圖
2.1 坐標變換
很多時候,用戶投稿的視頻會有顯示矩陣(Display matrix),它表示視頻被旋轉、被翻轉、或者被轉置。如果我們在服務端檢測到這種視頻,那么就需要對輸入視頻按照顯示矩陣進行坐標變換。無論是軟解的CPU數據幀還是硬解的CUDA幀,該引擎都能將其準確地渲染出來。
2.2 量化與反量化
信號的量化主要用于連續的YUV信號轉為離散的Y’CbCr,而反量化是將輸入視頻不同的色深轉換為連續信號的過程。該引擎支持8bit、10bit、12bit等不同量化精度的Y’CbCr信號與YUV信號的互相轉換,同時也支持pc range、tv range的轉換。
2.3 色度采樣
色度采樣(Chroma subsampling)是一種用于減少圖像或視頻中色度信息采樣率的技術。在數字圖像和視頻編碼中,顏色信息通常由亮度分量(Y)和兩個色度分量(U和V)組成。由于人眼對亮度信息更加敏感,而對色度信息的感知相對較弱,因此可以通過減少色度分量的采樣率來降低數據量,同時盡量保持圖像或視頻的質量。下圖為常見的像素格式YUV420的采樣坐標。
為了色度采樣盡可能平滑,我們采用了雙線性(Bilinear)插值策略進行上采樣和下采樣。例如對YUV420像素格式的視頻進行色度的上采樣, 我們并不會簡單地將一個色度像素共享給4個亮度像素, 而是對其進行插值,否則最后輸出的視頻會有色度過渡的不均勻。同理,對于色度的下采樣,我們也會采用同樣的插值方法。
圖片
常見的色度采樣坐標(4:2:0 chroma format)
2.4 色彩矩陣轉換
視頻處理中的一個關鍵參數是色彩矩陣(Color matrix),有時也稱為色彩空間(Color space)。該參數主要用于確定信號如何轉換為RGB,色彩矩陣轉換是將輸入信號通過色彩矩陣映射到RGB色彩空間的過程。
舉例來說,常見的BT.709標準就屬于一種色彩矩陣,它指導了如何將YUV信號轉換為RGB,其轉換方式如下:
圖片
為什么選擇了上述的矩陣而不是其他的呢?這是因為BT.709協議所指定的色彩空間與sRGB相對應,通過sRGB的色域坐標可以推導出上述的轉換矩陣。不同的色彩空間對應不同的轉換矩陣,具體的矩陣根據輸入的色彩矩陣而定。如果需要將R'G'B'轉換為Y'U'V',則需要對上述矩陣進行逆運算。
而對于特殊信號,例如ICtCp信號,我們會首先將其轉換為L'M'S'信號,然后再進行后續的轉換操作。
2.5 亮度傳遞函數轉換
亮度傳遞函數是一種用于調整圖像或視頻信號亮度級別的函數,分為兩種OETF(Opto-Electronic Transfer Function)和EOTF(Electro-Optical Transfer Function)。由于人眼對不同光線區域的敏感度不同,人眼對暗部細微亮度變化的敏銳程度較高,而對于高亮度的細微變化則較難察覺。為了以一種均勻的方式呈現信號,以符合人眼的感知,并且為了能夠編碼更多的信息,我們需要對信號進行OETF壓縮。相反地,如果我們需要解壓信號,則需要進行相反的處理,即EOTF。常見的EOTF曲線有Gamma2.4曲線和感知量化曲線(Perceptual Quantization,PQ),如下圖。感知量化曲線是一種最接近人眼感知的量化方法,因此它也被廣泛用作HDR視頻的主要亮度傳遞函數。
Gamma2.4和PQ的EOTF曲線
在該引擎中,我們實現了十幾種亮度傳遞函數的轉換,包括Gamma2.2,Gamma2.4,PQ,HLG等等。
2.6 色域映射
當涉及到色彩轉換或圖像處理時,色域映射是一個重要的概念。它指的是將一個色彩空間中的顏色值映射到另一個色彩空間中的過程。
假設有一個圖像,它使用BT.2020(一種廣色域)色彩空間進行編碼,但你希望在一個只支持sRGB色彩空間的設備上顯示這個圖像。由于這兩個色彩空間具有不同的色域范圍,直接將圖像的顏色值轉換為sRGB可能會導致顏色失真。在這種情況下,色域映射就派上用場了。色域映射算法會根據源色彩空間和目標色彩空間的特性,將圖像的顏色值進行適當的轉換,以確保在目標設備上顯示的圖像顏色盡可能接近原始圖像的顏色。
色域映射的方法一般有兩種,一種是基于Clip的方法,一種是基于壓縮的方法。首先介紹一下基于Clip方法的色域映射。想要做色域映射,首先需要將RGB線性信號轉換到XYZ色彩空間,具體轉換矩陣可以由r177協議得到。
比如BT.2020色域的RGB轉XYZ的公式如下:
圖片
要將XYZ信號轉換為BT.709 RGB信號,使用BT.709的XYZ到RGB的轉換矩陣就可以了,如下:
圖片
上述兩步過程就完成了最簡單的色域映射,它將廣色域的BT.2020 RGB轉換為了窄色域的BT.709 RGB。但是該方法有一個缺點,當廣色域轉換到窄色域會對信號進行截斷(Clip),從而丟失一些色彩信息,導致視頻的有些區域會過于飽和。一般來說,更好的色域映射方法是通過色域壓縮的方式進行。
色域壓縮可以選擇一個人眼看起來相對均勻的色彩空間進行壓縮,比如CIE Lab,CIE Luv,在飽和度為某個閾值內的色彩不進行壓縮,過于飽和的色彩才進行壓縮。在色域壓縮的時候,還需要考慮到色相映射(Hue mapping),如果不考慮色相,信號處理的過程可能會有色偏。有些色域壓縮的算法的復雜度會非常高,甚至需要用GPU來加速。
下圖為BT.2407協議中一種色域映射后的效果圖,左圖為基于Clip的色域映射,右圖為基于壓縮方法的色域映射,可以看到優秀的的色域映射算法可以呈現更豐富的細節,它對最后的效果呈現影響非常大。
Clip方法和色域壓縮方法效果對比圖
我們的轉換引擎也實現了一套自研的色域壓縮算法,可以將廣色域的視頻壓縮至sRGB而不會產生色彩溢出,同時也會保留了高飽和區域的細節。
2.7 色調映射(Tone mapping)
HDR圖像包含比傳統SDR圖像更大的亮度范圍,可以捕捉到更多的細節和光照信息。然而,SDR顯示設備(如計算機顯示器或手機屏幕)的亮度范圍有限,無法直接顯示HDR圖像中的所有細節和對比度。這就需要通過色調映射(Tone mapping)技術將HDR圖像轉換為SDR圖像,使其在常規顯示設備上呈現出較好的視覺效果。
下圖展示了不同的Tone mapping算法對圖像的影響,b為原始HDR圖片的色階圖,含有低亮度區域和高光區域,c為歸一化(Normalization)方法的進行Tone mapping,雖然c的高光被壓縮了,但是它的暗部細節也被等比例壓縮,造成暗部細節丟失。d則是簡單地將高光進行截斷(Clipping),雖然暗部細節保留了,但是高光細節丟失了。e表征了一種更高級的方法,既可以保證暗部細節丟失很少,又可以保留一定的高光細節。
不同算法對HDR 高光區域渲染的效果
圖片來源:ITU-R BT.2390-3
基于Clipping和基于壓縮算法的Tone mapping效果
圖片來源:https://www.bilibili.com/video/BV1WA411E7i7
我們對比了幾種常用的Tone mapping算法,并最終選擇了BT.2390協議中的一種曲線作為我們Tone mapping的指導算法。BT.2390算法的主要思想是將輸入的線性信號轉換為PQ空間,以使輸入信號更符合人眼的感知特性。然后設定了一個閾值,保證小于該閾值的信號盡可能接近原始信號,大于該閾值的信號通過Hermite樣條曲線將其壓縮,從而保證了暗部和高光都丟失比較少的細節。
通過BT.2390將線性信號壓縮至不同范圍的曲線 圖片來源:ITU-R BT.2390-3
通過色域映射和色調映射,B站最終實現的HDR轉SDR效果如下,可以看到,對比于VLC的效果,B站的效果做到了比較好的色相還原,并且保證了飽和度和亮度不溢出,暗部的細節也還原得較好。
圖片
圖片
圖片
B站自研HDR轉SDR效果(左)與VLC的效果(右)對比
2.8 HDR動態元數據渲染
動態元數據是相對于靜態元數據的一個定義,首先介紹一下靜態元數據。靜態元數據主要包含了整個播放序列的一些參數,包括最高亮度,最大平均亮度,色域信息等。
HDR視頻中靜態元數據不隨著視頻場景的變化而變化,整個播放序列維持著同一個值,終端可以根據靜態元數據來判斷自身的顯示能力能否顯示視頻內容,一般地,優秀的顯示軟件會根據自身的顯示能力對視頻信號進行Tone mapping處理,來保證不丟失高光細節。
靜態元數據的缺點是顯而易見的,通過靜態元數據進行Tone mapping的方法在整個播放序列中是不變的,以至于它不能適配復雜的場景。有時候它顯示高光細節是優秀的,但卻無法還原暗部細節;有時候它顯示暗部細節不錯,但顯示高光場景會過曝。總而言之,通過靜態元數據進行Tone mapping,它無法識別視頻中每個場景的亮度,以至于無法有效地去針對每一個場景進行Tone mapping。
對比靜態元數據,動態元數據則靈活很多。在擁有動態元數據的視頻中,視頻中的每個場景甚至每幀都會包含一組元數據,來標識視頻的亮度信息,它還會對每幀內容生成幾組調色曲線來適配不同亮度的顯示終端。終端在顯示該場景的時候,會根據自身的顯示能力來渲染調色曲線,從而讓每個場景,無論是暗部還是亮部,都能得到較完美的顯示,從而避免了暗部細節和高光細節的丟失。在服務端的色彩空間轉換模塊中,我們也會根據HDR的動態元數據來生成下變換后的SDR視頻,這樣生成的SDR視頻也會比全局的Tone mapping算法好很多。
包含HDR動態元數據的視頻主要包括杜比視界和國產高動態范圍視頻HDR Vivid,目前我們正在推動HDR Vivid在B站的上線。對于HDR Vivid在服務端的處理,我們除了正常編碼一路HDR Vivid,還會借助引擎中動態元數據渲染的能力,來生成一路SDR視頻,以適配不支持HDR 的手機。
HDR Vivid 視頻在服務端的轉換
3.工程化加速
對于該引擎的工程化加速,我們嘗試了很多辦法,具體大致有如下圖幾種:
工程化加速用到的方法
色彩空間轉換中,有很多參數是需要預先計算的,比如各種矩陣的參數,圖像插值的數值,亮度傳遞函數中的一些常數等等。如果這些參數在像素處理的時候再去計算會極大地浪費計算資源,所以在引擎初始化的時候就將其計算出來是一個不錯的選擇。除此,在CUDA計算中,矩陣參數和插值參數也是需要進行預拷貝處理,這樣可以避免程序在像素處理中進行頻繁拷貝而影響性能。
計算路徑優化的主要目的是為了消除重復計算,以找到一個色彩空間到另外一個色彩空間的最優轉換路徑。舉例來說,如果一個視頻只需要進行range轉換,那么我們無需整個轉換路徑走一遍,從而避免了額外的色彩轉換和電光轉換,如下圖:
pc range信號轉tv range計算路徑優化示意圖
對于CPU優化來說,色彩空間轉換中的冪運算和矩陣乘法對CPU的消耗很大。為了提高程序的性能,在CPU處理中,我們對頻繁使用的矩陣乘法和冪運算進行了指令集優化。這樣的優化顯著提升了程序的性能。
在對冪運算進行指令集優化的過程中,存在一個難點。常見的編譯器并沒有提供針對冪運算的向量化函數,因此我們需要手動編寫一套冪運算函數。為了實現冪運算的向量化優化,首先將pow函數寫成形如pow(x, m) = exp(m * log(x))的形式。然后,通過泰勒級數展開逐步實現了經過AVX指令集加速的log(x)和exp(x)函數。
這種優化策略充分利用了指令集的特性,以提高冪運算和矩陣乘法的計算效率。通過手動編寫冪運算函數并進行向量化優化,我們能夠更好地利用CPU的計算能力,從而加速色彩空間轉換的處理過程。
除了使用指令集加速,我們還采用了查找表進行優化,通過將復雜的計算提前存儲在查找表中,可以顯著減少計算量。
通過使用指令集優化和使用查找表優化策略,CPU端的性能提升達到如下:
CPU加速策略對性能的提升
將CPU端的代碼進行一系列優化后,通過4K視頻在AMD Ryzen 9 5950X上的測試,我們的色彩空間轉換引擎的性能達到了如下指標:
CPU端色彩空間轉換性能實驗數據
對于CUDA端優化來說,加速手段主要有C++模板和Kernel融合技術。由于色彩空間的屬性和種類非常多,需要寫很多if else分支,但是CUDA是天然不支持分支預測功能的,所以我們沒有將很多if else放在核函數內部,而是使用C++模板來編譯kernel函數,這樣可以防止kernel中出現很多分支,從而大大加快了GPU的處理速度。
此外,由于CUDA在執行全局內存(Gobal memory)的I/O操作時非常耗時,因此我們應避免為每個細小的操作編寫單獨的核函數(Kernel)。相反,我們可以將一些操作合并到一個核函數中以減少I/O開銷。例如,我們可以將range轉換和量化處理合并為一個核函數,將色彩矩陣轉換和亮度傳遞函數的轉換也可以合并為一個核函數(如下圖所示)。通過合并核函數,我們在一定程度上犧牲了一些通用性,但卻能大大節省GPU的I/O開銷。這樣做是一個明智的選擇,特別是在涉及I/O操作頻繁的情況下。
色彩空間轉換引擎中kernel融合示意圖
通過使用C++模板和使用Kernel融合策略,CUDA端的性能提升達到如下:
CUDA 加速策略對性能的提升
將CUDA端的代碼進行一系列優化后,通過4K視頻在RTX2060上的測試,我們的色彩空間引擎轉換性能達到了225FPS+(未編碼),對比于CPU端,處理速度得到了很大的提升,詳細數據如下表:
CUDA端色彩空間轉換性能實驗數據(未包含編解碼耗時)
4.總結與展望
目前,我們的色彩空間轉換引擎已經穩定地在線上為成千上萬的用戶提供服務。在用戶的觀看體驗中,視頻的色彩質量至關重要。因此,將視頻的色彩準確呈現在用戶眼前一直是我們追求的目標。我們將繼續致力于優化色彩轉換效果,以提供更好的用戶體驗。
我們實現了一套HDR轉SDR的算法,但是當下并沒有一個好的算法可以準確評估HDR轉SDR的效果,像HDR-VDP(一種客觀的圖像質量評估方法),它只能評估亮度、對比度、細節上的質量,并無法評估兩張圖像在色度上的差異。色差公式CIEDE2000可以比較兩個像素在亮度、色度、飽和度上的差異,但是由于HDR和SDR之間存在巨大的亮度范圍差異,僅僅使用CIEDE2000公式計算的色差值無法準確反映HDR轉SDR后的差異。在實際應用中,評估HDR轉SDR的效果通常需要結合其他指標和方法,如亮部細節、暗部細節、色相差異等。結合這些指標可以更全面地衡量HDR與SDR之間的差異,以便更好地評估轉換后的圖像質量和觀看體驗。
隨著高動態范圍的視頻內容越來越普及,HDR協議也在擴充新的標準,最新的高動態范圍標準主要有杜比視界以及HDR Vivid。其中,HDR Vivid是中國超高清視頻產業聯盟發布的國產高動態范圍的視頻技術標準,目前最新的開源框架FFmpeg已經集成了HDR Vivid元數據的解碼,我們正在將HDR Vivid元數據的渲染集成到我們自己的色彩轉換引擎中,來支持國產的動態范圍標準,相信未來的不久,大家就可以在B站客戶端上欣賞到來自國產高動態范圍標準的視頻了。
本期作者
羅引杰嗶哩嗶哩資深開發工程師
尹壯 嗶哩嗶哩資深開發工程師