服务热线:
您当前的位置:首页 > 世纪星月刊 > 第1期 (2011年1月)

【技术前沿】小小程序员的色彩空间

2011/9/30 14:41:35

 

作者:研发部 杨盛海

  假设有这么一个问题,一个油气田公司的报表,想表示在过去的一年里,十二个月中每个月的原油产量和天然气产量的趋势,怎么用一个简单的图表表示呢?大家可能会立刻想到,把每个月的产量在图表上标一个点,然后把这些点连起来,就成了趋势线,原油产量连成一条线,天然气产量连成另一条线。那么如何区分哪条线是原油,哪条线是天然气呢,答案也是很容易想到的,就是用红颜色的线表示原油,蓝颜色的线表示天然气。或者说红颜色的线表示原油,绿颜色的线表示天然气。到目前为止,用户和程序员想的都是一样的。

 

  这时候,用户说了,我想用黄色的线表示原油,用紫色的线表示天然气,这样不行?程序员稍稍迟疑了一下说:“行是行,只不过不如红、绿、蓝三种颜色来得方便。”稍微有点色彩方面知识的人都知道,红、绿、蓝这三种颜色是最纯净的三种色,非常鲜艳,称为三原色,其它的颜色都是由这几种颜色混和而成。从程序员的角度来看,产生所有颜色只要用RGB(r, g, b)这个函数就行,这里r、g、b这三个参数表示这三种色的量,最大为255,最小为0,比如想产生红色,只要用RGB(255,0,0)就行了,同理,绿色就是RGB(0,255,0),蓝色就是RGB(0,0,255),那么黄色是怎么出来的呢,这个问题困扰了笔者很长一段时间,直到会用RGB函数以后才试出来的,是RGB(255,255,0),也就是红色和绿色一混合就出来了。同样紫色也是如此,这得尝试一段时间才能得出结果,当然,色彩学得比较好的不用试也知道。这就是前面的用户说要画黄色线和紫色线时程序员比较迟疑的原因,毕竟不是三原色之一,还得试出来。

 

  在程序员纠结画什么颜色好时,用户又说话:“我想把成品油的产量也加上去,颜色随你定。”程序员这才放心,三条线正好使用三原色,正合适。当程序员正想准备实施编码时,用户又想起什么事来,对程序员说:“且慢!你能不能把成品油分类,像重油、轻油,还有石蜡这样的副产品也加上去。”程序员稍稍思索一下,数了数:原油、天然气、成品油、重油、轻油、石蜡。共6种,三原色占去三种、三原色中两两混合又是三种,也还合适。

 

  这时候用户出去喝咖啡,临走时对程序员说:“你先假设几个数,把图表画出来看看效果。”程序员继续编码,不一会儿图表出来,图表中的线条花花绿绿的非常好看,连程序员自己都很陶醉,等待用户过来检查成果。

 

  用户回来之后看着五颜色六色的图表也觉得很不错,忽然他又想起一个问题:“效果很不错,不过,我想把成品油再细分一下,不要重油、轻油,加上汽油、煤油、柴油、石蜡和沥青,天然气……,也细分一下,甲烷和乙炔,先暂时分这两种,以后可能分得更细,要不然把所有的这些成分做在界面上,设置成可以选择,我选哪个,就画哪个,全选上就全都画……”用户喝完咖啡,好像补充了能量,一下子想出更多需求,滔滔不绝向程序员表述。程序员一开始还频频点头,一边点头一边说行、没问题,到最后,却陷入沉思之中。

 

  我想大家都猜到程序员在想什么,如果只是规定好要画几条线,哪怕是再多的线也没问题,可以分颜色不断尝试。我们可以在Windows带的画图板中挨个测试,哪种颜色好看就用哪个,只要任何两种颜色之间不重复就行。当用户说完最后一条需求,程序员非常纠结,因为颜色不能再定死,用户选择了几种,程序员就得画上几条线,线条的数量都是不固定,程序员怎么能再试完再画呢?

 

  这就出现这样一个问题,如何生成指定数量的不同的颜色?另外再加一个要求:颜色要尽量好看。也就是说生成的颜色要符合两个要求,首要是区分开,其次是好看。

 

  程序员最初这样设想:把RGB三原色的量分成几份,每次加一份。比如要画条8条不同的线,那么,就把255分成8份,第一条线是RGB(0,0,0),第二条线是RGB(255/8,255/8,255/8),这里面的三个参数都要取整。第三条线就是(255*2/8,255*2/8,255*2/8),以此类推。这样实现起来是相当简单,可出来的效果却非常不令人满意,等程序员一画出来,当时就傻眼,画出来的线既不符合第一种要求,也不符合第二条要求。第一条是黑色,后面的依次灰度下降,全都是灰灰的线条,完全没有美感可言,而且相邻的两条线几乎分不出来,这个算法完全失败。

 

  程序员在第一种算法的基础上,吸取教训,产生第二种算法:比如要生成9种线,那么在色彩的每一个分量上都分别累加,前三个色彩在红色分量上区分,如第一种颜色为RGB(255/3,0,0),第二种:RGB(255*2/3,0,0),第三种(255,0,0),中间三种色彩在绿色分量上区分,后三种色彩在蓝色分量上区分。这种算法在结果上与前一种算法相比有了长足的进步,美观度有所提升,而且在量小的情况下也容易互相区分。但其缺点也显而易见,只有三种基本颜色,全靠其灰度来区分,当数量稍大时,两种颜色很容易辨别不出来。

 

  以上两种算法犯的毛病有一个共同点,就是没有充分利用色彩空间。何为色彩空间?就是将三个原色作为三个坐标轴,形成的一个正立方体,由此可见,原点的颜色为RGB(0,0,0),即为黑色,而与原点相对的那个点的颜色为RGB(255,255,255),即为白色。第一种算法产生的颜色都集中在从黑到白的这条直线上,自然就都是灰色。第二种算法都集中在三个坐标轴上,所以可选择的颜色相当局促。那么我们最终要做的就是充分利用这个色彩空间。

 

  程序员经过长久的思考,想出了以下的方法:为简单起见,不排除产生灰色的线,但要排除白色,因为在图表上显示出不出来,对于给定的数,把这个数均匀地分配到色彩空间中,比如对于9这个数,把红色分为2份,这就能产生3个不同的量,0,255/2,255,把绿色分为一份,可以产生2个不同的量,0和255,把蓝色分为一份,可以产生2个不同的量,0和255,把这其中的每一种做组合,就会产生12种组合(3*2*2),再去掉白色,还有11种,最后两种放弃不用,完全可行。这样,我们就要充分利用色彩空间。

 

  有人问了,你这红色分2份,绿色分1份,蓝色分1份,这份数是怎么出来?这个问题正是算法最关键的地方,如何均分色彩空间。我们力求均匀,也就是尽量让每个分量分的份数一样,比如都分2份,但是份数一样也会产生浪费,比如产生9种颜色,每个分量都分一份,产生2*2*2种颜色,不够,都分两份,就会产生3*3*3种颜色,产生27种,大部分用不上,太浪费。于是我们把每个分量一点点地往上加,比如都分一份不行,那么把其中一个分量加1,也就是红色分成2份,这下就够了。如何找到这刚好不够用的每份都分为1份,那就只好对所要求的颜色数量进行开立方,比如9开立方就是2点几,再取整,就是2,2的话就是分成一份,3的话就是分成2份,以此类推。我们在均分颜色空间时,就从这个开立方取整的数开始找,第一个分量加1看看够不够,不够再把第二个分量加1,最差不过把三个分量都加1。

 

  上面的这个算法是要花费一番工夫来编码实现,不过相对于实现之后的成就感,这点辛苦就不值得一提。任何一个成功的算法都是经过反复试算观察结果得到,改进不合理的算法,形成自己的思路,进而实现效果更好的算法,不正是程序员的工作职责所在吗?为工作中的小成就而喜悦,为生活中的小乐趣而快乐,也算的上人生一大美事。

 

 


企业邮箱  |  法律公告  |  隐私保护  |  联系我们  |