如何从默认图库中选择的图片获取正确的方向?

42

我有答案。https://dev59.com/7Yrda4cB1Zd3GeqPLFZH#32747566 - A.Sanchez.SD
这里有一个我发现的很棒的一行代码解决方案:>https://dev59.com/3WYq5IYBdhLWcg3w8VG8#34241250 它可能需要一些时间来加载,但我只是在图像视图后面放了一些文本,显示“正在加载图像”,当图像加载时,它会覆盖文本。 - Andrew Moreau
5个回答

113
如果照片是由您编写的程序拍摄的,则必须使用正确的旋转值设置Parameters.setRotation。
这取决于相机驱动程序,它将在保存图像之前旋转图像或将旋转值保存到exif TAG_ORIENTATION中。
因此,如果TAG_ORIENTATION为null或零,则图像处于正确的方向,否则您必须根据TAG_ORIENTATION中的值旋转图像。 代码: 从EXIF获取方向:
ExifInterface exif = null;
try {
    exif = new ExifInterface(path);
} catch (IOException e) {
    e.printStackTrace();
}  
int orientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, 
                                       ExifInterface.ORIENTATION_UNDEFINED);

获取旋转后的位图:

Bitmap bmRotated = rotateBitmap(bitmap, orientation);  

旋转位图的方法:

public static Bitmap rotateBitmap(Bitmap bitmap, int orientation) {

    Matrix matrix = new Matrix();
    switch (orientation) {
        case ExifInterface.ORIENTATION_NORMAL:
            return bitmap;
        case ExifInterface.ORIENTATION_FLIP_HORIZONTAL:
            matrix.setScale(-1, 1);
            break;
        case ExifInterface.ORIENTATION_ROTATE_180:
            matrix.setRotate(180);
            break;
        case ExifInterface.ORIENTATION_FLIP_VERTICAL:
            matrix.setRotate(180);
            matrix.postScale(-1, 1);
            break;
        case ExifInterface.ORIENTATION_TRANSPOSE:
            matrix.setRotate(90);
            matrix.postScale(-1, 1);
            break;
       case ExifInterface.ORIENTATION_ROTATE_90:
           matrix.setRotate(90);
           break;
       case ExifInterface.ORIENTATION_TRANSVERSE:
           matrix.setRotate(-90);
           matrix.postScale(-1, 1);
           break;
       case ExifInterface.ORIENTATION_ROTATE_270:
           matrix.setRotate(-90);
           break;
       default:
           return bitmap;
    }
    try {
        Bitmap bmRotated = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);
        bitmap.recycle();
        return bmRotated;
    }
    catch (OutOfMemoryError e) {
        e.printStackTrace();
        return null;
    }
}

1
我怎样才能从所选图库中获取图片的方向?你能指导我吗? - ramsraj111
32
使用 Exif 总是返回 0。 - ramsraj111
3
为了避免出现OutOfMemoryError错误,我在将Bitmap传递给rotateBitmap()函数之前,对其进行了调整大小的操作,如下所示: Bitmap myBitmap = BitmapFactory.decodeFile(picturePath); Bitmap resized = Bitmap.createScaledBitmap(myBitmap, 720, 1280, true); photo = rotateBitmap(picturePath, resized); - Phil
@Phil 不错,但是你在调整大小的函数中失去了宽度和高度之间的比例。我更喜欢使用 originalBitmap.getWidth() / 2 和 originalBitmap.getHeight() / 2;(你可以用任何你想要的数字替换 2)。 - Cocorico
1
如果使用exif返回0,则可以通过查询Android媒体存储来获取正确的方向: String[] orientationColumn = {Media.ORIENTATION}; Cursor cur = resolver.query(imageUri, orientationColumn, null, null, null); int orientation = -1; if (cur != null && cur.moveToFirst()) { orientation = cur.getInt(cur.getColumnIndex(orientationColumn[0])); } - Torleif
显示剩余3条评论

5

对我来说,ExifInterface像这样使用效果不错:

ExifInterface exifInterface = new ExifInterface(imagePath);
degree = Integer.parseInt(exifInterface.getAttribute(ExifInterface.TAG_ORIENTATION));

或者您可以尝试使用MediaStore来获取图像的详细信息,如下所示:

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]));
} 

相似解决方法:ExifInterface始终返回1 希望对您有所帮助.. :)

4

对于那些阅读这篇文章的人,请确保使用Android Support Library中在2016年12月引入的exifinterface:

compile "com.android.support:exifinterface:25.1.0" // or newer

关于这个库的详细信息可以在对应的 Android Developers Blog 文章中找到:介绍 ExifInterface 支持库

他们还提供了一个处理存储在 exif 接口中旋转信息的示例代码:

int rotation = 0;
int orientation = exifInterface.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL);

switch (orientation) {
   case ExifInterface.ORIENTATION_ROTATE_90:
     rotation = 90;
     break;
  case ExifInterface.ORIENTATION_ROTATE_180:
     rotation = 180;
     break;
  case ExifInterface.ORIENTATION_ROTATE_270:
     rotation = 270;
     break;
}

4
我按照上一个回答的方法尝试着创建一个管理图片的系统,包括旋转、大小调整、缓存和加载到ImageView中,但是我可以告诉你这真的是一场噩梦。即使所有这些都完成了,有时仍会在某些设备上因为内存不足而崩溃。答案是正确的,但在Android中管理位图很困难。
我的观点是不要重复造轮子,已经有了一个完美的设计。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);

7
如果大多数设备上的前置摄像头图像翻转,那么这种方法无效。Glide不能解决图像方向问题。 - NMP
2
我使用 Glide,但它无法处理基于 Exif 标签的方向。 - Moses Aprico
你用的是哪个版本?我很确定它曾经解决过我的问题。 - Santi Iglesias
1
顺便说一下,翻转前置摄像头不是问题所在,还有其他解决方案。 - Santi Iglesias

4
我的解决方案是从输入流创建ExifInterface。不要尝试从路径创建它,因为这可能是内容提供者路径,将无法给出正确的结果。将方向转换为度数,并在需要时旋转图像。以下是使用支持库(例如androidx.exifinterface.media.ExifInterface)的解决方案的关键代码。
int orientation = 0;
InputStream input = mContext.getContentResolver().openInputStream(uri);
if (input != null){
    ExifInterface exif = new ExifInterface(input);
    orientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL);
    input.close();
}

这是我完整的代码,用于从画廊中选择正确方向的位图,并且还可以设置最大尺寸。如果要使用它,请确保检查空返回情况。

public Bitmap getBitmapFromGalleryUri(Context mContext, Uri uri, Double maxSize)throws IOException {
    int orientation = 0;

    InputStream input = mContext.getContentResolver().openInputStream(uri);
    if (input != null){
        ExifInterface exif = new ExifInterface(input);
        orientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL);
        //Log.d("Utils", "rotation value = " + orientation);
        input.close();
    }


    input = mContext.getContentResolver().openInputStream(uri);

    BitmapFactory.Options onlyBoundsOptions = new BitmapFactory.Options();
    onlyBoundsOptions.inJustDecodeBounds = true;
    onlyBoundsOptions.inDither = true;//optional
    onlyBoundsOptions.inPreferredConfig = Bitmap.Config.ARGB_8888;//optional
    BitmapFactory.decodeStream(input, null, onlyBoundsOptions);
    try {
        input.close();

    } catch (NullPointerException e) {
        e.printStackTrace();
    }

    if ((onlyBoundsOptions.outWidth == -1) || (onlyBoundsOptions.outHeight == -1)) {
        return null;
    }

    int originalSize = (onlyBoundsOptions.outHeight > onlyBoundsOptions.outWidth) ? onlyBoundsOptions.outHeight : onlyBoundsOptions.outWidth;

    double ratio = (originalSize > maxSize) ? (originalSize / maxSize) : 1.0;

    BitmapFactory.Options bitmapOptions = new BitmapFactory.Options();
    bitmapOptions.inSampleSize = getPowerOfTwoForSampleRatio(ratio);
    bitmapOptions.inDither = true; //optional
    bitmapOptions.inPreferredConfig = Bitmap.Config.ARGB_8888;//
    input = mContext.getContentResolver().openInputStream(uri);
    Bitmap bitmap = BitmapFactory.decodeStream(input, null, bitmapOptions);
    try {
        input.close();

    } catch (NullPointerException e) {
        e.printStackTrace();
    }
    Matrix matrix = new Matrix();

    //Log.d("Utils", "rotation value = " + orientation);

    int rotationInDegrees = exifToDegrees(orientation);
    //Log.d("Utils", "rotationInDegrees value = " + rotationInDegrees);

    if (orientation != 0) {
        matrix.preRotate(rotationInDegrees);
    }

    int bmpWidth = 0;
    try {
        bmpWidth = bitmap.getWidth();
    } catch (NullPointerException e) {
        e.printStackTrace();
    }
    Bitmap adjustedBitmap = bitmap;
    if (bmpWidth > 0) {
        adjustedBitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);
    }

    return adjustedBitmap;

}
private static int getPowerOfTwoForSampleRatio(double ratio){
    int k = Integer.highestOneBit((int)Math.floor(ratio));
    if(k==0) return 1;
    else return k;
}

public 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;
}

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