之前在对图像进行基于直方图的处理时提到过,RGB图像每个颜色通道有256个色阶,所以RGB颜色空间最多可以有16777216(即256^3)种颜色。这是一个巨大的空间,一个显而易见的事实是对于大多数图像而言,这个颜色空间太大了——一千六百多万种颜色,几乎可以给全高清画质(1080p)的图像每个像素点分配一种颜色!大多数情况下,一个图像里面只有远远少得多的颜色,RGB颜色空间大量的颜色并没有利用上。也就是说,我们往往可以用少得多的颜色(几十到几百之间)完全描述一幅图像。
为什么要用更少的颜色描述同一幅图像?答案是其实很简单,更少的颜色意味着可以用更少的空间存储同一幅图像,在传输图像的时候也可以在不影响内容的前提下节省流量/带宽。举个简单的例子,如果我们可以用256种颜色来描述一副图像的话,那么每个像素只需要用1个字节存储;相比之下,RGB图像每个像素需要3个字节来存储。256色的图像可以节省66%的存储空间!当然,这个假设成立的前提是我们能够找到一组颜色来很好地代表原图像里的色彩,而这并不是一个简单的事情。
根据给定的图像计算出最优颜色组合的算法称为减色算法——顾名思义,就是减少描述图像所需要的颜色数量的算法。这类算法在计算机图形学发展的早起以及互联网兴起的时候曾引起研究人员的极大兴趣,因为它们可以帮助人们利用有限的硬件、网络资源获得最好的效果或最大的收益。在计算机图形学技术日趋成熟、互联网高度发达的今天,这些算法依然在不同的应用场合发挥着重要的作用。
减色算法主要有直接量化、统计量化(population based)、颜色空间分割以及聚类等方法。此外,利用人工神经网络来进行减色操作也可以获得良好的效果。
1. 直接量化
直接量化是最简单的减色算法,它只是对颜色空间进行直接的重采样来减少颜色。具体来说,直接量化对每个颜色通道单独重新采样,将每个通道的色阶从256减少到某个制定的数字。这样得到一个新的小的多的颜色空间,而原图像中的每一个像素则用在新的颜色空间中的最近邻取代:
...
// colors: the desired number of colors
// it is restricted to the cube of an integer here
// levs: color levels per channel
var levs = Math.ceil(Math.pow(colors, 1.0/3.0));
return src.map(function(c) {
// discretize each channel
var r = Math.round(Math.round((c.r / 255.0) * levs) / levs * 255.0);
var g = Math.round(Math.round((c.g / 255.0) * levs) / levs * 255.0);
var b = Math.round(Math.round((c.b / 255.0) * levs) / levs * 255.0);
return new Color(r, g, b, c.a);
});
直接量化的效果其实已经很不错了,用256色已经可以基本上恢复出原图的绝大部分颜色了,当然如果用更多的颜色效果会好得多。从图像的直方图也可以很清楚的看到直接量化的效果。直接量化之后的直方图只包含有限的几个峰值,它们正是直接量化时的颜色采样点。
原图
64色图像