GPU 實現 RGB -- YUV 轉換 (OpenGL)


GPU 實現 RGB -- YUV 轉換

前言

RGB --> YUV 轉換的公式是現成的,直接在 CPU 端轉換的話,只需要遍歷每個像素,得到新的 YUV 值,根據其內存分布規律,合理安排分布即可。然而在 CPU 端進行轉換,存在的問題運行效率太低,無法滿足高效轉換的需求。我們將目光投向擁有流水線體系的支持高速浮點數計算的硬件——GPU.

轉換公式如下:

GPU 上面的實現

考慮在 GPU 上執行 RGB --> YUV 轉換。GPU 的流水線操作:

vertices 
        ----> Pipeline ----> Out color
texture

所以將 RGB 圖像作為紋理輸入,流水線輸出我們需要的 YUV 數據。前面一部分很好理解,圖像作為唯一的紋理輸入,沒有別的選項。后面一部分的話,需要在輸出的時候輸出我們需要的 YUV 數據即可,在 fragment shader 中的輸出按常理就是每一個 fragment 的顏色,為實現讀取像素是 YUV 的目標,要調整輸出的數據。

考慮 YUV 格式內存分布,以 NV12 為例,一張圖片占用內存大小為:width x height * 3 / 2 (我們認為圖像的寬為 width 高為 height). 如果是 RGBA 的格式存儲的話,占用的內存空間大小是:width x height x 4 (因為 RGBA 一共4個通道)。如果我們把 OpenGL renderbuffer 大小設置成等於圖像的大小,那輸出的大小就是 RGBA 那一種的大小,和 YUV 格式的是對不上的。考慮 YUV 的分布特點,設計輸出的寬高為 (width / 4, height * 3 / 2). 示意圖如下:

Memory of a frame (yuv format)

  width / 4
|-------------|
|             |
|             | h
| chrominance |
|             |
|-------------|
|             | 
| luminance   | h / 2
|-------------|

因為每一個 out color 含有四個分量 RGBA 所以將寬度設為 width / 4, 那么正好每一行的像素就是原來 width 的數量。在 fragment shader 內部計算的時候,需要考慮當前處理的單個 fragment 是屬於 chrominance OR luminance, 可以用紋理坐標的 t 值的大小來判斷。

Chrominance

所謂的 RGBA 四個分量實際上代表四個不同的像素的 chrominance 值,也就是說需要做一定的 offset, 來獲取到當前像素附近的像素的值,我先假定 offset 為 1.0f / width. 故四個分量如下:

  1. (s, t)
  2. (s + off, t)
  3. (s + off x 2.0f, t)
  4. (s + off x 3.0f, t)

根據四個像素的 RGBA 值計算出四個 Y 通道的數據作為這個 fragment 的輸出顏色。

Luminance

仍然是一個像素四個分量,但是現在代表的是兩對 UV 分量。因為根據一個 RGBA 就可以算出 YUV 值,所以此處只需要做一個偏移。

  1. (s, t)
  2. (s + off x 2, t)

這里 offset 的設置可以乘 1 或 2 或 3,我覺得都可以,我只是取中道選擇了 2. 將上面兩個像素的 UV 分量作為這個 fragment 的輸出顏色。

readback pixel

最終用 glReadpixels() 函數,將我們輸出的顏色讀回來,就完成了。

補充

實際操作中遇到的一個問題是,如果設置了 GL_BLEND, 最終輸出的顏色會是混合以后的顏色,記得一定要確認關閉了 blending.

Written with StackEdit.


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM