web-dev-qa-db-ja.com

UIImagePickerControllerおよび既存の写真からのEXIFデータの抽出

UIImagePickerControllerが選択後に写真のメタデータを返さないことはよく知られています。ただし、アプリストア内のいくつかのアプリ(Mobile Fotos、PixelPipe)は、元のファイルとその中に保存されているEXIFデータを読み取ることができるようであるため、アプリは選択した写真から地理データを抽出できます。

/ private/var/mobile/Media/DCIM/100Apple /フォルダーから元のファイルを読み取り、EXIFライブラリーを介して実行することで、これを行うようです。

ただし、UIImagePickerControllerから返された写真をディスク上のファイルに一致させる方法を見つけることはできません。ファイルサイズを調べましたが、元のファイルはJPEGであり、返された画像は生のUIImageであるため、選択された画像のファイルサイズを知ることができません。

ハッシュのテーブルを作成し、各画像の最初のxピクセルと照合することを検討しています。しかし、これは少し上に見え、おそらくかなり遅いでしょう。

助言がありますか?

56
tomtaylor

このexif iPhoneライブラリをご覧になりましたか?

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

私の側で試してみます。 UIImagePickerControllerで撮影した写真からGPS(geotags)座標を取得したい

詳細を見ると、このライブラリはNSData情報を入力として受け取り、UIImagePickerControllerはスナップショットを取得した後にUIImageを返します。理論的には、UIImageのUIkitカテゴリから選択したものを使用すると

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]; }
29
yonel

これは、iOS5(ベータ4)とカメラロールで機能します(.hのブロックにはtype defが必要です)。

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

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
}
17

Appleは、画像からEXIFデータを読み取るために使用できるiOS4にImage I/O Frameworkを追加しました。 UIImagePickerControllerがEXIFデータが埋め込まれた画像を返すかどうかはわかりません。

編集: iOS4では、UIImagePickerControllerMediaMetadataデリゲートに渡される情報ディクショナリのUIImagePickerControllerDelegateキーの値を取得することにより、EXIFデータを取得できます。

5

写真が撮られた日付だけが欲しいという同様の質問がありましたが、上記のどれも簡単な方法で問題を解決するようには見えません(例えば、外部ライブラリなし)、ここにあなたが抽出できるすべてのデータがありますピッカーで選択した後の画像から:

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

とにかく、誰かの時間をいくらか節約できることを願っています。

4
plowman

ビルドするように契約されたアプリケーションについても、この作業にしばらく時間を費やしています。基本的に、APIは現在のところ有効ではありません。基本的な問題は、UIImageクラスが向きを除くすべてのEXIFデータをSTRIPSすることです。また、カメラロールに保存する機能は、このデータを取り除きます。したがって、基本的に余分なEXIFデータを取得して維持する唯一の方法は、アプリケーションのプライベート「カメラロール」に保存することです。私はこのバグをAppleで提出し、私たちが連絡してきたアプリのレビュー担当者に必要性を強調しました。いつか追加することを望みます。 「ストック」カメラアプリケーションでのみ機能するため、タグ付けはまったく役に立ちません。

[〜#〜] note [〜#〜]アプリストアの一部のアプリケーションhack私が見つけたのは、カメラロールに直接アクセスし、写真を直接保存してGEOデータを保存することです。ただし、これはカメラロール/保存された写真でのみ機能し、残りの写真ライブラリでは機能しません。お使いのコンピューターから携帯電話に「同期」された写真には、向きが取り除かれた以外のすべてのEXIFデータが含まれています。

これらのアプリケーションが承認された理由(カメラロールからDELETEすることもある)と、それを実行しないアプリケーションがまだ抑制されていないことを、まだ理解できません。

2
Urkle

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)
            }
    }
2
Aravindhan

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];
    }
}
1
lucius

これは、パブリックAPIが提供していないものですが、多くの人々に役立つ可能性があります。主な手段は Appleにバグを報告する です。これは必要なものを説明します(また、なぜそれが必要なのかを説明することも役立ちます)。あなたのリクエストが将来のリリースになることを願っています。

バグを提出した後、iPhone開発者プログラムメンバーシップに付属する開発者テクニカルサポート(DTS)インシデントの1つを使用することもできます。これを行う公的な方法がある場合、Appleエンジニアが知っています。

1
natevw

このメタデータを取得するには、低レベルのフレームワーク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

}
0
Adam Roberts

カメラロール画像にこれを使用しています

-(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メタ情報が含まれている場合は明らかに機能します

それが役に立てば幸い

0
Luca Iaco

画像から位置データを抽出する特定の理由はありますか?別の方法として、CoreLocationフレームワークを使用して場所を個別に取得することもできます。必要なジオデータのみである場合、これにより頭痛の種を軽減できます。

0
RupertP

ちょっと考えましたが、GitHubのThree20プロジェクトでTTPhotoViewControllerを試しましたか?

これにより、複数のソースから読み取ることができる画像ピッカーが提供されます。 UIImagePickerControllerの代替として使用できる場合があります。または、ソースから、必要な情報を取得する方法の手がかりが得られる場合があります。

0
Simon

UIImagePickerControllerによって返された画像データとディレクトリ内の各画像をハッシュし、それらを比較できる場合があります。

0
Frank Schmitt

これはSwift 3です。iOS8のサポートが必要な場合:

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

}
0
Sam

uIImagePickerControllerMediaURLで取得した写真にはexifタグがまったくないようです

0
RolandasR

IOS 10の場合-Swift 3

ピッカーのコールバックには、 メタデータinfoのキーがあるUIImagePickerControllerMediaMetadata dictがあります

Image picker metadata example

0
Efren