HSV/HSL转换到RGB色彩空间

Posted on Mar 8, 2013

处理图像时,有时候会遇到不同色彩空间转换的情况。比较常见的是HSV/HSL到RGB的转换。

HSV(HSB)/HSL简介

不同于RGB/CYMK,HSV和HSL是两种比较常见的基于圆柱坐标系的色彩表示方法。这种方法对颜色的表达,对人来说更直观,更易于感受。因此艺术家有时偏好使用 HSL或HSV 而不选择 三原色光模式(即RGB模型) 或 印刷四分色模式(即CMYK模型),因为它类似于人类感觉颜色的方式,具有较强的感知度。RGB 和 CMYK 分别是加法原色和减法原色模型,以原色组合的方式定义颜色,而 HSV 以人类更熟悉的方式封装了关于颜色的信息:“这是什么颜色?深浅如何?明暗如何?”。

色彩定义

  • H :Hue(色相),色彩的基本属性,就是平常所说的颜色名称,如红色、黄色等。
  • S :saturation(饱和度),色彩的纯度,越高色彩越纯,低则逐渐变灰,取0-100%的数值。
  • V :value(色调),取0-100%。
  • B :brightness(明度),取0-100%。
  • L :lightness(亮度),取0-100%。

HSL 和 HSV 二者都把颜色描述在圆柱坐标系内的点,这个圆柱的中心轴取值为自底部的黑色到顶部的白色而在它们中间是的灰色,绕这个轴的角度对应于“色相”,到这个轴的距离对应于“饱和度”,而沿着这个轴的高度对应于“亮度”,“色调”或“明度”。

这两种表示在用目的上类似,但在方法上有区别。二者在数学上都是圆柱,但 HSV(色相,饱和度,色调)在概念上可以被认为是颜色的倒圆锥体(黑点在下顶点,白色在上底面圆心),HSL 在概念上表示了一个双圆锥体和圆球体(白色在上顶点,黑色在下顶点,最大横切面的圆心是半程灰色)。注意尽管在 HSL 和 HSV 中“色相”指称相同的性质,它们的“饱和度”的定义是明显不同的。

因为 HSL 和 HSV 是设备依赖的 RGB 的简单变换,(h, s, l) 或 (h, s, v) 三元组定义的颜色依赖于所使用的特定红色、绿色和蓝色“加法原色”。每个独特的 RGB 设备都伴随着一个独特的 HSL 和 HSV 空间。但是 (h, s, l) 或 (h, s, v) 三元组在被约束于特定 RGB 空间比如 sRGB 的时候就变成明确的了。

HSV 模型在 1978 年由计算机图形学先驱 Alvy Ray Smith 创立,它是三原色光模式的一种非线性变换。

HSV -> RGB 转换

公式

$$ H ∈ [0°, 360°), S_{HSV} ∈ [0, 1], V ∈ [0, 1] $$

计算公式如下:

$$ C = V \times S_{HSV} $$

$$ H’=\dfrac {H} {60\degree} $$

$$ X = C \times ( 1 - |H’ mod 2 -1| ) $$

$$ (R_1,G_1,B_1) = \begin{cases} (0,0,0) & \text{ if H is undefined } \\ (C,X,0) & \text{ if } 0 \leq H’ < 1 \\ (X,C,0) & \text{ if } 1 \leq H’ < 2 \\ (0,C,X) & \text{ if } 2 \leq H’ < 3 \\ (0,X,C) & \text{ if } 3 \leq H’ < 4 \\ (X,0,C) & \text{ if } 4 \leq H’ < 5 \\ (C,0,X) & \text{ if } 5 \leq H’ < 6 \end{cases} $$

RGB结果:

$$ m = V - C $$

$$ (R,G,B) = (R_1+m, G_1+m, B_1+m) $$

代码

private function HSLtoRGB(alpha:Number=1,hue:Number=0, saturation:Number=0.5,lightness:Number=1):uint
{
  var a = Math.max(0,Math.min(1,alpha));
  saturation = Math.max(0,Math.min(1,saturation));
  lightness = Math.max(0,Math.min(1,lightness));
  hue = hue%360;
  if(hue<0)
    hue+=360;
  hue/=60;
  var C:Number = (1-Math.abs(2*lightness-1))*saturation;
  var X:Number = C*(1-Math.abs((hue%2)-1));
  var m:Number = lightness-0.5*C;
  C=(C+m)*255;
  X=(X+m)*255;
  m*=255;
  if(hue<1) return (Math.round(a*255)<<24)+(C<<16)+(X<<8)+m;
  if(hue<2) return (Math.round(a*255)<<24)+(X<<16)+(C<<8)+m;
  if(hue<3) return (Math.round(a*255)<<24)+(m<<16)+(C<<8)+X;
  if(hue<4) return (Math.round(a*255)<<24)+(m<<16)+(X<<8)+C;
  if(hue<5) return (Math.round(a*255)<<24)+(X<<16)+(m<<8)+C;
  return (Math.round(a*255)<<24)+(C<<16)+(m<<8)+X;
}

RGB色彩空间的坐标系

这张图比较有意思: 坐标系

总结

一开始接触这个有点束手无策,后来从维基百科上看到了HSV/HSL 到 RGB 的转换公式,而且也能搜索到现成的处理函数,但知其然也须知其所以然。大概是因为最初工作的影响,每次组会讨论总会从background开始讲起,循循善诱,娓娓道来,虽然没有学到多少计算机知识,但是态度决定一切,能认识那么多杰出的人才,踏实勤奋的工作作风,严谨求真的科研精神,在那里的一年是恐怕是我人生中最大的收获之一。(扯远了…)

写这篇blog的同时,还试用了一下 MathJax 这个插件,顺便学习了下 $$\LaTeX$$ 的基本语法,简直是神器,难怪MathJax有很多学术界和出版界的单位赞助。


参考资料

  1. http://en.wikipedia.org/wiki/HSL_and_HSV
  2. http://snipplr.com/view/34817/
  3. http://blog.wonderwhy-er.com/as3-hsl-to-rgb