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

发布于 2024-07-30 12:14:43 字数 452 浏览 10 评论 0原文

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

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

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

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

有什么建议么?

It's well known that UIImagePickerController doesn't return the metadata of the photo after selection. However, a couple of apps in the app store (Mobile Fotos, PixelPipe) seem to be able to read the original files and the EXIF data stored within them, enabling the app to extract the geodata from the selected photo.

They seem to do this by reading the original file from the /private/var/mobile/Media/DCIM/100APPLE/ folder and running it through an EXIF library.

However, I can't work out a way of matching a photo returned from the UIImagePickerController to a file on disk. I've explored file sizes, but the original file is a JPEG, whilst the returned image is a raw UIImage, making it impossible to know the file size of the image that was selected.

I'm considering making a table of hashes and matching against the first x pixels of each image. This seems a bit over the top though, and probably quite slow.

Any suggestions?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(18

孤独陪着我 2024-08-06 12:14:44

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

it seems that photo attained by UIImagePickerControllerMediaURL don't have exif tags at all

爱冒险 2024-08-06 12:14:44

为了获取此元数据,您必须使用较低级别的框架 AVFoundation。

看一下Apple的Squarecam示例(http://developer.apple.com/library/ios/#samplecode/SquareCam/Introduction/Intro.html)

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

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

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

}

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

}
过去的过去 2024-08-06 12:14:44

我将其用于相机胶卷图像

-(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 元信息,它显然可以工作

希望它有帮助

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

骄傲 2024-08-06 12:14:44

如果您仍然希望支持 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!)
        })
    }

}

This is in Swift 3 if you still want support for 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!)
        })
    }

}
晨曦÷微暖 2024-08-06 12:14:44

对于 iOS 10 - Swift 3

选择器的回调有一个 info 字典,其中有一个带有 元数据UIImagePickerControllerMediaMetadata

图像选择器元数据示例

For iOS 10 - Swift 3

The picker's callback has an info dict where there is a key with metadata: UIImagePickerControllerMediaMetadata

Image picker metadata example

纸伞微斜 2024-08-06 12:14:44

执行此操作的顽皮方法是遍历 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 将检测到这些方法名称​​。 所以不要这样做,好吗?

The naughty way to do this is to traverse the UIImagePickerViewController's views and pick out the selected image in the delegate 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);

}

That will give you the path to the full size image, which you can then open with an EXIF library of your choice.

But, this calls a Private API and these method names will be detected by Apple if you submit this app. So don't do this, OK?

我不咬妳我踢妳 2024-08-06 12:14:43

你看过这个 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;

UPDATE 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];
}

// 位置转换的辅助方法 -(NSMutableArray*) createLocArray:(double) val{ val = fabs(val); NSMutableArray* 数组 = [[NSMutableArray alloc] init]; 双度 = (int)val; [array addObject:[NSNumber numberWithDouble:deg]]; val = val - 度; 值=值*60; 双分钟 = (int) val; [array addObject:[NSNumber numberWithDouble:分钟]]; val = val - 分钟; 值=值*60; 双秒 = val; [数组 addObject:[NSNumber numberWithDouble:秒]]; 返回数组; } -(void) populateGPS:(EXFGPSLoc*) gpsLoc:(NSArray*) locArray{ 长 numDenumArray[2]; 长* 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.分钟 = fract; [骨折释放]; [EXFUtils ConvertRationalToFraction:&arrPtr :[locArray objectAtIndex:2]]; fract = [[EXFraction alloc] initWith:numDenumArray[0] :numDenumArray[1]]; gpsLoc.seconds = fract; [骨折释放]; }

Have you took a look at this exif iPhone library?

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

Gonna try it on my side. I'd like to get the GPS (geotags) coordinates from the picture that has been taken with the UIImagePickerController :/

After a deeper look, this library seems to take NSData info as an input and the UIImagePickerController returns a UIImage after taking a snapshot. In theory, if we use the selected from the UIkit category for UIImage

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

Then we can convert the UIImage into a NSData instance and then use it with the iPhone exif library.

UPDATE:

I gave a test to the library mentioned above and it seems to work. However because of my limited knwoledge about the EXIF format and the lack of high level API in the library, I don't manage to get the values for the EXIF tags.
Here's my code in case any of you can go further :


#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]];
....
....

The retrieving of tags definition is OK, but all tag values returns nil :(

In case you want to give a try to the library, you need to define a global variable to get it running (as explained in the doc but hum.. :/)

BOOL gLogging = FALSE;

UPDATE 2

Answer here : iPhone - access location information from a photo
A UIImage does not encapsulate the meta information, so we're stuck : for sure, no EXIF info will be given through this interface.

FINAL UPDATE

Ok I managed to get it working, at least to geotag properly pictures returned by the picker.

Before triggering the UIImagePickerController, it's up to you to use the CLLocationManager to retrieve the current CLocation

Once you have it, you can use this method that uses exif-iPhone library to geotag the UIImage from the CLLocation :



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

灵芸 2024-08-06 12:14:43

这适用于 iOS5(beta 4)和相机胶卷(您需要 .h 中的块的类型定义):

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

This works with iOS5 (beta 4) and the camera roll (you need type defs for the blocks in the .h):

-(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];
  }
}
極樂鬼 2024-08-06 12:14:43

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
}

There is a way in iOS 8

Without using any 3rd party EXIF library.

#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
}
别念他 2024-08-06 12:14:43

Apple在iOS4中添加了图像I/O框架,可用于从图片中读取EXIF数据。 我不知道 UIImagePickerController 是否返回嵌入了 EXIF 数据的图片。

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

Apple has added an Image I/O Framework in iOS4 which can be used to read EXIF data from pictures. I don't know if the UIImagePickerController returns a picture with the EXIF data embedded though.

Edit: In iOS4 you can fetch the EXIF data by grabbing the value of the UIImagePickerControllerMediaMetadata key in the info dictionary which is passed to the UIImagePickerControllerDelegate delegate.

一笑百媚生 2024-08-06 12:14:43

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

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

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

I had a similar question where I wanted just the date a picture was taken and none of the above appear to solve my problem in a simple way (e.g. no external libraries), so here is all of the data I could find which you can extract from an image after selecting it with the picker:

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

So when you select an image, you get output that looks like this (including 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";
}

Anyway, hopefully that saves someone else some time.

冧九 2024-08-06 12:14:43

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

注意应用商店中的一些应用程序对此进行了破解。 据我发现,直接访问相机胶卷并将照片直接保存到其中以保存 GEO 数据。 但是,这仅适用于相机胶卷/保存的照片,不适用于照片库的其余部分。 从计算机“同步”到手机的照片具有除方向之外的所有 EXIF 数据。

我仍然不明白为什么这些应用程序获得批准(哎呀,他们甚至从相机胶卷中删除),而我们的应用程序却没有执行这些操作,仍然被阻止。

I spend a while working on this as well for an application I was contracted to build. Basically as the API currently stands it is not possible. The basic problem is the UIImage class STRIPS all EXIF data except for the orientation out. Also the function to save to the camera roll strips this data out. So basically the only way to grab and maintain any extra EXIF data is to save it in a private "camera roll" in your application. I have filed this bug with apple as well and emphasized the need to the app reviewer reps we've been in contact with. Hopefully someday they'll add it in.. Otherwise it makes having GEO tagging completely useless as it only works in the "stock" camera application.

NOTE Some applications on the app store hack around this. By, what I have found, directly accessing the camera roll and SAVING photos straight to it to save GEO data. However this only works with the camera roll/saved photos and NOT the rest of the photo library. The photos "synced" to your phone from your computer have all EXIF data except for orientation stripped.

I still can't understand why those applications were approved (heck they even DELETE from the camera roll) and our application which does none of that is still being held back.

℡寂寞咖啡 2024-08-06 12:14:43

对于 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)
            }
    }

For iOS 8 and later you can use 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)
            }
    }
旧情勿念 2024-08-06 12:14:43

这是公共 API 不提供的东西,但可能对很多人有用。 您的主要方法是向 Apple 提交错误,描述您需要什么(并且解释一下可能会有所帮助)为什么你也需要它)。 希望您的请求能够纳入未来的版本中。

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

This is something that the public API does not provide, but could be useful to many people. Your primary recourse is to file a bug with Apple that describes what you need (and it can be helpful to explain why you need it as well). Hopefully your request could make it into a future release.

After filing a bug, you could also use one of the Developer Technical Support (DTS) incidents that came with your iPhone Developer Program membership. If there is a public way to do this, an Apple engineer will know. Otherwise, it may at least help get your plight a bit more attention within the mothership. Best of luck!

与君绝 2024-08-06 12:14:43

使用 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];
    }
}

Use the UIImagePickerControllerMediaURL dictionary key to get the file URL to the original file. Despite what the documentation says, you can get the file URL for photos and not only movies.

- (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];
    }
}
謸气贵蔟 2024-08-06 12:14:43

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

You might be able to hash the image data returned by the UIImagePickerController and each of the images in the directory and compare them.

我ぃ本無心為│何有愛 2024-08-06 12:14:43

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

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

Just a thought, but have you tried TTPhotoViewController in the Three20 project on GitHub?

That provides an image picker that can read from multiple sources. You may be able to use it as an alternative to UIImagePickerController, or the source might give you a clue how to work out how to get the info you need.

聚集的泪 2024-08-06 12:14:43

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

Is there a specific reason you want to extract the location data from the image? An alternative could be to get the location separately using the CoreLocation framework. If it's only the geodata you need, this might save you some headaches.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文