You need to enable JavaScript to run this app.
导航
管理图片 Exif 信息最佳实践
最近更新时间:2024.11.22 14:30:11首次发布时间:2024.11.22 14:30:11

以下为您介绍如何使用 HEIF 编码库来实现常见 Exif 处理,包括获取 Exif 信息、获取 Exif 中旋转信息、新增/更新指定 Tag、删除指定 Tag、在编码过程中写入 Exif 信息。

背景介绍

Exif(Exchangeable Image File Format)是用于存储数码照片中的元数据的标准格式。Exif 数据通常嵌入在图像文件中,包含了关于图像及其创建过程的各种信息,如基本图像信息、相机信息、GPS 信息等。

在 Exif 中,Tag 是用于标识和存储特定元数据信息的单元。Exif 数据是由多个不同的 Tag 组成的,每个 Tag 都有特定的编号、名称和数据格式,用于存储某一方面的信息。例如,有一个 Tag 用于存储照片的拍摄时间,另一个 Tag 用于存储相机的光圈值等。

当需要读取或修改 Exif 数据时,实际上是对其中的各个 Tag 进行操作。例如,在编程中,如果要获取一张照片的拍摄时间,就需要找到对应的表示拍摄时间的 Exif Tag,然后按照其数据格式读取其中的数据。

应用场景

veImageX 提供了多个 Exif 相关接口,支持多种处理能力。以下是两个典型应用场景:

  1. 保持图像方向一致:例如,在将 jpeg 原图编码为 HEIC 的过程中,可以通过提取 Exif 信息中的旋转信息,确保生成的 Heic 编码产物与原图方向保持一致,避免出现图像显示角度异常的情况,提升用户查看图像的体验。

  2. 保护用户隐私:例如,当上传照片时,可删除与隐私相关的 Exif 数据,例如 GPS 位置信息、相机的唯一标识符等。在节省网络带宽的同时,还能保护用户的隐私安全。

注意事项

  • 支持的原图格式有 JPEG、PNG、WEBP、HEIC。
  • 不支持对原图写入 Exif 信息。

前提条件

准备操作

配置白名单

HEIF 编码库为白名单能力,使用前请提交工单联系技术支持配置白名单。

获取配置信息

您需要在集成 HEIF 编码库前,在 veImageX 获取以下配置信息。

  1. 获取 APPID:在 veImageX 控制台 的应用管理页面,创建 APP 类型应用后。在应用卡片中查看 AppID 。

  2. 获取 Token:在上面应用卡片中,单击 查看 Token,获取账号唯一的 Token 值。

  3. 购买授权:在上面应用卡片中的 License 授权区域,单击 购买授权 按钮。在购买弹窗中,SDK 类型选择客户端 HEIF 库,版本类型选择 HEIF 编码。调试期间,建议您购买一个月有效期的免费试用版授权。购买完成后,获取授权码。

集成 HEIF 编码库

类型Android 端iOS 端
集成文档集成 HEIF 编码库-Android集成 HEIF 编码库-iOS

集成流程

  1. 添加 Maven 仓库

  2. 获取 SDK 版本号。

说明

请使用 3.2.1-tob 及之后版本。

  1. 添加 SDK 依赖。

说明

请务必添加 authorization 、nativeheifencoder 和 nativelibexif 依赖。

  1. 配置混淆规则
  2. 配置授权

说明

填入获取的 AppID、Token、授权码。

  1. 编码准备
  1. 获取 SDK 版本号。

说明

请使用 1.44.1.6-premium 及之后版本

  1. 接入编码库。

说明

请务必接入 ImageHeifEncoder 和 ImageExifEditor。

  1. 配置授权

说明

填入获取的 AppID、Token、授权码。

具体使用

接入 HEIF 编码库后,您可按照以下内容实现对图片 Exif 信息的获取、新增、更新、删除,以及在编码过程中写入 Exif 信息,使编码后 HEIC 图携带指定的 Exif 信息。

传入图片

在手机中传入一张测试图片。例如,可以使用 adb 命令将 PC 端图片导入到手机中。

adb push /Users/xxxxx/Desktop/ExifPics/exif.png /storage/emulated/0/ExifPic/exif.png
	

获取图片的 Exif 信息

支持提取 WebP、JPEG、PNG、HEIC 格式图片内的 Exif 信息,并以 Map 的形式展示,key 为具体 Tag 名称,Value 为 Tag 的内容。具体代码示例如下所示:

  • 获取图片中的全部 Exif 信息

说明

因为提取 Exif 信息操作涉及到 IO 操作,往往较为耗时,因此建议在子线程中调用该方法。

具体接口描述请参考集成 HEIF 编码库-Android

thread {
    val inputFilePath = "/storage/emulated/0/ExifPic/exif.png" //指定要获取 Exif 信息的图片文件在 Android 设备存储中的路径
    val exifHelper = BDExifHelper(inputFilePath)
    // 获取经过解析的 exif 的 map 信息
    exifHelper.exifTagMap?.let {
        runOnUiThread {
            mTvExifInfo1.text = it.toString()
        }
    }
}
	

示例:获取的 Exif 信息如下所示。

{YResoution=72,ExifVersion=Exif Version 2.1,FlashpixVersion=FlashPix Version 1.0,XResolution=72,ColorSpace=sRGB,PixelXDimension=3264,PixelYDimension=2448,Orientation=Right-top,Resolution=lnch}
  • 获取 Exif 中的旋转信息

具体接口描述请参考集成 HEIF 编码库-Android

thread {
    val inputFilePath = "/storage/emulated/0/ExifPic/exif.png" //指定要获取 Exif 旋转信息的图片文件在 Android 设备存储中的路径
    val exifHelper = BDExifHelper(inputFilePath)
    // 获取 Exif 中旋转信息
    val oriInfo = exifHelper.obtainOriInfo()
    runOnUiThread {
        mTvExifInfo2.text =
            "Note == Ori:{val:${oriInfo.value}, rotation:${oriInfo.rotation}, flipInHorizontal:${oriInfo.flipInHorizontal}, flipInVertical:${oriInfo.flipInVertical}}"
    }
}
	

示例:获取的 Exif 旋转信息如下所示。

Note == Ori:{val:6,rotation:270,flipInHorizontal:false,flipinVertical:false}

编辑图片的 Exif 信息

通过指定要编辑的 Tag 以及该 Tag 对应的 IFD、格式等信息,实现新增 Tag 或更新 Tag。您可参考 Exif 标准文档查询指定 Tag 对应信息。

  • 若原图中 Tag 内容为空,则表示新增内容;

  • 若原图中 Tag 内容不为空,则表示更新内容。

具体接口描述请参考集成 HEIF 编码库-Android

//指定 Tag 的内容
val str = "用户评论:xxxxxx"      
val strBytes = str.toByteArray(Charsets.UTF_8)
//设置 Exif 相关配置
this.exifHelper.setExifTag(
    //指定 Tag 名称,具体取值请见 BDExifTags。这里以 UserComment 为例
    BDExifTags.BDEXIF_TAG_USER_COMMENT,  
    //指定 UserComment 对应的 ifd,取值如下所示
    // 0:对应 EXIF_IFD_0th 内的 Tag,如 Xresolution
		// 1:对应 EXIF_IFD_1st 内的 Tag,如 Artist
		// 2:对应 EXIF_IFD_EXIF 内的 Tag,如 UserComment
		// 3:对应 EXIF_IFD_GPS 内的 Tag,如 GPSVersionID
		// 4:对应 EXIF_IFD_INTEROPERABILITY 内的 Tag,如 RelatedImageFileFormat
    2,  
    //指定 Tag 对应的格式,根据官方 Exif 标准可知 UserComment 的 Type为 ASCII 格式   
    BDExifFormats.BDEXIF_FORMAT_ASCII,  
    //传入前面准备好的用户评论字符串字节数组的长度
    strBytes.size,
    //传入实际的用户评论字符串对应的字节数组内容
    strBytes
)
runOnUiThread {
    val oriInfo = this.exifHelper.obtainOriInfo()
    mTvExifInfo.text = "after update:" +
            this.exifEditor!!.exifTagMap.toString() + "Note == Ori:{val:${oriInfo.value}, rotation:${oriInfo.rotation}, flipInHorizontal:${oriInfo.flipInHorizontal}, flipInVertical:${oriInfo.flipInVertical}}"
}
	

示例:

  1. 添加 UserComment 前的 Exif 信息如下所示。
{YResoution=72,ExifVersion=Exif Version 2.1,FlashpixVersion=FlashPix Version 1.0,XResolution=72,ColorSpace=Uncalibrated,ResolutionUnit=lnch}
  1. 添加 UserComment 后的 Exif 信息如下所示。
{Resoution=72,ExifVersion=Exif Version 2.1,FlashpixVersion=FlashPix Version 1.0,XResolution=72,UserComment=用户评论:xxxxxx,ColorSpace=Uncalibrated,ResolutionUnit=lnch}

删除图片的 Exif 信息

通过指定 Tag 名称删除图片中的 Exif 信息。您可在获取解析后图片的 Exif 信息后,选择要删除的 Tag。解析后 Exif 内容为 Map 类型,key 为具体 Tag 名称,Value 为 Tag 的内容。

说明

在 Exif 的众多 Tag 中,YResolution、ExifVersion、FlashpixVersion、XResolution、ColorSpace、ResolutionUni 被称为 Base Exif Tag。

  • 如果删除时指定了 Base Exif Tag,那么 Tag 对应的值将恢复为默认值。

  • 如果删除时指定了非 Base Exif Tag,那么 Tag 则会被删除。

具体接口描述请参考集成 HEIF 编码库-Android

thread {
    val inputFilePath = "/storage/emulated/0/ExifPic/exif.png"
    val exifHelper = BDExifHelper(inputFilePath)
    val deleteTags: Array<String>? = arrayOf("Orientation", "xxx", ...)
    exifHelper.deleteExifTag(deleteTags)
}
	

示例:

  1. 删除前的 Exif 信息如下所示。
{YResoution=72,ExifVersion=Exif Version 2.1,FlashpixVersion=FlashPix Version 1.0,XResolution=72,ColorSpace=sRGB,PixelXDimension=3264,PixelYDimension=2448,Orientation=Right-top,Resolution=lnch}
  1. 删除后的 Exif 信息如下所示。
after delete:{YResoution=72,ExifVersion=Exif Version 2.1,FlashpixVersion=FlashPix Version 1.0,XResolution=72,ColorSpace=Uncalibrated,ResolutionUnit=lnch}Note == Ori:{Val:0,rotation:0,flipInHorizontal:false,flipinVertical:false}

编码为携带 Exif 信息的 Heic 图片

您需要在获取图片 Exif 信息后,参考以下内容在原始图片编码为 Heic 的过程中,写入您指定的 Exif 数据,从而获取带有指定 Exif 数据的 HEIC 图。

例如,可以将 jpeg 原图编码为 Heic 的过程中,可以通过提取 Exif 信息中的旋转信息,并在编码过程写入使生成的 Heic 产物与原 jpeg 的方向保持一致。

具体接口描述请参考集成 HEIF 编码库-Android

val  inputFilePath = getInputFilePath()

val  exifHelper = BDExifHelper(inputFilePath)

val  outputStream : FileOutputStream = FileOutputStream(
        getOutFilePath(
                withCreate =  true,
                index = infoBean?.index
        )
)

var  heifEncodeConfig: HeifEncodeConfig = HeifEncodeConfig(inputFilePath, outputStream)

heifEncodeConfig.setExifData(exifHelper.exifData)

HeifEncoder.encode(heifEncodeConfig)  //编码后的 Heic 图片会保存在 outputStream 中,并持有对应 Exif 数据
	

常见问题

如何获取一个 Tag 的 IFD、Format 等信息?

  1. 在已知需要管理的 Tag 名称后,例如 Tag 为 Orientation,那么您可参考 Exif 标准文档查询到 Orientation 是属于 0th IFD。
    alt
  2. 根据 Tag 的名称查询 Orientation 的 Type,Type 为 SHORT。
    alt