【问题标题】:Image Orientation - Android图像方向 - Android
【发布时间】:2015-07-10 08:48:55
【问题描述】:

在过去一个月左右的时间里,我一直在断断续续地解决这个错误。每次我认为我已经修复它时,它似乎都会以某种形式回来。

这是旧的 Android“图像旋转 90 度”错误。我在这里(StackOverFlow)上阅读了无数帖子,并尝试了多种方法,但似乎无法修复它。

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

在我的应用程序中,用户选择他/她的个人资料图片,然后将其设置为 ImageView。图片是从手机图库中选择的

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

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

// 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

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

【问题讨论】:

  • Richard - 额外的 ExifInterface Orientations 真的对你有帮助吗?抱歉耽搁了我打了一轮高尔夫球...
  • 完全不用担心:)。到目前为止,似乎如此。我的一个 Beta 测试人员不在,所以他回来时会让他进行测试。但这绝对有助于解决我今天遇到的其他问题。非常感谢:)。
  • 很高兴来到这里.. 再次请注意,根据实际设备硬件,每个设备的相机方向会有所不同。某些设备的边框可能会更窄,这会导致相机在任一方向上旋转 90 度或更多度。
  • 我会记住这一点。非常感谢您的帮助。

标签: java android image bitmap exif


【解决方案1】:

您需要考虑所有方向,而不仅仅是 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;
}

【讨论】:

  • 非常感谢您的回复,我刚刚实现了您的方法,但我使用的测试图像仍然逆时针旋转了 90 度
  • @Richard 您是否尝试记录 exif int 以查看图像读取的内容?
  • int rotation = 0, int rotationInDegrees = 0. 刚刚回来
  • 我自己在纵向模式下拍摄了我正在测试的当前图像。 Android 图库也以纵向显示。所以我真的不确定发生了什么。
【解决方案2】:

这个问题真的很糟糕!我注意到这是选择照片而不是拍照时的问题。我发现答案隐藏在这个裁剪库的代码中,它似乎总是在裁剪时正确显示东西https://github.com/jdamcd/android-crop(尽管它有时会返回错误方向的东西)。无论如何,首先按照我在此答案中选择的方式选择照片:Pick image from fragment always return resultcode 0 in some devices

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

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

这是课程:

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

}

【讨论】:

  • 对不起,“在你需要的地方做这个”太含糊了。我的意思是,在活动结果中,在与选择照片相关的结果中,调用:setRotationVariables(data.getData) 其中“数据”是 Intent 参数的名称
【解决方案3】:

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

我无法在 mochilogic 答案的评论中添加所有这些内容,因此我将在此处写下:如果您不喜欢在 setRotationVariables(data.getData) 中使用它 - 这是从他的答案和此方法中使用类 ImageOrientationUtil 的另一种方法:

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

您可以将库中的 Uri 发送到此方法,使其返回正确的旋转角度,如他所做的那样按 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 并对其进行缩放和旋转,然后将其作为位图返回。

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

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-06-08
    • 1970-01-01
    • 2014-05-19
    • 1970-01-01
    相关资源
    最近更新 更多