56

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

他们似乎通过从/private/var/mobile/Media/DCIM/100APPLE/文件夹中读取原始文件并通过 EXIF 库运行它来做到这一点。

但是,我无法找到将 UIImagePickerController 返回的照片与磁盘上的文件匹配的方法。我研究了文件大小,但原始文件是 JPEG,而返回的图像是原始 UIImage,因此无法知道所选图像的文件大小。

我正在考虑制作一个哈希表并匹配每个图像的前 x 个像素。不过,这似乎有点过头了,而且可能很慢。

有什么建议么?

4

18 回答 18

29

你看过这个 exif iPhone 库吗?

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

会在我这边试试。我想从使用 UIImagePickerController 拍摄的图片中获取 GPS(地理标签)坐标:/

经过深入研究,这个库似乎将 NSData 信息作为输入,并且 UIImagePickerController 在拍摄快照后返回 UIImage。理论上,如果我们使用从 UIkit 类别中选择的 UIImage

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

然后我们可以将 UIImage 转换为 NSData 实例,然后将其与 iPhone exif 库一起使用。

更新:
我对上面提到的库进行了测试,它似乎有效。然而,由于我对 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:(

如果您想尝试使用该库,则需要定义一个全局变量以使其运行(如文档中所述,但是嗯.. :/)

BOOL gLogging = FALSE;

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

最终更新
好吧,我设法让它工作,至少对选择器返回的图片进行正确的地理标记。
在触发 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];
}

// Helper methods for location conversion -(NSMutableArray*) createLocArray:(double) val{ val = fabs(val); NSMutableArray* array = [[NSMutableArray alloc] init]; double deg = (int)val; [array addObject:[NSNumber numberWithDouble:deg]]; val = val - deg; val = val*60; double minutes = (int) val; [array addObject:[NSNumber numberWithDouble:minutes]]; val = val - minutes; val = val*60; double seconds = val; [array addObject:[NSNumber numberWithDouble:seconds]]; return array; } -(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; [fract release]; [EXFUtils convertRationalToFraction:&arrPtr :[locArray objectAtIndex:1]]; fract = [[EXFraction alloc] initWith:numDenumArray[0] :numDenumArray[1]]; gpsLoc.minutes = fract; [fract release]; [EXFUtils convertRationalToFraction:&arrPtr :[locArray objectAtIndex:2]]; fract = [[EXFraction alloc] initWith:numDenumArray[0] :numDenumArray[1]]; gpsLoc.seconds = fract; [fract release]; }

于 2009-12-27T21:37:16.837 回答
23

这适用于 iOS5(beta 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];
  }
}
于 2011-07-28T06:28:03.027 回答
17

有办法进去iOS 8

使用任何 3rd 方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
}
于 2015-11-12T13:34:23.823 回答
6

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

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

于 2010-09-28T08:29:34.360 回答
4

我有一个类似的问题,我只想要一张照片的拍摄日期,而以上似乎都没有以简单的方式解决我的问题(例如,没有外部库),所以这里是我能找到的所有数据,你可以提取使用选择器选择图像后从图像中:

// 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

因此,当您选择图像时,您会得到如下所示的输出(包括日期!):

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";
}

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

于 2015-02-02T21:35:59.067 回答
3

对于 iOS 8 及更高版本,您可以使用照片框架。

 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)
            }
    }
于 2016-11-11T12:35:34.683 回答
2

我也花了一些时间来处理这个我签约构建的应用程序。基本上,就目前的 API 而言,这是不可能的。基本问题是 UIImage 类剥离除方向外的所有 EXIF 数据。此外,保存到相机胶卷的功能会删除这些数据。因此,基本上获取和维护任何额外 EXIF 数据的唯一方法是将其保存在应用程序的私有“相机胶卷”中。我也向苹果提交了这个错误,并向我们接触过的应用审查代表强调了这一点。希望有一天他们会添加它。否则它会使 GEO 标记完全无用,因为它仅适用于“库存”相机应用程序。

注意App Store上的一些应用程序会解决这个问题。通过,我发现,直接访问相机胶卷并将照片直接保存到它以保存 GEO 数据。然而,这只适用于相机胶卷/保存的照片,而不适用于照片库的其余部分。从您的计算机“同步”到您手机的照片具有除方向剥离外的所有 EXIF 数据。

我仍然无法理解为什么这些应用程序被批准(他们甚至从相机胶卷中删除),而我们的应用程序仍然没有做到这一点。

于 2010-01-11T14:35:37.257 回答
1

这是公共 API 不提供的东西,但可能对许多人有用。您的主要办法是向Apple 提交描述您需要的错误(并且解释您为什么需要它也会有所帮助)。希望您的请求可以使其成为未来的版本。

提交错误后,您还可以使用 iPhone 开发人员计划会员资格附带的开发人员技术支持 (DTS) 事件之一。如果有公开的方式来做到这一点,苹果工程师就会知道。否则,它至少可以帮助您在母舰中获得更多关注。祝你好运!

于 2009-11-12T08:38:46.397 回答
1

使用UIImagePickerControllerMediaURL字典键获取原始文件的文件 URL。不管文档怎么说,您都可以获得照片的文件 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];
    }
}
于 2010-08-03T08:29:20.237 回答
0

只是一个想法,但是你在 GitHub 上的 Three20 项目中尝试过 TTPhotoViewController 吗?

这提供了一个可以从多个来源读取的图像选择器。您也许可以将它用作 UIImagePickerController 的替代品,或者源代码可能会为您提供如何计算如何获取所需信息的线索。

于 2009-11-09T15:30:01.943 回答
0

I'm using this for camera roll images

-(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");
    }];
}

It works obviously if the image contains gps meta informations

Hope it helps

于 2014-04-15T14:16:10.933 回答
0

似乎 UIImagePickerControllerMediaURL 获得的照片根本没有 exif 标签

于 2010-09-07T08:11:19.143 回答
0

您也许可以对 UIImagePickerController 返回的图像数据和目录中的每个图像进行哈希处理并进行比较。

于 2009-10-29T20:05:34.433 回答
0

您是否有特定原因要从图像中提取位置数据?另一种方法是使用 CoreLocation 框架单独获取位置。如果它只是您需要的地理数据,这可能会为您省去一些麻烦。

于 2009-11-09T15:54:16.293 回答
0

In order to get this metadata you'll have to use the lower level framework AVFoundation.

Take a look at Apple's Squarecam example (http://developer.apple.com/library/ios/#samplecode/SquareCam/Introduction/Intro.html)

Find the method below and add the line, I've added to the code. The metadata dictionary returned also contains a diagnostics NSDictionary object.

- (BOOL)writeCGImageToCameraRoll:(CGImageRef)cgImage withMetadata:(NSDictionary *)metadata
{

   NSDictionary *Exif = [metadata objectForKey:@"Exif"];   // Add this line

}
于 2012-08-02T06:54:26.483 回答
0

如果您仍然希望支持 iOS 8,这在 Swift 3 中:

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!)
        })
    }

}
于 2017-03-05T13:18:11.020 回答
0

对于 iOS 10 - 斯威夫特 3

选择器的回调有一个info字典,其中有一个带有metadata的键:UIImagePickerControllerMediaMetadata

图像选择器元数据示例

于 2017-10-10T01:19:33.717 回答
-2

这样做的调皮方法是遍历 UIImagePickerViewController 的视图并在委托回调中挑选出选定的图像。

- (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);

}

这将为您提供完整尺寸图像的路径,然后您可以使用您选择的 EXIF 库打开该图像。

但是,这会调用私有 API,如果您提交此应用程序,Apple将检测到这些方法名称。所以不要这样做,好吗?

于 2010-01-13T11:31:44.427 回答