Android开发中关于摄像头方向的理解

安卓开发中经常有需要使用摄像头的应用场景,对于初次接触的同学摄像头的方向是一个比较难弄清楚的概念,开发时很容易处理不当,本文将详述该部分内容帮助理解。

一、摄像头捕获的图像

先看一个简单的场景,打开手机的后置摄像头拍摄,摄像头捕获的图像帧数据可通过Camera.PreviewCallback回调中获取,也就是摄像头的输出数据,

void onPreviewFrame(byte[] data, Camera camera);

这里我们先忽略屏幕上的预览,只关注摄像头的输出。如果把它保存为图片或直接显示出来,可以看到图像和原始画面相比逆时针旋转了90度。



而我们如果同样使用iPhone手机拍摄,输出的结果是一个正向的图片。

二、摄像头的正向

为什么输出的图像相比原始画面旋转了90度?因为设备的摄像头存在一个“正向角度”,什么是摄像头的正向?
通俗一点讲,设备相当于人的身体,眼睛相当于摄像头,眼睛把接收到的画面反馈给大脑处理,相当于摄像头把接收到的数据给应用程序处理。人眼能判断出我们头顶向上的方向是我们视觉上的正向,而后置摄像头判断的正向并不是手机物理屏幕向上的方向,而是物理屏幕右侧的方向。我们想象一下,如果人眼是这个摄像头,它认为右侧才是我们的视觉正向,那我们看到的东西是不是都是旋转90度的?这样就比较好理解了。


上图是手机在竖直和水平方向摄像头“看”到的画面。
固定设备,指定的摄像头,正向角度固定的(0/90/180/270),和屏幕旋转、横竖屏切换无关,一般都在屏幕的右侧(但不排除某些厂商修改成别的)。
这个角度在代码中可通过Camera.CameraInfo的orientation获取,官方文档也有解释:

The orientation of the camera image. The value is the angle that the camera image needs to be rotated clockwise so it shows correctly on the display in its natural orientation. It should be 0, 90, 180, or 270.

For example, suppose a device has a naturally tall screen. The back-facing camera sensor is mounted in landscape. You are looking at the screen. If the top side of the camera sensor is aligned with the right edge of the screen in natural orientation, the value should be 90. If the top side of a front-facing camera sensor is aligned with the right of the screen, the value should be 270.

意思就是输出的图片需要顺时针旋转多少度,才能在自然方向上正确显示。这里的自然方向就是以标题栏左上角为原点的屏幕渲染坐标系,图片旋转后,把它放到渲染坐标系中,能和原始画面一样正常显示。



上图红点代表了图片的坐标原点,蓝点则代表屏幕渲染坐标的原点。只有做了旋转处理,渲染到屏幕上的预览图像才是正确的(和原始画面一样),而这个旋转的角度,就是orientation的值。
注意,正向始终在物理屏幕的右侧(想象音量键那边有一个正向箭头),orientation就等于从摄像头的角度(想象成人眼)看,从物理设备的正上方向(想象听筒位置有个箭头),需要顺时针旋转多少度才能到正向的箭头。所以,根据这个想象一下前后摄像头的区别,这个值后置摄像头是90,前置摄像头是270。

iPhone的摄像头正向就是物理设备的正上方,对应的正向角度是0,所以输出的图像是正向的。

三、如何正确地预览图像

其实不预览应用程序也能正确获取到摄像头的输入,但是一般应用打开摄像头后都会在屏幕上显示当前拍摄到的画面,这是用户的基本的使用体验。正确的预览图像就是让摄像头输出的图像能够正确的在屏幕上显示给用户。

如上节所述,摄像头采集到的图像按照orientation旋转和渲染坐标系对齐即可,这样就能正确显示图像了。不过这是在屏幕方向锁定的情况下,就是渲染坐标系始终在物理屏幕的左上方。

如果我们打开了设备陀螺仪(锁屏),屏幕可以在四个方向上切换,对应渲染的坐标原点(蓝点)会在物理屏幕的四个角上切换。
切换方向的角度可以通过activity.getWindowManager().getDefaultDisplay().getRotation();获取,和前后摄像头无关:

屏幕切换的角度值

这个角度可以理解为,以物理设备左上角为原点的渲染坐标系(听筒左边的角点),需要顺时针旋转多少度,才能变成当前的渲染坐标系。打开陀螺仪后,无论手机怎么旋转,当前的渲染坐标系永远以绝对左上角为原点(视觉左上方角点)。这个角度恰好和物理旋转的角度相反。

我们只要根据当前的切换角度+摄像头正向角度正确地设置显示角度就行了,官方文档也有现成的适配代码,详见setDisplayOrientation
注意,setDisplayOrientation只会对预览显示的图像有影响,并不会影响onPreviewFrame回调的数据。

 public static void setCameraDisplayOrientation(Activity activity,
         int cameraId, android.hardware.Camera camera) {
     android.hardware.Camera.CameraInfo info =
             new android.hardware.Camera.CameraInfo();
     android.hardware.Camera.getCameraInfo(cameraId, info);
     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 (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
         result = (info.orientation + degrees) % 360;
         result = (360 - result) % 360;  // compensate the mirror
     } else {  // back-facing
         result = (info.orientation - degrees + 360) % 360;
     }
     camera.setDisplayOrientation(result);
 }

如果还不理解这段代码的意思,看下这张图就明白了:


蓝点是渲染的坐标原点,红点是输出图片的原点。每一次旋转图片的原点就会变换到绝对位置的左上角,setDisplayOrientation要设置的值就是图像要顺时针旋转的角度,使图片能在渲染坐标系中正确显示。从图中也能看出来,它就是第二列的箭头需要顺时针旋转到第一列箭头的角度。

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

推荐阅读更多精彩内容