Android Matrix ,Camera2横屏情况下画面方向错误问题

本文主要讲解了Android Matrix对于图像转换的数学原理,文尾会给出Camera2在横屏状态下,画面方向错误(旋转90°)的解决方案。

Matrix在android视图应用中非常常见,比如照相,自定义view等,若要学习其逻辑原理需要有矩阵的数学知识,最少要知道矩阵乘法的运算规则。

Matrix本质上是一个3×3的矩阵(本文会称呼其为变换矩阵),如下图,主要用来对坐标做变换映射。
Matrix.png

缩放

android中的矩阵转换通过上图的转换矩阵乘以原坐标获得新的坐标(矩阵不满足乘法交换律,必须是转换矩阵在前,坐标矩阵在后,否则的话转换矩阵需要修改,各个位置含义与上图不同)。

讲一个简单的例子就能看懂是如何转换的了:原坐标为x0,y0,变换矩阵为下图中左侧的矩阵,转换后坐标为x1,y1,我们看下三者之间的关系。
image.png

可以得出
image.png

可以看出x坐标变换为原先的k1倍,y坐标变为原先的k2倍。这也就是k1,k2位置为MSCALE_X,MSCALE_Y,的原因,这两个位置决定了坐标的缩放。
Matrix提供了两个关于缩放的方法

void setScale(float sx, float sy);
void setScale(float sx, float sy, float px, float py);

前面两个参数sx,sy分别是X轴和Y轴的缩放比例;后面两个参数px,py,则是用以确认以哪个点位中心进行缩放,若使用第一个方法,则相当于默认缩放点为坐标系原点(图片左上角)。

Matrix matrix = new Matrix()
matrix.setScale(2f, 2f, 100f, 100f);

经过上边的转换后变换矩阵则变为:
image.png

可以理解为缩放后又做了一次平移,上图的变换矩阵乘以原坐标,可得:
image.png

位移

其实位移的数学原理其实我们也已经了解了:

变换关系为:
image.png

矩阵表示为:
image.png

Matrix提供了一个位移方法

void setTranslate(float dx, float dy)

方法的参数很简单,X轴方向的位移以及Y轴方向的位移。唯一需要了解的是,android的坐标系和我们平时习惯的不同,它是以左上角为原点,向右为X轴正方向,向下为Y轴正方向。

错切

错切的矩阵转换为:
image.png

可以获得变换关系为:
image.png

一种x和y变换互相线性相关的赶脚。如下图所示:这是k1和k2均为0.2时的错切
错切前.png

错切后.png
void setSkew(float kx, float ky)
void setSkew(float kx, float ky, float px, float py)

错切的方法一共有两个,参数类似于缩放变换。

旋转

这个是本篇文章的重点,因为其很常用,且数学原理不是那么容易理解(其实认真一点的话,高中数学知识就够用了)。
image.png

如图所示,其实就是相当于求(x,y)点旋转θ度得到(x1,y1)。获得(x1,y1)、(x,y)、θ角之间的关系。

最终可得(推导过程先不详细证明,稍后贴上):
image.png

矩阵描述则为:
image.png
void setRotate(float degrees)
void setRotate(float degrees, float px, float py)

Matrix提供的接口倒是很容易理解,第一个参数degrees是旋转角度,px,py,则是以哪个点为原心进行旋转。

(补充)推导过程:
推导过程.png

Matrix最下面的三个位置

MPERSP_0、MPERSP_1、MPERSP_2,这三个数字在3D变换中有着至关重要的作用,但我目前还未做研究,且使用情况较少,先不做讨论,最后写完再做补充。

Matrix的复合变换

复合变换就是图形出现两种或者两种以上的变换,如果有较好的图象思维能力,这部分会非常好理解和实践的。
Matrix 的复合变换实际上就是矩阵相乘,原理很简单。但是要知道矩阵不满足交换律,所有变换矩阵的前后顺序不能随意改动。所以新的变换矩阵和旧的变换矩阵前后顺序的不同会影响图片最终的外观,也就是说在两次变换中,[A]×[B]×[x0,y0]和[B]×[A]×[x0,y0]得到的坐标是不同的。
但是由于矩阵相乘满足结合律,你实际上可以理解为靠近[x0,y0]的矩阵先进行变换:[A]×[B]×[x0,y0]相当于[A]×([B]×[x0,y0]),也就是原坐标先进行了B变换再进行A变换。
当然Matrix类也停供了前置矩阵和后置矩阵来进行复合变换的方法(忘了说了,我们之前介绍的变换方法全部以set为开头,比如setTranslate,这些方法会重置之前的矩阵,相当于直接覆盖原矩阵):

//缩放
boolean preScale(float sx, float sy);
boolean preScale(float sx, float sy, float px, float py);
boolean postScale(float sx, float sy);
boolean postScale(float sx, float sy, float px, float py);
//平移
boolean preTranslate(float dx, float dy);
boolean postTranslate(float dx, float dy);
//错切
boolean preSkew(float kx, float ky);
boolean preSkew(float kx, float ky, float px, float py);
boolean postSkew(float kx, float ky);
boolean postSkew(float kx, float ky, float px, float py);
//旋转
boolean preRotate(float degrees);
boolean preRotate(float degrees, float px, float py);
boolean postRotate(float degrees);
boolean postRotate(float degrees, float px, float py);

Camera2横屏情况下画面方向错误解决方案

private void transformImage() {
        Matrix matrix = new Matrix();
        int rotation = getWindowManager().getDefaultDisplay().getRotation();
        if (rotation == Surface.ROTATION_90 || rotation == Surface.ROTATION_270) {
            //当屏幕方向为横屏时,我们先旋转90°(以预览界面的中心点为旋转点哦!!!)
            matrix.postRotate(90 * (rotation - 2), mPreviewTexture.getWidth() / 2.0f, mPreviewTexture.getHeight() / 2.0f);
            //然后图象的宽度要变为原来的高度,高度要变为原来的宽度‘
            //(如下,进行互换比例,当然还是要以预览界面中心为原点进行伸缩)
            matrix.postScale(1f * mPreviewTexture.getWidth() / mPreviewTexture.getHeight(),
                    1f * mPreviewTexture.getHeight() / mPreviewTexture.getWidth(),
                    mPreviewTexture.getWidth() / 2.0f, mPreviewTexture.getHeight() / 2.0f);
           //设置变换矩阵
            mPreviewTexture.setTransform(matrix);
        }
    }

你可能感兴趣的:(Android Matrix ,Camera2横屏情况下画面方向错误问题)