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这四个值,即可使原有颜色值产生相应系数的变化
改变偏移量的值,即颜色矩阵中第五列的值
在Anroid中,系统封装了一个类——ColorMatrix,也就是前面说所的颜色矩阵。通过这个类,可以很方便地通过改变矩阵值来处理颜色效果,创建一个ColorMatrix对象非常简单,代码如下所示。
第一个参数为0、1、2分别对应要改变的色调,即R,G,B。
改变的原理也是去修改颜色矩阵中对应位置的值。
当我们需要通过这个方法修改亮度时,需要保证前三个参数一致,即不会改变原图中三原色的比例,保证色度一致,只是通过调节这三个参数的值来改变亮度。
上面这三种调节方式并不是固定的,大家可以根据运算灵活搭配使用。除了使用上面三种方式单独进行颜色效果的处理之外,Android系统还封装了矩阵的乘法运算,即可实例化多个ColorMatrix,然后将他们混合,从而叠加处理效果,具体用法见后文。
滑动SeekBar获取输入值的代码如下所示。
设置图像矩阵的代码
需要注意的是,在设置好处理的颜色矩阵后,通过使用Paint类的setColorFilter()方法,将通过imageMatrix构造的ColorMatrixColorFilter对象传递进去,并使用这个画笔来绘制原来的图像,从而将颜色矩阵作用到原图中。
同时,Android系统也不允许直接修改原图,必须通过原图创建一个同样大小的Bitmap,并将原图绘制到该Bitmap中,以一个副本的形式来修改图像。代码如下所示,bm即为原图,bmp为创建的副本。
效果如下图所示:
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为创建的副本。
效果如下图所示:
相关文章推荐
- 使用C++实现JNI接口需要注意的事项
- Android IPC进程间通讯机制
- Android Manifest 用法
- [转载]Activity中ConfigChanges属性的用法
- Android之获取手机上的图片和视频缩略图thumbnails
- Android之使用Http协议实现文件上传功能
- Android学习笔记(二九):嵌入浏览器
- android string.xml文件中的整型和string型代替
- i-jetty环境搭配与编译
- android之定时器AlarmManager
- android wifi 无线调试
- Android Native 绘图方法
- Android java 与 javascript互访(相互调用)的方法例子
- android 代码实现控件之间的间距
- android FragmentPagerAdapter的“标准”配置
- Android"解决"onTouch和onClick的冲突问题
- android:installLocation简析
- android searchView的关闭事件
- SourceProvider.getJniDirectories