简介

Matrix ,中文里叫矩阵,高等数学里有介绍。Android中的Matrix类是一个3x3的位置坐标矩阵,在图像处理方面,主要是用于平面的缩放、平移、旋转等操作。

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)来进行相应的变换的。

下面我们来看看四种变换的具体情形。由于所有的图形都是有点组成,因此我们只需要考察一个点相关变换即可。

平移变换

假定有一个点的坐标是 P(x0,y0) P_(x_0,y_0) ,将其移动到 ,将其移动到 P(x,y) P_(x,y) ,再假定在x轴和y轴方向移动的大小分别为:

△x = x −  x \ - \ x0 x_0
△y = y −  y \ - \ y0 y_0

如下图所示:

不难知道:

x=  x = \ x0 x_0 + △x
y=  y = \ y0 y_0 + △y

如果用矩阵来表示的话,就可以写成:

旋转变换

围绕坐标原点旋转

假定有一个点的坐标是 P(x0,y0) P_(x_0,y_0) ,相对坐标原点顺时针旋转θ ,相对坐标原点顺时针旋转 \theta后的情形,同时假定P点离坐标原点的距离为r,如下图:

那么,

如果用矩阵,就可以表示为:

围绕某个点旋转

如果是围绕某个点 (xp,yp) (x_p,y_p) 顺时针旋转θ 顺时针旋转 \theta ,那么用矩阵表示为:

可以化为:

很显然:

  • 如下图所示,是将坐标原点移动到点 (xp,yp) (x_p,y_p) 后, P(x0,y0) P_(x_0,y_0)的新坐标。

  • 如下图所示,是将上一步变换后的 P(x0,y0) P_(x_0,y_0),围绕新的坐标原点顺时针旋转 θ \theta 。

  • 如下图所示,是经过上一步旋转变换后,再将坐标原点移回到原来的坐标原点。

所以,围绕某一点进行旋转变换,可以分成3个步骤,即首先将坐标原点移至该点,然后围绕新的坐标原点进行旋转变换,再然后将坐标原点移回到原先的坐标原点。

缩放变换

理论上而言,一个点是不存在什么缩放变换的,但考虑到所有图像都是由点组成,因此,如果图像在x轴和y轴方向分别放大k1和k2倍的话,那么图像中的所有点的x坐标和y坐标均会分别放大k1和k2倍,即:

x=  x = \ k1x0 k_1x_0
y=  y = \ k2y0 k_2y_0

用矩阵表示就是:

缩放变换比较好理解,就不多说了。

错切变换

错切变换(skew)在数学上又称为Shear mapping(可译为“剪切变换”)或者Transvection(缩并),它是一种比较特殊的线性变换。错切变换的效果就是让所有点的x坐标(或者y坐标)保持不变,而对应的y坐标(或者x坐标)则按比例发生平移,且平移的大小和该点到x轴(或y轴)的垂直距离成正比。错切变换,属于等面积变换,即一个形状在错切变换的前后,其面积是相等的。

比如下图,各点的y坐标保持不变,但其x坐标则按比例发生了平移。这种情况将水平错切。

下图各点的x坐标保持不变,但其y坐标则按比例发生了平移。这种情况叫垂直错切。

假定一个点 P(x0,y0) P_(x_0,y_0)经过错切变换后得到 P(x,y) P_(x,y),对于水平错切而言,应该有如下关系:

x= x0 x = \ x_0 + ky0 ky_0
y=  y = \ y0 y_0

用矩阵表示就是:

扩展到3 x 3的矩阵就是下面这样的形式:

同理,对于垂直错切,可以有:

在数学上严格的错切变换就是上面这样的。在Android中除了有上面说到的情况外,还可以同时进行水平、垂直错切,那么形式上就是:

对称变换

除了上面讲到的4中基本变换外,事实上,我们还可以利用Matrix,进行对称变换。所谓对称变换,就是经过变化后的图像和原图像是关于某个对称轴是对称的。比如,某点 经过对称变换后得到,

如果对称轴是x轴,难么,

x= x0 x = \ x_0
y= − y = \ - y0 y_0

用矩阵表示就是:

如果对称轴是y轴,那么,

x= −x0 x = \ -x_0
y=  y = \ y0 y_0

用矩阵表示就是:

如果对称轴是y = x,如图:

那么,

很容易可以解得:

x= x0 x = \ x_0
y=  y = \ y0 y_0

用矩阵表示就是:

同样的道理,如果对称轴是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对称,那么需使用这种转换:

关于对称轴为y = kx 或y = kx + b的情况,同样需要考虑这方面的问题。

参考:
http://blog.csdn.net/pathuang68/article/details/6991867

基本方法解析

构造函数

public Matrix()
public Matrix(Matrix src)

构造函数有两个,第一个是直接创建一个单位矩阵,第二个是根据提供的矩阵创建一个新的矩阵(采用deep copy)

单位矩阵如下:

isIdentity与isAffine

public boolean isIdentity()//判断是否是单位矩阵
public boolean isAffine()//判断是否是仿射矩阵

是否是单位矩阵很简单,就不做讲解了,这里是否是仿射矩阵可能大家不好理解。

首先来看看什么是仿射变换。仿射变换其实就是二维坐标到二维坐标的线性变换,保持二维图形的“平直性”(即变换后直线还是直线不会打弯,圆弧还是圆弧)和“平行性”(指保持二维图形间的相对位置关系不变,平行线还是平行线,而直线上点的位置顺序不变),可以通过一系列的原子变换的复合来实现,原子变换就包括:平移、缩放、翻转、旋转和错切。这里除了透视可以改变z轴以外,其他的变换基本都是上述的原子变换,所以,只要最后一行是0,0,1则是仿射矩阵。

####rectStaysRect

public boolean rectStaysRect()

判断该矩阵是否可以将一个矩形依然变换为一个矩形。当矩阵是单位矩阵,或者只进行平移,缩放,以及旋转90度的倍数的时候,返回true。

####reset

public void reset()

重置矩阵为单位矩阵。

####setTranslate

public void setTranslate(float dx, float dy)

设置平移效果,参数分别是x,y上的平移量。
效果图如下:

代码:

Matrix matrix = new Matrix();
canvas.drawBitmap(bitmap, matrix, paint);matrix.setTranslate(100, 1000);
canvas.drawBitmap(bitmap, matrix, paint);

setScale

public void setScale(float sx, float sy, float px, float py)
public void setScale(float sx, float sy)

两个方法都是设置缩放到matrix中,sx,sy代表了缩放的倍数,px,py代表缩放的中心。这里跟上面比较类似不做讲解了。

setRotate

public void setRotate(float degrees, float px, float py)
public void setRotate(float degrees)

和上面类似,不再讲解。

setSinCos

public void setSinCos(float sinValue, float cosValue, float px, float py)
public void setSinCos(float sinValue, float cosValue)

这个方法乍一看可能有点蒙,其实在前面的原理中,我们讲解了一个旋转的例子,他最终的矩阵效果是这样的:

其实旋转,就是使用了这样的matrix,显而易见,这里的参数就清晰了。
sinValue:对应图中的sin值
cosValue:对应cos值
px:中心的x坐标
py:中心的y坐标

看一个示例,我们把图像旋转90度,那么90度对应的sin和cos分别是1和0。

看代码如下:

Matrixmatrix = new Matrix();
matrix.setSinCos(1, 0, bitmap.getWidth() / 2, bitmap.getHeight() / 2);
canvas.drawBitmap(bitmap, matrix, paint);

setSkew

public void setSkew(float kx, float ky, float px, float py)
public void setSkew(float kx, float ky)

错切,这里kx,ky分别代表了x,y上的错切因子,px,py代表了错切的中心。不了解错切了在前面canvas变换中去查看,这里不再讲解。

setConcat

public boolean setConcat(Matrix a,Matrix b)

将当前matrix的值变为a和b的乘积,它的意义在下面的 进阶方法中来探讨。

进阶

上面的基本方法中,有关于变换的set方法都可以带来不同的效果,但是每个set都会把上个效果清除掉,例如依次调用了setSkew,setTranslate,那么最终只有setTranslate会起作用,那么如何才和将两种效果复合呢。Matrix给我们提供了很多方法。但是主要都是2类:

preXXXX:以pre开头,例如preTranslate
postXXXX:以post开头,例如postScale

他们分别代表了前乘,和后乘。看一段代码:

Matrix matrix = new Matrix();
matrix.setTranslate(100, 1000);
matrix.preScale(0.5f, 0.5f);

这里matrix前乘了一个scale矩阵,换算成数学式如下:

从上面可以看出,最终得出的matrix既包含了缩放信息也有平移信息。
后乘自然就是matrix在后面,而缩放矩阵在前面,由于矩阵前后乘并不等价,也就导致了他们的效果不同。我们来看看后乘的结果:

可以看到,结果跟上面不同,并且这也不是我们想要的结果,这里缩放没有更改,但是平移被减半了,换句话说,平移的距离也被缩放了。所以需要注意前后乘法的关系。

来看看他们对应的效果图:

前乘:

后乘:

可以明显看到,后乘的平移距离受了影响。

了解清除了前后乘的意义,在使用的过程中,多个效果的叠加时,一样要注意,否则效果达不到预期。

其他方法

matrix除了上面的方法外,还有一些其他的方法。

setRectToRect

public boolean setRectToRect(RectF src, RectF dst, ScaleToFit stf)

将rect变换成rect,上面的rectStaysRect已经说过,要保持rect只能做缩放平移和选择90度的倍数,那么这里其实也是一样,只是这几种变化,这里通过stf参数来控制。

ScaleToFit 有如下四个值:

  • FILL: 可能会变换矩形的长宽比,保证变换和目标矩阵长宽一致。
  • START:保持坐标变换前矩形的长宽比,并最大限度的填充变换后的矩形。至少有一边和目标矩形重叠。左上对齐。
  • CENTER: 保持坐标变换前矩形的长宽比,并最大限度的填充变换后的矩形。至少有一边和目标矩形重叠。
  • END:保持坐标变换前矩形的长宽比,并最大限度的填充变换后的矩形。至少有一边和目标矩形重叠。右下对齐。

这里使用谷歌的api demo的图片作为例子:

setPolyToPoly

public boolean setPolyToPoly(float[] src, int srcIndex,float[] dst, int dstIndex,int pointCount)

通过指定的0-4个点,原始坐标以及变化后的坐标,来得到一个变换矩阵。如果指定0个点则没有效果。

下面通过例子分别说明1到4个点的可以达到的效果:

1个点,平移

只指定一个点,可以达到平移效果:

代码如下:

float[] src = {0, 0};
int DX = 300;
float[] dst = {0 + DX, 0 + DX};
matrix.setPolyToPoly(src, 0, dst, 0, 1);
canvas.drawBitmap(bitmap, matrix, paint);
2个点,旋转或者缩放

两个点,可以达到旋转效果或者缩放效果,缩放比较简单,这里我们来看旋转效果,一个点指定中心,一点指出旋转的效果。

代码:

int bw = bitmap.getWidth();
int bh = bitmap.getHeight();
float[] src = {bw / 2, bh / 2, bw, 0};
float[] dst = {bw / 2, bh / 2, bw / 2 + bh / 2, bh / 2 + bw / 2};
matrix.setPolyToPoly(src, 0, dst, 0, 2);
canvas.drawBitmap(bitmap, matrix, paint);

图片的中心点作为旋转的中心,前后不变,右上角变化到了下方,所以导致图片旋转了90度。

3个点,错切

使用3个点,可以产生错切效果,指定3个顶点,一个固定,另外两个移动。

看图:

代码如下:

Matrix matrix = new Matrix();
int bw = bitmap.getWidth();
int bh = bitmap.getHeight();
float[] src = {0,0, 0, bh,bw,bh};
float[] dst = {0, 0, 200, bh, bw + 200, bh};
matrix.setPolyToPoly(src, 0, dst, 0, 3);
canvas.drawBitmap(bitmap, matrix, paint);
4个点,透视

透视就是观察的角度变化了。导致投射到平面上的二维图像变化了。

我们看下面的例子,更容易理解:

图片看起来好像倾斜了,实现特别简单:

Matrix matrix = new Matrix();
int bw = bitmap.getWidth();
int bh = bitmap.getHeight();
float[] src = {0, 0, 0, bh, bw, bh, bw, 0};
int DX = 100;
float[] dst = {0 + DX, 0, 0, bh, bw, bh, bw - DX, 0};
matrix.setPolyToPoly(src, 0, dst, 0, 4);
canvas.drawBitmap(bitmap, matrix, paint);

可以看到,只是把左右两个顶点往里面收拢了,这样就得出了一个有3d效果的透视图。

invert

public boolean invert(Matrix inverse)

反转当前矩阵,如果能反转就返回true并将反转后的值写入inverse,否则返回false。当前矩阵*inverse=单位矩阵。

反转前后有什么效果,我们来看看示例:

可以看到,反转之后,其实是对效果的一种反转。

mapPoints

public void mapPoints(float[] dst, int dstIndex, float[] src, int srcIndex,int pointCount)
public void mapPoints(float[] dst, float[] src)
public void mapPoints(float[] pts)

映射点的值到指定的数组中,这个方法可以在矩阵变换以后,给出指定点的值。
dst:指定写入的数组
dstIndex:写入的起始索引,x,y两个坐标算作一对,索引的单位是对,也就是经过两个值才加1
src:指定要计算的点
srcIndex:要计算的点的索引
pointCount:需要计算的点的个数,每个点有两个值,x和y。

mapVectors

public void mapVectors(float[] dst, int dstIndex, float[] src, int srcIndex,int vectorCount)
public void mapVectors(float[] dst, float[] src)
public void mapVectors(float[] vecs)

与上面的mapPoionts基本类似,这里是将一个矩阵作用于一个向量,由于向量的平移前后是相等的,所以这个方法不会对translate相关的方法产生反应,如果只是调用了translate相关的方法,那么得到的值和原本的一致。

mapRect

public boolean mapRect(RectF dst, RectF src)
public boolean mapRect(RectF rect)

返回值即是调用的rectStaysRect(),这个方法前面有讲过,这里把src中指定的矩形的左上角和右下角的两个点的坐标,写入dst中。

mapRadius

public float mapRadius(float radius)

返回一个圆圈半径的平均值,将matrix作用于一个指定radius半径的圆,随后返回的平均半径。

以上基本解析完毕了所有matrix的方法,以及一些高阶用法,本篇文章就到这里

Android Matrix的用法总结相关推荐

  1. Android Matrix的代码验证和应用

    Matrix介绍 : Android Matrix的用法总结(链接:ttp://blog.csdn.net/jdsjlzx/article/details/52741445) 代码验证 前面讲到的各种 ...

  2. Android之Adapter用法总结

    本文转自http://kb.cnblogs.com/a/2328334/,转载请注明原出处. Android之Adapter用法总结 作者:Devin Zhang  来源:博客园  发布时间:2012 ...

  3. Android的Adapter用法总结

    Android之Adapter用法总结 1.Adapter概念   定义为将一个类的接口变换成客户端所期待的一种接口,从而使原本因接口不匹配而无法在一起工作的两个类能够在一起工作.   在androi ...

  4. 了解Android Matrix转换

    原文 了解Android Matrix转换 很多年前,在学校我学习了矩阵. 我记不太清楚了,但我记得的是在想,"但是......你对这些知识做了什么呢?" 快进几年,我开始作为An ...

  5. Android webservice的用法详细讲解

    Android webservice的用法详细讲解 看到有很多朋友对WebService还不是很了解,在此就详细的讲讲WebService,争取说得明白吧.此文章采用的项目是我毕业设计的webserv ...

  6. android Matrix图片变换处理

    今天,讲讲android  Matrix图片变换处理的内容. Matrix 对于一个图片变换的处理,需要Matrix类的支持,它位于"android.graphics.Matrix&qu ...

  7. android room表关联,Android Room的用法

    Android Room的用法 SQLite是Android内置的轻量级关系型数据库,但直接使用SQLite core包做数据库操作有以下劣势:需要编写长且重复的代码,这会很耗时且容易出错. 管理SQ ...

  8. android图片显示组件,Android可循环显示图像的Android Gallery组件用法实例

    本文实例分析了Android可循环显示图像的Android Gallery组件用法.分享给大家供大家参考,具体如下: Gallery组件主要用于横向显示图像列表,不过按常规做法.Gallery组件只能 ...

  9. python中flatten_Python中flatten( ),matrix.A用法说明

    flatten()函数用法 flatten是numpy.ndarray.flatten的一个函数,即返回一个折叠成一维的数组.但是该函数只能适用于numpy对象,即array或者mat,普通的list ...

最新文章

  1. 深度学习实战篇-基于RNN的中文分词探索
  2. Oracle编程入门经典 第6章 在Oracle中处理语句
  3. 这几天又看了Gosu,发现也是蛮有意思
  4. Java如何连接openvas_gas: chinese Gui for openvAS(GAS)
  5. 多路查找树之2-3树的删除原理
  6. 2021年5月9日,是第108个母亲节,祝福所有的母亲节日快乐
  7. MSN无法登陆的八种情况
  8. Android ListView 滑动背景为黑色的解决办法 listview小知识整理
  9. 超人气新书《SEO实战密码——60天网站流量提高20倍》火爆热销
  10. ASP.NET Web应用程序和ASP.NET网站的区别
  11. 《明解c语言 入门篇》柴田望洋/著 205段代码
  12. [UVM]UVM Register Test Sequence
  13. Appium_3_环境配置_Appium-desktop配置
  14. 天河二号上运行ZHT(a zero-hop distributed table)
  15. 手机WiFi和热点为何不能同时开
  16. python helper方法_Python io_utils.ImportHelper方法代碼示例
  17. 网页引用Font Awesome图标
  18. 明翰全日制英国硕士学术写作V0.1(持续更新)
  19. python的numpy教程_ROS与Python入门教程-使用numpy
  20. 计算机软件吸附效应,试举出生活中的例子说明吸附现象的实际意义?

热门文章

  1. Swift自定义导航栏返回按钮
  2. R9 7950X和i9 13900k性能对比 R97950X和i913900k对比
  3. python语言创意绘画是什么意思_什么是创意美术
  4. Ubuntu修改登录密码
  5. oracle report builder 6i下载,report builder
  6. 基于深度学习的Action Recognition(行为识别)【二】
  7. 回力也在国外火了,接下来该是谁了
  8. Stable Diffusion 个人推荐的各种模型及设置参数、扩展应用等合集(不断更新中)
  9. SpringCloud的快速入门教程
  10. Python3,区区一段代码,自己就可以制作动漫头像,YYDS。