UIImagePickerController并从现有照片中提取EXIF数据

众所周知,UIImagePickerController在select后不会返回照片的元数据。 然而,应用程序商店(Mobile Fotos,PixelPipe)中的一些应用程序似乎能够读取存储在其中的原始文件和EXIF数据,使应用程序能够从所选照片中提取地理数据。

他们似乎是通过从/ private / var / mobile / Media / DCIM / 100APPLE /文件夹读取原始文件并通过EXIF库运行它。

但是,我不能找出一种方法来匹配从UIImagePickerController返回到磁盘上的文件的照片。 我已经探索过文件大小,但原始文件是JPEG,而返回的图像是一个原始的UIImage,使得不可能知道所选图像的文件大小。

我正在考虑做一个哈希表,并匹配每个图像的前x像素。 这看起来有点过头了,可能还是很慢的。

有什么build议么?

你看看这个exif iPhone库吗?

http://code.google.com/p/iphone-exif/

要尝试在我身边。 我想要从UIImagePickerController拍摄的图片中获取GPS(地理标签)坐标:/

经过更深入的了解,这个库似乎把NSData信息作为input,UIImagePickerController拍摄快照后返回一个UIImage。 理论上讲,如果我们使用从UIImage中select的UIkit类别

NSData * UIImageJPEGRepresentation ( UIImage *image, CGFloat compressionQuality ); 

然后我们可以将UIImage转换成一个NSData实例,然后在iPhone exif库中使用它。

更新:
我对上面提到的图书馆进行了testing,似乎有效。 不过,由于我对EXIF格式的限制以及库中高级API的缺乏,我无法获得EXIF标签的值。 这是我的代码,以防你们任何一个人可以走得更远:

 #import "EXFJpeg.h" - (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingImage:(UIImage *)image editingInfo:(NSDictionary *)editingInfo { NSLog(@"image picked %@ with info %@", image, editingInfo); NSData* jpegData = UIImageJPEGRepresentation (image,0.5); EXFJpeg* jpegScanner = [[EXFJpeg alloc] init]; [jpegScanner scanImageData: jpegData]; EXFMetaData* exifData = jpegScanner.exifMetaData; EXFJFIF* jfif = jpegScanner.jfif; EXFTag* tagDefinition = [exifData tagDefinition: [NSNumber numberWithInt:EXIF_DateTime]]; //EXFTag* latitudeDef = [exifData tagDefinition: [NSNumber numberWithInt:EXIF_GPSLatitude]]; //EXFTag* longitudeDef = [exifData tagDefinition: [NSNumber numberWithInt:EXIF_GPSLongitude]]; id latitudeValue = [exifData tagValue:[NSNumber numberWithInt:EXIF_GPSLatitude]]; id longitudeValue = [exifData tagValue:[NSNumber numberWithInt:EXIF_GPSLongitude]]; id datetime = [exifData tagValue:[NSNumber numberWithInt:EXIF_DateTime]]; id t = [exifData tagValue:[NSNumber numberWithInt:EXIF_Model]]; .... .... 

标签定义的检索是可以的,但所有的标签值返回nil 🙁

如果你想尝试一下库,你需要定义一个全局variables来让它运行(如文档中所解释的,但哼:/)

BOOL gLogging = FALSE;

更新2
回答这里: iPhone – 从照片访问位置信息 UIImage没有封装元信息 ,所以我们卡住了:当然,没有EXIF信息将通过这个接口给出。

最终更新
好吧,我设法得到它的工作,至less正确地理标签拾取器返回的图片。
在触发UIImagePickerController之前,您可以使用CLLocationManager来检索当前的CLocation
一旦你有了它,你可以使用这个方法使用exif-iPhone库来从CLLocation地理标记UIImage:

-(NSData*) geotagImage:(UIImage*)image withLocation:(CLLocation*)imageLocation { NSData* jpegData = UIImageJPEGRepresentation(image, 0.8); EXFJpeg* jpegScanner = [[EXFJpeg alloc] init]; [jpegScanner scanImageData: jpegData]; EXFMetaData* exifMetaData = jpegScanner.exifMetaData; // end of helper methods // adding GPS data to the Exif object NSMutableArray* locArray = [self createLocArray:imageLocation.coordinate.latitude]; EXFGPSLoc* gpsLoc = [[EXFGPSLoc alloc] init]; [self populateGPS: gpsLoc :locArray]; [exifMetaData addTagValue:gpsLoc forKey:[NSNumber numberWithInt:EXIF_GPSLatitude] ]; [gpsLoc release]; [locArray release]; locArray = [self createLocArray:imageLocation.coordinate.longitude]; gpsLoc = [[EXFGPSLoc alloc] init]; [self populateGPS: gpsLoc :locArray]; [exifMetaData addTagValue:gpsLoc forKey:[NSNumber numberWithInt:EXIF_GPSLongitude] ]; [gpsLoc release]; [locArray release]; NSString* ref; if (imageLocation.coordinate.latitude <0.0) ref = @"S"; else ref =@"N"; [exifMetaData addTagValue: ref forKey:[NSNumber numberWithInt:EXIF_GPSLatitudeRef] ]; if (imageLocation.coordinate.longitude <0.0) ref = @"W"; else ref =@"E"; [exifMetaData addTagValue: ref forKey:[NSNumber numberWithInt:EXIF_GPSLongitudeRef] ]; NSMutableData* taggedJpegData = [[NSMutableData alloc] init]; [jpegScanner populateImageData:taggedJpegData]; [jpegScanner release]; return [taggedJpegData autorelease]; } 

-(NSData*) geotagImage:(UIImage*)image withLocation:(CLLocation*)imageLocation { NSData* jpegData = UIImageJPEGRepresentation(image, 0.8); EXFJpeg* jpegScanner = [[EXFJpeg alloc] init]; [jpegScanner scanImageData: jpegData]; EXFMetaData* exifMetaData = jpegScanner.exifMetaData; // end of helper methods // adding GPS data to the Exif object NSMutableArray* locArray = [self createLocArray:imageLocation.coordinate.latitude]; EXFGPSLoc* gpsLoc = [[EXFGPSLoc alloc] init]; [self populateGPS: gpsLoc :locArray]; [exifMetaData addTagValue:gpsLoc forKey:[NSNumber numberWithInt:EXIF_GPSLatitude] ]; [gpsLoc release]; [locArray release]; locArray = [self createLocArray:imageLocation.coordinate.longitude]; gpsLoc = [[EXFGPSLoc alloc] init]; [self populateGPS: gpsLoc :locArray]; [exifMetaData addTagValue:gpsLoc forKey:[NSNumber numberWithInt:EXIF_GPSLongitude] ]; [gpsLoc release]; [locArray release]; NSString* ref; if (imageLocation.coordinate.latitude <0.0) ref = @"S"; else ref =@"N"; [exifMetaData addTagValue: ref forKey:[NSNumber numberWithInt:EXIF_GPSLatitudeRef] ]; if (imageLocation.coordinate.longitude <0.0) ref = @"W"; else ref =@"E"; [exifMetaData addTagValue: ref forKey:[NSNumber numberWithInt:EXIF_GPSLongitudeRef] ]; NSMutableData* taggedJpegData = [[NSMutableData alloc] init]; [jpegScanner populateImageData:taggedJpegData]; [jpegScanner release]; return [taggedJpegData autorelease]; }

//帮助位置转换的方法
– (NSMutableArray *)createLocArray:(double)val {
val = fabs(val);
NSMutableArray * array = [[NSMutableArray alloc] init];
double deg =(int)val;
[数组addObject:[NSNumber numberWithDouble:deg]];
val = val – deg;
val = val * 60;
double minutes =(int)val;
[数组addObject:[NSNumber numberWithDouble:分钟]];
val = val – 分钟;
val = val * 60;
double seconds = val;
[数组addObject:[NSNumber numberWithDouble:秒]];
返回数组;
}
– (void)populateGPS 🙁 EXFGPSLoc *)gpsLoc:(NSArray *)locArray {
long numDenumArray [2];
long * arrPtr = numDenumArray;
[EXFUtils convertRationalToFraction:&arrPtr:[locArray objectAtIndex:0]];
EXFraction * fract = [[EXFraction alloc] initWith:numDenumArray [0]:numDenumArray [1]];
gpsLoc.degrees = fract;
[分解];
[EXFUtils convertRationalToFraction:&arrPtr:[locArray objectAtIndex:1]];
fract = [[EXFraction alloc] initWith:numDenumArray [0]:numDenumArray [1]];
gpsLoc.minutes = fract;
[分解];
[EXFUtils convertRationalToFraction:&arrPtr:[locArray objectAtIndex:2]];
fract = [[EXFraction alloc] initWith:numDenumArray [0]:numDenumArray [1]];
gpsLoc.seconds = fract;
[分解];
}

这适用于iOS5(testing版4)和相机胶卷(您需要在.h中键入defs块):

 -(void) imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info { NSString *mediaType = [info objectForKey:UIImagePickerControllerMediaType]; if ([mediaType isEqualToString:(NSString*)kUTTypeImage]) { NSURL *url = [info objectForKey:UIImagePickerControllerReferenceURL]; if (url) { ALAssetsLibraryAssetForURLResultBlock resultblock = ^(ALAsset *myasset) { CLLocation *location = [myasset valueForProperty:ALAssetPropertyLocation]; // location contains lat/long, timestamp, etc // extracting the image is more tricky and 5.x beta ALAssetRepresentation has bugs! }; ALAssetsLibraryAccessFailureBlock failureblock = ^(NSError *myerror) { NSLog(@"cant get image - %@", [myerror localizedDescription]); }; ALAssetsLibrary *assetsLib = [[ALAssetsLibrary alloc] init]; [assetsLib assetForURL:url resultBlock:resultblock failureBlock:failureblock]; } } 

iOS 8有一个方法

使用任何第三方EXIF库。

 #import <Photos/Photos.h> - (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info { NSURL *url = [info objectForKey:UIImagePickerControllerReferenceURL]; PHFetchResult *fetchResult = [PHAsset fetchAssetsWithALAssetURLs:@[url] options:nil]; PHAsset *asset = fetchResult.firstObject; //All you need is //asset.location.coordinate.latitude //asset.location.coordinate.longitude //Other useful properties of PHAsset //asset.favorite //asset.modificationDate //asset.creationDate } 

苹果已经在iOS4中添加了一个Image I / O Framework,可以用来读取图片中的EXIF数据。 我不知道如果UIImagePickerController返回embeddedEXIF数据的图片。

编辑:在iOS4中,您可以通过抓取传递给UIImagePickerControllerDelegate委托的信息字典中的UIImagePickerControllerMediaMetadata键的值来获取EXIF数据。

我有一个类似的问题,我只想拍一张照片的date,以上都不是以简单的方式解决我的问题(例如没有外部库),所以这里是我可以find的所有数据,你可以提取从select器中select后的图像:

 // Inside whatever implements UIImagePickerControllerDelegate @import AssetsLibrary; // ... your other code here ... @implementation MYImagePickerDelegate - (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info { NSString *mediaType = info[UIImagePickerControllerMediaType]; UIImage *originalImage = info[UIImagePickerControllerOriginalImage]; UIImage *editedImage = info[UIImagePickerControllerEditedImage]; NSValue *cropRect = info[UIImagePickerControllerCropRect]; NSURL *mediaUrl = info[UIImagePickerControllerMediaURL]; NSURL *referenceUrl = info[UIImagePickerControllerReferenceURL]; NSDictionary *mediaMetadata = info[UIImagePickerControllerMediaMetadata]; NSLog(@"mediaType=%@", mediaType); NSLog(@"originalImage=%@", originalImage); NSLog(@"editedImage=%@", editedImage); NSLog(@"cropRect=%@", cropRect); NSLog(@"mediaUrl=%@", mediaUrl); NSLog(@"referenceUrl=%@", referenceUrl); NSLog(@"mediaMetadata=%@", mediaMetadata); if (!referenceUrl) { NSLog(@"Media did not have reference URL."); } else { ALAssetsLibrary *assetsLib = [[ALAssetsLibrary alloc] init]; [assetsLib assetForURL:referenceUrl resultBlock:^(ALAsset *asset) { NSString *type = [asset valueForProperty:ALAssetPropertyType]; CLLocation *location = [asset valueForProperty:ALAssetPropertyLocation]; NSNumber *duration = [asset valueForProperty:ALAssetPropertyDuration]; NSNumber *orientation = [asset valueForProperty:ALAssetPropertyOrientation]; NSDate *date = [asset valueForProperty:ALAssetPropertyDate]; NSArray *representations = [asset valueForProperty:ALAssetPropertyRepresentations]; NSDictionary *urls = [asset valueForProperty:ALAssetPropertyURLs]; NSURL *assetUrl = [asset valueForProperty:ALAssetPropertyAssetURL]; NSLog(@"type=%@", type); NSLog(@"location=%@", location); NSLog(@"duration=%@", duration); NSLog(@"assetUrl=%@", assetUrl); NSLog(@"orientation=%@", orientation); NSLog(@"date=%@", date); NSLog(@"representations=%@", representations); NSLog(@"urls=%@", urls); } failureBlock:^(NSError *error) { NSLog(@"Failed to get asset: %@", error); }]; } [picker dismissViewControllerAnimated:YES completion:nil]; } @end 

所以,当你select一个图像,你会得到这样的输出(包括date!):

 mediaType=public.image originalImage=<UIImage: 0x7fb38e00e870> size {1280, 850} orientation 0 scale 1.000000 editedImage=<UIImage: 0x7fb38e09e1e0> size {640, 424} orientation 0 scale 1.000000 cropRect=NSRect: {{0, 0}, {1280, 848}} mediaUrl=(null) referenceUrl=assets-library://asset/asset.JPG?id=AC072879-DA36-4A56-8A04-4D467C878877&ext=JPG mediaMetadata=(null) type=ALAssetTypePhoto location=(null) duration=ALErrorInvalidProperty assetUrl=assets-library://asset/asset.JPG?id=AC072879-DA36-4A56-8A04-4D467C878877&ext=JPG orientation=0 date=2014-07-14 04:28:18 +0000 representations=( "public.jpeg" ) urls={ "public.jpeg" = "assets-library://asset/asset.JPG?id=AC072879-DA36-4A56-8A04-4D467C878877&ext=JPG"; } 

无论如何,希望这可以节省别人一些时间。

我花了一段时间来处理这个问题,以及我承包build立的一个应用程序。 基本上,现在的API是不可能的。 基本的问题是UIImage类STRIPS所有的EXIF数据,除了方向out。 此外,保存到相机胶卷的function也可以将这些数据去掉。 所以基本上,抓住和维护任何额外EXIF数据的唯一方法是将其保存在应用程序中的私有“相机胶卷”中。 我也提交了这个苹果的错误,并强调了我们一直在接触的应用审查代表的需要。 希望有一天他们会把它join..否则,它使得GEO标记完全无用,因为它只能在“股票”相机应用程序中工作。

注意应用程序商店的一些应用程序破解了这个。 通过我所发现的,直接访问相机胶卷并直接保存照片以保存GEO数据。 但是,这只适用于相机胶卷/保存的照片,而不是照片库的其余部分。 从计算机“同步”到您的手机的照片具有所有EXIF数据,除了取消方向。

我仍然不明白为什么这些应用程序被批准(他们甚至从相机胶卷中删除)和我们的应用程序,这些都没有被阻止。

这是公共API没有提供的东西,但可能对许多人有用。 你主要的办法是向苹果公司提交一个描述你所需要的东西的错误 (这也可能有助于解释你为什么需要它)。 希望你的请求可以使它成为未来的版本。

提交bug后,您还可以使用iPhone开发者计划成员资格附带的开发者技术支持(DTS)事件之一。 如果有一个公开的方式来做到这一点,苹果工程师将知道。 否则,至less可以帮助你在母亲身上得到更多的关注。 祝你好运!

使用UIImagePickerControllerMediaURL字典键将文件URL获取到原始文件。 尽pipe文档说什么,您可以获取照片的文件URL,而不仅仅是电影。

 - (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info { // Try to get the original file. NSURL *originalFile = [info objectForKey:UIImagePickerControllerMediaURL]; if (originalFile) { NSData *fileData = [NSData dataWithContentsOfURL:originalFile]; } } 

对于iOS 8及更高版本,您可以使用Photos Framework。

  func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) { let url = info[UIImagePickerControllerReferenceURL] as? URL if url != nil { let fetchResult = PHAsset.fetchAssets(withALAssetURLs: [url!], options: nil) let asset = fetchResult.firstObject print(asset?.location?.coordinate.latitude) print(asset?.creationDate) } } 

您可以将由UIImagePickerController返回的图像数据和目录中的每个图像进行散列,并对它们进行比较。

只是一个想法,但你有没有尝试在GitHub Three20项目TTPhotoViewController?

这提供了一个图像select器,可以从多个来源读取。 您可以使用它作为UIImagePickerController的替代方法,或者源代码可能会告诉您如何获取所需的信息。

是否有一个特定的原因,你想从图像中提取位置数据? 另一种方法是使用CoreLocation框架分别获取位置。 如果只是你需要的地理数据,这可能会让你省点头痛。

顽皮的方法是遍历UIImagePickerViewController的视图,并在委托callback中挑出选定的图像。

 - (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info { id thumbnailView = [[[[[[[[[[picker.view subviews] objectAtIndex:0] subviews] objectAtIndex:0] subviews] objectAtIndex:0] subviews] objectAtIndex:0] subviews] objectAtIndex:0]; NSString *fullSizePath = [[[thumbnailView selectedPhoto] fileGroup] pathForFullSizeImage]; NSString *thumbnailPath = [[[thumbnailView selectedPhoto] fileGroup] pathForThumbnailFile]; NSLog(@"%@ and %@", fullSizePath, thumbnailPath); } 

这将为您提供全尺寸图像的path,然后您可以用您select的EXIF库打开。

但是,这调用一个私人API,如果你提交这个应用程序,这些方法名称被苹果检测到。 所以不要这样做,好吗?

看来UIImagePickerControllerMediaURL获得的照片根本没有exif标签

为了得到这个元数据,你必须使用底层框架AVFoundation。

看看苹果的Squarecam例子(http://developer.apple.com/library/ios/#samplecode/SquareCam/Introduction/Intro.html);

find下面的方法,并添加行,我已经添加到代码。 返回的元数据字典还包含诊断NSDictionary对象。

 - (BOOL)writeCGImageToCameraRoll:(CGImageRef)cgImage withMetadata:(NSDictionary *)metadata { NSDictionary *Exif = [metadata objectForKey:@"Exif"]; // Add this line } 

我正在使用这个相机胶卷图像

 -(CLLocation*)locationFromAsset:(ALAsset*)asset { if (!asset) return nil; NSDictionary* pickedImageMetadata = [[asset defaultRepresentation] metadata]; NSDictionary* gpsInfo = [pickedImageMetadata objectForKey:(__bridge NSString *)kCGImagePropertyGPSDictionary]; if (gpsInfo){ NSNumber* nLat = [gpsInfo objectForKey:(__bridge NSString *)kCGImagePropertyGPSLatitude]; NSNumber* nLng = [gpsInfo objectForKey:(__bridge NSString *)kCGImagePropertyGPSLongitude]; if (nLat && nLng) return [[CLLocation alloc]initWithLatitude:[nLat doubleValue] longitude:[nLng doubleValue]]; } return nil; } -(void) imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info { //UIImage *image = [info objectForKey:UIImagePickerControllerOriginalImage]; NSURL *assetURL = [info objectForKey:UIImagePickerControllerReferenceURL]; // create the asset library in the init method of your custom object or view controller //self.library = [[ALAssetsLibrary alloc] init]; // [self.library assetForURL:assetURL resultBlock:^(ALAsset *asset) { // try to retrieve gps metadata coordinates CLLocation* myLocation = [self locationFromAsset:asset]; // Do your stuff.... } failureBlock:^(NSError *error) { NSLog(@"Failed to get asset from library"); }]; } 

它很明显,如果图像包含gps元信息

希望它有帮助

这是在Swift 3中,如果你仍然想要支持iOS 8:

 import AssetsLibrary func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) { if picker.sourceType == UIImagePickerControllerSourceType.photoLibrary, let url = info[UIImagePickerControllerReferenceURL] as? URL { let assetLibrary = ALAssetsLibrary() assetLibrary.asset(for: url, resultBlock: { (asset) in if let asset = asset { let assetRep: ALAssetRepresentation = asset.defaultRepresentation() let metaData: NSDictionary = assetRep.metadata() as NSDictionary print(metaData) } }, failureBlock: { (error) in print(error!) }) } } 

对于iOS 10 – Swift 3

select器的callback有一个info字典,其中有一个关键字与元数据 : UIImagePickerControllerMediaMetadata

图像选择器元数据示例