Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android Matrix源碼詳解

Android Matrix源碼詳解

編輯:關於Android編程

Matrix的數學原理

在Android中,如果你用Matrix進行過圖像處理,那麼一定知道Matrix這個類。Android中的Matrix是一個3 x 3的矩陣,其內容如下: 

Matrix的對圖像的處理可分為四類基本變換:

  • Translate           平移變換
  • Rotate                旋轉變換
  • Scale                  縮放變換
  • Skew                  錯切變換

從字面上理解,矩陣中的MSCALE用於處理縮放變換,MSKEW用於處理錯切變換,MTRANS用於處理平移變換,MPERSP用於處理透視變換。實際中當然不能完全按照字面上的說法去理解Matrix。同時,在Android的文檔中,未見到用Matrix進行透視變換的相關說明,所以本文也不討論這方面的問題。

針對每種變換,Android提供了pre、set和post三種操作方式。其中

set用於設置Matrix中的值。

pre是先乘,因為矩陣的乘法不滿足交換律,因此先乘、後乘必須要嚴格區分。先乘相當於矩陣運算中的右乘。

post是後乘,因為矩陣的乘法不滿足交換律,因此先乘、後乘必須要嚴格區分。後乘相當於矩陣運算中的左乘。

除平移變換(Translate)外,旋轉變換(Rotate)、縮放變換(Scale)和錯切變換(Skew)都可以圍繞一個中心點來進行,如果不指定,在默認情況下是圍繞(0, 0)來進行相應的變換的。 

下面我們來看看四種變換的具體情形。由於所有的圖形都是有點組成,因此我們只需要考察一個點相關變換即可。

一、 平移變換

假定有一個點的坐標是 ,將其移動到 ,再假定在x軸和y軸方向移動的大小分別為:

如下圖所示:

不難知道:

如果用矩陣來表示的話,就可以寫成:

 

二、 旋轉變換

2.1    圍繞坐標原點旋轉:

假定有一個點 ,相對坐標原點順時針旋轉後的情形,同時假定P點離坐標原點的距離為r,如下圖:

那麼,

如果用矩陣,就可以表示為:

2.2    圍繞某個點旋轉

如果是圍繞某個點順時針旋轉,那麼可以用矩陣表示為:

可以化為:

很顯然,

1.  是將坐標原點移動到點後, 的新坐標。

2.     是將上一步變換後的,圍繞新的坐標原點順時針旋轉

3.    

經過上一步旋轉變換後,再將坐標原點移回到原來的坐標原點。

 所以,圍繞某一點進行旋轉變換,可以分成3個步驟,即首先將坐標原點移至該點,然後圍繞新的坐標原點進行旋轉變換,再然後將坐標原點移回到原先的坐標原點。

 三、 縮放變換

理論上而言,一個點是不存在什麼縮放變換的,但考慮到所有圖像都是由點組成,因此,如果圖像在x軸和y軸方向分別放大k1和k2倍的話,那麼圖像中的所有點的x坐標和y坐標均會分別放大k1和k2倍,即

用矩陣表示就是:

 

縮放變換比較好理解,就不多說了。

 四、 錯切變換

錯切變換(skew)在數學上又稱為Shear mapping(可譯為“剪切變換”)或者Transvection(縮並),它是一種比較特殊的線性變換。錯切變換的效果就是讓所有點的x坐標(或者y坐標)保持不變,而對應的y坐標(或者x坐標)則按比例發生平移,且平移的大小和該點到x軸(或y軸)的垂直距離成正比。錯切變換,屬於等面積變換,即一個形狀在錯切變換的前後,其面積是相等的。

比如下圖,各點的y坐標保持不變,但其x坐標則按比例發生了平移。這種情況將水平錯切。

 

下圖各點的x坐標保持不變,但其y坐標則按比例發生了平移。這種情況叫垂直錯切。

 

假定一個點經過錯切變換後得到,對於水平錯切而言,應該有如下關系:

用矩陣表示就是:

 

擴展到3 x 3的矩陣就是下面這樣的形式:

 

同理,對於垂直錯切,可以有:

在數學上嚴格的錯切變換就是上面這樣的。在Android中除了有上面說到的情況外,還可以同時進行水平、垂直錯切,那麼形式上就是:

 

五、 對稱變換

除了上面講到的4中基本變換外,事實上,我們還可以利用Matrix,進行對稱變換。所謂對稱變換,就是經過變化後的圖像和原圖像是關於某個對稱軸是對稱的。比如,某點 經過對稱變換後得到

如果對稱軸是x軸,難麼,

用矩陣表示就是:

如果對稱軸是y軸,那麼,

用矩陣表示就是:

如果對稱軸是y = x,如圖:

那麼,

很容易可以解得:

用矩陣表示就是:

同樣的道理,如果對稱軸是y = -x,那麼用矩陣表示就是:

 

特殊地,如果對稱軸是y = kx,如下圖:

那麼,

很容易可解得:

用矩陣表示就是:

當k = 0時,即y = 0,也就是對稱軸為x軸的情況;當k趨於無窮大時,即x = 0,也就是對稱軸為y軸的情況;當k =1時,即y = x,也就是對稱軸為y = x的情況;當k = -1時,即y = -x,也就是對稱軸為y = -x的情況。不難驗證,這和我們前面說到的4中具體情況是相吻合的。

如果對稱軸是y = kx + b這樣的情況,只需要在上面的基礎上增加兩次平移變換即可,即先將坐標原點移動到(0, b),然後做上面的關於y = kx的對稱變換,再然後將坐標原點移回到原來的坐標原點即可。用矩陣表示大致是這樣的:

需要特別注意:在實際編程中,我們知道屏幕的y坐標的正向和數學中y坐標的正向剛好是相反的,所以在數學上y = x和屏幕上的y = -x才是真正的同一個東西,反之亦然。也就是說,如果要使圖片在屏幕上看起來像按照數學意義上y = x對稱,那麼需使用這種轉換:

要使圖片在屏幕上看起來像按照數學意義上y = -x對稱,那麼需使用這種轉換:

 

關於對稱軸為y = kx 或y = kx + b的情況,同樣需要考慮這方面的問題。

第二部分 代碼驗證

在第一部分中講到的各種圖像變換的驗證代碼如下,一共列出了10種情況。如果要驗證其中的某一種情況,只需將相應的代碼反注釋即可。試驗中用到的圖片:

其尺寸為162 x 251。

每種變換的結果,請見代碼之後的說明。

package compattesttransformmatrix; 
import androidappActivity; 
import androidcontentContext; 
import androidgraphicsBitmap; 
import androidgraphicsBitmapFactory; 
import androidgraphicsCanvas; 
import androidgraphicsMatrix; 
import androidosBundle; 
import androidutilLog; 
import androidviewMotionEvent; 
import androidviewView; 
import androidviewWindow; 
import androidviewWindowManager; 
import androidviewViewOnTouchListener; 
import androidwidgetImageView; 
 
public class TestTransformMatrixActivity extends Activity 
implements 
OnTouchListener 
{ 
 private TransformMatrixView view; 
 @Override 
 public void onCreate(Bundle savedInstanceState) 
 { 
  superonCreate(savedInstanceState); 
  requestWindowFeature(WindowFEATURE_NO_TITLE); 
  thisgetWindow()setFlags(WindowManagerLayoutParamsFLAG_FULLSCREEN, WindowManagerLayoutParamsFLAG_FULLSCREEN); 
 
  view = new TransformMatrixView(this); 
  viewsetScaleType(ImageViewScaleTypeMATRIX); 
  viewsetOnTouchListener(this); 
   
  setContentView(view); 
 } 
  
 class TransformMatrixView extends ImageView 
 { 
  private Bitmap bitmap; 
  private Matrix matrix; 
  public TransformMatrixView(Context context) 
  { 
   super(context); 
   bitmap = BitmapFactorydecodeResource(getResources(), Rdrawablesophie); 
   matrix = new Matrix(); 
  } 
 
  @Override 
  protected void onDraw(Canvas canvas) 
  { 
   // 畫出原圖像 
   canvasdrawBitmap(bitmap, 0, 0, null); 
   // 畫出變換後的圖像 
   canvasdrawBitmap(bitmap, matrix, null); 
   superonDraw(canvas); 
  } 
 
  @Override 
  public void setImageMatrix(Matrix matrix) 
  { 
   thismatrixset(matrix); 
   supersetImageMatrix(matrix); 
  } 
   
  public Bitmap getImageBitmap() 
  { 
   return bitmap; 
  } 
 } 
 
 public boolean onTouch(View v, MotionEvent e) 
 { 
  if(egetAction() == MotionEventACTION_UP) 
  { 
   Matrix matrix = new Matrix(); 
   // 輸出圖像的寬度和高度(162 x 251) 
   Loge("TestTransformMatrixActivity", "image size: width x height = " + viewgetImageBitmap()getWidth() + " x " + viewgetImageBitmap()getHeight()); 
   // 平移 
   matrixpostTranslate(viewgetImageBitmap()getWidth(), viewgetImageBitmap()getHeight()); 
   // 在x方向平移viewgetImageBitmap()getWidth(),在y軸方向viewgetImageBitmap()getHeight() 
   viewsetImageMatrix(matrix); 
    
   // 下面的代碼是為了查看matrix中的元素 
   float[] matrixValues = new float[9]; 
   matrixgetValues(matrixValues); 
   for(int i = 0; i < 3; ++i) 
   { 
    String temp = new String(); 
    for(int j = 0; j < 3; ++j) 
    { 
     temp += matrixValues[3 * i + j ] + "\t"; 
    } 
    Loge("TestTransformMatrixActivity", temp); 
   } 
    
 
//   // 旋轉(圍繞圖像的中心點) 
//   matrixsetRotate(45f, viewgetImageBitmap()getWidth() / 2f, viewgetImageBitmap()getHeight() / 2f); 
//   
//   // 做下面的平移變換,純粹是為了讓變換後的圖像和原圖像不重疊 
//   matrixpostTranslate(viewgetImageBitmap()getWidth() * 5f, 0f); 
//   viewsetImageMatrix(matrix); 
// 
//   // 下面的代碼是為了查看matrix中的元素 
//   float[] matrixValues = new float[9]; 
//   matrixgetValues(matrixValues); 
//   for(int i = 0; i < 3; ++i) 
//   { 
//    String temp = new String(); 
//    for(int j = 0; j < 3; ++j) 
//    { 
//     temp += matrixValues[3 * i + j ] + "\t"; 
//    } 
//    Loge("TestTransformMatrixActivity", temp); 
//   } 
    
    
//   // 旋轉(圍繞坐標原點) + 平移(效果同2) 
//   matrixsetRotate(45f); 
//   matrixpreTranslate(-1f * viewgetImageBitmap()getWidth() / 2f, -1f * viewgetImageBitmap()getHeight() / 2f); 
//   matrixpostTranslate((float)viewgetImageBitmap()getWidth() / 2f, (float)viewgetImageBitmap()getHeight() / 2f); 
//   
//   // 做下面的平移變換,純粹是為了讓變換後的圖像和原圖像不重疊 
//   matrixpostTranslate((float)viewgetImageBitmap()getWidth() * 5f, 0f); 
//   viewsetImageMatrix(matrix); 
//   
//   // 下面的代碼是為了查看matrix中的元素 
//   float[] matrixValues = new float[9]; 
//   matrixgetValues(matrixValues); 
//   for(int i = 0; i < 3; ++i) 
//   { 
//    String temp = new String(); 
//    for(int j = 0; j < 3; ++j) 
//    { 
//     temp += matrixValues[3 * i + j ] + "\t"; 
//    } 
//    Loge("TestTransformMatrixActivity", temp); 
//   }    
    
//   // 縮放 
//   matrixsetScale(2f, 2f); 
//   // 下面的代碼是為了查看matrix中的元素 
//   float[] matrixValues = new float[9]; 
//   matrixgetValues(matrixValues); 
//   for(int i = 0; i < 3; ++i) 
//   { 
//    String temp = new String(); 
//    for(int j = 0; j < 3; ++j) 
//    { 
//     temp += matrixValues[3 * i + j ] + "\t"; 
//    } 
//    Loge("TestTransformMatrixActivity", temp); 
//   } 
//   
//   // 做下面的平移變換,純粹是為了讓變換後的圖像和原圖像不重疊 
//   matrixpostTranslate(viewgetImageBitmap()getWidth(), viewgetImageBitmap()getHeight()); 
//   viewsetImageMatrix(matrix); 
//   
//   // 下面的代碼是為了查看matrix中的元素 
//   matrixValues = new float[9]; 
//   matrixgetValues(matrixValues); 
//   for(int i = 0; i < 3; ++i) 
//   { 
//    String temp = new String(); 
//    for(int j = 0; j < 3; ++j) 
//    { 
//     temp += matrixValues[3 * i + j ] + "\t"; 
//    } 
//    Loge("TestTransformMatrixActivity", temp); 
//   } 
 
    
//   // 錯切 - 水平 
//   matrixsetSkew(5f, 0f); 
//   // 下面的代碼是為了查看matrix中的元素 
//   float[] matrixValues = new float[9]; 
//   matrixgetValues(matrixValues); 
//   for(int i = 0; i < 3; ++i) 
//   { 
//    String temp = new String(); 
//    for(int j = 0; j < 3; ++j) 
//    { 
//     temp += matrixValues[3 * i + j ] + "\t"; 
//    } 
//    Loge("TestTransformMatrixActivity", temp); 
//   } 
//   
//   // 做下面的平移變換,純粹是為了讓變換後的圖像和原圖像不重疊   
//   matrixpostTranslate(viewgetImageBitmap()getWidth(), 0f); 
//   viewsetImageMatrix(matrix); 
//   
//   // 下面的代碼是為了查看matrix中的元素 
//   matrixValues = new float[9]; 
//   matrixgetValues(matrixValues); 
//   for(int i = 0; i < 3; ++i) 
//   { 
//    String temp = new String(); 
//    for(int j = 0; j < 3; ++j) 
//    { 
//     temp += matrixValues[3 * i + j ] + "\t"; 
//    } 
//    Loge("TestTransformMatrixActivity", temp); 
//   } 
    
//   // 錯切 - 垂直 
//   matrixsetSkew(0f, 5f); 
//   // 下面的代碼是為了查看matrix中的元素 
//   float[] matrixValues = new float[9]; 
//   matrixgetValues(matrixValues); 
//   for(int i = 0; i < 3; ++i) 
//   { 
//    String temp = new String(); 
//    for(int j = 0; j < 3; ++j) 
//    { 
//     temp += matrixValues[3 * i + j ] + "\t"; 
//    } 
//    Loge("TestTransformMatrixActivity", temp); 
//   } 
//   
//   // 做下面的平移變換,純粹是為了讓變換後的圖像和原圖像不重疊    
//   matrixpostTranslate(0f, viewgetImageBitmap()getHeight()); 
//   viewsetImageMatrix(matrix); 
//   
//   // 下面的代碼是為了查看matrix中的元素 
//   matrixValues = new float[9]; 
//   matrixgetValues(matrixValues); 
//   for(int i = 0; i < 3; ++i) 
//   { 
//    String temp = new String(); 
//    for(int j = 0; j < 3; ++j) 
//    { 
//     temp += matrixValues[3 * i + j ] + "\t"; 
//    } 
//    Loge("TestTransformMatrixActivity", temp); 
//   }    
    
//   錯切 - 水平 + 垂直 
//   matrixsetSkew(5f, 5f); 
//   // 下面的代碼是為了查看matrix中的元素 
//   float[] matrixValues = new float[9]; 
//   matrixgetValues(matrixValues); 
//   for(int i = 0; i < 3; ++i) 
//   { 
//    String temp = new String(); 
//    for(int j = 0; j < 3; ++j) 
//    { 
//     temp += matrixValues[3 * i + j ] + "\t"; 
//    } 
//    Loge("TestTransformMatrixActivity", temp); 
//   } 
//   
//   // 做下面的平移變換,純粹是為了讓變換後的圖像和原圖像不重疊    
//   matrixpostTranslate(0f, viewgetImageBitmap()getHeight()); 
//   viewsetImageMatrix(matrix); 
//   
//   // 下面的代碼是為了查看matrix中的元素 
//   matrixValues = new float[9]; 
//   matrixgetValues(matrixValues); 
//   for(int i = 0; i < 3; ++i) 
//   { 
//    String temp = new String(); 
//    for(int j = 0; j < 3; ++j) 
//    { 
//     temp += matrixValues[3 * i + j ] + "\t"; 
//    } 
//    Loge("TestTransformMatrixActivity", temp); 
//   } 
    
//   // 對稱 (水平對稱) 
//   float matrix_values[] = {1f, 0f, 0f, 0f, -1f, 0f, 0f, 0f, 1f}; 
//   matrixsetValues(matrix_values); 
//   // 下面的代碼是為了查看matrix中的元素 
//   float[] matrixValues = new float[9]; 
//   matrixgetValues(matrixValues); 
//   for(int i = 0; i < 3; ++i) 
//   { 
//    String temp = new String(); 
//    for(int j = 0; j < 3; ++j) 
//    { 
//     temp += matrixValues[3 * i + j ] + "\t"; 
//    } 
//    Loge("TestTransformMatrixActivity", temp); 
//   } 
//   
//   // 做下面的平移變換,純粹是為了讓變換後的圖像和原圖像不重疊 
//   matrixpostTranslate(0f, viewgetImageBitmap()getHeight() * 2f); 
//   viewsetImageMatrix(matrix); 
//   
//   // 下面的代碼是為了查看matrix中的元素 
//   matrixValues = new float[9]; 
//   matrixgetValues(matrixValues); 
//   for(int i = 0; i < 3; ++i) 
//   { 
//    String temp = new String(); 
//    for(int j = 0; j < 3; ++j) 
//    { 
//     temp += matrixValues[3 * i + j ] + "\t"; 
//    } 
//    Loge("TestTransformMatrixActivity", temp); 
//   }    
    
//   // 對稱 - 垂直 
//   float matrix_values[] = {-1f, 0f, 0f, 0f, 1f, 0f, 0f, 0f, 1f}; 
//   matrixsetValues(matrix_values); 
//   // 下面的代碼是為了查看matrix中的元素 
//   float[] matrixValues = new float[9]; 
//   matrixgetValues(matrixValues); 
//   for(int i = 0; i < 3; ++i) 
//   { 
//    String temp = new String(); 
//    for(int j = 0; j < 3; ++j) 
//    { 
//     temp += matrixValues[3 * i + j ] + "\t"; 
//    } 
//    Loge("TestTransformMatrixActivity", temp); 
//   }  
//   
//   // 做下面的平移變換,純粹是為了讓變換後的圖像和原圖像不重疊 
//   matrixpostTranslate(viewgetImageBitmap()getWidth() * 2f, 0f); 
//   viewsetImageMatrix(matrix); 
//   
//   // 下面的代碼是為了查看matrix中的元素 
//   matrixValues = new float[9]; 
//   matrixgetValues(matrixValues); 
//   for(int i = 0; i < 3; ++i) 
//   { 
//    String temp = new String(); 
//    for(int j = 0; j < 3; ++j) 
//    { 
//     temp += matrixValues[3 * i + j ] + "\t"; 
//    } 
//    Loge("TestTransformMatrixActivity", temp); 
//   } 
 
    
//   // 對稱(對稱軸為直線y = x) 
//   float matrix_values[] = {0f, -1f, 0f, -1f, 0f, 0f, 0f, 0f, 1f}; 
//   matrixsetValues(matrix_values); 
//   // 下面的代碼是為了查看matrix中的元素 
//   float[] matrixValues = new float[9]; 
//   matrixgetValues(matrixValues); 
//   for(int i = 0; i < 3; ++i) 
//   { 
//    String temp = new String(); 
//    for(int j = 0; j < 3; ++j) 
//    { 
//     temp += matrixValues[3 * i + j ] + "\t"; 
//    } 
//    Loge("TestTransformMatrixActivity", temp); 
//   } 
//   
//   // 做下面的平移變換,純粹是為了讓變換後的圖像和原圖像不重疊    
//   matrixpostTranslate(viewgetImageBitmap()getHeight() + viewgetImageBitmap()getWidth(), 
//     viewgetImageBitmap()getHeight() + viewgetImageBitmap()getWidth()); 
//   viewsetImageMatrix(matrix); 
//   
//   // 下面的代碼是為了查看matrix中的元素 
//   matrixValues = new float[9]; 
//   matrixgetValues(matrixValues); 
//   for(int i = 0; i < 3; ++i) 
//   { 
//    String temp = new String(); 
//    for(int j = 0; j < 3; ++j) 
//    { 
//     temp += matrixValues[3 * i + j ] + "\t"; 
//    } 
//    Loge("TestTransformMatrixActivity", temp); 
//   } 
    
   viewinvalidate(); 
  } 
  return true; 
 } 
} 

下面給出上述代碼中,各種變換的具體結果及其對應的相關變換矩陣

1.     平移

輸出的結果:

請對照第一部分中的“一、平移變換”所講的情形,考察上述矩陣的正確性。

 2.     旋轉(圍繞圖像的中心點)

輸出的結果:

它實際上是

matrix.setRotate(45f,view.getImageBitmap().getWidth() / 2f, view.getImageBitmap().getHeight() / 2f);

matrix.postTranslate(view.getImageBitmap().getWidth()* 1.5f, 0f);

這兩條語句綜合作用的結果。根據第一部分中“二、旋轉變換”裡面關於圍繞某點旋轉的公式,

matrix.setRotate(45f,view.getImageBitmap().getWidth() / 2f, view.getImageBitmap().getHeight() / 2f);

所產生的轉換矩陣就是:

而matrix.postTranslate(view.getImageBitmap().getWidth()* 1.5f, 0f);的意思就是在上述矩陣的左邊再乘以下面的矩陣:

關於post是左乘這一點,我們在前面的理論部分曾經提及過,後面我們還會專門討論這個問題。

所以它實際上就是:

出去計算上的精度誤差,我們可以看到我們計算出來的結果,和程序直接輸出的結果是一致的。

 3.     旋轉(圍繞坐標原點旋轉,在加上兩次平移,效果同2)

根據第一部分中“二、旋轉變換”裡面關於圍繞某點旋轉的解釋,不難知道:

matrix.setRotate(45f,view.getImageBitmap().getWidth() / 2f, view.getImageBitmap().getHeight() / 2f);

等價於

matrix.setRotate(45f);

matrix.preTranslate(-1f* view.getImageBitmap().getWidth() / 2f, -1f *view.getImageBitmap().getHeight() / 2f);

matrix.postTranslate((float)view.getImageBitmap().getWidth()/ 2f, (float)view.getImageBitmap().getHeight() / 2f);

其中matrix.setRotate(45f)對應的矩陣是:

matrix.preTranslate(-1f* view.getImageBitmap().getWidth() / 2f, -1f * view.getImageBitmap().getHeight()/ 2f)對應的矩陣是:

由於是preTranslate,是先乘,也就是右乘,即它應該出現在matrix.setRotate(45f)所對應矩陣的右側。

 matrix.postTranslate((float)view.getImageBitmap().getWidth()/ 2f, (float)view.getImageBitmap().getHeight() / 2f)對應的矩陣是:

這次由於是postTranslate,是後乘,也就是左乘,即它應該出現在matrix.setRotate(45f)所對應矩陣的左側。

 所以綜合起來,

matrix.setRotate(45f);

matrix.preTranslate(-1f* view.getImageBitmap().getWidth() / 2f, -1f *view.getImageBitmap().getHeight() / 2f);

matrix.postTranslate((float)view.getImageBitmap().getWidth()/ 2f, (float)view.getImageBitmap().getHeight() / 2f);

對應的矩陣就是:

這和下面這個矩陣(圍繞圖像中心順時針旋轉45度)其實是一樣的:

因此,此處變換後的圖像和2中變換後的圖像時一樣的。

 4.     縮放變換

程序所輸出的兩個矩陣分別是:

其中第二個矩陣,其實是下面兩個矩陣相乘的結果:

 

大家可以對照第一部分中的“三、縮放變換”和“一、平移變換”說法,自行驗證結果。

 5.     錯切變換(水平錯切)

代碼所輸出的兩個矩陣分別是:

其中,第二個矩陣其實是下面兩個矩陣相乘的結果:

 

大家可以對照第一部分中的“四、錯切變換”和“一、平移變換”的相關說法,自行驗證結果。

 6.     錯切變換(垂直錯切)

代碼所輸出的兩個矩陣分別是:

其中,第二個矩陣其實是下面兩個矩陣相乘的結果:

大家可以對照第一部分中的“四、錯切變換”和“一、平移變換”的相關說法,自行驗證結果。

7.     錯切變換(水平+垂直錯切)

代碼所輸出的兩個矩陣分別是:

其中,後者是下面兩個矩陣相乘的結果:

大家可以對照第一部分中的“四、錯切變換”和“一、平移變換”的相關說法,自行驗證結果。

8.     對稱變換(水平對稱)

代碼所輸出的兩個各矩陣分別是:

其中,後者是下面兩個矩陣相乘的結果:

 

大家可以對照第一部分中的“五、對稱變換”和“一、平移變換”的相關說法,自行驗證結果。

9.     對稱變換(垂直對稱)

代碼所輸出的兩個矩陣分別是:

其中,後者是下面兩個矩陣相乘的結果:

大家可以對照第一部分中的“五、對稱變換”和“一、平移變換”的相關說法,自行驗證結果。

10.   對稱變換(對稱軸為直線y = x)

代碼所輸出的兩個矩陣分別是:

其中,後者是下面兩個矩陣相乘的結果:

 

大家可以對照第一部分中的“五、對稱變換”和“一、平移變換”的相關說法,自行驗證結果。

 11.   關於先乘和後乘的問題

由於矩陣的乘法運算不滿足交換律,我們在前面曾經多次提及先乘、後乘的問題,即先乘就是矩陣運算中右乘,後乘就是矩陣運算中的左乘。其實先乘、後乘的概念是針對變換操作的時間先後而言的,左乘、右乘是針對矩陣運算的左右位置而言的。以第一部分“二、旋轉變換”中圍繞某點旋轉的情況為例:

 

越靠近原圖像中像素的矩陣,越先乘,越遠離原圖像中像素的矩陣,越後乘。事實上,圖像處理時,矩陣的運算是從右邊往左邊方向進行運算的。這就形成了越在右邊的矩陣(右乘),越先運算(先乘),反之亦然。

 當然,在實際中,如果首先指定了一個matrix,比如我們先setRotate(),即指定了上面變換矩陣中,中間的那個矩陣,那麼後續的矩陣到底是pre還是post運算,都是相對這個中間矩陣而言的。

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持本站。

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved