为什么在某些安卓设备上使用相机意图拍摄的图片会被旋转?

467

我正在捕获一张图片并将其设置为图像视图。

public void captureImage() {

    Intent intentCamera = new Intent("android.media.action.IMAGE_CAPTURE");
    File filePhoto = new File(Environment.getExternalStorageDirectory(), "Pic.jpg");
    imageUri = Uri.fromFile(filePhoto);
    MyApplicationGlobal.imageUri = imageUri.getPath();
    intentCamera.putExtra(MediaStore.EXTRA_OUTPUT, imageUri);
    startActivityForResult(intentCamera, TAKE_PICTURE);
}

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent intentFromCamera) {
    super.onActivityResult(requestCode, resultCode, intentFromCamera);

    if (resultCode == RESULT_OK && requestCode == TAKE_PICTURE) {

        if (intentFromCamera != null) {
            Bundle extras = intentFromCamera.getExtras();
            if (extras.containsKey("data")) {
                bitmap = (Bitmap) extras.get("data");
            }
            else {
                bitmap = getBitmapFromUri();
            }
        }
        else {
            bitmap = getBitmapFromUri();
        }
        // imageView.setImageBitmap(bitmap);
        imageView.setImageURI(imageUri);
    }
    else {
    }
}

public Bitmap getBitmapFromUri() {

    getContentResolver().notifyChange(imageUri, null);
    ContentResolver cr = getContentResolver();
    Bitmap bitmap;

    try {
        bitmap = android.provider.MediaStore.Images.Media.getBitmap(cr, imageUri);
        return bitmap;
    }
    catch (Exception e) {
        e.printStackTrace();
        return null;
    }
}

但问题在于,有些设备每次旋转后图像会发生变化。例如,在三星设备上它效果良好,但在Sony Xperia上,图像会旋转90度,在Toshiba Thrive(平板电脑)上会旋转180度。


1
尝试在您的活动清单中添加以下内容: android:configChanges="orientation" android:screenOrientation="portrait" - Narendra Pal
1
我认为当您使用内部意图处理相机应用程序时,它会旋转图像。这取决于您如何持有设备来捕获图像。因此,您可以限制用户以特定方式拍摄图像,即用户始终通过纵向或横向持有设备来捕获图像。之后,您可以将其更改为特定角度以获取所需的图像。或者,另一种选择是制作自己的相机应用程序。 - Narendra Pal
3
我相信捕获意图会始终调用默认的相机应用程序,该应用程序在每个设备上都有特定的方向,并因此具有固定的照片方向。这不取决于用户拿设备的方式或调用该意图的活动的方向。 - Alex Cohn
1
为了避免存储权限问题,请参考回答,或使用Glide - Pavel
1
有人发现了ORIENTATION_UNDEFINED的解决方案吗?因为在一些设备上(例如Android 8模拟器),图像会旋转,而在另一些设备上(例如Android 9模拟器)它不会旋转,尽管Orientation值相同。如何知道图像是否需要旋转? - Harshit Saxena
显示剩余4条评论
23个回答

4
很遗憾,@jason-robinson上面的答案对我没有用。
尽管rotate函数完美运行:
public static Bitmap rotateImage(Bitmap source, float angle) {
    Matrix matrix = new Matrix();
    matrix.postRotate(angle);
    return Bitmap.createBitmap(source, 0, 0, source.getWidth(), source.getHeight(), matrix,
            true);
}

我必须执行以下操作才能获得方向,因为Exif方向始终为0。
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode,resultCode,data);
    if (requestCode == RESULT_LOAD_IMAGE && resultCode == RESULT_OK && data != null) {
            Uri selectedImage = data.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]));
            }
            InputStream imageStream = getContentResolver().openInputStream(selectedImage);
            Bitmap bitmap = BitmapFactory.decodeStream(imageStream);
            switch(orientation) {
                    case 90:
                            bitmap = rotateImage(chosen_image_bitmap, 90);
                            break;
                    case 180:
                            bitmap = rotateImage(chosen_image_bitmap, 180);
                            break;
                    case 270:
                            bitmap = rotateImage(chosen_image_bitmap, 270);
                            break;
                    default:
                            break;
            }
            imageView.setImageBitmap(bitmap );

2
始终为0,三星7 - djdance

4

通常建议使用ExifInterface来解决问题,就像Jason Robinson建议的那样。如果这种方法不起作用,您可以尝试查找最新拍摄图像的方向...

private int getImageOrientation(){
    final String[] imageColumns = { MediaStore.Images.Media._ID, MediaStore.Images.ImageColumns.ORIENTATION };
    final String imageOrderBy = MediaStore.Images.Media._ID+" DESC";
    Cursor cursor = getContentResolver().query(MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
            imageColumns, null, null, imageOrderBy);

    if(cursor.moveToFirst()){
        int orientation = cursor.getInt(cursor.getColumnIndex(MediaStore.Images.ImageColumns.ORIENTATION));
        cursor.close();
        return orientation;
    } else {
        return 0;
    }
}

1
我认为这段代码只能检测旋转发生的程度。现在我能够做到这一点,但在下一个任务中即旋转图像时却无法实现。 - Shirish Herwade
你说得没错,但是你在这个线程中没有要求旋转,所以让我们保持干净;)这就是为什么我把我的答案放到了你的另一个线程中解决旋转问题...希望这可以帮助你,它对我有效:http://stackoverflow.com/questions/14123809/code-to-rotate-image-captured-by-camera-intent-not-working-in-android - Chris
1
MediaStore.Images.ImageColumns.ORIENTATION 仅在 Android 10 及以上版本中可用。 - Ashutosh Sagar

4
我使用了一种不同的方法解决了它。你只需要检查宽度是否大于高度即可。
Matrix rotationMatrix = new Matrix();
if(finalBitmap.getWidth() >= finalBitmap.getHeight()){
    rotationMatrix.setRotate(-90);
}else{
    rotationMatrix.setRotate(0);
}

Bitmap rotatedBitmap = Bitmap.createBitmap(finalBitmap,0,0,finalBitmap.getWidth(),finalBitmap.getHeight(),rotationMatrix,true);

1
在我的情况下,我需要:rotationMatrix.setRotate(90); - Noor Hossain

3
所选答案使用了最常见的方法来回答这个和类似问题。然而,它不能在三星前置和后置摄像头上都使用。对于那些寻求适用于三星和其他主要制造商前后置摄像头的解决方案的人,nvhausid的答案很棒:https://dev59.com/_mUo5IYBdhLWcg3wvBeX#18915443。对于那些不想点击链接的人,相关技巧是使用CameraInfo而不是依赖EXIF。
Bitmap realImage = BitmapFactory.decodeByteArray(data, 0, data.length);
android.hardware.Camera.CameraInfo info = new android.hardware.Camera.CameraInfo();
android.hardware.Camera.getCameraInfo(mCurrentCameraId, info);
Bitmap bitmap = rotate(realImage, info.orientation);

详细代码请参考链接。


不,不同角度的旋转有问题(三星S7)。我指的是相册。 - djdance

2

这个评论就是我需要的。老兄,非常感谢你。 - Joel Nieman

1
最好尝试以特定方向拍摄照片。
android:screenOrientation="landscape"
android:configChanges="orientation|keyboardHidden"

为了获得最佳效果,请在相机视图活动中使用横向方向。


抱歉,它不起作用。事实上,在选项卡上,每次执行onActivityResult后都会奇怪地调用onCreate。 - Shirish Herwade
1
抱歉,问题就是这样。 - Shirish Herwade

1
这可能是显而易见的,但请记住您可以在服务器上处理其中一些图像处理问题。我使用了本主题中包含的响应来处理图像的即时显示。然而,我的应用程序需要将图像存储在服务器上(如果您希望图像在用户更换手机后仍然存在,则这可能是一个常见要求)。
许多关于此主题的主题中包含的解决方案并未讨论EXIF数据的缺乏持久性,这意味着您需要每次服务器加载图像时旋转图像。或者,您可以将EXIF方向数据发送到服务器,然后在那里根据需要旋转图像。
对我来说,在服务器上创建永久解决方案更容易,因为我不必担心Android的秘密文件路径。

你能在拍摄图像时旋转它一次并保存,以便无需再次旋转吗? - jk7
是的,你可以这样做,实际上这就是我最终实现的过程。我之前一直在尝试获取安卓手机图片的文件路径,以便进行操作,但一直遇到问题。这是帮助我的答案:https://dev59.com/fnA75IYBdhLWcg3wPmd8#36714242 - Braden Holt
有成千上万的应用程序可以在手机上旋转图像,但最好在服务器上旋转它们。每种情况都不同,但我会避免将工作放在您的服务器上的解决方案。尽可能地将计算外包给客户手机是您始终想要的选择。 - Vaidas

1

以下代码适用于我,它从文件URI获取位图,并在需要时进行旋转修复:

    private fun getCapturedImage(selectedPhotoUri: Uri): Bitmap {
        val bitmap = when {
            Build.VERSION.SDK_INT < 28 -> MediaStore.Images.Media.getBitmap(
                this.contentResolver,
                selectedPhotoUri
            )
            else -> {
                val source = ImageDecoder.createSource(this.contentResolver, selectedPhotoUri)
                ImageDecoder.decodeBitmap(source)
            }
        }

        // If the image is rotated, fix it
        return when (ExifInterface(contentResolver.run { openInputStream(selectedPhotoUri) }).getAttributeInt(
            ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_UNDEFINED)) {
            ExifInterface.ORIENTATION_ROTATE_90 ->
                Bitmap.createBitmap(bitmap, 0, 0, bitmap.width, bitmap.height, Matrix().apply {
                    postRotate(90F) }, true)
            ExifInterface.ORIENTATION_ROTATE_180 ->
                Bitmap.createBitmap(bitmap, 0, 0, bitmap.width, bitmap.height, Matrix().apply {
                    postRotate(180F) }, true)
            ExifInterface.ORIENTATION_ROTATE_270 ->
                Bitmap.createBitmap(bitmap, 0, 0, bitmap.width, bitmap.height, Matrix().apply {
                    postRotate(270F) }, true)
            else -> bitmap
        } 
    }

0

通过使用 Glide 库,您可以获取具有精确方向的图像,无需检查旋转

在 Kotlin 中

CoroutineScope(Dispatchers.IO).launch {
         var bitmap = Glide.with(context).asBitmap().load(imagePathOrUriOrLink)
                /*.apply(
                    RequestOptions()
                        .override(MAXIMUM_IMAGE_RESOLUTION)
                )*/ //uncomment it if you want original image
                /*.diskCacheStrategy(DiskCacheStrategy.NONE).skipMemoryCache(true)*/ //uncomment it you want to not cache image
                .submit().get()//this is synchronous approach  
}

使用这个依赖
api 'com.github.bumptech.glide:glide:4.12.0'
kapt 'com.github.bumptech.glide:compiler:4.12.0'

0

使用Glide库对我很有帮助。旋转会自动处理。

Bitmap bitmap = Glide.with(myContext).asBitmap().load(imageFilePath).submit(SIZE_ORIGINAL, SIZE_ORIGINAL).get();

然后你可以将该位图以JPEG格式保存到文件中,例如。
如果您只想加载到 ImageView 而不是保存到文件中:
Glide.with(myContext).load(imageFilePath).into(myImageView)

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