圖像處理之變換矩陣

圖像的處理大致有兩種:

  • 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處理錯切變換,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對象
    Matrix matrix = new Matrix();
    // 根據傳入的參數設置縮放比例
    matrix.setScale(scale_x, scale_y);
    Paint paint = new Paint();
    paint.setAntiAlias(true);
    // 根據縮放比例,把圖片draw到Canvas上
    canvas.drawBitmap(toTransform, matrix,paint);

如果scale_x = 1,scale_y = 1,那麼相當於沒有縮放,原圖效果如下:

當我們將scale_x 和scale_y 都設置成0.5時,也就是將原圖縮小一半,如題所示:

滿足以上效果,也可以直接設置矩陣

    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對象
    Matrix matrix = new Matrix();
    // 根據傳入的參數設置縮放比例
    float[] values = {
            0.5f,  0,   0,
              0,  0.5f, 0,
              0,   0,   1
    };
    matrix.setValues(values);
    Paint paint = new Paint();
    paint.setAntiAlias(true);
    // 根據縮放比例,把圖片draw到Canvas上
    canvas.drawBitmap(toTransform, matrix,paint);

原理:

最終得出的圖形旋轉矩陣爲:

[a     0     0
 0     b     0
 0     0     1]

a爲x軸方向的縮放係數,b爲y軸方向的縮放係數。

(2)平移操作

在當前畫布上,x方向和y方向,各移動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對象
    Matrix matrix = new Matrix();
    // 根據傳入的參數設置縮放比例
    matrix.setTranslate(100, 100);
    Paint paint = new Paint();
    paint.setAntiAlias(true);
    // 根據縮放比例,把圖片draw到Canvas上
    canvas.drawBitmap(toTransform, matrix,paint);

效果如下:

使用矩陣數組,我們將圖片縮小一倍之後再移動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對象
    Matrix matrix = new Matrix();
    // 根據傳入的參數設置縮放比例
    float[] values = {
            0.5f,  0,   100,
              0,  0.5f, 100,
              0,   0,   1
    };
    matrix.setValues(values);
    Paint paint = new Paint();
    paint.setAntiAlias(true);
    // 根據縮放比例,把圖片draw到Canvas上
    canvas.drawBitmap(toTransform, matrix,paint);

效果如下:

原理:

最終得出的圖形旋轉矩陣爲:

[1     0     a
 0     1     b
 0     0     1]

a爲x軸方向的平移,b爲y軸方向的平移。

(3)錯切操作

定義: 錯切是在某方向上,按照一定的比例對圖形的每個點到某條平行於該方向的直線的有向距離做放縮得到的平面圖形。

Matrix中有關錯切的兩個方法如下:

setSkew(float m, float n)
setSkew(float m, float n, float px, float py)

m爲x軸上的錯切因子,n爲y軸上的錯切因子,當前錯切操作的錯切座標軸中心點爲(px,py),如果沒有指定錯切座標軸中心點,那麼默認爲(0,0)。

如圖所示,我們將圖片按照座標軸分成1,2,3,4幾個部分,1和2爲x軸的負方向,3和4爲x軸的正方向,1和3爲y軸的負方向,2和4爲y軸的正方向。

假設錯切因子是m、n,圖片上所有的點用(x,y)表示,那麼錯切操作之後的點爲

(x + my, y + nx)

也就是說,圖片上的任一點都有一個從

(x,y)-->(x + my, y + nx)

轉變的過程。

當m=0時,轉變過程是

(x,y)-->(x , y + nx)

當n=0時,轉變過程是

(x,y)-->(x + my, y)

所以圖片不管怎麼錯切,x軸和y軸的點都不會變化。

執行以下代碼

matrix.setSkew(0.1f, 0.1f, toTransform.getWidth()/2, toTransform.getHeight()/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對象
    Matrix matrix = new Matrix();
    // 根據傳入的參數設置縮放比例
    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);
    // 根據縮放比例,把圖片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軸方向的錯切因子,n爲y軸方向的錯切因子。

(4)旋轉操作

假設中心點爲圖片的中央,在照中心點旋轉順時針旋轉30度。

matrix.setRotate(30, toTransform.getWidth()/2, toTransform.getHeight()/2);

假設中心點爲圖片的中央,在中心點逆時針旋轉30度。

matrix.setRotate(-30, toTransform.getWidth()/2, toTransform.getHeight()/2);

如圖所示

順時針

逆時針

旋轉原理:

其中,θ表示圖形旋轉的角度,最終得出的圖形旋轉矩陣爲:

[cosθ     -sinθ     0
 sinθ      cosθ     0
  0        0        1]
(5)逆矩陣操作

定義: 在線性代數中,給定一個 n 階方陣方形矩陣 A,若存在一 n 階方陣B,使得 AB=BA=In,其中In爲 n階單位矩陣,則稱A可逆的,且BA逆矩陣,記作A-1

Android的Matrix也有對逆矩陣的封裝

matrix.invert(matrix)

invert將矩陣逆轉,假設逆轉前的矩陣記作A,逆轉後的矩陣記作B,單位矩陣記作*C,那麼

C = A * B

或者

C = B * A

A和B互爲可逆。

舉例1: 平移操作的逆矩陣

向x軸方向移動100, 向y軸方向移動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]

不舉例了,總之兩個互逆矩陣相乘爲單位矩陣。(記住這個就可以了)

(6)isAffine

Android5.0新增接口,判斷是否是仿射矩陣。

什麼是仿射矩陣?

以上講到的平移,縮放、旋轉、錯切、逆轉都是仿射矩陣仿射矩陣最明顯的特徵就是3x3居中的第三行的數值都是[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);

以上代碼其實就是A * BB右乘A。(當然,也可以說A左乘B

(10)後乘變換

postConcat、postRotate、postScale、postSkew、postTranslate

    Matrix matrix = new Matrix();
    // 根據傳入的參數設置縮放比例
    float[] A = {
            1,    0,   0,
            0,    1,   0,
            0,    0,   1
    };
    matrix.setValues(A);
    matrix.postConcat(B);

以上代碼其實就是B * AB左乘A。(當然,也可以說A右乘B

(11)rectStaysRect

判斷該矩陣是否可以將一個矩形依然變換爲一個矩形。當矩陣是單位矩陣,或者只進行平移,縮放,以及旋轉90度的倍數的時候,返回true。

(12)setRectToRect
setRectToRect(RectF src, RectF dst, ScaleToFit stf)

將rect變換成rect。

ScaleToFit 有如下四個值:

FILL: 可能會變換矩形的長寬比,保證變換和目標矩陣長寬一致。
START: 保持座標變換前矩形的長寬比,並最大限度的填充變換後的矩形。至少有一邊和目標矩形重疊。左上對齊。
CENTER: 保持座標變換前矩形的長寬比,並最大限度的填充變換後的矩形。至少有一邊和目標矩形重疊。
END: 保持座標變換前矩形的長寬比,並最大限度的填充變換後的矩形。至少有一邊和目標矩形重疊。右下對齊。

(13)setSinCos
setSinCos(float sinValue, float cosValue, float px, float py)
setSinCos(float sinValue, float cosValue)

這個常用於矩陣的旋轉操作,之前我們講過,圖片旋轉之後的矩陣爲:

[cosθ     -sinθ     0
 sinθ      cosθ     0
  0        0        1]

假如需要旋轉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: 原始點
srcIndex:原始點的索引
dst: 目標點
dstIndex: 目標點的索引
pointCount: 點的數量取值範圍是[0, 4]

  • 當pointCount爲0時,則設置無效。
  • 當pointCount爲1時,則控制一個點,可以將一個點平移到另一個點,相當於平移操作。
    float[] src = {0, 0};
    float[] dst = {100, 100};
    matrix.setPolyToPoly(src, 0, dst, 0 ,1);
  • 當pointCount爲2時,則控制兩個點,兩個點可以控制旋轉和縮放操作,即以一點爲中心點,將圖片上的一點,移動到另一點。

如上圖所示,綠色線條爲直角座標,紅色點分別是A、B、O。

現在要求將圖片按照O旋轉,從點A旋轉到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);

效果如下:

我們已經將圖片上的A點移動到了B點,完全符合預想中的效果。(旋轉且縮放)

那麼,再舉一個例子,將B點移動到A點,O爲中心點。

代碼如下:

    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);

效果如下:

  • 當pointCount爲3時,則控制三個點,三個點可以控制錯切操作,即指定圖片上的三點,一點固定,另外兩點移動。

      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);
    

效果如下:

這個比較好理解,固定一點,其他兩點移動。數組裏面有三個點,第一個點爲固定點,後面兩個點爲移動點。

  • 當pointCount爲4時,則控制四個點,四個點可以實現透視效果。

矩陣變換有兩種:

仿射變換: 前面所說的平移,縮放、旋轉、錯切、逆轉都是仿射矩陣,仿射矩陣最明顯的特徵就是3x3居中的第三行的數值都是[0 0 1]。(2D效果)
透視變換: 可以展示3D效果的透視圖(3D效果)(影映射矩陣)

在PS中,如果圖片不是很正,那麼可以使用透視裁剪工具將圖片矯正。

上圖就是矯正前的圖片。

在Android中,將正圖轉換成以上透視圖就是從2D轉3D的過程,這個過程叫做透視變換。

代碼如下

    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);

選定圖四角上的四點,將上面兩個點縮小,效果如下:

(15)mapPoints
mapPoints(float[] dst, int dstIndex, float[] src, int srcIndex,int pointCount) 
mapPoints(float[] dst, float[] src) 
mapPoints(float[] pts)

映射點的值到指定的數組中,這個方法可以在矩陣變換以後,給出指定點的值。
dst:指定寫入的數組
dstIndex:寫入的起始索引,x,y兩個座標算作一對,索引的單位是對,也就是經過兩個值才加1
src:指定要計算的點
srcIndex:要計算的點的索引
pointCount:需要計算的點的個數,每個點有兩個值,x和y。

(16)mapRect
mapRect(RectF dst, RectF src)
mapRect(RectF rect)

返回值即是調用的rectStaysRect()。

(17)mapRadius
mapRadius(float radius)

返回一個圓圈半徑的平均值,將matrix作用於一個指定radius半徑的圓,隨後返回的平均半徑。

(18)mapVectors
mapVectors(float[] dst, int dstIndex, float[] src, int srcIndex,int vectorCount)     
mapVectors(float[] dst, float[] src) 
mapVectors(float[] vecs)

與上面的mapPoionts基本類似,這裏是將一個矩陣作用於一個向量,由於向量的平移前後是相等的,所以這個方法不會對translate相關的方法產生反應,如果只是調用了translate相關的方法,那麼得到的值和原本的一致。

[本章完...]

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章