Android Matrix的用法总结

简介

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) ,再假定在x轴和y轴方向移动的大小分别为:

△x = x  
△y = y  

如下图所示:

这里写图片描述

不难知道:

x=  + △x
y=  + △y

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

这里写图片描述

旋转变换

围绕坐标原点旋转

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

这里写图片描述

那么,

这里写图片描述

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

这里写图片描述

围绕某个点旋转

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

这里写图片描述

可以化为:

这里写图片描述

很显然:

  • 如下图所示,是将坐标原点移动到点 (xp,yp)的新坐标。

这里写图片描述

  • 如下图所示,是将上一步变换后的 P(x0,y0)

这里写图片描述

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

这里写图片描述

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

缩放变换

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

x= 
y= 

用矩阵表示就是:

这里写图片描述

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

错切变换

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

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

这里写图片描述

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

这里写图片描述

假定一个点P(x0,y0),对于水平错切而言,应该有如下关系:

x= x0
y= 

用矩阵表示就是:

这里写图片描述

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

这里写图片描述

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

这里写图片描述

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

这里写图片描述

对称变换

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

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

x= x0
y= 

用矩阵表示就是:

这里写图片描述

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

x= x0
y= 

用矩阵表示就是:

这里写图片描述

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

这里写图片描述

那么,

这里写图片描述

很容易可以解得:

x= x0
y= 

用矩阵表示就是:

这里写图片描述

同样的道理,如果对称轴是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)
   
   
  • 1
  • 2

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

单位矩阵如下:

这里写图片描述

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

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

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

####rectStaysRect

public boolean rectStaysRect()
   
   
  • 1

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

####reset

public void reset()
   
   
  • 1

重置矩阵为单位矩阵。

####setTranslate

public void setTranslate(float dx, float dy)
   
   
  • 1

设置平移效果,参数分别是x,y上的平移量。
效果图如下:
这里写图片描述

代码:

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

matrix.setTranslate(100, 1000);
canvas.drawBitmap(bitmap, matrix, paint);
   
   
  • 1
  • 2
  • 3
  • 4
  • 5
setScale
public void setScale(float sx, float sy, float px, float py)
public void setScale(float sx, float sy)
   
   
  • 1
  • 2

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

setRotate
public void setRotate(float degrees, float px, float py)
public void setRotate(float degrees)
   
   
  • 1
  • 2

和上面类似,不再讲解。

setSinCos
public void setSinCos(float sinValue, float cosValue, float px, float py)
public void setSinCos(float sinValue, float cosValue)
   
   
  • 1
  • 2

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

这里写图片描述

其实旋转,就是使用了这样的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);
   
   
  • 1
  • 2
  • 3
setSkew
public void setSkew(float kx, float ky, float px, float py)
public void setSkew(float kx, float ky)
   
   
  • 1
  • 2

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

setConcat
public boolean setConcat(Matrix a,Matrix b)
   
   
  • 1

将当前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);
   
   
  • 1
  • 2
  • 3

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

这里写图片描述

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

这里写图片描述

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

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

前乘:
这里写图片描述

后乘:
这里写图片描述

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

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

其他方法

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

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

将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)

   
   
  • 1
  • 2

通过指定的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);
   
   
  • 1
  • 2
  • 3
  • 4
  • 5
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);
   
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

图片的中心点作为旋转的中心,前后不变,右上角变化到了下方,所以导致图片旋转了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);
   
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
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);
   
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

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

invert
public boolean invert(Matrix inverse)
   
   
  • 1

反转当前矩阵,如果能反转就返回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)
   
   
  • 1
  • 2
  • 3

映射点的值到指定的数组中,这个方法可以在矩阵变换以后,给出指定点的值。
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)
   
   
  • 1
  • 2
  • 3

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

mapRect
public boolean mapRect(RectF dst, RectF src)
public boolean mapRect(RectF rect)
   
   
  • 1
  • 2

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

mapRadius
public float mapRadius(float radius)
   
   
  • 1

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

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

http://blog.csdn.net/jdsjlzx/article/details/52741445

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值