Android图像处理之图形特效处理
前面我们了解了关于图像色彩处理的相关技巧,下面继续来探讨图形图像方面的处理技巧。
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(Bitmap bitmap, int meshWidth, int meshHeight, float[] verts,int vertOffset, int[] colors, int colorOffset, Paint paint)
- bitmap:将要扭曲的图像。
- meshWidth:需要的横向网格数目。
- meshHeight:需要纵向网格数目。
- verts:网格交叉点坐标数组。
- vertOffset:verts数组中开始跳过的(x,y)坐标对的数目。
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;}}}
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);}
<?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图像处理之图形特效处理相关推荐
- android 特效绘图,Android绘图机制与处理技巧——Android图像处理之图形特效处理...
Android变形矩阵--Matrix 对于图像的图形变换,Android系统是通过矩阵来进行处理的,每个像素点都表达了其坐标的X.Y信息.Android的图形变换矩阵是一个3x3的矩阵,如下图所示: ...
- Android图像处理之画笔特效处理
不管是在我们的世界里,还是在Android的世界里,想要向神笔马良一样画出各种精彩绝伦的画,就必须得有一个前提------要有一支神奇的画笔.在前面的学习中,我们已经初步了解了一些常用的画笔属性,比如 ...
- Android图像处理之色彩特效处理
Android对于图片的处理,最常用到的数据结构时位图------bitmap,它包含了一张图片所有的数据,整个图片都是由点阵和颜色组成的,所谓点阵就是一个包含像素的矩阵,每一个元素对应着图片的一个像 ...
- Android图像处理整理
Android图像处理整理 参考:http://blog.csdn.net/luzhenyuxfcy/article/details/49427781 我们常用的处理方式基本都是在对像素矩阵按照一定的 ...
- Android 图形系统之图形缓冲区分配
BufferQueue 是 Android 中所有图形处理操作的核心.它的作用很简单:将生成图形数据缓冲区的一方(生产者)连接到接受数据以显示或进一步处理的一方(消费者).几乎所有在系统中移动图形数据 ...
- android图像处理系列之五-- 给图片添加边框(中)
前面一篇讲到给图片加边框的方式,只能给图片加一些有规则的边框,如果想加一些比较精美的效果,就有点麻烦了.下面就给出解决这个问题的思路. 思路是:一些比较精美的花边图片我们是很难用代码控制,就目前本人水 ...
- Qt 图形特效(Graphics Effect)介绍
原文链接:Qt 图形特效(Graphics Effect)介绍 QGraphicsEffect也是Qt-4.6引入的一个新功能.它让给图形元素QGraphicsItem增加更佳视觉效果的编程变得非常简 ...
- C语言百叶窗动画效果算法,用vb实现“百叶窗”的图形特效_visualbasic教程
在Powerpoint这样的软件中,各种各样的图形特效层出不穷,其中"百叶窗"的切换效果尤为新颖奇特.在VB中实现这样的图形特效十分简单方便.其方法是调用WINDOWS的API函数 ...
- android 涂鸦之图片叠加,android图像处理系列之七--图片涂鸦,水印-图片叠加...
图片涂鸦和水印其实是一个功能,实现的方式是一样的,就是一张大图片和一张小点图片叠加即可.前面在android图像处理系列之六--给图片添加边框(下)-图片叠加中也讲到了图片叠加,里面实现的原理是直接操 ...
最新文章
- 【数理逻辑】谓词逻辑 ( 谓词逻辑基本等值式 | 消除量词等值式 | 量词否定等值式 | 量词辖域收缩扩张等值式 | 量词分配等值式 )
- JS与flash交互通信控制的方法
- python算法攻略_算法基础及python实现笔记一(堆和DFS)
- Class绑定、Class对象绑定、v-if(条件渲染)、v-show(元素显示)、v-for(列表渲染)
- Mybatis高级应用 延迟加载
- 在线电子商务网站 分页 的封装
- 【java】对学生成绩进行排序
- SpringBoot使用thymefeal出现No mapping for GET /xxx的解决办法
- Idea Java代码生成器使用及模板自定义
- 加载类型库/DLL 时出错。 (Exception from HRESULT: 0x80029C4A (TYPE_E_CANTLOADLIBRARY)
- numpy.arccos详解
- python的dict
- 处理BigDecimal字段, java.math.BigDecimal cannot be cast to [Ljava.lang.Object;
- Kotlin开发利器之协程
- 常熟理工php实验三_常熟理工Oracle实验三_高燕教授
- 函数的极限与连续性的关系
- 内核线程注入(x64)
- Linux指令--traceroute,netstat,ss
- 2022-2028年全球及中国斯特林发动机行业投资前景分析
- FAT32、NTFS和exFAT