前言
每当遇到 ColorMatrix 这个东东,感觉头多大,什么矩阵、阶乘。。。放弃!
不敢面对,恐惧依在!所以,这次下决心要好好理解下,为了下次遇见,可以高傲的无视~
好了,废话多了,开始吧。
介绍
什么是矩阵阶乘?
这里截取百度百科里面的定义:
看了这个图,琢磨一下,应该大体知道是怎么计算的。简单的说下:
A的第一行所有元素 与 B的第一列所有元素,分别相乘,最后结果相加(即:A1×B1 + A2×B2+ A3×B3),作为C矩阵第一行第一列的元素。
其他元素的计算也是类似,以此类推。
所以最终公式就是:
乘积C的第m行第n列的元素 = 矩阵A的第m行的所有元素 与 矩阵B的第n列的所有元素 乘积 之和。
注意:
- 矩阵A、B相乘,必须满足矩阵A的列数与矩阵B的行数相等,或者矩阵A的行数与矩阵B的列数相等
- 矩阵相乘不满足交换律,即AB ≠ BA
色彩处理
在色彩效果处理中,通常会使用以下3个属性来描述一个图像:
- 色调——物理传播的颜色
- 饱和度——颜色的纯度,从0 ~ 100% 来描述,即灰白到饱和
- 亮度——颜色相对明暗程度
这里,是使用ColorMatrix来处理色彩效果,那么可知,该类是有关于以上3个属性的。
实际上,ColorMatrix 是一个4*5的矩阵,但在android中,它是使用一维数组的形式保存的,具体转换交个android自己处理就可以了。
[ a, b, c, d, e,
f, g, h, i, j,
k, l, m, n, o,
p, q, r, s, t ]
图片是由一个个像素组成的,每个像素多有一个颜色矩阵来保存颜色的RGBA值,其与目标图片的[R,G,B,A]阶乘:
最终结果是:
R' = a*R + b*G + c*B + d*A + e;
G' = f*R + g*G + h*B + i*A + j;
B' = k*R + l*G + m*B + n*A + o;
A' = p*R + q*G + r*B + s*A + t;
在默认的情况下,ColorMatrix的矩阵是这样的:
[ 1 0 0 0 0 - red vector
0 1 0 0 0 - green vector
0 0 1 0 0 - blue vector
0 0 0 1 0 ] - alpha vector
注意这里的顺序是:R、G、B、A
问题:色彩一般就是R、G、B、A,4个颜色通道(如图),为什么需要4×5阶乘的矩阵呢?
通过上面的计算计算结果,可以看到R’、G’、B’、A’最终的结果后面分别添加了e、j、o、t,这个就是添加的一阶。其作用是对颜色的偏移量。前面的四阶可以对色彩的每个分量进行乘/除运算(即对亮度计算),后面一阶是进行加/减 运算,(即对饱和度计算),比如只对G值单独增加色彩。
使用
通过上面的介绍,我们知道处理色彩,使用ColorMatrix就可以。但对于ColorMatrix的操作,也可以分为2中:
- 直接操作ColorMatrix数组值,用于简单的操作,或者是一些固定的数组值;
- 通过ColorMatrix的API处理,简单方便,对于复杂的处理不用自己计算;
直接修改ColorMatrix 数组值
1.当创建一个ColorMatrix对象时,或者reset()重置,默认的矩阵如下,即保留原来的图片色彩
/**
* 默认
*/
mColorMatrix = new ColorMatrix(new float[]{
1,0,0,0,0,
0,1,0,0,0,
0,0,1,0,0,
0,0,0,1,0,
});
2.只保留红色通道,即只保留图片的红色
/**
* 红色通道
*/
mColorMatrix = new ColorMatrix(new float[]{
1,0,0,0,0,
0,0,0,0,0,
0,0,0,0,0,
0,0,0,1,0,
});
3.只保留绿色通道
/**
* 绿色通道
*/
mColorMatrix = new ColorMatrix(new float[]{
0,0,0,0,0,
0,1,0,0,0,
0,0,0,0,0,
0,0,0,1,0,
});
4.只保留蓝色通道
/**
* 蓝色通道
*/
mColorMatrix = new ColorMatrix(new float[]{
0,0,0,0,0,
0,0,0,0,0,
0,0,1,0,0,
0,0,0,1,0,
});
5.改变色彩透明度
/**
* 色彩透明度
*/
mColorMatrix = new ColorMatrix(new float[]{
1,0,0,0,0,
0,1,0,0,0,
0,0,1,0,0,
0,0,0,0.5f,0,
});
6.改变色彩偏移量,即饱和度
这里改变绿色的偏移量,增加80。那么每个像素G值多会增加80。
/**
* 色彩偏移(饱和度)
*/
mColorMatrix = new ColorMatrix(new float[]{
1,0,0,0,0,
0,1,0,0,80,
0,0,1,0,0,
0,0,0,1,0,
});
7.改变色彩缩放量,即亮度
这里对角线的值增加1.3倍,那么最终颜色值会比原来亮很多。如果对角线的RGB值,取0,那么什么也不显示,那么就是黑色。
/**
* 色彩缩放(亮度)
*/
mColorMatrix = new ColorMatrix(new float[]{
1.3f,0,0,0,0,
0,1.3f,0,0,0,
0,0,1.3f,0,0,
0,0,0,1,0,
});
8.改变色彩旋转角度,即色调
这个理解起来有点难度。首先要理解什么是向量,分量。应该多学过,这里给个图看看,不懂的自行了解。对了,还需要三角函数的一点知识。
这里将R、G、B作为三维坐标的轴。如图:
一般旋转,指的是按照R、G、B的其中一个轴来旋转。
比如,按照B轴来旋转,那么B的颜色值不会改变,R和G的值则会改变。如图:
旋转之后,需要重新计算颜色值,那么就需要对R和G颜色向量先进行分解,然后在计算。如图:
可以知道,最后计算结果是:
R = G’sin(a) + R’cos(a)
G = G’cos(a) - R’sin(a)
如果是默认的矩阵进行以上的旋转,那么最终的结果是:
假设旋转的角度是a = 30度,那么最终的数组是:
mColorMatrix = new ColorMatrix(new float[]{
0.866f,0.5f,0,0,0,
-0.5f,0.866f,0,0,0,
0,0,1.3f,0,0,
0,0,0,1,0,
});
围绕其他颜色轴的分析也是如此。是不是觉得过于复杂了?我就是想旋转一下而已,给我说这么一大堆…
好吧,装逼过头了,其实我不太懂其中详细的算法(打脸了…)。
没错,android自然给我们提供了API方法,直接调用就可以了。下面会介绍,以上大致说下计算原理,了解一下,方便后面API的理解。
以上所有的效果图:(真是美丽)
ColorMatrix 类的使用
如果理解了上面一些计算,那么下面API的介绍就简单了,多是对应的。
构造函数:
ColorMatrix()
如果不配合使用set(), 则使用默认的矩阵
ColorMatrix(float[] src)
使用指定的矩阵src
ColorMatrix(ColorMatrix src)
使用src,创建新的对象
set(float[] src)
设置数组矩阵
set(ColorMatrix src)
将src对象复制给当前对象
reset()
重置,matrix则恢复默认值
setSaturation(float sat)
设置R G B整体的饱和度的放大倍数。
参数:
sat :表示把当前色彩整体饱和度的放大倍数。
取值为0:表示完全无色彩,即灰度图像(黑白图像);
取值为1:表示色彩不变动;
取值大于1:显示色彩过度饱和
使用:
mColorMatrix.reset();
mColorMatrix.setSaturation(5);
mPaint.setColorFilter(new ColorMatrixColorFilter(mColorMatrix));
canvas.drawBitmap(mBitmap, 0, 0, mPaint );
setScale(float rScale, float gScale, float bScale, float aScale)
设置缩放来改变亮度,可以分别对R、G、B、A操作。
参数:
4个参数,分别对应R、G、B、A的缩放倍数。(以下测试不考虑透明度)
当值全为0:那么最终颜色000000,则是黑色;
当值全为1:表示色彩不变动;
当值全大于1:亮度增强,足够大时,最终颜色为ffffff,则是白色;
使用:
mColorMatrix.reset();
float scale = 1.2f;
mColorMatrix.setScale(scale, scale, scale, 1);
mPaint.setColorFilter(new ColorMatrixColorFilter(mColorMatrix));
canvas.drawBitmap(mBitmap, 0, 0, mPaint );
setRotate(int axis, float degrees)
设置旋转,来改变色调。具体算法可以参考源码,或者参考上面介绍,可以验证下是否一致。
参数:
axis:表示围绕哪个颜色轴旋转,其值有3个
0:代表围绕 R 旋转
1:代表围绕 G 旋转
2:代表围绕 B 旋转
degrees:旋转的角度值
使用:
mColorMatrix.reset();
mColorMatrix.setRotate(1,30); //和前面直接使用数组,效果是一样的
mPaint.setColorFilter(new ColorMatrixColorFilter(mColorMatrix));
canvas.drawBitmap(mBitmap, 0, 0, mPaint );
getArray()
获取当前矩阵数值
对于色彩的变化,也不是很专业的。前面的方法,在我的范围内,已经够用了。以下方法很少用到,需要熟练知道阶乘的运算,还有对色彩的变化有一定的认识,即色彩结果值是不是你想要的结果。这里简单提一下这些方法。
为了更好说明介绍,假设当前的ColorMatrix对象是 colorMatrix
setConcat(ColorMatrix matA, ColorMatrix matB)
阶乘不满足交换律,那么按照阶乘运算规则:matA * matB,将最终结果赋值给当前对象的colorMatrix;
preConcat(ColorMatrix prematrix)
运算规则是:colorMatrix * prematrix,相当于调用 setConcat(this, prematrix);
postConcat(ColorMatrix postmatrix)
运算规则是:postmatrix 8 colorMatrix , 相当于调用 setConcat(postmatrix, this);
setRGB2YUV()
setYUV2RGB()
这2个方法主要是用于切换图像格式
由于在不同的应用领域中为了更好更准确的满足各自的需求,就出现了各种各样的色彩空间模型来量化的描述颜色。比较常接触到的就包括RGB/CMYK/YIQ/YUV/HSI等等。对于数字电子多媒体领域来说,我们经常接触到的色彩空间的概念,主要是RGB,YUV这两种。
RGB是按三基色加光系统的原理来描述颜色,而YUV则是按照亮度,色差的原理来描述颜色
所以,为了保证兼容性,需要进行RGB与YUV格式的互转。
关于色彩格式,具体可以参考这里:http://blog.chinaunix.net/uid-9068997-id-2010406.html
常用的特定效果
灰白效果
与setSaturation(0)效果是一致的,可以参看下源码。
mColorMatrix = new ColorMatrix(new float[]{
0.33f,0.59f,0.11f,0,0,
0.33f,0.59f,0.11f,0,0,
0.33f,0.59f,0.11f,0,0,
0.33f,0.59f,0.11f,0,0,
});
反转效果
/**
*反转效果
*/
mColorMatrix = new ColorMatrix(new float[]{
-1,0,0,0,255,
0,-1,0,0,255,
0,0,-1,0,255,
0,0,0,1,0,
});
怀旧效果
/**
* 怀旧效果
*/
mColorMatrix = new ColorMatrix(new float[]{
1/2f,1/2f,1/2f,0,0,
1/3f,1/3f,1/3f,0,0,
1/4f,1/4f,1/4f,0,0,
0,0,0,1,0,
});
去色效果
/**
* 去色效果
*/
mColorMatrix = new ColorMatrix(new float[]{
1.5f,1.5f,1.5f,0,-1,
1.5f,1.5f,1.5f,0,-1,
1.5f,1.5f,1.5f,0,-1,
0,0,0,1,0,
});
反色效果
/**
* 反色效果 (红 绿交换)
*/
mColorMatrix = new ColorMatrix(new float[]{
0,1,0,0,0,
1,0,0,0,0,
0,0,1,0,0,
0,0,0,1,0,
});
以上的效果图:
结束
嗯,终于,对于ColorMatrix有了一定的了解。一番总结之后,其实也并没有那么难(抛弃色彩算法)。
至此,再次遇见,是那么熟悉~
主要参考:
https://blog.csdn.net/harvic880925/article/details/51187277
《android群英传》