zl程序教程

您现在的位置是:首页 >  其它

当前栏目

图像的颜色空间

图像 空间 颜色
2023-09-14 08:59:07 时间

色彩的三要素 —— 色相、明度、纯度

  • 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之色相]
  • 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 空间中的颜色的 RG 和 B 的坐标的变换。

[编辑]从 RGB 到 HSL 或 HSV 的转换

设 (rgb) 分别是一个颜色的红、绿和蓝坐标,它们的值是在 0 到 1 之间的实数。设 max 等价于 rg 和 b 中的最大者。设 min 等于这些值中的最小者。要找到在 HSL 空间中的 (hsl) 值,这里的 h ∈ [0, 360)是角度的色相角,而 sl ∈ [0,1] 是饱和度和亮度,计算为:

h =\begin{cases}0^\circ & \mbox{if } max = min \\60^\circ \times \frac{g - b}{max - min} + 0^\circ,   & \mbox{if } max = r \mbox{ and } g \ge b \\60^\circ \times \frac{g - b}{max - min} + 360^\circ,   & \mbox{if } max = r \mbox{ and } g < b \\60^\circ \times \frac{b - r}{max - min} + 120^\circ, & \mbox{if } max = g \\60^\circ \times \frac{r - g}{max - min} + 240^\circ, & \mbox{if } max = b\end{cases}
l = \begin{matrix} \frac{1}{2} \end{matrix} (max + min)
s = \begin{cases}0 & \mbox{if } l = 0 \mbox{ or } max = min \\\frac{max-min}{max+min} = \frac{max-min}{2l}, & \mbox{if } 0  \frac{1}{2}\end{cases}


h 的值通常规范化到位于 0 到 360°之间。而 h = 0 用于 max = min 的(就是灰色)时候而不是留下 h 未定义。

HSL 和 HSV 有同样的色相定义,但是其他分量不同。HSV 颜色的 s 和 v 的值定义如下:

 

s = \begin{cases}0, & \mbox{if } max = 0 \\\frac{max - min}{max} = 1 - \frac{min}{max}, & \mbox{otherwise}\end{cases}
v = max \,

[编辑]从 HSL 到 RGB 的转换

给定 HSL 空间中的 (hsl) 值定义的一个颜色,带有 h 在指示色相角度的值域 [0, 360)中,分别表示饱和度和亮度的s 和 l 在值域 [0, 1] 中,相应在 RGB 空间中的 (rgb) 三原色,带有分别对应于红色、绿色和蓝色的 rg 和 b 也在值域 [0, 1] 中,它们可计算为:

首先,如果 s = 0,则结果的颜色是非彩色的、或灰色的。在这个特殊情况,rg 和 b 都等于 l。注意 h 的值在这种情况下是未定义的。

当 s ≠ 0 的时候,可以使用下列过程:[1]

 

q=\begin{cases}l \times (1+s), & \mbox{if } l < \frac{1}{2} \\l+s-(l \times s), & \mbox{if } l \ge \frac{1}{2}\end{cases}
p = 2 \times l - q \,
h_k = {h \over 360} \, h 规范化到值域 [0,1)内)
t_R = h_k+\frac{1}{3} \,
t_G = h_k \,
t_B = h_k-\frac{1}{3} \,
\mbox{if } t_C < 0 \rightarrow t_C = t_C + 1.0 \quad \mbox{for each}\,C \in \{R,G,B\}
\mbox{if } t_C > 1 \rightarrow t_C = t_C - 1.0 \quad \mbox{for each}\,C \in \{R,G,B\}


对于每个颜色向量 Color = (ColorRColorGColorB) = (rgb),

 

{Color}_C =\begin{cases}p+ \left((q-p) \times 6 \times t_C\right), & \mbox{if } t_C < \frac{1}{6}  \\q, & \mbox{if } \frac{1}{6} \le t_C < \frac{1}{2}  \\p+\left((q-p) \times 6 \times (\frac{2}{3} - t_C) \right), & \mbox{if } \frac{1}{2} \le t_C < \frac{2}{3} \\p, & \mbox{otherwise }\end{cases}
\mbox{for each}\,C \in \{R,G,B\}

[编辑]从 HSV 到 RGB 的转换

类似的,给定在 HSV 中 (hsv) 值定义的一个颜色,带有如上的 h,和分别表示饱和度和明度的 s 和 v 变化于 0 到 1 之间,在 RGB 空间中对应的 (rgb) 三原色可以计算为:

h_i \equiv \left\lfloor \frac{h}{60} \right\rfloor \pmod{6}
f = \frac{h}{60} - h_i
p = v \times (1 - s) \,
q = v \times (1 - f \times s) \,
t = v \times (1 - (1 - f) \times s) \,


对于每个颜色向量 (rgb),

 

(r, g, b) = \begin{cases}(v, t, p), & \mbox{if } h_i = 0  \\(q, v, p), & \mbox{if } h_i = 1  \\(p, v, t), & \mbox{if } h_i = 2  \\(p, q, v), & \mbox{if } h_i = 3  \\(t, p, v), & \mbox{if } h_i = 4  \\(v, p, q), & \mbox{if } h_i = 5  \\\end{cases}


展示的 RGB 值的范围是 0.0 到 1.0。

RGBHSLHSV结果
(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)  
 
//(1)Matlab RGB转换为HSV

I = imread('Lena.jpg');
HSV = rgb2hsv(I);
H = HSV(:, :, 1);
S = HSV(:, :, 2);
V = HSV(:, :, 3);
figure;
subplot(2, 3, 1); imshow(I);
//subplot(2, 3, 2); imshow(HSV);
subplot(2, 3, 4); imshow(H);
subplot(2, 3, 5); imshow(S);
subplot(2, 3, 6); imshow(V);

 

//(2)Opencv中RGB转换为HSV

// BRGtoHSVShow.cpp : 定义控制台应用程序的入口点。
//

//#include "stdafx.h"
#include "cv.h"
#include "cxcore.h"
#include "highgui.h"
#include "cvaux.h"

int main( )
{
 IplImage* src = NULL;
 IplImage* floatsrc = NULL;
 IplImage* floathsv = NULL;
 IplImage* floatimgH = NULL;
 IplImage* floatimgS = NULL;
 IplImage* floatimgV = NULL;

 cvNamedWindow( "src", 1 );
 cvNamedWindow( "H通道", 1 );
 cvNamedWindow( "S通道", 1 );
 cvNamedWindow( "V通道", 1 );

 src = cvLoadImage( "lena.jpg", -1);
cvShowImage( "src", src );
 CvSize size = cvGetSize( src );

 //先将图像转换成float型的
 floatsrc = cvCreateImage( size, IPL_DEPTH_32F, 3 );
 floathsv = cvCreateImage( size, IPL_DEPTH_32F, 3 );
 floatimgH = cvCreateImage( size, IPL_DEPTH_32F, 1 );
 floatimgS = cvCreateImage( size, IPL_DEPTH_32F, 1 );
 floatimgV = cvCreateImage( size, IPL_DEPTH_32F, 1 );

 //将src从8位转换到32位的float型
 cvConvertScale( src, floatsrc, 1.0/255.0, 0 );//归一化之后方能够显示
 //cvConvertScale( src, floatsrc, 1, 0 );
 //cvShowImage("floatsrc",floatsrc);
 //cvWaitKey(-1);

 //将float型图像 从BGR转换到HSV  如果需要转换到其他的颜色空间 那么改变CV_BGR2HSV即可
 //cvCvtColor要求两个参数的类型必须完全相同,所以要转为float型
 cvCvtColor( floatsrc, floathsv, CV_BGR2HSV );

 //将三通道图像 分解成3个单通道图像,H对应的通道时0,S、V对应的通道时1和2
 //cvCvtPixToPlane(picHSV, h_plane, s_plane, v_plane, 0);
 cvSplit( floathsv, floatimgH, floatimgS, floatimgV, NULL);

 cvShowImage( "src", src );
 cvShowImage( "H通道", floatimgH );
 cvShowImage( "S通道", floatimgS );
 cvShowImage( "V通道", floatimgV );
//CV_BGR2HSV

 cvWaitKey( 0 );

 cvReleaseImage( &src );
 cvReleaseImage( &floathsv );
 cvReleaseImage( &floatimgH );
 cvReleaseImage( &floatimgS );
 cvReleaseImage( &floatimgV );

 return 0;
}

 

 

HIS模型反应了人的视觉系统观察彩色的方式,使用非常接近于人对彩色感知的方式来定义彩色。对于图像处理来说,这种模型的优

势在于将颜色信息和灰度信息分开了。色调(Hue)分量是描述一种纯色的颜色属性(如红色,绿色,黄色),饱和度(Saturation)分量是一

种纯色被白光稀释的程度的度量,也可以理解为颜色的浓淡程度(如深红色,淡绿色),亮度(Instensity)分量描述颜色的亮暗程度。这个

模型的建立基于以下两个重要事实:

       I分量与图像的色彩信息无关; 

         H和S分量与人感受颜色的方式紧密相连。

 

HIS颜色空间,如下图所示:

 

 

 

正因为在HIS彩色空间中亮度和色度是互相分离的,所以在彩色图像分割应用中具有较大优势。但是目前很少有硬件设备支持这种彩

色模型,因此需要从其他颜色空间进行转换。

 

以下是几种常用的RGB-HSI转换公式:

 

 

以下是对这几种转换方式的对比

 

 

 

 一个例子:

核心的转换公式:

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;
}