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

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

推荐阅读更多精彩内容