圖像的處理大致有兩種:
- ColorMatrix:顏色矩陣挪哄,5x4的矩陣私沮。
- Matrix:變換矩陣,3*3的矩陣香璃。
本章主要講解變換矩陣
这难。
(1)基本矩陣
[scale_x skew_x trans_x
skew_y scale_y trans_y
persp_0 persp_1 persp_2]
其中,scale處理縮放變換葡秒,skew處理錯(cuò)切變換姻乓,trans處理平移變換,persp處理透視變換同云。
初始矩陣如下:
[1 0 0
0 1 0
0 0 1]
(2)圖像縮放
直接貼一下代碼
Bitmap result = pool.get(toTransform.getWidth(), toTransform.getHeight(), Bitmap.Config.ARGB_8888);
if(result == null){
result = Bitmap.createBitmap(toTransform.getWidth(), toTransform.getHeight(), Bitmap.Config.ARGB_8888);
}
Canvas canvas = new Canvas(result);
// 初始化Matrix對(duì)象
Matrix matrix = new Matrix();
// 根據(jù)傳入的參數(shù)設(shè)置縮放比例
matrix.setScale(scale_x, scale_y);
Paint paint = new Paint();
paint.setAntiAlias(true);
// 根據(jù)縮放比例糖权,把圖片draw到Canvas上
canvas.drawBitmap(toTransform, matrix,paint);
如果scale_x = 1堵腹,scale_y = 1炸站,那么相當(dāng)于沒有縮放,原圖效果如下:
當(dāng)我們將scale_x 和scale_y 都設(shè)置成0.5時(shí)疚顷,也就是將原圖縮小一半旱易,如題所示:
滿足以上效果禁偎,也可以直接設(shè)置矩陣
Bitmap result = pool.get(toTransform.getWidth(), toTransform.getHeight(), Bitmap.Config.ARGB_8888);
if(result == null){
result = Bitmap.createBitmap(toTransform.getWidth(), toTransform.getHeight(), Bitmap.Config.ARGB_8888);
}
Canvas canvas = new Canvas(result);
// 初始化Matrix對(duì)象
Matrix matrix = new Matrix();
// 根據(jù)傳入的參數(shù)設(shè)置縮放比例
float[] values = {
0.5f, 0, 0,
0, 0.5f, 0,
0, 0, 1
};
matrix.setValues(values);
Paint paint = new Paint();
paint.setAntiAlias(true);
// 根據(jù)縮放比例,把圖片draw到Canvas上
canvas.drawBitmap(toTransform, matrix,paint);
原理:
最終得出的圖形旋轉(zhuǎn)矩陣為:
[a 0 0
0 b 0
0 0 1]
a為x軸方向的縮放系數(shù)阀坏,b為y軸方向的縮放系數(shù)如暖。
(2)平移操作
在當(dāng)前畫布上,x方向和y方向忌堂,各移動(dòng)100距離盒至。
Bitmap result = pool.get(toTransform.getWidth(), toTransform.getHeight(), Bitmap.Config.ARGB_8888);
if(result == null){
result = Bitmap.createBitmap(toTransform.getWidth(), toTransform.getHeight(), Bitmap.Config.ARGB_8888);
}
Canvas canvas = new Canvas(result);
// 初始化Matrix對(duì)象
Matrix matrix = new Matrix();
// 根據(jù)傳入的參數(shù)設(shè)置縮放比例
matrix.setTranslate(100, 100);
Paint paint = new Paint();
paint.setAntiAlias(true);
// 根據(jù)縮放比例,把圖片draw到Canvas上
canvas.drawBitmap(toTransform, matrix,paint);
效果如下:
使用矩陣數(shù)組士修,我們將圖片縮小一倍之后再移動(dòng)100距離枷遂。
Bitmap result = pool.get(toTransform.getWidth(), toTransform.getHeight(), Bitmap.Config.ARGB_8888);
if(result == null){
result = Bitmap.createBitmap(toTransform.getWidth(), toTransform.getHeight(), Bitmap.Config.ARGB_8888);
}
Canvas canvas = new Canvas(result);
// 初始化Matrix對(duì)象
Matrix matrix = new Matrix();
// 根據(jù)傳入的參數(shù)設(shè)置縮放比例
float[] values = {
0.5f, 0, 100,
0, 0.5f, 100,
0, 0, 1
};
matrix.setValues(values);
Paint paint = new Paint();
paint.setAntiAlias(true);
// 根據(jù)縮放比例,把圖片draw到Canvas上
canvas.drawBitmap(toTransform, matrix,paint);
效果如下:
原理:
最終得出的圖形旋轉(zhuǎn)矩陣為:
[1 0 a
0 1 b
0 0 1]
a為x軸方向的平移棋嘲,b為y軸方向的平移酒唉。
(3)錯(cuò)切操作
定義:
錯(cuò)切是在某方向上,按照一定的比例對(duì)圖形的每個(gè)點(diǎn)到某條平行于該方向的直線的有向距離做放縮得到的平面圖形沸移。
Matrix中有關(guān)錯(cuò)切的兩個(gè)方法如下:
setSkew(float m, float n)
setSkew(float m, float n, float px, float py)
m為x軸上的錯(cuò)切因子痪伦,n為y軸上的錯(cuò)切因子,當(dāng)前錯(cuò)切操作的錯(cuò)切坐標(biāo)軸中心點(diǎn)為(px雹锣,py)网沾,如果沒有指定錯(cuò)切坐標(biāo)軸中心點(diǎn),那么默認(rèn)為(0,0)蕊爵。
如圖所示绅这,我們將圖片按照坐標(biāo)軸分成1,2,3,4幾個(gè)部分,1和2為x軸的負(fù)方向在辆,3和4為x軸的正方向证薇,1和3為y軸的負(fù)方向,2和4為y軸的正方向匆篓。
假設(shè)錯(cuò)切因子是m浑度、n,圖片上所有的點(diǎn)用(x鸦概,y)表示箩张,那么錯(cuò)切操作之后的點(diǎn)為
(x + my, y + nx)
也就是說窗市,圖片上的任一點(diǎn)都有一個(gè)從
(x先慷,y)-->(x + my, y + nx)
轉(zhuǎn)變的過程咨察。
當(dāng)m=0時(shí)论熙,轉(zhuǎn)變過程是
(x,y)-->(x 摄狱, y + nx)
當(dāng)n=0時(shí)脓诡,轉(zhuǎn)變過程是
(x无午,y)-->(x + my, y)
所以圖片不管怎么錯(cuò)切祝谚,x軸和y軸的點(diǎn)都不會(huì)變化宪迟。
執(zhí)行以下代碼
matrix.setSkew(0.1f, 0.1f, toTransform.getWidth()/2, toTransform.getHeight()/2);
錯(cuò)切后的效果如下:
使用矩陣數(shù)組的代碼如下(錯(cuò)切坐標(biāo)軸的中心點(diǎn)是圖片的左上角的點(diǎn)):
Bitmap result = pool.get(toTransform.getWidth(), toTransform.getHeight(), Bitmap.Config.ARGB_8888);
if(result == null){
result = Bitmap.createBitmap(toTransform.getWidth(), toTransform.getHeight(), Bitmap.Config.ARGB_8888);
}
Canvas canvas = new Canvas(result);
// 初始化Matrix對(duì)象
Matrix matrix = new Matrix();
// 根據(jù)傳入的參數(shù)設(shè)置縮放比例
float[] values = {
1, 0.1f, 0,
0.1f, 1, 0,
0, 0, 1
};
matrix.setValues(values);
Paint paint = new Paint();
paint.setColor(Color.BLUE);
paint.setStrokeWidth(5);
paint.setAntiAlias(true);
// 根據(jù)縮放比例,把圖片draw到Canvas上
canvas.drawBitmap(toTransform, matrix,paint);
canvas.drawLine(0, toTransform.getHeight()/2, toTransform.getWidth(), toTransform.getHeight()/2, paint);
canvas.drawLine(toTransform.getWidth()/2, 0, toTransform.getWidth()/2, toTransform.getHeight(), paint);
原理:
其中m為x軸方向的錯(cuò)切因子交惯,n為y軸方向的錯(cuò)切因子次泽。
(4)旋轉(zhuǎn)操作
假設(shè)中心點(diǎn)為圖片的中央,在照中心點(diǎn)旋轉(zhuǎn)順時(shí)針旋轉(zhuǎn)30度席爽。
matrix.setRotate(30, toTransform.getWidth()/2, toTransform.getHeight()/2);
假設(shè)中心點(diǎn)為圖片的中央箕憾,在中心點(diǎn)逆時(shí)針旋轉(zhuǎn)30度。
matrix.setRotate(-30, toTransform.getWidth()/2, toTransform.getHeight()/2);
如圖所示
順時(shí)針
逆時(shí)針
旋轉(zhuǎn)原理:
其中拳昌,θ表示圖形旋轉(zhuǎn)的角度袭异,最終得出的圖形旋轉(zhuǎn)矩陣為:
[cosθ -sinθ 0
sinθ cosθ 0
0 0 1]
(5)逆矩陣操作
定義:
在線性代數(shù)中,給定一個(gè) n 階方陣方形矩陣 A炬藤,若存在一 n 階方陣B御铃,使得 AB=BA=In,其中In為 n階單位矩陣沈矿,則稱A是可逆的上真,且B 是A 的逆矩陣,記作A-1羹膳。
Android的Matrix也有對(duì)逆矩陣的封裝
matrix.invert(matrix)
invert
將矩陣逆轉(zhuǎn)睡互,假設(shè)逆轉(zhuǎn)前的矩陣記作A,逆轉(zhuǎn)后的矩陣記作B陵像,單位矩陣記作*C就珠,那么
C = A * B
或者
C = B * A
A和B互為可逆。
舉例1:
平移操作的逆矩陣
向x軸方向移動(dòng)100醒颖, 向y軸方向移動(dòng)100
[1 0 100
0 1 100
0 0 1]
它的逆矩陣是
[1 0 -100
0 1 -100
0 0 1]
舉例2:
縮放操作的逆矩陣
[0.5 0 0
0 2 0
0 0 1]
它的逆矩陣是
[2 0 0
0 0.5 0
0 0 1]
不舉例了妻怎,總之兩個(gè)互逆矩陣相乘為單位矩陣。(記住這個(gè)就可以了)
(6)isAffine
Android5.0新增接口泞歉,判斷是否是仿射矩陣逼侦。
什么是仿射矩陣?
以上講到的平移腰耙,縮放榛丢、旋轉(zhuǎn)、錯(cuò)切挺庞、逆轉(zhuǎn)都是仿射矩陣晰赞,仿射矩陣最明顯的特征就是3x3居中的第三行的數(shù)值都是[0 0 1]。
(7)isIdentity
判斷是否為單位矩陣。
(8)reset
將矩陣重置為單位矩陣宾肺。
(9)前乘變換
preConcat溯饵、preRotate侵俗、preScale锨用、preSkew、preTranslate
pre
稱之為前乘隘谣,也可以稱之為右乘增拥。
float[] A = {
1, 0, 0,
0, 1, 0,
0, 0, 1
};
matrix.setValues(A);
matrix.preConcat(B);
以上代碼其實(shí)就是A * B,B右乘A寻歧。(當(dāng)然掌栅,也可以說A左乘B)
(10)后乘變換
postConcat、postRotate码泛、postScale猾封、postSkew、postTranslate
Matrix matrix = new Matrix();
// 根據(jù)傳入的參數(shù)設(shè)置縮放比例
float[] A = {
1, 0, 0,
0, 1, 0,
0, 0, 1
};
matrix.setValues(A);
matrix.postConcat(B);
以上代碼其實(shí)就是B * A噪珊,B左乘A晌缘。(當(dāng)然,也可以說A右乘B)
(11)rectStaysRect
判斷該矩陣是否可以將一個(gè)矩形依然變換為一個(gè)矩形痢站。當(dāng)矩陣是單位矩陣磷箕,或者只進(jìn)行平移,縮放阵难,以及旋轉(zhuǎn)90度的倍數(shù)的時(shí)候岳枷,返回true。
(12)setRectToRect
setRectToRect(RectF src, RectF dst, ScaleToFit stf)
將rect變換成rect呜叫。
ScaleToFit 有如下四個(gè)值:
FILL:
可能會(huì)變換矩形的長(zhǎng)寬比空繁,保證變換和目標(biāo)矩陣長(zhǎng)寬一致。
START:
保持坐標(biāo)變換前矩形的長(zhǎng)寬比朱庆,并最大限度的填充變換后的矩形家厌。至少有一邊和目標(biāo)矩形重疊。左上對(duì)齊椎工。
CENTER:
保持坐標(biāo)變換前矩形的長(zhǎng)寬比饭于,并最大限度的填充變換后的矩形。至少有一邊和目標(biāo)矩形重疊维蒙。
END:
保持坐標(biāo)變換前矩形的長(zhǎng)寬比掰吕,并最大限度的填充變換后的矩形。至少有一邊和目標(biāo)矩形重疊颅痊。右下對(duì)齊殖熟。
(13)setSinCos
setSinCos(float sinValue, float cosValue, float px, float py)
setSinCos(float sinValue, float cosValue)
這個(gè)常用于矩陣的旋轉(zhuǎn)操作,之前我們講過斑响,圖片旋轉(zhuǎn)之后的矩陣為:
[cosθ -sinθ 0
sinθ cosθ 0
0 0 1]
假如需要旋轉(zhuǎn)90度
matrix.setRotate(90);
或者
//sin90 = 1菱属,cos90 = 0
matrix.setSinCos(1, 0);
以上兩種寫法都是可以的钳榨。
(14)setPolyToPoly
setPolyToPoly(float[] src, int srcIndex, float[] dst, int dstIndex, int pointCount)
src:
原始點(diǎn)
srcIndex:
原始點(diǎn)的索引
dst:
目標(biāo)點(diǎn)
dstIndex:
目標(biāo)點(diǎn)的索引
pointCount:
點(diǎn)的數(shù)量取值范圍是[0, 4]
- 當(dāng)pointCount為0時(shí),則設(shè)置無(wú)效纽门。
- 當(dāng)pointCount為1時(shí)薛耻,則控制一個(gè)點(diǎn),可以將一個(gè)點(diǎn)平移到另一個(gè)點(diǎn)赏陵,相當(dāng)于平移操作饼齿。
float[] src = {0, 0};
float[] dst = {100, 100};
matrix.setPolyToPoly(src, 0, dst, 0 ,1);
- 當(dāng)pointCount為2時(shí),則控制兩個(gè)點(diǎn)蝙搔,兩個(gè)點(diǎn)可以控制旋轉(zhuǎn)和縮放操作缕溉,即以一點(diǎn)為中心點(diǎn),將圖片上的一點(diǎn)吃型,移動(dòng)到另一點(diǎn)证鸥。
如上圖所示,綠色線條為直角坐標(biāo)勤晚,紅色點(diǎn)分別是A枉层、B、O运翼。
現(xiàn)在要求將圖片按照O旋轉(zhuǎn)返干,從點(diǎn)A旋轉(zhuǎn)到B,代碼如下:
float[] src = {toTransform.getWidth() / 2, toTransform.getHeight() / 2, toTransform.getWidth(), 0};
float[] dst = {toTransform.getWidth() / 2, toTransform.getHeight() / 2, toTransform.getWidth() / 2, toTransform.getHeight()};
matrix.setPolyToPoly(src, 0, dst, 0 ,2);
效果如下:
我們已經(jīng)將圖片上的A點(diǎn)移動(dòng)到了B點(diǎn)血淌,完全符合預(yù)想中的效果矩欠。(旋轉(zhuǎn)且縮放)
那么,再舉一個(gè)例子悠夯,將B點(diǎn)移動(dòng)到A點(diǎn)癌淮,O為中心點(diǎn)。
代碼如下:
float[] src = {toTransform.getWidth() / 2, 0, toTransform.getWidth(), 0};
float[] dst = {toTransform.getWidth() / 2, 0, toTransform.getWidth() * 3 / 4, 0};
matrix.setPolyToPoly(src, 0, dst, 0 ,2);
效果如下:
-
當(dāng)pointCount為3時(shí)沦补,則控制三個(gè)點(diǎn)乳蓄,三個(gè)點(diǎn)可以控制錯(cuò)切操作,即指定圖片上的三點(diǎn)夕膀,一點(diǎn)固定虚倒,另外兩點(diǎn)移動(dòng)。
float[] src = {0, 0, 0, toTransform.getHeight(), toTransform.getWidth(), toTransform.getHeight()}; float[] dst = {0, 0, 200, toTransform.getHeight(), toTransform.getWidth() + 100, toTransform.getHeight()}; matrix.setPolyToPoly(src, 0, dst, 0 ,3);
效果如下:
這個(gè)比較好理解产舞,固定一點(diǎn)魂奥,其他兩點(diǎn)移動(dòng)。數(shù)組里面有三個(gè)點(diǎn)易猫,第一個(gè)點(diǎn)為固定點(diǎn)耻煤,后面兩個(gè)點(diǎn)為移動(dòng)點(diǎn)。
- 當(dāng)pointCount為4時(shí),則控制四個(gè)點(diǎn)哈蝇,四個(gè)點(diǎn)可以實(shí)現(xiàn)透視效果棺妓。
矩陣變換有兩種:
仿射變換:
前面所說的平移,縮放炮赦、旋轉(zhuǎn)怜跑、錯(cuò)切、逆轉(zhuǎn)都是仿射矩陣眼五,仿射矩陣最明顯的特征就是3x3居中的第三行的數(shù)值都是[0 0 1]妆艘。(2D效果)
透視變換:
可以展示3D效果的透視圖(3D效果)(影映射矩陣)
在PS中彤灶,如果圖片不是很正看幼,那么可以使用透視裁剪工具將圖片矯正。
上圖就是矯正前的圖片幌陕。
在Android中诵姜,將正圖轉(zhuǎn)換成以上透視圖就是從2D轉(zhuǎn)3D的過程,這個(gè)過程叫做透視變換搏熄。
代碼如下
float[] src = {0, 0, 0, toTransform.getHeight(), toTransform.getWidth(), toTransform.getHeight(), toTransform.getWidth(), 0};
int offset = 100;
float[] dst = {0 + offset, 0, 0, toTransform.getHeight(), toTransform.getWidth(), toTransform.getHeight(), toTransform.getWidth()-offset, 0};
matrix.setPolyToPoly(src, 0, dst, 0 ,4);
選定圖四角上的四點(diǎn)棚唆,將上面兩個(gè)點(diǎn)縮小,效果如下:
(15)mapPoints
mapPoints(float[] dst, int dstIndex, float[] src, int srcIndex,int pointCount)
mapPoints(float[] dst, float[] src)
mapPoints(float[] pts)
映射點(diǎn)的值到指定的數(shù)組中心例,這個(gè)方法可以在矩陣變換以后宵凌,給出指定點(diǎn)的值。
dst:指定寫入的數(shù)組
dstIndex:寫入的起始索引止后,x瞎惫,y兩個(gè)坐標(biāo)算作一對(duì),索引的單位是對(duì)译株,也就是經(jīng)過兩個(gè)值才加1
src:指定要計(jì)算的點(diǎn)
srcIndex:要計(jì)算的點(diǎn)的索引
pointCount:需要計(jì)算的點(diǎn)的個(gè)數(shù)瓜喇,每個(gè)點(diǎn)有兩個(gè)值,x和y歉糜。
(16)mapRect
mapRect(RectF dst, RectF src)
mapRect(RectF rect)
返回值即是調(diào)用的rectStaysRect()乘寒。
(17)mapRadius
mapRadius(float radius)
返回一個(gè)圓圈半徑的平均值,將matrix作用于一個(gè)指定radius半徑的圓匪补,隨后返回的平均半徑伞辛。
(18)mapVectors
mapVectors(float[] dst, int dstIndex, float[] src, int srcIndex,int vectorCount)
mapVectors(float[] dst, float[] src)
mapVectors(float[] vecs)
與上面的mapPoionts基本類似,這里是將一個(gè)矩陣作用于一個(gè)向量夯缺,由于向量的平移前后是相等的蚤氏,所以這個(gè)方法不會(huì)對(duì)translate相關(guān)的方法產(chǎn)生反應(yīng),如果只是調(diào)用了translate相關(guān)的方法喳逛,那么得到的值和原本的一致瞧捌。
[本章完...]