My friends, my life, my style - James S.F. Hsieh

11/28/2009

由之前的文章 RGB 的物理意義是什麼? 可以了解到, RGB 是利用三原色合成的比例來描述色彩, 但是這不是唯一的一種描述方式, 我們可以用另一種方式來描述 RGB 所描述的色彩: 給定一個顏色, 它有多純 (摻雜白色的程度), 它有多亮. 這種描述方式就是所謂的 色相 Hue, 飽和度 Saturation, 亮度 Lightness, HSL (HSI, HSB) 色彩模型. 以下是將 H 定義為 0~360 度 S, L 定義正規劃在 0~1 之間的示意圖, 色彩分部會是一個錐體.



Figure 1


要如何做呢? Digital Image Processing 這本書的 4.6.1 節有完整的證明如何將 RGB 與 HSL 相互轉換所以我就不在此贅述. 我對過程中的第一個轉換 trichromatic coefficient 挺有興趣, 它可以把 RGB 轉換成 rgb 的三色係數定義如下:
X = R + G + B  且 L 亮度的定義是 L = X / 3
r = R / X,     g = G / X,     b = B / X   (X 不等於 0)     
且 1 = r + g + b


所以我們可以將 (R,G,B) 轉換成 (r, g, b, L) 係數, 系數上的意義是什麼呢? (r, g, b) 其實是色彩成分的比例關係且總合必定為 1 (記得, 人眼對色彩的感知是相對的, 重點就是比例), 而 L 則為 RGB 的平均成分, 或稱平均強度. (R,G,B) 與 (r, g, b, L) 可以相互轉換. 你是否想過為什麼 RGB 是個三維空間卻需要四個係數才能表達呢, 其實該轉換並非線性轉換, 仔細觀察你會發現 (r, g, b)  只有兩個維度喔. 以下是 rgb 的圖形, 不過我把三個軸換掉了, 紅軸與 OW 向量平行, 綠軸與 Pr Pg 向量平行, 藍軸與 Pb Qb 向量平行, 原因是因為這樣可以很容易觀察 rgb 是落在這個正三角形的平面上.




Figure 2


以下是個非常簡單的數學證明:
任意一點 P = (r, g, b) 且 r, g, b 介於 (0, 1) 之間可知 P 點落在第一卦限中
取向量 Q (1, 1, 1) 為參考向量與 P 點做內積可以得其投影量
 P × Q =  r  x 1 + g x 1 + b x 1 = r + g + b = 1 
可知 任意一點 P 在 Q 的投影量 (或稱 紅軸的分量) 都相同
得證所有點都落在 Pr Pg Pb 三角形平面上
而 HSL 轉換的意義就是: H 為 P W 跟 W Pr 的夾角, S 為 W P 與 W P' 的比例 且 P' 為 W P 向量 延伸到三角形邊線的一點, L 則為量度. 我們可以知道 H S 其實就是在描述 (r, g, b) 這個三角形平面.


看過 HSL 與 RGB 相互轉換公式可以知道該轉換需要多次計算三角函數與根號, 我跟國軍 (我同事, 不是政府的國軍喔, 雖然我還是國防役 :P) 開始思考, 如果我們希望調整 Hue, Saturation 與 Lightness 是否可以不要轉換到 HSL 就可以輕易辦到呢? 根據我們的研究結果是可以的喔, 最簡單的就是處理 Lightness, 其實就是對 (R, G, B) 三個值做 Scale, 其次呢就是調整 P 點 (r, g, b) 的位置就可以做到另外兩個 H 與 S 的調整, 做法是先將 (r, g, b) 基底換成上圖的三軸, 轉換的矩陣為 C 反矩陣為 Ci 可以輕易算出, 我們就可以對轉換後的向量根據 OW軸做 θ  Rotate 來達到 Hue 的調整, Rotate 矩陣為 Ro 矩陣. 對 Saturation 則對另外兩軸做 Scale 就可以達成, Scale 矩陣為 Sc 矩陣.


所以調整 Hue, Saturation. Lightness 可以用
(R,G,B) x (C x Sc(l, s) x Ro(h) x Ci)

Figure 3


這些矩陣運算來達成, X 係數的轉換是純量乘除所以可以提出抵銷, 所以整個計算幾乎等於是在 對 Figure 3 的 RGB 空間對 SW 這個軸做 Hue 的旋轉與 Lightness 的縮放, Saturation 的調整等於是在對另正交另兩軸做等量的縮放. 且 Sc 與 Ro 當中只有 θ 跟 scale 兩個變數很容易計算, 而且整結果可以合成一個矩陣就夠了, 在 GPU 中使用 HLSL 做矩陣向量計算的速度非常的快, 會比轉換 HSL 再轉回來快的多, 整體大概只需要 27個 instructions 就可以做到整個 HSL color adjust 的計算.  以 ATI Radeon HD 4890 為例大約有 11333MPixels/Sec, image 為 FullHD 下FPS 為 5465.

後來, 我的同事們發現這個算法有個重大的問題是, Hue 旋轉的過程中會讓 RGB 超出第一卦限, 並且旋轉後 Saturation 會跟著改變, 導致顏色出現問題, 我實驗的結果的確也是如此, 看來效果是不等價....... 數學不好啊有學到!! 我們可以用一下這張圖 Figure 4 來驗證, 該圖包含了 RGB 全部的色彩 (4096 x 4096), Figure 5 是調整 Hue 60 度下的結果, 我刻意把顏色標為黑色是超出第一卦限的部分. 如果將 Saturation 值調整為 0.5, 也就是降低一半的飽和度, 則不會再出現黑色, 因為所有的顏色都會落在正三角形中間的圓形中.


Figure 4



Figure 5

最有趣的是, 如果你拿 Figure 1 做 Hue 的調整, 那效果就向中間彩色圓盤在旋轉喔! 有趣吧. 感謝國軍跟他女友的指導才能找到更快的算法 :P 謝謝~~