首页 > 代码库 > Android图片色彩处理ColorMatrix

Android图片色彩处理ColorMatrix

Android图片的处理一般是将图片转成位图(Bitmap)来的处理,位图包含着一张图片的所有数据。图片实际上是由很多个单一颜色的点阵组成的,这些点越小图片就越细腻,点的颜色是由色值ARGB,即透明度、红、绿、蓝四个分量决定。而整张图片的处理实际上就是对这些点的分量做调整。
Android系统中提供一个用于调整颜色的矩阵ColorMatrix,这是一个4x5的数字矩阵。而颜色的色值则保存在一个4x1的矩阵中。色值的区间为[0,255]。以下是ColorMatrix部分注解

/**
 * 4x5 matrix for transforming the color and alpha components of a Bitmap.
 * The matrix can be passed as single array, and is treated as follows:
 *
 * <pre>
 *  [ a, b, c, d, e,
 *    f, g, h, i, j,
 *    k, l, m, n, o,
 *    p, q, r, s, t ]</pre>
 *
 * <p>
 * When applied to a color <code>[R, G, B, A]</code>, the resulting color
 * is computed as:
 * </p>
 *
 * <pre>
 *   R‘ = a*R + b*G + c*B + d*A + e;
 *   G‘ = f*R + g*G + h*B + i*A + j;
 *   B‘ = k*R + l*G + m*B + n*A + o;
 *   A‘ = p*R + q*G + r*B + s*A + t;</pre>
 *
 * <p>
 * That resulting color <code>[R‘, G‘, B‘, A‘]</code>
 * then has each channel clamped to the <code>0</code> to <code>255</code>
 * range.
 * </p>
 */

由此上面注释我们可以知颜色矩阵m,和颜色分量矩阵C。

m=?????afkpbglqchmrdinsejot?????C=?????RGBA?????
<script type="math/tex; mode=display" id="MathJax-Element-242"> m= \begin{bmatrix} a & b & c & d & e \ f & g & h & i & j \ k & l & m & n & o \ p & q & r & s & t \end{bmatrix} C= \begin{bmatrix} R\ G\ B\ A \end{bmatrix} </script>


即新的颜色分量C’是颜色矩阵m乘以颜色分量矩阵新加一列值为1的5x1的矩阵所得的4x1矩阵,矩阵乘法公式可看注解

C=?????afkpbglqchmrdinsejot?????????????RGBA1????????=?????aRfRkRpRbGgGlGqGcBhBmBrBdAiAnAsAejot?????=??????RGBA??????
<script type="math/tex; mode=display" id="MathJax-Element-241"> C‘= \left[ \begin{matrix} a & b & c & d & e \ f & g & h & i & j \ k & l & m & n & o \ p & q & r & s & t \end{matrix} \right] \left[ \begin{matrix} R\ G\ B\ A\ 1 \end{matrix} \right] = \left[ \begin{matrix} aR & bG & cB & dA & e \ fR & gG & hB & iA & j \ kR & lG & mB & nA & o \ pR & qG & rB & sA & t \end{matrix} \right] = \left[ \begin{matrix} R‘\ G‘\ B‘\ A‘ \end{matrix} \right] </script>
由此可见新的颜色色值和处理之前的颜色色值之间的关系,矩阵m中各行的前四个分别决定新色值的RGBA,各行的第五个系数为新色值分量的偏移量。初始的颜色矩阵为:

?????10000100001000010000?????
<script type="math/tex; mode=display" id="MathJax-Element-183"> \left[ \begin{matrix} 1 & 0 & 0 & 0 & 0 \ 0 & 1 & 0 & 0 & 0 \ 0 & 0 & 1 & 0 & 0 \ 0 & 0 & 0 & 1 & 0 \end{matrix} \right] </script>
在使用时颜色矩阵的值是以一个一维float类型数组的形式来存储的,以下是一个通过ColorMatrix处理Bitmap图片的方法,实际上是ColorMatrix中初始化的矩阵,所以图片没变化,可自己更改数组。

private float[] mColorMatrix = new float[20];
private Bitmap bitmap= BitmapFactory.decodeResource(getResources(),R.mipmap.maidou);

public void reset() {
        final float[] a = mArray;
        Arrays.fill(a, 0);
        a[0] = a[6] = a[12] = a[18] = 1;
    }

private Bitmap setImageMatrix(Bitmap bitmap){
        reset();
        Bitmap bmp = Bitmap.createBitmap(bitmap.getWidth(),bitmap.getHeight(), Bitmap.Config.ARGB_8888);
        ColorMatrix colorMatrix = new ColorMatrix();
        colorMatrix.set(mColorMatrix);
        Canvas canvas = new Canvas(bmp);
        Paint paint = new Paint();
        paint.setColorFilter(new ColorMatrixColorFilter(colorMatrix));
        canvas.drawBitmap(bitmap,0,0,paint);
        return bmp;
    }

由于Android系统不允许直接对原图做修改,因此先创建一个副本图片,再通过ColorMatrix的set方法设置颜色矩阵,使用Paint类的setColorFilter()方法,传入一个带有ColorMatrix对象的ColorMatrixColorFilter对象,使用这支修饰过的画笔在Canvas上绘制原图,从而使颜色矩阵作用在原图上。


我们使用照相机时往往能看到不同的照相效果,如:灰度、怀旧等其实也是通过改变图像的色值来显示效果。而改变色值不同的效果有不同的颜色矩阵,举例有:灰度效果(a)、图像反转(b)、怀旧效果(c)、高饱和度(d)

a=?????0.33F0.33F0.33F00.59F0.59F0.59F00.11F0.11F0.11F000010000?????b=??????10000?10000?1011111110?????c=?????0.393F0.349F0.272F00.769F0.686F0.534F00.189F0.168F0.131F000010000?????d=?????1.438F?0.062F?0.062F0?0.122F1.378F?0.122F0?0.016F?0.016F1.483F00001?0.03F0.05F?0.02F0?????
<script type="math/tex; mode=display" id="MathJax-Element-280"> a= \left[ \begin{matrix} 0.33F & 0.59F & 0.11F & 0 & 0 \ 0.33F & 0.59F & 0.11F & 0 & 0 \ 0.33F & 0.59F & 0.11F & 0 & 0 \ 0 & 0 & 0 & 1 & 0 \end{matrix} \right] b= \left[ \begin{matrix} -1 & 0 & 0 & 1 & 1 \ 0 & -1 & 0 & 1 & 1 \ 0 & 0 & -1 & 1 & 1 \ 0 & 0 & 0 & 1 & 0 \end{matrix} \right] c= \left[ \begin{matrix} 0.393F & 0.769F & 0.189F & 0 & 0 \ 0.349F & 0.686F & 0.168F & 0 & 0 \ 0.272F & 0.534F & 0.131F & 0 & 0 \ 0 & 0 & 0 & 1 & 0 \end{matrix} \right] d= \left[ \begin{matrix} 1.438F & -0.122F & -0.016F & 0 & -0.03F \ -0.062F & 1.378F & -0.016F & 0 & 0.05F \ -0.062F & -0.122F & 1.483F & 0 & -0.02F \ 0 & 0 & 0 & 1 & 0 \end{matrix} \right] </script>
效果分别为:
技术分享
技术分享
技术分享
技术分享


再回来看ColorMatrix类,其实除了直接设置矩阵的值外,该类还封装了一些API来快速调整矩阵参数,如:通过setRotate()方法设置色调、setSaturation()方法设置饱和度、setScale()方法设置亮度。这些方法使用起来很简单

public static Bitmap handleImageEffect(Bitmap bitmap,float rotate,float saturation,float scale){
        // 创建副本,用于将处理过的图片展示出来而不影响原图,Android系统也不允许直接修改原图
        Bitmap bmp = Bitmap.createBitmap(bitmap.getWidth(),bitmap.getHeight(), Bitmap.Config.ARGB_8888);
        Canvas canvas = new Canvas(bmp);
        Paint paint = new Paint();

        // 修改色调,即色彩矩阵围绕某种颜色分量旋转
        ColorMatrix rotateMatrix = new ColorMatrix();
        // 0,1,2分别代表像素点颜色矩阵中的Red,Green,Blue分量
        rotateMatrix.setRotate(0,rotate);
        rotateMatrix.setRotate(1,rotate);
        rotateMatrix.setRotate(2,rotate);

        // 修改饱和度
        ColorMatrix saturationMatrix = new ColorMatrix();
        saturationMatrix.setSaturation(saturation);

        // 修改亮度,即某种颜色分量的缩放
        ColorMatrix scaleMatrix = new ColorMatrix();
        // 分别代表三个颜色分量的亮度
        scaleMatrix.setScale(scale,scale,scale,1);

        //将三种效果结合
        ColorMatrix imageMatrix = new ColorMatrix();
        imageMatrix.postConcat(rotateMatrix);
        imageMatrix.postConcat(saturationMatrix);
        imageMatrix.postConcat(scaleMatrix);

        paint.setColorFilter(new ColorMatrixColorFilter(imageMatrix));
        canvas.drawBitmap(bitmap,0,0,paint);
        return bmp;
    }

以上便是三个方法简单的使用,由于ColorMatrix类方法比较少,也比较容易看懂,可以自行看源码:
色调调节setRotate(int axis, float degrees):
其中第一个参数axis是固定可选的,为0、1、2,分别表示改变Red、Green、Blue三个颜色分量,第二个参数degrees表示旋转角度,由旋转角度通过三角函数变换得到不同的矩阵,其中a为角度,单位为°。

0=?????10000cosa?sina00sina1000010000?????1=?????cosa0sina00100?sina0cosa000010000?????2=?????cosa?sina00sinacosa00001000010000?????
<script type="math/tex; mode=display" id="MathJax-Element-268"> 0= \left[ \begin{matrix} 1 & 0 & 0 & 0 & 0 \ 0 & cos\angle a & sin\angle a & 0 & 0 \ 0 & -sin\angle a & 1 & 0 & 0 \ 0 & 0 & 0 & 1 & 0 \end{matrix} \right] 1= \left[ \begin{matrix} cos\angle a & 0 & -sin\angle a & 0 & 0 \ 0 & 1 & 0 & 0 & 0 \ sin\angle a & 0 & cos\angle a & 0 & 0 \ 0 & 0 & 0 & 1 & 0 \end{matrix} \right] 2= \left[ \begin{matrix} cos\angle a & sin\angle a & 0 & 0 & 0 \ -sin\angle a & cos\angle a & 0 & 0 & 0 \ 0 & 0 & 1 & 0 & 0 \ 0 & 0 & 0 & 1 & 0 \end{matrix} \right] </script>
饱和度调节setSaturation(float sat):
这个方法只改颜色部分即左上九个参数, 有一定的公式,大致为如下:
?????R+satRR0GG+satG0BBB+sat000010000?????
<script type="math/tex; mode=display" id="MathJax-Element-269"> \left[ \begin{matrix} R + sat & G & B & 0 & 0 \ R & G + sat & B & 0 & 0 \ R & G & B + sat & 0 & 0 \ 0 & 0 & 0 & 1 & 0 \end{matrix} \right] </script>
其中:

invSat = 1 - sat
R = 0.213f * invSat
G = 0.715f * invSat
B = 0.072f * invSat

当饱和度sat为0时图片显灰度。
亮度调节setScale(float rScale, float gScale, float bScale,float aScale):
其矩阵为:

?????rScaleRR0GgScaleG0BBbScale0000aScale0000?????
<script type="math/tex; mode=display" id="MathJax-Element-270"> \left[ \begin{matrix} rScale & G & B & 0 & 0 \ R & gScale & B & 0 & 0 \ R & G & bScale & 0 & 0 \ 0 & 0 & 0 & aScale & 0 \end{matrix} \right] </script>
当亮度为0时图片呈黑色。
效果叠加
preConcat(ColorMatrix prematrix)和postConcat(ColorMatrix postmatrix)两个方法分别是将目标效果矩阵放在本矩阵之前和放在本矩阵之后,由于矩阵的乘法一般不满足交换律,因此前后关系会对效果有不同的影响。

<script type="text/javascript"> $(function () { $(‘pre.prettyprint code‘).each(function () { var lines = $(this).text().split(‘\n‘).length; var $numbering = $(‘
    ‘).addClass(‘pre-numbering‘).hide(); $(this).addClass(‘has-numbering‘).parent().append($numbering); for (i = 1; i <= lines; i++) { $numbering.append($(‘
  • ‘).text(i)); }; $numbering.fadeIn(1700); }); }); </script>

    Android图片色彩处理ColorMatrix