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);
        }
    }
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,242评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,769评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,484评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,133评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,007评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,080评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,496评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,190评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,464评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,549评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,330评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,205评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,567评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,889评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,160评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,475评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,650评论 2 335

推荐阅读更多精彩内容

  • 我们在自定义 View 控件时随处可见 Matrix 的身影,主要用于坐标转换映射,我们可以通过 Matrix 矩...
    张可_阅读 24,527评论 5 35
  • 前言 最近翻阅关于从2D视频或者图片中重构3D姿态的文章及其源码,发现都有关于摄像机参数的求解,查找了相关资料,做...
    予汐阅读 6,011评论 0 3
  • 一、Matrix的作用 累加平移、旋转、缩放... ...变换,Android帮我们封装好了,不需要我们自己去计算...
    何荣虎阅读 427评论 0 0
  • 一、简介 Android android.graphics.Matrix 类是一个3 x 3的矩阵(方阵),上一张...
    SharpChen阅读 11,973评论 2 12
  • 写在前面:本文主要通过学习安卓自定义View进阶-Matrix原理这篇文章去学习Matrix 的原理,因此大部分文...
    梧叶已秋声阅读 982评论 0 0