Android中圖像變換Matrix的原理、代碼驗證和應用
https://blog.csdn.net/pathuang68/article/details/6991988
第一部分 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對稱,那麼需使用這種轉換:
第二部分 代碼驗證
在第一部分中講到的各種圖像變換的驗證代碼如下,一共列出了10種情況。如果要驗證其中的某一種情況,隻需將相應的代碼反注釋即可。試驗中用到的圖片:
其尺寸為162 x 251。
每種變換的結果,請見代碼之後的說明。
- <span style="font-size:13px;"></span><pre name="code" class="java">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);
- this.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);
- //
- // // 下麵的代碼是為了查看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);
- // }
- // // 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);
- //
- // // 下麵的代碼是為了查看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);
- // }
- // // 4. 縮放
- // matrix.setScale(2f, 2f);
- // // 下麵的代碼是為了查看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);
- // }
- //
- // // 做下麵的平移變換,純粹是為了讓變換後的圖像和原圖像不重疊
- // matrix.postTranslate(view.getImageBitmap().getWidth(), view.getImageBitmap().getHeight());
- // view.setImageMatrix(matrix);
- //
- // // 下麵的代碼是為了查看matrix中的元素
- // 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);
- // }
- // // 5. 錯切 - 水平
- // matrix.setSkew(0.5f, 0f);
- // // 下麵的代碼是為了查看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);
- // }
- //
- // // 做下麵的平移變換,純粹是為了讓變換後的圖像和原圖像不重疊
- // matrix.postTranslate(view.getImageBitmap().getWidth(), 0f);
- // view.setImageMatrix(matrix);
- //
- // // 下麵的代碼是為了查看matrix中的元素
- // 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);
- // }
- // // 6. 錯切 - 垂直
- // matrix.setSkew(0f, 0.5f);
- // // 下麵的代碼是為了查看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);
- // }
- //
- // // 做下麵的平移變換,純粹是為了讓變換後的圖像和原圖像不重疊
- // matrix.postTranslate(0f, view.getImageBitmap().getHeight());
- // view.setImageMatrix(matrix);
- //
- // // 下麵的代碼是為了查看matrix中的元素
- // 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);
- // }
- // 7. 錯切 - 水平 + 垂直
- // matrix.setSkew(0.5f, 0.5f);
- // // 下麵的代碼是為了查看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);
- // }
- //
- // // 做下麵的平移變換,純粹是為了讓變換後的圖像和原圖像不重疊
- // matrix.postTranslate(0f, view.getImageBitmap().getHeight());
- // view.setImageMatrix(matrix);
- //
- // // 下麵的代碼是為了查看matrix中的元素
- // 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);
- // }
- // // 8. 對稱 (水平對稱)
- // float matrix_values[] = {1f, 0f, 0f, 0f, -1f, 0f, 0f, 0f, 1f};
- // matrix.setValues(matrix_values);
- // // 下麵的代碼是為了查看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);
- // }
- //
- // // 做下麵的平移變換,純粹是為了讓變換後的圖像和原圖像不重疊
- // matrix.postTranslate(0f, view.getImageBitmap().getHeight() * 2f);
- // view.setImageMatrix(matrix);
- //
- // // 下麵的代碼是為了查看matrix中的元素
- // 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);
- // }
- // // 9. 對稱 - 垂直
- // float matrix_values[] = {-1f, 0f, 0f, 0f, 1f, 0f, 0f, 0f, 1f};
- // matrix.setValues(matrix_values);
- // // 下麵的代碼是為了查看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);
- // }
- //
- // // 做下麵的平移變換,純粹是為了讓變換後的圖像和原圖像不重疊
- // matrix.postTranslate(view.getImageBitmap().getWidth() * 2f, 0f);
- // view.setImageMatrix(matrix);
- //
- // // 下麵的代碼是為了查看matrix中的元素
- // 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);
- // }
- // // 10. 對稱(對稱軸為直線y = x)
- // float matrix_values[] = {0f, -1f, 0f, -1f, 0f, 0f, 0f, 0f, 1f};
- // matrix.setValues(matrix_values);
- // // 下麵的代碼是為了查看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);
- // }
- //
- // // 做下麵的平移變換,純粹是為了讓變換後的圖像和原圖像不重疊
- // matrix.postTranslate(view.getImageBitmap().getHeight() + view.getImageBitmap().getWidth(),
- // view.getImageBitmap().getHeight() + view.getImageBitmap().getWidth());
- // view.setImageMatrix(matrix);
- //
- // // 下麵的代碼是為了查看matrix中的元素
- // 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);
- // }
- view.invalidate();
- }
- 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. 關於先乘和後乘的問題
由於矩陣的乘法運算不滿足交換律,我們在前麵曾經多次提及先乘、後乘的問題,即先乘就是矩陣運算中右乘,後乘就是矩陣運算中的左乘。其實先乘、後乘的概念是針對變換操作的時間先後而言的,左乘、右乘是針對矩陣運算的左右位置而言的。以第一部分“二、旋轉變換”中圍繞某點旋轉的情況為例:
越靠近原圖像中像素的矩陣,越先乘,越遠離原圖像中像素的矩陣,越後乘。事實上,圖像處 最後更新:2017-04-02 17:51:24 上一篇:
android在界麵上使用URI編程 ----開發筆記收集
下一篇:
國航OA項目技術總結(二)關於JVM虛擬機中對象的創建,手機端提速的重要優化
相關內容
現代汽車揭秘研發進展:ADAS、自動駕駛、V2X和未來出行的現狀和創新
阿裏雲麵向企業效率的雲上產品全解析——雲投屏 雲上會議,省時高效
開發者論壇一周精粹(第十一期):阿裏怎麼發工資?自研薪酬管理係統首次曝光
《Jersey用戶指南》–序言
達摩院成立,雲棲大會概念股橫空出世!
CameraLink通信接口的一般定義
【Kafka源碼】Kafka代碼模塊
基於阿裏雲MaxCompute實現複雜事件檢測
穀歌搜索成功秘訣:以簡潔贏得用戶
magento -- 推薦插件 -- 智能版Js和Css合並 --Lesti::Merge(Smart Merging of Js and Css)