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