iOS 读取修改图片的Exif信息

作者: 飞行的孤独员FG | 来源:发表于2016-10-20 02:16 被阅读5141次

1.Exif简介

可交换图像文件格式常被简称为Exif(Exchangeable image file format),是专门为数码相机的照片设定的,可以记录数码照片的属性信息和拍摄数据。

Exif可以附加于JPEG、TIFF、RIFF、EXIF、GPS等文件之中,为其增加有关数码相机拍摄信息的内容和索引图或图像处理软件的版本信息。

Exif信息以0xFFE1作为开头标记,后两个字节表示Exif信息的长度。所以Exif信息最大为64 kB,而内部采用TIFF格式。

2.读取Exif信息

Exif信息是通过ImageIO框架来实现的,ImageIO框架在iOS中偏低层,所以提供的接口都是C风格的,关键数据也都是使用CoreFoundation进行存储。进行数据的操作也就需要CoreFoundation和上层Foundation之间进行桥接转换。

1. 获取图片文件

NSURL *fileUrl = [[NSBundle mainBundle] URLForResource:@"YourPic" withExtension:@""];

2.创建CGImageSourceRef

CGImageSourceRef imageSource = CGImageSourceCreateWithURL((CFURLRef)fileUrl, NULL);

3.利用imageSource获取全部ExifData

CFDictionaryRef imageInfo = CGImageSourceCopyPropertiesAtIndex(imageSource, 0,NULL);

4.从全部ExifData中取出EXIF文件

NSDictionary *exifDic = (__bridge NSDictionary *)CFDictionaryGetValue(imageInfo, kCGImagePropertyExifDictionary) ;

5.打印全部Exif信息及EXIF文件信息

NSLog(@"All Exif Info:%@",imageInfo);
NSLog(@"EXIF:%@",exifDic);

一张佳能相机拍摄的照片中的Exif信息:

All Exif Info:{
    ColorModel = RGB;
    DPIHeight = 200;
    DPIWidth = 200;
    Depth = 8;
    Orientation = 1;
    PixelHeight = 667;
    PixelWidth = 1000;
    ProfileName = "sRGB IEC61966-2.1";
    "{Exif}" =  {
        ApertureValue = "3.375";
        BodySerialNumber = 214014001512;
        ColorSpace = 1;
        ComponentsConfiguration =         (
            1,
            2,
            3,
            0
        );
        CustomRendered = 0;
        DateTimeDigitized = "2016:07:05 16:12:02";
        DateTimeOriginal = "2016:07:05 16:12:02";
        ExifVersion =         (
            2,
            3
        );
        ExposureBiasValue = 0;
        ExposureMode = 0;
        ExposureProgram = 3;
        ExposureTime = "0.0004";
        FNumber = "3.2";
        Flash = 16;
        FlashPixVersion =         (
            1,
            0
        );
        FocalLength = 168;
        FocalPlaneResolutionUnit = 2;
        FocalPlaneXResolution = "3545.827586206897";
        FocalPlaneYResolution = "3526.530612244898";
        ISOSpeedRatings =         (
            400
        );
        LensMake = "Canon 35mm f1.4";
        LensModel = "2016/09/21 11:04:31";
        LensSerialNumber = 0000c08f5f;
        LensSpecification =         (
            70,
            200,
            0,
            0
        );
        MeteringMode = 3;
        PixelXDimension = 1000;
        PixelYDimension = 667;
        RecommendedExposureIndex = 400;
        SceneCaptureType = 0;
        SensitivityType = 2;
        ShutterSpeedValue = "11.375";
        SubsecTime = 795;
        SubsecTimeDigitized = 30;
        SubsecTimeOriginal = 30;
        WhiteBalance = 0;
    };
    "{IPTC}" =     {
        DateCreated = 20160705;
        DigitalCreationDate = 20160705;
        DigitalCreationTime = 161202;
        TimeCreated = 161202;
    };
    "{JFIF}" =     {
        DensityUnit = 0;
        JFIFVersion =         (
            1,
            0,
            1
        );
        XDensity = 200;
        YDensity = 200;
    };
    "{TIFF}" =     {
        DateTime = "2016:07:08 16:45:32";
        Make = Canon;
        Model = "Canon EOS-1D X";
        Orientation = 1;
        ResolutionUnit = 2;
        Software = "ACDSee Pro 8";
        XResolution = 200;
        YResolution = 200;
    };
}

3.写入Exif信息

1. 获取图片中的EXIF文件和GPS文件

NSData *imageData = UIImageJPEGRepresentation([UIImage imageNamed:@"YourImage"], 1);

CGImageSourceRef source = CGImageSourceCreateWithData((__bridge CFDataRef)data, NULL);

NSDictionary *imageInfo = (__bridge NSDictionary*)CGImageSourceCopyPropertiesAtIndex(source, 0, NULL);

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

2. 修改EXIF文件和GPS文件中的部分信息

[exifDic setObject:[NSNumber numberWithFloat:1234.3] forKey:(NSString *)kCGImagePropertyExifExposureTime];
[exifDic setObject:@"SenseTime" forKey:(NSString *)kCGImagePropertyExifLensModel];
    
[GPSDic setObject:@"N" forKey:(NSString*)kCGImagePropertyGPSLatitudeRef];
[GPSDic setObject:[NSNumber numberWithFloat:116.29353] forKey:(NSString*)kCGImagePropertyGPSLatitude];

[metaDataDic setObject:exifDic forKey:(NSString*)kCGImagePropertyExifDictionary];
[metaDataDic setObject:GPSDic forKey:(NSString*)kCGImagePropertyGPSDictionary];

3. 将修改后的文件写入至图片中

CFStringRef UTI = CGImageSourceGetType(source);
NSMutableData *newImageData = [NSMutableData data];
CGImageDestinationRef destination = CGImageDestinationCreateWithData((__bridge CFMutableDataRef)newImageData, UTI, 1,NULL);

//add the image contained in the image source to the destination, overidding the old metadata with our modified metadata
CGImageDestinationAddImageFromSource(destination, source, 0, (__bridge CFDictionaryRef)metaDataDic);
CGImageDestinationFinalize(destination);

4. 保存图片

NSString *directoryDocuments =  NSTemporaryDirectory();
[newImageData writeToFile: directoryDocuments atomically:YES];

5. 查看修改后图片的Exif信息

CIImage *testImage = [CIImage imageWithData:newImageData];
NSDictionary *propDict = [testImage properties];
NSLog(@"Properties %@", propDict);    

修改后的Exif信息:

Properties {
    ColorModel = RGB;
    Depth = 8;
    Orientation = 1;
    PixelHeight = 667;
    PixelWidth = 1000;
    ProfileName = "sRGB IEC61966-2.1";
    "{Exif}" =     {
        ColorSpace = 1;
        ExposureTime = "1234.3";
        LensModel = SenseTime;
        PixelXDimension = 1000;
        PixelYDimension = 667;
    };
    "{GPS}" =     {
        Latitude = "116.2935333333333";
        LatitudeRef = N;
    };
    "{JFIF}" =     {
        DensityUnit = 0;
        JFIFVersion =         (
            1,
            0,
            1
        );
        XDensity = 72;
        YDensity = 72;
    };
    "{TIFF}" =     {
        Orientation = 1;
    };
}

4.注意事项

关于无法修改Exif值的几点注意事项:

1. 传入的数据格式与Exif规定的不符

Exif的每条信息都有对应的数据类型,如:String Float... 如果数据类型传入错误将无法写入文件。

2. 传入的字段超过规定字段长度

3. 相互依赖的字段只添加了一个字段

在GPS文件中经纬度的度数的字段与经纬度的方向的字段相互依赖,修改经/纬度数需要经/纬方向字段的存在,否则修改无效。

5.外部链接

相关文章

网友评论

  • Hunter琼:你好 怎么的照片具有gps文件 我手机照的怎么没有?? 必须是佳能这样相机吗??
  • 流年_橙子: 题主 ,我给图片加个内存信息,图片在内存中查看有坐标信息,我保存到沙盒里的图片取出来查看并没有坐标信息啊,但是我如果用系统的方法保存的相册里就有了。
    流年_橙子:@飞行的孤独员FG :sweat_smile: 一年了 题主 我已经忘了这个事情了:smiley:
    飞行的孤独员FG:保存到沙盒的方式需从元数据保存,直接图片转NSData EXIF的信息就丢失了。
  • c065d1bd5517:如果是读取系统相册的照片可以用这种方式获取Exif么?在- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary<NSString *,id> *)info 方法中怎么使用这种方式?
    HanOBa:同问。
    求指导
  • a3517bb4c1dc:有没有demo啊
  • 9ab67bf6dc3f:哥们 你修改信息保存的第三步 - 将修改后的文件写入至图片中 貌似少了一句代码CGImageDestinationFinalize(destination); 所以照你的写 newImageData内容始终未空。
    让我找了好久好久!
    9ab67bf6dc3f:今天注意了下内存,发现内存不会释放, 需要添加
    CFRelease(imageSource);
    CFRelease(UTI);
    CFRelease(destination);
    9ab67bf6dc3f:@飞行的孤独员FG 首先感谢回复。我也不知道怎么描述我的问题,我直接说我的需求吧, 我上传的是原图,我希望可以修改这张原图exif的宽高值,我试着改了但是没有效果, 其他信息都能改,我在想这个宽高是不是不可改变的? 会根据图片实际宽高自动设置的, 所以手动只改这个值是无效的? 如果不可修改,我得考虑其他办法了。
    飞行的孤独员FG:@心里苦啊 多谢指正,已添加。
  • 前行哲:一张图片如果使用UIImageJPEGRepresentation或者UIImagePNGRepresentation转换成NSData进行上传,此时貌似会丢失Exif信息,这时要怎么处理呢?转完data后再重新写入Exif信息,再上传?
    飞行的孤独员FG:@继续前行 嗯,肯定的
    前行哲:这相当于直接传原图了,不进行任何压缩,压缩后重新写入貌似也可以。
    飞行的孤独员FG:@继续前行 因为exif也是个文件,直接取图拿到的是 UIImage,并没有exif文件。解决方法是将图片连同 exif 存入沙盒,从沙盒中直接把 file 转成nsdata上传就可以了

本文标题:iOS 读取修改图片的Exif信息

本文链接:https://www.haomeiwen.com/subject/svhfuttx.html