Android学习笔记之ColorMatrix、图像处理

1.色彩矩阵

  图片是由点阵和颜色值组成的,点阵是一个包含像素的举证,每个元素对应着图片的一个像素。当我知道颜色和矩阵有关时比较惊讶,大学时学的线性代数在这里用上了。
  在Android中处理图像的色彩效果的类是颜色矩阵ColorMatrix。颜色矩阵是一个4x5的数字矩阵。对于每个像素点,都有一个颜色分量矩阵用来保存颜色的RGBA值。
A = { a b c d e f g h i j k l m n o p q r s t } C = { R G B A 1 } A= \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\} C= \left\{ \begin{matrix} R \\ G \\ B \\ A \\ 1 \end{matrix} \right\} A=afkpbglqchmrdinsejotC=RGBA1
  矩阵A是一个4x5的颜色矩阵,在Android中使用一维数组的形式来存储[a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t]。矩阵C是一个颜色矩阵分量。
  使用矩阵乘法运算AC来处理颜色分量矩阵,矩阵A、C,乘法运算过程,如下。
R = { R 1 G 1 B 1 A 1 } = A C { a b c d e f g h i j k l m n o p q r s t } { R G B A 1 } = { a R b G c B d A e f R g G h B i A j R k G l B m A n o R p G q B r A s t } = { R 1 G 1 B 1 A 1 } R =\left\{ \begin{matrix} R1\\G1 \\ B1 \\ A1 \end{matrix} \right\} =AC \\ \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 \\ Rk & Gl & Bm & An & o \\ Rp & Gq & Br & As & t \end{matrix} \right\} =\left\{ \begin{matrix} R1\\G1 \\ B1 \\ A1 \end{matrix} \right\} R=R1G1B1A1=ACafkpbglqchmrdinsejotRGBA1=aRfRRkRpbGgGGlGqcBhBBmBrdAiAAnAsejot=R1G1B1A1
R1 = a x R + b x G + c x B + d x A + e;
G1 = f x R + g x G + h x B + i x A + j;
B1 = k x R + l x G + m x B + n x A +o;
A1 = p x R + q x G + r x B + s x A + t;
A = { a b c d e f g h i j k l m n o p q r s t } A= \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\} A=afkpbglqchmrdinsejot

  • 第一行的abcde值用来决定新的颜色值中的R——红色
  • 第二行的fghij值用来决定新的颜色值中的G——绿色
  • 第三行的klmno值用来决定新的颜色值中的B——蓝色
  • 第四行的pqrst值用来决定新的颜色值中的A——透明色

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

R1 = a x R + b x G + c x B + d x A + e,令a=1,b、c、d、e都等于0,结果为R1 =R。构造一个矩阵,如下。
A = { 1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 1 0 } A= \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\} A=10000100001000010000
  如果把这个矩阵代入R1=AC,R1=R。这个矩阵通常被用来作为初始颜色矩阵来使用,不会对原有颜色值进行任何变化。
  改变颜色的两种方法:一是直接改变颜色的offset,二是改变对应RGBA的系数。

1.1改变偏移量

A = { 1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 1 0 } A = { 1 0 0 0 100 0 1 0 0 100 0 0 1 0 0 0 0 0 1 0 } A= \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\} A= \left\{ \begin{matrix} 1 & 0 & 0 & 0 & 100\\ 0 & 1 & 0 & 0 & 100 \\ 0 &0 & 1& 0& 0 \\ 0 & 0 & 0 & 1 & 0 \end{matrix} \right\} A=10000100001000010000A=100001000010000110010000
  红色、绿色分量增加了100,红绿混合会得到黄色,最后图片颜色偏黄。

1.2改变颜色系数

A = { 1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 1 0 } A = { 1 0 0 0 0 0 2 0 0 0 0 0 1 0 0 0 0 0 1 0 } A= \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\} A= \left\{ \begin{matrix} 1 & 0 & 0 & 0 & 0\\ 0 & 2 & 0 & 0 & 0\\ 0 &0 & 1& 0& 0 \\ 0 & 0 & 0 & 1 & 0 \end{matrix} \right\} A=10000100001000010000A=10000200001000010000
  改变的绿色,图片颜色偏绿,后面会有相应的展示。

2.使用ColorMatrix来改变图片的色光属性

2.1 色调

  setRotate(int axis, float degrees),axis使用0、1、2来代表R、G、B三种颜色。degrees处理的值。

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

2.2 饱和度

  setSaturation(float sat),当sat为0时,图片就变成黑白图片了。

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

2.3 亮度

  setScale(float rScale, float gScale, float bScale,float aScale),当三原色以相同比例进行混合时,就会显示出白色,系统正是使用这个原理来改变一个图像的亮度。当亮度为0时,图片就变为黑色了。

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

2.4 效果混合postConcat

  将上面三种ColorMatrix 叠加,需要通过ColorMatrix 的postConcat方法。

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

2.5 示例

  通过三个seekbar来控制图片效果的变化。
seekbar的监听事件

//MID_VALUE =127        seekbar最大值255
// float mHue = 0;
// float mStauration = 1.0f;
// float mLum = 1.0f;	
    @Override
    public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {

        int id = seekBar.getId();
        switch (id) {
            case R.id.sb_1:

                mHue = (progress - MID_VALUE) * 1.0F / MID_VALUE * 180;
                break;

            case R.id.sb_2:

                mStauration = progress * 1.0F / MID_VALUE;
                break;

            case R.id.sb_3:
                mLum = progress * 1.0F / MID_VALUE;
                break;
        }

        iv.setImageBitmap(handleImageEffect(bm, mHue, mStauration, mLum));
    }

设置图像矩阵的代码

    public 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 saturationMatrix = new ColorMatrix();
        saturationMatrix.setSaturation(saturation);

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

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

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

  设置好颜色矩阵后,需要使用paint类的setColorFilter方法,将imageMatrix构造的ColorMatrixColorFilter对象传递进去。
  Android系统不允许直接修改原图,必须通过原图创建一个同样大小的Bitmap,并将原图绘制到新的bitmap中。直接修改原图会报错java.lang.IllegalStateException: Immutable bitmap passed to Canvas constructor。
在这里插入图片描述

3.使用颜色矩阵来改变图片

  接下来我们通过直接修改矩阵中的值来改变图片的显示效果。
布局文件xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">


    <ImageView
        android:id="@+id/mm_iv"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="2" />

    <GridLayout
        android:id="@+id/mm_gl"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="3"
        android:columnCount="5"
        android:rowCount="4" />


    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginStart="10dp"
        android:layout_marginEnd="10dp"
        android:orientation="horizontal">

        <Button
            android:id="@+id/mm_but_change"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="改变" />

        <Button
            android:id="@+id/mm_but_reset"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_marginStart="20dp"
            android:layout_weight="1"
            android:text="重置" />


    </LinearLayout>


</LinearLayout>

  动态创建edittext,添加到gridlayout

    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_mmactivity);

        mm_but_change = findViewById(R.id.mm_but_change);
        mm_but_reset = findViewById(R.id.mm_but_reset);
        mm_iv = findViewById(R.id.mm_iv);
        mm_gl = findViewById(R.id.mm_gl);

        bm = BitmapFactory.decodeResource(getResources(), R.mipmap.dgdfg);
        mm_iv.setImageBitmap(bm);

        mm_gl.post(new Runnable() {
            @Override
            public void run() {

                etWidth = mm_gl.getWidth() / 5;
                etHeight = mm_gl.getHeight() / 4;
                addEts();
                initMatrix();
            }
        });
  }
      /**
     * 添加edittext
     */
    private void addEts() {

        for (int i = 0; i < ets.length; i++) {

            EditText editText = new EditText(this);
            editText.setGravity(Gravity.CENTER);
            ets[i] = editText;
            mm_gl.addView(editText, etWidth, etHeight);
        }

    }
        /**
     * 设置edittext的值
     */
    private void initMatrix() {

        for (int i = 0; i < ets.length; i++) {

            if (i % 6 == 0) {

                ets[i].setText(1 + "");
            } else {

                ets[i].setText(0 + "");
            }
        }
    }

  获得修改后的EditText值,将矩阵值设置给颜色矩阵。

    private void getMatrix() {

        for (int i = 0; i < 20; i++) {

            colorM[i] = Float.valueOf(ets[i].getText().toString());
        }
    }

    private void setImageMatrix() {

        Bitmap bitmap = Bitmap.createBitmap(bm.getWidth(), bm.getHeight(), Bitmap.Config.ARGB_8888);
        ColorMatrix colorMatrix = new ColorMatrix();
        colorMatrix.set(colorM);
        Canvas canvas = new Canvas(bitmap);
        Paint paint = new Paint();
        paint.setColorFilter(new ColorMatrixColorFilter(colorMatrix));
        canvas.drawBitmap(bm, 0, 0, paint);
        mm_iv.setImageBitmap(bitmap);
    }

  最后,设置点击事件,重置是将图片还原,改变是将现有的矩阵作用到图片。

        mm_but_change.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {

                getMatrix();
                setImageMatrix();
            }
        });
        mm_but_reset.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {

                initMatrix();
                getMatrix();
                setImageMatrix();
            }
        });

在这里插入图片描述

3.1几种图片颜色矩阵处理效果

3.1.1 灰度效果

{ 0.33 0.59 0.11 0 0 0.33 0.59 0.11 0 0 0.33 0.59 0.11 0 0 0 0 0 1 0 } \left\{ \begin{matrix} 0.33 & 0.59 & 0.11 & 0 & 0\\ 0.33 & 0.59 & 0.11 & 0 & 0 \\ 0.33 &0.59 & 0.11& 0& 0 \\ 0 & 0 & 0 & 1 & 0 \end{matrix} \right\} 0.330.330.3300.590.590.5900.110.110.11000010000
在这里插入图片描述

3.1.2 图像颜色反转

{ − 1 0 0 1 1 0 − 1 0 1 1 0 0 − 1 1 1 0 0 0 1 0 } \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\} 10000100001011111110
在这里插入图片描述

3.1.3 怀旧效果

{ 0.393 0.769 0.189 0 0 0.349 0.686 0.168 0 0 0.272 0.534 0.131 0 0 0 0 0 1 0 } \left\{ \begin{matrix} 0.393 & 0.769 & 0.189 & 0 &0\\ 0.349 &0.686 & 0.168 & 0 & 0\\ 0.272 &0.534 & 0.131& 0& 0 \\ 0 & 0 & 0 & 1 & 0 \end{matrix} \right\} 0.3930.3490.27200.7690.6860.53400.1890.1680.131000010000
在这里插入图片描述

3.1.4 去色效果

{ 1.5 1.5 1.5 0 − 1 1.5 1.5 1.5 0 − 1 1.5 1.5 1.5 0 − 1 0 0 0 1 0 } \left\{ \begin{matrix} 1.5 & 1.5 & 1.5 & 0 &-1\\ 1.5 &1.5 & 1.5 & 0 & -1\\ 1.5 &1.5 & 1.5& 0& -1 \\ 0 & 0 & 0 & 1 & 0 \end{matrix} \right\} 1.51.51.501.51.51.501.51.51.5000011110
在这里插入图片描述

3.1.5 高饱和度

{ 1.438 − 0.112 − 0.016 0 − 0.03 − 0.062 1.378 − 0.016 0 0.05 − 0.062 − 0.122 1.483 0 − 0.02 0 0 0 1 0 } \left\{ \begin{matrix} 1.438 & -0.112 & -0.016 & 0 &-0.03\\ -0.062 &1.378 & -0.016 & 0 & 0.05\\ -0.062 &-0.122 & 1.483& 0& -0.02 \\ 0 & 0 & 0 & 1 & 0 \end{matrix} \right\} 1.4380.0620.06200.1121.3780.12200.0160.0161.483000010.030.050.020
在这里插入图片描述

4.像素点分析

  通过改变每个像素点argb值,来达到修改图片。android系统提供了Bitmap.getPixels()方法来获取这个bitmap的像素点。

bm.getPixels(int[] pixels, int offset, int stride,
                          int x, int y, int width, int height)
pixels:接收每个点的颜色
offset:写入到pixels中的第一个像素索引值
stride:pixels中的行间距
x:从位图中读取的第一个像素的X坐标值
y:从位图中读取的第一个像素的y坐标值
width:从每一行中读取的像素宽度
height:读取的行数

一般使用

bm.getPixels(oldPx, 0, bm.getWidth(), 0, 0, width, height);

具体代码

 public Bitmap handleImagePixelsOldPhoto(Bitmap bm) {
 		//由于不能修改原始图片,需要创建一个图片的副本bmp 
        Bitmap bmp = Bitmap.createBitmap(bm.getWidth(), bm.getHeight(),
                Bitmap.Config.ARGB_8888);
        int width = bm.getWidth();
        int height = bm.getHeight();
        int color = 0;
        int r, g, b, a, r1, g1, b1;
		//初始化像素数组的大小
        int[] oldPx = new int[width * height];
        int[] newPx = new int[width * height];
		//获取像素点
        bm.getPixels(oldPx, 0, bm.getWidth(), 0, 0, width, height);
        for (int i = 0; i < width * height; i++) {
            color = oldPx[i];
            a = Color.alpha(color);
            r = Color.red(color);
            g = Color.green(color);
            b = Color.blue(color);
			//效果的公式,用来改变像素点的颜色
            r1 = (int) (0.393 * r + 0.769 * g + 0.189 * b);
            g1 = (int) (0.349 * r + 0.686 * g + 0.168 * b);
            b1 = (int) (0.272 * r + 0.534 * g + 0.131 * b);
			//最大值255
            if (r1 > 255) {
                r1 = 255;
            }
            if (g1 > 255) {
                g1 = 255;
            }
            if (b1 > 255) {
                b1 = 255;
            }

            newPx[i] = Color.argb(a, r1, g1, b1);
        }
        bmp.setPixels(newPx, 0, width, 0, 0, width, height);
        return bmp;
    }

在这里插入图片描述

4.1常用图像像素点处理效果

4.1.1 底片效果

处理方法都是前辈们研究出来的,这里我们只是使用。贴出代码和效果图。

    public static Bitmap handleImageNegative(Bitmap bm) {
        int width = bm.getWidth();
        int height = bm.getHeight();
        int color;
        int r, g, b, a;

        Bitmap bmp = Bitmap.createBitmap(width, height
                , Bitmap.Config.ARGB_8888);

        int[] oldPx = new int[width * height];
        int[] newPx = new int[width * height];
        bm.getPixels(oldPx, 0, width, 0, 0, width, height);

        for (int i = 0; i < width * height; i++) {
            color = oldPx[i];
            r = Color.red(color);
            g = Color.green(color);
            b = Color.blue(color);
            a = Color.alpha(color);

            r = 255 - r;
            g = 255 - g;
            b = 255 - b;

            if (r > 255) {
                r = 255;
            } else if (r < 0) {
                r = 0;
            }
            if (g > 255) {
                g = 255;
            } else if (g < 0) {
                g = 0;
            }
            if (b > 255) {
                b = 255;
            } else if (b < 0) {
                b = 0;
            }
            newPx[i] = Color.argb(a, r, g, b);
        }
        bmp.setPixels(newPx, 0, width, 0, 0, width, height);
        return bmp;
    }

在这里插入图片描述

4.1.2 浮雕效果

直接贴代码。

 public static Bitmap handleImagePixelsRelief(Bitmap bm) {
        Bitmap bmp = Bitmap.createBitmap(bm.getWidth(), bm.getHeight(),
                Bitmap.Config.ARGB_8888);
        int width = bm.getWidth();
        int height = bm.getHeight();
        int color = 0, colorBefore = 0;
        int a, r, g, b;
        int r1, g1, b1;

        int[] oldPx = new int[width * height];
        int[] newPx = new int[width * height];

        bm.getPixels(oldPx, 0, bm.getWidth(), 0, 0, width, height);
        for (int i = 1; i < width * height; i++) {
            colorBefore = oldPx[i - 1];
            a = Color.alpha(colorBefore);
            r = Color.red(colorBefore);
            g = Color.green(colorBefore);
            b = Color.blue(colorBefore);

            color = oldPx[i];
            r1 = Color.red(color);
            g1 = Color.green(color);
            b1 = Color.blue(color);

            r = (r - r1 + 127);
            g = (g - g1 + 127);
            b = (b - b1 + 127);
            if (r > 255) {
                r = 255;
            }
            if (g > 255) {
                g = 255;
            }
            if (b > 255) {
                b = 255;
            }
            newPx[i] = Color.argb(a, r, g, b);
        }
        bmp.setPixels(newPx, 0, width, 0, 0, width, height);
        return bmp;
    }

在这里插入图片描述

  • 5
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值