本文来源:Android Camera1 教程 · 第二章 · 预览第6部分。其镜面处理部分,描述错误。旋转270或90度,效果相同,并不能解决镜面问题。
如果没有做任何画面方向的校正,我们看到的画面很可能是横向的,这是因为手机上的摄像头传感器方向不一定是垂直的。在做预览画面方向的校正之前我们先来了解五个概念,分别是自然方向、设备方向、局部坐标系、屏幕方向和摄像头传感器方向。
自然方向
当我们谈论方向的时候,实际上都是相对于某一个 0° 方向的角度,这个 0° 方向被称作自然方向,例如人站立的时候就是自然方向,你总不会认为一个人要倒立的时候才是自然方向吧,而接下来我们要谈论的设备方向就有的自然方向的定义。
设备方向
设备方向指的是硬件设备在空间中的方向与其自然方向的顺时针夹角。这里提到的自然方向指的就是我们手持一个设备的时候最习惯的方向,比如手机我们习惯竖着拿,而平板我们则习惯横着拿,所以通常情况下手机的自然方向就是竖着的时候,平板的自然方向就是横着的时候。
以手机为例,我们可以有以下四个比较常见的设备方向:
- 当我们把手机垂直放置且屏幕朝向我们的时候,设备方向为 0°,即设备自然方向
- 当我们把手机向右横放且屏幕朝向我们的时候,设备方向为 90°
- 当我们把手机倒着放置且屏幕朝向我们的时候,设备方向为 180°
- 当我们把手机向左横放且屏幕朝向我们的时候,设备方向为 270°
了解了设备方向的概念之后,我们可以通过 OrientationEventListener 监听设备的方向,进而判断设备当前是否处于自然方向,当设备的方向发生变化的时候会回调 OrientationEventListener.onOrientationChanged(int) 方法,传给我们一个 0° 到 359° 的方向值,其中 0° 就代表设备处于自然方向。
局部坐标系
所谓的局部坐标系指的是当设备处于自然方向时,相对于设备屏幕的坐标系,该坐标系是固定不变的,不会因为设备方向的变化而改变,下图是基于手机的局部坐标系示意图:
- x 轴是当手机处于自然方向时,和手机屏幕平行且指向右边的坐标轴。
- y 轴是当手机处于自然方向时,和手机屏幕平行且指向上方的坐标轴。
- z 轴是当手机处于自然方向时,和手机屏幕垂直且指向屏幕外面的坐标轴。
为了进一步解释【坐标系是固定不变的,不会因为设备方向的变化而改变】的概念,这里举个例子,当我们把手机向右横放且屏幕朝向我们的时候,此时设备方向为 90°,局部坐标系相对于手机屏幕是保持不变的,所以 y 轴正方向指向右边,x 轴正方向指向下方,z 轴正方向还是指向屏幕外面,如下图所示:
屏幕方向
屏幕方向指的是屏幕上显示画面与局部坐标系 y 轴的顺时针夹角,注意这里实际上指的是显示的画面,而不是物理硬件上的屏幕,只是我们习惯上称作屏幕方向而已。
为了更清楚的说明这个概念,我们举一个例子,假设我们将手机向右横放看电影,此时画面是朝上的,如下图所示:
从上图来看,手机向右横放会导致设备方向变成了 90°,但是屏幕方向却是 270°,因为它是相对局部坐标系 y 轴的顺时针夹角,所以跟设备方向没有任何关系。如果把图中的设备换成是平板,结果就不一样了,因为平板横放的时候就是它的设备自然方向,y 轴朝上,屏幕画面显示的方向和 y 轴的夹角是 0°,设备方向也是 0°。
总结一下,设备方向和屏幕方向之间没有任何关系,设备方向是相对于其现实空间中自然方向的角度,而屏幕方向是相对局部坐标系的角度。
摄像头传感器方向
摄像头传感器方向指的是传感器采集到的画面方向经过顺时针旋转多少度之后才能和局部坐标系的 y 轴正方向一致,Camera.CameraInfo.orientation 属性。
例如 orientation 为 90° 时,意味我们将摄像头采集到的画面顺时针旋转 90° 之后,画面的方向就和局部坐标系的 y 轴正方向一致,换个说法就是原始画面的方向和 y 轴的夹角是逆时针 90°。
画面方向校正
介绍完几个方向的概念之后,我们就来说下如何校正相机的预览画面。我们会举几个例子,由简到繁逐步说明预览画面校正过程中要注意的事项。
首先我们要知道的是摄像头传感器方向只有 0°、90°、180°、270° 四个可选值,并且这些值是相对于局部坐标系 的 y 轴定义出来的,现在假设一个相机 APP 的画面在手机上是竖屏显示,也就是屏幕方向是 0° ,并且假设摄像头传感器的方向是 90°,如果我们没有校正画面的话,则显示的画面如下图所示(忽略画面变形):
很明显,上面显示的画面内容方向是错误的,里面的人物应该是垂直向上显示才对,所以我们应该吧摄像头采集到的画面顺时针旋转 90°,才能得到正确的显示结果,如下图所示:
上面的例子是建立在我们的屏幕方向是 0° 的时候,如果我们要求屏幕方向是 90°,也就是手机向左横放的时候画面才是正的,并且假设摄像头传感器的方向还是 90°,如果我们没有校正画面的话,则显示的画面如下图所示(忽略画面变形):
此时,我们知道传感器的方向是 90°,如果我们将传感器采集到的画面顺时针旋转 90° 显然是无法得到正确的画面,因为它是相对于局部坐标系 y 轴的角度,而不是实际屏幕方向,所以在做画面校正的时候我们还要把实际屏幕方向也考虑进去,这里实际屏幕方向是 90°,所以我们应该把传感器采集到的画面顺时针旋转 180°(摄像头传感器方向 + 实际屏幕方向) 才能得到正确的画面,显示的画面如下图所示(忽略画面变形):
总结一下,在校正画面方向的时候要同时考虑两个因素,即摄像头传感器方向和屏幕方向。接下来我们要回到我们的相机应用里,看看通过代码是如何实现预览画面方向校正的。