RBG与HSB转换

RGB三原色是基于人肉眼对光线的生理作用。人眼内有三种椎状体“对这三种光线频率所能感受的带宽最大,也能独立刺激这三种颜色的受光体”,因此RGB称为三原色。比如,黄色波长的光对人眼的刺激效果,和红色与绿色同时刺激人眼相同,所以,对人来说R+G=yellow,即(255,255,0).

相对于RGB,HSB(也叫HSV)模式更便于描述人眼对与颜色的感觉。如图的HSB椎形坐标,横截面从下往上亮度值从0%到100%递增;横截面的中心点是灰色的,随着半径增大,饱和度从0增大到100%;色相取值0°~360°,代表截面上红、黄、绿、青、蓝、粉红的颜色变化。
 

HSB坐标系最顶部的截面最外圈,饱和度和亮度都是100%,随着色相从0°到360°变化,RGB值的变化如上图,红色、绿色、蓝色分别位于0°、120°、240°;0°到60°之间绿色分量均匀增加,60°到120°之间红色分量均匀减少,以此类推。将  这个演示中的S和B调到100%,调整H的值观察RGB的变换就能发现这个规律。这时RGB中最大值一定是255(因为亮度为100%),最小值一定是0(如果不是,比如RGB=(10,20,255),可以看成在(0,10,255)分量的基础上加上一个灰色分量(10,10,10),加灰色后饱和度就不是100%了)。
     


HSB转RGB

要算出(h,s,b)对应的(r,g,b),可以分3步,以HSB=(130°,50%,80%)为例说明:

1、先算出(h,100%,100%)对应的(r',g',b')。
先固定色相,色环图上色相H∈[-60°,60°]时红色分量最大,H∈[60°,180°]时绿色分量最大,H∈[180°,300°]时蓝色分量最大,此时B=100%,所以RGB的最大分量为255。
先算出HSB=(130°,100%,100%)对应的(r',g',b'):H=130°,在[60°,180°]区间,所以绿色分量为g'=255,红色分量r'=0;进一步地,130°处于[120°,180°],在这60°的区间上,色环上蓝色分量对应地从0递增到255,所以b'=(130°-120°)/60°*255=43。所以(r',g',b')=(0,255,43)

2、固定色相后再调整饱和度,算出(h,s,100%)对应的(r'',g'',b'')。
在亮度B=100%时,从  演示中发现,饱和度S降低,即“不饱和度”(1-S)升高,会使得RGB与最大值255相差的部分对应增大,RGB三个分量越趋于相同就使得图像越灰。所以,
r''=r'+(255-r')*(1-S) .................................... ①
g''和b''用同样的方法求出。当然,r'g'b'中的最大值不会变化。(r'',g'',b'')=(128,255,149)

3、最后算出(h,s,b)对应的(r,g,b)。
最后调整亮度,只要依照亮度值的百分比缩小就行了,(r,g,b)=(r'',g'',b'')*80%=(102,204,119)就是HSB=(130°,50%,80%)对应的RGB
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public float [] hsb2rgb( float [] hsb) {
     float [] rgb= new float [ 3 ];     
     //先令饱和度和亮度为100%,调节色相h
     for ( int offset= 240 ,i= 0 ;i< 3 ;i++,offset-= 120 ) {
         //算出色相h的值和三个区域中心点(即0°,120°和240°)相差多少,然后根据坐标图按分段函数算出rgb。但因为色环展开后,红色区域的中心点是0°同时也是360°,不好算,索性将三个区域的中心点都向右平移到240°再计算比较方便
         float x=Math.abs((hsb[ 0 ]+offset)% 360 - 240 );
         //如果相差小于60°则为255
         if (x<= 60 ) rgb[i]= 255 ;
         //如果相差在60°和120°之间,
         else if ( 60 <x && x< 120 ) rgb[i]=(( 1 -(x- 60 )/ 60 )* 255 );
         //如果相差大于120°则为0
         else rgb[i]= 0 ;
     }
     //在调节饱和度s
     for ( int i= 0 ;i< 3 ;i++)
         rgb[i]+=( 255 -rgb[i])*( 1 -hsb[ 1 ]);
     //最后调节亮度b
     for ( int i= 0 ;i< 3 ;i++)
         rgb[i]*=hsb[ 2 ];
     return rgb;
}



RGB转HSB

这个从HSB转RGB倒推回去就会很容易知道亮度和饱和度的算法

亮度:B=max(R,G,B)/255

饱和度:1-S=min(R,G,B)/255,所以饱和度S=1-min(R,G,B)/255

色相:先算出(r,g,b)对应的饱和度和亮度均为100%时的(r',g',b'),然后既可以根据色环的规律求出色相了。
r'、g'、b'的序号用0、1、2表示,max、min、mid分别是r'g'b'中最大值、最小值和中间值;设maxIndex、minIndex、midIndex分别是r'g'b'中最大值、最小值和中间值的序号。首先将色相h定位到(maxIndex*120°)然后根据中间值算出偏移量(mid/255*60°),既
h=(maxIndex*120°)±(mid/255*60°)....................................②
关于②中的±号的确定方法:将当0、1、2顺时针写成一圈,当midIndex在maxIndex的顺时针方向(既minIndex在maxIndex的逆时针方向)时取正,否则取负,原因看色环就能知道。所以±号可以用(maxIndex-minIndex+3)%3是否等于1来判断,是取正,否则取负。②式可以改写成:
h=(maxIndex*120°)+((maxIndex-minIndex+3)%3==1?1:-1)*(mid/255*60°)....................................③
以(r,g,b)=(102,204,85)为例,前两步已求出亮度为80%,饱和度为50%。当亮度和饱和度均为100%而色相不变时,最大值g'一定是255;最小值r'一定是0;中间值b'按照上面HSB转RGB的后两步反推回去,先得出亮度为100%时b''=102/80%=128,再由①式得出饱和度为100%时b'=43。
因为g'=255最大,先定位到色环上的120°的位置,因为蓝色分量不为0,所以要从120°顺时针偏移b'/255*60°=10°,即H=130°

?
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
public float [] rgb2hsb( float [] rgb) {
     float [] hsb = new float [ 3 ];
     float [] rearranged = rgb.clone();      
     int maxIndex = 0 ,minIndex = 0 ;
     float tmp;     
     //将rgb的值从小到大排列,存在rearranged数组里
     for ( int i= 0 ;i< 2 ;i++) {
         for ( int j= 0 ;j< 2 -i;j++)
             if (rearranged[j]>rearranged[j+ 1 ]) {
                 tmp=rearranged[j+ 1 ];
                 rearranged[j+ 1 ]=rearranged[j];
                 rearranged[j]=tmp;
             }              
     }
     //rgb的下标分别为0、1、2,maxIndex和minIndex用于存储rgb中最大最小值的下标
     for ( int i= 0 ;i< 3 ;i++) {
         if (rearranged[ 0 ]==rgb[i]) minIndex=i;
         if (rearranged[ 2 ]==rgb[i]) maxIndex=i;
     }
     //算出亮度
     hsb[ 2 ]=rearranged[ 2 ]/ 255 .0f;
     //算出饱和度
     hsb[ 1 ]= 1 -rearranged[ 0 ]/rearranged[ 2 ];
     //算出色相
     hsb[ 0 ]=maxIndex* 120 + 60 * (rearranged[ 1 ]/hsb[ 1 ]/rearranged[ 2 ]+( 1 - 1 /hsb[ 1 ])) *((maxIndex-minIndex+ 3 )% 3 == 1 ? 1 :- 1 );
     //防止色相为负值
     hsb[ 0 ]=(hsb[ 0 ]+ 360 )% 360 ;
     return hsb;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值