图像方向 - Android

Image Orientation - Android

在过去一个月左右的时间里,我一直在断断续续地与这个错误作斗争。每次我认为我已经修复它时,它似乎会以某种形式恢复。

这是旧的 Android "Image Rotated 90 degrees" 错误。我在这里 (Whosebug) 上阅读了无数帖子,也尝试了很多方法,但似乎无法修复它。

我仍然收到旋转不正确的图像。

在我的应用程序中,用户选择 his/her 个人资料图片,然后将其设置为 ImageView。图片选自 Phones 图库

两天前我实现了以下代码,它适用于我在 Phone 上测试过的所有图像。然而,当我的一位 Beta 测试人员尝试时,他的图像再次旋转。他将图像发给我进行测试,但它们在我的 Phone 上显示良好。所以我越来越沮丧。

这是我用来获取图像方向的方法:

// Gets an Images Orientation
public static int getOrientationEXIF(Context context, Uri uri) {

    int orientation = 0;

    try {

        ExifInterface exif = new ExifInterface(uri.getPath());

        orientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL);

        switch (orientation) {

            case ExifInterface.ORIENTATION_ROTATE_90:
                orientation = 90;
                return orientation;

            case ExifInterface.ORIENTATION_ROTATE_180:
                orientation = 180;
                return orientation;

        }

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

    return 0;
}

然后我使用这个方法得到一个旋转的位图:

// Rotate a Bitmap
public static Bitmap rotate(float rotationValue, String filePath) {
    Bitmap original= BitmapFactory.decodeFile(filePath);

    int width = original.getWidth();

    int height = original.getHeight();

    Matrix matrix = new Matrix();

    matrix.postRotate(rotationValue);

    Bitmap rotated = Bitmap.createBitmap(original, 0, 0, width, height, matrix, true);

    return rotated;
}

我只是不知道该怎么做了。

如果有人能帮我解决这个问题,我会很高兴

提前致谢


更新

在实施建议的方法后,我刚刚在我的日志中看到了以下代码行:

JHEAD can't open 'file:/external/images/media/3885'

我不确定这是什么意思


更新 #2

我想我可能已经解决了问题,我得到了文件的正确图像路径。

您需要考虑所有方向,而不仅仅是 90 或 180。我正在使用这个

    File curFile = new File("path-to-file"); // ... This is an image file from my device.
    Bitmap rotatedBitmap;

            try {
                ExifInterface exif = new ExifInterface(curFile.getPath());
                int rotation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL);
                int rotationInDegrees = exifToDegrees(rotation);
                Matrix matrix = new Matrix();
                if (rotation != 0f) {matrix.preRotate(rotationInDegrees);}
                rotatedBitmap = Bitmap.createBitmap(bitmap,0,0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);


            }catch(IOException ex){
                Log.e(TAG, "Failed to get Exif data", ex);
            }

和:

 /**
 * Gets the Amount of Degress of rotation using the exif integer to determine how much
 * we should rotate the image.
 * @param exifOrientation - the Exif data for Image Orientation
 * @return - how much to rotate in degress
 */
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;
}

这个问题真的很糟糕!我注意到这是选择照片而不是拍摄照片时的一个问题。我在这个裁剪库的代码中找到了答案,它似乎总是正确显示内容 https://github.com/jdamcd/android-crop while cropping (despite it sometimes returning things with wrong orientation afterwards). Anyways, first start off by choosing the photo the way I choose in this answer:

接下来在您需要的地方执行此操作:

private void setRotationVariables(Uri uri)
{
   m_rotationInDegrees = ImageOrientationUtil.getExifRotation(ImageOrientationUtil
        .getFromMediaUri(
            this,
            getContentResolver(),
            uri));
}

这里是 class:

import android.content.ContentResolver;
import android.content.Context;
import android.database.Cursor;
import android.media.ExifInterface;
import android.net.Uri;
import android.os.ParcelFileDescriptor;
import android.provider.MediaStore;
import android.support.annotation.Nullable;
import android.text.TextUtils;
import java.io.Closeable;
import java.io.File;
import java.io.FileDescriptor;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;


public class ImageOrientationUtil
{

private static final String SCHEME_FILE = "file";
private static final String SCHEME_CONTENT = "content";

public static void closeSilently(@Nullable Closeable c) {
    if (c == null) return;
    try {
        c.close();
    } catch (Throwable t) {
        // Do nothing
    }
}

public static int getExifRotation(File imageFile) {
    if (imageFile == null) return 0;
    try {
        ExifInterface exif = new ExifInterface(imageFile.getAbsolutePath());
        // We only recognize a subset of orientation tag values
        switch (exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_UNDEFINED)) {
            case ExifInterface.ORIENTATION_ROTATE_90:
                return 90;
            case ExifInterface.ORIENTATION_ROTATE_180:
                return 180;
            case ExifInterface.ORIENTATION_ROTATE_270:
                return 270;
            default:
                return ExifInterface.ORIENTATION_UNDEFINED;
        }
    } catch (IOException e) {
      //  Log.e("Error getting Exif data", e);
        return 0;
    }
}

@Nullable
public static File getFromMediaUri(Context context, ContentResolver resolver, Uri uri) {
    if (uri == null) return null;

    if (SCHEME_FILE.equals(uri.getScheme())) {
        return new File(uri.getPath());
    } else if (SCHEME_CONTENT.equals(uri.getScheme())) {
        final String[] filePathColumn = { MediaStore.MediaColumns.DATA, MediaStore.MediaColumns.DISPLAY_NAME };
        Cursor cursor = null;
        try {
            cursor = resolver.query(uri, filePathColumn, null, null, null);
            if (cursor != null && cursor.moveToFirst()) {
                final int columnIndex = (uri.toString().startsWith("content://com.google.android.gallery3d")) ?
                    cursor.getColumnIndex(MediaStore.MediaColumns.DISPLAY_NAME) :
                    cursor.getColumnIndex(MediaStore.MediaColumns.DATA);
                // Picasa images on API 13+
                if (columnIndex != -1) {
                    String filePath = cursor.getString(columnIndex);
                    if (!TextUtils.isEmpty(filePath)) {
                        return new File(filePath);
                    }
                }
            }
        } catch (IllegalArgumentException e) {
            // Google Drive images
            return getFromMediaUriPfd(context, resolver, uri);
        } catch (SecurityException ignored) {
            // Nothing we can do
        } finally {
            if (cursor != null) cursor.close();
        }
    }
    return null;
}

private static String getTempFilename(Context context) throws IOException {
    File outputDir = context.getCacheDir();
    File outputFile = File.createTempFile("image", "tmp", outputDir);
    return outputFile.getAbsolutePath();
}

@Nullable
private static File getFromMediaUriPfd(Context context, ContentResolver resolver, Uri uri) {
    if (uri == null) return null;

    FileInputStream input = null;
    FileOutputStream output = null;
    try {
        ParcelFileDescriptor pfd = resolver.openFileDescriptor(uri, "r");
        FileDescriptor fd = pfd.getFileDescriptor();
        input = new FileInputStream(fd);

        String tempFilename = getTempFilename(context);
        output = new FileOutputStream(tempFilename);

        int read;
        byte[] bytes = new byte[4096];
        while ((read = input.read(bytes)) != -1) {
            output.write(bytes, 0, read);
        }
        return new File(tempFilename);
    } catch (IOException ignored) {
        // Nothing we can do
    } finally {
        closeSilently(input);
        closeSilently(output);
    }
    return null;
}

}

mochilogic 的回答很好,但他的评论也很正确: 抱歉,“在你需要的地方做这件事”太含糊了.."

我无法将所有这些添加到 mochilogic 答案的评论中,因此我将在此处写下:如果您不喜欢在 setRotationVariables(data.getData) 中使用它 - 这是使用 [=29= 的另一种方法] ImageOrientationUtil 来自他的回答和这个方法:

            private void setRotationVariables(Uri uri)
            {
                m_rotationInDegrees = ImageOrientationUtil.getExifRotation           
               (ImageOrientationUtil.getFromMediaUri(
                this,
                getContentResolver(),
               uri));
              }

您可以将 Uri 从画廊发送到此方法,使其 return 像他那样按 memebr 或像我那样按值正确旋转:

  private static int setRotationVariables(Uri uri) {
    int rotationInDegrees = ImageOrientationUtil.getExifRotation(ImageOrientationUtil
            .getFileFromMediaUri(
                    appCtx,
                    appCtx.getContentResolver(),
                    uri));
    Log.d(TAG, "setRotationVariables:" + "according to original Image Uri Exif details we need to rotate in "+rotationInDegrees + " Degrees");
    return rotationInDegrees;
}

然后在将 Uri 缩放为位图后的调用函数上,您可以使用带有矩阵的 rotationInDegrees 创建位图。

你可以在我的代码中看到它,在这个方法中我获取 Uri 并对其进行缩放和旋转,然后 return 将其作为位图。

但首先 - 基本上这就是您需要的:

    int rotationDegree = setRotationVariables(uri);

   if (rotationDegree > 0) {
        Matrix matrix = new Matrix();
        matrix.setRotate(rotationDegree);
        Log.w(TAG, "recreating bitmap with rotation of " + rotationDegree + " degrees" );
        bitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);
    }

如果有人需要,这是完整的方法代码。

    public static Bitmap getScaledBitmapFromUri(Uri uri) throws FileNotFoundException, IOException {
           final int TRY_SCALE_TO_THIS_SIZE = 1024;
            Log.d(TAG, "getScaledBitmapFromUri:: calling       setRotationVariables() to figure rotationDegree");
           int rotationDegree = setRotationVariables(uri);
           Context ctx = MessagingApp.getContext();
           InputStream input = ctx.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);
           input.close();

           if ( (onlyBoundsOptions.outWidth == -1) || (onlyBoundsOptions.outHeight == -1) )
                return null;
           int BiggestOriginalSize = (onlyBoundsOptions.outHeight > onlyBoundsOptions.outWidth) ? onlyBoundsOptions.outHeight : onlyBoundsOptions.outWidth;
           //we will add 1 to Math.round (BiggestOriginalSize / (double)TRY_SCALE_TO_THIS_SIZE) in order to harden the scaling(we need smaller images for this project!)
    double ratio = (BiggestOriginalSize > TRY_SCALE_TO_THIS_SIZE) ? (1 + Math.round(BiggestOriginalSize / (double) TRY_SCALE_TO_THIS_SIZE)) : 1.0;
    Log.w(TAG, "getScaledBitmapFromUri:: originalSize: " + BiggestOriginalSize + "PX, TRY_SCALE_TO_THIS_SIZE (if original is bigger):" + TRY_SCALE_TO_THIS_SIZE +"PX");

    BitmapFactory.Options bitmapOptions = new BitmapFactory.Options();
    bitmapOptions.inSampleSize = getPowerOfTwoForSampleRatio(ratio);   //this one will give abstract results (sometimes bigger then TRY_SCALE_TO_THIS_SIZE)
    Log.w(TAG, format("bitmapOptions.inSampleSize: " + bitmapOptions.inSampleSize));
    bitmapOptions.inJustDecodeBounds = false;   //check this out!!! maybe delete?

    bitmapOptions.inDither = true;//optional
    bitmapOptions.inPreferredConfig = Bitmap.Config.ARGB_8888;//optional
    //bitmapOptions.rogetrotationInDegrees
    input = ctx.getContentResolver().openInputStream(uri);
    Bitmap bitmap = BitmapFactory.decodeStream(input, null, bitmapOptions);
    //bitmap = findExactInSampleSize(onlyBoundsOptions, TRY_SCALE_TO_THIS_SIZE, bitmap);   // this one will never give bigger length then TRY_SCALE_TO_THIS_SIZE
    if (rotationDegree > 0) {
        Matrix matrix = new Matrix();
        matrix.setRotate(rotationDegree);
        Log.w(TAG, "recreating bitmap with rotation of " + rotationDegree + " degrees" );
        bitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);
    }
    Log.w(TAG, "after decodeStream : bitmap.getWidth(): " + bitmap.getWidth() + "PX, bitmap.getHeight(): " + bitmap.getHeight() +"PX.");
    input.close();
    return bitmap;
}