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 ...
随机推荐
- JVM之浮点数(float)表示
1. 浮点数的组成:符号位.指数位.尾数位. 1.1 符号位: 占1位,表示正负数: 1.2 指数位: 占8位: 1.3 尾数位: 占23位. 2. 浮点数的表示: 2.1 取值: sflag * ...
- 牛客练习赛35-函数的魔法-floyd
函数的魔法 思路 :如果 可以从A到B最终 都会是233范围内的数字进行转换,注意 这里 建图 为单向图 这个运算未必符合交换关系. #include<bits/stdc++.h> us ...
- wget用法汇总
wget是在Linux下开发的开放源代码的软件,作者是Hrvoje Niksic,后来被移植到包括Windows在内的各个平台上.它有以下功能和特点: (1)支持断点下传功能:这一点,也是网络蚂蚁和F ...
- Stm32常见英文缩写
Stm32常见英文缩写 https://wenku.baidu.com/view/4b9c2eee5022aaea998f0f5b.html STM32嵌入式开发常见缩写 https://wenku. ...
- [CF536D]Tavas in Kansas
[CF536D]Tavas in Kansas 题目大意: 一张\(n(n\le2000)\)个点,\(m(m\le10^5)\)条边的无向带权连通图(权值可以为负).A.B两人分别在\(s,t\)点 ...
- [COCI2013]DLAKAVAC
[COCI2013]DLAKAVAC 题目大意: 有一个长度为\(m(m\le1500)\)的\(01\)串\(A\),进行\(k(k\le10^{18})\)次操作.一次操作完的串中若\(A_i=1 ...
- 奶牛编号(Cowids) [NOIP模拟]
问题描述作为一个神秘的电脑高手,Farmer John 用二进制数字标识他的奶牛.然而,他有点迷信,标识奶牛用的二进制数字,必须只含有 K 位“1”(1 <= K <= 10). 当然,每 ...
- 快速排序 [Qsort]
在做USACO1.4 等差数列的时候,我发现如果用结构体+sort就会超时,用二维数组+qsort就能AC,所以为了不忘记Quick Sort,我还是把代码贴出来以备以后要看吧. void qsort ...
- 移动游戏ui设计(二)--游戏世界的基本法则
游戏世界的基本法则游戏体验的层次感很重要,制作一款趣味性强,宗旨明确的游戏并不简单. l 风格统一性:要先明白游戏的game genres,因为游戏类型与风格密切联系.常见游戏类型有:动作类.冒险类 ...
- 前端利器Emmet
前端利器Emmet Emmet可以通过缩写生成代码片段,可以提升前端开发的效率.使用方法就是输入HTML或者CSS的缩写,然后按tab键自动生成.原文在这里 后代 > <!-- nav&g ...