SYPictureMetadata
使用ImageIO
和易于使用的模型读写图像元数据。
屏幕截图
元数据支持
ImageIO键值
自iOS 11以来,ImageIO添加了很多键值,它们大多关于DNG、HEIC和IPTC扩展数据,目前不支持。完整列表可在Keys/Unsupported.txt中找到。请随意向我提交PR,最好附上测试用例:)
目前我们支持iOS 14.4中的628个定义键值中的325个,约51%。
测试覆盖率
目前已有基本测试,覆盖大约 32% 的已定义获取器和 3% 的已定义设置器。如果您需要在您的应用程序中支持特定的属性,也应测试这些属性,并且欢迎您提交 PR 来增加此库的覆盖率。
Xcode 报告此库的测试覆盖率为 36.4%。
照片标题(iOS 14)
我已为 iOS 14 照片标题增加了初步支持。由于这些数据不像其他元数据一样存储在图像数据中,而是存储在 ImageIO
中直到图像导出,因此我创建了一个名为 assetCaption
的只读属性,属于 PHAsset
扩展,用于获取这些信息。
更多详情见 SYMetadataExtensions.swift
请注意
当将元数据保存到文件时,可能会遇到以下问题
- 未写入元数据
- 元数据已被修改
- 添加了元数据
- 应删除的元数据没有被删除,因为 ImageIO 在另一个命名空间中找到了类似的元数据(例如,如果存在 IPTC.byLine,则删除 TIFF.artist 就没效果)
此库使用 ImageIO
,它有其自身的限制和值检查。这只是在框架暴露的 NSDictionary
结构周围的一个包装器,并不完美。请在使用此库编辑元数据时,对您的应用程序进行充分的测试,测试应用程序示例中可以找到测试图像集。此库不能保证数据完整性,如 libexif
或 exiv2
一样。
示例
添加IPTC数据示例
// this is available in the Example project
let imageURL = TestFile.iptc2.url!
// load metadata from original file (please handle errors, the type is SYMetadata.Error)
let metadata = try! SYMetadata(fileURL: imageURL)
// create IPTC container if not present
if (metadata.metadataIPTC == nil) {
metadata.metadataIPTC = SYMetadataIPTC()
}
// edit metadata
metadata.metadataIPTC?.keywords = ["Some test keywords", "added by SYMetadata example app"];
metadata.metadataIPTC?.city = "Lyon";
metadata.metadataIPTC?.credit = "© Me 2017";
// create new image data with original image data and edited metadata
let originalImageData = try! Data(contentsOf: imageURL)
let imageDataWithMetadata = try! metadata.apply(to: originalImageData)
// log the delta in file size
print("File size delta:", imageDataWithMetadata.count - originalImageData.count);
// File size delta: 60325
// load metadata for newly cerated image
let reloadedMetadata = try! SYMetadata(imageData: imageDataWithMetadata)
// log the differences between files
print("Differences:\n", reloadedMetadata.originalDictionary.metadataDifferences(from: metadata.originalDictionary, includeValuesInDiff: true).jsonString)
// Differences:
// {
// "{Exif}" : {
// "ShutterSpeedValue" : "Updated: 8.643856 -> 8.643855995239512"
// },
// "{IPTC}" : {
// "City" : "Added: Lyon",
// "Credit" : "Added: © Me 2017",
// "Keywords" : "Updated: beach, baywatch, LA, sunset -> Some test keywords, added by SYMetadata example app"
// },
// "{JFIF}" : {
// "DensityUnit" : "Added: 0",
// "JFIFVersion" : "Added: 1, 0, 1",
// "XDensity" : "Added: 72",
// "YDensity" : "Added: 72"
// }
// }
删除所有元数据
let imageURL = TestFile.iptc2.url!
// load metadata from original file (please handle errors, the type is SYMetadata.Error)
let metadata = try! SYMetadata(fileURL: imageURL)
// create new image data with original image data and strip all metadata
let originalImageData = try! Data(contentsOf: imageURL)
let imageDataWithoutMetadata = try! SYMetadata.stripAllMetadata(from: originalImageData)
// log the delta in file size
print("File size delta:", imageDataWithoutMetadata.count - originalImageData.count);
// File size delta: 73491
// load metadata for newly cerated image
let reloadedMetadata = try! SYMetadata(imageData: imageDataWithoutMetadata)
// log the differences between files
print("Differences:\n", reloadedMetadata.originalDictionary.metadataDifferences(from: metadata.originalDictionary, includeValuesInDiff: false).jsonString)
// Differences:
// {
// "{Exif}" : {
// "ApertureValue" : "Removed",
// "ComponentsConfiguration" : "Removed",
// "CompressedBitsPerPixel" : "Removed",
// "Contrast" : "Removed",
// "CustomRendered" : "Removed",
// "DateTimeDigitized" : "Removed",
// "DateTimeOriginal" : "Removed",
// "DigitalZoomRatio" : "Removed",
// "ExifVersion" : "Removed",
// "ExposureBiasValue" : "Removed",
// "ExposureMode" : "Removed",
// "ExposureProgram" : "Removed",
// "ExposureTime" : "Removed",
// "FileSource" : "Removed",
// "Flash" : "Removed",
// "FlashPixVersion" : "Removed",
// "FNumber" : "Removed",
// "FocalLength" : "Removed",
// "FocalLenIn35mmFilm" : "Removed",
// "GainControl" : "Removed",
// "ISOSpeedRatings" : "Removed",
// "LightSource" : "Removed",
// "MaxApertureValue" : "Removed",
// "MeteringMode" : "Removed",
// "Saturation" : "Removed",
// "SceneCaptureType" : "Removed",
// "SceneType" : "Removed",
// "SensingMethod" : "Removed",
// "Sharpness" : "Removed",
// "ShutterSpeedValue" : "Removed",
// "WhiteBalance" : "Removed"
// },
// "{IPTC}" : {
// "Byline" : "Removed",
// "BylineTitle" : "Removed",
// "Caption\/Abstract" : "Removed",
// "CopyrightNotice" : "Removed",
// "DateCreated" : "Removed",
// "DigitalCreationDate" : "Removed",
// "DigitalCreationTime" : "Removed",
// "Keywords" : "Removed",
// "ObjectName" : "Removed",
// "TimeCreated" : "Removed"
// },
// "{JFIF}" : {
// "DensityUnit" : "Added",
// "IsProgressive" : "Removed",
// "JFIFVersion" : "Added",
// "XDensity" : "Added",
// "YDensity" : "Added"
// },
// "{TIFF}" : {
// "Artist" : "Removed",
// "Copyright" : "Removed",
// "DateTime" : "Removed",
// "ImageDescription" : "Removed",
// "Make" : "Removed",
// "Model" : "Removed",
// "PhotometricInterpretation" : "Removed",
// "ResolutionUnit" : "Removed",
// "Software" : "Removed",
// "XResolution" : "Removed",
// "YResolution" : "Removed"
// },
// "DPIHeight" : "Removed",
// "DPIWidth" : "Removed"
// }
// log kept metadata
print("Kept metadata:\n", reloadedMetadata.originalDictionary.jsonString)
// Kept metadata:
// {
// "{Exif}" : {
// "ColorSpace" : 1,
// "PixelXDimension" : 1920,
// "PixelYDimension" : 1080
// },
// "{JFIF}" : {
// "DensityUnit" : 0,
// "JFIFVersion" : [
// 1,
// 0,
// 1
// ],
// "XDensity" : 72,
// "YDensity" : 72
// },
// "{TIFF}" : {
// "Orientation" : 1
// },
// "ColorModel" : "RGB",
// "Depth" : 8,
// "Orientation" : 1,
// "PixelHeight" : 1080,
// "PixelWidth" : 1920,
// "ProfileName" : "sRGB IEC61966-2.1"
// }
致谢
在这个库内的评论中,您可以找到对我帮助很大的链接。主要是:
- http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/index.html
- http://www.exiv2.org/tags.html
- https://github.com/Nikita2k/SimpleExif
- https://raw.githubusercontent.com/wiki/drewnoakes/metadata-extractor/docs/CIFFspecV1R04.pdf
- https://www.adobe.com/content/dam/Adobe/en/products/photoshop/pdfs/dng_spec_1.4.0.0.pdf
- https://www.awaresystems.be/imaging/tiff/tifftags/privateifd/gps.html
- https://docs.oracle.com/javase/9/docs/api/javax/imageio/metadata/doc-files/tiff_metadata.html#ImageMetadata
- https://openimageio.readthedocs.io/en/release-2.1.11.0/stdmetadata.html
许可证
在您想用的任何项目中使用它,提及我的名字并不要怪我出故障 :)
-- syan