注:本篇文章爲轉載文章,因爲原文格式排版較亂,但是內容非常棒,所以整理一下,方便以後查看。
查看原文請戳:http://blog.csdn.net/pathuang68/article/details/6991988
Matrix介紹文章請戳:http://blog.csdn.net/pathuang68/article/details/6991867
package com.pat.testtransformmatrix;
import android.app.Activity;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.os.Bundle;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.view.Window;
import android.view.WindowManager;
import android.view.View.OnTouchListener;
import android.widget.ImageView;
public class TestTransformMatrixActivity extends Activity implements OnTouchListener
{
private TransformMatrixView view;
@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN);
view = new TransformMatrixView(this);
view.setScaleType(ImageView.ScaleType.MATRIX);
view.setOnTouchListener(this);
setContentView(view);
}
class TransformMatrixView extends ImageView
{
private Bitmap bitmap;
private Matrix matrix;
public TransformMatrixView(Context context){
super(context);
bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.sophie);
matrix = new Matrix();
}
@Override
protected void onDraw(Canvas canvas){
// 畫出原圖像
canvas.drawBitmap(bitmap, 0, 0, null);
// 畫出變換後的圖像
canvas.drawBitmap(bitmap, matrix, null);
super.onDraw(canvas);
}
@Override
public void setImageMatrix(Matrix matrix){
this.matrix.set(matrix);
super.setImageMatrix(matrix);
}
public Bitmap getImageBitmap(){
return bitmap;
}
}
public boolean onTouch(View v, MotionEvent e){
if(e.getAction() == MotionEvent.ACTION_UP){
Matrix matrix = new Matrix();
// 輸出圖像的寬度和高度(162 x 251)
Log.e("TestTransformMatrixActivity", "image size: width x height = " + view.getImageBitmap().getWidth() + " x " + view.getImageBitmap().getHeight());
// 1. 平移
matrix.postTranslate(view.getImageBitmap().getWidth(), view.getImageBitmap().getHeight());
// 在x方向平移view.getImageBitmap().getWidth(),在y軸方向view.getImageBitmap().getHeight()
view.setImageMatrix(matrix);
// 下面的代碼是爲了查看matrix中的元素
float[] matrixValues = new float[9];
matrix.getValues(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";
}
Log.e("TestTransformMatrixActivity", temp);
}
// // 2. 旋轉(圍繞圖像的中心點)
// matrix.setRotate(45f, view.getImageBitmap().getWidth() / 2f, view.getImageBitmap().getHeight() / 2f);
// // 做下面的平移變換,純粹是爲了讓變換後的圖像和原圖像不重疊
// matrix.postTranslate(view.getImageBitmap().getWidth() * 1.5f, 0f);
// view.setImageMatrix(matrix);
// // 3. 旋轉(圍繞座標原點) + 平移(效果同2)
// 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.postTranslate((float)view.getImageBitmap().getWidth() * 1.5f, 0f);
// view.setImageMatrix(matrix);
// // 4. 縮放
// matrix.setScale(2f, 2f);
// // 做下面的平移變換,純粹是爲了讓變換後的圖像和原圖像不重疊
// matrix.postTranslate(view.getImageBitmap().getWidth(), view.getImageBitmap().getHeight());
// view.setImageMatrix(matrix);
// // 5. 錯切 - 水平
// matrix.setSkew(0.5f, 0f);
// // 做下面的平移變換,純粹是爲了讓變換後的圖像和原圖像不重疊
// matrix.postTranslate(view.getImageBitmap().getWidth(), 0f);
// view.setImageMatrix(matrix);
// // 6. 錯切 - 垂直
// matrix.setSkew(0f, 0.5f);
// // 做下面的平移變換,純粹是爲了讓變換後的圖像和原圖像不重疊
// matrix.postTranslate(0f, view.getImageBitmap().getHeight());
// view.setImageMatrix(matrix);
// 7. 錯切 - 水平 + 垂直
// matrix.setSkew(0.5f, 0.5f);
// // 做下面的平移變換,純粹是爲了讓變換後的圖像和原圖像不重疊
// matrix.postTranslate(0f, view.getImageBitmap().getHeight());
// view.setImageMatrix(matrix);
// // 8. 對稱 (水平對稱)
// float matrix_values[] = {1f, 0f, 0f, 0f, -1f, 0f, 0f, 0f, 1f};
// matrix.setValues(matrix_values);
// // 做下面的平移變換,純粹是爲了讓變換後的圖像和原圖像不重疊
// matrix.postTranslate(0f, view.getImageBitmap().getHeight() * 2f);
// view.setImageMatrix(matrix);
// // 9. 對稱 - 垂直
// float matrix_values[] = {-1f, 0f, 0f, 0f, 1f, 0f, 0f, 0f, 1f};
// matrix.setValues(matrix_values);
// // 做下面的平移變換,純粹是爲了讓變換後的圖像和原圖像不重疊
// matrix.postTranslate(view.getImageBitmap().getWidth() * 2f, 0f);
// view.setImageMatrix(matrix);
// // 10. 對稱(對稱軸爲直線y = x)
// float matrix_values[] = {0f, -1f, 0f, -1f, 0f, 0f, 0f, 0f, 1f};
// matrix.setValues(matrix_values);
// // 做下面的平移變換,純粹是爲了讓變換後的圖像和原圖像不重疊
// matrix.postTranslate(view.getImageBitmap().getHeight() + view.getImageBitmap().getWidth(),
// view.getImageBitmap().getHeight() + view.getImageBitmap().getWidth());
// view.setImageMatrix(matrix);
view.invalidate();
}
return true;
}
}
下面給出上述代碼中,各種變換的具體結果及其對應的相關變換矩陣
- 平移
輸出的結果:
請對照第一部分中的“一、平移變換”所講的情形,考察上述矩陣的正確性。
- 旋轉(圍繞圖像的中心點)
輸出的結果:
它實際上是
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是左乘這一點,我們在前面的理論部分曾經提及過,後面我們還會專門討論這個問題。
所以它實際上就是:
出去計算上的精度誤差,我們可以看到我們計算出來的結果,和程序直接輸出的結果是一致的。
- 旋轉(圍繞座標原點旋轉,在加上兩次平移,效果同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中變換後的圖像時一樣的。
- 縮放變換
程序所輸出的兩個矩陣分別是:
其中第二個矩陣,其實是下面兩個矩陣相乘的結果:
大家可以對照第一部分中的“三、縮放變換”和“一、平移變換”說法,自行驗證結果。
- 錯切變換(水平錯切)
代碼所輸出的兩個矩陣分別是:
其中,第二個矩陣其實是下面兩個矩陣相乘的結果:
大家可以對照第一部分中的“四、錯切變換”和“一、平移變換”的相關說法,自行驗證結果。
- 錯切變換(垂直錯切)
代碼所輸出的兩個矩陣分別是:
其中,第二個矩陣其實是下面兩個矩陣相乘的結果:
大家可以對照第一部分中的“四、錯切變換”和“一、平移變換”的相關說法,自行驗證結果。
- 錯切變換(水平+垂直錯切)
代碼所輸出的兩個矩陣分別是:
其中,後者是下面兩個矩陣相乘的結果:
大家可以對照第一部分中的“四、錯切變換”和“一、平移變換”的相關說法,自行驗證結果。
- 對稱變換(水平對稱)
代碼所輸出的兩個各矩陣分別是:
其中,後者是下面兩個矩陣相乘的結果:
大家可以對照第一部分中的“五、對稱變換”和“一、平移變換”的相關說法,自行驗證結果。
- 對稱變換(垂直對稱)
代碼所輸出的兩個矩陣分別是:
其中,後者是下面兩個矩陣相乘的結果:
大家可以對照第一部分中的“五、對稱變換”和“一、平移變換”的相關說法,自行驗證結果。
- 對稱變換(對稱軸爲直線)
代碼所輸出的兩個矩陣分別是:
其中,後者是下面兩個矩陣相乘的結果:
大家可以對照第一部分中的“五、對稱變換”和“一、平移變換”的相關說法,自行驗證結果。
- 關於先乘和後乘的問題
由於矩陣的乘法運算不滿足交換律,我們在前面曾經多次提及先乘、後乘的問題,即先乘就是矩陣運算中右乘,後乘就是矩陣運算中的左乘。其實先乘、後乘的概念是針對變換操作的時間先後而言的,左乘、右乘是針對矩陣運算的左右位置而言的。以第一部分“二、旋轉變換”中圍繞某點旋轉的情況爲例:
越靠近原圖像中像素的矩陣,越先乘,越遠離原圖像中像素的矩陣,越後乘。事實上,圖像處理時,矩陣的運算是從右邊往左邊方向進行運算的。這就形成了越在右邊的矩陣(右乘),越先運算(先乘),反之亦然。
當然,在實際中,如果首先指定了一個matrix,比如我們先setRotate(),即指定了上面變換矩陣中,中間的那個矩陣,那麼後續的矩陣到底是pre還是post運算,都是相對這個中間矩陣而言的。
所有這些,其實都是很自然的事情。
再次感謝原博主的文章