Android 濾鏡效果和顏色通道過濾

鋸齒流沙發表於2017-12-26

當今是靠臉吃飯的時代,平時的人像、風景照、美食照等都需要加上一款優美的濾鏡,才能讓照片更加精緻,更加吸引人,這樣的照片能夠讓你在朋友圈更加出眾,別具一格。

因此一個智慧手機當然少不了一款美顏APP,在商店中有眾多的美顏APP,例如我比較喜歡的:Snapseed、InterPhoto、美顏相機和美人相機等等。這些相機都擁有非常美和優秀的濾鏡,正因為有這些優秀的濾鏡,才能讓你的照片變得更美,因此作為開發者的我們需要了解這些濾鏡是怎麼做出來的,一般都是通過Android自帶的濾鏡處理、OpenGL處理或者通過顏色RGB的濾鏡處理。本文我們不介紹OpenGL,因為OpenGL確實是一個很強大的影象處理技術。

Android的濾鏡效果就是對影象進行一定的過濾加工處理,一般的使用Paint設定濾鏡效果分為兩類:

1、Android自帶的濾鏡效果,而該濾鏡效果可以分為:

1)模糊遮罩濾鏡(BlurMaskFilter);

2)浮雕遮罩濾鏡(EmbossMaskFilter)。

2、顏色RGB的濾鏡處理,可以通過ColorMatrix設定。

通過Android的濾鏡效果和顏色通道過濾這兩種方式,可以對影象做出很好的濾鏡效果,接下來學習下如何處理濾鏡。

Android自帶的濾鏡效果

Android的濾鏡處理使用paint.setMaskFilter(maskfilter)方法設定,其中可以設定BlurMaskFilter和EmbossMaskFilter這兩款濾鏡,而這兩款濾鏡都是繼承MaskFilter這個基類。

BlurMaskFilter

BlurMaskFilter:即模糊遮罩濾鏡,通過該濾鏡可以使影象呈現模糊效果。

使用:

public class MaskFilterView extends View {

    private int progress = 10;
    private Paint paint;

    public MaskFilterView(Context context) {
        super(context);
        paint = new Paint(Paint.ANTI_ALIAS_FLAG);
    }

    public MaskFilterView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        paint = new Paint(Paint.ANTI_ALIAS_FLAG);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        //需要關閉硬體加速(沒有關閉則沒效果)
        setLayerType(View.LAYER_TYPE_SOFTWARE, null);

        paint.setColor(Color.RED);
        RectF r = new RectF(100, 100, 300, 300);
        /**模糊遮罩濾鏡效果
         * BlurMaskFilter.Blur.INNER
         * BlurMaskFilter.Blur.NORMAL
         * BlurMaskFilter.Blur.OUTER
         * BlurMaskFilter.Blur.SOLID
         */
		paint.setMaskFilter(new BlurMaskFilter(progress, BlurMaskFilter.Blur.NORMAL));
        canvas.drawRect(r , paint);

    }


    public void setProgress(int progress) {
        if (progress <= 0){
            return;
        }
        this.progress = progress;
        postInvalidate();
    }
}
複製程式碼

Activity:

public class MaskFilterActivity extends AppCompatActivity {

    private SeekBar mSeekBar;
    private MaskFilterView mFilterView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_mask_filter);
        mSeekBar = (SeekBar)this.findViewById(R.id.seekBar);
        mFilterView = (MaskFilterView)this.findViewById(R.id.my_view);
        mSeekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
            @Override
            public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
                mFilterView.setProgress(progress);
            }

            @Override
            public void onStartTrackingTouch(SeekBar seekBar) {

            }

            @Override
            public void onStopTrackingTouch(SeekBar seekBar) {

            }
        });
    }
}
複製程式碼

效果圖:

View.png

在MaskFilterView的onDraw方法中,首先需要new一個BlurMaskFilter物件,我們看下其構造方法:

public BlurMaskFilter(float radius, Blur style)

radius:模糊的半徑,值越大模糊就越擴散。

style:模糊濾鏡使用的型別,總共有四種型別可以選擇,它們分別是:

1)INNER:在影象內部產生模糊;

2)NORMAL:將整個影象模糊掉;

3)OUTER:在Alpha邊界外產生一層模糊,而且將原本的影象變透明。

4)SOLID:在影象的Alpha外邊界產生一層與Paint顏色一致的模糊效果,但不影響影象本身。

注意:這裡我們需要需要關閉硬體加速,否則沒效果。

上圖效果中我們使用了INNER,接下來我們看看其他三種效果。

NORMAL:
paint.setMaskFilter(new BlurMaskFilter(progress, BlurMaskFilter.Blur.NORMAL));
canvas.drawRect(r , paint);
複製程式碼

View

OUTER:
paint.setMaskFilter(new BlurMaskFilter(progress, BlurMaskFilter.Blur.OUTER));
canvas.drawRect(r , paint);
複製程式碼

View

SOLID:
paint.setMaskFilter(new BlurMaskFilter(progress, BlurMaskFilter.Blur.SOLID));
canvas.drawRect(r , paint);
複製程式碼

View

以上便是模糊遮罩濾鏡的使用了,下面我們看看浮雕遮罩濾鏡的使用效果。

EmbossMaskFilter

EmbossMaskFilter(浮雕遮罩濾鏡)讓影象呈現一種凹凸不平的面具效果。

使用

paint.setMaskFilter(new EmbossMaskFilter(new float[]{30,30,30}, 0.2f, 20, progress));
canvas.drawBitmap(bitmap, 100, 300, paint);
複製程式碼

EmbossMaskFilter效果圖

使用的時候,需要new一個EmbossMaskFilter物件,並通過paint.setMaskFilter方法設定,我們主要看EmbossMaskFilter的構造方法引數。

public EmbossMaskFilter(float[] direction, float ambient, float specular, float blurRadius)
複製程式碼

direction:指定長度為xxx的陣列標量[x,y,z],用來指定光源的位置;

ambient:指定周邊背景光源(0~1);

specular:指鏡面反射係數;

blurRadius:指定模糊半徑。

注意:使用EmbossMaskFilter同樣需要關閉硬體加速,否則沒效果。

顏色RGB的濾鏡處理

顏色RGB的濾鏡處理是通過ColorMatrix來實現的,即顏色矩陣,然後將ColorMatrix設定到ColorMatrixColorFilter,通過setColorFilter方法設定ColorMatrixColorFilter,這樣就完成了設定顏色過濾器。setColorFilter還可以設定所有ColorFilter的子類,包括LightingColorFilter和PorterDuffColorFilter。

ColorMatrix顧名思義就是顏色矩陣,那麼濾鏡的所有處理效果都是通過顏色矩陣的變換實現的。所以需要讀者能夠熟悉基本的矩陣運算方法。如果讀者目前還不認識矩陣,怎麼辦?沒關係,接下來我們簡單介紹下矩陣,以及基本的運算方法。讀者可以自行參考百度百科。

矩陣

定義:

m×n矩陣

矩陣加法:

矩陣加法

矩陣減法:

矩陣減法

矩陣乘法:

矩陣乘法

第一個矩陣A的第一行,與第二個矩陣B的第一列的數字分別相乘,得到的結果相加,最終的值做為結果矩陣的第(1,1)位置的值(即第一行,第一列)。 同樣,A矩陣的第一行與B矩陣的第二列的數字分別相乘然後相加,得到的值作為結果矩陣第(1,2)位置的值(即第一行第二列)。

色彩矩陣: 一般的色彩矩陣使用四階表示,也就是RGBA

四階色彩矩陣

半透明的色彩矩陣,如:

半透明

Android色彩矩陣使用五階矩陣

色彩五階矩陣

在四階矩陣的基礎上,增加一階,而第五階表示偏移量,上圖表示紅色分量值更改為原來的2倍,綠色分量增加100(1*100+100);

ColorMatrix的使用

呼叫紅色(R),綠色增加兩倍。

 @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        //需要關閉硬體加速(沒有關閉則沒效果)
        setLayerType(View.LAYER_TYPE_SOFTWARE, null);
        paint.setColor(Color.RED);
        ColorMatrix matrix = new ColorMatrix(new float[]{
				0,0,0,0,0,
				0,1,0,0,200,
				0,0,1,0,0,
				0,0,0,1,0,
		});
        paint.setColorFilter(new ColorMatrixColorFilter(matrix ));
        canvas.drawBitmap(bitmap, null, new RectF(0, 0, 400, 400*bitmap.getHeight()/bitmap.getWidth()), paint);
    }
複製程式碼

效果圖

先new一個ColorMatrixColorFilter物件,並把ColorMatrix設定進去,setColorFilter方法把ColorMatrixColorFilter物件設定進去就可以完成設定顏色過濾器。

反相效果
ColorMatrix matrix = new ColorMatrix(new float[]{
				-1,0,0,0,255,
				0,-1,0,0,255,
				0,0,-1,0,255,
				0,0,0,1,0,
		});
paint.setColorFilter(new ColorMatrixColorFilter(matrix ));
canvas.drawBitmap(bitmap, null, new RectF(0, 0, 400, 400*bitmap.getHeight()/bitmap.getWidth()), paint);
複製程式碼

效果圖

顏色增強

顏色增強:可以起到一個變亮的效果,通過矩陣縮放方式。

ColorMatrix matrix = new ColorMatrix(new float[]{
				1.2f,0,0,0,0,
				0,1.2f,0,0,0,
				0,0,1.2f,0,0,
				0,0,0,1.2f,0,
		});
paint.setColorFilter(new ColorMatrixColorFilter(matrix ));
canvas.drawBitmap(bitmap, null, new RectF(0, 0, 400, 400*bitmap.getHeight()/bitmap.getWidth()), paint);
複製程式碼

效果圖.png

黑白圖片

去色原理:只要把RGB三通道的色彩資訊設定成一樣;即:R=G=B,那麼影象就變成了灰色,並且,為了保證影象亮度不變,同一個通道中的R+G+B=1。 如:0.213+0.715+0.072=1;也就是RGB分別為:0.213,0.715,0.072; 三個數字是根據色彩光波頻率及色彩心理學計算出來的。

ColorMatrix matrix = new ColorMatrix(new float[]{
                0.213f, 0.715f, 0.072f, 0, 0,
                0.213f, 0.715f, 0.072f, 0, 0,
                0.213f, 0.715f, 0.072f, 0, 0,
                0, 0, 0, 1f, 0,
        });
paint.setColorFilter(new ColorMatrixColorFilter(matrix ));
canvas.drawBitmap(bitmap, null, new RectF(0, 0, 400, 400*bitmap.getHeight()/bitmap.getWidth()), paint);
複製程式碼

效果圖.png

髮色效果

髮色效果:如紅色和綠色交換——把第一行和第二行交換。

ColorMatrix matrix = new ColorMatrix(new float[]{
			0,1f,0,0,0,
			1f,0,0,0,0,
			0,0,1f,0,0,
			0,0,0,1f,0,
		});
paint.setColorFilter(new ColorMatrixColorFilter(matrix ));
canvas.drawBitmap(bitmap, null, new RectF(0, 0, 400, 400*bitmap.getHeight()/bitmap.getWidth()), paint);
複製程式碼

效果圖.png

復古風格
ColorMatrix matrix = new ColorMatrix(new float[]{
				1/2f,1/2f,1/2f,0,0,
				1/3f,1/3f,1/3f,0,0,
				1/4f,1/4f,1/4f,0,0,
				0,0,0,1f,0,
			});
paint.setColorFilter(new ColorMatrixColorFilter(matrix ));
canvas.drawBitmap(bitmap, null, new RectF(0, 0, 400, 400*bitmap.getHeight()/bitmap.getWidth()), paint);
複製程式碼

效果圖.png

色彩運算

ColorMatrix色彩矩陣,除了以上直接設定矩陣的方式,還可以使用ColorMatrix類的方法來設定進行色彩運算:

1)色彩的縮放運算(matrix.setScale):也就是四階矩陣RGBA對應的乘法運算。

ColorMatrix matrix = new ColorMatrix();
//setScale(float rScale, float gScale, float bScale, float aScale)
matrix.setScale(2, 2, 2f, 1);
paint.setColorFilter(new ColorMatrixColorFilter(matrix ));
canvas.drawBitmap(bitmap, null, new RectF(0, 0, 400, 400*bitmap.getHeight()/bitmap.getWidth()), paint);
複製程式碼

效果圖.png

上圖中RGB都方法原來的兩倍。

2)色彩的平移運算,也就是矩陣加法運算。

ColorMatrix的API

通過上面的學習我們知道ColorMatrix其強大之處在於通過矩陣運算可以實現顏色通道過濾,我們有必要學習ColorMatrix的API。

構造方法

構造方法.png

三個構造方法,可以不需要引數,可以傳一個float陣列(矩陣),也可以ColorMatrix物件作為引數。

看下ColorMatrix()方法裡面的reset()方法。

/**
     * Set this colormatrix to identity:
     * <pre>
     * [ 1 0 0 0 0   - red vector
     *   0 1 0 0 0   - green vector
     *   0 0 1 0 0   - blue vector
     *   0 0 0 1 0 ] - alpha vector
     * </pre>
     */
    public void reset() {
        final float[] a = mArray;
        Arrays.fill(a, 0);
        a[0] = a[6] = a[12] = a[18] = 1;
    }
複製程式碼

該方法主要是初始化一個矩陣,而且將RGBA都設定為1。

set方法

set

提供兩個set方法,可以設定float陣列(矩陣),也可以設定ColorMatrix物件,跟構造方法對應起來。

matrix.set(new float[]{
                1/2f,1/2f,1/2f,0,0,
                1/3f,1/3f,1/3f,0,0,
                1/4f,1/4f,1/4f,0,0,
                0,0,0,1f,0,
        });
paint.setColorFilter(new ColorMatrixColorFilter(matrix ));
canvas.drawBitmap(bitmap, null, new RectF(0, 0, 400, 400*bitmap.getHeight()/bitmap.getWidth()), paint);
複製程式碼

效果圖.png

setScale方法

設定色彩的縮放函式,上面已經介紹過該方法了.

setRotate

該方法是色彩旋轉函式

setRotate

axis:代表繞哪一個軸旋轉,0,1,2 (0紅色,1綠色,2藍色);

degrees:旋轉的度數。

該函式已經做好了矩陣的設定和運算了。

matrix.setRotate(0,progress);
paint.setColorFilter(new ColorMatrixColorFilter(matrix ));
canvas.drawBitmap(bitmap, null, new RectF(0, 0, 400, 400*bitmap.getHeight()/bitmap.getWidth()), paint);
複製程式碼

效果圖.png

setSaturation

setSaturation:設定飽和度

setSaturation

方法內部已經運算好的了,我們只需要傳一個float型別引數sat。

sat:1表示是原來不變,0表示灰色;如果大於1增加飽和度。

matrix.setSaturation(progress);
paint.setColorFilter(new ColorMatrixColorFilter(matrix ));
canvas.drawBitmap(bitmap, null, new RectF(0, 0, 400, 400*bitmap.getHeight()/bitmap.getWidth()), paint);
複製程式碼

效果圖.png

效果圖.png

setRGB2YUV

效果圖.png

RGB轉成YUV,對應的還有一個方法setYUV2RGB,也就是YUV轉成RGB。

setConcat
setConcat(ColorMatrix matA, ColorMatrix matB):將顏色矩陣matA和matB複合,相當與對圖片進行matA矩陣處理再進行矩陣matB處理。

matrixA.preConcat(ColorMatrix prematrix):等價於setConcat(matrixA, prematrix)

matrixA.postConcat(ColorMatrix postmatrix):等價於setConcat(prematrix,matrixA)
複製程式碼

ColorFilter

ColorFilter作為顏色過濾器,有三個子類來實現顏色過濾: 1)ColorMatrixColorFilter:即色彩矩陣的顏色過濾器,配合ColorMatrix使用。 2)LightingColorFilter:即光照顏色過濾器,能過濾顏色和增強色彩的方法。 3)PorterDuffColorFilter:即圖形混合濾鏡,該是圖形學的一個理論飛躍。

上文中已經使用過了ColorMatrixColorFilter,並且配合ColorMatrix使用。接下來主要介紹LightingColorFilter和PorterDuffColorFilter。

LightingColorFilter

光照顏色過濾器,也就是ColorMatrixColorFilter的簡化版本。

public LightingColorFilter(@ColorInt int mul, @ColorInt int add) {
        mMul = mul;
        mAdd = add;
    }
複製程式碼

通過構造方法設定過濾顏色,引數解析:

mul:multiply,也就是乘法 ;

add:加法,也就是顏色偏移量。

paint.setColorFilter(new LightingColorFilter(0x00ff00, 0xff0000));
canvas.drawBitmap(bitmap, null, new RectF(0, 0, 400, 400*bitmap.getHeight()/bitmap.getWidth()), paint);
複製程式碼

效果圖.png

PorterDuffColorFilter
public PorterDuffColorFilter(@ColorInt int color, @NonNull PorterDuff.Mode mode) {
        mColor = color;
        mMode = mode;
    }
複製程式碼

color:源顏色, mode:色彩的混合模式:

1)PorterDuff.Mode.CLEAR:所繪製不會提交到畫布上 ;

2)PorterDuff.Mode.SRC:顯示上層繪製圖片 ;

3)PorterDuff.Mode.DST:顯示下層繪製圖片 ;

4)PorterDuff.Mode.SRC_OVER:正常繪製顯示,上下層繪製疊蓋 ;

5)PorterDuff.Mode.DST_OVER:上下層都顯示,下層居上顯示 ;

6)PorterDuff.Mode.SRC_IN:取兩層繪製交集,顯示上層;

7)PorterDuff.Mode.DST_IN:取兩層繪製交集。顯示下層;

8)PorterDuff.Mode.SRC_OUT:取上層繪製非交集部分;

9)PorterDuff.Mode.DST_OUT:取下層繪製非交集部分;

10)PorterDuff.Mode.SRC_ATOP:取下層非交集部分與上層交集部分;

11)PorterDuff.Mode.DST_ATOP:取上層非交集部分與下層交集部分;

12)PorterDuff.Mode.XOR:異或:去除兩圖層交集部分;

13)PorterDuff.Mode.DARKEN:取兩圖層全部區域,交集部分顏色加深;

14)PorterDuff.Mode.LIGHTEN:取兩圖層全部,點亮交集部分顏色;

15)PorterDuff.Mode.MULTIPLY:取兩圖層交集部分疊加後顏色;

16)PorterDuff.Mode.SCREEN:取兩圖層全部區域,交集部分變為透明色。

paint.setColorFilter(new PorterDuffColorFilter(Color.BLUE, PorterDuff.Mode.SRC_IN));
canvas.drawBitmap(bitmap, null, new RectF(0, 0, 400, 400*bitmap.getHeight()/bitmap.getWidth()), paint);
複製程式碼

效果圖.png

相關文章