美文网首页Android开发Android开发经验谈Android技术知识
Android开发之图像处理那点事——变换

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

作者: 李晨玮 | 来源:发表于2018-11-21 23:16 被阅读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

    相关文章

      网友评论

        本文标题:Android开发之图像处理那点事——变换

        本文链接:https://www.haomeiwen.com/subject/pkuzfqtx.html