YUV <——> RGB 轉換算法

本文首發在個人我的博客: blog.shenyuanluo.com/,喜歡的朋友歡迎訂閱。html

YUV

簡述

YUV: 是一種顏色空間,基於 YUV 的顏色編碼是流媒體的經常使用編碼方式,這種表達方式起初是爲了彩色電視與黑白電視之間的信號兼容;其中git

  • Y: 表示明亮度(Luminance 或 Luma),也稱灰度圖。
  • U、V: 表示色度(Chrominance 或 Chroma),做用是描述影像的色彩及飽和度,用於指定像素的顏色。

Y’CbCr:(也稱爲 YUV),是 YUV 的壓縮版本,不一樣之處在於 Y’CbCr 用於 數字圖像 領域,YUV 用於 模擬信號 領域;MPEGDVD、攝像機中常說的 YUV 實際上是 Y'CbCr,兩者轉換爲 RGBA 的轉換矩陣是不一樣的。github

  • Cr:(色度紅)反應了 RGB 輸入信號 紅色 部分與 RGB 信號亮度值之間的差別(即,當前顏色對 紅色 的偏移程度)。
  • Cb:(色度紅)反應了 RGB 輸入信號 藍色 部分與 RGB 信號亮度值之間的差別(即,當前顏色對 藍色 的偏移程度)。

注意: 如無特殊說明,本文討論的 YUV 均指 Y'CbCr算法

格式

YUV存儲格式:

  • planar: 先存儲 Y,而後 U,而後 V
  • packed: yuv 交叉存儲。

常見格式

  1. yuv444: packet 採樣(yuv yuv yuv)和 planar 採樣(yyyy uuuu vvvv
  2. yuv422: packet 採樣
    • yuvy: YUYV YUYV
    • uyvy: UYVY UYVY
  3. yuv422p: planar採樣:YYYY UU VV
  4. yuv420: packet採樣: YUV Y YUV Y
  5. yuv420p: planar採樣
    • I420:數組

    • YV12: 性能

  6. yuv420sp:Y 是planar採樣,UV 是packet採樣
    • NV12: 測試

    • NV21:優化

RGB

簡介

RGB: 是一種加色模型,將紅(Red)、綠(Green)、藍(Blue)三原色的色光以不一樣的比例相加,以產生多種多樣的色光;且三原色的紅綠藍不可能用其餘單色光合成。編碼

  1. 浮點表示方式: 取值範圍爲 0.0 ~ 1.0(如在 OpenGL 中對每一個子像素點的表示就是使用這個表示方式)。
  2. 整數表示: 取值範圍爲 0 ~ 255 或者 00 ~ FF(如 RGBA_8888RGB_565)。

格式

索引形式

  1. RGB1: 每一個像素用 1 個 bit 表示 01 兩種值,可表示的顏色範圍爲雙色,即最傳統的黑和白;須要調色板,不過調色板只包含兩種顏色。
  2. RGB4: 每一個像素用 4 個 bit 表示,4 個 bit 所可以表示的索引範圍是 0~15,共 16 個。也就是能夠表示 16 種顏色。即調色板中包含 16 中顏色。
  3. RGB8: 每一個像素用 8 個 bit 表示。8 個 bit 所可以表示的索引範圍是 0~255,共 256 個。也就是能夠表示 256 種顏色。即調色板中包含 256 種顏色。

像素形式

  1. RGB555:
    • 概述: 每個像素用 16 個 bit(2個字節)來表示,但最高位不用R 用 5 個 bitG 用 5 個 bitB 用 5 個 bit 表示。spa

    • 內存示意圖:

    • 獲取具體像素值方法:(假設 color 爲存儲某一個像素點的變量)

      • R = color & 0x7C00 // 獲取高字節 5 個 bit
      • G = color & 0x03E0 // 獲取中間的 5 個 bit
      • B = color & 0x001F // 獲取低字節 5 個 bit
  2. RGB565:
    • 概述: 每個像素用 16 個 bit(2 個字節)來表示,R 用 5 個 bitG 用 6 個 bitB 用 5 個 bit 表示。

    • 內存示意圖:

    • 獲取具體像素值方法:(假設 color 爲存儲某一個像素點的變量)

      • R = color & 0xF800 // 獲取高字節 5 個 bit
      • G = color & 0x07E0 // 獲取中間的 6 個 bit
      • B = color & 0x001F // 獲取低字節 5 個 bit
  3. RGB24:
    • 概述: 每個像素用 24 個 bit(3個字節)來表示,RGB 均用 8 bit 表示。

    • 內存示意圖:

    • 獲取具體像素值方法:(假設 color 爲存儲某一個像素點的變量)

      • R = color & 0x0000FF
      • G = color & 0x00FF00
      • B = color & 0xFF0000
  4. RGB32:
    • 概述: 每個像素用 32 個 bit(4個字節)來表示,RGB 均用 8 bit 表示,最後 1 個字節保留

    • 內存示意圖:

    • 獲取具體像素值方法:(假設 color 爲存儲某一個像素點的變量)

      • R = color & 0x0000FF00
      • G = color & 0x00FF0000
      • B = color & 0xFF000000

轉換

轉換矩陣

注意: 這裏的轉換矩陣中,當轉換爲 RGB 讀取 YUV 時,須要將 U(Cb)V(Cr) 的取值範圍整數表示時,轉換爲:[-128, 127];浮點數表示時,轉換爲:[-0.5, 0.5]

(這是由於:U(Cb)V(Cr) 取值範圍是 [﹣128, 127],對應的浮點數表示爲 [﹣0.5, 0.5];而在存儲時,爲了方便存儲,跟 Y 數據同樣,統一用一個(無符號)字節表示,即取值範圍是 [0, 255],對應的浮點數表示爲:[0, 1]。)

特別注意:OpenGL 內置的矩陣(如 mat2mat3mat4 )是 列主序,即須要將下列轉換矩陣轉換成 轉置矩陣

YUV ——> RGB

  1. 常規轉換標準:

  2. BT.601 標準:(SD TV)

  3. BT.709 標準:(HD TV)

RGB ——> YUV

  1. 常規轉換標準:

  2. BT.601 標準:(SD TV)

  3. BT.709 標準:(HD TV)

算法優化

舉例:YUV ——> RGB 常規轉換矩陣。

常規轉換:(浮點運算)

r = y                  + (1.370705 * v);
g = y - (0.337633 * u) - (0.698001 * v);
b = y + (1.732446 * u);
複製代碼

優化1:避免浮點運算

從上述算法,能夠看到存在許多的浮點運算,而在算法優化中,最好能避免浮點運算(比較耗時)來提升效率。

所以,同時對錶達式中全部子項乘以 256 來對結果進行 四捨五入昨晚新的 整數係數,最後再對計算結果再右移 8 位(除以 256);即,(注意:這裏的轉換是有損的,精度會有所下降

256 * r = 256 * y                        + (256 * 1.370705 * v);
256 * g = 256 * y - (256 * 0.337633 * u) - (256 * 0.698001 * v);
256 * b = 256 * y + (256 * 1.732446 * u);
複製代碼

===》

r = ((256 * y             + (351 * v))>>8);
g = ((256 * y - (86  * u) - (179 * v))>>8);
b = ((256 * y + (444 * u))            >>8);
複製代碼

優化2:避免乘法運算

從上述算法,能夠看到存在許多的乘法運算,而乘法一樣也很好使,最好能避免乘法運算(使用位移運算代替)來提供效率。

所以,將全部表達式中的子項係數,拆解成整數(該整數必需是 2 的次冪,這樣可使用位移運算)相加的形式。

例如:

351 = 256 + 64 + 16 + 8 + 4 + 2 + 1 = 2^8 + 2^6 + 2^4 + 2^3 + 2^2 + 2^1 + 2^0
複製代碼

===》

r = (((y<<8) + (v<<8) + (v<<6) + (v<<4) + (v<<3) + (v<<2) + (v<<1) + v)                   >> 8);
g = (((y<<8) - (u<<6) - (u<<4) - (u<<2) - (u<<1) - (v<<7) - (v<<5) - (v<<4) - (v<<1) - v) >> 8);
b = (((y<<8) + (u<<8) + (u<<7) + (u<<5) + (u<<4) + (u<<3) + (u<<2))                       >> 8);
複製代碼

優化3:查表

在常規轉換表達式中,變量的取值範圍是已經肯定,Y:[0, 255],U:[-128, 127],V:[-128, 127];那麼就可使用一維數組存儲結果來提升效率。

所以,將表達式中相關的變量計算結果分別存儲在 4 個一位數組中,在使用計算時,直接經過數組查詢便可得到表達式相乘結果。

例如:對於表達式 256 * 1.370705 * v

int rv = 0;     // 計算 R 值 V 係數

rv = 256 * 1.370705 = 351;
            
for (int i = 0; i < 256; i++)
{
    m_rv[i] = ((i - 128) * rv)>>8;
}
複製代碼

===》

r = y + m_rv[v];
g = y - m_gu[u] - m_gv[v];
b = y + m_bu[u];
複製代碼

算法性能比較

測試說明

  • 系統:macOS 10.13.5
  • 環境:Xcode 9.4
  • 測試文件:480 x 360,454 幀(文件大小:117,676,800 Byte)
  • 轉換矩陣:常規標準
  • 流程描述:從源文件讀取數據,對數據進行轉換(I420 ——> RGB24),但不寫入文件

測試結果

轉換方式 循環次數 平均耗時(μs/次) 平均每幀耗時(μs)
浮點運算 50 2837788.36 6250.64
避免浮點運算 50 2650935.74 5839.07
避免乘法運算 50 3031586.02 6677.50
查表法 50 2674598.74 5891.19

Demo


參考

相關文章
相關標籤/搜索