Android:修圖技術(shù)之濾鏡效果實現(xiàn)及原理分析——ColorMatrix

色光三原色

Android對于圖片的處理以故,最常使用到的數(shù)據(jù)結(jié)構(gòu)是位圖——Bitmap,它包含了一張圖片所有的數(shù)據(jù)裆操。整個圖片都是由點陣和顏色值組成的怒详,所謂點陣就是一個包含像素的矩陣,每一個元素對應(yīng)著圖片的一個像素踪区。而顏色值——ARGB昆烁,分別對應(yīng)著透明度、紅缎岗、綠静尼、藍這四個通道分量,他們共同決定了每個像素點顯示的顏色。上圖顯示的就是色光三原色鼠渺。

色彩矩陣分析

在Android中蜗元,系統(tǒng)使用一個顏色矩陣——ColorMatrix,來處理圖像的色彩效果系冗。對于圖像的每個像素點奕扣,都有一個顏色分量矩陣用來保存顏色的RGBA值(下圖矩陣C),Android中的顏色矩陣是一個 4x5 的數(shù)字矩陣掌敬,它用來對圖片的色彩進行處理(下圖矩陣A)惯豆。 如下:

如果我們想要改變一張圖像的色彩顯示效果,在Android系統(tǒng)中奔害,我們會用矩陣的乘法運算來修改顏色分量矩陣的值楷兽。上面矩陣A就是一個 4x5 的顏色矩陣。在Android中华临,它會以一維數(shù)組的形式來存儲[a,b,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t]芯杀,而C則是一個顏色矩陣分量。在處理圖像時雅潭,使用矩陣乘法運算AC來處理顏色分量矩陣揭厚,如下:

利用線性代數(shù)知識可知:

R1 = aR + bG + cB + dA + e;
G1 = fR + gG + hB + iA + j;
B1 = kR + lG + mB + nA + o;
A1 = pR + qG + rB + sA + t;

從這個公式可以發(fā)現(xiàn),矩陣A中:

  • 第一行的 abcde 用來決定新的顏色值中的R——紅色
  • 第二行的 fghij 用來決定新的顏色值中的G——綠色
  • 第三行的 klmno 用來決定新的顏色值中的B——藍色
  • 第四行的 pqrst 用來決定新的顏色值中的A——透明度
  • 矩陣A中第五列——ejot 值分別用來決定每個分量中的 offset 扶供,即偏移量

這樣劃分好后筛圆,這些值的作用就比較明確了。

初始顏色矩陣

接下來椿浓,我們重新看一下矩陣變換的計算公式太援,以R分量為例,

R1 = aR + bG + cB + dA + e;

如果令 a=1扳碍,b提岔、c、d笋敞、e都等于0碱蒙,則有 R1=R 。同理對第二液样、三振亮、四巧还、行進行操作鞭莽,可以構(gòu)造出一個矩陣,如下:

把這個矩陣代入公式 R=AC麸祷,根據(jù)矩陣乘法運算法則澎怒,可得R1=R,G1=G,B1=B喷面,A1=A星瘾。即不會對原有顏色進行任何修改,所以這個矩陣通常被用來作為初始顏色矩陣惧辈。

改變顏色值

那么琳状,當我們想要改變顏色值的時候,通常有兩種方法盒齿。

  • 改變顏色的 offset(偏移量)的值念逞;
  • 改變對應(yīng) RGBA 值的系數(shù)。
1.改變偏移量

從前面的分析中可知边翁,改變顏色的偏移量就是改變顏色矩陣的第五列的值翎承,其他保持初始矩陣的值即可。如下示例:

上面的操作中改變了 R符匾、G 對應(yīng)的顏色偏移量叨咖,那么結(jié)果就是圖像的紅色和綠色分量增加了100,即整體色調(diào)偏黃顯示啊胶。
看看下面的對比圖就一目了然了:

左原圖——右圖增加了紅綠分量
2.改變顏色系數(shù)

如下操作:

改變 G 分量對應(yīng)的系數(shù) g 的值甸各,增加到2倍,這樣在矩陣運算后焰坪,圖像會整體色調(diào)偏綠顯示痴晦。
請看下面的對比圖:

左原圖——右圖綠色分量系數(shù)為2

通過前面的分析,我們知道調(diào)整顏色矩陣可以改變圖像的色彩效果琳彩,圖像的色彩處理很大程度上就是在尋找處理圖像的顏色矩陣誊酌。
下面,我們著手寫一個demo露乏,模擬一個 4x5 的顏色矩陣來體驗一下上面對顏色矩陣的分析碧浊。
先來看看效果圖:

ColorMatrix

關(guān)鍵代碼是將 4x5 矩陣轉(zhuǎn)換成一維數(shù)組,然后再將這一維數(shù)組設(shè)置到ColorMatrix類里去瘟仿,請看代碼:

    //將矩陣設(shè)置到圖像
    private void setImageMatrix() {
        Bitmap bmp = Bitmap.createBitmap(bitmap.getWidth(), bitmap.getHeight(), Bitmap.Config.ARGB_8888);
        ColorMatrix colorMatrix = new ColorMatrix();
        colorMatrix.set(mColorMatrix);//將一維數(shù)組設(shè)置到ColorMatrix

        Canvas canvas = new Canvas(bmp);
        Paint paint = new Paint();
        paint.setColorFilter(new ColorMatrixColorFilter(colorMatrix));
        canvas.drawBitmap(bitmap, 0, 0, paint);
        iv_photo.setImageBitmap(bmp);
    }

這個demo里面的代碼比較簡單箱锐,我在這里就全部貼出來了,先上xml布局:

<?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:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.example.deeson.mycolormatrix.MainActivity"
    android:orientation="vertical">

    <ImageView
        android:id="@+id/iv_photo"
        android:layout_width="300dp"
        android:layout_height="0dp"
        android:layout_weight="3"
        android:layout_gravity="center_horizontal"/>

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

    </GridLayout>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal">

        <Button
            android:id="@+id/btn_change"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="change"/>
        <Button
            android:id="@+id/btn_reset"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="reset"/>
    </LinearLayout>

</LinearLayout>

在 MainActivity 類這里有一個地方要注意的就是劳较,我們無法在 onCreate() 方法中獲得 4x5 矩陣視圖的寬高值驹止,所以通過 View 的 post() 方法,在視圖創(chuàng)建完畢后獲得其寬高值观蜗。如下:

matrixLayout.post(new Runnable() {
    @Override
    public void run() {
        mEtWidth = matrixLayout.getWidth() / 5;
        mEtHeight = matrixLayout.getHeight() / 4;
        addEts();
        initMatrix();
    }

});

接下來是 MainActivity 類的全部代碼臊恋,也沒有幾行,相信大家都能看得明白墓捻,如下:

package com.example.deeson.mycolormatrix;

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.text.InputType;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.GridLayout;
import android.widget.ImageView;

/**
 * @author Deeson
 */
public class MainActivity extends AppCompatActivity implements View.OnClickListener {

    Bitmap bitmap;
    ImageView iv_photo;
    GridLayout matrixLayout;
    //每個edittext的寬高
    int mEtWidth;
    int mEtHeight;
    //保存20個edittext
    EditText[] mEts = new EditText[20];

    //一維數(shù)組保存20個矩陣值
    float[] mColorMatrix = new float[20];


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.iv_model);
        iv_photo = (ImageView) findViewById(R.id.iv_photo);
        matrixLayout = (GridLayout) findViewById(R.id.matrix_layout);
        Button btn_change = (Button) findViewById(R.id.btn_change);
        Button btn_reset = (Button) findViewById(R.id.btn_reset);
        btn_change.setOnClickListener(this);
        btn_reset.setOnClickListener(this);
        iv_photo.setImageBitmap(bitmap);

        //我們無法在onCreate()方法中獲得視圖的寬高值抖仅,所以通過View的post()方法,在視圖創(chuàng)建完畢后獲得其寬高值
        matrixLayout.post(new Runnable() {
            @Override
            public void run() {
                mEtWidth = matrixLayout.getWidth() / 5;
                mEtHeight = matrixLayout.getHeight() / 4;
                addEts();
                initMatrix();
            }

        });
    }

    //動態(tài)添加edittext
    private void addEts() {
        for (int i = 0; i < 20; i++) {
            EditText et = new EditText(this);
            et.setInputType(InputType.TYPE_CLASS_NUMBER | InputType.TYPE_NUMBER_FLAG_DECIMAL);
            mEts[i] = et;
            matrixLayout.addView(et, mEtWidth, mEtHeight);
        }
    }

    //初始化顏色矩陣
    private void initMatrix() {
        for (int i = 0; i < 20; i++) {
            if (i % 6 == 0) {
                mEts[i].setText(String.valueOf(1));
            } else {
                mEts[i].setText(String.valueOf(0));
            }
        }
    }


    //獲取矩陣值
    private void getMatrix() {
        for (int i = 0; i < 20; i++) {
            String matrix = mEts[i].getText().toString();
            boolean isNone = null == matrix || "".equals(matrix);
            mColorMatrix[i] = isNone ? 0.0f : Float.valueOf(matrix);
            if (isNone) {
                mEts[i].setText("0");
            }
        }
    }

    //將矩陣設(shè)置到圖像
    private void setImageMatrix() {
        Bitmap bmp = Bitmap.createBitmap(bitmap.getWidth(), bitmap.getHeight(), Bitmap.Config.ARGB_8888);
        ColorMatrix colorMatrix = new ColorMatrix();
        colorMatrix.set(mColorMatrix);//將一維數(shù)組設(shè)置到ColorMatrix

        Canvas canvas = new Canvas(bmp);
        Paint paint = new Paint();
        paint.setColorFilter(new ColorMatrixColorFilter(colorMatrix));
        canvas.drawBitmap(bitmap, 0, 0, paint);
        iv_photo.setImageBitmap(bmp);
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.btn_change:
                //作用矩陣效果
                break;
            case R.id.btn_reset:
                //重置矩陣效果
                initMatrix();
                break;
        }
        //作用矩陣效果
        getMatrix();
        setImageMatrix();
    }
}

MyColorMatrixDemo 下載地址

至此,我們可以通過 MyColorMatrixDemo 來對圖像的色彩特效進行精確地修改撤卢。
在了解了Android的顏色矩陣之后环凿,我們知道可以通過修改顏色矩陣的值來改變圖像的色彩顯示效果。接下來放吩,我們看看Android系統(tǒng)給我們提供的快速修改圖像色彩特效的API智听。

圖像的色光屬性

在色彩處理中,通常使用以下三個角度來描述一個圖像渡紫。

  • 色調(diào)——物體傳播的顏色
  • 飽和度——顏色的純度瞭稼,從0(灰)到100%(飽和)來進行描述
  • 亮度——顏色的相對明暗程度

在Android 的 ColorMatrix 顏色矩陣中也封裝了一些 API 來快速調(diào)整上面這三個顏色參數(shù),而不用每次都去計算矩陣的值腻惠。

下面我們來看看如何調(diào)用這些API:
詳情可參考這個文檔 https://developer.android.com/reference/android/graphics/ColorMatrix.html

  • 色調(diào)

Android系統(tǒng)提供了 setRotate(int axis, float degrees)方法來修改顏色的色調(diào)环肘。第一個參數(shù),用0集灌、1悔雹、2分別代表紅、綠欣喧、藍三個顏色通道腌零,第二個參數(shù)就是要修改的值,如下:

ColorMatrix hueMatrix = new ColorMatrix();
hueMatrix.setRotate(0,hue0);
hueMatrix.setRotate(1,hue1);
hueMatrix.setRotate(2,hue2);

Android系統(tǒng)的 setRotate(int axis, float degrees) 方法其實就是對色彩的旋轉(zhuǎn)運算唆阿。RGB色是如何旋轉(zhuǎn)的呢益涧,首先用R、G驯鳖、B三色建立三維坐標系闲询,如下:

RBG坐標

這里,我們可以把一個色彩值看成三維空間里的一個點浅辙,色彩值的三個分量可以看成該點對應(yīng)的坐標(三維坐標)扭弧。我們先不考慮,在三個維度綜合情況下是怎么旋轉(zhuǎn)的记舆,我們先看看鸽捻,在某個軸做為Z軸,在另兩個軸形成的平面上旋轉(zhuǎn)的情況泽腮。假如御蒲,我們現(xiàn)在需要圍繞藍色軸進行旋轉(zhuǎn),我們對著藍色箭頭觀察由紅色和綠色構(gòu)造的平面诊赊。然后順時針旋轉(zhuǎn) α 度厚满。 如下圖所示:

圍繞藍色軸進行旋轉(zhuǎn) α 度

在圖中,我們可以看到豪筝,在旋轉(zhuǎn)后痰滋,原 R 在 R 軸的分量變?yōu)椋篟*cosα兄世,且原G分量在旋轉(zhuǎn)后在 R 軸上也有了分量掂碱,所以我們要加上這部分分量,因此最終的結(jié)果為 R’=R*cosα + G*sinα绞旅,同理严望,在計算 G’ 時多艇,因為 R 的分量落在了負軸上,所以我們要減去這部分像吻,故 G’=G*cosα - R*sinα峻黍;
回憶之前講過的矩陣乘法運算法則,下圖:
![][01]

R1 = aR + bG + cB + dA + e;
G1 = fR + gG + hB + iA + j;
B1 = kR + lG + mB + nA + o;
A1 = pR + qG + rB + sA + t;

我們可以計算出圍繞藍色分量軸順時針旋轉(zhuǎn) α 度的顏色矩陣如下:

同理拨匆,我們可以得出圍繞紅色分量軸順時針旋轉(zhuǎn) α 度的顏色矩陣:

圍繞綠色分量軸順時針旋轉(zhuǎn) α 度的顏色矩陣:

通過上面的分析姆涩,我們可以知道,當圍繞紅色分量軸進行色彩旋轉(zhuǎn)時惭每,由于當前紅色分量軸的色彩是不變的骨饿,而僅利用三角函數(shù)來動態(tài)的變更綠色和藍色的顏色值。這種改變就叫做色相調(diào)節(jié)台腥。當圍繞紅色分量軸旋轉(zhuǎn)時宏赘,是對圖片就行紅色色相的調(diào)節(jié);同理黎侈,當圍繞藍色分量軸旋轉(zhuǎn)時察署,就是對圖片就行藍色色相調(diào)節(jié);當然峻汉,當圍繞綠色分量軸旋轉(zhuǎn)時贴汪,就是對圖片進行綠色色相的調(diào)節(jié).

下面是Android系統(tǒng)對色調(diào)修改的源碼,我們可以看得到休吠,源碼對第二個參數(shù)進行轉(zhuǎn)換成弧度嘶是,即對紅、綠蛛碌、藍三個顏色通道分別進行旋轉(zhuǎn)聂喇,那我們在第二個參數(shù)中傳入我們平時用的度數(shù)即可。通過對源碼的閱讀蔚携,我們也知道希太,第二個參數(shù)最終被設(shè)置的數(shù)值范圍為 [-1,1] ,然后再設(shè)置到顏色矩陣中酝蜒。即我們在第二個參數(shù)傳入[-180誊辉,180]范圍的值(一個最小周期)即可。
另外亡脑,相信大家都可以看得出來堕澄,系統(tǒng)對存儲顏色矩陣的一維數(shù)組元素的值的修改正好符合上面的分析邀跃。

/**
     * 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();
        }
    }
  • 飽和度

Android系統(tǒng)提供了 setSaturation(float sat) 方法來修改顏色的飽和度。參數(shù) float sat:表示把當前色彩飽和度放大的倍數(shù)蛙紫。取值為0表示完全無色彩拍屑,即灰度圖像(黑白圖像);取值為1時坑傅,表示色彩不變動僵驰;當取值大于1時,顯示色彩過度飽和 如下:

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

同樣貼出修改飽和度值的源碼唁毒,通過源碼我們可以看到系統(tǒng)是通過改變顏色矩陣中對角線上系數(shù)的比例來改變飽和度蒜茴。

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

當三原色以相同比例進行混合時,就會顯示出白色浆西。Android系統(tǒng)正是利用這個原理對圖像進行亮度的改變粉私。如下:

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

同樣貼出修改亮度值的源碼,當亮度為 0 時近零,圖像就變成全黑了。通過對源碼的閱讀秒赤,我們可以知道猪瞬, 系統(tǒng)將顏色矩陣置為初始初始顏色矩陣,再將紅痊项、綠、藍训枢、透明度四個分量通道對應(yīng)的系數(shù)修改成我們傳入的值。

    /**
     * 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系統(tǒng)還封裝了矩陣的乘法運算。它提供了 postConcat(ColorMatrix postmatrix) 方法來將矩陣的作用效果混合十酣,從而疊加處理效果涩拙。如下:

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

下面际长,通過一個demo來給大家看看,修改色調(diào)兴泥、飽和度工育、亮度的效果。
首先我們看看效果圖郁轻,如下:

ColorMatrix

這里的 demo 通過滑動三個 SeekBar 來改變不同的值翅娶,并將這些數(shù)值作用到對應(yīng)色調(diào)文留、飽和度好唯、亮度的顏色矩陣中,最后通過 ColorMatrix 的 postConcat() 方法來混合這三個被修改的顏色矩陣的顯示效果燥翅。
關(guān)鍵設(shè)置圖像矩陣的代碼如下:

    public static Bitmap handleImageEffect(Bitmap oriBmp, Bitmap bmp, float hue, float saturation, float lum) {
        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(oriBmp, 0, 0, paint);

        return bmp;
    }

這里需要注意的是骑篙,Android系統(tǒng)不允許直接修改原圖,類似 Photoshop 中的鎖定森书,必須通過原圖創(chuàng)建一個同樣大小的 Bitmap 靶端,并將原圖繪制到該 Bitmap 中,以一個副本的形式來修改圖像凛膏。
在設(shè)置好需要處理的顏色矩陣后杨名,通過使用 Paint 類的 setColorFilter() 方法,將通過 imageMatrix 構(gòu)造的 ColorMatrixColorFilter 對象傳遞進去猖毫,并使用這個畫筆來繪制原來的圖像台谍,從而將顏色矩陣作用到圖像中。

接下來我們看看這個demo的xml代碼吁断,很簡單趁蕊,一個 ImageView ,三個 SeekBar 仔役,如下:

<?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:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.example.deeson.mycolor.MainActivity"
    android:orientation="vertical">

    <ImageView
        android:id="@+id/iv_photo"
        android:layout_width="300dp"
        android:layout_height="300dp"
        android:layout_gravity="center_horizontal"
        android:layout_marginTop="20dp"
        android:src="@drawable/iv_model0"/>

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="20dp"
        android:layout_marginLeft="5dp"
        android:textColor="@android:color/black"
        android:text="色調(diào)"
        />
    <SeekBar
        android:id="@+id/seekbarHue"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:max="200"
        android:progress="100"/>
    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="10dp"
        android:layout_marginLeft="5dp"
        android:textColor="@android:color/black"
        android:text="飽和度"
        />
    <SeekBar
        android:id="@+id/seekbarSaturation"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:max="200"
        android:progress="100"/>
    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="10dp"
        android:layout_marginLeft="5dp"
        android:textColor="@android:color/black"
        android:text="亮度"
        />
    <SeekBar
        android:id="@+id/seekbarLum"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:max="200"
        android:progress="100"/>

</LinearLayout>

然后是 MainActivity 類的代碼掷伙,就是獲取三個 SeekBar 的值,如下:

package com.example.deeson.mycolor;

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

/**
 * @author Deeson
 */
public class MainActivity extends AppCompatActivity implements SeekBar.OnSeekBarChangeListener {

    ImageView iv_photo;
    float mHue = 0.0f;
    float mSaturation = 1f;
    float mLum = 1f;
    float MID_VALUE;
    Bitmap oriBitmap,newBitmap;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        iv_photo = (ImageView) findViewById(R.id.iv_photo);
        SeekBar barHue = (SeekBar) findViewById(R.id.seekbarHue);
        SeekBar barSaturation = (SeekBar) findViewById(R.id.seekbarSaturation);
        SeekBar barLum = (SeekBar) findViewById(R.id.seekbarLum);
        MID_VALUE = barHue.getMax() * 1.0F / 2;
        oriBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.iv_model0);
        //Android系統(tǒng)不允許直接修改原圖
        newBitmap = Bitmap.createBitmap(oriBitmap.getWidth(), oriBitmap.getHeight(), Bitmap.Config.ARGB_8888);
        barHue.setOnSeekBarChangeListener(this);
        barSaturation.setOnSeekBarChangeListener(this);
        barLum.setOnSeekBarChangeListener(this);
    }

    @Override
    public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
        switch (seekBar.getId()) {
            case R.id.seekbarHue:
                mHue = (progress - MID_VALUE) * 1.0F / MID_VALUE * 180;
                break;
            case R.id.seekbarSaturation:
                mSaturation = progress * 1.0F / MID_VALUE;
                break;
            case R.id.seekbarLum:
                mLum = progress * 1.0F / MID_VALUE;
                break;
        }
        iv_photo.setImageBitmap(ImageHelper.handleImageEffect(oriBitmap,newBitmap, mHue, mSaturation, mLum));
    }

    @Override
    public void onStartTrackingTouch(SeekBar seekBar) {

    }

    @Override
    public void onStopTrackingTouch(SeekBar seekBar) {

    }
}

MyColorDemo 源碼下載

寫到這里又兵,我們差不多就可以自己實現(xiàn)一個圖像美顏處理的APP任柜。當然這只是一個整體的思路,中間還有很多的細節(jié)需要去完善沛厨。我們現(xiàn)在都知道改變顏色矩陣的系數(shù)就能達到一種意想不到的圖片風(fēng)格宙地,比如什么xx色調(diào),xx濾鏡等等俄烁。無非是每種色調(diào)的顏色矩陣的系數(shù)不一樣而已绸栅。

一些常用的圖像處理效果的顏色矩陣

灰度效果
灰度效果
灰度效果
圖像反轉(zhuǎn)
圖像反轉(zhuǎn)
圖像反轉(zhuǎn)
懷舊效果
懷舊效果
懷舊效果
去色效果
去色效果
去色效果
高飽和度
高飽和度
高飽和度
色彩反色

這里是紅綠反色,另外紅藍页屠、藍綠反色原理一樣粹胯,就是把顏色初始矩陣中對應(yīng)顏色通道的值交換處理蓖柔,如下:

色彩反色——紅綠反色
色彩反色——紅綠反色

后續(xù)

學(xué)習(xí)資料
《Android群英傳》

源碼下載
MyColorMatrixDemo 下載地址
MyColorDemo 源碼下載

下一篇更精彩
Android:修圖技術(shù)之瘦臉效果的實現(xiàn)(drawBitmapMesh)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市风纠,隨后出現(xiàn)的幾起案子况鸣,更是在濱河造成了極大的恐慌,老刑警劉巖竹观,帶你破解...
    沈念sama閱讀 206,311評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件镐捧,死亡現(xiàn)場離奇詭異,居然都是意外死亡臭增,警方通過查閱死者的電腦和手機懂酱,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,339評論 2 382
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來誊抛,“玉大人列牺,你說我怎么就攤上這事∞智裕” “怎么了瞎领?”我有些...
    開封第一講書人閱讀 152,671評論 0 342
  • 文/不壞的土叔 我叫張陵,是天一觀的道長随夸。 經(jīng)常有香客問我九默,道長,這世上最難降的妖魔是什么宾毒? 我笑而不...
    開封第一講書人閱讀 55,252評論 1 279
  • 正文 為了忘掉前任驼修,我火速辦了婚禮,結(jié)果婚禮上伍俘,老公的妹妹穿的比我還像新娘邪锌。我一直安慰自己,他們只是感情好癌瘾,可當我...
    茶點故事閱讀 64,253評論 5 371
  • 文/花漫 我一把揭開白布觅丰。 她就那樣靜靜地躺著,像睡著了一般妨退。 火紅的嫁衣襯著肌膚如雪妇萄。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,031評論 1 285
  • 那天咬荷,我揣著相機與錄音冠句,去河邊找鬼。 笑死幸乒,一個胖子當著我的面吹牛懦底,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播罕扎,決...
    沈念sama閱讀 38,340評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼聚唐,長吁一口氣:“原來是場噩夢啊……” “哼丐重!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起杆查,我...
    開封第一講書人閱讀 36,973評論 0 259
  • 序言:老撾萬榮一對情侶失蹤扮惦,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后亲桦,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體崖蜜,經(jīng)...
    沈念sama閱讀 43,466評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,937評論 2 323
  • 正文 我和宋清朗相戀三年客峭,在試婚紗的時候發(fā)現(xiàn)自己被綠了豫领。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,039評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡桃笙,死狀恐怖氏堤,靈堂內(nèi)的尸體忽然破棺而出沙绝,到底是詐尸還是另有隱情搏明,我是刑警寧澤,帶...
    沈念sama閱讀 33,701評論 4 323
  • 正文 年R本政府宣布闪檬,位于F島的核電站星著,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏粗悯。R本人自食惡果不足惜虚循,卻給世界環(huán)境...
    茶點故事閱讀 39,254評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望样傍。 院中可真熱鬧横缔,春花似錦、人聲如沸衫哥。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,259評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽撤逢。三九已至膛锭,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間蚊荣,已是汗流浹背初狰。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評論 1 262
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留互例,地道東北人奢入。 一個月前我還...
    沈念sama閱讀 45,497評論 2 354
  • 正文 我出身青樓,卻偏偏與公主長得像媳叨,于是被迫代替她去往敵國和親腥光。 傳聞我的和親對象是個殘疾皇子丁存,可洞房花燭夜當晚...
    茶點故事閱讀 42,786評論 2 345

推薦閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 171,504評論 25 707
  • 圖像色彩處理 Android對圖片的處理,通常用到的數(shù)據(jù)結(jié)構(gòu)就是位圖—Bitmap柴我,它包含了一張圖片的所有數(shù)據(jù)解寝。整...
    SheldonChen4215閱讀 4,207評論 0 6
  • 由于H.264等壓縮算法都是在YUV的顏色空間上進行的,所有在進行壓縮前艘儒,首先要進行顏色空間的轉(zhuǎn)換聋伦。如果攝像頭采集...
    眷卿三世閱讀 13,540評論 2 6
  • 中午十一點三十一分 文/邸銘心 頭等艙的你和經(jīng)濟艙的我 你和我到底有什么不同呢? 你是你界睁,我是我觉增,我們都是個體,不...
    邸銘心閱讀 419評論 0 0
  • 1. 前天晚上嘹履,娟兒打來電話,約我出去吃日本料理债热,原因很簡單:她分手了砾嫉,要慶祝一下。 吃飯的時候窒篱,我用過來人的腔調(diào)...
    小樂爸爸閱讀 378評論 0 2