Android图像处理(Bitmap与矩阵)

Android图像处理(Bitmap与矩阵)

在Android开发的时候经常会有对Bitmap进行处理的需求,例如处理饱和度,亮度等信息,或者加滤镜等需求
效果
以上图的效果为例,这几个效果是我手机自带的图片处理效果可以简单的将原图处理成黑白,高亮度等这样的风格.

色相/饱和度/亮度

这里写图片描述
首先介绍一下图片处理的一些基本概念

色相

色相就是指图片所传递的颜色,以图片为例,中间的圆环代表的就是色相,我们可以通过改变某个颜色的色相来达到改变颜色的目的,例如我们现在显示的颜色是红色,沿着色相环顺时针旋转180°那么图片的颜色就变成了绿色了

饱和度

所谓饱和度指的就是颜色的纯度,还是这张图片,以红色为例,沿着这个圆盘的直径方向显示的颜色都是红色,但是我们可以发现,越靠近圆心红色的纯度就越低,越远离圆心颜色的纯度就越高,这就是改变颜色的饱和度,一个图片的饱和度越高,那么图片就会越浓艳,而如果一直将图片的饱和度降低,那么图片最后就会是一张黑白照片了

亮度

亮度就是我们日常生活中的亮度概念,上图中上线的这条线就是亮度,如果亮度到头了,那么就是纯白色,如果亮度最低,就是一张纯黑色的图片了

改变图片的三种属性

我们开始上代码
打开我们的AndroidStudio,新建一个工程,创建一个类ImageHelper,我们将在这个类里去改变图片的各个属性,直接上代码

package com.lanou3g.bitmapdemo.change;

import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.ColorMatrix;
import android.graphics.ColorMatrixColorFilter;
import android.graphics.Paint;

/**
 * Created by 陈丰尧 
 * 来改变图片的色相 饱和度  和亮度
 */

public class ImageHelper {
    
    
    /**
     *
     * @param resource 原图
     * @param hue 色相
     * @param saturation 饱和度
     * @param lum 亮度
     * @return
     */
    public static Bitmap getChangedBitmap(Bitmap resource,
                                          float hue,
                                          float saturation,
                                          float lum){
        Bitmap out = Bitmap.createBitmap(resource.getWidth()
                ,resource.getHeight(), Bitmap.Config.ARGB_8888);
        Canvas canvas = new Canvas(out);
        Paint paint = new Paint();
        paint.setAntiAlias(true);//抗锯齿

        //调整饱和度
        ColorMatrix saturationMatrix = new ColorMatrix();
        saturationMatrix.setSaturation(saturation);

        //调整色相
        ColorMatrix hueMatrix = new ColorMatrix();
        hueMatrix.setRotate(0,hue);//调整红像素的色相
        hueMatrix.setRotate(1,hue);
        hueMatrix.setRotate(2,hue);

        //调整亮度
        ColorMatrix lumMatrix = new ColorMatrix();
        lumMatrix.setScale(lum,lum,lum,1);

        //把色相/饱和度/明度 合并成一个ColorMatrix
        ColorMatrix colorMatrix = new ColorMatrix();
        colorMatrix.postConcat(hueMatrix);
        colorMatrix.postConcat(saturationMatrix);
        colorMatrix.postConcat(lumMatrix);

        paint.setColorFilter(new ColorMatrixColorFilter(colorMatrix));
        canvas.drawBitmap(resource,0,0,paint);
        return out;

    }
}

我们将通过这个类来改变图片的色相饱和度和亮度,这个类只有一个方法,传入四个参数分辨是原图,色相,饱和度和亮度,我们是没有办法直接操作原图的,所以在方法一开始我们就利用Canvas画出一个新的图片,将原图调整好输出到这张新的图片上,而调整饱和度,色相,和亮度都是利用了ColorMatrix这个类,调整色相的时候是调用setRotate方法,这个方法的第一个参数指的是要调整的颜色,0代表红像素,1代表绿像素,2代表蓝像素;方法setScale是用来调整亮度的,四个参数分别值得是红,绿,蓝,而透明的这个参数我们一般不会调整它的亮度的,所以我们这里直接给1,接下来我们就可以把这三个ColorMatrix都给到我们的Paint对象了,这样就可以调整了
接下来是布局文件

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/activity_change_color"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    android:orientation="vertical"
    tools:context="com.lanou3g.bitmapdemo.change.ChangeColorActivity">

    <ImageView
        android:id="@+id/change_color_iv"
        android:layout_width="300dp"
        android:layout_height="300dp"
        android:layout_gravity="center_horizontal"/>

    <SeekBar
        android:id="@+id/hue_seek_bar"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:max="256"
        android:progress="128"/>
    <SeekBar
        android:id="@+id/saturation_seek_bar"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:max="256"
        android:progress="128"/>
    <SeekBar
        android:id="@+id/lum_seek_bar"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:max="256"
        android:progress="128"/>

</LinearLayout>

这里我们给一个ImageView ,三个SeekBar 分别就是用来调整色相,饱和度透明度的
接着是Activity的代码

package com.lanou3g.bitmapdemo.change;

import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.ImageView;
import android.widget.SeekBar;

import com.lanou3g.bitmapdemo.R;

public class ChangeColorActivity extends AppCompatActivity implements SeekBar.OnSeekBarChangeListener {
    
    
    private ImageView mChangeColorIv;
    private SeekBar mHueSeekBar, mSaturationSeekBar, mLumSeekBar;

    private Bitmap mBitmap;

    private float mHue = 0, mSaturation = 1f, mLum = 1f;
    private static final int MID_VALUE = 128;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_change_color);

        mChangeColorIv = (ImageView) findViewById(R.id.change_color_iv);
        mHueSeekBar = (SeekBar) findViewById(R.id.hue_seek_bar);
        mSaturationSeekBar = (SeekBar) findViewById(R.id.saturation_seek_bar);
        mLumSeekBar = (SeekBar) findViewById(R.id.lum_seek_bar);

        //获得图片资源
        mBitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.test);
        mChangeColorIv.setImageBitmap(mBitmap);

        //对seekBar设置监听
        mHueSeekBar.setOnSeekBarChangeListener(this);
        mSaturationSeekBar.setOnSeekBarChangeListener(this);
        mLumSeekBar.setOnSeekBarChangeListener(this);


    }

    @Override
    public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
        switch (seekBar.getId()) {
            case R.id.hue_seek_bar:
                //色相的范围是正负180
                mHue = (progress - MID_VALUE) * 1f / MID_VALUE * 180;
                break;
            case R.id.saturation_seek_bar:
                //范围是0-2;
                mSaturation = progress * 1f / MID_VALUE;
                break;
            case R.id.lum_seek_bar:
                mLum = progress * 1f / MID_VALUE;
                break;
        }

        Bitmap bitmap = ImageHelper.getChangedBitmap(mBitmap,
                mHue, mSaturation, mLum);
        mChangeColorIv.setImageBitmap(bitmap);
    }

    @Override
    public void onStartTrackingTouch(SeekBar seekBar) {

    }

    @Override
    public void onStopTrackingTouch(SeekBar seekBar) {

    }
}

我们在代码中通过对三个SeekBar设置监听,来达到改变各个值得目的,这里需要说明的是
色相的取值范围是±180之间,参照我们之前给的图片,色相实际上是对色相盘进行旋转,而饱和度的取值是0-2,这里2并不是最大值,而是2是效果比较好的一个值,亮度也是0-2,0代表纯黑,2也是一个还能看的取值,1代表原始图片,我们运行一下看看效果
色相

饱和度

亮度

颜色矩阵

其实我们无论是改变图片的色相,饱和度还是亮度,都是使用的是矩阵相乘,在Android中一个像素点可以用矩阵来表示

RGBA1

而我们操作的矩阵 则是一个四行五列的矩阵
afkpbglqchmrdinsejot

只要是对图片进行改变就是操作这个矩阵,然后将这个变换矩阵与图片的原始颜色矩阵进行相乘,就会得到每一个像素新的颜色矩阵.例如将矩阵中的e和j都填上100,然后其他元素除了主对角线为1,其他都是0,这样 新的颜色矩阵就会向黄色偏移,那么我们来操作这样的矩阵就能达到滤镜的效果
下面我们来实现一个怀旧风格滤镜,

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/activity_change_color"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    android:orientation="vertical"
    tools:context="com.lanou3g.bitmapdemo.change.ChangeColorActivity">

    <ImageView
        android:id="@+id/change_color_iv"
        android:layout_width="300dp"
        android:layout_height="300dp"
        android:layout_gravity="center_horizontal"/>

    <SeekBar
        android:id="@+id/hue_seek_bar"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:max="256"
        android:progress="128"/>
    <SeekBar
        android:id="@+id/saturation_seek_bar"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:max="256"
        android:progress="128"/>
    <SeekBar
        android:id="@+id/lum_seek_bar"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:max="256"
        android:progress="128"/>

    <Button
        android:id="@+id/old_time_btn"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="怀旧风格"/>

</LinearLayout>

在布局文件中添加一个按钮

package com.lanou3g.bitmapdemo.change;

import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.ColorMatrix;
import android.graphics.ColorMatrixColorFilter;
import android.graphics.Paint;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.SeekBar;

import com.lanou3g.bitmapdemo.R;

public class ChangeColorActivity extends AppCompatActivity implements SeekBar.OnSeekBarChangeListener {
    
    
    private ImageView mChangeColorIv;
    private SeekBar mHueSeekBar, mSaturationSeekBar, mLumSeekBar;

    private Bitmap mBitmap;

    private float mHue = 0, mSaturation = 1f, mLum = 1f;
    private static final int MID_VALUE = 128;

    private Button oldTimeBtn;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_change_color);

        mChangeColorIv = (ImageView) findViewById(R.id.change_color_iv);
        mHueSeekBar = (SeekBar) findViewById(R.id.hue_seek_bar);
        mSaturationSeekBar = (SeekBar) findViewById(R.id.saturation_seek_bar);
        mLumSeekBar = (SeekBar) findViewById(R.id.lum_seek_bar);

        //获得图片资源
        mBitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.test);
        mChangeColorIv.setImageBitmap(mBitmap);

        //对seekBar设置监听
        mHueSeekBar.setOnSeekBarChangeListener(this);
        mSaturationSeekBar.setOnSeekBarChangeListener(this);
        mLumSeekBar.setOnSeekBarChangeListener(this);

        oldTimeBtn = (Button) findViewById(R.id.old_time_btn);
        oldTimeBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                //直接操作矩阵 来改变图片的风格(加滤镜);
                Bitmap bmp = Bitmap.createBitmap(mBitmap.getWidth(), mBitmap.getHeight(),
                        Bitmap.Config.ARGB_8888);
                Canvas canvas = new Canvas(bmp);
                Paint paint = new Paint();
                paint.setAntiAlias(true);
                //操作矩阵
                ColorMatrix colorMatrix = new ColorMatrix(new float[]{
                        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
                });
                paint.setColorFilter(new ColorMatrixColorFilter(colorMatrix));
                canvas.drawBitmap(mBitmap, 0, 0, paint);
                mChangeColorIv.setImageBitmap(bmp);
            }
        });
    }

    @Override
    public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
        switch (seekBar.getId()) {
            case R.id.hue_seek_bar:
                //色相的范围是正负180
                mHue = (progress - MID_VALUE) * 1f / MID_VALUE * 180;
                break;
            case R.id.saturation_seek_bar:
                //范围是0-2;
                mSaturation = progress * 1f / MID_VALUE;
                break;
            case R.id.lum_seek_bar:
                mLum = progress * 1f / MID_VALUE;
                break;
        }

        Bitmap bitmap = ImageHelper.getChangedBitmap(mBitmap,
                mHue, mSaturation, mLum);
        mChangeColorIv.setImageBitmap(bitmap);
    }

    @Override
    public void onStartTrackingTouch(SeekBar seekBar) {

    }

    @Override
    public void onStopTrackingTouch(SeekBar seekBar) {

    }
}

当点击按钮的时候,直接作用在矩阵上,绘制出新的图片,下面来看一看效果
怀旧风格
可以看到,图片的风格就被处理成这种怀旧的了

猜你喜欢

转载自blog.csdn.net/cfy137000/article/details/54646912