zoukankan      html  css  js  c++  java
  • 利用矩阵来变换图片

              在上一个主题中,学习了android图像的颜色处理。没读过的朋友可以点击一下链接学习:

    http://i.cnblogs.com/EditPosts.aspx?catid=744685

          那么在从这一篇文章开始,继续学习android的图像处理知识之图像的变换。比如说图像的缩放,拉伸,平移,旋转,以及高级点的图像的渐变和最有用的像素点变换方法。在这里进行一一的学习。这些知识也是我在学习后,总结在这里的,毕竟知识是积累起来的,每天积累一点学习一点,就会进步的很快。

          好了,废话不多说。在这篇文章中,将学习最基本的图像变换知识,即图像的缩放,拉伸等。而其中的原理就是利用矩阵来实现这些效果。我们还是按照老规矩,先学习一下基础的知识,然后再进行实战的代码编写。

    一、必须要知道的基础知识

          下面有一张图片,完美的解释了矩阵变换的知识点。如下:

          如上图所示,矩阵A就是我们需要设置的矩阵,而C代表着图像上每一个像素点的位置(X为横坐标,Y为纵坐标),R就是A*C得来的新的坐标位置。这样子通过这样子的矩阵运算,我们就把一张图片的每一个像素点的坐标都做了改变,因此就会呈现出不同的效果。而这就是矩阵变换实现缩放,拉伸等效果的基本原理。我们还需要知道的是,矩阵A的某些元素是会控制一些特殊的效果的,下图就是一个很好的总结:

         那么怎么根据矩阵画出效果呢?对应于android中的API,其实就是利用了Canvas的一个方法,如下:

    //画出根据矩阵的变换后的图像
    canvas.drawBitmap(bmp, matrix, null);

         其中bmp对应于一个Bitmap图片,而matrix是一个Matrix对象。那么下面的问题就是我们要怎么来设置这个矩阵了,也就是A。设定A有两种方式。

         第一种,直接赋值

         很容易知道矩阵A其实是3*3的矩阵,有9个数值。因此,我们只需要给Matrix的setValues方法传入一个长度为9的数值数组即可。

        第二种,利用缩放,旋转等封装好的api

        如果你觉得第一种方式太麻烦,不利于控制,没关系。对于常见的缩放,旋转等效果,android已经给你封装好了API,只要你调用相关方法,就可以自动设置好矩阵A。方法很多,举出一些如下:

    Matrix matrix=new Matrix();
    
    matrix.setTranslate(100, 50);//水平方向平移量为100,垂直方向平移量为50
    
    matrix.setRotate(30, 20, 30);//旋转30度,其中旋转围绕点(20,30)进行,默认为(0,0)
    
    matrix.setScale(1, 2);//缩放,水平方向放大1倍,垂直方向放大2倍
    
    matrix.setScale(1, 2,20,30);//依然是缩放,但是以点(20,30)为基准进行缩放
    
    matrix.setSkew(5,10);//将图片水平方向倾斜3,垂直方向倾斜10
    
    matrix.setSkew(5,10,24,24);//同样的道理,为倾斜找个基准点(24,24)
    
    canvas.drawBitmap(bitmap,matrix, paint);

          好了,基础的知识你差不多都知道了。下面我们赶紧编写一些实际的代码来试试吧。

    二、实战

          首先新建android项目“图形变换”。编写activity_main.xml代码,如下:

     1 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
     2     xmlns:tools="http://schemas.android.com/tools"
     3     android:layout_width="match_parent"
     4     android:layout_height="match_parent"
     5     android:orientation="vertical" 
     6     android:gravity="center">
     7 
     8     <TextView
     9         android:layout_width="match_parent"
    10         android:layout_height="wrap_content"
    11         android:textSize="25sp"
    12         android:gravity="center"
    13         android:background="#cc00ff"
    14         android:text="图像图形变换" />
    15     <Button 
    16         android:id="@+id/btn1"
    17         android:layout_width="match_parent"
    18         android:layout_height="wrap_content"
    19         android:onClick="btnMatrix"
    20         android:textSize="25sp"
    21         android:text="矩阵变换"/>
    22     <Button 
    23         android:id="@+id/btn2"
    24         android:layout_width="match_parent"
    25         android:layout_height="wrap_content"
    26         android:onClick="btnXFermode"
    27         android:textSize="25sp"
    28         android:text="画笔风格"/>
    29      <Button 
    30         android:id="@+id/btn3"
    31         android:layout_width="match_parent"
    32         android:layout_height="wrap_content"
    33         android:onClick="btnShader"
    34         android:textSize="25sp"
    35         android:text="图片渲染实验"/>
    36      <Button 
    37         android:id="@+id/btn4"
    38         android:layout_width="match_parent"
    39         android:layout_height="wrap_content"
    40         android:onClick="btnLShader"
    41         android:textSize="25sp"
    42         android:text="线性渲染实验"/>
    43       <Button 
    44         android:id="@+id/btn5"
    45         android:layout_width="match_parent"
    46         android:layout_height="wrap_content"
    47         android:onClick="btnMesh"
    48         android:textSize="25sp"
    49         android:text="像素块实验"/>
    50      
    51 
    52 </LinearLayout>

          怎么那么多按钮啊。嘿嘿,这说明我们还有好多知识要学习要实验呢,下面的一系列文章会一一实现这些按钮功能的。不用着急,我们这篇文章先来实现第一个按钮“矩阵变换”的效果,就是我们几天所学的内容。

         然后需要一个自定义的view来画出原来的图片和经过矩阵变换后的图片,这样有比较才有感觉。新建类MyMatrixView继承自View,代码如下:

     1 package com.fuly.image;
     2 
     3 import android.content.Context;
     4 import android.graphics.Bitmap;
     5 import android.graphics.BitmapFactory;
     6 import android.graphics.Canvas;
     7 import android.graphics.Matrix;
     8 import android.util.AttributeSet;
     9 import android.view.View;
    10 
    11 public class MyMatrixView extends View{
    12 
    13     private Bitmap bmp;
    14     private Matrix matrix;//颜色矩阵
    15     
    16     public MyMatrixView(Context context) {
    17         super(context);
    18         initView();
    19     }
    20     public MyMatrixView(Context context, AttributeSet attrs) {
    21         super(context, attrs);
    22         initView();
    23     }
    24     public MyMatrixView(Context context, AttributeSet attrs, int defStyleAttr) {
    25         super(context, attrs, defStyleAttr);
    26         initView();
    27     }
    28     
    29     private void initView(){//初始化方法
    30         bmp = BitmapFactory.decodeResource(getResources(), R.drawable.hudie2);
    31         setMatrix(new Matrix());//刚开始给个空矩阵即可
    32     }
    33 
    34     /**
    35      * 该方法用来设置颜色矩阵
    36      * @param matrix  颜色矩阵
    37      */
    38     public void setMatrix(Matrix matrix){
    39         this.matrix = matrix;
    40     }
    41     
    42 
    43     protected void onDraw(Canvas canvas) {
    44         super.onDraw(canvas);
    45         //画出原来的图像
    46         canvas.drawBitmap(bmp, 0, 0, null);
    47         //画出根据矩阵的变换后的图像
    48         canvas.drawBitmap(bmp, matrix, null);
    49     }
    50 
    51 }

          在代码中,我们首先将两张相同的图片画在了同一个地方。这样子,当其中的一张图片旋转或者拉伸,就很好的得到对比效果了。所用的图片资源,读者可自行替换为自己的一张小图片。下面就是新建imagematrix.xml文件,将这个自定义的view放进去。代码如下:

     1 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
     2     xmlns:tools="http://schemas.android.com/tools"
     3     android:layout_width="match_parent"
     4     android:layout_height="match_parent"
     5     android:orientation="vertical" 
     6     android:gravity="center">
     7 
     8    <LinearLayout 
     9        android:layout_width="match_parent"
    10        android:layout_height="0dp"
    11        android:layout_weight="2">
    12        <com.fuly.image.MyMatrixView
    13            android:id="@+id/myview"
    14            android:layout_width="wrap_content"
    15            android:layout_height="wrap_content"/>
    16    </LinearLayout>
    17    <LinearLayout 
    18        android:layout_width="match_parent"
    19        android:layout_height="0dp"
    20        android:layout_weight="3">
    21        <GridLayout 
    22            android:id="@+id/mGrid"
    23            android:layout_width="match_parent"
    24            android:layout_height="match_parent"
    25            android:columnCount="3"
    26            android:rowCount="3"></GridLayout>
    27    </LinearLayout>
    28    <LinearLayout 
    29        android:layout_width="match_parent"
    30        android:layout_height="wrap_content"
    31        >
    32        <Button 
    33            android:id="@+id/btnChange"
    34            android:layout_width="0dp"
    35            android:layout_weight="1"
    36            android:layout_height="wrap_content"
    37            android:onClick="btnChange"
    38            android:text="改变"/>
    39         <Button 
    40            android:id="@+id/btnReset"
    41            android:layout_width="0dp"
    42            android:layout_weight="1"
    43            android:onClick="btnReset"
    44            android:layout_height="wrap_content"
    45            android:text="恢复"/>
    46    </LinearLayout>
    47 
    48 </LinearLayout>

          在这里,我们仍旧准备了两个按钮,用来改变和还原,而GridLayout就是用来设置矩阵A的。好了,下面就开始新建MatrixActivity,用于将这个布局显示出来。注意不要忘记给这个活动注册哈。代码很简单,不用多解释的,注释也很详细。如下:

     1 package com.fuly.image;
     2 
     3 import android.app.Activity;
     4 import android.graphics.Matrix;
     5 import android.os.Bundle;
     6 import android.view.View;
     7 import android.widget.EditText;
     8 import android.widget.GridLayout;
     9 
    10 public class MatrixActivity extends Activity{
    11     
    12     private MyMatrixView mv;
    13     private GridLayout mGrid;
    14     private int mEtWidth;
    15     private int mEtHeight;
    16     private Matrix matrix = new Matrix();
    17     private float[] mMtatrix = new float[9];
    18     
    19     private EditText[] Ets = new EditText[9];
    20     
    21     protected void onCreate(Bundle savedInstanceState) {
    22         super.onCreate(savedInstanceState);
    23         setContentView(R.layout.imagematrix);
    24         
    25         mGrid = (GridLayout) findViewById(R.id.mGrid);
    26         mv = (MyMatrixView) findViewById(R.id.myview);
    27         //这个方法在mGrid被画出来后调用
    28         mGrid.post(new Runnable(){
    29 
    30             public void run() {
    31                 mEtWidth = mGrid.getWidth()/3;
    32                 mEtHeight = mGrid.getHeight()/3;
    33                 initGrid();
    34                 initMatrix();
    35             }
    36             
    37         });
    38         
    39     }
    40     /*
    41      * 初始化GridLayout
    42      */
    43     private void initGrid(){
    44         for(int i=0;i<9;i++){
    45             EditText et = new EditText(this);
    46             Ets[i] = et;
    47             mGrid.addView(et,mEtWidth,mEtHeight);
    48         }
    49     }
    50     /*
    51      * 该法初始化矩阵
    52      */
    53     private void initMatrix(){
    54         for(int i=0;i<9;i++){
    55             if(i%4 == 0){
    56             Ets[i].setText(String.valueOf(1));
    57             }else{
    58                 Ets[i].setText(String.valueOf(0));
    59             }
    60         }
    61     }
    62     /*
    63      * 该方法获取矩阵
    64      */
    65     private void getMatrix(){
    66         for(int i=0;i<9;i++){
    67             mMtatrix[i]= Float.valueOf(Ets[i].getText().toString());
    68         }
    69         matrix.setValues(mMtatrix);
    70     }
    71     //下面是两个按钮事件
    72     public void btnChange(View v){
    73         getMatrix();
    74         mv.setMatrix(matrix);
    75         mv.invalidate();
    76     }
    77 
    78     public void btnReset(View v){
    79         initMatrix();
    80         getMatrix();
    81         mv.setMatrix(matrix);
    82         mv.invalidate();
    83     }
    84 }

          东西都准备齐全了,下面就在MainActivity里面添加按钮事件,跳转到我们这个活动中来吧。如下:

     1 package com.fuly.image;
     2 
     3 import android.os.Bundle;
     4 import android.view.View;
     5 import android.app.Activity;
     6 import android.content.Intent;
     7 
     8 
     9 public class MainActivity extends Activity {
    10 
    11    
    12     protected void onCreate(Bundle savedInstanceState) {
    13         super.onCreate(savedInstanceState);
    14         setContentView(R.layout.activity_main);
    15     }
    16      
    17     //下面是按钮事件
    18     public void btnMatrix(View v){
    19         Intent intent = new Intent(this,MatrixActivity.class);
    20         startActivity(intent);
    21     }
    22    
    23 }

          OK,一切都好了,运行程序吧。下面是两张效果图:

                                  

          刚开始是左边的图片,然后设定好矩阵的值,点击改变是右边的图片,点击恢复后又变为左边的图片。

          经过上面的实际演练,不知道你有没有掌握矩阵变换的知识呢?你可以试着采用第二种矩阵设置的方式,试一试,这样子能更好的检验你的学习效果。我就不尝试了。在下一篇文章中,将会学习利用画笔风格来实现图片效果。赶快和我一起学习吧!另外,如果继续学习,请妥善保存好本篇代码,因为后面的文章是在这个代码上进行再书写的。

  • 相关阅读:
    线程的五种状态
    ajax回调打开新窗体防止浏览器拦截有效方法
    mysql 如果字段为null自动返回需要的信息sql
    String 与 StringBuffer的区别
    Windows Git中文文件名乱码
    定义函数指针
    hello world
    C++析构函数调用异常问题研究
    企业开发的时候,有可能碰到的问题
    jmap
  • 原文地址:https://www.cnblogs.com/fuly550871915/p/4886353.html
Copyright © 2011-2022 走看看