数字图像处理领域中常见的几种色彩模式

在数字图像处理过程中,常见的几种色彩模式有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 音视频入门项目

觉得不错,点个在看呗~

展开阅读全文
©️2020 CSDN 皮肤主题: 游动-白 设计师: 上身试试 返回首页
实付0元
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值