iOS 拍照中加入GPS和具体地理位置

时间:2022-11-09 08:27:02

最近有一个需求,要求用手机拍个照片,并切需要拍摄时间,拍摄gps,拍摄具体街道信息。

首先要感谢PhotoGPSdemo的作者,你可以到这里下载demo http://www.cocoachina.com/bbs/read.php?tid=130501

以前,总认为jpg就是包含了像素信息的2进制文件,其实,jpg中还可以包含许多起它的信息,只不过我们平常用查看jpg属性时,系统没有给我们把信息全部显示出来而已!

在iOS中,提供了一组函数来操作jpg的这些额外的信息,你需要#import <ImageIO/ImageIO.h>才能使用他们。

首先,需要注意的是,UIImage对象中是没有这些信息的,它仅仅包含图像本身,jpg才包含这些信息,请不要弄混。

先看看怎么从jpg图片中取得各种额外信息,代码如下

- (IBAction)test
{
NSString * home = NSHomeDirectory();
NSData *data = [NSData dataWithContentsOfFile:[NSString stringWithFormat:@"%@/Documents/xx.jpg",home]]; CGImageSourceRef imgSource = CGImageSourceCreateWithData((__bridge CFDataRef)data, NULL); CFDictionaryRef metaDataDicRef = CGImageSourceCopyPropertiesAtIndex(imgSource,,nil);
NSLog(@"cfdictionary %@",(__bridge NSDictionary *)metaDataDicRef); NSDictionary *dic = (__bridge NSDictionary *)metaDataDicRef; NSDictionary *exifDic = [dic objectForKey:(NSString *)kCGImagePropertyExifDictionary]; NSString *location = [exifDic objectForKey:(NSString *)kCGImagePropertyExifCameraOwnerName];
NSLog(@"location is %@",location); NSString *DateTimeOriginal = [exifDic objectForKey:(NSString *)kCGImagePropertyExifDateTimeOriginal];
NSLog(@"time is %@",DateTimeOriginal); CFRelease(metaDataDicRef);
CFRelease(imgSource);
}

首先,需要用到CGImageSourceRef类,不能用UIImage类型(需要验证。。。),这种CGImageSourceRef类型可以通过函数取出jpg的额外数据,这些数据叫做metaData,翻译过来就是元数据。得到的metaData是以字典形式返回的,其中的key是固定的,具体声明见下方

CFStringRef kCGImagePropertyTIFFDictionary;
CFStringRef kCGImagePropertyGIFDictionary;
CFStringRef kCGImagePropertyJFIFDictionary;
CFStringRef kCGImagePropertyExifDictionary;
CFStringRef kCGImagePropertyPNGDictionary;
CFStringRef kCGImagePropertyIPTCDictionary;
CFStringRef kCGImagePropertyGPSDictionary;
CFStringRef kCGImagePropertyRawDictionary;
CFStringRef kCGImagePropertyCIFFDictionary;
CFStringRef kCGImageProperty8BIMDictionary;
CFStringRef kCGImagePropertyDNGDictionary;
CFStringRef kCGImagePropertyExifAuxDictionary;

这些key中,有许多key对应的对象还是一个字典,比如kCGImagePropertyExifDictionary所对应的对象包含一下key值

const CFStringRef kCGImagePropertyExifExposureTime;
const CFStringRef kCGImagePropertyExifFNumber;
const CFStringRef kCGImagePropertyExifExposureProgram;
const CFStringRef kCGImagePropertyExifSpectralSensitivity;
const CFStringRef kCGImagePropertyExifISOSpeedRatings;
const CFStringRef kCGImagePropertyExifOECF;
const CFStringRef kCGImagePropertyExifVersion;
const CFStringRef kCGImagePropertyExifDateTimeOriginal;
const CFStringRef kCGImagePropertyExifDateTimeDigitized;
const CFStringRef kCGImagePropertyExifComponentsConfiguration;
const CFStringRef kCGImagePropertyExifCompressedBitsPerPixel;
const CFStringRef kCGImagePropertyExifShutterSpeedValue;
const CFStringRef kCGImagePropertyExifApertureValue;
const CFStringRef kCGImagePropertyExifBrightnessValue;
const CFStringRef kCGImagePropertyExifExposureBiasValue;
const CFStringRef kCGImagePropertyExifMaxApertureValue;
const CFStringRef kCGImagePropertyExifSubjectDistance;
const CFStringRef kCGImagePropertyExifMeteringMode;
const CFStringRef kCGImagePropertyExifLightSource;
const CFStringRef kCGImagePropertyExifFlash;
const CFStringRef kCGImagePropertyExifFocalLength;
const CFStringRef kCGImagePropertyExifSubjectArea;
const CFStringRef kCGImagePropertyExifMakerNote;
const CFStringRef kCGImagePropertyExifUserComment;
const CFStringRef kCGImagePropertyExifSubsecTime;
const CFStringRef kCGImagePropertyExifSubsecTimeOrginal;
const CFStringRef kCGImagePropertyExifSubsecTimeDigitized;
const CFStringRef kCGImagePropertyExifFlashPixVersion;
const CFStringRef kCGImagePropertyExifColorSpace;
const CFStringRef kCGImagePropertyExifPixelXDimension;
const CFStringRef kCGImagePropertyExifPixelYDimension;
const CFStringRef kCGImagePropertyExifRelatedSoundFile;
const CFStringRef kCGImagePropertyExifFlashEnergy;
const CFStringRef kCGImagePropertyExifSpatialFrequencyResponse;
const CFStringRef kCGImagePropertyExifFocalPlaneXResolution;
const CFStringRef kCGImagePropertyExifFocalPlaneYResolution;
const CFStringRef kCGImagePropertyExifFocalPlaneResolutionUnit;
const CFStringRef kCGImagePropertyExifSubjectLocation;
const CFStringRef kCGImagePropertyExifExposureIndex;
const CFStringRef kCGImagePropertyExifSensingMethod;
const CFStringRef kCGImagePropertyExifFileSource;
const CFStringRef kCGImagePropertyExifSceneType;
const CFStringRef kCGImagePropertyExifCFAPattern;
const CFStringRef kCGImagePropertyExifCustomRendered;
const CFStringRef kCGImagePropertyExifExposureMode;
const CFStringRef kCGImagePropertyExifWhiteBalance;
const CFStringRef kCGImagePropertyExifDigitalZoomRatio;
const CFStringRef kCGImagePropertyExifFocalLenIn35mmFilm;
const CFStringRef kCGImagePropertyExifSceneCaptureType;
const CFStringRef kCGImagePropertyExifGainControl;
const CFStringRef kCGImagePropertyExifContrast;
const CFStringRef kCGImagePropertyExifSaturation;
const CFStringRef kCGImagePropertyExifSharpness;
const CFStringRef kCGImagePropertyExifDeviceSettingDescription;
const CFStringRef kCGImagePropertyExifSubjectDistRange;
const CFStringRef kCGImagePropertyExifImageUniqueID;
const CFStringRef kCGImagePropertyExifGamma;
const CFStringRef kCGImagePropertyExifCameraOwnerName;
const CFStringRef kCGImagePropertyExifBodySerialNumber;
const CFStringRef kCGImagePropertyExifLensSpecification;
const CFStringRef kCGImagePropertyExifLensMake;
const CFStringRef kCGImagePropertyExifLensModel;
const CFStringRef kCGImagePropertyExifLensSerialNumber;

这里的每一个key都有具体的含义,代表了jpg的一些信息。这些key和key的值时有严格的格式限定的,你用了自己定义的key或者不符合规则的值,是不能正确地写入jpg文件的(待会要提到如何写入metaData信息)。


如何用UIImagePickerController拍摄照片后加入信息呢?用这个控件拍照后,默认生成的metaData信息较少,只有拍摄时间等几个信息,没有gps信息,更没有街道信息,我们需要手动加入。


#pragma mark - UIImagePickerControllerDelegate



- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info


{


_mediaInfo =[NSMutableDictionarydictionaryWithDictionary:info];



if (!_locationManager) {


_locationManager = [[CLLocationManager alloc]init];


[_locationManagersetDelegate:self];


[_locationManagersetDistanceFilter:kCLDistanceFilterNone];


[_locationManagersetDesiredAccuracy:kCLLocationAccuracyBest];


}


[_locationManagerstartUpdatingLocation];



[picker dismissViewControllerAnimated:YEScompletion:^


{



}];


}



#pragma mark CLLocationManagerDelegate


- (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation{


[manager stopUpdatingLocation];



UIImage *originalImage= (UIImage *) [_mediaInfoobjectForKey:UIImagePickerControllerOriginalImage];


NSData *imageNSData = UIImageJPEGRepresentation(originalImage,1);



CGImageSourceRef imgSource = CGImageSourceCreateWithData((__bridge CFDataRef)imageNSData, NULL);


//this is the type of image (e.g., public.jpeg)


CFStringRef UTI = CGImageSourceGetType(imgSource);



//this will be the data CGImageDestinationRef will write into


NSMutableData *newImageData = [NSMutableDatadata];


CGImageDestinationRef destination = CGImageDestinationCreateWithData((__bridge  CFMutableDataRef)newImageData, UTI, 1, NULL);



if(!destination)


{


NSLog(@"***Could not create image destination ***");


return;


}



//get original metadata


NSDictionary *dict = [_mediaInfoobjectForKey:UIImagePickerControllerMediaMetadata];


NSMutableDictionary *metadata = [NSMutableDictionarydictionaryWithDictionary:dict];



//set gps info into metadata,we need a dictionary


NSDictionary * gpsDict=[newLocation GPSDictionary];


if (metadata && gpsDict) {


[metadata setValue:gpsDict forKey:(NSString*)kCGImagePropertyGPSDictionary];


}



//get location detail by CLGeocoder, this needs wifi


CLGeocoder *geocoder = [[CLGeocoder alloc] init];


[geocoder reverseGeocodeLocation:newLocation completionHandler:^(NSArray *placemarks, NSError *error){


if(error != nil)


{


NSLog(@"CLGeocoder error :%@ ",error);


}


else//if we can get place info ,we set it into meteData dic with kCGImagePropertyExifCameraOwnerName key


{


if(placemarks.count > 0)


{


CLPlacemark *placemark = [placemarks objectAtIndex:0];


NSMutableDictionary *eifDic = (NSMutableDictionary *)[metadata objectForKey:(NSString *)kCGImagePropertyExifDictionary];


[eifDic setObject:placemark.nameforKey:(NSString *)kCGImagePropertyExifCameraOwnerName];


}


}



//add the image contained in the image source to the destination, overidding the old metadata with our modified metadata


CGImageDestinationAddImageFromSource(destination, imgSource, 0, (__bridge  CFDictionaryRef) metadata);



BOOL success = NO;


success = CGImageDestinationFinalize(destination);



if(!success) {


NSLog(@"***Could not create data from image destination ***");


return ;


}



CFRelease(imgSource);


CFRelease(destination);



[self sendData:newImageData];


[self writeTest:newImageData];



}];




}


 

@implementation CLLocation (GPSDictionary)

-(NSDictionary*)GPSDictionary{

NSDateFormatter *formatter  = [[NSDateFormatteralloc] init];

[formatter setDateFormat:@"hh:mm:ss.SS"];

CLLocation *location=self;

NSDictionary *gpsDict   = [NSDictionarydictionaryWithObjectsAndKeys:

[NSNumbernumberWithFloat:fabs(location.coordinate.latitude)], kCGImagePropertyGPSLatitude,

((location.coordinate.latitude >= 0) ? @"N" : @"S"), kCGImagePropertyGPSLatitudeRef,

[NSNumbernumberWithFloat:fabs(location.coordinate.longitude)], kCGImagePropertyGPSLongitude,

((location.coordinate.longitude >= 0) ? @"E" : @"W"), kCGImagePropertyGPSLongitudeRef,

[formatter stringFromDate:[location timestamp]], kCGImagePropertyGPSTimeStamp,

nil];

NSLog(@"gpsDict i %@",gpsDict);

return gpsDict;

}

@end

 

上面代码需要手动改改,它的最终目的就是用 CGImageDestinationAddImageFromSource(destination, imgSource, 0, (__bridge CFDictionaryRef) metadata);函数用新的metaData信息生成新的jpg数据。

其中gps数据用了系统自定义的key值写入,街道信息没有找到合适的key,就冒用了kCGImagePropertyExifCameraOwnerName这个key。