【问题标题】:Saved Bitmap has incorrect orientation保存的位图方向不正确
【发布时间】:2019-02-11 09:20:55
【问题描述】:

这个问题已经被问过无数次了,但在保存Bitmap时,没有一个能真正解决旋转问题。

这就是我最初将Bitmap 保存到我的设备的方式:

FFmpegMediaMetadataRetriever mmr = new FFmpegMediaMetadataRetriever();
mmr.setDataSource(mStringFilePath);
//Time is us
int mPresentationTime = mPlayer.getPresentationTime();
Bitmap mBitmap = mmr.getFrameAtTime(mPresentationTime, FFmpegMediaMetadataRetriever.OPTION_CLOSEST);

File mFileBitmap = new File(directoryToStore, "test.png");
try {
    FileOutputStream outputStream = new FileOutputStream(mFileBitmap);
    mBitmap.compress(Bitmap.CompressFormat.PNG, 100, outputStream);
    outputStream.close();
} catch (Exception e) {
    e.printStackTrace();
}

上面保存了方向错误的.png

然后我看到this 的答案,但问题是它会将已保存的Bitmap 旋转到正确的方向。例如,如果您想将Bitmap 设置为ImageView,这很好。但是,如果我想分享Bitmap,或者想在设备库中打开它,那么方向仍然是不正确的。然后我必须执行与上述相同的过程 - FileOutputStream 等。这将导致同样的问题。

如何以正确的方向将Bitmap 保存到设备中?

编辑 1

尝试Long提供的答案。

我使用您提供的 2 方法创建了一个名为 RotateBit 的新类。然后我将代码更改为:

FFmpegMediaMetadataRetriever mmr = new FFmpegMediaMetadataRetriever();
mmr.setDataSource(mStringFilePath);
//Time is us
int mPresentationTime = mPlayer.getPresentationTime();
Bitmap mBitmapBeforeRotation = mmr.getFrameAtTime(mPresentationTime, FFmpegMediaMetadataRetriever.OPTION_CLOSEST);

int rotatingInt = RotateBit.getBitmapOriention(mStringFilePath);
Bitmap mBitmap = RotateBit.rotateBitmap(mBitmapBeforeRotation, rotatingInt);

File mFileBitmap = new File(directoryToStore, "test.png");
try {
    FileOutputStream outputStream = new FileOutputStream(mFileBitmap);
    mBitmap.compress(Bitmap.CompressFormat.PNG, 100, outputStream);
    outputStream.close();
} catch (Exception e) {
    e.printStackTrace();
}

但旋转仍然不正确。

编辑 2

我注意到这个问题与FFmpegMediaMetadataRetriever有关,使用MediaMetadataRetriever时不会出现这个问题。

【问题讨论】:

  • 你有Bitmap mBitmap 所以旋转它然后保存
  • @pskink 请参阅编辑 1。
  • "rotation is incorrect." 是什么意思? rotatingInt 你有什么?
  • @pskink 如果视频是纵向录制的,那么我想将位图保存为相同的尺寸(纵向),但 Bitmap/png 向左旋转了 90 度。跨度>
  • rotatingInt 中有什么?尝试使用Log.d?

标签: java android bitmap


【解决方案1】:
public static int getBitmapOriention(String path){
    ExifInterface exif = null;
    int orientation = 0;
    try {
        exif = new ExifInterface(path);
        orientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION,
                ExifInterface.ORIENTATION_UNDEFINED);
        //Log.e("getBitmapOriention", "getBitmapOriention: "+orientation );
    } catch (Exception e) {
        e.printStackTrace();
    }
    return orientation;
}
/**
 * @param bitmap bitmap from path
 * @param orientation ExifInterface
 * @return oriention flag
 */
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;
    }
}

对我有用

rotateBitmap(mBitmap,getBitmapOriention(mPath));

【讨论】:

  • 请参阅编辑 1
  • 这段代码在我的设备上运行良好。一些三星设备也能正常工作,但使用你的设备我仍然没有找到解决方案
【解决方案2】:

您需要使用ExifInterface 进行旋转。

public static void normalizeImageForUri(Context context, Uri uri, ImageView imgImageView) {
        try {
//            Log.d(TAG, "URI value = " + uri.getPath());
            ExifInterface exif = new ExifInterface(getRealPathFromURI(context, uri));
//            Log.d(TAG, "Exif value = " + exif);
            int orientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_UNDEFINED);
            Bitmap bitmap = MediaStore.Images.Media.getBitmap(context.getContentResolver(), uri);
            Bitmap rotatedBitmap = rotateBitmap(bitmap, orientation);
            imgImageView.setImageBitmap(rotatedBitmap);
        } catch (IOException e) {
            e.printStackTrace();
        }
}

用于检查位图的旋转:

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

更多详情请参考此链接:

How to get the Correct orientation of the image selected from the Default Image gallery

https://github.com/google/cameraview/issues/22#issuecomment-269321811

【讨论】:

  • 让我试试这个,因为我使用一个应用程序从视频文件中获取 Exif 信息,并且旋转为 90。所以,这可能只是工作。
  • 奇怪的是,当我通过 FFmpeg 运行视频然后尝试保存图像时,旋转是正确的。
  • 你用的是什么设备?
  • 三星 J7 Pro。是的,我确实尝试过。
  • 每台三星设备都有方向问题。这在我的应用程序中完美运行。
【解决方案3】:

我已经设法创建了一个解决方法。这可能不应该这样做,但现在,它可以工作。


我的Activity 设置为纵向/横向,具体取决于我正在播放的视频的旋转。所以,我可以用它来确定是否旋转Bitmap

我首先像以前一样创建一个Bitmap

Bitmap mBitmapBeforeRotation = mmr.getFrameAtTime(mPresentationTime, FFmpegMediaMetadataRetriever.OPTION_CLOSEST);

然后我得到屏幕的旋转,Bitmap 的宽度/高度并相应地旋转Bitmap

int mBitW = Integer.parseInt(String.valueOf(mBitmapBeforeRotation.getWidth()));
int mBitH = Integer.parseInt(String.valueOf(mBitmapBeforeRotation.getHeight()));

int orientation = getResources().getConfiguration().orientation;
if (orientation == Configuration.ORIENTATION_LANDSCAPE) {
// Do nothing because Landscape doesn't give wrong Bitmap rotation
} else {
    if (mBitW>mBitH){
    Matrix matrix = new Matrix();
    matrix.postRotate(90);
    Bitmap fixedBitmapRotation = Bitmap.createBitmap(mBitmapBeforeRotation, 0, 0, mBitmapBeforeRotation.getWidth(), mBitmapBeforeRotation.getHeight(), matrix, true);

    String nameOfImage = "nameOfImage" + ".jpeg";
    final File mFile = new File(directoryyToStore, nameOfImage);

    try {
        FileOutputStream outputStream = new FileOutputStream(mFile);
        fixedBitmapRotation.compress(Bitmap.CompressFormat.JPEG, 100, outputStream);
        outputStream.close();

    } catch (Exception e) {
        e.printStackTrace();
    }
    texeBit = mp.getPath();

}

只是一个旁注。 我不必使用屏幕旋转来确定是否应该旋转Bitmap。我可以先用MediaMetadataRetriever 创建一个Bitmap(因为MediaMetadataRetriever 给了我Bitmap 的正确旋转),然后按如下方式进行:

int videoWidth;
int videoHeight;

MediaMetadataRetriever retriever = new MediaMetadataRetriever();
retriever.setDataSource(this, mVideoUri);

Bitmap bmp = retriever.getFrameAtTime();

videoWidth = bmp.getWidth();
videoHeight = bmp.getHeight();

我可以像上面那样做。


我不会接受我自己的答案,因为我没有在多个设备上进行过测试,而且这是解决此问题的一种非常“hack-ish”的方式。

【讨论】:

    猜你喜欢
    • 2017-06-25
    • 1970-01-01
    • 2015-05-04
    • 2020-09-09
    • 2016-04-16
    • 2010-11-23
    • 1970-01-01
    相关资源
    最近更新 更多