图像的处理大致有两种:
- ColorMatrix:颜色矩阵,5x4的矩阵。
- Matrix:变换矩阵,3*3的矩阵。
本章主要讲解变换矩阵
。
(1)基本矩阵
[scale_x skew_x trans_x
skew_y scale_y trans_y
persp_0 persp_1 persp_2]
其中,scale处理缩放变换,skew处理错切变换,trans处理平移变换,persp处理透视变换。
初始矩阵如下:
[1 0 0
0 1 0
0 0 1]
(2)图像缩放
直接贴一下代码
Bitmap result = pool.get(toTransform.getWidth(), toTransform.getHeight(), Bitmap.Config.ARGB_8888);
if(result == null){
result = Bitmap.createBitmap(toTransform.getWidth(), toTransform.getHeight(), Bitmap.Config.ARGB_8888);
}
Canvas canvas = new Canvas(result);
// 初始化Matrix对象
Matrix matrix = new Matrix();
// 根据传入的参数设置缩放比例
matrix.setScale(scale_x, scale_y);
Paint paint = new Paint();
paint.setAntiAlias(true);
// 根据缩放比例,把图片draw到Canvas上
canvas.drawBitmap(toTransform, matrix,paint);
如果scale_x = 1,scale_y = 1,那么相当于没有缩放,原图效果如下:
当我们将scale_x 和scale_y 都设置成0.5时,也就是将原图缩小一半,如题所示:
满足以上效果,也可以直接设置矩阵
Bitmap result = pool.get(toTransform.getWidth(), toTransform.getHeight(), Bitmap.Config.ARGB_8888);
if(result == null){
result = Bitmap.createBitmap(toTransform.getWidth(), toTransform.getHeight(), Bitmap.Config.ARGB_8888);
}
Canvas canvas = new Canvas(result);
// 初始化Matrix对象
Matrix matrix = new Matrix();
// 根据传入的参数设置缩放比例
float[] values = {
0.5f, 0, 0,
0, 0.5f, 0,
0, 0, 1
};
matrix.setValues(values);
Paint paint = new Paint();
paint.setAntiAlias(true);
// 根据缩放比例,把图片draw到Canvas上
canvas.drawBitmap(toTransform, matrix,paint);
原理:
最终得出的图形旋转矩阵为:
[a 0 0
0 b 0
0 0 1]
a为x轴方向的缩放系数,b为y轴方向的缩放系数。
(2)平移操作
在当前画布上,x方向和y方向,各移动100距离。
Bitmap result = pool.get(toTransform.getWidth(), toTransform.getHeight(), Bitmap.Config.ARGB_8888);
if(result == null){
result = Bitmap.createBitmap(toTransform.getWidth(), toTransform.getHeight(), Bitmap.Config.ARGB_8888);
}
Canvas canvas = new Canvas(result);
// 初始化Matrix对象
Matrix matrix = new Matrix();
// 根据传入的参数设置缩放比例
matrix.setTranslate(100, 100);
Paint paint = new Paint();
paint.setAntiAlias(true);
// 根据缩放比例,把图片draw到Canvas上
canvas.drawBitmap(toTransform, matrix,paint);
效果如下:
使用矩阵数组,我们将图片缩小一倍之后再移动100距离。
Bitmap result = pool.get(toTransform.getWidth(), toTransform.getHeight(), Bitmap.Config.ARGB_8888);
if(result == null){
result = Bitmap.createBitmap(toTransform.getWidth(), toTransform.getHeight(), Bitmap.Config.ARGB_8888);
}
Canvas canvas = new Canvas(result);
// 初始化Matrix对象
Matrix matrix = new Matrix();
// 根据传入的参数设置缩放比例
float[] values = {
0.5f, 0, 100,
0, 0.5f, 100,
0, 0, 1
};
matrix.setValues(values);
Paint paint = new Paint();
paint.setAntiAlias(true);
// 根据缩放比例,把图片draw到Canvas上
canvas.drawBitmap(toTransform, matrix,paint);
效果如下:
原理:
最终得出的图形旋转矩阵为:
[1 0 a
0 1 b
0 0 1]
a为x轴方向的平移,b为y轴方向的平移。
(3)错切操作
定义:
错切是在某方向上,按照一定的比例对图形的每个点到某条平行于该方向的直线的有向距离做放缩得到的平面图形。
Matrix中有关错切的两个方法如下:
setSkew(float m, float n)
setSkew(float m, float n, float px, float py)
m为x轴上的错切因子,n为y轴上的错切因子,当前错切操作的错切坐标轴中心点为(px,py),如果没有指定错切坐标轴中心点,那么默认为(0,0)。
如图所示,我们将图片按照坐标轴分成1,2,3,4几个部分,1和2为x轴的负方向,3和4为x轴的正方向,1和3为y轴的负方向,2和4为y轴的正方向。
假设错切因子是m、n,图片上所有的点用(x,y)表示,那么错切操作之后的点为
(x + my, y + nx)
也就是说,图片上的任一点都有一个从
(x,y)-->(x + my, y + nx)
转变的过程。
当m=0时,转变过程是
(x,y)-->(x , y + nx)
当n=0时,转变过程是
(x,y)-->(x + my, y)
所以图片不管怎么错切,x轴和y轴的点都不会变化。
执行以下代码
matrix.setSkew(0.1f, 0.1f, toTransform.getWidth()/2, toTransform.getHeight()/2);
错切后的效果如下:
使用矩阵数组的代码如下(错切坐标轴的中心点是图片的左上角的点):
Bitmap result = pool.get(toTransform.getWidth(), toTransform.getHeight(), Bitmap.Config.ARGB_8888);
if(result == null){
result = Bitmap.createBitmap(toTransform.getWidth(), toTransform.getHeight(), Bitmap.Config.ARGB_8888);
}
Canvas canvas = new Canvas(result);
// 初始化Matrix对象
Matrix matrix = new Matrix();
// 根据传入的参数设置缩放比例
float[] values = {
1, 0.1f, 0,
0.1f, 1, 0,
0, 0, 1
};
matrix.setValues(values);
Paint paint = new Paint();
paint.setColor(Color.BLUE);
paint.setStrokeWidth(5);
paint.setAntiAlias(true);
// 根据缩放比例,把图片draw到Canvas上
canvas.drawBitmap(toTransform, matrix,paint);
canvas.drawLine(0, toTransform.getHeight()/2, toTransform.getWidth(), toTransform.getHeight()/2, paint);
canvas.drawLine(toTransform.getWidth()/2, 0, toTransform.getWidth()/2, toTransform.getHeight(), paint);
原理:
其中m为x轴方向的错切因子,n为y轴方向的错切因子。
(4)旋转操作
假设中心点为图片的中央,在照中心点旋转顺时针旋转30度。
matrix.setRotate(30, toTransform.getWidth()/2, toTransform.getHeight()/2);
假设中心点为图片的中央,在中心点逆时针旋转30度。
matrix.setRotate(-30, toTransform.getWidth()/2, toTransform.getHeight()/2);
如图所示
顺时针
逆时针
旋转原理:
其中,θ表示图形旋转的角度,最终得出的图形旋转矩阵为:
[cosθ -sinθ 0
sinθ cosθ 0
0 0 1]
(5)逆矩阵操作
定义:
在线性代数中,给定一个 n 阶方阵方形矩阵 A,若存在一 n 阶方阵B,使得 AB=BA=In,其中In为 n阶单位矩阵,则称A是可逆的,且B 是A 的逆矩阵,记作A-1。
Android的Matrix也有对逆矩阵的封装
matrix.invert(matrix)
invert
将矩阵逆转,假设逆转前的矩阵记作A,逆转后的矩阵记作B,单位矩阵记作*C,那么
C = A * B
或者
C = B * A
A和B互为可逆。
举例1:
平移操作的逆矩阵
向x轴方向移动100, 向y轴方向移动100
[1 0 100
0 1 100
0 0 1]
它的逆矩阵是
[1 0 -100
0 1 -100
0 0 1]
举例2:
缩放操作的逆矩阵
[0.5 0 0
0 2 0
0 0 1]
它的逆矩阵是
[2 0 0
0 0.5 0
0 0 1]
不举例了,总之两个互逆矩阵相乘为单位矩阵。(记住这个就可以了)
(6)isAffine
Android5.0新增接口,判断是否是仿射矩阵。
什么是仿射矩阵?
以上讲到的平移,缩放、旋转、错切、逆转都是仿射矩阵,仿射矩阵最明显的特征就是3x3居中的第三行的数值都是[0 0 1]。
(7)isIdentity
判断是否为单位矩阵。
(8)reset
将矩阵重置为单位矩阵。
(9)前乘变换
preConcat、preRotate、preScale、preSkew、preTranslate
pre
称之为前乘,也可以称之为右乘。
float[] A = {
1, 0, 0,
0, 1, 0,
0, 0, 1
};
matrix.setValues(A);
matrix.preConcat(B);
以上代码其实就是A * B,B右乘A。(当然,也可以说A左乘B)
(10)后乘变换
postConcat、postRotate、postScale、postSkew、postTranslate
Matrix matrix = new Matrix();
// 根据传入的参数设置缩放比例
float[] A = {
1, 0, 0,
0, 1, 0,
0, 0, 1
};
matrix.setValues(A);
matrix.postConcat(B);
以上代码其实就是B * A,B左乘A。(当然,也可以说A右乘B)
(11)rectStaysRect
判断该矩阵是否可以将一个矩形依然变换为一个矩形。当矩阵是单位矩阵,或者只进行平移,缩放,以及旋转90度的倍数的时候,返回true。
(12)setRectToRect
setRectToRect(RectF src, RectF dst, ScaleToFit stf)
将rect变换成rect。
ScaleToFit 有如下四个值:
FILL:
可能会变换矩形的长宽比,保证变换和目标矩阵长宽一致。
START:
保持坐标变换前矩形的长宽比,并最大限度的填充变换后的矩形。至少有一边和目标矩形重叠。左上对齐。
CENTER:
保持坐标变换前矩形的长宽比,并最大限度的填充变换后的矩形。至少有一边和目标矩形重叠。
END:
保持坐标变换前矩形的长宽比,并最大限度的填充变换后的矩形。至少有一边和目标矩形重叠。右下对齐。
(13)setSinCos
setSinCos(float sinValue, float cosValue, float px, float py)
setSinCos(float sinValue, float cosValue)
这个常用于矩阵的旋转操作,之前我们讲过,图片旋转之后的矩阵为:
[cosθ -sinθ 0
sinθ cosθ 0
0 0 1]
假如需要旋转90度
matrix.setRotate(90);
或者
//sin90 = 1,cos90 = 0
matrix.setSinCos(1, 0);
以上两种写法都是可以的。
(14)setPolyToPoly
setPolyToPoly(float[] src, int srcIndex, float[] dst, int dstIndex, int pointCount)
src:
原始点
srcIndex:
原始点的索引
dst:
目标点
dstIndex:
目标点的索引
pointCount:
点的数量取值范围是[0, 4]
- 当pointCount为0时,则设置无效。
- 当pointCount为1时,则控制一个点,可以将一个点平移到另一个点,相当于平移操作。
float[] src = {0, 0};
float[] dst = {100, 100};
matrix.setPolyToPoly(src, 0, dst, 0 ,1);
- 当pointCount为2时,则控制两个点,两个点可以控制旋转和缩放操作,即以一点为中心点,将图片上的一点,移动到另一点。
如上图所示,绿色线条为直角坐标,红色点分别是A、B、O。
现在要求将图片按照O旋转,从点A旋转到B,代码如下:
float[] src = {toTransform.getWidth() / 2, toTransform.getHeight() / 2, toTransform.getWidth(), 0};
float[] dst = {toTransform.getWidth() / 2, toTransform.getHeight() / 2, toTransform.getWidth() / 2, toTransform.getHeight()};
matrix.setPolyToPoly(src, 0, dst, 0 ,2);
效果如下:
我们已经将图片上的A点移动到了B点,完全符合预想中的效果。(旋转且缩放)
那么,再举一个例子,将B点移动到A点,O为中心点。
代码如下:
float[] src = {toTransform.getWidth() / 2, 0, toTransform.getWidth(), 0};
float[] dst = {toTransform.getWidth() / 2, 0, toTransform.getWidth() * 3 / 4, 0};
matrix.setPolyToPoly(src, 0, dst, 0 ,2);
效果如下:
-
当pointCount为3时,则控制三个点,三个点可以控制错切操作,即指定图片上的三点,一点固定,另外两点移动。
float[] src = {0, 0, 0, toTransform.getHeight(), toTransform.getWidth(), toTransform.getHeight()}; float[] dst = {0, 0, 200, toTransform.getHeight(), toTransform.getWidth() + 100, toTransform.getHeight()}; matrix.setPolyToPoly(src, 0, dst, 0 ,3);
效果如下:
这个比较好理解,固定一点,其他两点移动。数组里面有三个点,第一个点为固定点,后面两个点为移动点。
- 当pointCount为4时,则控制四个点,四个点可以实现透视效果。
矩阵变换有两种:
仿射变换:
前面所说的平移,缩放、旋转、错切、逆转都是仿射矩阵,仿射矩阵最明显的特征就是3x3居中的第三行的数值都是[0 0 1]。(2D效果)
透视变换:
可以展示3D效果的透视图(3D效果)(影映射矩阵)
在PS中,如果图片不是很正,那么可以使用透视裁剪工具将图片矫正。
上图就是矫正前的图片。
在Android中,将正图转换成以上透视图就是从2D转3D的过程,这个过程叫做透视变换。
代码如下
float[] src = {0, 0, 0, toTransform.getHeight(), toTransform.getWidth(), toTransform.getHeight(), toTransform.getWidth(), 0};
int offset = 100;
float[] dst = {0 + offset, 0, 0, toTransform.getHeight(), toTransform.getWidth(), toTransform.getHeight(), toTransform.getWidth()-offset, 0};
matrix.setPolyToPoly(src, 0, dst, 0 ,4);
选定图四角上的四点,将上面两个点缩小,效果如下:
(15)mapPoints
mapPoints(float[] dst, int dstIndex, float[] src, int srcIndex,int pointCount)
mapPoints(float[] dst, float[] src)
mapPoints(float[] pts)
映射点的值到指定的数组中,这个方法可以在矩阵变换以后,给出指定点的值。
dst:指定写入的数组
dstIndex:写入的起始索引,x,y两个坐标算作一对,索引的单位是对,也就是经过两个值才加1
src:指定要计算的点
srcIndex:要计算的点的索引
pointCount:需要计算的点的个数,每个点有两个值,x和y。
(16)mapRect
mapRect(RectF dst, RectF src)
mapRect(RectF rect)
返回值即是调用的rectStaysRect()。
(17)mapRadius
mapRadius(float radius)
返回一个圆圈半径的平均值,将matrix作用于一个指定radius半径的圆,随后返回的平均半径。
(18)mapVectors
mapVectors(float[] dst, int dstIndex, float[] src, int srcIndex,int vectorCount)
mapVectors(float[] dst, float[] src)
mapVectors(float[] vecs)
与上面的mapPoionts基本类似,这里是将一个矩阵作用于一个向量,由于向量的平移前后是相等的,所以这个方法不会对translate相关的方法产生反应,如果只是调用了translate相关的方法,那么得到的值和原本的一致。
[本章完...]