繼上一篇《Android開發(fā)之圖像處理那點(diǎn)事——濾鏡》,這次我們來(lái)講一下圖片的變換操作阻星,本篇主要是介紹常見的圖像變換4種操作(平移、縮放已添、旋轉(zhuǎn)妥箕、錯(cuò)切)
對(duì)于圖像的顏色處理,Andriod系統(tǒng)給我們提供一個(gè)很方便的顏色矩陣類ColorMatrix更舞,同樣的畦幢,Android系統(tǒng)也給圖像的變換處理提供了矩陣類Matrix,相比顏色矩陣的4 * 5缆蝉,變換矩陣來(lái)得更加簡(jiǎn)單宇葱,它是由一個(gè)3 * 3的數(shù)字矩陣組成瘦真,我們來(lái)了解一下變換原理。
變換矩陣
在上一篇文章中黍瞧,我們講到像素點(diǎn)的顏色是由RGBA1組成的诸尽,分別代表紅、綠印颤、藍(lán)您机、透明度各通道值還有顏色偏移量,在變換矩陣中也是類似的年局,像素點(diǎn)的位置是由矩陣C(X际看、Y、L)組成的矢否,分別代表像素點(diǎn)在X仲闽、Y軸的位置還有偏移量。
根據(jù)矩陣的乘法僵朗,我們可以得出下面的等式:
X1 = a * X + b * Y + c
Y1 = d * X + e * Y + f
L = g * X + h * Y + i
當(dāng)a=1赖欣,b和c=0,可以知道X1=1X+0+0=1X验庙,
當(dāng)e=1畏鼓,d和f=0,可以知道Y1=0+1Y+0=1Y
當(dāng)i=1壶谒,g和h=0,可以知道1=0+0+1=1
由此我們可以得到變換矩陣的單位矩陣:
在日常開發(fā)中膳沽,我們涉及到的圖像變換大致有平移汗菜、縮放、旋轉(zhuǎn)挑社、錯(cuò)切(很少)陨界,這些操作就是對(duì)這3 * 3的矩陣做數(shù)值上的調(diào)整,和上一篇講顏色矩陣變化是類似的痛阻,不清楚的朋友菌瘪,具體可以參考上一篇文章的講解,下面我們快速過(guò)一下流程阱当。
平移操作
像素點(diǎn)的平移其實(shí)就是對(duì)像素點(diǎn)的x和y坐標(biāo)進(jìn)行移動(dòng)俏扩,如下圖,從點(diǎn)p1移動(dòng)到點(diǎn)p弊添,假設(shè)p1(x1录淡,y1)分別平移了x0和y0距離得到p(x,y)油坝,
得到的公式就是:
x = x1 + x0
y = y1 + y0
矩陣所表現(xiàn)出來(lái)的形式:
縮放操作
對(duì)于單個(gè)像素點(diǎn)是不存在縮放操作的嫉戚,但如果是一系列的像素點(diǎn)刨裆,我們就可以根據(jù)x和y軸做一定比例的縮放,假設(shè)縮放比例為K1彬檀,得到的公式是:
x = K1 * x0
y = k1 * y0
矩陣所表現(xiàn)出來(lái)的形式:
旋轉(zhuǎn)操作
像素點(diǎn)的旋轉(zhuǎn)是圍繞一個(gè)點(diǎn)旋轉(zhuǎn)一定的角度得到新的像素點(diǎn)位置帆啃,為了便于理解,這里帶上一張圖:
如上圖以原點(diǎn)為中心旋轉(zhuǎn)窍帝,點(diǎn)(x0努潘,y0)旋轉(zhuǎn)了β°角度到了p(x,y)盯桦,假設(shè)斜邊為r慈俯,根據(jù)三角函數(shù)我們可以知道:
x0 = r * cosα
y0 = r * sinα
那么x和y就是x0和y0旋轉(zhuǎn)了α角度后,再旋轉(zhuǎn)了β角度拥峦,根據(jù)三角函數(shù)可以推出:
x = r * cos(α + β) = r * cosα * cosβ - r * sinα * sinβ = x0 * cosβ - y0 * sinβ
y = r * sin(α + β) = r * sinα * cosβ + r * cosα * sinβ = y0 * cosβ + x0 * sinβ
將上面的式子代入贴膘,根據(jù)推導(dǎo)公式,我們可以知道矩陣的表現(xiàn)形式為:
錯(cuò)切操作
這個(gè)平時(shí)用的比較少略号,大概了解一下就可以刑峡,錯(cuò)切分為水平錯(cuò)切和垂直錯(cuò)切。
水平錯(cuò)切效果就是讓所有像素點(diǎn)的Y軸坐標(biāo)不變玄柠,X軸坐標(biāo)按照比例進(jìn)行平移突梦,且平移的大小與該點(diǎn)到Y(jié)軸的距離成成正比,計(jì)算公式為:
x = x0 + k1 * y0
y = y0
矩陣變現(xiàn)形式為:
垂直錯(cuò)切讓所有像素點(diǎn)的X軸坐標(biāo)不變羽利,Y軸坐標(biāo)按照比例進(jìn)行平移宫患,且平移的大小與該點(diǎn)到X軸的距離成成正比,計(jì)算公式為:
x = x0
y = y0+ k2 * x0
矩陣變現(xiàn)形式為:
兩個(gè)方向都錯(cuò)切这弧,公式為:
x = x0 + k1 * y0
y = k2 * x0 * y0
根據(jù)以上的矩陣表現(xiàn)方式娃闲,我們可以推出圖像的變換規(guī)律為:
Matrix
了解了各種變換原理后,我們來(lái)看下Matrix類匾浪,這是Android系統(tǒng)給我們提供的圖像變換矩陣類皇帮,打開源碼,我們可以看到這9個(gè)常量:
public static final int MSCALE_X = 0; //!< use with getValues/setValues
public static final int MSKEW_X = 1; //!< use with getValues/setValues
public static final int MTRANS_X = 2; //!< use with getValues/setValues
public static final int MSKEW_Y = 3; //!< use with getValues/setValues
public static final int MSCALE_Y = 4; //!< use with getValues/setValues
public static final int MTRANS_Y = 5; //!< use with getValues/setValues
public static final int MPERSP_0 = 6; //!< use with getValues/setValues
public static final int MPERSP_1 = 7; //!< use with getValues/setValues
public static final int MPERSP_2 = 8; //!< use with getValues/setValues
我們把它按3 * 3的矩陣形式排開蛋辈,可以得到:
對(duì)比下剛才上面我們推導(dǎo)的矩陣表現(xiàn)形式属拾,怎么樣,是不是更有感覺了冷溶,我們可以得到如下信息:
MTRANS_X渐白、MTRANS_Y 決定了平移(Translate)
MSCALE_X、MSCALE_Y 決定了縮放( Scale)
MSCALE_X逞频、MSKEW_X礼预、MSCALE_Y、MSKEW_Y 決定了旋轉(zhuǎn)( Rotate)
MSKEW_X虏劲、MSKEW_Y 決定了錯(cuò)切( Skew)
其實(shí)圖像的變換操作無(wú)非就是對(duì)這個(gè)3 * 3的矩陣進(jìn)行值的變化托酸,而Matrix類只是幫我們封裝好了這些操作褒颈,讓我們無(wú)需關(guān)心細(xì)節(jié),更加專注業(yè)務(wù)的實(shí)現(xiàn)励堡,我們可以在Matrix中找到各操作對(duì)應(yīng)的調(diào)用方法:
簡(jiǎn)單的看個(gè)小Demo:
上面分別對(duì)圖像進(jìn)行了平移谷丸、縮放、旋轉(zhuǎn)应结、錯(cuò)切操作刨疼,貼一下核心代碼:
Matrix matrix = new Matrix();
matrix.setTranslate(50, 50);//x,y坐標(biāo)平移50像素
canvas.drawBitmap(mBitmap, matrix, null);
Matrix matrix = new Matrix();
matrix.setScale(2, 2);//x鹅龄,y坐標(biāo)放大原來(lái)2倍
canvas.drawBitmap(mBitmap, matrix, null);
Matrix matrix = new Matrix();
matrix.setRotate(20);//x揩慕,y坐標(biāo)旋轉(zhuǎn)20度
canvas.drawBitmap(mBitmap, matrix, null);
Matrix matrix = new Matrix();
matrix.setSkew(2, 2);//x,y坐標(biāo)按比例錯(cuò)切平移2
canvas.drawBitmap(mBitmap, matrix, null);
當(dāng)然這些操作也是可以組合起來(lái)的扮休,Matrix里提供了一系列的postXXX迎卤,preXXX方法:
其實(shí)就是線性代數(shù)中矩陣的左乘和右乘,由于矩陣的乘法是不滿足乘法交換律的玷坠,所以變換操作的執(zhí)行順序是對(duì)結(jié)果有影響的蜗搔,繼續(xù)來(lái)個(gè)小Demo:
實(shí)現(xiàn)代碼:
Matrix matrix = new Matrix();
matrix.setTranslate(200, 200);
matrix.postScale(2, 2);
matrix.postRotate(20);
canvas.drawBitmap(mBitmap, matrix, null);
實(shí)現(xiàn)代碼:
Matrix matrix = new Matrix();
matrix.setTranslate(200, 200);
matrix.preScale(2, 2);
matrix.preRotate(20);
canvas.drawBitmap(mBitmap, matrix, null);
簡(jiǎn)單粗暴可以這樣理解:Matrix操作是一系列的任務(wù)存放在一個(gè)隊(duì)列里兄渺,pre是把當(dāng)前任務(wù)插入隊(duì)列前缝龄,post是插入隊(duì)列后,set則是清空隊(duì)列所有任務(wù)挂谍,再入隊(duì)二拐。
這些看似很簡(jiǎn)單很基礎(chǔ)的東西卻恰恰是最重要的,它可以創(chuàng)造出很多東西凳兵,比如圖片的自由手勢(shì)縮放,微博上面的自定義貼紙企软,美圖秀秀里的相框拼接等效果都離不開它庐扫。
源碼下載:
這里附上源碼地址(歡迎Star,歡迎Fork):BeautyImageDemo