色调、亮度和饱和度

  http://www.telecarto.com/content/maincontent/multimediadesign/ColorPhoto/cha_512.htm

从人的视觉系统看,色彩可用色调、饱和度和亮度来描述。人眼看到的任一彩色光都是这三个特性的综合效果,这三个特性可以说是色彩的三要素,其中色调与光波的波长有直接关系,亮度和饱和度与光波的幅度有关。

一、色调与色相:
    绘画中要求有固定的色彩感觉,有统一的色调,否则难以表现画面的情调和主题。例如我们说一幅画具红色调,是指它在色彩上总体偏红。计算机在图像处理上采用数字化,可以非常精确地表现色彩的变化,色调是相对连续变化的。用一个园环来表现色谱的变化,就构成了一个色彩连续变化的色环

colorring.GIF (23730 bytes)

二、亮度与明度:
  1、同一物体因受光不同会产生明度上的变化

apple.gif (15966 bytes)
  2、不同颜色的光,强度相同时照射同一物体也会产生不同的亮度感觉。

image 

明度也可以说是指各种纯正的色彩相互比较所产生的明暗差别。在纯正光谱中,黄色的明度最高,显得最亮;其次是橙、绿;再其次是红、蓝;紫色明度最低,显得最暗。

三、饱和度与纯度:
淡色的饱和度比浓色要低一些
饱和度还和亮度有关,同一色调越亮或越暗越不纯
    饱和度越高,色彩越艳丽、越鲜明突出,越能发挥其色彩的固有特性。但饱和度高的色彩容易让人感到单调刺眼。饱和度低,色感比较柔和协调,可混色太杂则容易让人感觉浑浊,色调显得灰暗。

 

对于同一色调的彩色光,饱和度越深,颜色越鲜明或说越纯,相反则越淡。

light.GIF (10564 bytes)

在饱和的彩色光中增加白光的成分,相当于增加了光能,因而变得更亮了,但是它的饱和度却降低了。若增加黑色光的成分,相当于降低了光能,因而变得更暗,其饱和度也降低了。

 

颜色空间

 

RGB

计算机色彩显示器显示色彩的原理与彩色电视机一样,都是采用R、G、B相加混色的原理,通过发射出三种不同强度的电子束,使屏幕内侧覆盖的红、绿、蓝磷光材料发光而产生色彩的。这种色彩的表示方法称为RGB色彩空间表示。在多媒体计算机技术中,用的最多的是RGB色彩空间表示。
    根据三基色原理,用基色光单位来表示光的量,则在RGB色彩空间,任意色光F都可以用R、G、B三色不同分量的相加混合而成:

F=r [ R ] + g [ G ] + b [ B ]

 

fig5-4.gif (11080 bytes)    RGB色彩空间还可以用一个三维的立方体来描述。

    我们可知自然界中任何一种色光都可由R、G、B三基色按不同的比例相加混合而成,当三基色分量都为0(最弱)时混合为黑色光;当三基色分量都为k(最强)时混合为白色光。任一色彩F是这个立方体坐标中的一点,调整三色系数r、g、b中的任一系数都会改变F的坐标值,也即改变了F的色值。RGB色彩空间采用物理三基色表示,因而物理意义很清楚,适合彩色显象管工作。然而这一体制并不适应人的视觉特点。因而,产生了其它不同的色彩空间表示法。

HSI

HSI色彩空间是从人的视觉系统出发,用色调(Hue)、色饱和度(Saturation或Chroma)和亮度(Intensity或Brightness)来描述色彩。HSI色彩空间可以用一个圆锥空间模型来描述。用这种描述HIS色彩空间的圆锥模型相当复杂,但确能把色调、亮度和色饱和度的变化情形表现得很清楚。

conea.GIF (19468 bytes)coneb.GIF (10739 bytes)conec.GIF (4108 bytes)coned.GIF (23036 bytes)conee.GIF (17268 bytes)

(A)HSI圆锥空间模型

(B)线条示意图:圆锥上亮度、色度和饱和度的关系。

(C)纵轴表示亮度:亮度值是沿着圆锥的轴线度量的,沿着圆锥轴线上的点表示完全不饱和的颜色,按照不同的灰度等级,最亮点为纯白色、最暗点为纯黑色。

(D)圆锥纵切面:描述了同一色调的不同亮度和饱和度关系。

(E)圆锥横切面:色调H为绕着圆锥截面度量的色环,圆周上的颜色为完全饱和的纯色,色饱和度为穿过中心的半径横轴。

 

    通常把色调和饱和度通称为色度,用来表示颜色的类别与深浅程度。由于人的视觉对亮度的敏感程度远强于对颜色浓淡的敏感程度,为了便于色彩处理和识别,人的视觉系统经常采用HSI色彩空间,它比RGB色彩空间更符合人的视觉特性。在图像处理和计算机视觉中大量算法都可在HSI色彩空间中方便地使用,它们可以分开处理而且是相互独立的。因此,在HSI色彩空间可以大大简化图像分析和处理的工作量。

    HSI色彩空间和RGB色彩空间只是同一物理量的不同表示法,因而它们之间存在着转换关系,如公式所示:

其中

 

YUV(Lab)色彩空间

   在现代彩色电视系统中,通常采用三管彩色摄像机或彩色CCD(点耦合器件)摄像机,它把摄得的彩色图像信号,经分色、分别放大校正得到RGB,再经过矩阵变换电路得到亮度信号Y和两个色差信号R-Y、B-Y,最后发送端将亮度和色差三个信号分别进行编码,用同一信道发送出去。这就是我们常用的YUV色彩空间。

    采用YUV色彩空间的重要性是它的亮度信号Y和色度信号U、V是分离的。如果只有Y信号分量而没有U、V分量,那么这样表示的图就是黑白灰度图。彩色电视采用YUV空间正是为了用亮度信号Y解决彩色电视机与黑白电视机的兼容问题,使黑白电视机也能接收彩色信号。

    根据美国国家电视制式委员会,NTSC制式的标准,当白光的亮度用Y来表示时,它和红、绿、蓝三色光的关系可用如下式的方程描述:

Y=0.3 R + 0.59 G + 0.11B

    这就是常用的亮度公式。色差U、V是由B-Y、R-Y按不同比例压缩而成的。YUV色彩空间与RGB色彩空间的转换关系如下:

    如果要由YUV空间转化成RGB空间,只要进行相反的逆运算即可。

    与YUV色彩空间类似的还有Lab色彩空间,它也是用亮度和色差来描述色彩分量,其中L为亮度、a和b分别为各色差分量。

 

CMY色彩空间

    彩色印刷或彩色打印的纸张是不能发射光线的,因而印刷机或彩色打印机就只能使用一些能够吸收特定的光波而反射其它光波的油墨或颜料。油墨或颜料的三基色是青(Cyan)、品红(Magenta)和黄(Yellow),简称为CMY。青色对应蓝绿色;品红对应紫红色。理论上说,任何一种由颜料表现的色彩都可以用这三种基色按不同的比例混合而成,这种色彩表示方法称CMY色彩空间表示法。彩色打印机和彩色印刷系统都采用CMY色彩空间。
    由CMY混合的色彩又称为相减混色。因为CMY空间正好与RGB空间互补,也即用白色减去RGB空间中的某一色彩值就等于同样色彩在CMY空间中的值。RGB空间与CMY空间的互补关系如下表所示:

image

    根据这个原理,很容易把RGB空间转换成CMY空间。由于彩色墨水和颜料的化学特性,用等量的CMY三基色得到的黑色不是真正的黑色,因此在印刷术中常加一种真正的黑色(black ink),所以CMY又写成CMYK。
    实际应用中,一幅图像在计算机中用RGB空间显示;用RGB或SHI空间编辑处理;打印输出时要转换成CMY空间;如果要印刷,则要转换成CMYK四幅印刷分色图,用于套印彩色印刷品。

 

这里还有个颜色转化公式的网址:

http://www.easyrgb.com/index.php?X=MATH#text18

 

所以自己如果要调节饱和度就要把RGB空间转为HSI,计算后再转回来。

原图:

image

改变H分量,颜色发生反转,这个是逆时针 ,参见第一张转盘图。

image

改变S分量,越小说明灰度越大越不纯。

image

改变L分量,是控制亮度的,不过感觉算法缺陷了,PS的效果很好。还需要努力了

image

 

代码主要写了HSL处理类,算法是从上面那个网址抄的,其实还有很多以后慢慢用:

 

public class HSL
{
    private int _r;
    public int R
    {
        get { return _r; }
        set
        {
            if (value > 255)
                _r = 255;
            else if (value < 0)
                _r = 0;
            else
                _r = value;
        }
    }

    private int _g;
    public int G
    {
        get { return _g; }
        set
        {
            if (value > 255)
                _g = 255;
            else if (value < 0)
                _g = 0;
            else
                _g = value;
        }
    }

    private int _b;
    public int B
    {
        get { return _b; }
        set
        {
            if (value > 255)
                _b = 255;
            else if (value < 0)
                _b = 0;
            else
                _b = value;
        }
    }

    private double _h;
    public double H
    {
        get { return _h; }
        set
        {
            if (value < 0)
            {
                _h = value + 1;
            }
            else if (_h > 1)
            {
                _h = 1 - value;
            }
            else
            {
                _h = value;
            }
        }
    }

    private double _s;
    public double S
    {
        get { return _s; }
        set
        {
            if (value > 1)
                _s = 1;
            else if (value < 0)
                _s = 0;
            else
                _s = value;

        }
    }

    private double _l;
    public double L
    {
        get { return _l; }
        set
        {
            if (value > 1)
                _l = 1;
            else if (value < 0)
                _l = 0;
            else
                _l = value;
        }
    }

    public HSL(double hue, double saturation, double lightness)
    {
        H = hue;
        S = saturation;
        L = lightness;
    }

    public HSL(int red, int green, int blue)
    {
        R = red;
        G = green;
        B = blue;

        SetHSL();
    }

    protected double Min(double num1, double num2, double num3)
    {
        return Math.Min(Math.Min(num1, num2), num3);
    }

    protected double Max(double num1, double num2, double num3)
    {
        return Math.Max(Math.Max(num1, num2), num3);
    }

    public void GetRGB()
    {
        if (S == 0)                      //HSL from 0 to 1
        {
            R = (int)(L * 255);              //RGB results from 0 to 255,注意小数int的地方,查了5个小时找到的
            G = (int)(L * 255);
            B = (int)(L * 255);
        }
        else
        {
            double var_2;
            if (L < 0.5)
                var_2 = L * (1 + S);
            else
                var_2 = (L + S) - (S * L);

            double var_1 = 2 * L - var_2;

            R = (int)(255 * HueToRGB(var_1, var_2, H + (1/3.0)));
            G = (int)(255 * HueToRGB(var_1, var_2, H));
            B = (int)(255 * HueToRGB(var_1, var_2, H - (1 / 3.0)));
        }
    }

    private double HueToRGB(double v1, double v2, double vH)             //Function Hue_2_RGB
    {
        if (vH < 0) vH += 1;
        if (vH > 1) vH -= 1;
        if ((6 * vH) < 1) return (v1 + (v2 - v1) * 6.0 * vH);
        if ((2 * vH) < 1) return (v2);
        if ((3 * vH) < 2) return (v1 + (v2 - v1) * ((2 / 3.0) - vH) * 6);
        return (v1);
    }

    public void SetHSL()
    {
        double var_R = (R / 255.0);                     //RGB from 0 to 255
        double var_G = (G / 255.0);
        double var_B = (B / 255.0);

        double var_Min = Min(var_R, var_G, var_B);    //Min. value of RGB
        double var_Max = Max(var_R, var_G, var_B);    //Max. value of RGB
        double del_Max = var_Max - var_Min;             //Delta RGB value

        L = (var_Max + var_Min) / 2.0;

        if (del_Max == 0)                     //This is a gray, no chroma...
        {
            H = 0;                                //HSL results from 0 to 1
            S = 0.0;
        }
        else                                    //Chromatic data...
        {
            if (L < 0.5)
                S = del_Max / (var_Max + var_Min);
            else
                S = del_Max / (2.0 - var_Max - var_Min);

            double del_R = (((var_Max - var_R) / 6.0) + (del_Max / 2.0)) / del_Max;
            double del_G = (((var_Max - var_G) / 6.0) + (del_Max / 2.0)) / del_Max;
            double del_B = (((var_Max - var_B) / 6.0) + (del_Max / 2.0)) / del_Max;

            if (var_R == var_Max)
                _h = del_B - del_G;
            else
                if (var_G == var_Max) _h = (1 / 3.0) + del_R - del_B;
                else
                    _h = (2 / 3.0) + del_G - del_R;

            if (_h < 0)
                _h += 1;
            if (_h > 1)
                _h -= 1;

        }
    }

}

 

调用:注意参数的范围都是 [-1,1]

public static Bitmap SetHSL(Bitmap b, double hue, double saturation, double lightness)
{
    HSL hsl = new HSL(0, 0.0, 0.0);
    ColorDelegate colorDelegate = (ref int red, ref int green, ref int blue) =>
    {
        hsl.R = red;
        hsl.G = green;
        hsl.B = blue;
        hsl.SetHSL();

        hsl.H += hue;
        hsl.S += saturation;
        hsl.L += lightness;

        hsl.GetRGB();

        red = hsl.R;
        green = hsl.G;
        blue = hsl.B;
    };

    return LoopPixel(b, colorDelegate);
}

 

最后拷贝图像还学了招,亏我之前还写真的复制了份,这样任何类型的图片都可以处理了。

new Bitmap(_changeImage)
private static int GetPerPixelFormatLength(PixelFormat pixelFormat)
{
    int length = 0;
    switch (pixelFormat)
    {
        case PixelFormat.Format24bppRgb:
            length = 3;
            break;
        case PixelFormat.Format32bppRgb:
        case PixelFormat.Format32bppArgb:
            length = 4;
            break;
    }

    return length;
}
//Bytes Per Pixel
           int BPP = GetPerPixelFormatLength(b.PixelFormat);
就到这里吧,睡觉睡觉。

posted on 2010-01-26 01:12  六道众生  阅读(12508)  评论(0编辑  收藏  举报

导航