Android EXIF数据一直是0,怎么改?

Android EXIF data always 0, how to change it?

我有一个应用程序可以使用本机 Camera 拍摄照片,然后将它们上传到服务器。我的问题是所有照片的 EXIF 方向值为 0,这会弄乱其他地方的显示。

如何更改 EXIF 方向?我不是在寻找一种方法来针对每种情况进行更正,只是将其更改为不同的值。

我使用的是三星 Galaxy Note 4

我尝试了这个在拍照前设置相机方向的解决方案:Setting Android Photo EXIF Orientation

Camera c = Camera.open();
c.setDisplayOrientation(90);

Camera.Parameters params = mCamera.getParameters();
params.setRotation(0); // tried 0, 90, 180
c.setParameters(params);

但它不会影响生成的 EXIF 数据,它仍然始终为 0

我还尝试了这些解决方案,其中图像在拍摄后旋转:EXIF orientation tag value always 0 for image taken with portrait camera app android

虽然这会旋转照片,但 EXIF 方向仍然始终为 0。

我也试过直接设置EXIF数据:How to save Exif data after bitmap compression in Android

private Camera.PictureCallback mPicture = new Camera.PictureCallback() {
    @Override
    public void onPictureTaken(byte[] data, Camera camera) {
        final File pictureFile = getOutputMediaFile(MEDIA_TYPE_IMAGE, "");
        try {
            FileOutputStream fos = new FileOutputStream(pictureFile);

            ExifInterface exif = new ExifInterface(pictureFile.toString());
            exif.setAttribute(ExifInterface.TAG_ORIENTATION, "3");
            exif.saveAttributes();

            fos.write(data);
            fos.close();

            //upload photo..
        }
    }
}

但上传后 EXIF 方向仍然为 0。

我也看过这些解决方案:

Exif data TAG_ORIENTATION always 0

How to write exif data to image in Android?

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

但是都是通过旋转校正方向,不影响EXIF数据,或者直接设置EXIF数据,好像不行。

如何将文件的 EXIF 方向数据从 0 更改为 3?

更新:

这是我的上传代码:

Bitmap sBitmap = null;
final File sResizedFile = getOutputMediaFile(MEDIA_TYPE_IMAGE, "_2");
try {
    sBitmap = BitmapFactory.decodeStream(new FileInputStream(pictureFile), null, options);
} catch (FileNotFoundException e) {
    Log.e("App", "[MainActivity] unable to convert pictureFile to bitmap");
    e.printStackTrace();
    return;
}

// ... compute sw and sh int values

Bitmap sOut = Bitmap.createScaledBitmap(sBitmap, sw, sh, false);
Bitmap rotatedBitmap = rotateBitmap(sOut, 3);
FileOutputStream sfOut;
try {
    sfOut = new FileOutputStream(sResizedFile);
    rotatedBitmap.compress(Bitmap.CompressFormat.JPEG, 70, sfOut);
    sfOut.flush();
    sfOut.close();
    sBitmap.recycle();
    sOut.recycle();
    rotatedBitmap.recycle();
} catch (Exception e) {
    Log.e("App", "[MainActivity] unable to save thumbnail");
    e.printStackTrace();
    return;
}
// upload small thumbnail
TransferObserver sObserver = transferUtility.upload(
            "stills/small",        /* The bucket to upload to */
            filename + ".jpg",     /* The key for the uploaded object */
            sResizedFile           /* The file where the data to upload exists */
);

如您所见,EXIF 信息在 Android(尤其是三星设备)上不可靠。

然而,phone SQL 保存媒体对象引用的数据库是可靠的。我建议走这条路。

从 Uri 获取方向:

private static int getOrientation(Context context, Uri photoUri) {
    Cursor cursor = context.getContentResolver().query(photoUri,
            new String[]{MediaStore.Images.ImageColumns.ORIENTATION}, null, null, null);

    if (cursor.getCount() != 1) {
        cursor.close();
        return -1;
    }

    cursor.moveToFirst();
    int orientation = cursor.getInt(0);
    cursor.close();
    cursor = null;
    return orientation;
}

然后初始化旋转Bitmap:

public static Bitmap rotateBitmap(Context context, Uri photoUri, Bitmap bitmap) {
    int orientation = getOrientation(context, photoUri);
    if (orientation <= 0) {
        return bitmap;
    }
    Matrix matrix = new Matrix();
    matrix.postRotate(orientation);
    bitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, false);
    return bitmap;
}

如果您想更改图片的方向,请尝试以下代码段:

public static boolean setOrientation(Context context, Uri fileUri, int orientation) {
    ContentValues values = new ContentValues();
    values.put(MediaStore.Images.Media.ORIENTATION, orientation);
    int rowsUpdated = context.getContentResolver().update(fileUri, values, null, null);
    return rowsUpdated > 0;
}

如果您设置了图片的方向,以后它会一直设置在正确的方向。稍后需要使用ExifInterface,因为图像已经以适当的方式旋转。

如果这个方法不理想,可以试试this method

参考这个GitHub项目https://github.com/pandiaraj44/Camera. It has the custom camera activity where EXIF TAG_ORIENTATION was handled correctly. You can clone the project and check. For code details please refer https://github.com/pandiaraj44/Camera/blob/master/app/src/main/java/com/pansapp/cameraview/CameraFragment.java

有一个class读取和更新图像的这些信息。

要更新属性,您可以像这样使用

ExifInterface ef = new ExifInterface(filePath);
            ef.setAttribute(MAKE_TAG, MAKE_TAG);
            ef.setAttribute(ExifInterface.TAG_ORIENTATION, orientation+"");
            ef.saveAttributes();

阅读时可以这样使用

 ExifInterface exif = null;
        try {
            exif = new ExifInterface(absolutePath+path);
        } catch (IOException e) {
            e.printStackTrace();
        }
        int orientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION,
                ExifInterface.ORIENTATION_UNDEFINED);

希望对你有所帮助

原来我的代码能够设置 EXIF 数据,但是 Android 解释此数据的方式与 iOS 和 Chrome 在 [=30 上的解释方式之间存在差异=](我检查结果文件的地方)解释它。

这是设置 EXIF 方向所需的唯一代码:

ExifInterface exif = new ExifInterface(pictureFile.toString());
exif.setAttribute(ExifInterface.TAG_ORIENTATION, "3");
exif.saveAttributes();

但是,设置 3 在 iOS 上显示为 0,并且图像在 Chrome 中是横向的。

设置 6 在 iOS 上显示为 3,图像在 Chrome.

中显示正确

您已接受您自己的答案作为解决方案。无论如何,我的咆哮只是有用的附带信息......

您的答案中的 "However..." 表明虽然您现在知道原因,但您没有解决办法。

Turns out my code was able to set the EXIF data, but there is a discrepancy between how Android interprets this data and how iOS... interprets it.

这可能是字节顺序问题。您可以尝试通过在十六进制编辑器中打开您的 jpeg 并找到...来手动更改 Exif 的字节序设置...

  • 字节 45 78 69 66(构成 "Exif" 文本)后跟两个零字节 00 00
  • 那么应该是49 49(makes "II" text),表示以little endian格式读取数据。
  • 如果换成4D 4D(或"MM"文字)则阅读端 会将数据视为大端。

在 iOS 中进行测试,看看数字现在是否正确。

关于这个……

However, setting 3 shows up as0 on iOS and the image is sideways in Chrome.
Setting 6 shows up as 3 on iOS and the image looks right in Chrome.

我唯一可以补充的是 iOS Mac 是 Big Endian* 和 Android/PC 是 小字节序。本质上 Little Endian reads/writes 字节是从右到左,而 Big Endian 相反。

在二进制中:011 表示 3110 表示 6。 3 和 6 之间的区别只是那些 1 和 0 的位的读取顺序。因此,读取为 zero-one-one 的系统得到 3 的结果,但另一个 Endian 系统将读取一个与 one-one-zero 具有相同位的字节并告诉你结果是6。我无法解释为什么 "3 在没有分析字节的测试文件的情况下显示为 0",但这对我来说是一个奇怪的结果。

</end rant>
<sleep>

*注意:虽然Mac是Big Endian,仔细检查说iOS毕竟使用Little Endian系统。不过,您的数字仍然表明存在 Big vs Little Endian 问题。