56

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

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

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

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

助言がありますか?

4

18 に答える 18

29

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

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

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

よく調べてみると、このライブラリはNSData情報を入力として受け取っているようで、UIImagePickerControllerはスナップショットを撮った後にUIImageを返します。理論的には、UIImageのUIkitカテゴリから選択したものを使用する場合

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

次に、UIImageをNSDataインスタンスに変換し、iPhoneexifライブラリで使用できます。

更新:
上記のライブラリにテストを行いましたが、機能しているようです。ただし、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]];
....
....

タグ定義の取得はOKですが、すべてのタグ値は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];
}

// 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(ベータ4)とカメラロール(.hのブロックにタイプ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];
  }
}
于 2011-07-28T06:28:03.027 に答える
17

方法があります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
}
于 2015-11-12T13:34:23.823 に答える
6

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

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

于 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以降では、PhotosFrameworkを使用できます

 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データをSTRIPSすることです。また、カメラロールに保存する機能はこのデータを取り除きます。したがって、基本的に、追加のEXIFデータを取得して維持する唯一の方法は、アプリケーションのプライベートな「カメラロール」に保存することです。私もこのバグをアップルに提出し、私たちが連絡を取っているアプリレビュー担当者にその必要性を強調しました。うまくいけば、いつか彼らはそれを追加するでしょう。そうでなければ、それは「ストック」カメラアプリケーションでのみ機能するので、GEOタグ付けを完全に役に立たなくします。

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

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

于 2010-01-11T14:35:37.257 に答える
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 に答える
1

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

バグを報告した後、iPhone開発者プログラムのメンバーシップに付属する開発者テクニカルサポート(DTS)インシデントの1つを使用することもできます。これを行う公的な方法があれば、Appleのエンジニアは知っているでしょう。そうでなければ、それは少なくともあなたの窮状を母性の中でもう少し注意を引くのを助けるかもしれません。頑張ってください!

于 2009-11-12T08:38:46.397 に答える
0

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

于 2009-10-29T20:05:34.433 に答える
0

考えただけですが、GitHubのThree20プロジェクトでTTPhotoViewControllerを試しましたか?

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

于 2009-11-09T15:30:01.943 に答える
0

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

于 2009-11-09T15:54:16.293 に答える
0

UIImagePickerControllerMediaURLによって取得された写真には、exifタグがまったくないようです

于 2010-09-07T08:11:19.143 に答える
0

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

}
于 2012-08-02T06:54:26.483 に答える
0

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

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

それが役に立てば幸い

于 2014-04-15T14:16:10.933 に答える
0

iOS 8のサポートが必要な場合は、これはSwift3にあります。

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

iOS10の場合-Swift3

ピッカーのコールバックには、メタデータinfoを持つキーがある場合のdictがあります。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 に答える