android: android 中的ColorMatrix (转)
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;
}
} }
本文转自:
android: android 中的ColorMatrix (转)的更多相关文章
- Android学习探索之Java 8 在Android 开发中的应用
前言: Java 8推出已经将近2年多了,引入很多革命性变化,加入了函数式编程的特征,使基于行为的编程成为可能,同时减化了各种设计模式的实现方式,是Java有史以来最重要的更新.但是Android上, ...
- Stack Overflow 排错翻译 - Closing AlertDialog.Builder in Android -Android环境中关闭AlertDialog.Builder
Stack Overflow 排错翻译 - Closing AlertDialog.Builder in Android -Android环境中关闭AlertDialog.Builder 转自:ht ...
- Google官方关于Android架构中MVP模式的示例续-DataBinding
基于前面的TODO示例,使用Data Binding库来显示数据并绑定UI元素的响应动作. 这个示例并未严格遵循 Model-View-ViewModel 或 Model-View-Presenter ...
- android studio 中移除module和恢复module
一.移除Android Studio中module 在Android Studio中想要删除某个module时,在Android Studio中选中module,右键发现没有delete,如图: An ...
- 如何在Android应用中引入外部网页
在某些情况下,我们需要在Android应用中引入外部网页,这里记录一下如何操作(其实很简单^.^). 先介绍一下开发环境: 开发工具:Android Studio 1.5 SDK API版本:17 操 ...
- NDK笔记(二)-在Android Studio中使用ndk-build
前面一篇我们接触了CMake,这一篇写写关于ndk-build的使用过程.刚刚用到,想到哪儿写哪儿. 环境背景 Android开发IDE版本:AndroidStudio 2.2以上版本(目前已经升级到 ...
- Android Studio中Button等控件的Text中字符串默认大写的解决方法
初学Android的时候,在Android Studio中xml里面添加一个Button.EditText等控件后,它的Text总是会显示大写,即使你输入的字符串是小写也不行,控制字符串大小写的属性是 ...
- .Net程序员之不学Java做安卓开发:Android Studio中的即时调试窗口
对学.Net的人来说,JAVA开发是一场噩梦. .net中的即时窗口,调试时直接在里面写代码,对程序中的各种方法/属性进行调用,很方便. Android Studio中找了好久,参考如下网址,也有类似 ...
- 如何将Eclipse中的项目迁移到Android Studio 中
如何将Eclipse中的项目迁移到Android Studio 中 如果你之前有用Eclipse做过安卓开发,现在想要把Eclipse中的项目导入到Android Studio的环境中,那么首先要做的 ...
- 第三方侧滑菜单SlidingMenu在android studio中的使用
南尘:每天进步一点点! 前面讲了官方的侧滑菜单DrawerLayout的使用,其实早在官方没有推出这个之前,就有很多第三方的jar包如SlidingMenu等,感谢开源的力量. SlidingMenu ...
随机推荐
- shell几种字符串加密解密的方法
第一种:[ Python 与 Bash Shell 的结合 ]这个命令会让你输入一个字符串,然后会再输出一串加密了的数字. 加密代码[照直输入]:python -c 'print reduce(lam ...
- 详解Spring中的ApplicationListener和ContextRefreshedEvent
ApplicationListener和ContextRefreshedEvent一般都是成对出现的.最近在面试中问到了被面试者对于这两个的用法,面试者大多数被问懵了.可见基础知识的掌握程度.基于此本 ...
- python urllib 库
urllib模块中的方法 1.urllib.urlopen(url[,data[,proxies]]) 打开一个url的方法,返回一个文件对象,然后可以进行类似文件对象的操作.本例试着打开google ...
- Alpha(1/10)
鐵鍋燉腯鱻 项目:小鱼记账 团队成员 项目燃尽图 冲刺情况描述 站立式会议照片 各成员情况 团队成员 学号 姓名 git地址 博客地址 031602240 许郁杨 (组长) https://githu ...
- [Python]Flask 源代码分析-config
flask有一个Config类,含from_object, from_pyfile, from_environ等载入命名空间的方法.载入命名空间的本质是载入一个dict[key]=value的字典. ...
- luogu P2962 [USACO09NOV]灯Lights 高斯消元
目录 题目链接 题解 题目链接 luogu P2962 [USACO09NOV]灯Lights 题解 可以折半搜索 map合并 复杂度 2^(n / 2)*logn 高斯消元后得到每个点的翻转状态 爆 ...
- Java并发编程(九)-- 进程饥饿和公平锁
上一章已经提到“如果一个进程被多次回滚,迟迟不能占用必需的系统资源,可能会导致进程饥饿”,本文我们详细的介绍一下“饥饿”和“公平”. Java中导致饥饿的原因 在Java中,下面三个常见的原因会导致线 ...
- nodejs EventEmitter 发送消息
var util = require('util'); var evem = require('events').EventEmitter; function myem(){ evem.call(th ...
- SwipeableFlatList 实现类似于QQ列表滑动
import React, { Component } from "react"; import { FlatList, RefreshControl, StyleSheet, T ...
- [CF528D]Fuzzy Seach
Description: 有两个基因串S和T,他们只包含AGCT四种字符.现在你要找出T在S中出现了几次. 有一个门限值k≥0.T在S的第i(1≤i≤|S|-|T|+1)个位置中出现的条件如下:把T的 ...