一些设备在拍照时 Android 相机会出现无法解释的旋转(不在 EXIF 中)

30
我正在做的事情看起来似乎很简单,但是在阅读了我能找到的每一个Stackoverflow答案并搜索了每一篇文章后,我仍然不知所措。
我正在使用预览SurfaceView,在我的AndroidManifest.xml中将屏幕方向设置为“landscape”,从活动中捕获图像。
我遵循了示例相机应用程序代码,并认为一切都正常工作,直到我在几个运行1.5版本的Motorola设备上尝试我的应用程序时才意识到问题。
我已经成功运行OrientationEventListener,并使用反射检查是否设置了旋转,如下所示:
final int latchedOrientation = roundOrientation(mLastOrientation + 90);

Parameters parameters = preview.camera.getParameters();

JPLog.d("Setting camera rotation = %d", latchedOrientation);
try {
    // if >= 2.0
    Method method = Camera.Parameters.class.getMethod("setRotation",
        int.class);

    if(method != null) {
        method.invoke(parameters, latchedOrientation);
    }

} catch(Throwable t) {
    // if < 2.0
    parameters.set("rotation", latchedOrientation);
}

preview.camera.setParameters(parameters);
- 运作良好。latchedOrientation = 0,图片没有EXIF头中的任何旋转。 - 也运作良好。latchedOrientation = 0,图片OK。 - 图片被旋转了。latchedOrientation = 0,图片中没有EXIF旋转元素。 - 图片被旋转了。latchedOrientation = 0,图片中没有EXIF旋转元素。
这些摩托罗拉设备出了什么问题?我原以为我的问题是摩托罗拉相机驱动程序没有旋转图像,因此找到了适用于Android的Sanselan EXIF读取类,并准备自己旋转它们。有趣的是,存在EXIF头但没有旋转元素。
如果我手动将旋转角度设置为90度,则摩托罗拉设备上的图像效果完美,但现在G1和NexusOne的图像旋转了90度(不是我想要的)。这里肯定有些事情我没有理解。
我怀疑这不是1.5的问题,否则就会有人发布相关信息了吧?

为什么在一些安卓设备上,使用相机意图捕获的图片会旋转? - Shirish Herwade
4个回答

47

我遇到了这个问题,我使用了这种方法来捕获图像(不创建自定义相机)。

final Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
intent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(image));
startActivityForResult(intent, 0);

并在onActivityResult(int requestCode,int resultCode,Intent data){}中完成其余部分。

但是原始图像(实际SD卡图像)是正确的,并且当我像这样获取时,Bitmap被旋转了。 Bitmap bmp = BitmapFactory.decodeStream(..

解决方案:

    try {
        File f = new File(SD_CARD_IMAGE_PATH);
        ExifInterface exif = new ExifInterface(f.getPath());
        int orientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL);

        int angle = 0;

        if (orientation == ExifInterface.ORIENTATION_ROTATE_90) {
            angle = 90;
        } 
        else if (orientation == ExifInterface.ORIENTATION_ROTATE_180) {
            angle = 180;
        } 
        else if (orientation == ExifInterface.ORIENTATION_ROTATE_270) {
            angle = 270;
        }

        Matrix mat = new Matrix();
        mat.postRotate(angle);

        Bitmap bmp = BitmapFactory.decodeStream(new FileInputStream(f), null, null);
        Bitmap correctBmp = Bitmap.createBitmap(bmp, 0, 0, bmp.getWidth(), bmp.getHeight(), mat, true);                 
    }
    catch (IOException e) {
        Log.w("TAG", "-- Error in setting image");
    }   
    catch(OutOfMemoryError oom) {
        Log.w("TAG", "-- OOM Error in setting image");
    }

6
好的解决方案,我稍作改进。当angle=0时,无需对其进行解码并创建旋转为0的第二个位图。如果在不受此问题影响的设备上运行,则应将此过程的内存消耗减少50%。 - WarrenFaith
在某些设备上,在纵向模式下拍照时,简单的旋转会导致图像拉伸,因此我花了一些时间来解决旋转方法的问题。找到的解决方案是:code Matrix matrix = new Matrix(); matrix.postTranslate(-width / 2, -height / 2); matrix.postRotate(angle); matrix.postTranslate(width / 2, height / 2); - fox

10
这实际上是一个设备特定的问题,主要影响摩托罗拉设备。Google开发人员在API 8中包含了一个setDisplayOrientation调用来解决这个问题。主要的错误报告在这里
对于那些无法使用API 8的人,有两种常见的解决方案: 覆盖onDraw
在顶层ViewGroup中覆盖onDraw并将画布旋转90度以补偿旋转。请注意,您的触摸事件也需要旋转。 使用横向模式
将活动锁定为横向模式,但绘制资产时好像是纵向模式。这意味着您可以进行布局并旋转图像资产以看起来像是在纵向模式下,以使视图看起来正常。不幸的是,这使得使用菜单变得困难,因为菜单会水平打开。
我还看到人们使用动画控制器旋转视图。这里的缺点是我无法克服的旋转视图无法拉伸以填满屏幕。请参见Georg的答案以获取示例实现。

不幸的是,目前没有好的方法来解决这个问题,而且似乎Moto相机驱动程序中存在一个错误。 - Scott Merritt
这在我的摩托罗拉驱动程序中有效。我只需用函数“http://developer.android.com/reference/android/hardware/Camera.html#setDisplayOrientation(int)”替换此行:mCamera.setDisplayOrientation(90); 并使用cameraid = Camera.CameraInfo.CAMERA_FACING_FRONT。 - Ho Luong

4

这是我在活动中使用 onActivityResult() 的代码。返回的意图是选择一种类型为 image/* 的图像。这对我来说很有效!

Uri imageUri = intent.getData();
                String[] orientationColumn = {MediaStore.Images.Media.ORIENTATION};
                Cursor cur = managedQuery(imageUri, orientationColumn, null, null, null);
                int orientation = -1;
                if (cur != null && cur.moveToFirst()) {
                    orientation = cur.getInt(cur.getColumnIndex(orientationColumn[0]));
                }  
                Matrix matrix = new Matrix();
                matrix.postRotate(orientation);

我需要在我的应用程序中捕获图像,并设置像我在问题中发布的片段中那样的参数。如果我可以触发一个意图让另一个应用程序为我拍照,那将更容易。 - Scott Merritt
你仍然可以在你的应用程序存储使用这个URI来处理相机拍摄的图像。这只是一个将图像旋转到正确方向的代码,无论它来自哪里。 - dbaugh

0

看起来“使用横屏模式”建议是唯一真正有效的方法。似乎这可以在清单中或通过在活动 onCreate 中调用 setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE) 来完成。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接