前面我们了解了关于图像色彩处理的相关技巧,下面继续来探讨图形图像方面的处理技巧。

1.Android变形矩阵------Matrix

对于图像的色彩处理,Android系统提供了ColorMatrix颜色矩阵来帮助我们进行图像处理。而对于图像的图形变换,Android系统也是通过矩阵来进行处理的,每个像素点都表达了其左边的X、Y信息。Android的图形变换矩阵是一个3*3的矩阵,如下图所示。

当使用变换矩阵去处理每一个像素点的时候,与颜色矩阵的矩阵乘法一样,计算公式如下。

X1 = a*X + b*Y + c
Y1 = d*X + e*Y + f
1  = g*X + h*Y + i

通常情况下,会让g=h=0, i=1,这样使1  = g*X + h*Y + i恒成立。因此,只需要着重关注上面几个参数就可以了。

与色彩变幻矩阵的初始矩阵一行,图形变换矩阵也有一个初始矩阵。很明显,就是对角线a、e、i为1,其他元素值为0的矩阵。如下图所示。

图像的变形处理通常包含以下四类基本变换。

  • Translate:平移变换
  • Rotate:旋转变换
  • Scale:算放变换
  • Skew:错切变换

1.1平移变换

平移变换的做编制变换过程如下图所示,即将每个像素点都进行平移变换。

当p(x0,y0)平移到p(x,y)时,坐标值发生了如下左图所示的变换,如果写成矩阵形式就如下有图。

通过计算可以发现如上左图等式。这也就是前面所说的实现平移过程的平移公式。

1.2 旋转变换

旋转变换即指一个点围绕一个中心旋转到一个新的点,如下图所示。

当从p(x0,y0)点,以坐标原点为旋转中心旋转到p(x,y)点时,可以将点的坐标都表达成OP与X轴正方向夹角的函数表达式,如下所示。

如果写成矩阵形式就如下图所示。

通过计算,可以还原以上等式,上图也就是变换矩阵。

前面时以坐标原点为旋转中心的旋转变换,如果以任意点O为旋转中心来进行旋转变换,通常需要以下三个步骤。

  • 将原点坐标平移到O点。
  • 使用前面讲的以坐标原点为中心的旋转方法进行旋转变换。
  • 将坐标原点还原。

通过以上三个步骤,实现了任意点为旋转中心的旋转变换。

1.3 缩放变换

一个像素点时不存在缩放的概念的,但是由于图像时由很多个像素点组成的,如果将每个点的坐标都进行相同比例的缩放,最终就会让整个图像形成缩放的效果,缩放效果的计算公式如下。

如果写成矩阵形式,如下所示。

1.4 错切变换

错切变换(skew) 在数学上又称为Shear mapping(可译为“剪切变换”)或者Transvection(缩并),他是一种比较特殊的线性变换。错切变换的效果就是让所有点的X坐标(或者Y坐标)保持不变,而对应的Y坐标(或者X坐标)则按比例发生平移,且平移的大小和该点到X轴(或Y轴)的垂直距离成正比。

错切变换通常包含两种------水平错切与垂直错切,分别如下图如所示。

错切变换的计算公式如下所示。

通过计算,就可以还原到以上等式。因此,下图所示的矩阵即为错切变换矩阵。

有上面的分析可以发现,这个3*3的矩阵与色彩变换矩阵一样,每个位置的元素所表示的功能是有规律的,总结的规律如下所示。

可以发现,A、B、C、D、E、F、这六个矩阵元素分别对应以下变换。

  • A和E控制Scale:缩放变换
  • B和D控制Skew:错切变换
  • C和F控制Trans:平移变换
  • A、B、D、E共同控制Rotate:平移变换

在了解了矩阵变换规律后,通过类似色彩矩阵中模拟矩阵的例子来模拟以下变形矩阵。整个代码与模拟颜色矩阵所使用的代码基本一致。在图形变换矩阵中,同样是通过一个一维数组来模拟矩阵,并通过setValues()方法将一个一维数组转换为图形变换矩阵,代码如下所示。

        float[] matrixs = new float[9];Matrix matrix = new Matrix();matrix.setValues(matrixs);

当活的了变换矩阵后,就可以通过以下代码,将一个图像以这个变换矩阵的形式绘制出来。

        canvas.drawBitmap(bitmap, matrix, null);

程序运行后的初始界面如图所示。

   
        整个程序的源码和xml布局文件如下所示。

public class MainActivity4 extends AppCompatActivity{private ImageView imageView;private GridLayout group;private int mEtWidth;private int mEtHeight;private EditText[] mEts = new EditText[20];private Bitmap bitmap;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.matrix_image);imageView = (ImageView)findViewById(R.id.iv);group = (GridLayout)findViewById(R.id.group);bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.iv);imageView.setImageBitmap(bitmap);group.post(new Runnable() {@Overridepublic void run() {mEtWidth = group.getWidth()/3;mEtHeight = group.getHeight()/3;addEts();initMatrix();}});}//添加EditTextprivate void addEts() {for (int i = 0; i < 9; i++) {EditText editText = new EditText(MainActivity4.this);mEts[i] = editText;group.addView(editText,mEtWidth,mEtHeight);}}//初始化颜色矩形为初始状态private void initMatrix() {for (int i = 0; i < 9; i++) {if ( i % 4 == 0 ) {mEts[i].setText("1");} else {mEts[i].setText("0");}}}//获取矩阵值private float[] getMatrix() {float[] matrixs = new float[9];for (int i = 0; i < 9; i++) {matrixs[i] = Float.valueOf(mEts[i].getText().toString());}return matrixs;}//将矩阵值设置到图像private void setImageMatrix(float[] matrixs) {Bitmap bmp = Bitmap.createBitmap(bitmap.getWidth(), bitmap.getHeight(), Bitmap.Config.ARGB_8888);Matrix matrix = new Matrix();matrix.setValues(matrixs);Canvas canvas = new Canvas(bmp);canvas.drawBitmap(bitmap, matrix, null);imageView.setImageBitmap(bmp);}public void btnChanged(View view){setImageMatrix(getMatrix());}public void btnReset(View view){initMatrix();setImageMatrix(getMatrix());}
}
<?xml version="1.0" encoding="utf-8"?>
<LinearLayoutxmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent"android:layout_height="match_parent"android:orientation="vertical"><ImageViewandroid:id="@+id/iv"android:layout_width="wrap_content"android:layout_height="0dp"android:layout_weight="2"/><GridLayoutandroid:id="@+id/group"android:columnCount="3"android:rowCount="3"android:layout_width="match_parent"android:layout_height="0dp"android:layout_weight="3" /><LinearLayoutandroid:layout_width="match_parent"android:layout_height="wrap_content"><Buttonandroid:text="change"android:onClick="btnChanged"android:layout_width="0dp"android:layout_weight="1"android:layout_height="wrap_content" /><Buttonandroid:text="reset"android:onClick="btnReset"android:layout_width="0dp"android:layout_weight="1"android:layout_height="wrap_content" /></LinearLayout>
</LinearLayout>

当然,与色彩矩阵一样,Android系统同样提供了一些API来简化矩阵运算。我们不必每次去设置矩阵元素的值。Android中使用Matrix类来封装矩阵,并提供了以下几个操作方法来实现上面的四种变换方式。

  • matrixX.setTranslate()----平移变换
  • matrixX.setRotate()------旋转变换
  • matrixX.setScale()----缩放变换
  • matrixX.setSkew()----错切变换

Matrix类的set方法会重置矩阵中的所有值,而post和pre方法不会,这两个方法重用来实现矩阵的混合作用。不过要注意的是,矩阵运算不满足交换率,所以矩阵乘法的前乘和后乘是两种不同的运算方式。举个例子来说,比如要实现以下效果。

  • 先平移到(300,100)
  • 再旋转45度
  • 最后平移到(200,200)

如果使用后乘运算,表示当前哭真乘上参数代表的矩阵,代码如下所示。

matrixX.setRotate(45);
matrixX.postTranslate(200,200);
        如果使用前乘算法,表示参数代表的矩阵乘上当前矩阵,代码如下所示。
matrixX.preTranslate(200,200);
matrixX.setRotate(45);

2.像素块分析

与我们当初了解色彩的处理效果时类似,使用了颜色矩阵与像素点两种方式来进行图像色彩的处理,这里再进行图像的特效处理的时候,也有两种方式,即前面讲的使用矩阵来进行图像变换和马上就要讲的使用drawBitmapMesh()方法来进行处理。drawBitmapMesh()与操作像素点来改变色彩的原理类似,只不过时把图像分成了一个个的小块,然后通过每个图像快来修改整个图像。
该方法的代码如下所示。
drawBitmapMesh(Bitmap bitmap, int meshWidth, int meshHeight, float[] verts,int vertOffset, int[] colors, int colorOffset, Paint paint)
这个方法的参数很多,关键的参数如下。
  • bitmap:将要扭曲的图像。
  • meshWidth:需要的横向网格数目。
  • meshHeight:需要纵向网格数目。
  • verts:网格交叉点坐标数组。
  • vertOffset:verts数组中开始跳过的(x,y)坐标对的数目。
其中最重要的参数是一个数组------verts。
前面说了,要使用drawBitmapMesh()方法先要将图片分割为若干个图像块。所以,在图像上横纵各画N-1条线,将图像分成N块,而这横纵各N条线就交织成了N*N个点,而每个点的坐标则以x1,y1,x2,y2,...,xn,yn的形式保存在verts数组中。也就是说verts数组的每两位用来保存一个交织点,第一个是横坐标,第二个是纵坐标。而整个drawBitmapMesh()方法改变图像的方式,就是靠这些坐标值的改变来重新定位每一个图像块,从而达到图像效果处理的功能。这里借用API Demo中的一张图来形象地解释一下这个过程,如左图所示。当改变某些交叉点地坐标时,就会让整个像素快随之发生扭曲,如右图所示。
  
由此可见,drawBitmapMesh()方法的功能非常强大,基本可以实现所有的图像特效,但使用起来非常复杂,其关键就是在于计算、确定新的交叉点的坐标。下面来看看如何使用强大的drawBitmapMesh()方法来实现一个旗帜飞扬的效果。
要达到旗帜飞扬的效果,只需要让图片中每个交织点的横坐标较之前坐标不发生变化,而纵纵坐标之前坐标呈现一个三角函数周期性变化即可。
首先来获取交叉点的坐标,并将坐标保存到orig数组中,这里的代码基本可以作为目标代码使用,其获取交叉点坐标的原理就是通过循环遍历所有的交叉线,并按比例获取其坐标,代码如下所示。
        float width = bitmap.getWidth();float height = bitmap.getHeight();int index = 0;//获取交叉点的坐标,并将其保存到orig数组中。for (int y = 0; y<=HEIGHT; y++) {float fy = height * y /HEIGHT;for (int x = 0; x <= WIDTH; x++) {float fx = width * x / WIDTH;orig[index*2 + 0] = verts[index*2 + 0] = fx;//这里人为将坐标+100是为了让图像下移,便面扭曲后被屏幕遮挡orig[index*2 + 1] = verts[index*2 + 1] = fy+100;index +=1;}}
接下来,在onDraw()方法中改变交叉点的纵坐标的值,为了实现旗帜飘扬的效果,使用一个正弦函数Sinx来改变交叉点纵坐标的值,而横坐标的值不变,并将变化后的值保存到verts数组中,代码如下所示。
    private void flagWave() {//改变纵坐标的值,实现旗帜飞扬for (int j=0; j<=HEIGHT; j++) {for (int i=0; i<=WIDTH; i++) {verts[(j*(WIDTH+1) + i) * 2 + 0] += 0;float offsetY = (float)Math.sin((float) i / WIDTH * 2 * Math.PI + k * Math.PI);verts[(j*(WIDTH+1) + i) * 2 + 1] = orig[(j*(WIDTH+1) + i) * 2 + 1] + offsetY * A;}}}
在以上的代码中,通过正弦函数,按当前点所在横坐标的位置来确定纵坐标的偏移量。其中A表示正弦函数中的振幅大小,最后在onDraw方法中使用一下代码即可将处理后的图像绘制出来。
        canvas.drawBitmapMesh(bitmap, WIDTH, HEIGHT, verts, 0, null, 0, null);
运行程序后,就可以得到一个静态的扭曲图像,如下图所示。

为了能够让图像动起来,可以充分利用正弦函数的周期性来实现,在获取纵坐标的偏移量时,给函数增加一个周期即可,代码如下所示。

    @Overrideprotected void onDraw(Canvas canvas) {flagWave();k += 0.1f;canvas.drawBitmapMesh(bitmap, WIDTH, HEIGHT, verts, 0, null, 0, null);postInvalidateDelayed(20);}
这样,在每次重绘的时候,通过改变香味来改变偏移量,从而造成一个动态的效果,就好像旗帜在风中飘扬一样。
使用drawBitmapMesh()方法可以创建很多复杂的图像特效,但是对它的使用也相对比较复杂,并需要开发者对图像处理有很深的功底。同时,对算法的要求也比较高,需要计算各种特效下不同的坐标点变化规律,从而设计出不同的特效。
以下是布局xml文件和自定义控件代码。
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:app="http://schemas.android.com/apk/res-auto"xmlns:tools="http://schemas.android.com/tools"android:layout_width="match_parent"android:layout_height="match_parent"tools:context="test.chenj.study_5_2.MainActivity"><!--<test.chenj.study_5_2.MyView--><!--android:layout_width="wrap_content"--><!--android:layout_height="wrap_content"--><!--/>--><test.chenj.study_5_2.DomaticFlagImageViewandroid:src="@drawable/iv"android:layout_width="wrap_content"android:layout_height="match_parent" />
</LinearLayout>
package test.chenj.study_5_2;import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.drawable.BitmapDrawable;
import android.support.annotation.Nullable;
import android.util.AttributeSet;
import android.widget.ImageView;/*** 旗帜飘扬效果的动态 图片控件* Created by 72312 on 2017/12/27.*/public class DomaticFlagImageView extends android.support.v7.widget.AppCompatImageView{private static final int HEIGHT = 90; // 图像分割成N个小块,每个小块的高度private static final int WIDTH = 90; // 图像分割成N个小块,每个小块的宽度private static final int A = 45; // 正弦中振幅的大小private float k=0;private Bitmap bitmap;float[] verts = new float[(HEIGHT*2)*(WIDTH*2)];float[] orig = new float[(HEIGHT*2)*(WIDTH*2)];public DomaticFlagImageView(Context context) {super(context);}public DomaticFlagImageView(Context context, @Nullable AttributeSet attrs) {super(context, attrs);}public DomaticFlagImageView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);}@Overrideprotected void onFinishInflate() {super.onFinishInflate();//在绘图完成后,取得bitmapbitmap = ((BitmapDrawable) getDrawable()).getBitmap();float width = bitmap.getWidth();float height = bitmap.getHeight();int index = 0;//获取交叉点的坐标,并将其保存到orig数组中。for (int y = 0; y<=HEIGHT; y++) {float fy = height * y /HEIGHT;for (int x = 0; x <= WIDTH; x++) {float fx = width * x / WIDTH;orig[index*2 + 0] = verts[index*2 + 0] = fx;//这里人为将坐标+100是为了让图像下移,便面扭曲后被屏幕遮挡orig[index*2 + 1] = verts[index*2 + 1] = fy+100;index +=1;}}}private void flagWave() {//改变纵坐标的值,实现旗帜飞扬for (int j=0; j<=HEIGHT; j++) {for (int i=0; i<=WIDTH; i++) {verts[(j*(WIDTH+1) + i) * 2 + 0] += 0;float offsetY = (float)Math.sin((float) i / WIDTH * 2 * Math.PI + k * Math.PI);verts[(j*(WIDTH+1) + i) * 2 + 1] = orig[(j*(WIDTH+1) + i) * 2 + 1] + offsetY * A;}}}@Overrideprotected void onDraw(Canvas canvas) {flagWave();k += 0.1f;canvas.drawBitmapMesh(bitmap, WIDTH, HEIGHT, verts, 0, null, 0, null);postInvalidateDelayed(20);}
}
以下是动态效果图。

Android图像处理之图形特效处理相关推荐

  1. android 特效绘图,Android绘图机制与处理技巧——Android图像处理之图形特效处理...

    Android变形矩阵--Matrix 对于图像的图形变换,Android系统是通过矩阵来进行处理的,每个像素点都表达了其坐标的X.Y信息.Android的图形变换矩阵是一个3x3的矩阵,如下图所示: ...

  2. Android图像处理之画笔特效处理

    不管是在我们的世界里,还是在Android的世界里,想要向神笔马良一样画出各种精彩绝伦的画,就必须得有一个前提------要有一支神奇的画笔.在前面的学习中,我们已经初步了解了一些常用的画笔属性,比如 ...

  3. Android图像处理之色彩特效处理

    Android对于图片的处理,最常用到的数据结构时位图------bitmap,它包含了一张图片所有的数据,整个图片都是由点阵和颜色组成的,所谓点阵就是一个包含像素的矩阵,每一个元素对应着图片的一个像 ...

  4. Android图像处理整理

    Android图像处理整理 参考:http://blog.csdn.net/luzhenyuxfcy/article/details/49427781 我们常用的处理方式基本都是在对像素矩阵按照一定的 ...

  5. Android 图形系统之图形缓冲区分配

    BufferQueue 是 Android 中所有图形处理操作的核心.它的作用很简单:将生成图形数据缓冲区的一方(生产者)连接到接受数据以显示或进一步处理的一方(消费者).几乎所有在系统中移动图形数据 ...

  6. android图像处理系列之五-- 给图片添加边框(中)

    前面一篇讲到给图片加边框的方式,只能给图片加一些有规则的边框,如果想加一些比较精美的效果,就有点麻烦了.下面就给出解决这个问题的思路. 思路是:一些比较精美的花边图片我们是很难用代码控制,就目前本人水 ...

  7. Qt 图形特效(Graphics Effect)介绍

    原文链接:Qt 图形特效(Graphics Effect)介绍 QGraphicsEffect也是Qt-4.6引入的一个新功能.它让给图形元素QGraphicsItem增加更佳视觉效果的编程变得非常简 ...

  8. C语言百叶窗动画效果算法,用vb实现“百叶窗”的图形特效_visualbasic教程

    在Powerpoint这样的软件中,各种各样的图形特效层出不穷,其中"百叶窗"的切换效果尤为新颖奇特.在VB中实现这样的图形特效十分简单方便.其方法是调用WINDOWS的API函数 ...

  9. android 涂鸦之图片叠加,android图像处理系列之七--图片涂鸦,水印-图片叠加...

    图片涂鸦和水印其实是一个功能,实现的方式是一样的,就是一张大图片和一张小点图片叠加即可.前面在android图像处理系列之六--给图片添加边框(下)-图片叠加中也讲到了图片叠加,里面实现的原理是直接操 ...

最新文章

  1. 【数理逻辑】谓词逻辑 ( 谓词逻辑基本等值式 | 消除量词等值式 | 量词否定等值式 | 量词辖域收缩扩张等值式 | 量词分配等值式 )
  2. JS与flash交互通信控制的方法
  3. python算法攻略_算法基础及python实现笔记一(堆和DFS)
  4. Class绑定、Class对象绑定、v-if(条件渲染)、v-show(元素显示)、v-for(列表渲染)
  5. Mybatis高级应用 延迟加载
  6. 在线电子商务网站 分页 的封装
  7. 【java】对学生成绩进行排序
  8. SpringBoot使用thymefeal出现No mapping for GET /xxx的解决办法
  9. Idea Java代码生成器使用及模板自定义
  10. 加载类型库/DLL 时出错。 (Exception from HRESULT: 0x80029C4A (TYPE_E_CANTLOADLIBRARY)
  11. numpy.arccos详解
  12. python的dict
  13. 处理BigDecimal字段, java.math.BigDecimal cannot be cast to [Ljava.lang.Object;
  14. Kotlin开发利器之协程
  15. 常熟理工php实验三_常熟理工Oracle实验三_高燕教授
  16. 函数的极限与连续性的关系
  17. 内核线程注入(x64)
  18. Linux指令--traceroute,netstat,ss
  19. 2022-2028年全球及中国斯特林发动机行业投资前景分析
  20. FAT32、NTFS和exFAT

热门文章

  1. Nacos 学习笔记2 - 搭建 Nacos 集群
  2. 电子承兑汇票贴现的最大风险有哪些
  3. 一组有关IT公司的笑话
  4. Discuz!新插件推荐之:国外发帖审核
  5. Fisco-Bcos智能合约开发案例----商品溯源
  6. mysql学生选课系统的关系模型_数据库系统原理ER模型与关系模型
  7. 新中国第一位博士!答辩导师团豪华到令人害怕
  8. 专业显卡打游戏测试软件,通吃游戏和渲染!专业卡和游戏卡实际测试对比
  9. Centos 7, Torque 单节点部署
  10. 广东省大学计算机一级考试试题类型,大学计算机一级考试试题及答案