Matrix ,中文里叫矩阵,高等数学里有介绍。Android中的Matrix类是一个3x3的位置坐标矩阵,在图像处理方面,主要是用于平面的缩放、平移、旋转等操作。
首先了解下这个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
构造函数有两个,第一个是直接创建一个单位矩阵,第二个是根据提供的矩阵创建一个新的矩阵(采用deep copy)
单位矩阵如下:
是否是单位矩阵很简单,就不做讲解了,这里是否是仿射矩阵可能大家不好理解。
首先来看看什么是仿射变换。仿射变换其实就是二维坐标到二维坐标的线性变换,保持二维图形的“平直性”(即变换后直线还是直线不会打弯,圆弧还是圆弧)和“平行性”(指保持二维图形间的相对位置关系不变,平行线还是平行线,而直线上点的位置顺序不变),可以通过一系列的原子变换的复合来实现,原子变换就包括:平移、缩放、翻转、旋转和错切。这里除了透视可以改变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); 12345两个方法都是设置缩放到matrix中,sx,sy代表了缩放的倍数,px,py代表缩放的中心。这里跟上面比较类似不做讲解了。
和上面类似,不再讲解。
这个方法乍一看可能有点蒙,其实在前面的原理中,我们讲解了一个旋转的例子,他最终的矩阵效果是这样的:
其实旋转,就是使用了这样的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); 123错切,这里kx,ky分别代表了x,y上的错切因子,px,py代表了错切的中心。不了解错切了在前面canvas变换中去查看,这里不再讲解。
将当前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); 123这里matrix前乘了一个scale矩阵,换算成数学式如下:
从上面可以看出,最终得出的matrix既包含了缩放信息也有平移信息。 后乘自然就是matrix在后面,而缩放矩阵在前面,由于矩阵前后乘并不等价,也就导致了他们的效果不同。我们来看看后乘的结果:
可以看到,结果跟上面不同,并且这也不是我们想要的结果,这里缩放没有更改,但是平移被减半了,换句话说,平移的距离也被缩放了。所以需要注意前后乘法的关系。
来看看他们对应的效果图:
前乘:
后乘:
可以明显看到,后乘的平移距离受了影响。
了解清除了前后乘的意义,在使用的过程中,多个效果的叠加时,一样要注意,否则效果达不到预期。
matrix除了上面的方法外,还有一些其他的方法。
将rect变换成rect,上面的rectStaysRect已经说过,要保持rect只能做缩放平移和选择90度的倍数,那么这里其实也是一样,只是这几种变化,这里通过stf参数来控制。
ScaleToFit 有如下四个值:
FILL: 可能会变换矩形的长宽比,保证变换和目标矩阵长宽一致。 START:保持坐标变换前矩形的长宽比,并最大限度的填充变换后的矩形。至少有一边和目标矩形重叠。左上对齐。 CENTER: 保持坐标变换前矩形的长宽比,并最大限度的填充变换后的矩形。至少有一边和目标矩形重叠。 END:保持坐标变换前矩形的长宽比,并最大限度的填充变换后的矩形。至少有一边和目标矩形重叠。右下对齐。这里使用谷歌的api demo的图片作为例子:
通过指定的0-4个点,原始坐标以及变化后的坐标,来得到一个变换矩阵。如果指定0个点则没有效果。
下面通过例子分别说明1到4个点的可以达到的效果:
只指定一个点,可以达到平移效果:
代码如下:
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); 12345两个点,可以达到旋转效果或者缩放效果,缩放比较简单,这里我们来看旋转效果,一个点指定中心,一点指出旋转的效果。
代码:
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); 123456图片的中心点作为旋转的中心,前后不变,右上角变化到了下方,所以导致图片旋转了90度。
使用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); 1234567透视就是观察的角度变化了。导致投射到平面上的二维图像变化了。
我们看下面的例子,更容易理解:
图片看起来好像倾斜了,实现特别简单:
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); 12345678可以看到,只是把左右两个顶点往里面收拢了,这样就得出了一个有3d效果的透视图。
反转当前矩阵,如果能反转就返回true并将反转后的值写入inverse,否则返回false。当前矩阵*inverse=单位矩阵。
反转前后有什么效果,我们来看看示例:
可以看到,反转之后,其实是对效果的一种反转。
映射点的值到指定的数组中,这个方法可以在矩阵变换以后,给出指定点的值。 dst:指定写入的数组 dstIndex:写入的起始索引,x,y两个坐标算作一对,索引的单位是对,也就是经过两个值才加1 src:指定要计算的点 srcIndex:要计算的点的索引 pointCount:需要计算的点的个数,每个点有两个值,x和y。
与上面的mapPoionts基本类似,这里是将一个矩阵作用于一个向量,由于向量的平移前后是相等的,所以这个方法不会对translate相关的方法产生反应,如果只是调用了translate相关的方法,那么得到的值和原本的一致。
返回值即是调用的rectStaysRect(),这个方法前面有讲过,这里把src中指定的矩形的左上角和右下角的两个点的坐标,写入dst中。
返回一个圆圈半径的平均值,将matrix作用于一个指定radius半径的圆,随后返回的平均半径。
以上基本解析完毕了所有matrix的方法,以及一些高阶用法,本篇文章就到这里
http://blog.csdn.net/jdsjlzx/article/details/52741445