一、矩陣運算
矩陣的加法與減法:
1.運算規則
設矩陣
則
簡言之,兩個矩陣相加減,即它們相同位置的元素相加減。
注意:只有對於兩個行數、列數分別相等(同型矩陣),加減法運算纔有意義,即加減法運算是可行的。
2.運算性質
交換律:A+B=B+A
結合律:(A+B)+C=A+(B+C)
矩陣與數的乘法:
1.運算規則
數乘以矩陣A,就是將數乘以矩陣A中的每一個元素,記爲A或A。
特別地,稱爲的負矩陣。
2.運算性質
結合律:
分配律:
例:已知兩個矩陣,,滿足矩陣方程A+2X=B,求未知矩陣X。
解:
矩陣與矩陣的乘法:
1.運算規則
設
則A與B的乘積C是這樣一個矩陣:
(1)行數與(左矩陣)A相同,列數與(右矩陣)B相同;
(2)C的第i行與第j列的元素由A的第i行元素與B的第j列元素對應相乘,現取乘積之和。
定義:設A爲的矩陣,B爲的矩陣,那麼稱的矩陣C爲矩陣A與B的乘積,記作C=AB,其中矩陣C中的第i行和第j列元素可以表示爲:
如下所示:
2.運算性質(假設運算都是可行的)
(1)結合律:
(2)分配律:(左分配律);(右分配律)
(3)
二、ColorMatrix色彩變換
對於色彩的存儲,Bitmap類使用一個32位的數值來保存,紅、綠、藍及透明度各佔8位,每個色彩分量的取值範圍是0~255。透明度爲0表示完全透明,爲255時色彩完全可見。
1.色彩信息的矩陣表示
由於一個色彩信息包含R、G、B、Alpha信息,所以,我們必然要使用一個四階色彩變換矩陣來修改色彩的每一個分量值。
注意:對於色彩變換矩陣,這裏的色彩順序是R、G、B、A,而不是A、R、G、B。
如果想將色彩(0,255,0,255)更改爲半透明,則可以使用下面的矩陣運算來表示:
上面使用四階矩陣完全可以改變圖片的RGBA值,但考慮一種情況:如果我們只想在原有的R色上增加一些分量呢?
這時,我們就得再多加一階來表示平移變換。所以,一個既包含線性變換又包含平移變換的組合變換稱爲仿射變換。使用四階色彩變換矩陣來修改色彩,只能對色彩的每個分量值進行乘(除)運算。如果要對這些分量值進行加減法運算(平移變換),則只能通過五階矩陣來完成。
考慮下面這個變換:
(1)紅色分量值更改爲原來的2倍。(2)綠色分量值增加100。
這個變換使用四階矩陣的乘法無法實現。所以,應該在四階色彩變換矩陣上增加一個“啞元座標”,來實現所列的矩陣運算。
(在這個矩陣中,分量值用的是100)
2.Android中的色彩變換矩陣
在Android中,色彩變換矩陣的表示形式也是五階的。所以,在默認情況下,色彩變換矩陣的形式如下:
Android中的色彩變換矩陣是用ColorMatrix類來表示的。使用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));
3.示例:彩色圖片的藍色通道輸出
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()函數裏面創建Paint對象,否則會造成內存回收,嚴重影響性能。
色彩的幾種運算方式:
1.色彩的平移運算
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,即增大綠色的飽和度。效果如下:
在應用ColorMatrix類後,圖片中每個像素的綠色值都增加了50,從小狗的臉上也可以看出來。
2)色彩反轉/反相功能
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.色彩的縮放運算
1)調節高度
色彩的縮放運算其實就是色彩的乘法運算。將色彩變換矩陣對角線上分別代表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
});
2)通道輸出
由於在色彩變換矩陣中對角線上的數的取值範圍爲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 colorMatrix = 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 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
});
3.色彩的旋轉運算
RGB色是如何旋轉的呢?首先用R、G、B三色建立立體座標系,如下圖所示。
所以,我們可以把一個色彩值看成三維空間裏的一個點,色彩值的三個分量可以看成該點的座標(三維座標)。我們先不考慮在三個維度綜合情況下是怎麼旋轉的,來看看將某個軸作爲Z軸,在另外兩個軸形成的平面上旋轉的情況。下圖分析了將藍色軸作爲Z軸,僅在紅一綠平面上旋轉a度的情況。
(1)繞藍色軸旋轉度。
對應的色彩變換矩陣:
(2)繞紅色軸旋轉度。
對應的色彩變換矩陣:
(3)繞綠色軸旋轉度。
對應的色彩變換矩陣:
當圍繞紅色軸進行色彩旋轉時,由於當前紅色軸的色彩是不變的,而僅利用三角函數來動態變更綠色和藍色的顏色值,這種改變就叫作色相調節。當圍繞紅色軸旋轉時,是對圖片進行紅色色相的調節;當圍繞藍色軸旋轉時,是對圖片進行藍色色相的調節;當圍繞綠色軸旋轉時,是對圖片進行綠色色相的調節。
4.色彩的投射運算
先回過頭看看色彩變換矩陣運算的公式,如下:
在上式中,把紅色運算單獨標記出來,在運算中,它們就是利用G、B、A的顏色值的分量來增加紅色值的。
來看具體的運算:
注意:最終結果的220=0.2x100+1x200,可見綠色分量在原有綠色分量的基礎上增加了紅色分量值的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
});
首先了解一下去色原理:只要把R、G、B三通道的色彩信息設置成一樣,即R=G=B, 圖像就變成了灰色。並且,爲了保證圖像亮度不變,同一個通道中的R+G+B=1,如0.213+0.715+0.072=1。
下面談一下0.213、0.715、0.072這三個數字的由來。
按理說應該把R、G、B平分,都是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(float[] src)
ColorMatrix(ColorMatrix src)
在這三個構造函數中,我們已經使用過第二個構造函數;至於第三個構造函數,就是利用另一個ColorMatrix實例來複制一個一樣的ColorMatrix對象。
2.設置和重置函數
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其中一個分量的飽和度,但當我們需要整體增強色彩飽和度時,需要如何來做呢?ColorMatrix提供了一個函數來整體增強色彩飽和度,如下:
// 整體增強色彩飽和度,即同時增強R、G、B的色彩飽和度
public void setSaturation(float sat)
● sat:表示把當前色彩飽和度放大的倍數。取值0時,表示完全無色彩,即黑白圖像;
當取值1時,表示色彩不變動;當取值大於1時,顯示色彩過度飽和。
舉個例子:滑塊默認在1倍的位置,向左到底是0,向右到底是20 (飽和度放大20倍)。
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;
}
}
4.setScale()函數——色彩縮放
public void setScale(float rScale, float gScale, float bScale, float aScale)
● 4個參數分別對應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);
注:canvas.save()使得之前的位圖不會被translate(偏移)。我們知道每調用drawXXX,都產生一個新圖層。
5.setRotate()函數——色彩旋轉
上面在講解色彩旋轉運算時,列出了在色彩旋轉時的效果和原理。由於涉及正、餘弦函數的計算,而且這些公式推導起來具有一定的難度,所以Android 已經封裝好了色彩旋轉的函數。
public void setRotate(int axis, float degree)
● axis:將旋轉圍繞某一個顏色軸進行。
取值有:0 圍繞紅色軸旋轉;1 圍繞綠色軸旋轉;2 圍繞藍色軸旋轉
● degree:旋轉的角度
來看一下當圍繞某一個顏色軸旋轉時色相變化的效果。
代碼與調節飽和度的代碼只有兩點不同。
(1)設置SeekBar的範圍
mSeekBar.setMax(360);
mSeekBar.setProgress(180);
(2)在handleColorRotateBmp()函數中處理當前progress
ColorMatrix colorMatirx = new ColorMatrix();
colorMatrix.setRotate(0, progress - 180);
paint.setColorFilter(new ColorMatrixColorFilter(colorMatrix));
同理,圍繞綠色軸旋轉的效果爲:
ColorMatrix相乘:
public void setConcat(ColorMatrix matA, ColorMatrix matB)
● 函數接收兩個ColorMatrix矩陣matA和matB,乘法規則:matA×matB,將結果作爲當前ColorMatirx的值。
public void preConcat(ColorMatrix prematrix)
● 含義就是:當前矩陣A × prematrix
public void postConcat(ColorMatrix postmatrix)
● 含義就是:postmatrix × 當前矩陣A