Android中有两个比较重要的矩阵,ColorMatrix和Matrix。ColorMatrix用来改变bitmap的颜色和透明度,Matrix用来对bitmap平移、缩放、错切。对矩阵的概念不理解可参考:https://zh.wikipedia.org/wiki/%E7%9F%A9%E9%98%B5#%E7%9F%A9%E9%98%B5%E4%B9%98%E6%B3%95
【ColorMatrix(色彩矩阵)】
Android中Bitmap色彩用了一个[R, G, B, A],4*1的矩阵来保存。
如果想改变一个Bitmap的色彩该怎么办?现在来了解下ColorMatrix的相关知识。ColorMatrix 是一个4*5的矩阵。
我们用[R’, G’, B’, A’]来保存新的bitmap色彩,4*5必须和5*1矩阵相乘才能得到4*1矩阵,于是运算关系如下:
根据矩阵乘法通过如下运算,便能如下求出一个新的色彩矩阵了。
为什么要使用4*5矩阵而不是4*4矩阵?。因为只有4*5矩阵可以单独改变一种颜色值。比如你改变e,只会影响R’。
ColorMatrix的默认矩阵如下图所示
可以看出,进行色彩变换运算后色彩值仍然不变。
知道ColorMatrix的运算原理后,我们就可以做很多事情了。
【黑白图片】
黑白图片的去色原理:只要把RGB三通道的色彩信息设置成一样;即:R=G=B,那么图像就变成了灰色,并且,为了保证图像亮度不变,同一个通道中的R+G+B应该接近1。
在matlab中按照 0.2989 R,0.5870 G 和 0.1140 B 的比例构成像素灰度值。
在OpenCV中按照 0.299 R, 0.587 G 和 0.114 B 的比例构成像素灰度值。
在Android中按照0.213 R,0.715 G 和 0.072 B 的比例构成像素灰度值。
这些比例主要是根据人眼中三种不同的感光细胞的感光强度比例分配的,因此并没有一个确切值,不同工具调试出来的效果也不尽相同。
知道了RGB相关配色后,相关核心代码如下。
private Bitmap handleColorMatrix(){ Canvas canvas = new Canvas(mTempBmp); // 创建一个画布 Paint paint = new Paint(); // 新建paint paint.setAntiAlias(true); //抗锯齿 //黑白 ColorMatrix colorMatrix = new ColorMatrix(new float[]{ 0.213f, 0.715f, 0.072f, 0, 0, 0.213f, 0.715f, 0.072f, 0, 0, 0.213f, 0.715f, 0.072f, 0, 0, 0, 0, 0, 1, 0, }); paint.setColorFilter(new ColorMatrixColorFilter(colorMatrix));// 设置颜色变换效果 canvas.drawBitmap(mOriginBmp, 0, 0, paint); return mTempBmp; }
运行测试效果如下:
【色彩偏移与缩放】
我们可以通过增加最后一列的值来相应增加或减少某种颜色的值。
也可以通过改变对角线上的比例来进行色彩缩放。
比如给红色增加20.
ColorMatrix colorMatrix = new ColorMatrix(new float[]{ 1, 0, 0, 0, 20, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, });
给绿色扩大到1.2倍。
ColorMatrix colorMatrix = new ColorMatrix(new float[]{ 1, 0, 0, 0, 0, 0, 1.2f, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, });
此外ColorMatrix提供了一个 setScale 来进行色彩缩放。
/** * Set this colormatrix to scale by the specified values. */ public void setScale(float rScale, float gScale, float bScale, float aScale) { final float[] a = mArray; for (int i = 19; i > 0; --i) { a[i] = 0; } a[0] = rScale; a[6] = gScale; a[12] = bScale; a[18] = aScale; }
【色彩饱和度】
ColorMatrix提供了一个 setSaturation 通过改变对角线上的比例来改变饱和度。
/** * Set the matrix to affect the saturation of colors. * * @param sat 饱和度的值,取值0,1 */ public void setSaturation(float sat) { reset(); float[] m = mArray; final float invSat = 1 - sat; final float R = 0.213f * invSat; final float G = 0.715f * invSat; final float B = 0.072f * invSat; m[0] = R + sat; m[1] = G; m[2] = B; m[5] = R; m[6] = G + sat; m[7] = B; m[10] = R; m[11] = G; m[12] = B + sat; }
可以看出,当sat取值为0时,即是黑白图片。
【色彩旋转】
看到旋转一词,可能有点蒙,何为色彩旋转?我们可以将RGB看做是一个坐标系(r,g,b)。
那么坐标系如下。
所以,我们可以把一个色彩值看成三维空间里的一个点,色彩值的三个分量可以看成该点的坐标(三维坐标)。假如,我们现在需要围绕蓝色轴进行旋转,我们对着蓝色箭头观察由红色和绿色构造的平面。然后顺时针旋转α度。
在图中,我们可以看到,在旋转后,原R在R轴的分量变为:R*cosα,且原G分量在旋转后在R轴上也有了分量,所以我们要加上这部分分量,因此最终的结果为R’=R*cosα+G*sinα,同理,在计算G’时,因为R的分量落在了负轴上,所以我们要减去这部分,故G’=G*cosα-R*sinα;
于是,我们可以求出矩阵如下。
同理,围绕红色轴旋转的矩阵如下。
围绕绿色轴旋转的矩阵如下。
同样,ColorMatrix提供了一个 setRotate(int axis, float degrees) 来进行色彩旋转。
public void setRotate(int axis, float degrees) { reset(); double radians = degrees * Math.PI / 180d; float cosine = (float) Math.cos(radians); float sine = (float) Math.sin(radians); switch (axis) { case 0: // 围绕红色轴旋转 mArray[6] = mArray[12] = cosine; mArray[7] = sine; mArray[11] = -sine; break; case 1: // 围绕绿色轴旋转 mArray[0] = mArray[12] = cosine; mArray[2] = -sine; mArray[10] = sine; break; case 2: // 围绕蓝色色轴旋转 mArray[0] = mArray[6] = cosine; mArray[1] = sine; mArray[5] = -sine; break; default: throw new RuntimeException(); } }
参考代码:
package com.yongdaimi.android.ffapitest; import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.ColorMatrix; import android.graphics.ColorMatrixColorFilter; import android.graphics.Paint; import android.graphics.drawable.BitmapDrawable; import android.os.Bundle; import android.support.annotation.Nullable; import android.support.v7.app.AppCompatActivity; import android.view.View; import android.widget.Button; import android.widget.ImageView; public class MatrixDemoActivity extends AppCompatActivity implements View.OnClickListener { private ImageView iv_display; private Bitmap mOriginBitmap; private Bitmap mTmpBitmap; private Button bt_change_color; private Button bt_revert_color; @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_matrix_demo); iv_display = findViewById(R.id.iv_display); bt_change_color = findViewById(R.id.bt_change_color); bt_change_color.setOnClickListener(this); bt_revert_color = findViewById(R.id.bt_revert_color); bt_revert_color.setOnClickListener(this); mOriginBitmap = ((BitmapDrawable) iv_display.getDrawable()).getBitmap(); mTmpBitmap = Bitmap.createBitmap(mOriginBitmap.getWidth(), mOriginBitmap.getHeight(), Bitmap.Config.ARGB_8888); } private Bitmap handleColorMatrix() { Canvas canvas = new Canvas(mTmpBitmap); Paint paint = new Paint(); paint.setAntiAlias(true); ColorMatrix colorMatrix = new ColorMatrix(new float[]{ 1, 0, 0, 0, 0, 0, 1.2f, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, }); /* ColorMatrix colorMatrix = new ColorMatrix(new float[]{ 1, 0, 0, 0, 20, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0 }); */ /* ColorMatrix colorMatrix = new ColorMatrix(new float[]{ 0.213f, 0.715f, 0.072f, 0, 0, 0.213f, 0.715f, 0.072f, 0, 0, 0.213f, 0.715f, 0.072f, 0, 0, 0, 0, 0, 1, 0 }); */ paint.setColorFilter(new ColorMatrixColorFilter(colorMatrix)); canvas.drawBitmap(mOriginBitmap, 0, 0, paint); return mTmpBitmap; } @Override public void onClick(View view) { switch (view.getId()) { case R.id.bt_change_color: handleColorMatrix(); iv_display.setImageBitmap(mTmpBitmap); break; case R.id.bt_revert_color: iv_display.setImageBitmap(mOriginBitmap); break; default: break; } } }
本文转自: