色彩矩阵分析
在Android中,系统使用一个颜色矩阵— —ColorMatrix,来处理图像的色彩效果。对于图像的每个像素点,都有一个颜色分量矩阵用来保存颜色的RGBA值(下图矩阵C),Android中的颜色矩阵是一个4x5的数字矩阵,它用来对图片的色彩进行处理(下图矩阵M)。如下:
如果我们想要改变一张图像的色彩显示效果,在Android系统中,我们会用矩阵的乘法运算来修改颜色分量矩阵的值。上面矩阵M就是一个4x5的颜色矩阵。在Android中,它会以一维数组的形式来存储[a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t],而C则是一个颜色矩阵分量。在处理图像时,使用矩阵乘法运算MC来处理颜色分量矩阵,如下:
利用线性代数知识可知:
R' = aR + bG + cB + dA + e
G' = fR + gG + hB + iA + j
B' = kR + lG + mB + nA + o
A' = pR + qG + rB + sA + t
从这个公式可以发现,矩阵M中:
- 第一行的 abcde 用来决定新的颜色值中的R— —红色
- 第二行的 fghij 用来决定新的颜色值中的G— —绿色
- 第三行的 klmno 用来决定新的颜色值中的B— —蓝色
- 第四行的 pqrst 用来决定新的颜色值中的 — —透明度
- 矩阵M中第五列 ejot 值分别用来决定每个分量中的 offset ,即偏移量
这样划分好后,这些值的作用就比较明确了。
初始颜色矩阵
接下来,我们重新看一下矩阵变换的计算公式,以红色分量为例:
R' = aR + bG + cB + dA + e
如果令 a=1,b、c、d、e都等于0,则有R’=R。同理对二、三、四行进行操作,可以构造出一个矩阵,如下:
把这个矩阵代入公式 N=MC,根据矩阵乘法运算法则,可得 R’=R,G’=G,B’=B,A’=A。即不会对原有颜色进行任何修改,所以这个矩阵通常被用来作为初始颜色矩阵。
改变颜色值
那么,当我们想要改变颜色值的时候,通常有两种方法:
- 改变颜色的offset(偏移量)的值
- 改变对应 RGBA 值的系数
-
改变偏移量
从前面的分析中可知,改变颜色的偏移量就是改变颜色矩阵的第五列的值,其他保持初始矩阵的值即可。如下示例:
上面的操作中改变了R、G对应的颜色偏移量,那么结果就是图像的红色和绿色分量增加了100,即整体色调偏黄显示。 -
改变颜色系数
如下操作:
改变G分量对应的系数g的值,增加到2倍,这样在矩阵运算后,图像会整体色调偏绿显示。
色调
Android系统提供了 setRotate(int axis, float degrees) 方法来修改颜色的色调。第一个参数 axis,用0、1、2分别代表红、绿、蓝三个颜色通道;第二个参数 degrees 就是要修改的值。如下:
ColorMatrix hueMatrix = new ColorMatrix();
hueMatrix.setRotate(0, hue0);
hueMatrix.setRotate(1, hue1);
hueMatrix.setRotate(2, hue2);
Android系统的 setRotate(int axis, float degrees) 方法其实就是对色彩的旋转运算。RGB色是如何旋转的呢,首先用R、G、B三色建立三维坐标系,如下:
RGB坐标
这里我们可以把一个色彩值看成三维空间里的一个点,色彩值的三个分量可以看成该点对应的坐标(三维坐标)。我们先不考虑,在三个维度综合情况下是怎么旋转的,我们先看看,在某个轴作为Z轴,在另两个轴形成的平面上旋转的情况。假如,我们现在需要围绕蓝色轴进行旋转,我们对着蓝色箭头观察由红色和绿色构造的平面。然后顺时针旋转 度。如下图所示:
围绕蓝色轴进行旋转
度
在图中,我们可以看到,在旋转后,原R在R轴的分量变为:Rcos ,且G分量在旋转后在R轴上也有了分量,所以我们要加上这部分分量,因此最终的结果为 R’ = Rcos + Gsin ,同理,在计算G’时,因为R的分量落在了负轴上,所以我们要减去这部分,故 G’ = Gcos - Rsin 。
饱和度
Android系统提供了 setSaturation(float sat) 方法来修改颜色的饱和度。参数 sat:表示把当前色彩饱和度放大的倍数。取值为0表示完全无色彩,即灰度图像(黑白图像);取值为1时,表示色彩不变动;当取值大于1时,显示色彩过度饱和。如下:
ColorMatrix saturationMatrix = new ColorMatrix();
saturationMatrix.setSaturation(saturation);
亮度
当三原色以相同比例进行混合时,就会显示出白色。Android正是利用这个原理对图像进行亮度的改变。如下:
ColorMatrix lumMatrix = new ColorMatrix();
lumMatrix.setScale(lum, lum, lum, 1);