Android EXIF数据总是0,怎么改?

Posted

技术标签:

【中文标题】Android EXIF数据总是0,怎么改?【英文标题】:Android EXIF data always 0, how to change it? 【发布时间】:2017-01-16 22:36:38 【问题描述】:

我有一个应用程序,它使用本机 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 Orientation还是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

how to set camera Image orientation?

但它们都涉及通过旋转来纠正方向,这不会影响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 */
);

【问题讨论】:

您在哪个设备上尝试? 三星 Galaxy Note 4 @Cbas,您上传到在线存储桶的“EXIF方向值为0” 之前还是之后?我刚刚测试了亚马逊的 AWS 存储桶,它们上传后保留了 EXIF。您必须使用不同的基于存储桶的服务器(例如:Google Cloud),对吧?在您的第一个代码 sn-p 之后,您说 “它不会影响生成的 EXIF 数据,它仍然始终为 0” 所以这是代码问题?然后在你的第二个代码 sn-p 之后你说 “但是 EXIF 方向仍然是 0 上传后。” 所以这是一个上传/服务器问题?请确认 EXIF 值在上传前还是仅在上传后始终为 0? @VC.One 上传前为 0,绝对是代码问题 【参考方案1】:

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

但是,保存对 Media 对象的引用的手机 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;

如果你想改变图片的方向,试试下面的sn-p:

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

【讨论】:

是的,你是对的,我们应该从图库中获取 Uri 数据的方向。谢谢你的解决方案对我有用!!! 这个答案你绝对应该得到超过 10 票。谢谢 像魅力一样工作。谢谢! java.lang.NullPointerException: Attempt to invoke interface method 'int android.database.Cursor.getCount()' on a null object reference 不幸的是【参考方案2】:

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

您的答案中的“但是...” 表明您现在知道原因,但没有解决办法。

原来我的代码能够设置 EXIF 数据,但是有一个 Android如何解释这些数据与ios如何... 解释它。

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

字节45 78 69 66(生成“Exif”文本)后跟两个零字节00 00。 那么它应该是49 49(使“II”文本)这意味着以小端格式读取数据。 如果你用4D 4D(或“MM”文本)替换它,那么阅读端 会将数据视为大端。

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

关于这个……

但是,设置3 在 iOS 上显示为0 并且图像是横向的 铬。 设置6 在iOS 上显示为3,并且图像在Chrome 中看起来正确。

我唯一可以补充的是 iOS Mac 是 Big Endian* 而 Android/PC 是 Little Endian。本质上,Little Endian 从右到左读取/写入字节,而 Big Endian 则相反。

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

&lt;/end rant&gt;&lt;sleep&gt;

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

【讨论】:

【参考方案3】:

事实证明,我的代码能够设置 EXIF 数据,但 Android 解释此数据的方式与 Mac 上的 iOS 和 Chrome(我正在检查结果文件)如何解释它之间存在差异。

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

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

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

设置 6 在 iOS 上显示为 3,并且图像在 Chrome 中看起来正确。

【讨论】:

+1 表示自己的问题的进展。您应该提到过 Android / iOS 测试问题。我唯一可以补充的是,iOS 是 Big Endian,而 Android 是 Little Endian。我将在答案框中展开...【参考方案4】:

参考这个 GitHub 项目https://github.com/pandiaraj44/Camera。它具有正确处理 EXIF TAG_ORIENTATION 的自定义相机活动。您可以克隆项目并检查。代码详情请参考https://github.com/pandiaraj44/Camera/blob/master/app/src/main/java/com/pansapp/cameraview/CameraFragment.java

【讨论】:

ExifInterface:无效图像:ExifInterface 获得了不受支持的图像格式文件(ExifInterface 仅支持 JPEG 和某些 RAW 图像格式)或损坏的 JPEG 文件到 ExifInterface。当我运行你的代码时【参考方案5】:

有一个类可以读取和更新这些图像信息。

要更新你可以像这样使用的属性

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

希望对你有帮助

【讨论】:

以上是关于Android EXIF数据总是0,怎么改?的主要内容,如果未能解决你的问题,请参考以下文章

在图像上设置 Exif 数据总是在 Android 7 上返回错误

从图库中选择的 Android 图像方向始终为 0:Exif TAG

如何从 Android 相机图像中去除 EXIF 数据

如何在Android中将exif数据写入图像?

Android 下的EXIF

设置 Android 照片 EXIF 方向