數字圖像處理領域中常見的幾種色彩模式

在數字圖像處理過程中,常見的幾種色彩模式有RGB, HSL\HSV和YCbCr


RGB: 通過對紅(R), 綠(G), 藍(B)三個顏色通道的變化和疊加來得到其它顏色,三個分量的範圍都是[0, 255]
HSL\HSV: 將RGB色彩模式中的點在圓柱座標系中進行表述,分爲色相(Hue), 飽和度(Saturation), 亮度(Lightness)\明度(Value)三個通道。
  • 色相(H):色彩的基本屬性,就是日常所說的顏色名稱,如紅色、黃色等,取值範圍爲[0, 360);

  • 飽和度(S):色彩的純度,越高色彩越純,低則逐漸變灰,取值範圍[0, 100%];

  • 明度(V),亮度(L):像素灰度值的強度,亮度越高則圖像越發白,否則圖像越黑,取值範圍[0, 100%];

YCbCr: 一般我們所說的YUV都是指YCbCr,YCbCr 有許多取樣格式,如 444,422,420等

  • Y:明亮度,像素灰度值的強度;

  • Cb:藍色色度分量;

  • Cr:紅色色度分量;

Cb和Cr代表的是色度,描述影像色彩和飽和度,用於指定像素的顏色

在數字圖像處理中,選擇合適的色彩模式往往能達到事半功倍的效果

此處以Android平臺上操作圖像的亮度,對比度和飽和度來進行說明,首先了解下三者的概念:

  • 亮度:像素灰度值的強度,亮度越高則圖像越發白,否則圖像越黑;

  • 飽和度:色彩的純度,越高色彩越純越亮,低則逐漸變灰變暗;

  • 對比度:圖像中像素之間的差異,對比度越高圖像細節越突出,反之細節不明顯;

從上面的概念上來看,如果要操作圖像的亮度和飽和度,在HSL\HSV色彩空間中進行是最方便的,直接操作相應的分量即可;而對比度的操作可以直接在RGB色彩空間中進行

在Android中,我們用ImageView顯示一張圖片

然後拿到ImageView內部的bitmap對象

(imageView.drawable as BitmapDrawable).bitmap
從bitmap中獲取RGB數據
fun fetchRgbaFromBitmap(bitmap: Bitmap): IntArray {    val buffer = ByteBuffer.allocate(bitmap.byteCount).order(ByteOrder.nativeOrder())    bitmap.copyPixelsToBuffer(buffer)    val rgbaBytes = buffer.array()    val rgba = IntArray(rgbaBytes.size)    val count = rgbaBytes.size / 4    for (i in 0 until count) {        rgba[i * 4] = rgbaBytes[i * 4].toInt() and 0xff           // R        rgba[i * 4 + 1] = rgbaBytes[i * 4 + 1].toInt() and 0xff   // G        rgba[i * 4 + 2] = rgbaBytes[i * 4 + 2].toInt() and 0xff   // B        rgba[i * 4 + 3] = rgbaBytes[i * 4 + 3].toInt() and 0xff   // A    }    return rgba}
RGB色彩空間中調整對比度算法(調整對比度的算法很多,此處只是一個簡單實現):
R,G,B分量除255做歸一化處理;
((歸一化的分量 -  0.5)  *  飽和度係數  +  0.5)  *  255;
核心代碼:
 val count = originBitmapRgba!!.size / 4 for (i in 0 until count) {      var r = originBitmapRgba!![i * 4]      var g = originBitmapRgba!![i * 4 + 1]      var b = originBitmapRgba!![i * 4 + 2]      val a = originBitmapRgba!![i * 4 + 3]
      val cr = ((r / 255f) - 0.5f) * CONTRACT_RATIO      val cg = ((g / 255f) - 0.5f) * CONTRACT_RATIO      val cb = ((b / 255f) - 0.5f) * CONTRACT_RATIO
      r = ((cr + 0.5f) * 255f).toInt()      g = ((cg + 0.5f) * 255f).toInt()      b = ((cb + 0.5f) * 255f).toInt()      val newColor = Color.rgb(Util.clamp(r, 0, 255),                Util.clamp(g, 0, 255),  Util.clamp(b, 0, 255))      ColorUtils.setAlphaComponent(newColor, a)      tmpBitmapPixels!![i] = newColor}
對比度係數CONTRACT_RATIO爲1.5的效果

對比度係數CONTRACT_RATIO爲3的效果,可以看到圖像細節更突出,畫面更有層次感

亮度和飽和度的調節也可以在RGB色彩空間中應用相關算法進行,此處先將RGB轉化到HSL色彩空間,然後根據係數對S分量和L分量進行增益即可
從RGB到HSL\HSV的轉換算法如下(摘自百度百科):

從HSL\HSV到RGB的轉化算法,詳情查看百度百科...

Android中RGB和HSL的相互轉化,SDK已經幫我們實現好了, 
ColorUtils#RGBToHSL:
/**
* Convert RGB components to HSL (hue-saturation-lightness).
* <ul>
* <li>outHsl[0] is Hue [0 .. 360)</li>
* <li>outHsl[1] is Saturation [0...1]</li>
* <li>outHsl[2] is Lightness [0...1]</li>
* </ul>
*
* @param r      red component value [0..255]
* @param g      green component value [0..255]
* @param b      blue component value [0..255]
* @param outHsl 3-element array which holds the resulting HSL components
*/
public static void RGBToHSL(@IntRange(from = 0x0, to = 0xFF) int r,
            @IntRange(from = 0x0, to = 0xFF) int g, @IntRange(from = 0x0, to = 0xFF) int b,
            @NonNull float[] outHsl)
ColorUtils#HSLToColor:
/**
* Convert HSL (hue-saturation-lightness) components to a RGB color.
* <ul>
* <li>hsl[0] is Hue [0 .. 360)</li>
* <li>hsl[1] is Saturation [0...1]</li>
* <li>hsl[2] is Lightness [0...1]</li>
* </ul>
* If hsv values are out of range, they are pinned.
* @param hsl 3-element array which holds the input HSL components
* @return the resulting RGB color
*/
@ColorInt
public static int HSLToColor(@NonNull float[] hsl)
調整飽和度核心代碼:
val count = originBitmapRgba!!.size / 4
for (i in 0 until count) {
   val r = originBitmapRgba!![i * 4]
   val g = originBitmapRgba!![i * 4 + 1]
   val b = originBitmapRgba!![i * 4 + 2]
   val a = originBitmapRgba!![i * 4 + 3]
   ColorUtils.RGBToHSL(r, g, b, hsl)
   val s = hsl[1]  * SATURATION_RATIO
   hsl[1] = Util.clamp(s,0f, 1f)
   val newColor = ColorUtils.HSLToColor(hsl)
   ColorUtils.setAlphaComponent(newColor, a)
   tmpBitmapPixels!![i] = newColor
}
飽和度係數SATURATION_RATIO爲1.5的效果, 可以看到圖像變亮變純

飽和度係數SATURATION_RATIO爲0.5的效果,可以看到圖像變暗變灰

調整亮度核心代碼:
val count = originBitmapRgba!!.size / 4
for (i in 0 until count) {
   val r = originBitmapRgba!![i * 4]
   val g = originBitmapRgba!![i * 4 + 1]
   val b = originBitmapRgba!![i * 4 + 2]
   val a = originBitmapRgba!![i * 4 + 3]
   ColorUtils.RGBToHSL(r, g, b, hsl)
   hsl[2] = hsl[2] * LIGHTNESS_RATIO
   val newColor = ColorUtils.HSLToColor(hsl)
   ColorUtils.setAlphaComponent(newColor, a)
   tmpBitmapPixels!![i] = newColor
}
亮度係數LIGHTNESS_RATIO爲1.5的效果, 可以看到圖像整體發白

亮度係數LIGHTNESS_RATIO爲0.5的效果,可以看到圖像整體變黑了

DEMO

代碼傳送門:

https://github.com/sifutang/ImageProcess.git 


技術交流,歡迎加我微信:ezglumes ,拉你入技術交流羣。

推薦閱讀:

音視頻面試基礎題

OpenGL ES 學習資源分享

一文讀懂 YUV 的採樣與格式

OpenGL 之 GPUImage 源碼分析

推薦幾個堪稱教科書級別的 Android 音視頻入門項目

覺得不錯,點個在看唄~

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章