本文首發在我的個人部落格: blog.shenyuanluo.com/,喜歡的朋友歡迎訂閱。
YUV
簡述
YUV: 是一種顏色空間,基於 YUV
的顏色編碼是流媒體的常用編碼方式,這種表達方式起初是為了彩色電視與黑白電視之間的訊號相容;其中
- Y: 表示明亮度(Luminance 或 Luma),也稱灰度圖。
- U、V: 表示色度(Chrominance 或 Chroma),作用是描述影像的色彩及飽和度,用於指定畫素的顏色。
Y’CbCr:(也稱為 YUV),是 YUV
的壓縮版本,不同之處在於 Y’CbCr
用於 數字影象 領域,YUV
用於 模擬訊號 領域;MPEG
、DVD
、攝像機中常說的 YUV
其實是 Y'CbCr
,二者轉換為 RGBA
的轉換矩陣是不同的。
- Cr:(色度紅)反應了
RGB
輸入訊號 紅色 部分與RGB
訊號亮度值之間的差異(即,當前顏色對 紅色 的偏移程度)。 - Cb:(色度紅)反應了
RGB
輸入訊號 藍色 部分與RGB
訊號亮度值之間的差異(即,當前顏色對 藍色 的偏移程度)。
注意: 如無特殊說明,本文討論的
YUV
均指Y'CbCr
。
格式
YUV儲存格式:
- planar: 先儲存
Y
,然後U
,然後V
。 - packed:
yuv
交叉儲存。
常見格式
- yuv444: packet 取樣(
yuv yuv yuv
)和 planar 取樣(yyyy uuuu vvvv
) - yuv422: packet 取樣
- yuvy:
YUYV YUYV
- uyvy:
UYVY UYVY
- yuvy:
- yuv422p: planar取樣:
YYYY UU VV
- yuv420: packet取樣:
YUV Y YUV Y
- yuv420p: planar取樣
-
I420:
-
YV12:
-
- yuv420sp:
Y
是planar取樣,UV
是packet取樣-
NV12:
-
NV21:
-
RGB
簡介
RGB: 是一種加色模型,將紅(Red
)、綠(Green
)、藍(Blue
)三原色的色光以不同的比例相加,以產生多種多樣的色光;且三原色的紅綠藍不可能用其他單色光合成。
- 浮點表示方式: 取值範圍為
0.0 ~ 1.0
(如在 OpenGL 中對每個子畫素點的表示就是使用這個表示方式)。 - 整數表示: 取值範圍為
0 ~ 255
或者00 ~ FF
(如 RGBA_8888、RGB_565)。
格式
索引形式
- RGB1: 每個畫素用 1 個
bit
表示0
,1
兩種值,可表示的顏色範圍為雙色,即最傳統的黑和白;需要調色盤,不過調色盤只包含兩種顏色。 - RGB4: 每個畫素用 4 個
bit
表示,4 個bit
所能夠表示的索引範圍是0~15
,共 16 個。也就是可以表示 16 種顏色。即調色盤中包含 16 中顏色。 - RGB8: 每個畫素用 8 個
bit
表示。8 個bit
所能夠表示的索引範圍是0~255
,共 256 個。也就是可以表示 256 種顏色。即調色盤中包含 256 種顏色。
畫素形式
- RGB555:
-
概述: 每一個畫素用 16 個
bit
(2個位元組)來表示,但最高位不用,R 用 5 個bit
、G 用 5 個bit
、B 用 5 個bit
表示。 -
記憶體示意圖:
-
獲取具體畫素值方法:(假設
color
為儲存某一個畫素點的變數)R = color & 0x7C00
// 獲取高位元組 5 個 bitG = color & 0x03E0
// 獲取中間的 5 個 bitB = color & 0x001F
// 獲取低位元組 5 個 bit
-
- RGB565:
-
概述: 每一個畫素用 16 個
bit
(2 個位元組)來表示,R 用 5 個bit
、G 用 6 個bit
、B 用 5 個bit
表示。 -
記憶體示意圖:
-
獲取具體畫素值方法:(假設
color
為儲存某一個畫素點的變數)R = color & 0xF800
// 獲取高位元組 5 個 bitG = color & 0x07E0
// 獲取中間的 6 個 bitB = color & 0x001F
// 獲取低位元組 5 個 bit
-
- RGB24:
-
概述: 每一個畫素用 24 個
bit
(3個位元組)來表示,R、G、B 均用 8bit
表示。 -
記憶體示意圖:
-
獲取具體畫素值方法:(假設
color
為儲存某一個畫素點的變數)R = color & 0x0000FF
G = color & 0x00FF00
B = color & 0xFF0000
-
- RGB32:
-
概述: 每一個畫素用 32 個
bit
(4個位元組)來表示,R、G、B 均用 8bit
表示,最後 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
內建的矩陣(如 mat2
、mat3
、mat4
)是 列主序,即需要將下列轉換矩陣轉換成 轉置矩陣 !
YUV ——> RGB
-
常規轉換標準:
-
BT.601 標準:(SD TV)
-
BT.709 標準:(HD TV)
RGB ——> YUV
-
常規轉換標準:
-
BT.601 標準:(SD TV)
-
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 |