#.概述:
1.如同人眼看东西分上下一样,摄像头也有其“正向”,正常情况下,Android手机后置、前置摄像头的“正向”朝向为手机的“右侧”(默认如此,除非手机厂商修改设置)。
(这里运行代码做过测试发现,前置摄像头也是以右侧为正向,而不是有些资料上说的左侧。)
摄像头直接返回的画面,都是以此方向为作为画面的上方向。若不加处理,直接存储到手机中或展示到View中,画面很可能不“正确”。这里的“正确”指界面存储/展示的图像方向,与摄像头拍摄时人肉眼看到的画面方向一致。
2.通过设置摄像头预览角度,可以让摄像头默认的输出画面顺时针旋转指定角度。根据前置、后置摄像头的基准应偏移角度,再考量上当前手机画面的旋转角度,可以计算出对应的摄像头预览角度,以便让摄像头输出画面“正确”。
##.摄像头的正向
(下面这段文字和图片摘取自Android开发中关于摄像头方向的理解 - 简书)
为什么输出的图像相比原始画面旋转了90度?因为设备的摄像头存在一个“正向角度”,什么是摄像头的正向?
通俗一点讲,设备相当于人的身体,眼睛相当于摄像头,眼睛把接收到的画面反馈给大脑处理,相当于摄像头把接收到的数据给应用程序处理。人眼能判断出我们头顶向上的方向是我们视觉上的正向,而后置摄像头判断的正向并不是手机物理屏幕向上的方向,而是物理屏幕右侧的方向。我们想象一下,如果人眼是这个摄像头,它认为右侧才是我们的视觉正向,那我们看到的东西是不是都是旋转90度的?这样就比较好理解了。
上图是手机在竖直和水平方向摄像头“看”到的画面。
##.手机旋转角度获取
/**
* 获取当前设备的旋转角度
* Android手机的旋转角度,指的是当前手机画面相对于手机画面"自然方向"的顺时针旋转角度。
* 当手机画面正上方指向手机头部时,手机画面处于"自然方向"。
* 手机画面旋转角度与手机的物理旋转角度正好相反。(这样才能保证用户当前的画面方向不变)
* 返回的值只有四种:Surface.ROTATION_0、Surface.ROTATION_90、Surface.ROTATION_180、Surface.ROTATION_270,
* 分别代表画面顺时针旋转0、90、180、270度。
*/
public static int getRotation(Activity activity){
if(activity == null){
return Surface.ROTATION_0;
}
return activity.getWindowManager().getDefaultDisplay().getRotation();
}
##.如何设置手机预览角度,让画面“正确”
###.1.Android支持对预览画面设置偏转角度,此时摄像头的输出的预览画面会从默认状态顺时针旋转指定角度再输出。
例如Camera1方案中对应的API为setDisplayOrientation(),不过只能设置0/90/180/270四个角度值。
(注意,这只是改变预览画面显示在View上的偏转角度,实际输出的画面角度并未变化,例如输出到OES纹理中的画面。当拍照时,默认存储的是摄像头直接输出的画面,如果要保证照片画面“正确”,需要自己对摄像头原始输出画面进行矩阵变换,做旋转或翻转等,然后再保存。)
###2.如果把画面“正确”定义为最终输出画面的存储/展示的图像方向,与人眼以当前手机“屏幕正向”角度看到的画面一致
那么根据前面的内容,可以得出结论,让输出画面“正确”,其实只需要将输出画面,偏转一定的角度就行了。
(可以这么来理解,若摄像头正向相对当前屏幕正向顺时针方向为W度,那么把默认画面直接拿去显示,等于把画面逆时针旋转了W度,于是预览画面若提前顺时针旋转W度,则画面正好“正确”。)
(下面2.1~2.3是过程分析与偏转角度计算公式的推导过程)
####2.1那么结论,预览画面应该的偏转方向
2.1.1后置摄像头:
顺时针偏转角度R = 摄像头“正向” 相对于 当前屏幕画面“正向”的偏转角度 = W
2.1.2前置摄像头,是从相反的方向来看,若不考虑最初画面的镜像翻转:
逆时针偏转角度R0 = 摄像头“正向” 相对于 当前屏幕画面“正向”的偏转角度 = W
顺时针偏转角度R1 = 360 - R0 = 360 - W
但android会自动将前置摄像头预览画面在其x轴方向做镜像翻转后再旋转,所以考虑到最初的镜像处理的影响:
最终的偏转角度R = 360 - R1
####2.2
手机屏幕画面画面的处于自然状态,指屏幕的“正向”为竖直向上时,记此时摄像头“正向” 相对于 当前屏幕画面“正向”的偏转角度为W0;
记录手机屏幕画面的偏转角度为degree(指相对于自然状态的顺时针偏转角度);
当手机逆时针旋转,degree增大,W减小;当手机顺时针旋转,degree减小,W增大。
所以:W = (W0 - degree)%360;
####2.3 将W代入到2.1的计算公式中,得出最终的计算公式
2.3.1对于后置摄像头:
R = W = (W0 - degree)%360
其中,degree为0时的R值可从API中获取到,为CameraInfo.orientation,故:
R = (CameraInfo.orientation - degree)%360
2.3.2 对于前置摄像头:
不考虑镜像:
R0 = W = (W0 - degree)%360
R1 = 360 - R0 = 360 - W = ( (360 - W0) + degree )%360
其中,degree为0时的R1值可从API中获取到,为CameraInfo.orientation,故:
R1 = 360 - R0 = 360 - W = (CameraInfo.orientation+ degree)%360
那么,考虑到镜像:
R = 360 - R1
附:根据上述公式可知,标准规则下,对于后置摄像头CameraInfo.orientation=90,偏转角度R=90;
对于前置摄像头CameraInfo.orientation=270,偏转角度R=90。
####2.4
使用CameraInfo.orientation有个好处,就算手机厂商把默认手机摄像头的正向修改了,就算不在手机的右侧,但只要从API中获取CameraInfo.orientation值是对应正确的,分别代表degree=0时的R/R1值,那么上述计算公式的结果依然是正确的。
##.计算的示例代码如下,代码本身很简单,较难理解的只是上面讲到的那个计算公式:
public void setDisplayOrientation(Activity activity) {
if(activity == null || mCamera == null || mCameraInfo == null){
return;
}
int rotation = activity.getWindowManager().getDefaultDisplay().getRotation();
int degrees = 0;
switch (rotation) {
case Surface.ROTATION_0:
degrees = 0;
break;
case Surface.ROTATION_90:
degrees = 90;
break;
case Surface.ROTATION_180:
degrees = 180;
break;
case Surface.ROTATION_270:
degrees = 270;
break;
}
int result;
if (mCameraInfo.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
result = (mCameraInfo.orientation + degrees) % 360;
result = (360 - result) % 360; // compensate the mirror
} else { // back-facing
result = (mCameraInfo.orientation - degrees + 360) % 360;
}
try {
mCamera.setDisplayOrientation(result);
} catch (Exception e) {
ILog.e(TAG, "setDisplayOrientation", e);
}
}
主要参考:Android开发中关于摄像头方向的理解 - 简书