Camera 图像传感器取景方向 预览方向

手机摄像头(Camera)的图像数据来源于摄像头硬件的图像传感器,这个图像传感器被固定到手机上后会有一个默认的取景方向,这个取景方向恰好是当手机向左侧横放时的方向,其坐标原点位于手机横放时的左上角。手机的正常方向和图像传感器默认取景方向示意图如下:


image.png
  1. Camera预览方向处理
    从上面的示意图可知,图像传感器的取景方向与手机正常方向成90度夹角,按理来说,当我们以正常的手机方向打开相机(Camera)时,看到的预览图像应该是横向的。但是,当我们打开系统相机后,看到的预览图像却是正常的,即预览图像与手机方向一致。这是因为系统自带的相机在Android系统底层根据当前手机屏幕的方向对图像传感器采集到的数据进行了旋转,所以无论我们怎么旋转手机屏幕,看到的相机预览图片始终是”正常”的。而对于自定义的相机,如果没有对图像传感器采集的图片进行旋转处理,那么看到的预览图片就是横向的,效果如下图所示:


    image.png

    为了解决自定义相机预览方向不正常情况,Android系统提供了一个API来手动设置Camera的预览方向,即Camera.setDisplayOrientation(int rotateDegree),默认情况下该方法的值为0,与图像传感器取景方向一致。旋转方法:

首先,通过Display的getOrientation()获得当前手机的方向,如Surface.ROTATION_0表示手机竖屏时正常方向、Surface.ROTATION_90表示手机方向向右手边横向放置等(沿顺时针判断)。其中,Display display = getWindowManager().getDefaultDisplay()获得。
其次,对于后置摄像头来说,它的预览成像为CameraInfo.orientatio- phoneDegree,但由于这个值可能为负,角度值不能为负故需要加上360求正;对于前置摄像头(front camera)来说,它的预览图像在旋转之前是水平翻转的,也就是前置摄像头的预览成像是沿图像的中央垂直线翻转过来,就像用户照镜子一样的效果。因此,在得到前置摄像头的旋转角度后(rotation = CameraInfo.orientatio + degrees),还需要对其进行水平翻转(rotation = 360-rotation),即取rotation的负数即可,但是由于旋转的角度不能是负数,因此再加上360求正。其中,CameraInfo.orientatio是图像感应器相对于手机竖直正常方向的角度值、手机方向为相对于竖直正常方向沿顺时针转动的方向值。另外,当我们得到前后置摄像头旋转的方向后还需要对360求余,以防止旋转的角度超过一周360度的情况。


image.png

具体代码如下:

private int getPreviewRotateDegree(){
    int phoneDegree = 0;
    int result = 0;
    //获得手机方向
    int phoneRotate =getWindowManager().getDefaultDisplay().getOrientation();
    //得到手机的角度
    switch (phoneRotate) {
        case Surface.ROTATION_0: phoneDegree = 0; break;        //0
        case Surface.ROTATION_90: phoneDegree = 90; break;      //90
        case Surface.ROTATION_180: phoneDegree = 180; break;    //180
        case Surface.ROTATION_270: phoneDegree = 270; break;    //270
    }
    //分别计算前后置摄像头需要旋转的角度
    Camera.CameraInfo cameraInfo = new CameraInfo();
    if(isFrontCamera){          
        Camera.getCameraInfo(Camera.CameraInfo.CAMERA_FACING_FRONT, cameraInfo);
        result = (cameraInfo.orientation + phoneDegree) % 360;
        result = (360 - result) % 360;
    }else{
        Camera.getCameraInfo(Camera.CameraInfo.CAMERA_FACING_BACK, cameraInfo);
        result = (cameraInfo.orientation - phoneDegree +360) % 360;
    }
    return result;
}
 
//进行Camera预览旋转
Camera mCamera = Camera.open();
int rotateDegree = getPreviewRotateDegree();
mCamera.setDisplayOrientation(rotateDegree);

注意:上述方法适用于默认预览为竖屏应用,setDisplayOrientation (int degrees)只对预览时旋转图片有效,但对onPreviewFrame(byte[],Camera)、JPEG拍照、视频录制的图片旋转无效。
2.Camera拍照方向处理
由于使用Camera进行拍照时,是直接将图像传感器采集到的图像数据直接存储到Sdcard卡,它通常不与预览时看到的画面方向一致,而是与图像传感器的方向一致。也就是说,当我们竖着拿着手机拍摄时,得到的照片看起来是不正常的(横向的),这是因为竖着拿着手机正好与图像传感器的方向相差了90度;当横着拿着手机拍摄时,得到的照片看起来才是正常的,。效果如下图所示。


image.png

Camera拍摄照片方向的处理与手机的方向紧密相关,而由于拍摄照片时手机的方向是不确定的,因此需要手机的方向感应器(OrientationEventListener)来捕获手机的实时旋转角度,当手机方向发现偏转时OrientationEventListener的onOrientationChanged(int orientation)方法会立即被回调,orientation即为实时变化的角度。旋转方法:
首先,为了使相机对方向不那么敏感,可以采用一个范围来限定手机当前方向的角度值,比如当手机的方向处于45度~ 90度时,我们就认定手机当前转动的角度为90度,依次类推得到手机大概的方向角度值。
其次,计算前后置摄像头需要旋转的角度。Camera的预览效果是获得图像传感器采集的图像数据后再将其显示在显示屏上,而拍摄照片则是直接将图像传感器采集的图像数据保存到Sdcard上,因此,它们处理旋转时的角度计算是不同的。由于图像传感器的取景方向与手机竖直方向恰好相差90度,因此,对于后置摄像头来说,其旋转的角度应该手机实际变化的角度加上图像传感器与手机之间的夹角,即mOrientation=cameraInfo.orientation +phoneDegree;对于前置摄像头来说,旋转的角度mOrientation=cameraInfo.orientation – phoneDegree。以手机方向改变270度为例,效果如下图(2)所示,后置摄像头需旋转的角度为(270+90),可见刚好为360度使摄像头与图像传感器方向一致,那么旋转的角度进行求余处理后刚好为0。由于前置摄像头是水平翻转的,因此需要对需要进行水平翻转处理,也就是180度的问题,最终旋转的角度为|(90-270)|=180。


image.png

image.png

具体代码如下:
private void startOrientationListener() {
OrientationEventListener mOrEventListener = new OrientationEventListener(mContext) {
@Override
public void onOrientationChanged(int orientation) {
//计算手机当前方向的角度值
int phoneDegree = 0;
if (((orientation >= 0) && (orientation <= 45))|| (orientation > 315) &&(orientation<=360)) {
phoneDegree = 0;
} else if ((orientation > 45) && (orientation <= 135)) {
phoneDegree = 90;
} else if ((orientation > 135) && (orientation <= 225)) {
phoneDegree = 180;
} else if ((orientation > 225) && (orientation <= 315)) {
phoneDegree = 270;
}
//分别计算前后置摄像头需要旋转的角度
Camera.CameraInfo cameraInfo = new CameraInfo();
if(mFragment.isFrontCamera()){
Camera.getCameraInfo(Camera.CameraInfo.CAMERA_FACING_FRONT, cameraInfo);
mOrientation = (cameraInfo.orientation - phoneDegree +360) % 360;
}else{
Camera.getCameraInfo(Camera.CameraInfo.CAMERA_FACING_BACK, cameraInfo);
mOrientation = (cameraInfo.orientation + phoneDegree) % 360;
}
};
//启动方向感应器
mOrEventListener.enable();
}

注意:由于上述涉及的角度值都是正数且不大于360度,因此,需要对相关角度进行求正和求余处理。
3.JPEG图片方向处理
有这么一种情况,如果有一款自定义相机的拍照功能忘记处理图片旋转的问题,那么我们
在使用的过程中就会看到拍下的JPEG照片显示方向“不正常”。针对于这种情况,可以通过Android API提供的ExifInterface接口来解决,该接口存储了指定JPEG图片的详细信息,比如拍摄时的角度、曝光度、分辨率等等。旋转方法:
首先,根据图片路径创建一个ExifInterface对象,再调用其getAttributeInt(
ExifInterface.TAG_ORIENTATION,ExifInterface.ORIENTATION_NORMAL)方法,得到JPEG拍摄时的角度。
其次,调用Matrix的postRotate(degree)方法对图片进行旋转,然后再使用Bitmap.createBitmap方法得到最终的位图对象。
具体代码如下:

public static int getPictureDegress(String filePath) {
int degree = 0;
ExifInterface exifInterface = null;
try {
exifInterface = new ExifInterface(filePath);
} catch (IOException e) {
e.printStackTrace();
}
if (exifInterface != null) {
//获得图片拍摄角度,第二个的作用是如果这个属性不存在,则作为默认值返回
int orientation = exifInterface.getAttributeInt(
ExifInterface.TAG_ORIENTATION,
ExifInterface.ORIENTATION_NORMAL);
switch (orientation) {
case ExifInterface.ORIENTATION_ROTATE_90:
degree = 90;
break;
case ExifInterface.ORIENTATION_ROTATE_180:
degree = 180;
break;
case ExifInterface.ORIENTATION_ROTATE_270:
degree = 270;
break;
default:
degree = 0;
break;
}
return degree;
}
return 0;
}
/** 旋转图片
* @param imgPath 原图路径
* @ param imgPath
*/
public static Bitmap setBitmapDegreeZero(String imgPath) {
Bitmap mBitmap = null;
int degree = getPictureDegress(imgPath);
if (degree != 0) {
mBitmap = BitmapFactory.decodeFile(imgPath);
Matrix matrix = new Matrix();
matrix.postRotate(degree);
mBitmap = Bitmap.createBitmap(mBitmap, 0, 0, mBitmap.getWidth(),
mBitmap.getHeight(), matrix, true);
}
return mBitmap;
}

效果演示:

image.png

————————————————
原文链接:https://blog.csdn.net/andrexpert/article/details/54388929

你可能感兴趣的:(Camera 图像传感器取景方向 预览方向)