媒体附加属性:ExifInterface
Exif:可交换图像文件格式(Exchangeable image file format),是一种用于存储照片元数据的标准格式。包含了照片的拍摄参数和环境等信息。比如图片的方向Orientation,方向信息用于指示照片的正确显示方向,以便在不同设备上正确显示图片。
TIFF:标签图像文件格式(Tagged Image File Format),是一种广泛使用的图像文件格式,被广泛用于数字相机和数字图像处理软件中。在 TIFF 文件中,IFD 用于存储图像的数据和元数据,如图像的高度、宽度、位深度、颜色空间等。
IFD:图像文件目录(Image File Directory),它的主要作用是组织和存储图像相关的信息,方便对图像数据的读取和修改。是 TIFF文件格式中的一个重要组成部分。
借助工具可以查看到照片里的这些信息,推荐两个在线查看的工具网站:exif.tuchong和bejson.com/image/exif。
1、ExifInterface
在Android中,借助ExifInterface类描述多媒体文件的附加信息,比如拍摄的设备厂商,日期时间,曝光时间,快门速度等。自Android 2.0开始就已经有ExifInterface类,位于android.media包中。
1.1、构造
通常传入媒体文件file对象构造一个包含该媒体信息的ExifInterface实例:
public ExifInterface(@NonNull File file) throws IOException {
if (file == null) {
throw new NullPointerException("file cannot be null");
}
initForFilename(file.getAbsolutePath());
}
或者直接传入包含路径的文件名filename:
/**
* Reads Exif tags from the specified image file.
*
* @param filename the name of the file of the image data
* @throws NullPointerException if file name is null
* @throws IOException if an I/O error occurs while retrieving file descriptor via
* {@link FileInputStream#getFD()}.
*/
public ExifInterface(@NonNull String filename) throws IOException {
if (filename == null) {
throw new NullPointerException("filename cannot be null");
}
initForFilename(filename);
}
当然还有另外三种不常用的构造方式,传入文件描述符或媒体流:
public ExifInterface(@NonNull InputStream inputStream, @ExifStreamType int streamType)
throws IOException {
}
public ExifInterface(@NonNull InputStream inputStream) throws IOException {
this(inputStream, STREAM_TYPE_FULL_IMAGE_DATA);
}
public ExifInterface(@NonNull FileDescriptor fileDescriptor) throws IOException {
}
推荐使用AndroidX库androidx.exifinterface.media包中的ExifInterface以提供更好的兼容性:Avoid using android.media.ExifInterface; use androidx.exifinterface.media.ExifInterface instead More... (Ctrl+F1) ,Inspection info: The android.media.ExifInterface implementation has some known security bugs in older versions of Android. There is a new implementation available of this library in the support library, which is preferable.
1.2、其它方法*
ExifInterface类还提供了很多实用的方法,比如通过getThumbnail()方法可以生成多媒体缩略图,返回一个字节数组:
/**
* Returns the JPEG compressed thumbnail inside the image file, or {@code null} if there is no
* JPEG compressed thumbnail.
* The returned data can be decoded using
* {@link android.graphics.BitmapFactory#decodeByteArray(byte[],int,int)}
*/
public byte[] getThumbnail() {
if (mThumbnailCompression == DATA_JPEG || mThumbnailCompression == DATA_JPEG_COMPRESSED) {
return getThumbnailBytes();
}
return null;
}
然后可以再借助BitmapFactory的decodeByteArray(byte[],int,int)方法来解析这个字节数组从而得到Bitmap进行显示或者保存缩略图。
2、媒体属性读写
图片的的Exif信息和MP3的ID3标签类似,使用了属性和值的存储方式。
2.1、获取媒体属性
获取媒体属性可以通过ExifInterface的getAttributeInt(String tag,int defaultValue)方法,该方法的第二个参数为我们设置的默认值,如果成功获取则返回相应Tag的值,获取不到对应属性则返回传入的默认值。
val orientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL)
或通过getAttribute(String tag)方法,该方法直接返回结果,如果获取属性失败则返回null。
val orientation = exif.getAttribute(ExifInterface.TAG_ORIENTATION)
2.2、写入更改媒体属性
通过ExifInterface的setAttribute(String tag, String value)方法来设置或修改对应TAG属性的值:
val exif = ExifInterface(path)
exif.setAttribute(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_ROTATE_180.toString())
2.3、多媒体TAG
ExifInterface中定义了非常丰富的媒体TAG可以直接使用:
// Primary image IFD Exif Private tags (See JEITA CP-3451C Section 4.6.8 Tag Support Levels)
private static final ExifTag[] IFD_EXIF_TAGS = new ExifTag[] {
new ExifTag(TAG_EXPOSURE_TIME, 33434, IFD_FORMAT_URATIONAL),
new ExifTag(TAG_F_NUMBER, 33437, IFD_FORMAT_URATIONAL),
new ExifTag(TAG_EXPOSURE_PROGRAM, 34850, IFD_FORMAT_USHORT),
new ExifTag(TAG_SPECTRAL_SENSITIVITY, 34852, IFD_FORMAT_STRING),
new ExifTag(TAG_PHOTOGRAPHIC_SENSITIVITY, 34855, IFD_FORMAT_USHORT),
new ExifTag(TAG_OECF, 34856, IFD_FORMAT_UNDEFINED),
new ExifTag(TAG_SENSITIVITY_TYPE, 34864, IFD_FORMAT_USHORT),
new ExifTag(TAG_STANDARD_OUTPUT_SENSITIVITY, 34865, IFD_FORMAT_ULONG),
new ExifTag(TAG_RECOMMENDED_EXPOSURE_INDEX, 34866, IFD_FORMAT_ULONG),
new ExifTag(TAG_ISO_SPEED, 34867, IFD_FORMAT_ULONG),
new ExifTag(TAG_ISO_SPEED_LATITUDE_YYY, 34868, IFD_FORMAT_ULONG),
new ExifTag(TAG_ISO_SPEED_LATITUDE_ZZZ, 34869, IFD_FORMAT_ULONG),
new ExifTag(TAG_EXIF_VERSION, 36864, IFD_FORMAT_STRING),
new ExifTag(TAG_DATETIME_ORIGINAL, 36867, IFD_FORMAT_STRING),
new ExifTag(TAG_DATETIME_DIGITIZED, 36868, IFD_FORMAT_STRING),
new ExifTag(TAG_OFFSET_TIME, 36880, IFD_FORMAT_STRING),
new ExifTag(TAG_OFFSET_TIME_ORIGINAL, 36881, IFD_FORMAT_STRING),
new ExifTag(TAG_OFFSET_TIME_DIGITIZED, 36882, IFD_FORMAT_STRING),
new ExifTag(TAG_COMPONENTS_CONFIGURATION, 37121, IFD_FORMAT_UNDEFINED),
new ExifTag(TAG_COMPRESSED_BITS_PER_PIXEL, 37122, IFD_FORMAT_URATIONAL),
new ExifTag(TAG_SHUTTER_SPEED_VALUE, 37377, IFD_FORMAT_SRATIONAL),
new ExifTag(TAG_APERTURE_VALUE, 37378, IFD_FORMAT_URATIONAL),
new ExifTag(TAG_BRIGHTNESS_VALUE, 37379, IFD_FORMAT_SRATIONAL),
new ExifTag(TAG_EXPOSURE_BIAS_VALUE, 37380, IFD_FORMAT_SRATIONAL),
new ExifTag(TAG_MAX_APERTURE_VALUE, 37381, IFD_FORMAT_URATIONAL),
new ExifTag(TAG_SUBJECT_DISTANCE, 37382, IFD_FORMAT_URATIONAL),
new ExifTag(TAG_METERING_MODE, 37383, IFD_FORMAT_USHORT),
new ExifTag(TAG_LIGHT_SOURCE, 37384, IFD_FORMAT_USHORT),
new ExifTag(TAG_FLASH, 37385, IFD_FORMAT_USHORT),
new ExifTag(TAG_FOCAL_LENGTH, 37386, IFD_FORMAT_URATIONAL),
new ExifTag(TAG_SUBJECT_AREA, 37396, IFD_FORMAT_USHORT),
new ExifTag(TAG_MAKER_NOTE, 37500, IFD_FORMAT_UNDEFINED),
new ExifTag(TAG_USER_COMMENT, 37510, IFD_FORMAT_UNDEFINED),
new ExifTag(TAG_SUBSEC_TIME, 37520, IFD_FORMAT_STRING),
new ExifTag(TAG_SUBSEC_TIME_ORIGINAL, 37521, IFD_FORMAT_STRING),
new ExifTag(TAG_SUBSEC_TIME_DIGITIZED, 37522, IFD_FORMAT_STRING),
new ExifTag(TAG_FLASHPIX_VERSION, 40960, IFD_FORMAT_UNDEFINED),
new ExifTag(TAG_COLOR_SPACE, 40961, IFD_FORMAT_USHORT),
new ExifTag(TAG_PIXEL_X_DIMENSION, 40962, IFD_FORMAT_USHORT, IFD_FORMAT_ULONG),
new ExifTag(TAG_PIXEL_Y_DIMENSION, 40963, IFD_FORMAT_USHORT, IFD_FORMAT_ULONG),
new ExifTag(TAG_RELATED_SOUND_FILE, 40964, IFD_FORMAT_STRING),
new ExifTag(TAG_INTEROPERABILITY_IFD_POINTER, 40965, IFD_FORMAT_ULONG),
new ExifTag(TAG_FLASH_ENERGY, 41483, IFD_FORMAT_URATIONAL),
new ExifTag(TAG_SPATIAL_FREQUENCY_RESPONSE, 41484, IFD_FORMAT_UNDEFINED),
new ExifTag(TAG_FOCAL_PLANE_X_RESOLUTION, 41486, IFD_FORMAT_URATIONAL),
new ExifTag(TAG_FOCAL_PLANE_Y_RESOLUTION, 41487, IFD_FORMAT_URATIONAL),
new ExifTag(TAG_FOCAL_PLANE_RESOLUTION_UNIT, 41488, IFD_FORMAT_USHORT),
new ExifTag(TAG_SUBJECT_LOCATION, 41492, IFD_FORMAT_USHORT),
new ExifTag(TAG_EXPOSURE_INDEX, 41493, IFD_FORMAT_URATIONAL),
new ExifTag(TAG_SENSING_METHOD, 41495, IFD_FORMAT_USHORT),
new ExifTag(TAG_FILE_SOURCE, 41728, IFD_FORMAT_UNDEFINED),
new ExifTag(TAG_SCENE_TYPE, 41729, IFD_FORMAT_UNDEFINED),
new ExifTag(TAG_CFA_PATTERN, 41730, IFD_FORMAT_UNDEFINED),
new ExifTag(TAG_CUSTOM_RENDERED, 41985, IFD_FORMAT_USHORT),
new ExifTag(TAG_EXPOSURE_MODE, 41986, IFD_FORMAT_USHORT),
new ExifTag(TAG_WHITE_BALANCE, 41987, IFD_FORMAT_USHORT),
new ExifTag(TAG_DIGITAL_ZOOM_RATIO, 41988, IFD_FORMAT_URATIONAL),
new ExifTag(TAG_FOCAL_LENGTH_IN_35MM_FILM, 41989, IFD_FORMAT_USHORT),
new ExifTag(TAG_SCENE_CAPTURE_TYPE, 41990, IFD_FORMAT_USHORT),
new ExifTag(TAG_GAIN_CONTROL, 41991, IFD_FORMAT_USHORT),
new ExifTag(TAG_CONTRAST, 41992, IFD_FORMAT_USHORT),
new ExifTag(TAG_SATURATION, 41993, IFD_FORMAT_USHORT),
new ExifTag(TAG_SHARPNESS, 41994, IFD_FORMAT_USHORT),
new ExifTag(TAG_DEVICE_SETTING_DESCRIPTION, 41995, IFD_FORMAT_UNDEFINED),
new ExifTag(TAG_SUBJECT_DISTANCE_RANGE, 41996, IFD_FORMAT_USHORT),
new ExifTag(TAG_IMAGE_UNIQUE_ID, 42016, IFD_FORMAT_STRING),
new ExifTag(TAG_CAMERA_OWNER_NAME, 42032, IFD_FORMAT_STRING),
new ExifTag(TAG_BODY_SERIAL_NUMBER, 42033, IFD_FORMAT_STRING),
new ExifTag(TAG_LENS_SPECIFICATION, 42034, IFD_FORMAT_URATIONAL),
new ExifTag(TAG_LENS_MAKE, 42035, IFD_FORMAT_STRING),
new ExifTag(TAG_LENS_MODEL, 42036, IFD_FORMAT_STRING),
new ExifTag(TAG_GAMMA, 42240, IFD_FORMAT_URATIONAL),
new ExifTag(TAG_DNG_VERSION, 50706, IFD_FORMAT_BYTE),
new ExifTag(TAG_DEFAULT_CROP_SIZE, 50720, IFD_FORMAT_USHORT, IFD_FORMAT_ULONG)
};
3、ExifInterface始终返回0方向
有时候通过ExifInterface获取图片方向始终返回0,可能有以下几个原因:
3.1、获取不到属性原因
①图片没有方向信息:有些图片可能没有正确的方向信息,导致ExifInterface返回0方向。这可能是因为图片没有被正确地设置方向属性,或者是在拍摄时设备没有正确记录方向信息。
②ExifInterface读取失败:ExifInterface在读取图片Exif信息时可能会出现错误,导致无法获取正确的方向信息。这可能是由于图片格式不受支持或者图片损坏导致的。
3.2、解决办法
针对这个问题,可以尝试以下解决方法:
①检查图片是否包含方向信息:可以使用其他工具或库来检查图片的Exif信息,确认是否存在方向信息。如果没有方向信息,那么ExifInterface返回0方向是正常的。
②使用其他库或方法读取方向信息:如果ExifInterface无法正确读取方向信息,可以尝试使用其他的图片处理库或方法来读取方向信息。例如,可以使用第三方库如Glide或Picasso来加载图片,并通过它们提供的方法获取方向信息。
③检查图片格式和完整性:确保图片格式正确,并且没有损坏。可以尝试使用其他图片查看器或编辑器打开图片,确认是否能够正确显示和编辑。