您的位置:首页 > 移动开发 > Android开发

Android色彩矩阵处理图像

2016-06-28 16:41 483 查看
最近在做个人项目时,遇到了这样一个需求,在获取到网络上图片之后,需要对图片的亮度进行一些处理,学习了一下Android中如何通过色彩矩阵处理色调、饱和度和亮度的知识,在这篇博客中记录一下供以后查阅。

Android中对于图片的处理,最常使用到的数据结构是位图——Bitmap,它包含了一张图片所有的数据。这个数据都是由点阵和颜色值组成的,所谓点阵就是一个包含像素的矩阵,每一个元素对应着图片的一个像素。而颜色值——ARGB,分别对应透明度、红、绿、蓝这四个通道分量,它们共同决定了每个像素点显示的颜色,下图就显示了自然界中的色光三原色。



色彩矩阵

在色彩处理中,通常使用一下三个角度来描述一个图像。

色调——物体传播的颜色

饱和度——颜色的纯度,从0(灰)到100%(饱和)来进行描述

亮度——颜色的相对明暗程度

而在Android中,系统使用一个颜色矩阵——ColorMatrix,来处理图像的这些色彩效果。Android中的颜色矩阵是一个4*5的数字矩阵,它用来对图片的色彩进行处理。而对于每个像素点,都有一个颜色分量矩阵来保存颜色的RGBA值,如图所示:





图中矩阵M就是一个4*5的颜色矩阵。在Android中,它会以一维数组的形式来存储,而C则是一个颜色矩阵分量。在处理图像时,使用矩阵乘法运算MC来处理颜色分量矩阵:



根据线性代数中的矩阵乘法运算法则,我们可以发现,颜色矩阵是按一下方式划分的

第一行的abcde值用来决定新的颜色值中的R——红色

第二行的fghij值用来决定新的颜色值中的G——绿色

第三行的klmno值用来决定新的颜色值中的B——蓝色

第四行的pqrst值用来决定新的颜色值中的A——透明度

矩阵M中的第五列——ejot值分别用来决定每个分量中的offset,即偏移量

以R分量的计算过程为例:

R1 = a*R + b*G + c*B + d*A + e;

令a=1,b、c、d、e都等于0,那么计算结果为R1 = R

同理 令a,g,m,s四个值为1,其他值为0的颜色矩阵不会改变原有颜色值。

如果需要改变原有颜色值的矩阵分量,有两种方法:

改变a,g,m,s这四个值,即可使原有颜色值产生相应系数的变化

改变偏移量的值,即颜色矩阵中第五列的值

使用ColorMatrix

除了自定义一个冗长的一维数组矩阵来处理图像颜色,Android内部封装了一些API来快速调整生成矩阵,而不用每次都自己去计算矩阵值。

在Anroid中,系统封装了一个类——ColorMatrix,也就是前面说所的颜色矩阵。通过这个类,可以很方便地通过改变矩阵值来处理颜色效果,创建一个ColorMatrix对象非常简单,代码如下所示。

ColorMatrix hueMatrix = new ColorMatrix();


改变色调

直接上方法的源代码:

/**
* Set the rotation on a color axis by the specified values.
* <p>
* <code>axis=0</code> correspond to a rotation around the RED color
* <code>axis=1</code> correspond to a rotation around the GREEN color
* <code>axis=2</code> correspond to a rotation around the BLUE color
* </p>
*/
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) {
// Rotation around the red color
case 0:
mArray[6] = mArray[12] = cosine;
mArray[7] = sine;
mArray[11] = -sine;
break;
// Rotation around the green color
case 1:
mArray[0] = mArray[12] = cosine;
mArray[2] = -sine;
mArray[10] = sine;
break;
// Rotation around the blue color
case 2:
mArray[0] = mArray[6] = cosine;
mArray[1] = sine;
mArray[5] = -sine;
break;
default:
throw new RuntimeException();
}
}


第一个参数为0、1、2分别对应要改变的色调,即R,G,B。

改变的原理也是去修改颜色矩阵中对应位置的值。

饱和度

源码:

/**
* Set the matrix to affect the saturation of colors.
*
* @param sat A value of 0 maps the color to gray-scale. 1 is identity.
*/
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;
}


亮度

/**
* 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;
}


当我们需要通过这个方法修改亮度时,需要保证前三个参数一致,即不会改变原图中三原色的比例,保证色度一致,只是通过调节这三个参数的值来改变亮度。

上面这三种调节方式并不是固定的,大家可以根据运算灵活搭配使用。除了使用上面三种方式单独进行颜色效果的处理之外,Android系统还封装了矩阵的乘法运算,即可实例化多个ColorMatrix,然后将他们混合,从而叠加处理效果,具体用法见后文。

实战

我们通过滑动SeekBar来改变上文所述三个数值,并将这些数值作用到对应的矩阵中。最后通过postConcat()方法来显示混合后的处理结果。

滑动SeekBar获取输入值的代码如下所示。

@Override
public void onProgressChanged(SeekBar var1, int progress, boolean var3){
switch(var1.getId()){
case R.id.sb_Hue:
mHue = (progress - MID_VALUE) * 1.0F / MID_VALUE * 180;
break;
case R.id.sb_Sat:
mSat = progress * 1.0F / MID_VALUE;
break;
case R.id.sb_Lum:
mLum = progress * 1.0F / MID_VALUE;
break;
}
mImageView.setImageBitmap(ImageHelper.handleImageEffect(bitmap, mHue, mSat, mLum));
}


设置图像矩阵的代码

public static Bitmap handleImageEffect(Bitmap bm, float hue, float saturation, float lum){
Bitmap bmp = Bitmap.createBitmap(bm.getWidth(), bm.getHeight(), Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bmp);
Paint paint = new Paint();

ColorMatrix hueMatrix = new ColorMatrix();
hueMatrix.setRotate(0, hue);
hueMatrix.setRotate(1, hue);
hueMatrix.setRotate(2, hue);

ColorMatrix satMatrix = new ColorMatrix();
satMatrix.setSaturation(saturation);

ColorMatrix lumMatrix = new ColorMatrix();
lumMatrix.setScale(lum, lum, lum, 1);

ColorMatrix imageMatrix = new ColorMatrix();
imageMatrix.postConcat(hueMatrix);
imageMatrix.postConcat(satMatrix);
imageMatrix.postConcat(lumMatrix);

paint.setColorFilter(new ColorMatrixColorFilter(imageMatrix));
canvas.drawBitmap(bm, 0, 0, paint);

return bmp;
}


需要注意的是,在设置好处理的颜色矩阵后,通过使用Paint类的setColorFilter()方法,将通过imageMatrix构造的ColorMatrixColorFilter对象传递进去,并使用这个画笔来绘制原来的图像,从而将颜色矩阵作用到原图中。

同时,Android系统也不允许直接修改原图,必须通过原图创建一个同样大小的Bitmap,并将原图绘制到该Bitmap中,以一个副本的形式来修改图像。代码如下所示,bm即为原图,bmp为创建的副本。

效果如下图所示:



内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  android 图片 位图