图像的颜色空间
色彩的三要素 —— 色相、明度、纯度
-
H色相(表,表现):即色彩的相貌和特征。自然界中色彩的种类很多,色相指色彩的种类和名称。如;红、橙、黄、绿、青、蓝、紫等等颜色的种类变化就叫色相。
-
B明度(表,面子):指色彩的亮度或明度,也叫明亮度。颜色有深浅、明暗的变化。比如,深黄、中黄、淡黄、柠檬黄等黄颜色在明度上就不一样,紫红、深红、玫瑰红、大红、朱红、桔红等红颜色在亮度上也不尽相同。这些颜色在明暗、深浅上的不同变化,也就是色彩的又一重要特征一一明度变化。
色彩的明度变化有许多种情况,一是不同色相之间的明度变化。如:白比黄亮、黄比橙亮、橙比红亮、红比紫亮、紫比黑亮;二是在某种颜色中加白色,亮度就会逐渐提高,加黑色亮度就会变暗,但同时它们的纯度(颜色的饱和度)就会降低,三是相同的颜色,因光线照射的强弱不同也会产生不同的明暗变化。。 -
S纯度(里,里子):指色彩的鲜艳程度,也叫饱和度。原色是纯度最高的色彩。颜色混合的次数越多,纯度越低,反之,纯度则高。原色中混入补色,纯度会立即降低、变灰。物体本身的色彩,也有纯度高低之分,西红柿与苹果相比,西红柿的纯度高些,苹果的纯度低些。
关于水的色彩观念 —— 水,首先是无色的;其次是白色的(可以降低纯度,提高明度;最后是任意色的(经光照反色多彩或折射七彩)。
HSB,S=纯色/白色,B就看作一盏灯(通过“0%黑色”到“100%黑色”来模拟这盏灯的亮度值)
B灯你也可以看作黑暗程度,其值为0的时候,就当B属性不存在,只显示“颜色另两个属性”。其值>0%时表示B灯启动,对“颜色另两个属性”开始发生作用。
HSL,S=纯色/50%灰色,L也看作一盏灯(但它模拟的方式和HSB中的B不同,“<50%”代表黑色的量,50%代表白色和黑色的量都为0,“>50%~100%”代表白色的量)
L灯可以看作一盏“两极灯”,它有黑暗和光明两个端点,维持在当中的50%时表示L属性不存在,只显示“颜色另两个属性”。其值向两级移动时表示L灯启动,对“颜色另两个属性”开始发生作用。
HSL色彩模型又是什么?
HSL同样使用了3个分量来描述色彩,与RGB使用的三色光不同,HSL色彩的表述方式是:H(hue)色相,S(saturation)饱和度,以及L(lightness)亮度。
听起来一样复杂?稍后你就会发现,与“反人类”的RGB模型相比,HSL是多么的友好。
-
HSL的H(hue)分量,代表的是人眼所能感知的颜色范围,这些颜色分布在一个平面的色相环上,取值范围是0°到360°的圆心角,每个角度可以代表一种颜色。色相值的意义在于,我们可以在不改变光感的情况下,通过旋转色相环来改变颜色。在实际应用中,我们需要记住色相环上的六大主色,用作基本参照:360°/0°红、60°黄、120°绿、180°青、240°蓝、300°洋红,它们在色相环上按照60°圆心角的间隔排列。
- HSL的S(saturation)分量,指的是色彩的饱和度,它用0%至100%的值描述了相同色相、明度下色彩纯度的变化。数值越大,颜色中的灰色越少,颜色越鲜艳,呈现一种从理性(灰度)到感性(纯色)的变化。
![HSL之饱和度]
-
HSL的L(lightness)分量,指的是色彩的明度,作用是控制色彩的明暗变化。它同样使用了0%至100%的取值范围。数值越小,色彩越暗,越接近于黑色;数值越大,色彩越亮,越接近于白色。
![HSL之明度]
采用HSL颜色体系后, 更能便捷地选取自己偏好或当前合适的颜色.
特别对于随机色的表示, 采用RGB可能不太友好(有时偏暗, 外观不佳), 可试试HSL. 如下所示(仅示范, 细心调试后更佳):
CGFloat hue = ( arc4random() % 256 / 256.0 ); // 0.0 to 1.0 CGFloat saturation = ( arc4random() % 128 / 256.0 ) + 0.5; // 0.5 to 1.0, away from white CGFloat brightness = ( arc4random() % 128 / 256.0 ) + 0.5; // 0.5 to 1.0, away from black UIColor *color = [UIColor colorWithHue:hue saturation:saturation brightness:brightness alpha:1.0];
形式定义
HSL 和 HSV 在数学上定义为在 RGB 空间中的颜色的 R, G 和 B 的坐标的变换。
[编辑]从 RGB 到 HSL 或 HSV 的转换
设 (r, g, b) 分别是一个颜色的红、绿和蓝坐标,它们的值是在 0 到 1 之间的实数。设 max 等价于 r, g 和 b 中的最大者。设 min 等于这些值中的最小者。要找到在 HSL 空间中的 (h, s, l) 值,这里的 h ∈ [0, 360)是角度的色相角,而 s, l ∈ [0,1] 是饱和度和亮度,计算为:
h 的值通常规范化到位于 0 到 360°之间。而 h = 0 用于 max = min 的(就是灰色)时候而不是留下 h 未定义。
HSL 和 HSV 有同样的色相定义,但是其他分量不同。HSV 颜色的 s 和 v 的值定义如下:
[编辑]从 HSL 到 RGB 的转换
给定 HSL 空间中的 (h, s, l) 值定义的一个颜色,带有 h 在指示色相角度的值域 [0, 360)中,分别表示饱和度和亮度的s 和 l 在值域 [0, 1] 中,相应在 RGB 空间中的 (r, g, b) 三原色,带有分别对应于红色、绿色和蓝色的 r, g 和 b 也在值域 [0, 1] 中,它们可计算为:
首先,如果 s = 0,则结果的颜色是非彩色的、或灰色的。在这个特殊情况,r, g 和 b 都等于 l。注意 h 的值在这种情况下是未定义的。
当 s ≠ 0 的时候,可以使用下列过程:[1]
- (h 规范化到值域 [0,1)内)
对于每个颜色向量 Color = (ColorR, ColorG, ColorB) = (r, g, b),
[编辑]从 HSV 到 RGB 的转换
类似的,给定在 HSV 中 (h, s, v) 值定义的一个颜色,带有如上的 h,和分别表示饱和度和明度的 s 和 v 变化于 0 到 1 之间,在 RGB 空间中对应的 (r, g, b) 三原色可以计算为:
对于每个颜色向量 (r, g, b),
展示的 RGB 值的范围是 0.0 到 1.0。
RGB | HSL | HSV | 结果 |
---|---|---|---|
(1, 0, 0) | (0°, 1, 0.5) | (0°, 1, 1) | |
(0.5, 1, 0.5) | (120°, 1, 0.75) | (120°, 0.5, 1) | |
(0, 0, 0.5) | (240°, 1, 0.25) | (240°, 1, 0.5) |
核心的转换公式:
RGB-->HSI
截图来自中科院刘定生老师的《数字图像处理与分析》课件。
HSI-->RGB
具体的数学公式参照冈萨雷斯版《数字图像处理(第三版)》432-434页,中译版的260-261页。
下面贴代码:
#include "opencv_libs.h" #include <highgui.h> #include <cv.h> #include <math.h> /* * 描述:实现RGB颜色模型到HSI颜色模型之间的相互转换 * 作者:qdsclove(qdsclove@gmail.com) * 时间:16:01 4/17 星期三 2013 */ // 将HSI颜色空间的三个分量组合起来,便于显示 IplImage* catHSImage(CvMat* HSI_H, CvMat* HSI_S, CvMat* HSI_I) { IplImage* HSI_Image = cvCreateImage( cvGetSize( HSI_H ), IPL_DEPTH_8U, 3 ); for(int i = 0; i < HSI_Image->height; i++) { for(int j = 0; j < HSI_Image->width; j++) { double d = cvmGet( HSI_H, i, j ); int b = (int)(d * 255/360); d = cvmGet( HSI_S, i, j ); int g = (int)( d * 255 ); d = cvmGet( HSI_I, i, j ); int r = (int)( d * 255 ); cvSet2D( HSI_Image, i, j, cvScalar( b, g, r ) ); } } return HSI_Image; } // 将HSI颜色模型的数据转换为RGB颜色模型的图像 IplImage* HSI2RGBImage(CvMat* HSI_H, CvMat* HSI_S, CvMat* HSI_I) { IplImage * RGB_Image = cvCreateImage(cvGetSize(HSI_H), IPL_DEPTH_8U, 3 ); int iB, iG, iR; for(int i = 0; i < RGB_Image->height; i++) { for(int j = 0; j < RGB_Image->width; j++) { // 该点的色度H double dH = cvmGet( HSI_H, i, j ); // 该点的色饱和度S double dS = cvmGet( HSI_S, i, j ); // 该点的亮度 double dI = cvmGet( HSI_I, i, j ); double dTempB, dTempG, dTempR; // RG扇区 if(dH < 120 && dH >= 0) { // 将H转为弧度表示 dH = dH * 3.1415926 / 180; dTempB = dI * (1 - dS); dTempR = dI * ( 1 + (dS * cos(dH))/cos(3.1415926/3 - dH) ); dTempG = (3 * dI - (dTempR + dTempB)); } // GB扇区 else if(dH < 240 && dH >= 120) { dH -= 120; // 将H转为弧度表示 dH = dH * 3.1415926 / 180; dTempR = dI * (1 - dS); dTempG = dI * (1 + dS * cos(dH)/cos(3.1415926/3 - dH)); dTempB = (3 * dI - (dTempR + dTempG)); } // BR扇区 else { dH -= 240; // 将H转为弧度表示 dH = dH * 3.1415926 / 180; dTempG = dI * (1 - dS); dTempB = dI * (1 + (dS * cos(dH))/cos(3.1415926/3 - dH)); dTempR = (3* dI - (dTempG + dTempB)); } iB = dTempB * 255; iG = dTempG * 255; iR = dTempR * 255; cvSet2D( RGB_Image, i, j, cvScalar( iB, iG, iR ) ); } } return RGB_Image; } int main() { IplImage* img = cvLoadImage("lena.bmp"); // 三个HSI空间数据矩阵 CvMat* HSI_H = cvCreateMat( img->height, img->width, CV_32FC1 ); CvMat* HSI_S = cvCreateMat( img->height, img->width, CV_32FC1 ); CvMat* HSI_I = cvCreateMat( img->height, img->width, CV_32FC1 ); // 原始图像数据指针, HSI矩阵数据指针 uchar* data; // rgb分量 byte img_r, img_g, img_b; byte min_rgb; // rgb分量中的最小值 // HSI分量 float fHue, fSaturation, fIntensity; for(int i = 0; i < img->height; i++) { for(int j = 0; j < img->width; j++) { data = cvPtr2D(img, i, j, 0); img_b = *data; data++; img_g = *data; data++; img_r = *data; // Intensity分量[0, 1] fIntensity = (float)((img_b + img_g + img_r)/3)/255; // 得到RGB分量中的最小值 float fTemp = img_r < img_g ? img_r : img_g; min_rgb = fTemp < img_b ? fTemp : img_b; // Saturation分量[0, 1] fSaturation = 1 - (float)(3 * min_rgb)/(img_r + img_g + img_b); // 计算theta角 float numerator = (img_r - img_g + img_r - img_b ) / 2; float denominator = sqrt( pow( (img_r - img_g), 2 ) + (img_r - img_b)*(img_g - img_b) ); // 计算Hue分量 if(denominator != 0) { float theta = acos( numerator/denominator) * 180/3.14; if(img_b <= img_g) { fHue = theta ; } else { fHue = 360 - theta; } } else { fHue = 0; } // 赋值 cvmSet( HSI_H, i, j, fHue ); cvmSet( HSI_S, i, j, fSaturation); cvmSet( HSI_I, i, j, fIntensity ); } } IplImage* HSI_Image = catHSImage( HSI_H, HSI_S, HSI_I ); IplImage* RGB_Image = HSI2RGBImage( HSI_H, HSI_S, HSI_I ); cvShowImage("img", img); cvShowImage("HSI Color Model", HSI_Image); cvShowImage("RGB Color Model", RGB_Image); cvWaitKey(0); cvReleaseImage( &img ); cvReleaseImage( &HSI_Image ); cvReleaseImage( &RGB_Image ); cvReleaseMat( &HSI_H); cvReleaseMat( &HSI_S); cvReleaseMat( &HSI_I); cvDestroyAllWindows(); return 0; }