内容均来自
矩阵的概念
略,参考源博客
色彩矩阵
对于色彩的存储,Bitmap类使用一个32位的数值来保存。红、绿、蓝及透明度各占8位,每一个色彩分量的取值范围是0-255。透明度为0表示完全透明,为255时,色彩完全可见。
色彩的矩阵表示
四阶矩形
一个色彩信息包含R、G、B、Alpha信息,所以,必然要使用一个4阶
色彩变换矩阵来修改色彩的每一个分量值(如果不想修改,对应的数值给0):

上图中,每个颜色占据一行,即占据一个维度,除了本分量值外,其他为0;
矩阵计算时,每行中的各个数字与另一矩阵中的一列中的各个数字分别相乘,然后相加为新值;
注意:对于色彩变换矩阵,这里的色彩顺序是R、G、B、A而不是A、R、G、B!
将色彩(0,255,0,255)更改为半透明时,可以使用下面的的矩阵运算来表示:

五阶矩阵
使用四阶矩阵完全可以改变图片的RGBA值了,但考虑一种情况,如果我们只想在原有的R色上增加一些分量呢?
这时,我们就得再多加一阶来表示平移变换。
一个既包含线性变换(乘法)
,又包含平移变换(加法)
的组合变换,称为仿射变换。
使用四阶的色彩变换矩阵来修改色彩,只能够对色彩的每一个分量值进行乘(除)运算,如果要对这些分量值进行加减法的运算(平移变换),只能通过五阶矩阵来完成。
比如:
1、红色分量值更改为原来的2倍(线性);
2、绿色分量增加100(平移);
则使用4阶矩阵的乘法无法实现,所以,应该在四阶色彩变换矩阵上增加一个“哑元坐标”,来实现所列的矩阵运算:

Android中的色彩矩阵
色彩变换矩阵的表示形式,是五阶的,在默认情况下,色彩变换矩阵的形式:

Android中的色彩矩阵是用ColorMatrix
类来表示的,使用如下:
// 生成色彩矩阵
ColorMatrix colorMatrix = new ColorMatrix(new float[]{
1, 0, 0, 0, 0,
0, 1, 0, 0, 0,
0, 0, 1, 0, 0,
0, 0, 0, 0.5, 0,
});
mPaint.setColorFilter(new ColorMatrixColorFilter(colorMatrix));
示例代码1(单个颜色通道输出)
val paint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
setARGB(255, 200, 100, 100)
}
// 绘制原始位图
canvas.drawRect(0f, 0f, width / 2.toFloat(), 600f, paint)
canvas.translate(width / 2.toFloat(), 0f)
// 生成色彩矩阵(把红色和蓝色都去掉,仅显示绿色值)
val colorMatrix = ColorMatrix(floatArrayOf(
0f, 0f, 0f, 0f, 0f, // RED
0f, 1f, 0f, 0f, 0f, // GREEN
0f, 0f, 0f, 0f, 0f, // BLUE
0f, 0f, 0f, 1f, 0f)) // ALPHA
paint.colorFilter = ColorMatrixColorFilter(colorMatrix)
canvas.drawRect(0f, 0f, width / 2.toFloat(), 600f, paint)

示例代码2(图片多颜色单个绿色通道输出)
private var bitmap: Bitmap
private var paint: Paint
init {
bitmap = BitmapFactory.decodeResource(context.resources, R.mipmap.chrome)
paint = Paint(Paint.ANTI_ALIAS_FLAG)
}
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
// 绘制原始位图
canvas.drawBitmap(bitmap, null, Rect(0, 0, 250, 250 * bitmap.height / bitmap.width), paint)
canvas.translate(0f, 260f)
// 生成色彩矩阵(把红色和蓝色都去掉,仅显示绿色值)
val colorMatrix = ColorMatrix(floatArrayOf(
0f, 0f, 0f, 0f, 0f, // RED
0f, 1f, 0f, 0f, 0f, // GREEN
0f, 0f, 0f, 0f, 0f, // BLUE
0f, 0f, 0f, 1f, 0f)) // ALPHA
paint.colorFilter = ColorMatrixColorFilter(colorMatrix)
canvas.drawBitmap(bitmap, null, Rect(0, 0, 250, 250 * bitmap.height / bitmap.width), paint)
}

色彩的几种运算方式
色彩的平移运算
有了上面的铺垫,这个理解起来就简单了;
色彩的平移运算,实际上就是色彩的加法运算。就是在色彩变换矩阵的最后一行加上某个值;这样可以增加特定色彩的饱和度;

示例:为上图增大绿色饱和度
// 饱和度绿色
val colorMatrix = ColorMatrix(floatArrayOf(
1f, 0f, 0f, 0f, 0f, // RED
0f, 1f, 0f, 0f, 50f, // GREEN
0f, 0f, 1f, 0f, 0f, // BLUE
0f, 0f, 0f, 1f, 0f)) // ALPHA

注意:由于图片是由一个个像素组成的,所以用每个像素所对应的色彩数组,来乘转换矩阵,结果就是转换后的当前点的颜色值;所以,在应用ColorMatrics后,图片中每个像素的绿色值都增加了50;
色彩平移除了增加指定颜色饱和度以外,另一个应用就是色彩反转:
// 色彩反相
val colorMatrix = ColorMatrix(floatArrayOf(
-1f, 0f, 0f, 0f, 255f, // RED
0f, -1f, 0f, 0f, 255f, // GREEN
0f, 0f, -1f, 0f, 255f, // BLUE
0f, 0f, 0f, 1f, 0f)) // ALPHA

色彩的缩放
色彩的缩放运算其实就是色彩的乘法运算。在色彩矩阵对角线上的分别代表R、G、B、A的几个值,将其分别乘以指定的值。这就是所谓的缩放变换。

可以针对某一个颜色值进行放大缩小运算,但当对R、G、B、A同时进行放大缩小时,就是对亮度进行调节!
// 色彩缩放,(全部为增加亮度)
val colorMatrix = ColorMatrix(floatArrayOf(
1.2f, 0f, 0f, 0f, 0f, // RED
0f, 1.2f, 0f, 0f, 0f, // GREEN
0f, 0f, 1.2f, 0f, 0f, // BLUE
0f, 0f, 0f, 1.2f, 0f)) // ALPHA

缩放变换的特殊应用(通道输出)
上面我们输出过绿色通道;
由于在色彩变换矩阵中,对角线上的数的取值范围是从0-1的,所以当取0时,这个色彩就完全不显示,所以当我们R、G都取0,而独有B取1时,就只显示了蓝色,所形成的图像也就是我们通常说的蓝色通道;

色彩的旋转运算
RGB色是如何旋转的呢,首先用R、G、B三色建立立体坐标系:

我们可以把一个色彩值看成三维空间里的一个点,色彩值的三个分量可以看成该点的坐标(三维坐标)。我们先不考虑,在三个维度综合情况下是怎么旋转的,我们先看看,在某个轴做为Z轴,在另两个轴形成的平面上旋转的情况,下图分析了,在将蓝色轴做为Z轴,仅在红—绿平面上
旋转a度的情况:

在图中,我们可以看到,在旋转后,原R在R轴的分量变为:原R*cosa(投影);
但原G分量在旋转后,在R轴上也有了分量,但分量落在了负轴上,所以我们要减去这部分分量,所以最终的结果是最终的R=原R*cosa-原G*sina;
下面就看下关于几种旋转计算及结果矩阵,(注意:这几个图只标记了原X轴色彩分量的旋转,没有把Y轴色彩分量的旋转标记出来)
绕蓝色轴旋转a°

对应的色彩变换矩阵是:

绕红色轴旋转a度

对应的色彩变换矩阵是:

绕绿色轴旋转a度

对应的色彩变换矩阵是:

当围绕红色轴进行色彩旋转时,由于当前红色轴的色彩是不变的,而仅利用三角函数来动态的变更绿色和蓝色的颜色值。这种改变就叫做色相调节!当围绕红色轴旋转时,是对图片就行红色色相的调节;同理,当围绕蓝色颜色轴旋转时,就是对图片就行蓝色色相调节;当然,当围绕绿色轴旋转时,就是对图片进行绿色色相的调节.
色彩的投射运算
色彩矩阵运算的公式:

红色运算给单独拉了出来,红色标记的那几个元素a12,a13,a14
,在运算中,是利用G、B、A的颜色值的分量
来增加红色值的。
比如:

注意:最终结果的220=0.2*100+1*200,可见绿色分量在原有绿色分量的基础上,增加了红色分量值的0.2倍;利用其它色彩分量的倍数来更改自己色彩分量的值,这种运算就叫投射运算
。
下图阴影部分;对这些值进行修改时,修改所使用的增加值来自于其它色彩分量的信息。

色彩投射的应用之 - 黑白照片
// 黑白
colorMatrix = ColorMatrix(floatArrayOf(
0.213f, 0.715f, 0.072f, 0f, 0f, // RED
0.213f, 0.715f, 0.072f, 0f, 0f, // GREEN
0.213f, 0.715f, 0.072f, 0f, 0f, // BLUE
0f, 0f, 0f, 1f, 0f)) // ALPHA

彩色转黑白原理:
- 只要把RGB三通道的色彩信息设置成一样;即:R=G=B,那么图像就变成了灰色;
- 为了保证图像亮度不变,同一个通道中的R+G+B=1:如:0.213+0.715+0.072=1;
三个数字的由来:0.213, 0.715, 0.072;
按理说应该把RGB平分,都是0.3333333,可以试试0.33,原因还是看源博客吧;
投射运算之色彩反色
利用色彩矩阵将两个颜色反转,这种操作就叫做色彩反色 (如下:红绿色反转)
colorMatrix = ColorMatrix(floatArrayOf(
0f, 1f, 0f, 0f, 0f, // RED, 由绿色替换
1f, 0f, 0f, 0f, 0f, // GREEN,由红色替换
0f, 0f, 1f, 0f, 0f, // BLUE
0f, 0f, 0f, 1f, 0f)) // ALPHA

投射运算之颜色变旧
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

ColorMatrix函数
Android中ColorMatrix自带了一些函数来帮我们完成一些调整饱和度、色彩旋转等操作;
ColorMatrix的构造函数:
ColorMatrix() // new 一个新的
ColorMatrix(float[] src) // 指定创建
ColorMatrix(ColorMatrix src) // 以src为源copy 一个
setSaturation - 设置饱和度
通过色彩的平移运算,可单独增加R、G、B的饱和度,但如果整体呢,就需要用到 setSaturation
函数了;
// 整体增强颜色饱和度,即同时增强R,G,B的色彩饱和度
public void setSaturation(float sat)
参数float sat:表示把当前色彩饱和度放大的倍数。取值
- 0表示完全无色彩,即灰度图像(黑白图像;
- 1时,表示色彩不变动;
- 大于1时,显示色彩过度饱和,饱和度放大多少倍;

setScale - 色彩缩放
// 分别对应R,G,B,A颜色值的缩放倍数
public void setScale(float rScale, float gScale, float bScale,float aScale)
效果如下:

setRotate-色彩旋转
参考源博客,上面介绍了色彩旋转的原理,涉及到正余弦函数,同时Android封装好了色彩旋转的函数:
/**
* 将旋转围绕某一个颜色轴旋转 degrees 度
* axis=0 围绕红色轴旋转
* axis=1 围绕绿色轴旋转
* axis=2 围绕蓝色轴旋转
*/
public void setRotate(int axis, float degrees);
示例代码
seekBar {
id = android.R.id.text1
max = 360 // 整个旋转度数的范围是-180到180,360为一个周期
progress = 180
onSeekBarChangeListener {
onProgressChanged { _, progress, _ ->
view7.colorMatrix.setRotate(0,progress-180*.10f)
view7.postInvalidate()
}
}
}.lparams(width = ViewGroup.LayoutParams.MATCH_PARENT)

ColorMatrix相乘
涉及到矩阵相乘;ColorMatrix矩阵相乘涉及到3个方法:
// matA*matB,然后将结果做为当前ColorMatrix的值(覆盖当前)
public void setConcat(ColorMatrix matA, ColorMatrix matB)
// 就是将当前的矩阵乘以 prematrix
public void preConcat(ColorMatrix prematrix)
// postmatrix*当前矩阵,上面为前乘,这里为后乘
public void postConcat(ColorMatrix postmatrix)
矩阵的前乘 与 后乘 的结果是不一样的;
setConcat()
下面两个颜色矩阵相乘:

因为矩阵相乘的原则是:第一个矩阵的列数需等于第2个矩阵的行数,明显这里不是这样的,那如何相乘呢?
Android提供了一个方案,让这两个矩阵相乘,就是把第一个矩阵的最后一列单独拿出来,另外加到结果上;

参考源博客
网友评论