在進行照片相關處理時,就會遇到如何保留原始照片的圖像信息,一些圖片分享社區通常會顯示作者所使用的拍攝設備和曝光信息等等,來對所拍攝照片在技術參數的細節分享。
什么是EXIF
可交換圖像文件格式常被簡稱為Exif(Exchangeable image file format),是專門為數碼相機的照片設定的,可以記錄數碼照片的屬性信息和拍攝數據。
更多詳細介紹EXIF
原因
利用ImageIO可以打印一下metadata,下面是一張JPEG圖片正常情況下的metadata:
{
ColorModel = RGB;
DPIHeight = 72;
DPIWidth = 72;
Depth = 8;
Orientation = 1;
PixelHeight = 1080;
PixelWidth = 1440;
ProfileName = "sRGB IEC61966-2.1";
"{Exif}" = {
ApertureValue = "2.52606882168926";
BrightnessValue = "2.87380073800738";
ColorSpace = 1;
ComponentsConfiguration = (
1,
2,
3,
0
);
DateTimeDigitized = "2016:01:31 23:44:07";
DateTimeOriginal = "2016:01:31 23:44:07";
ExifVersion = (
2,
2,
1
);
ExposureBiasValue = 0;
ExposureMode = 0;
ExposureProgram = 2;
ExposureTime = "0.05";
FNumber = "2.4";
Flash = 24;
FlashPixVersion = (
1,
0
);
FocalLenIn35mmFilm = 33;
FocalLength = "4.12";
ISOSpeedRatings = (
50
);
LensMake = Apple;
LensModel = "iPhone 5 back camera 4.12mm f/2.4";
LensSpecification = (
"4.12",
"4.12",
"2.4",
"2.4"
);
MeteringMode = 5;
PixelXDimension = 1440;
PixelYDimension = 1080;
SceneCaptureType = 0;
SceneType = 1;
SensingMethod = 2;
ShutterSpeedValue = "4.321956769055745";
SubjectArea = (
1631,
1223,
1795,
1077
);
SubsecTimeDigitized = 336;
SubsecTimeOriginal = 336;
WhiteBalance = 0;
};
"{JFIF}" = {
DensityUnit = 0;
JFIFVersion = (
1,
0,
1
);
XDensity = 72;
YDensity = 72;
};
"{TIFF}" = {
DateTime = "2016:01:31 23:44:07";
Make = Apple;
Model = "iPhone 5";
Orientation = 1;
ResolutionUnit = 2;
Software = "9.2";
XResolution = 72;
YResolution = 72;
};
}
可能還會有些GPS信息等.
很多時候我們拿到圖片后客戶端會需要進行一些處理,例如壓縮,縮放等等,避免傳輸很大的圖片浪費資源。這時候對圖片的信息就會有損失, 進行數據轉換時會用到如下函數:
UIImagePNGRepresentation(UIImage * image);
UIImageJPEGRepresentation(UIImage * image, CGFloat compressionQuality);
以上兩個函數將UIImage轉NSData 都是將圖片質量進行壓縮,在實際測試中發現是會將EXIF信息丟失。
UIImagePNGRepresentation
{
ColorModel = RGB;
Depth = 8;
PixelHeight = 3024;
PixelWidth = 4032;
ProfileName = "sRGB IEC61966-2.1";
"{PNG}" = {
Chromaticities = (
"0.3127",
"0.329",
"0.64",
"0.33",
"0.3",
"0.6000000000000001",
"0.15",
"0.06"
);
Gamma = "0.45455";
InterlaceType = 0;
sRGBIntent = 0;
};
}
UIImageJPEGRepresentation
{
ColorModel = RGB;
Depth = 8;
Orientation = 6;
PixelHeight = 3024;
PixelWidth = 4032;
ProfileName = "sRGB IEC61966-2.1";
"{Exif}" = {
ColorSpace = 1;
PixelXDimension = 4032;
PixelYDimension = 3024;
};
"{JFIF}" = {
DensityUnit = 0;
JFIFVersion = (
1,
0,
1
);
XDensity = 72;
YDensity = 72;
};
"{TIFF}" = {
Orientation = 6;
};
}
這就導致在上傳圖片時本身圖片的EXIF信息就已經不對了。
那怎么解決上傳圖片時保留EXIF信息呢?
1、最簡單粗暴的方式就是直接傳原圖,不進行任何處理,這樣就不丟失了。(但這樣圖片動不動就好幾M,很浪費資源)
2、將原圖的EXIF信息取出,再設置給處理好的圖片,最后傳輸圖片到服務器的過程中不要將NSData轉為UIImage即可,也就是說處理好后的圖片不要在進行轉換以免丟失EXIF信息。
Demo:
ALAsset *asset = ..;
UIImage *tempImg = [UIImage imageWithCGImage:asset.defaultRepresentation.fullScreenImage];
//保存到程序的緩存目錄
[SysTool createFileExistsAtPath:kUploadCachePath];
NSString *fileName = [ImageTool createUploadFileName];
NSString *filePath = [NSString stringWithFormat:@"%@/%@",kUploadCachePath,fileName];
if ([ImageTool saveImageCompress:tempImg WithName:fileName path:kUploadCachePath]) {
[_photoList addObject:filePath];
}
ALAssetRepresentation *image_representation = [asset defaultRepresentation];
// 取出原圖exif、tiff等信息
CFDictionaryRef imageMetaData = (__bridge CFDictionaryRef)image_representation.metadata;
// 壓縮的圖片重寫exif/tiff信息
NSData *compressData = [NSData dataWithContentsOfFile:filePath];
NSData *exifData = [ImageTool createMetaData:compressData metaDic:(__bridge NSDictionary *)imageMetaData];
// 上傳圖片到服務器...
[Upload data:exifData];
ImageTool
+ (NSData *)createMetaData:(NSData *)imgData metaDic:(NSDictionary *)metaDic {
// 1原圖exif/tiff信息
CFDictionaryRef exif = (CFDictionaryRef)CFDictionaryGetValue((__bridge CFDictionaryRef)metaDic, kCGImagePropertyExifDictionary);
CFDictionaryRef tiff = (CFDictionaryRef)CFDictionaryGetValue((__bridge CFDictionaryRef)metaDic, kCGImagePropertyTIFFDictionary);
// 2獲取壓縮的圖片
CGImageSourceRef compressSource = CGImageSourceCreateWithData((__bridge CFDataRef)imgData, NULL);
CFDictionaryRef compressImageMetaData = CGImageSourceCopyPropertiesAtIndex(compressSource,0,NULL);
CFMutableDictionaryRef compressImageMetaDataMu = CFDictionaryCreateMutableCopy(kCFAllocatorDefault, 50, compressImageMetaData);
// 3將原來的exif/tiff等信息設置到壓縮后的圖片上
if (exif) {
CFDictionarySetValue(compressImageMetaDataMu, kCGImagePropertyExifDictionary, exif);
}
if (tiff) {
CFDictionarySetValue(compressImageMetaDataMu, kCGImagePropertyTIFFDictionary, tiff);
}
NSData *exifData = [self saveImageWithImageData:imgData Properties:(__bridge NSDictionary*)compressImageMetaDataMu];
return exifData;
}
// 將圖片的exif信息寫入到圖片流
+ (NSData *)saveImageWithImageData:(NSData *)data Properties:(NSDictionary *)properties {
NSMutableDictionary *dataDic = [NSMutableDictionary dictionaryWithDictionary:properties];
// 設置properties屬性
CGImageSourceRef imageRef = CGImageSourceCreateWithData((__bridge CFDataRef)data, NULL);
CFStringRef uti = CGImageSourceGetType(imageRef);
NSMutableData *data1 = [NSMutableData data];
CGImageDestinationRef destination = CGImageDestinationCreateWithData((__bridge CFMutableDataRef)data1, uti, 1, NULL);
if (!destination) {
NSLog(@"error");
return nil;
}
CGImageDestinationAddImageFromSource(destination, imageRef, 0, (__bridge CFDictionaryRef)dataDic);
BOOL check = CGImageDestinationFinalize(destination);
if (!check) {
NSLog(@"error");
return nil;
}
CFRelease(destination);
CFRelease(uti);
return data1;
}
其他
當拍照保存到相冊以往我們都是簡單的調用UIImagePickerController,用系統相機拍照然后直接調用以下API也是會丟失EXIF:
UIImageWriteToSavedPhotosAlbum(UIImage *image, __nullable id completionTarget, __nullable SEL completionSelector, void * __nullable contextInfo);
所以可以調用ALAssetsLibrary的API:
// 保存到系統相冊,并附帶meta信息
ALAssetsLibrary *library = [[ALAssetsLibrary alloc] init];
[library writeImageToSavedPhotosAlbum:imageToSave.CGImage metadata:(__bridge NSDictionary*)metaDic completionBlock:^(NSURL *assetURL, NSError *error) {
}];
保存到系統相冊對比