英文:
Why can't I read EXIF from my loaded UIImage, but still can from NSData?
问题
Can EXIF metadata only be ready from the NSData?(EXIF元数据只能从NSData中读取吗?)
英文:
I have some EXIF stored in a UIImage that I store using NSFileManager writeToFile:atomically:
.
Now, when I try to read back the EXIF metadata imageProperties
here shows the metadata I expect:
NSData *data = [NSData dataWithContentsOfFile:filePath];
source = CGImageSourceCreateWithData((__bridge CFDataRef)data, NULL);
imageProperties = CGImageSourceCopyPropertiesAtIndex(source, 0, NULL);
However, if I read the data as a UIImage first, the data is gone:
UIImage *writtenImage = [UIImage imageWithContentsOfFile:filePath];
CGImageSourceRef source = CGImageSourceCreateWithData((__bridge CFDataRef)UIImagePNGRepresentation(writtenImage), NULL);
CFDictionaryRef imageProperties = CGImageSourceCopyPropertiesAtIndex(source, 0, NULL);
Additionally, even on converting the data variable above to an image, it fails:
NSData *data = [NSData dataWithContentsOfFile:filePath];
UIImage *writtenImage = [UIImage imageWithData:data];
CGImageSourceRef source = CGImageSourceCreateWithData((__bridge CFDataRef)UIImagePNGRepresentation(writtenImage), NULL);
CFDictionaryRef imageProperties = CGImageSourceCopyPropertiesAtIndex(source, 0, NULL);
Can EXIF metadata only be ready from the NSData?
答案1
得分: 1
EXIF 是一种用于某些图像文件(如 png
、jpg
、tif
等)的标头。当这些文件被解压后,图像就变成了完全不同的东西 - 一个表示像素数据的数组,此时 EXIF 标头不再相关。因此,当你有一个图像容器的 NSData
对象时,与 UIImage
对象中封装的图像数据完全不同(EXIF 标头在那里不再存在)。
你仍然可以推断出某些元数据并自行填充它,但这与 UIImage
无关,在这种情况下,你直接与容器(文件)一起工作:
typedef NS_CLOSED_ENUM(uint8_t, TDWEXIFOrientationValue) {
TDWEXIFOrientationValueTopLeft = 1,
TDWEXIFOrientationValueTopRight,
TDWEXIFOrientationValueBottomRight,
TDWEXIFOrientationValueBottomLeft,
TDWEXIFOrientationValueLeftTop,
TDWEXIFOrientationValueRightTop,
TDWEXIFOrientationValueRightBottom,
TDWEXIFOrientationValueLeftBotton,
};
- (void)writeEXIFHeader:(CGImageSourceRef)image {
/* 获取文件类型 */
CFStringRef UTI = CGImageSourceGetType(image);
if (!UTI) {
/* 根据需要处理无法检索文件类型的错误 */
return;
}
NSMutableData *imageData = [NSMutableData new];
/* 创建一个用于保存文件的图像目标 */
CGImageDestinationRef destination = CGImageDestinationCreateWithData((__bridge CFMutableDataRef)imageData, UTI, 1, NULL);
if (!destination) {
/* 根据需要处理创建 CGImageDestinationRef 的错误 */
return;
}
const void *keys[] = {
kCGImageDestinationDateTime,
kCGImageDestinationOrientation,
kCGImageDestinationMergeMetadata
};
TDWEXIFOrientationValue orientationValue = TDWEXIFOrientationValueTopLeft;
const void *values[] = {
CFDateCreate(kCFAllocatorDefault, CFAbsoluteTimeGetCurrent()), // 为图片设置当前日期和时间
CFNumberCreate(kCFAllocatorDefault, kCFNumberCharType, &orientationValue), // 默认方向
kCFBooleanTrue // 合并 EXIF 元数据而不是覆盖它
};
/* 使用修改后的元数据创建 CFDictionaryRef */
CFDictionaryRef exifData = CFDictionaryCreate(kCFAllocatorDefault, keys, values, sizeof(keys) / sizeof(keys[0]), NULL, NULL);
/* 向目标中添加图像 */
CFErrorRef errorRef = nil;
if (!CGImageDestinationCopyImageSource(destination, image, exifData, &errorRef)) {
CFStringRef errorDescription = CFErrorCopyDescription(errorRef);
/* 根据需要处理创建写入 EXIF 数据的错误 */
CFRelease(errorDescription);
}
}
英文:
EXIF is a header for certain image files (containers like png
, jpg
, tif
, etc..). When the files are unpacked, the image is a completely different thing - an array of data representing pixels, where EXIF header is no longer relevant. Thus, when you have an NSData
object of an image container it's a completely different beast compared to the image data incapsulated in a UIImage
object (EXIF header is just not present there).
You can still deduce certain metadata and fill it yourself, but this has nothing to do with UIImage
, in this case you just work with the containers (files) directly:
typedef NS_CLOSED_ENUM(uint8_t, TDWEXIFOrientationValue) {
TDWEXIFOrientationValueTopLeft = 1,
TDWEXIFOrientationValueTopRight,
TDWEXIFOrientationValueBottomRight,
TDWEXIFOrientationValueBottomLeft,
TDWEXIFOrientationValueLeftTop,
TDWEXIFOrientationValueRightTop,
TDWEXIFOrientationValueRightBottom,
TDWEXIFOrientationValueLeftBotton,
};
- (void)writeEXIFHeader:(CGImageSourceRef)image {
/* get the file type */
CFStringRef UTI = CGImageSourceGetType(image);
if (!UTI) {
/* Handle Error Retrieving File Type Accordingly */
return;
}
NSMutableData *imageData = [NSMutableData new];
/* create an image destination for saving the file */
CGImageDestinationRef destination = CGImageDestinationCreateWithData((__bridge CFMutableDataRef)imageData, UTI, 1, NULL);
if (!destination) {
/* Handle Error Creating CGImageDestinationRef Accordingly */
return;
}
const void *keys[] = {
kCGImageDestinationDateTime,
kCGImageDestinationOrientation,
kCGImageDestinationMergeMetadata
};
TDWEXIFOrientationValue orientationValue = TDWEXIFOrientationValueTopLeft;
const void *values[] = {
CFDateCreate(kCFAllocatorDefault, CFAbsoluteTimeGetCurrent()), // Sets current date and time for the picture
CFNumberCreate(kCFAllocatorDefault, kCFNumberCharType, &orientationValue), // Default orientation
kCFBooleanTrue // merge exif metadata instead of overwriting it
};
/* create an CFDictionaryRef with the modified metadata */
CFDictionaryRef exifData = CFDictionaryCreate(kCFAllocatorDefault, keys, values, sizeof(keys) / sizeof(keys[0]), NULL, NULL);
/* add an image to the destination */
CFErrorRef errorRef = nil;
if (!CGImageDestinationCopyImageSource(destination, image, exifData, &errorRef)) {
CFStringRef errorDescription = CFErrorCopyDescription(errorRef);
/* Handle Error Creating Writing EXIF data Accordingly */
CFRelease(errorDescription);
}
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论