iOS-Exif信息的讀取和寫入

首先說(shuō)下接觸Exif的背景,公司有拍照取證的功能,比如,發(fā)生了車禍,需要拍下現(xiàn)場(chǎng)照片取證,除了添加拍攝地點(diǎn),經(jīng)緯度,app賬號(hào),還有防偽功能。也就是防止別人篡改照片,修改相應(yīng)的信息。故提出在照片的exif信息中添加防偽的唯一信息。

什么是Exif

可交換圖像文件格式(英語(yǔ):Exchangeable image file format,官方簡(jiǎn)稱Exif),是專門為數(shù)碼相機(jī)的照片設(shè)定的,可以記錄數(shù)碼照片的屬性信息和拍攝數(shù)據(jù)。

Exif最初由日本電子工業(yè)發(fā)展協(xié)會(huì)在1996年制定,版本為1.0。1998年,升級(jí)到2.1,增加了對(duì)音頻文件的支持。2002年3月,發(fā)表了2.2版。

Exif可以附加于JPEG、TIFF、RIFF等文件之中,為其增加有關(guān)數(shù)碼相機(jī)拍攝信息的內(nèi)容和索引圖或圖像處理軟件的版本信息。

Windows 7操作系統(tǒng)具備對(duì)Exif的原生支持,通過(guò)鼠標(biāo)右鍵點(diǎn)擊圖片打開菜單,點(diǎn)擊屬性并切換到詳細(xì)信息標(biāo)簽下即可直接查看Exif信息。

Exif信息是可以被任意編輯的,因此只有參考的功能。Exif信息以0xFFE1作為開頭標(biāo)記,后兩個(gè)字節(jié)表示Exif信息的長(zhǎng)度。所以Exif信息最大為64 kb,而內(nèi)部采用TIFF格式。

一、 imageIO的初步了解

ImageIO框架提供了讀取與寫入圖片數(shù)據(jù)的基本方法,使用它可以直接獲取到圖片文件的內(nèi)容數(shù)據(jù),ImageIO框架中包含6個(gè)頭文件,其中完成主要功能的是CGImageSource和CGImageDestination兩個(gè)頭文件中定義的方法:
  1. CGImageSource: 負(fù)責(zé)圖片數(shù)據(jù)的讀取。
  2. CGImageDestination: 負(fù)責(zé)圖片數(shù)據(jù)的寫入。
  3. CGImageMetadata: 圖片文件數(shù)據(jù)類。
  4. CGImageProperties: 框架中用到的字符串常量和宏。
  5. ImageIOBase: 預(yù)處理邏輯。

二、 項(xiàng)目應(yīng)用

圖片信息操作都是針對(duì)二進(jìn)制數(shù)據(jù)Data來(lái)操作的,注(在圖片轉(zhuǎn)data和data轉(zhuǎn)Image的時(shí)候,蘋果會(huì)將部分exif信息抹除,所以如果需要保存數(shù)據(jù)到本地,最好通過(guò)data數(shù)據(jù)保存,上傳圖片數(shù)據(jù)到服務(wù)器,也建議通過(guò)data上傳)。
廢話不多說(shuō)先上代碼:

  1. 通過(guò)圖片data數(shù)據(jù)獲取圖片圖片資源引用。
    CGImageSourceRef source = CGImageSourceCreateWithData((__bridge CFDataRef)imageData, NULL);
  1. 再通過(guò)圖片資源獲取圖片信息
    NSDictionary *imageInfo = (__bridge NSDictionary*)CGImageSourceCopyPropertiesAtIndex(source, 0, NULL);
    

imageInfo其實(shí)都是key是定義好的字典對(duì)象。

IMAGEIO_EXTERN const CFStringRef kCGImagePropertyTIFFDictionary  IMAGEIO_AVAILABLE_STARTING(10.4, 4.0);
IMAGEIO_EXTERN const CFStringRef kCGImagePropertyGIFDictionary  IMAGEIO_AVAILABLE_STARTING(10.4, 4.0);
IMAGEIO_EXTERN const CFStringRef kCGImagePropertyJFIFDictionary  IMAGEIO_AVAILABLE_STARTING(10.4, 4.0);
IMAGEIO_EXTERN const CFStringRef kCGImagePropertyExifDictionary  IMAGEIO_AVAILABLE_STARTING(10.4, 4.0);
IMAGEIO_EXTERN const CFStringRef kCGImagePropertyPNGDictionary  IMAGEIO_AVAILABLE_STARTING(10.4, 4.0);
IMAGEIO_EXTERN const CFStringRef kCGImagePropertyIPTCDictionary  IMAGEIO_AVAILABLE_STARTING(10.4, 4.0);
IMAGEIO_EXTERN const CFStringRef kCGImagePropertyGPSDictionary  IMAGEIO_AVAILABLE_STARTING(10.4, 4.0);
IMAGEIO_EXTERN const CFStringRef kCGImagePropertyRawDictionary  IMAGEIO_AVAILABLE_STARTING(10.4, 4.0);
IMAGEIO_EXTERN const CFStringRef kCGImagePropertyCIFFDictionary  IMAGEIO_AVAILABLE_STARTING(10.4, 4.0);
IMAGEIO_EXTERN const CFStringRef kCGImagePropertyMakerCanonDictionary  IMAGEIO_AVAILABLE_STARTING(10.5, 4.0);
IMAGEIO_EXTERN const CFStringRef kCGImagePropertyMakerNikonDictionary  IMAGEIO_AVAILABLE_STARTING(10.5, 4.0);
IMAGEIO_EXTERN const CFStringRef kCGImagePropertyMakerMinoltaDictionary  IMAGEIO_AVAILABLE_STARTING(10.5, 4.0);
IMAGEIO_EXTERN const CFStringRef kCGImagePropertyMakerFujiDictionary  IMAGEIO_AVAILABLE_STARTING(10.5, 4.0);
IMAGEIO_EXTERN const CFStringRef kCGImagePropertyMakerOlympusDictionary  IMAGEIO_AVAILABLE_STARTING(10.5, 4.0);
IMAGEIO_EXTERN const CFStringRef kCGImagePropertyMakerPentaxDictionary  IMAGEIO_AVAILABLE_STARTING(10.5, 4.0);
IMAGEIO_EXTERN const CFStringRef kCGImageProperty8BIMDictionary  IMAGEIO_AVAILABLE_STARTING(10.4, 4.0);
IMAGEIO_EXTERN const CFStringRef kCGImagePropertyDNGDictionary  IMAGEIO_AVAILABLE_STARTING(10.5, 4.0);
IMAGEIO_EXTERN const CFStringRef kCGImagePropertyExifAuxDictionary  IMAGEIO_AVAILABLE_STARTING(10.5, 4.0);
IMAGEIO_EXTERN const CFStringRef kCGImagePropertyOpenEXRDictionary  IMAGEIO_AVAILABLE_STARTING(10.9, 11.3);
IMAGEIO_EXTERN const CFStringRef kCGImagePropertyMakerAppleDictionary  IMAGEIO_AVAILABLE_STARTING(10.10, 7.0);
IMAGEIO_EXTERN const CFStringRef kCGImagePropertyFileContentsDictionary IMAGEIO_AVAILABLE_STARTING(10.13, 11.0);
  1. 接下來(lái)重頭戲來(lái)了,也就是對(duì)圖片信息進(jìn)行編輯。
    首先對(duì)copy一份新的json對(duì)象,然后再對(duì)拷貝的新對(duì)象進(jìn)行修改。
    NSMutableDictionary *metaDataDic = [imageInfo mutableCopy];

    NSMutableDictionary *exifDic =[[metaDataDic objectForKey:(NSString*)kCGImagePropertyExifDictionary]mutableCopy];

添加自定義的對(duì)象,但key是固定的,使用的是kCGImagePropertyExifUserComment。
自定義對(duì)象里用的是進(jìn)行加密后的手機(jī)號(hào)碼。記住,exif信息對(duì)數(shù)據(jù)的結(jié)構(gòu)有嚴(yán)格要求,必須嚴(yán)格遵守,不然,可能無(wú)法添加成功。這里我只找到了UserComment字段來(lái)存放自定義的字符串信息。

    /****** begin 添加手機(jī)號(hào)碼 ******/
    NSString *dateString  = [self.dateFormatter stringFromDate:[NSDate date]];
    [exifDic setValue:dateString forKey:(NSString *)kCGImagePropertyExifDateTimeOriginal];
    NSString *phone = [NSString stringWithFormat:@"xxxsdk_%@",[PMPluginLoader sharedInstance].pluginMobilePhone];
    NSString *decryptKey = [PMPluginUtils pluginRunEnv] == PluginEnv_stg ? kDescryptKey_stg: kDescryptKey_prd;

    phone = [PMAESUtils threeEncrypt:phone withKey:decryptKey];

    [exifDic setObject:phone forKey:(NSString *)kCGImagePropertyExifUserComment];
    /****** end 添加手機(jī)號(hào)碼 ******/
 /****** begin 添加定位信息 ******/
    NSMutableDictionary *GPSDic =[[metaDataDic objectForKey:(NSString*)kCGImagePropertyGPSDictionary]mutableCopy];
    if (!GPSDic) {
        GPSDic = [NSMutableDictionary dictionaryWithCapacity:2];
    }
    CGFloat latitudeValue = self.locaiton.coordinate.latitude;
    CGFloat longitudeValue = self.locaiton.coordinate.longitude;

    [GPSDic setObject: [NSNumber numberWithFloat:latitudeValue] forKey:(NSString*)kCGImagePropertyGPSLatitude]; 
    [GPSDic setObject:(latitudeValue>0?@"N":@"S") forKey:(NSString*)kCGImagePropertyGPSLatitudeRef];

    [GPSDic setObject: [NSNumber numberWithFloat:longitudeValue] forKey:(NSString*)kCGImagePropertyGPSLongitude];
    [GPSDic setObject:(longitudeValue>0?@"E":@"W") forKey:(NSString*)kCGImagePropertyGPSLongitudeRef];

//注意: 設(shè)置的value值必須是數(shù)值對(duì)象。且必須同步設(shè)置經(jīng)緯度,以及東西經(jīng)、南北緯。少一個(gè)都會(huì)設(shè)置失敗。
    /****** end 添加定位信息 ******/

    /****** begin 添加時(shí)間DateTime ******/
    NSMutableDictionary *TIFFDic =[[metaDataDic objectForKey:(NSString*)kCGImagePropertyTIFFDictionary]mutableCopy];
    if (!TIFFDic) {
        TIFFDic = [NSMutableDictionary dictionary];
    }
    NSString *dateTimeString = [[TIFFDic objectForKey:(NSString*)kCGImagePropertyTIFFDateTime]mutableCopy];
    if (!dateTimeString) {

        [TIFFDic setObject:dateString forKey:(NSString *)kCGImagePropertyTIFFDateTime];
    }
    /****** end 添加時(shí)間DateTime ******/

    //將修改后的信息重新設(shè)置到對(duì)應(yīng)的json對(duì)象里。
    [metaDataDic setValue:GPSDic forKey:(NSString *)kCGImagePropertyGPSDictionary];
    [metaDataDic setValue:exifDic forKey:(NSString *)kCGImagePropertyExifDictionary];
    [metaDataDic setValue:TIFFDic forKey:(NSString *)kCGImagePropertyTIFFDictionary];

  1. 保存添加exif信息后的圖片data。
    CFStringRef UTI = CGImageSourceGetType(source);
//創(chuàng)建空的data對(duì)象
    NSMutableData *newImageData = [NSMutableData data];
//根據(jù)容器data,UTl對(duì)象創(chuàng)建圖片寫入句柄對(duì)象。
    CGImageDestinationRef destination = CGImageDestinationCreateWithData((__bridge CFMutableDataRef)newImageData, UTI, 1,NULL);
    
//將圖片信息寫入圖片句柄對(duì)象。
    CGImageDestinationAddImageFromSource(destination, source, 0, (__bridge CFDictionaryRef)metaDataDic);
    BOOL success = CGImageDestinationFinalize(destination);
    CFRelease(UTI);
    CFRelease(destination);
    CFRelease(source);
    if (!success) {
        NSLog(@"添加exif信息失敗");
    }
return newImageData;   //添加exif信息后的圖片data對(duì)象。

最后,在項(xiàng)目里面因?yàn)橐灿玫搅颂砑铀〉墓δ埽俅我矊⑾鄳?yīng)代碼貼出,供有需要的同學(xué)參考。

+ (UIImage *)addImageWatermark:(UIImage *)image text:(NSString *)text{
    
    CGFloat imageWidth  = image.size.width;
    CGFloat imageHeight = image.size.height;
    if (imageWidth/imageHeight > kScreenWidth/kScreenHeight) {
        CGFloat finalImageWidth = (imageHeight*kScreenWidth/kScreenHeight);
        image = [self tp_imagecutWithOriginalImage:image withCutRect:CGRectMake((imageWidth - finalImageWidth)*0.5, 0, finalImageWidth, imageHeight)];
    }else{
        CGFloat finalImageHeight = (imageWidth*kScreenHeight/kScreenWidth);
        image = [self tp_imagecutWithOriginalImage:image withCutRect:CGRectMake(0, (imageHeight - finalImageHeight)*0.5, imageWidth, finalImageHeight)];
    }
    
    image = [UIImage getWaterMarkImage:image andTitle:text andMarkFont:[UIFont boldSystemFontOfSize:20] andMarkColor:[UIColor colorWithRed:192/255.0 green:192/255.0 blue:192/255.0 alpha:1] markMutilple:NO];
    
    return image;
}


+ (UIImage *)tp_imagecutWithOriginalImage:(UIImage *)originalImage withCutRect:(CGRect)rect {
    CGImageRef subImageRef = CGImageCreateWithImageInRect(originalImage.CGImage, rect);
    CGRect smallRect = CGRectMake(0, 0, CGImageGetWidth(subImageRef), CGImageGetHeight(subImageRef));
    // 開啟圖形上下文
    UIGraphicsBeginImageContext(smallRect.size);
    CGContextRef context = UIGraphicsGetCurrentContext();
    CGContextDrawImage(context, smallRect, subImageRef);
    UIImage * image = [UIImage imageWithCGImage:subImageRef];
    // 關(guān)閉圖形上下文
    UIGraphicsEndImageContext();
    
    CGImageRelease(subImageRef);
    
    return image;
}

結(jié)語(yǔ): 此篇文章的目的,主要是為了記錄自己使用過(guò)的知識(shí)點(diǎn),一來(lái)加深印象,二來(lái)也是為自己做下筆記。所以,文章大部分都以代碼為主,讀起來(lái)很是生涉,請(qǐng)諒解。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 大概翻譯了一下,非常的粗糙,慢慢修正。請(qǐng)高手自動(dòng)飄過(guò)~ 因?yàn)樽鰣D像壓縮時(shí)會(huì)損失相機(jī)寫入的如光圈、快門等信息,所以自...
    流年易逝_(tái)李閱讀 5,556評(píng)論 0 1
  • 1.Exif簡(jiǎn)介 可交換圖像文件格式常被簡(jiǎn)稱為Exif(Exchangeable image file forma...
    飛行的孤獨(dú)員FG閱讀 14,218評(píng)論 18 34
  • 版權(quán)聲明:本文為博主原創(chuàng)文章,遵循 CC 4.0 BY-SA 版權(quán)協(xié)議,轉(zhuǎn)載請(qǐng)附上原文出處鏈接和本聲明。本文鏈接:...
    pyx0225閱讀 1,881評(píng)論 0 0
  • JPEG格式和標(biāo)志JPEG文件都是以十六進(jìn)制的 0xFFD8 開始,以 0xFFD9 結(jié)束。在JPEG數(shù)據(jù)中,0x...
    cain_huang閱讀 7,930評(píng)論 7 2
  • http://feihu.me/blog/2015/how-to-handle-image-orientation...
    wzf_taker閱讀 1,825評(píng)論 1 4

友情鏈接更多精彩內(nèi)容