Android開發(fā)之圖像處理那點事——變換

繼上一篇《Android開發(fā)之圖像處理那點事——濾鏡》,這次我們來講一下圖片的變換操作,本篇主要是介紹常見的圖像變換4種操作(平移、縮放、旋轉(zhuǎn)、錯切)

對于圖像的顏色處理,Andriod系統(tǒng)給我們提供一個很方便的顏色矩陣類ColorMatrix,同樣的,Android系統(tǒng)也給圖像的變換處理提供了矩陣類Matrix,相比顏色矩陣的4 * 5,變換矩陣來得更加簡單,它是由一個3 * 3的數(shù)字矩陣組成,我們來了解一下變換原理。

變換矩陣

在上一篇文章中,我們講到像素點的顏色是由RGBA1組成的,分別代表紅、綠、藍、透明度各通道值還有顏色偏移量,在變換矩陣中也是類似的,像素點的位置是由矩陣C(X、Y、L)組成的,分別代表像素點在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)、錯切(很少),這些操作就是對這3 * 3的矩陣做數(shù)值上的調(diào)整,和上一篇講顏色矩陣變化是類似的,不清楚的朋友,具體可以參考上一篇文章的講解,下面我們快速過一下流程。

平移操作

像素點的平移其實就是對像素點的x和y坐標(biāo)進行移動,如下圖,從點p1移動到點p,假設(shè)p1(x1,y1)分別平移了x0和y0距離得到p(x,y),

平移操作

得到的公式就是:

x = x1 + x0
y = y1 + y0

矩陣所表現(xiàn)出來的形式:

平移操作

縮放操作

對于單個像素點是不存在縮放操作的,但如果是一系列的像素點,我們就可以根據(jù)x和y軸做一定比例的縮放,假設(shè)縮放比例為K1,得到的公式是:

x = K1 * x0
y = k1 * y0

矩陣所表現(xiàn)出來的形式:

縮放操作

旋轉(zhuǎn)操作

像素點的旋轉(zhuǎn)是圍繞一個點旋轉(zhuǎn)一定的角度得到新的像素點位置,為了便于理解,這里帶上一張圖:

旋轉(zhuǎn)操作

如上圖以原點為中心旋轉(zhuǎ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)形式為:

旋轉(zhuǎn)操作

錯切操作

這個平時用的比較少,大概了解一下就可以,錯切分為水平錯切和垂直錯切。

水平錯切效果就是讓所有像素點的Y軸坐標(biāo)不變,X軸坐標(biāo)按照比例進行平移,且平移的大小與該點到Y(jié)軸的距離成成正比,計算公式為:

x = x0 + k1 * y0
y = y0

矩陣變現(xiàn)形式為:

水平錯切

垂直錯切讓所有像素點的X軸坐標(biāo)不變,Y軸坐標(biāo)按照比例進行平移,且平移的大小與該點到X軸的距離成成正比,計算公式為:

x = x0 
y = y0+ k2 * x0

矩陣變現(xiàn)形式為:

垂直錯切

兩個方向都錯切,公式為:

x = x0 + k1 * y0
y = k2 * x0 * y0
水平/垂直錯切

根據(jù)以上的矩陣表現(xiàn)方式,我們可以推出圖像的變換規(guī)律為:

矩陣變換規(guī)律

Matrix

了解了各種變換原理后,我們來看下Matrix類,這是Android系統(tǒng)給我們提供的圖像變換矩陣類,打開源碼,我們可以看到這9個常量:

    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的矩陣形式排開,可以得到:

矩陣

對比下剛才上面我們推導(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 決定了錯切( Skew)
其實圖像的變換操作無非就是對這個3 * 3的矩陣進行值的變化,而Matrix類只是幫我們封裝好了這些操作,讓我們無需關(guān)心細節(jié),更加專注業(yè)務(wù)的實現(xiàn),我們可以在Matrix中找到各操作對應(yīng)的調(diào)用方法:

矩陣的操作方法

簡單的看個小Demo:

上面分別對圖像進行了平移、縮放、旋轉(zhuǎn)、錯切操作,貼一下核心代碼:

            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)放大原來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)按比例錯切平移2
            canvas.drawBitmap(mBitmap, matrix, null);

當(dāng)然這些操作也是可以組合起來的,Matrix里提供了一系列的postXXX,preXXX方法:

矩陣的操作方法

其實就是線性代數(shù)中矩陣的左乘和右乘,由于矩陣的乘法是不滿足乘法交換律的,所以變換操作的執(zhí)行順序是對結(jié)果有影響的,繼續(xù)來個小Demo:

先平移,再放大,再旋轉(zhuǎn)

實現(xiàn)代碼:

            Matrix matrix = new Matrix();
            matrix.setTranslate(200, 200);
            matrix.postScale(2, 2);
            matrix.postRotate(20);
            canvas.drawBitmap(mBitmap, matrix, null);
先平移,再旋轉(zhuǎn),再放大

實現(xiàn)代碼:

            Matrix matrix = new Matrix();
            matrix.setTranslate(200, 200);
            matrix.preScale(2, 2);
            matrix.preRotate(20);
            canvas.drawBitmap(mBitmap, matrix, null);

簡單粗暴可以這樣理解:Matrix操作是一系列的任務(wù)存放在一個隊列里,pre是把當(dāng)前任務(wù)插入隊列前,post是插入隊列后,set則是清空隊列所有任務(wù),再入隊。

這些看似很簡單很基礎(chǔ)的東西卻恰恰是最重要的,它可以創(chuàng)造出很多東西,比如圖片的自由手勢縮放,微博上面的自定義貼紙,美圖秀秀里的相框拼接等效果都離不開它。

源碼下載:

這里附上源碼地址(歡迎Star,歡迎Fork):BeautyImageDemo

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容