Android获取相机位图的方向?并将其旋转回-90度。

70

我有这段代码:

//choosed a picture
public void onActivityResult(int requestCode, int resultCode, Intent data) {

    if (resultCode == RESULT_OK) {
        if (requestCode == ImageHelper.SELECT_PICTURE) {

            String picture           = "";

            Uri selectedImageUri     = data.getData();
            //OI FILE Manager
            String filemanagerstring = selectedImageUri.getPath();
            //MEDIA GALLERY
            String selectedImagePath = ImageHelper.getPath(mycontext, selectedImageUri);

            picture=(selectedImagePath!=null)?selectedImagePath:filemanagerstring;

这只是一个从图库中选择图片的工具。虽然它不错,但当我在ImageView上打开使用相机拍摄的“竖屏模式”下的照片时,看起来很好,而使用相机拍摄的“横屏模式”下的照片则会以-90度的方式打开。

如何将这些照片旋转回来呢?

    Bitmap output       = Bitmap.createBitmap(newwidth, newheight, Config.ARGB_8888);
    Canvas canvas       = new Canvas(output);

我尝试了这个:

Log.e("w h", bitmap.getWidth()+" "+bitmap.getHeight());
if (bitmap.getWidth()<bitmap.getHeight()) canvas.rotate(-90);

但是这个方法不起作用,所有图片大小都为: *2560 1920像素(无论竖屏或横屏)

我该怎么才能将横屏的照片旋转回来?

3个回答

242
如果使用数字相机或智能手机拍摄照片,旋转通常会作为图像文件的一部分存储在照片的 Exif 数据中。您可以使用 Android 的 ExifInterface 读取图像的 Exif 元数据。
首先,创建一个 ExifInterface
ExifInterface exif = new ExifInterface(uri.getPath());

接下来,找到当前的旋转角度:

int rotation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL);  

将 Exif 旋转转换为角度:

int rotationInDegrees = exifToDegrees(rotation);

在哪里

private static int exifToDegrees(int exifOrientation) {        
    if (exifOrientation == ExifInterface.ORIENTATION_ROTATE_90) { return 90; } 
    else if (exifOrientation == ExifInterface.ORIENTATION_ROTATE_180) {  return 180; } 
    else if (exifOrientation == ExifInterface.ORIENTATION_ROTATE_270) {  return 270; }            
    return 0;    
 }

然后使用图像的实际旋转作为参考点,使用Matrix旋转图像。

Matrix matrix = new Matrix();
if (rotation != 0) {matrix.preRotate(rotationInDegrees);}

你可以使用Bitmap.createBitmap方法创建一个带有旋转效果的新图像,该方法需要一个Matrix参数:

Bitmap.createBitmap(Bitmap source, int x, int y, int width, int height, Matrix m, boolean filter)

其中Matrix m保存了新的旋转:

Bitmap adjustedBitmap = Bitmap.createBitmap(sourceBitmap, 0, 0, width, height, matrix, true);

看这个教程获取一个有用的源代码例子:


@GunnarKarlsson 这里的sourceBitmap是什么? - Bitmap adjustedBitmap = Bitmap.createBitmap(sourceBitmap, 0, 0, width, height, matrix, true); 因为我只有源Uri,没有Bitmap。 - Narendra Singh
@DroidWormNarendra 请参考:https://dev59.com/p2865IYBdhLWcg3wTczh - Gunnar Karlsson
3
我遇到了一个错误,提示它不是一个文档。 E/JHEAD: 无法打开 '/document/image:82297'。 - Martin Seal
不需要手动读取 Exif 数据和旋转。Glide 已经完成了一切... - Brijesh Kumar
2
很棒的答案,谢谢。@JamesTan 如果你遇到了文件未找到的异常,请使用以下代码: private String getPath(Uri uri) { String[] data = { MediaStore.Images.Media.DATA }; CursorLoader loader = new CursorLoader(this, uri, data, null, null, null); Cursor cursor = loader.loadInBackground(); int column_index = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA); cursor.moveToFirst(); return cursor.getString(column_index); } - s0i1
显示剩余4条评论

6
如果您使用 Jetpack CameraX,在 onImageCaptured 方法中,您可以像这样从 imageProxy 中访问 EXIF 数据提供的旋转角度:
image.imageInfo.rotationDegrees

然后在设置您的图像时,您可以根据这个角度旋转您的图像


你可以通过getter更好地完成它。imageProxy.getImageInfo().getRotationDegrees()。此外,如果您正在使用ImageAnalysis,则代理图像的方式也是相同的。 - BCJuan

4
上一个回答在技术方面完美无缺,但我努力创建了一个管理图片的系统,包括旋转、调整大小、缓存和加载到ImageViews中,我可以告诉你这是一场噩梦。即使所有工作都完成了,有时仍会在某些设备上因内存不足而崩溃。
我的观点是不要重复造轮子,它已经有一个完美的设计。Google本身鼓励您使用Glide。它只需一行代码即可运行,非常易于使用,大小轻巧,功能数量少,默认情况下它可以管理EXIF,并且它的内存使用效果非常好。它就像黑魔法一样编码 ;)
我不确定Picasso是否也可以管理EXIF,但以下是它们两者的简短介绍:

https://inthecheesefactory.com/blog/get-to-know-glide-recommended-by-google/en

我的建议:不要浪费时间,直接使用它们。你只需要一行代码就可以解决问题:

Glide.with(context).load("http://i.imgur.com/DvpvklR.png").into(imageView);

4
是的,但问题在于,如果你解码/降采样了一些位数,或者使用JPEG格式进行了压缩,那么EXIF信息就不会被保留,所以你需要将属性重置到新文件上。 - ngatirauks
1
这在使用Google相机的Google像素上无法工作。 - nayan dhabarde
我非常推荐使用Glide。在RecyclerView中,当我使用单个图像滚动时,出现了相当大的延迟,但是一旦我改用Glide而不是自己处理,差异就是天壤之别。当然,你也可以自己实现同样的效果,但是上面那行代码简直是无价之宝。 - Tokens94

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