1、概念解释。
“自然方向”:指当宽比高短时,我们看到的手机的方向(竖屏),就是自然方向。
2、相机图像传感器采集图像的方向。
由于手机Camera拍摄到的图片来自相机的图像传感器,而相机是固定到手机上的,所以相机的图像传感器的图像采集方向就固定住了。因此只能对采集后的图片数据进行旋转以便得到正确的预览。在自然方向进行前后摄像头拍照,未进行任何旋转得到的图片如下:
前摄像头:
后摄像头:
以上图片是在手机“自然方向”获取得,在手机屏幕其他方向时,该图片也会变化。
3、图像校正,进行预览旋转。
我们可以通过CameraInfo对象的Orientation属性获取图像传感器的图像采集方向,该属性是一个角度值,只能有0,90,180,270四个中的一个,这几个角度值是相对于手机的“自然方向”来说的。再直白点就是,相机所采集的图像只要按着该角度顺时针旋转就可以得到正确的预览图像数据。
然后通过Camera的setDisplayOrientation方法改变预览方向(顺时针旋转)。后置摄像头为90,前置摄像头的Orientation通常为270。所以,当是后置摄像头时,setDispayOrientation(90)即可将预览图像校正,及顺时针旋转90。而当是前置摄像头时,设置270就不行了,因为前置摄像头会在旋转预览方向之前,对预览方向来一个水平翻转,所以设置值应该是90,及顺时针旋转90。
4、图解演示旋转过程。
(1)手机自然方向如下:
(2)后置摄像头传感器采集的图像如下:
所以只要顺时针旋转90度即可。
(3)前置摄像头传感器采集的图像如下:
接下来进行旋转校正
5、根据相机情况与屏幕情况进行计算得到最终orientation,用于预览校正。
public void setCameraDisplayOrientationNew(Activity activity){
Camera.CameraInfo cameraInfo=new Camera.CameraInfo();
Camera.getCameraInfo(CameraId,cameraInfo);
//当前屏幕的方向,0为自然方向,1为顺时针旋转90,2为顺时针180,3为顺时针270
int rotation = activity.getWindowManager().getDefaultDisplay().getRotation();
int degree=0;//当前手机顺时针旋转的角度
switch (rotation){
case Surface.ROTATION_0:
degree=0;
break;
case Surface.ROTATION_90:
degree=90;
break;
case Surface.ROTATION_180:
degree=180;
break;
case Surface.ROTATION_270:
degree=270;
break;
}
int result;
if(cameraInfo.facing== Camera.CameraInfo.CAMERA_FACING_FRONT){//前置
//前置摄像头会进行水平镜像旋转(逆时针旋转180)
result= (cameraInfo.orientation + 180-degree)%360;
}else{
//后置摄像头无需水平翻转,直接是差值
result = (cameraInfo.orientation - degree +360 )%360;
}
mCamera.setDisplayOrientation(result);
}
6、上面的算法,也或许看不懂,没关系,接下来详细介绍一下用到的各个方向。
(1)、Activity的Rotation
在Activity中,提供了一个接口,可以获取当前手机的旋转角度。
getWindowManager().getDefaultDisplay().getRotation();
这个接口返回的值主要有如下4个:
Surface.ROTATION_0,
Surface.ROTATION_90,
Surface.ROTATION_180,
Surface.ROTATION_270
依次代表手机的方向如下:
对于Activity,默认的手机方向时:竖屏Home键在下面,这个方向是Activity的0度方向。
注意:它的角度规律是:“逆时针”方向旋转递增,每次递增90度。
(2)、传感器得到的屏幕方向
通过OrientationEventListener类,监听传感器给出的信息来确定屏幕的方向,这个方法得到的方向比较准确,而且多少度角度都可以得到,而不是固定0、90、180、270。及它不需要等待手机旋转了90度才会有反应,而是在手机旋转了任何角度的时候,都会回调onOrientationChanged告诉你当前手机的角度,当然,如果你只希望得到4个角度的变化事件,可以自己在该函数中做判断。调用函数如下:
mOrientationListener = new OrientationEventListener(this,
SensorManager.SENSOR_DELAY_NORMAL) {
@Override
public void onOrientationChanged(int orientation) {
Log.v("haha","Orientation changed to " + orientation);
}
};
if (mOrientationListener.canDetectOrientation()) {
Log.v(TAG, "Can detect orientation");
mOrientationListener.enable();
} else {
Log.v(TAG, "Cannot detect orientation");
mOrientationListener.disable();
}
它的值分别对应的手机方向如下:
对于Sensor,默认的手机方向是:竖屏Home键在下面,这个是Sensor的0度方向。
注意:它的角度递增规律是:“顺时针”方向旋转,每次递增90度。
(3)、相机的Preview方向
Camera通过setDisplayOrientation来设置预览角度,各值对应手机方向如下:
对于Camera,默认情况下,手机横屏,HOME键在右边,这个方向是Camera的0度方向。
注意:它的角度递增规律是:“顺时针”方向旋转,每次递增90度。
所以,对于不管是前置还是后置摄像头,对应屏幕上述的各个方向,设置预览角度就可以得到校正后的预览图像。
接下来具体分析一下:
摄像头方向 | 屏幕方向,顺时针 | rotation,逆时针 | cameraInfo.orientation | preview计算公式: 前:(cameraInfo.orientation + 180-rotation)%360 后:(cameraInfo.orientation - rotation +360 )%360 | preview值,顺时针 |
前摄像头 | 0(home键下侧) | 0 | 270 | (270+180+0)%360 | 90 |
90(home键左侧) | 270 | 270 | (270+180-270)%360 | 180 | |
180(home键上侧) | 180 | 270 | (270+180-180)%360 | 270 | |
270(home键右侧) | 90 | 270 | (270+180-90)%360 | 0 | |
后摄像头 | 0(home键下侧) | 0 | 90 | (90-0+360)%360 | 90 |
90(home键左侧) | 270 | 90 | (90-270+360)%360 | 180 | |
180(home键上侧) | 180 | 90 | (90-180+360)%360 | 270 | |
270(home键右侧) | 90 | 90 | (90-90+360)%360 | 0 |
通过上述给的算法公式获得preview与上述要求的相机的Preview方向正好对应,说明该公式正确。可以使相机预览方向正确。