在 iOS 上使用修改后的元数据(无重新编码)保存原始图像数据
Posted
技术标签:
【中文标题】在 iOS 上使用修改后的元数据(无重新编码)保存原始图像数据【英文标题】:Save original image data with modified metadata (no re-encoding) on iOS 【发布时间】:2013-01-31 11:14:22 【问题描述】:我想在临时文件夹中保存带有一些元数据更改的图像,而不重新编码实际的图像数据。
我发现能够做到这一点的唯一方法是ALAssetsLibrary/writeImageDataToSavedPhotosAlbum:metadata:completionBlock:,但是,这个方法会将图像保存到照片库中。相反,我想将图像保存到临时文件夹(例如通过电子邮件共享,而不填充照片库)。
我尝试过使用CGImageDestinationRef
(CGImageDestinationAddImageFromSource),但它只能使用解码图像创建,这意味着它在保存时会重新编码(测试,像素字节看起来不同)。
除了使用CGImageDestinationRef
之外,还有其他可用于ios的方法/类可以将图像数据与元数据一起保存吗?也欢迎提出解决方法的建议。
【问题讨论】:
您能否详细说明一下 - 您从哪里获取图像(相机?其他?) 没关系,我把它们当作未解码的NSData
(通常从 ALAsset 读取)。
考虑到您已经从某个来源读取,它已经被编码,更新了元数据,如果您只是将它写入文件,它会如何失败?你知道,[data writeToFile: atomically:]
。只是想知道。
@NandeepMali 如果我将其直接写入文件,我将无法更改元数据。我基本上有 1 个NSData
(未解码的图像)对象和 1 个NSDictionary
(元数据)对象,我需要以某种方式合并在一起而不改变图像像素。
啊,我明白了。您是否有正在使用的特定图像格式?
【参考方案1】:
这是 iOS SDK 的一个令人沮丧的问题。首先,我建议提交enhancement request。
现在,这是一个潜在的解决方法:如果 ALAsset 是由您创建的(即,它的 editable
属性是 YES
),那么您基本上可以读取数据、写入元数据、再次读取、保存到磁盘,然后用原始元数据写入。
这种方法将避免创建重复的图像。
请阅读 // cmets,因为我为了简洁而跳过了一些内容(例如构建元数据字典):
ALAsset* asset; //get your asset, don't use this empty one
if (asset.editable)
// get the source data
ALAssetRepresentation *rep = [asset defaultRepresentation];
Byte *buffer = (Byte*)malloc(rep.size);
// add error checking here
NSUInteger buffered = [rep getBytes:buffer fromOffset:0.0 length:rep.size error:nil];
NSData *sourceData = [NSData dataWithBytesNoCopy:buffer length:buffered freeWhenDone:YES];
// make your metadata whatever you want
// you should use actual metadata, not a blank dictionary
NSDictionary *metadataDictionary = [NSDictionary dictionary];
// these are __weak to avoid creating an ARC retain cycle
NSData __weak *originalData = sourceData;
NSDictionary __weak *originalMetadata = [rep metadata];
[asset setImageData:sourceData
metadata:metadataDictionary
completionBlock:^(NSURL *assetURL, NSError *error)
//now get your data and write it to file
if (!error)
//get your data...
NSString *assetPath = [assetURL path];
NSData *targetData = [[NSFileManager defaultManager] contentsAtPath:assetPath];
//...write to file...
NSArray *searchPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentPath = [searchPaths lastObject];
NSURL *fileURL = [NSURL fileURLWithPath:documentPath];
[targetData writeToURL:fileURL atomically:YES];
//...and put it back the way it was
[asset setImageData:originalData metadata:originalMetadata completionBlock:nil];
else
// handle error on setting data
NSLog(@"ERROR: %@", [error localizedDescription]);
];
else
// you'll need to make a new ALAsset which you have permission to edit and then try again
如您所见,如果 ALAsset 不属于您,您将需要创建一个,这会将照片添加到用户的库中,这正是您想要避免的。但是,您可能已经猜到了,即使您的应用程序创建了 ALAsset,您也无法从用户的照片库中删除它。 (请随时为此提出另一个增强请求。)
因此,如果照片/图像是在您的应用中创建的,那么这对您有用。
但如果没有,它将创建一个用户必须删除的附加副本。
唯一的选择是自己解析 NSData,这会很痛苦。据我所知,没有开源库可以填补 iOS SDK 中的这一空白。
【讨论】:
谢谢。我找到了一个开源库,但有一段时间没有更新,只支持部分元数据。 这是它的链接:code.google.com/p/iphone-exif。到目前为止,我只看过它的描述,因为它似乎只适用于 exif。 @AaronBrager 我得到assetURL = nil,尽管asset是可编辑的。知道为什么会发生这种情况吗? @Matias 来自文档:“如果图像未保存,assetURL
是 nil
。” 检查 error
以了解原因。
@AaronBrager 奇怪的是错误也是 nil。【参考方案2】:
除了 iphone-exif 和 Aaron 的回答,您可能还想查看 the libexif c library。
另见 HCO23 对How to write or modify EXIF data for an existing image on the filesystem, without loading the image? 的回复
(@HCO23 在 iOS 项目中使用了 libexif)。
还值得注意的是,应用商店中的许多“专业”元数据编辑应用似乎通过创建 sidecar/xmp 文件来绕过这个问题。
【讨论】:
【参考方案3】:试试这个:
- (void)saveImage: (UIImage*)image
if (image != nil)
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,
NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString* path = [documentsDirectory stringByAppendingPathComponent:
@"test.png" ];
NSData* data = UIImagePNGRepresentation(image);
[data writeToFile:path atomically:YES];
- (UIImage*)loadImage
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,
NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString* path = [documentsDirectory stringByAppendingPathComponent:
@"test.png" ];
UIImage* image = [UIImage imageWithContentsOfFile:path];
[self sendAction:path];
return image;
【讨论】:
谢谢,但这会重新编码图像,除非它们最初是 png,在这种情况下我不确定。以上是关于在 iOS 上使用修改后的元数据(无重新编码)保存原始图像数据的主要内容,如果未能解决你的问题,请参考以下文章
如何在 iOS 12 中替换 NSKeyedArchiver 的初始化程序 init(forWritingWith:) 来编码 CKRecord 的元数据
ArcGis 别人发来的.mpk文件修改后保存,又需要用到原文件,再次重新下载,怎么打开都是修改后的样子
怎么将datagridview中修改后的数据通过按钮点击直接保存到数据库啊?