色调饱和度分析

转载 2013年12月05日 21:34:08

HSL颜色处理

 

颜色的规律,个人理解 纯属扯淡 见谅。有些网上抄的 炒剩饭。

知识的海洋很深奥 就像一个神奇的盒子 貌似理解了 可能有些东西再过几年都没能理解

颜色模式

色彩是由物体所反射光的波长来决定的

RGB加色模式

 

RGB为三个能量值 能量值的强弱直观的反映了明与暗 能量值越强越亮

 

而CMY减色模式是跟RGB互逆的一种方式

介质是白的 然后要把颜料涂上去 展现各种色彩。

物体是什么 物体可以反射光 但同时也要吸收一定的能量值,

所以从本质上看把颜料涂上去是一个变暗的过程。CMY正符合了这种原理。

你看RGB三原色交叉部分不正是CMY吗 自然界是多么的神奇。

 

HSB(HSL) 色调饱和度亮度模式

以另外一种不同的理念进行色彩的调配

H色调 0~360 圆环形式 以角度表示

S 饱和度 0~1 之间的小数

L 亮度 0~1 之间的小数

 

什么是纯色

先来看一下windows的拾色器

 

最上头黑色框里面那些颜色 最最最顶端的部分。

是不是感觉他们最鲜艳 最惹眼,嗯 因为他们RGB之间的差异化最大

RGB产生颜色的原理就是RGB三个能量值产生差异化 组合。

所以我们才能看到这些花花绿绿的颜色。

纯色一个特点那就是最大值跟最小值的差是255 ,差异达到最大。

也就是说RGB一定有一个颜色是0 一个是255,否则不能称之为纯色

纯亮度表示就是只能量值没有差异 也指灰度图

 

在液晶屏上以仰视的角度看上面的那幅图  你会看到底部饱和度比较低的部分。TFT屏幕都是这样 可视角度比较差

就像这样的效果:

 

这就是为什么确定RGB为三基色,其实我也不懂 但是看了上图估计也不需要过多的技术解释了吧。

 

纯色的计算

通过观察windows画图板拾色器 就是上面中间那个破图。我们知道 如果饱和度固定为1 亮度固定为0.5 ,那么色调即纯色 纯色即色调。纯色的定义参见上面。

从RGB值的变化规律可以看出色调的变化是连续平缓 首尾相接的 可以看成是一个360度的圆 红色是0度。他的变化规律是:

 

又是看图

这鬼的规律啊 哪有什么规律 花花绿绿好看吧 有点像披萨,说错了 有点像烧饼。

三个规律

1至始至终都有一个值是0 一个值是255。

2整个过程中同一时间总是只有一个值在变

3三个数的全排列 那么总共有6种组合

形象点来说

有一个值在增加 增满过后另一个值再增加 本值再减小 就像这样从左往右 以长江后浪推前浪 前仆后继的方式达到原点  有点像合金弹头游戏里面那种循环的无缝衔接背景 哥们儿你听懂我在说什么了吗。其实我也没搞懂自己在说什么。

 

通过以上我们写段代码枚举一些个纯色

但是首先有一点要明确: 就是那块披萨 就是60度 值是255

我们把色调分成1~360的表现形式,但是RGB是以(255,255,255)的表现形式 那么他们之间怎么对应呢?那就是60度=255

每前进一度scale+=(255*6)*(1/360)

RGB可以表达255*255*255=16581375种颜色 但是他可以表达的纯色只有255*6=1530种

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
public void getHue()
 
{
 
    Graphics gph = Graphics.FromHwnd(this.Handle);
 
    int indx = 0;
 
  
 
    //状态是Up 颜色是绿色 这已经很明显了吧,这正是0度时候推进的趋势
 
    bool high = true;//Up
 
    int scale = 1;//0红色 1绿色 2蓝色
 
    int[] cor = { 255, 0, 0 };//0度角 红色
 
  
 
    Color[] cors = new Color[36];
 
    Color result = Color.FromArgb(cor[0], cor[1], cor[2]);
 
    int curScale = 0;
 
    //推进以便 生成所有色调的颜色表
 
    //这里的30指前进30步 与其他并无关系
 
    //具体前进的范围为: 步数*(255D * 6 * (每步的大小 / 360D))
 
    //注意每步大小的值不能超过60 因为60=255 scale的值域是[0,255]
 
    while (indx < 30)
 
    {
 
        if (high == true)
 
        {
 
            curScale = scale % 3;
 
            cor[curScale] += (int)(255D * 6 * (10 / 360D));
 
        }
 
        else if (high == false)
 
        {
 
            curScale = (scale - 1) % 3;//< 0 ? 0 : (scale - 1) % 3;
 
            cor[curScale] -= (int)(255D * 6 * (10 / 360D));
 
        }
 
  
 
        gph.DrawString(indx.ToString(), new Font(new FontFamily("宋体"), 9), new SolidBrush(Color.Black), 15 * (indx - 1), 50);
 
  
 
        if (cor[curScale] < 0)
 
        {
 
            cor[curScale] = 0;
 
            scale++;
 
            high = !high;
 
            gph.DrawString("▓", new Font(new FontFamily("宋体"), 9), new SolidBrush(Color.Black), 15 * (indx - 1), 90);
 
            continue;
 
        }
 
        else if (cor[curScale] > 255)
 
        {
 
            cor[curScale] = 255;
 
            //scale++;
 
            high = !high;
 
            gph.DrawString("▇ ", new Font(new FontFamily("宋体"), 9), new SolidBrush(Color.Black), 15 * (indx - 1), 90);
 
            continue;
 
        }
 
  
 
        cors[indx] = Color.FromArgb(cor[0], cor[1], cor[2]);
 
        gph.FillRectangle(new SolidBrush(Color.FromArgb(cor[0], cor[1], cor[2])), 15 * indx++, 0, 15, 20);
 
        gph.DrawString(curScale.ToString(), new Font(new FontFamily("宋体"), 9), new SolidBrush(Color.Black), 15 * (indx - 1), 30);
 
  
 
        gph.DrawString(scale.ToString(), new Font(new FontFamily("宋体"), 9), new SolidBrush(Color.Black), 15 * (indx - 1), 70);
 
    }
 
}

 


 

 

增加他们之间的密度 越密过度越平滑

最平滑的状况是 scale+=1

 

越稀 则越粗糙 他的极限是 scale+=(255*6)*(60/360)

即 scale+=255 超过255是无意义的 因为scale的取值是0~255

 

别看几句破代码 调试的时候可是费了我好大劲儿。

 

我们已经能枚举所有色调,实际上他是一个从0度(255,0,0)偏移的过程。但是更改色调有没有一种快速的方式计算呢。难道每确定一个颜色都进行255*6=1530次循环?不会吧。

自然不会 但是最多进行6次循环是必须的 因为网上的公式也要进行6个switch 囧 说明砖家该研究的早就研究过了 咱不可能超过砖家 对吧。

快速计算的方式就是~~~ 加速每次偏转的值 更改偏转的次数 嗯 条件是不少于6次。怎么样换汤不换药吧~~~ 囧

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
public Color getHue(int theta)//角度0~360
 
{
 
    int times = theta / 60, Mod = theta % 60;
 
    bool high = true;
 
    int scale = 1, indx = 0;
 
    int[] cor = { 255, 0, 0 };
 
    int curScale = 0;
 
    while (indx <times )
 
    {
 
        
 
        if (high == true)
 
        {
 
            curScale = scale % 3;
 
            cor[curScale] += 255 ;
 
        }
 
        else if (high == false)
 
        {
 
            curScale = (scale - 1) % 3;
 
            cor[curScale] -= 255 ;
 
        }
 
        
 
        if (cor[curScale] <= 0)
 
        {
 
            cor[curScale] = 0;
 
            scale++;
 
            high = !high;
 
        }
 
        else if (cor[curScale] >= 255)
 
        {
 
            cor[curScale] = 255;
 
            high = !high;
 
        }
 
        indx++;
 
    }
 
  
 
    if (high == true)
 
        cor[scale % 3] += (int)(255D * 6 * (Mod / 360D)) > 255 ? 255 : (int)(255D * 6 * (Mod / 360D));
 
    else if (high == false)
 
        cor[(scale - 1) % 3] -= (int)(255D * 6 * (Mod / 360D)) < 0 ? 0 : (int)(255D * 6 * (Mod / 360D));
 
    
 
    return Color.FromArgb(cor[0], cor[1], cor[2]);
 
}

 


 

 

纯色 饱和度 HSL RGB 他们之间到底有什么奥秘

我们来做个windows画图板那样的拾色器顺便 测试下上面函数的正确性

 

色调有了 然后就是处理饱和度

这个规律比色调那个简单多了

还是通过观察拾色器

R2为处理后的值 R1为处理前的值 :R2=R1+(127-R1)*S

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
public void drawPalette()
 
{
 
    Graphics gph = Graphics.FromHwnd(this.Handle);
 
    //生成颜色表
 
    Color[] cors = new Color[(int)(360D / 11)];
 
    for (int i = 0; i < 360 - 11; i += 11)
 
        cors[(i / 11)] = getHue(i);
 
  
 
    //饱和度处理
 
    float s = 0;//饱和度
 
    for (int i = 0; i < 20; i++)//10行 10个等级的饱和度
 
    {
 
        for (int j = 0; j < cors.Length; j++)//颜色数
 
        {
 
            //计算方式 r'=r+(127-r)s
 
            Color corTmp = Color.FromArgb((int)(cors[j].R + (127 - cors[j].R) * s),
 
                (int)(cors[j].G + (127 - cors[j].G) * s),
 
                (int)(cors[j].B + (127 - cors[j].B) * s));
 
  
 
            gph.FillRectangle(new SolidBrush(corTmp), 15 * j, 15 * i, 15, 15);
 
        }
 
        s += (1f / 20);
 
    }
 
  
 
    //this.BackColor = getHue(59);//10 * i++
 
}

 


 

 

但是有一点要说明下 如果一幅图像的色调降得很低了接近灰度了 说明他的颜色信息已经损失了 再去还原他的色调是有困难的

 

上图虽然已经达到我们要的目的了,但是我们就是爱折腾 就是要让他不一样 ~~

把for循环里面的颜色计算代码改成这样:

1
2
3
4
5
Color corTmp = Color.FromArgb((int)(cors[j].R + (255 - cors[j].R) * s),
 
                (int)(cors[j].G + (0 - cors[j].G) * s),
 
                (int)(cors[j].B + (0 - cors[j].B) * s));

 


 

 

这反映了一个现象 虽然某些颜色看上去不是红色

但是他三基色的组成里红色成分越多被侵蚀就越严重 红色成分越少越抗侵蚀,看 多么的完美 这与photoshop里的调色常识不谋而合。

 

说道他们的奥秘嘛 ,咳咳咳 。其实也没什么奥秘

总结下:

饱和度度/彩度 指颜色信息 降低容易 降低到一定程度 还原可就难了。

色调其实是指饱和度为1 亮度为0.5的 这种特例情况 也指纯色。纯色定义见前面

亮度、饱和度的调整 都是以纯色为初始的 所以找出纯色很关键。

从纯色算法以及能量值差异原理可以知道 色调主要跟RGB里的最大值最小值相关 他们之间的差异反映了色调 他们跟另外一个值进行调配 反映了饱和度。最大值的强弱反映了可见度 即亮度。

何以见得呢。

调用.net自带的函数得到饱和度

Color c = Color.FromArgb(210, 23, 232);

MessageBox.Show(c.GetSaturation().ToString());

输出结果是0.8196079

你可以试验一下把210改到(23,232)范围内的任意值 最后输出的结果都是一样的。

 

 

网上写的那些公式

 

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
public Color getRGBfromHSV(double _h, double _s, double _v)//色相 饱和度 亮度
 
{
 
    int h1 = (int)(_h / 60 % 6);//确定色调在色带中的位置 其实就是确定色调
 
    double f = _h / 60 - h1;
 
//确定最大值最小值 亮度
 
    double p = (_v * (1 - _s));
 
    double q = (_v * (1 - f * _s));
 
    double t = (_v * (1 - (1 - f) * _s));
 
  
 
Color cor;
 
//按色调对这些值进行分配
 
    switch (h1)
 
    {
 
        case 0:
 
            cor = Color.FromArgb((int)(_v * 255), (int)(t * 255), (int)(p * 255));
 
            break;
 
        case 1:
 
            cor = Color.FromArgb((int)(q * 255), (int)(_v * 255), (int)(p * 255));
 
            break;
 
        case 2:
 
            cor = Color.FromArgb((int)(p * 255), (int)(_v * 255), (int)(t * 255));
 
            break;
 
        case 3:
 
            cor = Color.FromArgb((int)(p * 255), (int)(q * 255), (int)(_v * 255));
 
            break;
 
        case 4:
 
            cor = Color.FromArgb((int)(t * 255), (int)(p * 255), (int)(_v * 255));
 
            break;
 
        default:
 
            cor = Color.FromArgb((int)(_v * 255), (int)(p * 255), (int)(q * 255));
 
            break;
 
    }
 
    return cor;
 
}

 


 

我照着公式的原理写了段代码 神了 确实可以用 但是说实话我真没搞太懂。看问题要研究他的本质 不能老抄别人的。

 

 另一段改自C++的转换代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
public Color GetRGBColorFromHSV(double _h, double _s, double _v)//色相 饱和度 亮度
{
    double r, g, b;
 
    if (_s == 0)//如果饱和度为0则是灰度图 rgb三个值都将等于亮度
    {
        r = _v;
        g = _v;
        b = _v;
    }
    else
    {
        double[] dp = new double[3];
        double xh = _h * 6;//色相x6 ?
        if (xh == 6) xh = 0;//色环是一个360度的圆环 如果超过最大值则变为0
        //int i = (int)(Math.Floor(xh) + 0.1);//色相x6取整数+0.1 ?
        int i = (int)xh;
        dp[0] = 1; dp[2] = 0; xh -= i;
        if (i % 2 != 0)// (i&1) //如果i是奇数
            dp[1] = 1 - xh;
        else
            dp[1] = xh;
 
        //处理色调
        for (int n = 0; n < 3; ++n)
        {
            dp[n] = (((dp[n]) - 0.5) * (_s) + 0.5);
            //SATFORMAT(dp[n], _s);
        }
 
 
        //处理亮度
        if (_v == 0.5) { }
        else if (_v < 0.5)
        {
            if (_v < 0) _v = 0;
            for (int n = 0; n < 3; ++n)
                dp[n] = ((dp[n]) * (_v) * 2);
            //BLACKFORMAT(dp[n], _v)
        }
        else
        {
            if (_v > 1) _v = 1;
            for (int n = 0; n < 3; ++n)
                dp[n] = (1 - (1 - (dp[n])) * (1 - (_v)) * 2);
            //WHITEFORMAT(dp[n], _v);
        }
 
 
        //三个元素的全排列 ?
 
        switch (i)
        {
            case 0:
                r = dp[0]; g = dp[1]; b = dp[2];
                break;
            case 1:
                r = dp[1]; g = dp[0]; b = dp[2];
                break;
            case 2:
                r = dp[2]; g = dp[0]; b = dp[1];
                break;
            case 3:
                r = dp[2]; g = dp[1]; b = dp[0];
                break;
            case 4:
                r = dp[1]; g = dp[2]; b = dp[0];
                break;
            default:
                r = dp[0]; g = dp[2]; b = dp[1];
                break;
        }
         
 
    }
 
    //return ((int)(b * 255 + .5) << 16) + ((int)(g * 255 + .5) << 8) + (int)(r * 255 + .5);
    Color cor = Color.FromArgb((int)(r * 255 + 0.5), ((int)(g * 255 + 0.5)), ((int)(b * 255 + 0.5)));
    return cor;
}

 调用上面的函数来整点花哨的效果 一个圆环形的色带:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
Class1 c2 = new Class1();
Graphics gph = Graphics.FromHwnd(this.Handle);
 
Color[] cors = new Color[360];
 
gph.TranslateTransform(200, 200);
 
System.Drawing.Drawing2D.GraphicsPath ph = new System.Drawing.Drawing2D.GraphicsPath();
ph.AddEllipse(-100, -100, 200, 200);
ph.AddEllipse(-80, -80, 160, 160);
 
gph.SetClip(ph, System.Drawing.Drawing2D.CombineMode.Intersect);
for (int k = 0; k < 360; k++)
{
    cors[k] = c2.GetRGBColorFromHSV(1D/360 * k, 1D, 0.5D);
 
    gph.RotateTransform(1);
    gph.DrawLine(new Pen(new SolidBrush(cors[k]), 2), 0, 0, 100, 0);
 
}
 
gph.FillEllipse(new SolidBrush(Color.Transparent), -80, -80, 160, 160);

 

 

亮度部分我暂时不想写了 累了 下次补上。

亮度0.5是个分水岭 高于0.5的时候RGB三个值都是增的趋势 低于0.5时是减的趋势

 原文链接:http://www.cnblogs.com/assassinx/archive/2012/10/04/2711217.html

相关文章推荐

直方图、直方图的均衡化作用及图像的信噪比(SNR)

1.什么是直方图? 直方图(Histogram)又称质量分布图。是一种统计报告图,由一系列高度不等的纵向条纹或线段表示数据分布的情况。 一般用横轴表示数据类型,纵轴表示分布情况。 ...

彩色图像颜色和饱和度调整办法

简介   本篇主要是对,摄像头模组吐出的rgb图像做效果参数优化:调整图像颜色和饱和度方面的记录。 相关颜色空间转换   主要是在YCrCb格式下完成,所以首先第一步就是需要将图像从RGB...

android开发 修改图片色调饱和度、色相、亮度处理

处理后: 下面贴代码: 一、图片处理层: package com.jacp.tone.view; import java.util.ArrayList; import and...
  • nnmmbb
  • nnmmbb
  • 2016年03月04日 17:45
  • 698

android图像处理系列之三--图片色调饱和度、色相、亮度处理

原图: 处理后: 下面贴代码: 一、图片处理层: package com.jacp.tone.view; import java.util.ArrayList; impor...
  • SJF0115
  • SJF0115
  • 2012年02月17日 11:08
  • 15783

在cocos2dx 中添加自定义 shaders --- 改变 HSL 色调,饱和度, 亮度

步骤1. 准备 shaders文件- ccShad_Hsl.h /*******************************************************************...

PhotoShop算法实现--色调/饱和度调整(四)

PhotoShop色彩平衡(偏色校正)(一) kezunhai@gmail.com http://blog.csdn.net/kezunhai           图像色调/饱和度调节,就是让用户...

生成按色调饱和度排列的BMP位图

1、首先需要了解BMP文件的结构 生成的BMP为32位真彩色图像(一个像素4个字节BGRA),所以不需要调色板。 2、还需要了解的是HSB和RGB的转换公式 3、程序代码 1、bm...

色调、亮度和饱和度

基本资料 定义    彩色(colours),指除消色以外的各种颜色,各有不同的色调、亮度和饱和度。   彩色,可分为无彩色和有彩色两大类。前者如黑、白。灰,后者如红、黄.蓝等七彩。   有彩色就是...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:色调饱和度分析
举报原因:
原因补充:

(最多只允许输入30个字)