Android开发Android开发经验谈Android技术知识

Android开发之图像处理那点事——变换

2018-11-21  本文已影响238人  李晨玮

继上一篇《Android开发之图像处理那点事——滤镜》,这次我们来讲一下图片的变换操作,本篇主要是介绍常见的图像变换4种操作(平移、缩放、旋转、错切)

对于图像的颜色处理,Andriod系统给我们提供一个很方便的颜色矩阵类ColorMatrix,同样的,Android系统也给图像的变换处理提供了矩阵类Matrix,相比颜色矩阵的4 * 5,变换矩阵来得更加简单,它是由一个3 * 3的数字矩阵组成,我们来了解一下变换原理。

变换矩阵

在上一篇文章中,我们讲到像素点的颜色是由RGBA1组成的,分别代表红、绿、蓝、透明度各通道值还有颜色偏移量,在变换矩阵中也是类似的,像素点的位置是由矩阵C(X、Y、L)组成的,分别代表像素点在X、Y轴的位置还有偏移量。

变换矩阵

根据矩阵的乘法,我们可以得出下面的等式:

X1 = a * X + b * Y + c
Y1 = d * X + e * Y + f
L  = g * X + h * Y + i

当a=1,b和c=0,可以知道X1=1X+0+0=1X,
当e=1,d和f=0,可以知道Y1=0+1Y+0=1Y
当i=1,g和h=0,可以知道1=0+0+1=1
由此我们可以得到变换矩阵的单位矩阵:

单位矩阵

在日常开发中,我们涉及到的图像变换大致有平移、缩放、旋转、错切(很少),这些操作就是对这3 * 3的矩阵做数值上的调整,和上一篇讲颜色矩阵变化是类似的,不清楚的朋友,具体可以参考上一篇文章的讲解,下面我们快速过一下流程。

平移操作

像素点的平移其实就是对像素点的x和y坐标进行移动,如下图,从点p1移动到点p,假设p1(x1,y1)分别平移了x0和y0距离得到p(x,y),

平移操作

得到的公式就是:

x = x1 + x0
y = y1 + y0

矩阵所表现出来的形式:

平移操作

缩放操作

对于单个像素点是不存在缩放操作的,但如果是一系列的像素点,我们就可以根据x和y轴做一定比例的缩放,假设缩放比例为K1,得到的公式是:

x = K1 * x0
y = k1 * y0

矩阵所表现出来的形式:

缩放操作

旋转操作

像素点的旋转是围绕一个点旋转一定的角度得到新的像素点位置,为了便于理解,这里带上一张图:

旋转操作

如上图以原点为中心旋转,点(x0,y0)旋转了β°角度到了p(x,y),假设斜边为r,根据三角函数我们可以知道:

x0 = r * cosα
y0 = r * sinα

那么x和y就是x0和y0旋转了α角度后,再旋转了β角度,根据三角函数可以推出:

x = r * cos(α + β) = r * cosα * cosβ - r * sinα * sinβ = x0 * cosβ - y0 * sinβ
y = r * sin(α + β) = r * sinα * cosβ + r * cosα * sinβ =  y0 * cosβ + x0 * sinβ

将上面的式子代入,根据推导公式,我们可以知道矩阵的表现形式为:

旋转操作

错切操作

这个平时用的比较少,大概了解一下就可以,错切分为水平错切和垂直错切。

水平错切效果就是让所有像素点的Y轴坐标不变,X轴坐标按照比例进行平移,且平移的大小与该点到Y轴的距离成成正比,计算公式为:

x = x0 + k1 * y0
y = y0

矩阵变现形式为:

水平错切

垂直错切让所有像素点的X轴坐标不变,Y轴坐标按照比例进行平移,且平移的大小与该点到X轴的距离成成正比,计算公式为:

x = x0 
y = y0+ k2 * x0

矩阵变现形式为:

垂直错切

两个方向都错切,公式为:

x = x0 + k1 * y0
y = k2 * x0 * y0
水平/垂直错切

根据以上的矩阵表现方式,我们可以推出图像的变换规律为:

矩阵变换规律

Matrix

了解了各种变换原理后,我们来看下Matrix类,这是Android系统给我们提供的图像变换矩阵类,打开源码,我们可以看到这9个常量:

    public static final int MSCALE_X = 0;   //!< use with getValues/setValues
    public static final int MSKEW_X  = 1;   //!< use with getValues/setValues
    public static final int MTRANS_X = 2;   //!< use with getValues/setValues
    public static final int MSKEW_Y  = 3;   //!< use with getValues/setValues
    public static final int MSCALE_Y = 4;   //!< use with getValues/setValues
    public static final int MTRANS_Y = 5;   //!< use with getValues/setValues
    public static final int MPERSP_0 = 6;   //!< use with getValues/setValues
    public static final int MPERSP_1 = 7;   //!< use with getValues/setValues
    public static final int MPERSP_2 = 8;   //!< use with getValues/setValues

我们把它按3 * 3的矩阵形式排开,可以得到:

矩阵

对比下刚才上面我们推导的矩阵表现形式,怎么样,是不是更有感觉了,我们可以得到如下信息:
MTRANS_X、MTRANS_Y 决定了平移(Translate)
MSCALE_X、MSCALE_Y 决定了缩放( Scale)
MSCALE_X、MSKEW_X、MSCALE_Y、MSKEW_Y 决定了旋转( Rotate)
MSKEW_X、MSKEW_Y 决定了错切( Skew)
其实图像的变换操作无非就是对这个3 * 3的矩阵进行值的变化,而Matrix类只是帮我们封装好了这些操作,让我们无需关心细节,更加专注业务的实现,我们可以在Matrix中找到各操作对应的调用方法:

矩阵的操作方法

简单的看个小Demo:

上面分别对图像进行了平移、缩放、旋转、错切操作,贴一下核心代码:

            Matrix matrix = new Matrix();
            matrix.setTranslate(50, 50);//x,y坐标平移50像素
            canvas.drawBitmap(mBitmap, matrix, null);
            Matrix matrix = new Matrix();
            matrix.setScale(2, 2);//x,y坐标放大原来2倍
            canvas.drawBitmap(mBitmap, matrix, null);
            Matrix matrix = new Matrix();
            matrix.setRotate(20);//x,y坐标旋转20度
            canvas.drawBitmap(mBitmap, matrix, null);
            Matrix matrix = new Matrix();
            matrix.setSkew(2, 2);//x,y坐标按比例错切平移2
            canvas.drawBitmap(mBitmap, matrix, null);

当然这些操作也是可以组合起来的,Matrix里提供了一系列的postXXX,preXXX方法:

矩阵的操作方法

其实就是线性代数中矩阵的左乘和右乘,由于矩阵的乘法是不满足乘法交换律的,所以变换操作的执行顺序是对结果有影响的,继续来个小Demo:

先平移,再放大,再旋转

实现代码:

            Matrix matrix = new Matrix();
            matrix.setTranslate(200, 200);
            matrix.postScale(2, 2);
            matrix.postRotate(20);
            canvas.drawBitmap(mBitmap, matrix, null);
先平移,再旋转,再放大

实现代码:

            Matrix matrix = new Matrix();
            matrix.setTranslate(200, 200);
            matrix.preScale(2, 2);
            matrix.preRotate(20);
            canvas.drawBitmap(mBitmap, matrix, null);

简单粗暴可以这样理解:Matrix操作是一系列的任务存放在一个队列里,pre是把当前任务插入队列前,post是插入队列后,set则是清空队列所有任务,再入队。

这些看似很简单很基础的东西却恰恰是最重要的,它可以创造出很多东西,比如图片的自由手势缩放,微博上面的自定义贴纸,美图秀秀里的相框拼接等效果都离不开它。

源码下载:

这里附上源码地址(欢迎Star,欢迎Fork):BeautyImageDemo

上一篇下一篇

猜你喜欢

热点阅读