Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> Android開發 >> 關於android開發 >> 自定義控件三部曲之繪圖篇(八)——Paint之ColorMatrix與濾鏡效果

自定義控件三部曲之繪圖篇(八)——Paint之ColorMatrix與濾鏡效果

編輯:關於android開發

自定義控件三部曲之繪圖篇(八)——Paint之ColorMatrix與濾鏡效果


這篇主要講解ColorMatrix的相關知識,這裡將涉及到矩陣乘法的相關知識。所以這篇是比較有難度的。

一、矩陣概述

1、定義

 

\

稱為m*n矩陣

2、矩陣乘法

\

矩陣乘法其實並不難,它的意思就是將第一個矩陣A的第一行,與第二個矩陣B的第一列的數字分別相乘,得到的結果相加,最終的值做為結果矩陣的第(1,1)位置的值(即第一行,第一列)。
同樣,A矩陣的第一行與B矩陣的第二列的數字分別相乘然後相加,結果做為結果矩陣第(1,2)位置的值(即第一行第二列)。
再如,A矩陣的第二行與B矩陣的第一列的數字分別相乘,然後相加,結果做為結果矩陣的第(2,1)位置的值(即第二行第一列)
算法其實並不難,這裡要說明一個問題:

A矩陣的列數必須與B矩陣的行數相同,才能相乘!因為我們需要把A中的一行中的各個數字與B矩陣中的一列中的各個數字分別相乘,所以A的列數與B的行數必須相同才行!矩陣A乘以矩陣B和矩陣B乘以矩陣A的結果必然是不一樣的。

 

\

 

二、色彩矩陣

對於色彩的存儲,Bitmap類使用一個32位的數值來保存。紅、綠、藍及透明度各占8位,每一個色彩分量的取值范圍是0-255。透明度為0表示完全透明,為255時,色彩完全可見。

1、色彩信息的矩陣表示

四階表示
由於一個色彩信息包含R、G、B、Alpha信息,所以,我們必然要使用一個4階色彩變換矩陣來修改色彩的每一個分量值:

 

\

注意:對於色彩變換矩陣,這裡的色彩順序是R、G、B、A而不是A、R、G、B!!!
如果想將色彩(0,255,0,255)更改為半透明時,可以使用下面的的矩陣運算來表示:

\

為什麼使用五階矩陣
上面使用四階矩陣完全可以改變圖片的RGBA值了,但考慮一種情況,如果我們只想在原有的R色上增加一些分量呢?
這時,我們就得再多加一階來表示平移變換。所以,一個既包含線性變換,又包含平移變換的組合變換,稱為仿射變換。使用四階的色彩變換矩陣來修改色彩,只能夠對色彩的每一個分量值進行乘(除)運算,如果要對這些分量值進行加減法的運算(平移變換),只能通過五階矩陣來完成。
考慮下面這個變換:
1、紅色分量值更改為原來的2倍;
2、綠色分量增加100;
則使用4階矩陣的乘法無法實現,所以,應該在四階色彩變換矩陣上增加一個“啞元坐標”,來實現所列的矩陣運算:

\

這個矩陣中,分量值用的是100

三、Android中的色彩矩陣

1、概述

在上面的所有講解之後,大家也應該看出來了,色彩變換矩陣的表示形式,肯定是五階的那種,所以大家看一下,在默認情況下,色彩變換矩陣的形式:

 

\

Android中的色彩矩陣是用ColorMatrics類來表示的。使用ColorMatrix的方法如下

 

// 生成色彩矩陣  
ColorMatrix colorMatrix = new ColorMatrix(new float[]{  
        1, 0, 0, 0, 0,  
        0, 1, 0, 0, 0,  
        0, 0, 1, 0, 0,  
        0, 0, 0, 0.5, 0,  
});  
mPaint.setColorFilter(new ColorMatrixColorFilter(colorMatrix));  
有關setColorFilter()函數的其它用法,下篇文章我們將會詳細講述,這篇我們只知道怎麼設置ColorMatrix對象就可以了。

2、示例1(單個顏色的藍色通道輸出)

下面我們舉個例子來簡單看一下,我們對一個顏色值進行ColorMatrix操作會怎樣:

 

 

public class MyView extends View {
    private Paint mPaint = new Paint();
    private Bitmap bitmap;// 位圖

    public MyView(Context context, AttributeSet attrs) {
        super(context, attrs);   
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        mPaint.setAntiAlias(true);
        mPaint.setARGB(255,200,100,100);
        // 繪制原始位圖
        canvas.drawRect(0,0,500,600,mPaint);

        canvas.translate(550,0);
        // 生成色彩矩陣
        ColorMatrix colorMatrix = new ColorMatrix(new float[]{
                0, 0, 0, 0, 0,
                0, 0, 0, 0, 0,
                0, 0, 1, 0, 0,
                0, 0, 0, 1, 0,
        });
        mPaint.setColorFilter(new ColorMatrixColorFilter(colorMatrix));
        canvas.drawRect(0,0,500,600,mPaint);
    }
}
在上面中,我們先將圖筆顏色值設為(255,200,100,100),然後對其進行ColorMatrix顏色值運算,把紅色和綠色都去掉,僅顯示藍色值;只顯示藍色值的效果在Photoshop中叫做藍色通道。效果圖如下:
左側是原圖,右側是該圖對應的藍色通道
\

 

這裡只是對一個顏色值,而ColorMatrics的最厲害的地方在於,能夠很批量地改變圖像中的所有顏色值。下面我們就對圖像應用ColorMatrics的例子來看看,如果只顯示圖像中的藍色通道會怎樣

3、示例2(圖片多顏色的藍色通道輸出)

下面我們就舉個給Bitmap應用ColorMatrix的例子:

 

 

public class MyView extends View {
    private Paint mPaint = new Paint();
    private Bitmap bitmap;// 位圖

    public MyView(Context context, AttributeSet attrs) {
        super(context, attrs);

        mPaint.setAntiAlias(true);
        // 獲取位圖
        bitmap = BitmapFactory.decodeResource(context.getResources(), R.drawable.dog);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        // 繪制原始位圖
        canvas.drawBitmap(bitmap, null, new Rect(0, 0, 500, 500 * bitmap.getHeight() / bitmap.getWidth()), mPaint);

        canvas.translate(510, 0);
        // 生成色彩矩陣
        ColorMatrix colorMatrix = new ColorMatrix(new float[]{
                0, 0, 0, 0, 0,
                0, 0, 0, 0, 0,
                0, 0, 1, 0, 0,
                0, 0, 0, 1, 0,
        });
        mPaint.setColorFilter(new ColorMatrixColorFilter(colorMatrix));
        canvas.drawBitmap(bitmap, null, new Rect(0, 0, 500, 500 * bitmap.getHeight() / bitmap.getWidth()), mPaint);
    }
}
這裡分兩次繪制了一個bitmap,第一次繪制了一個原始圖像,然後利用ColorMatrix生成了一個僅包含藍色的圖像,用過PhotoShop的同學應該很清楚這個跟Photoshop中的藍色通道的效果是一致的。效果圖如下:
\

 

大家注意哦,不要在onDraw裡new Paint對象,上節中我為了省事就直接在onDraw()函數中直接new 了Paint對象,由於onDraw函數在刷新時會連續調用多次,所以如果在其中不斷的new對象,會造成程序不斷的GC(內存回收),是會嚴重影響性能的!在程序中,我有時會了為了方便理解,就直接在onDraw()中創建對象了,大家在實際應用中一定要杜絕這種應用哦。

 

四、色彩的幾種運算方式

在簡單理解了ColorMatrics的使用方式後,我們來詳細來看看色彩的幾種運算方式;

1、色彩的平移運算

色彩的平移運算,實際上就是色彩的加法運算。其實就是在色彩變換矩陣的最後一行加上某個值;這樣可以增加特定色彩的飽和度

 

\

比如,同樣是上面的圖片,我們給它應用下面的色彩值:

 

ColorMatrix colorMatrix = new ColorMatrix(new float[]{
        1, 0, 0, 0, 0,
        0, 1, 0, 0, 50,
        0, 0, 1, 0, 0,
        0, 0, 0, 1, 0,
});

 

在綠色值上添加增量50,即增大綠色的飽和度。效果圖如下:

\

同樣,左側是原圖,右側是增大綠色飽和度後的效果;大家要特別注意的是,由於圖片是由一個個像素組成的,所以用每個像素所對應的色彩數組,來乘轉換矩陣,結果就是轉換後的當前點的顏色值;所以,在應用ColorMatrics後,圖片中每個像素的綠色值都增加了50,從小狗臉上也可以看出來,狗臉也變綠了(它可能看到他女朋友跟人家跑了,哈哈)!
色彩平移除了增加指定顏色飽和度以外,另一個應用就是色彩反轉(PhotoShop中的反相功能)

色彩反轉/反相功能
色彩反轉就是求出每個色彩的補值來做為目標圖像的對應顏色值:

 

ColorMatrix colorMatrix = new ColorMatrix(new float[]{
        -1,0,0,0,255,
        0,-1,0,0,255,
        0,0,-1,0,255,
        0,0,0,1,0
});
效果圖如下:
\

 

 

2、色彩的縮放運算

色彩的縮放運算其實就是色彩的乘法運算。在色彩矩陣對角線上的分別代表R、G、B、A的幾個值,將其分別乘以指定的值。這就是所謂的縮放變換。

 

\

我們可以針對某一個顏色值進行放大縮小運算,但當對R、G、B、A同時進行放大縮小時,就是對亮度進行調節!
看下面的將亮度增大1.2倍的代碼:

 

ColorMatrix colorMatrix = new ColorMatrix(new float[]{
        1.2f, 0, 0, 0, 0,
        0, 1.2f, 0, 0, 50,
        0, 0, 1.2f, 0, 0,
        0, 0, 0, 1.2f, 0,
});
效果圖如下:
\

3、縮放變換的特殊應用(通道輸出)

由於在色彩變換矩陣中,對角線上的數的取值范圍是從0-1的,所以當取0時,這個色彩就完全不顯示,所以當我們R、G都取0,而獨有B取1時,就只顯示了藍色,所形成的圖像也就是我們通常說的藍色通道;看下幾個通道輸出的效果圖:

 

\

紅色通道矩陣:

 

ColorMatrix colorMatrix = new ColorMatrix(new float[]{
       1, 0, 0, 0, 0,
       0, 0, 0, 0, 0,
       0, 0, 0, 0, 0,
       0, 0, 0, 1, 0,
});
綠色通道矩陣:
ColorMatrix colorMatrix2 = new ColorMatrix(new float[]{
        0, 0, 0, 0, 0,
        0, 1, 0, 0, 0,
        0, 0, 0, 0, 0,
        0, 0, 0, 1, 0,
});
藍色通道矩陣:
ColorMatrix colorMatrix3 = new ColorMatrix(new float[]{
        0, 0, 0, 0, 0,
        0, 0, 0, 0, 0,
        0, 0, 1, 0, 0,
        0, 0, 0, 1, 0,
});

4、色彩的旋轉運算

RGB色是如何旋轉的呢,首先用R、G、B三色建立立體坐標系:
\

 

所以,我們可以把一個色彩值看成三維空間裡的一個點,色彩值的三個分量可以看成該點的坐標(三維坐標)。我們先不考慮,在三個維度綜合情況下是怎麼旋轉的,我們先看看,在某個軸做為Z軸,在另兩個軸形成的平面上旋轉的情況,下圖分析了,在將藍色軸做為Z軸,僅在紅—綠平面上旋轉a度的情況:

\

在圖中,我們可以看到,在旋轉後,原R在R軸的分量變為:原R*cosa,但原G分量在旋轉後,在R軸上也有了分量,但分量落在了負軸上,所以我們要減去這部分分量,所以最終的結果是最終的R=原R*cosa-原G*sina;
下面就看下關於幾種旋轉計算及結果矩陣,(注意:這幾個圖只標記了原X軸色彩分量的旋轉,沒有把Y軸色彩分量的旋轉標記出來)
繞藍色軸旋轉a度

\

對應的色彩變換矩陣是

\

繞紅色軸旋轉a度

\

對應的色彩變換矩陣是

\

繞綠色軸旋轉a度

\

對應的色彩變換矩陣是

\

當圍繞紅色軸進行色彩旋轉時,由於當前紅色軸的色彩是不變的,而僅利用三角函數來動態的變更綠色和藍色的顏色值。這種改變就叫做色相調節!當圍繞紅色軸旋轉時,是對圖片就行紅色色相的調節;同理,當圍繞藍色顏色軸旋轉時,就是對圖片就行藍色色相調節;當然,當圍繞綠色軸旋轉時,就是對圖片進行綠色色相的調節.
下面我們做一個動態的調節,針對紅 色色相。

\

這個效果圖表示的是,在滾輪正中間位置時表示旋轉角度為0度,最右側位置表示向正方向旋轉180,左側到底表示負方向旋轉180.
同理可以得到圍繞綠色軸旋轉的效果圖:

\

最後是,圍繞藍色軸旋轉的效果圖:

\

下面我們會再次講到ColorMatrics的色彩旋轉函數,這裡先理解原理和效果,代碼後面會給出。

5、色彩的投射運算

我們再回過頭來看看色彩矩陣運算的公式:

 

\

其中我把紅色運算給單獨拉了出來,紅色標記的那幾個元素a21,a31,a41,在運算中,是利用R、B、A的顏色值的分量來增加紅色值的。
來看具體的運算:

\

注意:最終結果的140=1*100+0.2*200,可見紅色分量在原有紅色分量的基礎上,增加了綠色分量值的0.2倍;利用其它色彩分量的倍數來更改自己色彩分量的值,這種運算就叫投射運算。
下圖陰影部分;對這些值進行修改時,修改所使用的增加值來自於其它色彩分量的信息。

\

色彩投射的一個最簡單應用就是變為黑白圖片:

 

ColorMatrix colorMatrix = 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, 1, 0,
});
效果圖:
\

 

首先了解一下去色原理:只要把RGB三通道的色彩信息設置成一樣;即:R=G=B,那麼圖像就變成了灰色,並且,為了保證圖像亮度不變,同一個通道中的R+G+B=1:如:0.213+0.715+0.072=1;
三個數字的由來:0.213, 0.715, 0.072;
按理說應該把RGB平分,都是0.3333333。三個數字應該是根據色彩光波頻率及色彩心理學計算出來的(本人是這麼認為,當然也查詢了一些資料,目前尚未找到准確答案)。
在作用於人眼的光線中,彩色光要明顯強於無色光。對一個圖像按RGB平分理論給圖像去色的話,人眼就會明顯感覺到圖像變暗了(當然可能有心理上的原因,也有光波的科學依據)另外,在彩色圖像中能識別的一下細節也可能會丟失。
所以google最終給我們的顏色值就是上面的比例:0.213, 0.715, 0.072;
所以,在給圖像去色時我們保留了大量的G通道信息,使得圖像不至於變暗或者綠色信息不至於丟失(我猜想)。
投射運算的另一個應用是:色彩反色
當我們利用色彩矩陣將兩個顏色反轉,這種操作就叫做色彩反色
比如,下面的的將紅色和綠色反色(紅綠反色)

 

ColorMatrix colorMatrix = new ColorMatrix(new float[]{
        0,1,0,0,0,
        1,0,0,0,0,
        0,0,1,0,0,
        0,0,0,1,0
});
效果圖如下:
\

 

左側的圖為原圖,右邊為紅綠反色以後的效果圖;
從矩陣中可以看出紅綠反色的關鍵在於,第一行用綠色來代替了紅色,第二行用紅色代替了綠色。
類似可以有紅藍反色,綠藍反色等,對應矩陣難度不大,就不再細講了。
變舊照片
投射運算的另一個應用是照片變舊,對應矩陣如下:

 

ColorMatrix colorMatrix = 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,1,0
});
\

五、ColorMatrix函數

上面講了利用色彩矩陣的來做一些運算,但這些都是需要特定的色彩設計基礎的,Android中ColorMatrix自帶了一些函數來幫我們完成一些調整飽和度、色彩旋轉等操作的函數,我們就一一來看看

1、構造函數

ColorMatrix共有三個構造函數:

 

 

ColorMatrix()
ColorMatrix(float[] src)
ColorMatrix(ColorMatrix src)
這三個構造函數中,上面我們已經使用過第二個構造函數了,第三個構造函數,就是利用另一個ColorMatrix實例來復制一個一樣的ColorMatrix對象。

2、設置、重置函數

第一個構造函數ColorMatrix(),需要與其它函數共用才行:
public void set(ColorMatrix src)
public void set(float[] src)
public void reset()
這裡是設置和重置函數,重置後,對應的數組為:
/**
* Set this colormatrix to identity:
* [ 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
*/
這些函數難度都不大,就不再講了

3、setSaturation——設置飽和度

上面我們講過,我們可以通過色彩的平移運算單獨增強R,G,B其中一個的飽和度,但當我們需要整體增強圖像的飽和度時需要如何來做呢?ColorMatrics給我們提供了一個方法來整體增強圖像的飽和度,函數如下:
//整體增強顏色飽和度,即同時增強R,G,B的色彩飽和度
public void setSaturation(float sat)
其中:
參數float sat:表示把當前色彩飽和度放大的倍數。取值為0表示完全無色彩,即灰度圖像(黑白圖像);取值為1時,表示色彩不變動;當取值大於1時,顯示色彩過度飽和
我們來看個例子:
\

 

滑塊默認在一倍的位置,向左到底是0,向右到底是20(即飽和度放大20倍)
下面來看看代碼:
先來看看布局代碼:(main.xml)

 



    

    

        

        

    

布局很簡單,根據效果圖不難理解出來;
然後來看看效果處理代碼(MyActivity.java)
public class MyActivity extends Activity {
    private SeekBar mSeekBar;
    private ImageView mImageView;
    private Bitmap mOriginBmp,mTempBmp;
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        mImageView = (ImageView) findViewById(R.id.img);
        mSeekBar = (SeekBar)findViewById(R.id.seekbar);
        mOriginBmp = BitmapFactory.decodeResource(getResources(), R.drawable.dog);
        mTempBmp = Bitmap.createBitmap(mOriginBmp.getWidth(), mOriginBmp.getHeight(),
                Bitmap.Config.ARGB_8888);


        mSeekBar.setMax(20);
        mSeekBar.setProgress(1);

        mSeekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
            @Override
            public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {

                Bitmap bitmap = handleColorMatrixBmp(progress);
                mImageView.setImageBitmap(bitmap);
            }

            @Override
            public void onStartTrackingTouch(SeekBar seekBar) {

            }

            @Override
            public void onStopTrackingTouch(SeekBar seekBar) {

            }
        });

    }

    private Bitmap  handleColorMatrixBmp(int progress){
        // 創建一個相同尺寸的可變的位圖區,用於繪制調色後的圖片
        Canvas canvas = new Canvas(mTempBmp); // 得到畫筆對象
        Paint paint = new Paint(); // 新建paint
        paint.setAntiAlias(true); // 設置抗鋸齒,也即是邊緣做平滑處理
        ColorMatrix mSaturationMatrix = new ColorMatrix();
        mSaturationMatrix.setSaturation(progress);

        paint.setColorFilter(new ColorMatrixColorFilter(mSaturationMatrix));// 設置顏色變換效果
        canvas.drawBitmap(mOriginBmp, 0, 0, paint); // 將顏色變化後的圖片輸出到新創建的位圖區
        // 返回新的位圖,也即調色處理後的圖片
        return mTempBmp;
    }
}
最關鍵的位置在於,在調整progress時,生成對應圖像的過程:
private Bitmap  handleColorMatrixBmp(int progress){
    // 創建一個相同尺寸的可變的位圖區,用於繪制調色後的圖片
    Canvas canvas = new Canvas(mTempBmp); // 得到畫筆對象
    Paint paint = new Paint(); // 新建paint
    paint.setAntiAlias(true); // 設置抗鋸齒,也即是邊緣做平滑處理
    ColorMatrix mSaturationMatrix = new ColorMatrix();
    mSaturationMatrix.setSaturation(progress);

    paint.setColorFilter(new ColorMatrixColorFilter(mSaturationMatrix));// 設置顏色變換效果
    canvas.drawBitmap(mOriginBmp, 0, 0, paint); // 將顏色變化後的圖片輸出到新創建的位圖區
    // 返回新的位圖,也即調色處理後的圖片
    return mTempBmp;
}
mTempBmp是生成的一個跟原始的bitmap同樣大小的空白圖片,然後在設置的Paint的ColorMatrics之後,利用canvas.drawBitmap(mOriginBmp, 0, 0, paint);在原始圖片的基礎上應用Paint把生成的圖像畫在canvas上。drawBitmap()的第一個參數表示的是源圖像;
源碼在文章底部給出

4、setScale——色彩縮放

同樣,對於色彩的縮放運算ColorMatrics也已經為我們封裝了一個函數:
public void setScale(float rScale, float gScale, float bScale,float aScale)
總共有四個參數,分別對應R,G,B,A顏色值的縮放倍數。
比如,在小狗圖片中,綠色占大部分,所以我們僅將綠色放大1.3倍:
canvas.drawBitmap(bitmap, null, new Rect(0, 0, 500, 500 * bitmap.getHeight() / bitmap.getWidth()), mPaint);

canvas.save();
canvas.translate(510, 0);
// 生成色彩矩陣
ColorMatrix colorMatrix = new ColorMatrix();
colorMatrix.setScale(1,1.3f,1,1);
mPaint.setColorFilter(new ColorMatrixColorFilter(colorMatrix));

canvas.drawBitmap(bitmap, null, new Rect(0, 0, 500, 500 * bitmap.getHeight() / bitmap.getWidth()), mPaint);
效果圖如下:
\

 

在僅將綠色放大1.3倍後,整個圖片看起來更鮮艷了有沒有。

5、setRotate——色彩旋轉

上面在講解色彩旋轉運算時,給大家列出了在色彩旋轉時的效果和原理,由於涉及到正余弦函數的計算,而且這些公式推導起來相當具有難度,所以Android的大大們,已經給我們封裝好了色彩旋轉的函數:

 

 

/**
 * 將旋轉圍繞某一個顏色軸旋轉
 * axis=0 圍繞紅色軸旋轉
 * axis=1 圍繞綠色軸旋轉
 * axis=2 圍繞藍色軸旋轉
 */
public void setRotate(int axis, float degrees);
這裡有兩個參數:
int axis:表示圍繞哪個軸旋轉,取值為0,1,2;取0時表示圍繞紅色軸旋轉;取值1時,表示圍繞綠色軸旋轉;取值2時,表示圍繞藍色軸旋轉;float degrees:表示旋轉的度數在上面在介紹色彩旋轉的算法時,已經給大家看了當圍繞某一個軸旋轉時色相變化的效果圖,同樣是利用上面的圖像和滑動條的框架,只是在處理時有些不同:
\

 

處理代碼如下:

 

public class SecondActivity extends Activity {
    private SeekBar mSeekBar;
    private ImageView mImageView;
    private Bitmap mOriginBmp,mTempBmp;
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        mImageView = (ImageView) findViewById(R.id.img);
        mSeekBar = (SeekBar)findViewById(R.id.seekbar);
        mOriginBmp = BitmapFactory.decodeResource(getResources(), R.drawable.dog);
        mTempBmp = Bitmap.createBitmap(mOriginBmp.getWidth(), mOriginBmp.getHeight(),
                Bitmap.Config.ARGB_8888);


        mSeekBar.setMax(360);
        mSeekBar.setProgress(180);

        mSeekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
            @Override
            public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {

                Bitmap bitmap = handleColorRotateBmp(progress);
                mImageView.setImageBitmap(bitmap);
            }

            @Override
            public void onStartTrackingTouch(SeekBar seekBar) {

            }

            @Override
            public void onStopTrackingTouch(SeekBar seekBar) {

            }
        });

    }

    private Bitmap handleColorRotateBmp(int progress){

        // 創建一個相同尺寸的可變的位圖區,用於繪制調色後的圖片
        Canvas canvas = new Canvas(mTempBmp); // 得到畫筆對象
        Paint paint = new Paint(); // 新建paint
        paint.setAntiAlias(true); // 設置抗鋸齒,也即是邊緣做平滑處理
        ColorMatrix colorMatrix = new ColorMatrix();

        colorMatrix.setRotate(0,progress-180);
        paint.setColorFilter(new ColorMatrixColorFilter(colorMatrix));// 設置顏色變換效果
        canvas.drawBitmap(mOriginBmp, 0, 0, paint); // 將顏色變化後的圖片輸出到新創建的位圖區
        // 返回新的位圖,也即調色處理後的圖片
        return mTempBmp;
    }
}
這裡的代碼與調節飽和度的代碼都是一樣的,只是有兩點不同:
第一:設置SeekBar范圍:
mSeekBar.setMax(360);
mSeekBar.setProgress(180);
第二:處理當前progress
在handleColorRotateBmp函數中:
ColorMatrix colorMatrix = new ColorMatrix();
colorMatrix.setRotate(0,progress-180);
paint.setColorFilter(new ColorMatrixColorFilter(colorMatrix));
將當前progress位置減去180,即中間位置的數字。所以在中間位置的色彩旋轉度數為0,整個旋轉度數的范圍是-180到180;360度正好是正余弦函數的一個最小正周期
代碼難度不大,就不再細講了。

6、ColorMatrics相乘

這部分是ColorMatrics裡最難的部分了……因為涉及到兩個矩陣相乘……不過這段基本用不到,理解不了的同學也無所謂……
矩陣相乘涉及到三個函數:
public void setConcat(ColorMatrix matA, ColorMatrix matB)
這個函數接收兩個ColorMatrix矩陣matA和matB,乘法規則為matA*matB,然後將結果做為當前ColorMatrix的值。(為什麼要強調乘法規則為matA*matB?我們前面講過矩陣A*矩陣B和矩陣B*矩陣A的結果是不一樣的!)
public void preConcat(ColorMatrix prematrix)
假如當前矩陣的A,而preConcat的意思就是將當前的矩陣A乘以prematrix
public void postConcat(ColorMatrix postmatrix)
上面prematrix是當前矩陣A*prematrix;而postConcat函數的意義就是postmatrix*當前矩陣A;這就是一個前乘,一個是後乘的區別!我們上面已經很清楚的講了,前乘和後乘結果是不一樣的!
setConcat(ColorMatrix matA, ColorMatrix matB)
下面我們先來看看setConcat
public void setConcat(ColorMatrix matA, ColorMatrix matB)
我們提了setConcat會將matA乘以matB,將結果做為當前ColorMatrics實例的顏色矩陣。所以會把當前ColorMatrics實例以前的顏色矩陣給覆蓋掉!這是我們首先需要提示的。
然後來看看怎麼乘吧,比如有下面兩個顏色矩陣:
\

 

估計有些同學看到這些數字的時候,心頭會有一萬頭草泥馬奔過……數學算起來確實是有些難度……慢慢看吧……
我們前面講過,只有當第一個矩陣的列數等於第二個矩陣的行數的時候,才能相乘!這泥媽不對啊……明明第一個矩陣有五列,而第二個矩陣只有四行……第一個矩陣的最後一列這泥馬要打光棍的節奏啊……
這不光是光棍的問題,這兩個矩陣是根本沒辦法相乘的,因為第一個矩陣的列數和第二個矩陣的行數不相等!
那為了解決這個問題,Android提供了一個方案,讓這兩個矩陣相乘,就是把第一個矩陣的最後一列單獨拿出來,另外加到結果上,即:

\

為了驗證上面的運算法則,我們使用setConcat函數來做一下實驗:

 

public class MyActivity extends Activity {
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        ColorMatrix colorMatrix1 = new ColorMatrix(new float[]{
                0.1f, 0.2f, 0.3f, 0.4f, 0.5f,
                0, 1, 0, 0, 0,
                0, 0, 1, 0, 0,
                0, 0, 0, 1, 0,
        });


        ColorMatrix colorMatrix2 = new ColorMatrix(new float[]{
                0.11f, 0, 0, 0, 0,
                0, 0.22f, 0, 0, 0,
                0, 0, 0.33f, 0, 0,
                0, 0, 0, 0.44f, 0,
        });


        ColorMatrix resultMatrix = new ColorMatrix(new float[]{
                0, 0, 0, 0, 0,
                0, 0, 0, 0, 0,
                0, 0, 0, 0, 0,
                0, 0, 0, 0, 0,
        });
        resultMatrix.setConcat(colorMatrix1,colorMatrix2);

        Log.d("qijian",printArray(colorMatrix1.getArray()));
        Log.d("qijian",printArray(colorMatrix2.getArray()));
        Log.d("qijian",printArray(resultMatrix.getArray()));
    }


    private String printArray(float[] array){
        StringBuilder builder = new StringBuilder("array dump:\n");
        for (int i=0;i這段代碼很好理解,生成三個ColorMatrics對象colorMatrix1、colorMatrix2和resultMatrix,然後利用resultMatrix.setConcat函數將colorMatrix1與colorMatrix2相乘,結果會覆蓋resultMatrix的原有矩陣,最後利用日志把colorMatrix1、colorMatrix2和resultMatrix的最終值打印出來:
\

 

大家可以拿筆來算算,結果是與上面的算法一致的。 preConcat(ColorMatrix prematrix) 然後我們來看看postConcat的運算原理:

 

public void preConcat(ColorMatrix prematrix)
假如當前ColorMatrix中所對應的矩陣是A,而preConcat的意思就是將當前的矩陣A乘以prematrix
示例代碼如下:
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);

    ColorMatrix colorMatrix1 = new ColorMatrix(new float[]{
            0.1f, 0.2f, 0.3f, 0.4f, 0.5f,
            0, 1, 0, 0, 0,
            0, 0, 1, 0, 0,
            0, 0, 0, 1, 0,
    });

    ColorMatrix colorMatrix2 = new ColorMatrix(new float[]{
            0.11f, 0, 0, 0, 0,
            0, 0.22f, 0, 0, 0,
            0, 0, 0.33f, 0, 0,
            0, 0, 0, 0.44f, 0,
    });

    //打印出原始的colorMatrix1的矩陣
    Log.d("qijian",printArray(colorMatrix1.getArray()));

    colorMatrix1.preConcat(colorMatrix2);
    Log.d("qijian",printArray(colorMatrix2.getArray()));
    //打印出乘後的colorMatrix1的矩陣
    Log.d("qijian",printArray(colorMatrix1.getArray()));
}
結果如下:
\

 

從結果也可以看出,preConcat的意義就是將當前矩陣乘以prematrix矩陣
postConcat(ColorMatrix postmatrix)
下面我們再看看 postConcat(ColorMatrix postmatrix)

 

public void postConcat(ColorMatrix postmatrix)
我們上面講了,上面prematrix是當前矩陣A*prematrix;而postConcat函數的意義就是postConcat*當前矩陣A;剛好與preConcat反過來。所以如果我們在實例中,利用colorMatrix2.postConcat(colorMatrix1);它得到結果應該是與colorMatrix1.preConcat(colorMatrix2);得到結果是一樣的,我們來實驗一下:
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);

    ColorMatrix colorMatrix1 = new ColorMatrix(new float[]{
            0.1f, 0.2f, 0.3f, 0.4f, 0.5f,
            0, 1, 0, 0, 0,
            0, 0, 1, 0, 0,
            0, 0, 0, 1, 0,
    });


    ColorMatrix colorMatrix2 = new ColorMatrix(new float[]{
            0.11f, 0, 0, 0, 0,
            0, 0.22f, 0, 0, 0,
            0, 0, 0.33f, 0, 0,
            0, 0, 0, 0.44f, 0,
    });

    Log.d("qijian",printArray(colorMatrix1.getArray()));
    Log.d("qijian",printArray(colorMatrix2.getArray()));
    colorMatrix2.postConcat(colorMatrix1);
    Log.d("qijian",printArray(colorMatrix2.getArray()));
}
結果如下:
\

 

可以看到結果與上面的一模一樣哦。這同時也驗證了postConcat函數與preConcat函數正好反過來,一個前乘一個後乘

7、getArray()獲取當前矩陣數組

前面我們已經用到過getArray函數了,getArray函數的意義就是返回當前ColorMatrics對象中的所保存的矩陣

 

 

public float[] getArray()
返回值是float[]數組,它的索引順序為:
\

 

好啦,本篇到這裡就結束了,有關矩陣的知識是比較有難度的,但是這篇對於圖像處理是至關重要的,因為在有些相機軟件中會有各種濾鏡效果,這些濾鏡效果大部分就是通過更改ColorMatrics矩陣來完成的!當然要完全會構造ColorMatrics矩陣是需要色彩設計相關的知識的;相信通過本篇知識,你也能寫出些濾鏡效果了,做出來一個簡單的圖片處理APP也不是問題了哦。下篇將繼續給大家說圖像處理
源碼在文章底部給出

這篇文章真泥馬難謄寫,從makedown抄到網上寫成Html真是想死的節奏啊……光抄過來用了三個小時……MyGOD,關鍵是CSDN的markdown的界面真是太丑了,不方便大家閱讀
源碼內容:
1、BlogColorMatrix:圖片處理及最後的SetConcat、PreConcat和PostConcat計算都在這裡
2、BlogProgressMetrics:通過滾動軸動態改變圖像的飽和度和色相的源碼在這裡哦

 

 

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved