Frage

Es ist bekannt, dass UIImagePickerController nach der Auswahl nicht die Metadaten des Fotos zurückgibt.Einige Apps im App Store (Mobile Fotos, PixelPipe) scheinen jedoch in der Lage zu sein, die Originaldateien und die darin gespeicherten EXIF-Daten zu lesen, sodass die App die Geodaten aus dem ausgewählten Foto extrahieren kann.

Sie scheinen dies zu tun, indem sie die Originaldatei aus dem lesen /private/var/mobile/Media/DCIM/100APPLE/ Ordner kopieren und über eine EXIF-Bibliothek ausführen.

Ich kann jedoch keine Möglichkeit finden, ein vom UIImagePickerController zurückgegebenes Foto einer Datei auf der Festplatte zuzuordnen.Ich habe die Dateigrößen untersucht, aber die Originaldatei ist eine JPEG-Datei, während das zurückgegebene Bild ein rohes UIImage ist, sodass es unmöglich ist, die Dateigröße des ausgewählten Bildes zu kennen.

Ich denke darüber nach, eine Tabelle mit Hashes zu erstellen und diese mit den ersten x Pixeln jedes Bildes abzugleichen.Das scheint allerdings etwas übertrieben und wahrscheinlich ziemlich langsam zu sein.

Irgendwelche Vorschläge?

War es hilfreich?

Lösung 18

Die freche Art und Weise, dies zu tun, ist die UIImagePickerViewController Ansichten zu durchqueren und das ausgewählte Bild in den Delegaten Rückruf auswählen.

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

}

Das gibt Ihnen den Pfad zum Bild in voller Größe, die Sie dann mit einer EXIF-Bibliothek Ihrer Wahl offen.

Aber, nennt dies eine private API und diese Methodennamen werden von Apple erkannt werden, wenn Sie diese App einreichen. So dies nicht tun, OK?

Andere Tipps

Haben Sie sich diese Exif-iPhone-Bibliothek angesehen?

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

Werde es auf meiner Seite versuchen.Ich möchte die GPS-Koordinaten (Geotags) aus dem Bild erhalten, das mit dem UIImagePickerController aufgenommen wurde :/

Bei näherer Betrachtung scheint diese Bibliothek NSData-Informationen als Eingabe zu verwenden und der UIImagePickerController gibt nach der Erstellung eines Schnappschusses ein UIImage zurück.Theoretisch, wenn wir das Ausgewählte aus der UIkit-Kategorie für UIImage verwenden

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

Dann können wir das UIImage in eine NSData-Instanz konvertieren und es dann mit der iPhone-Exif-Bibliothek verwenden.

AKTUALISIEREN:
Ich habe die oben erwähnte Bibliothek getestet und es scheint zu funktionieren.Aufgrund meiner begrenzten Kenntnisse über das EXIF-Format und des Fehlens einer High-Level-API in der Bibliothek schaffe ich es jedoch nicht, die Werte für die EXIF-Tags abzurufen.Hier ist mein Code für den Fall, dass einer von euch weitermachen kann:


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

Das Abrufen der Tag-Definition ist in Ordnung, es werden jedoch alle Tag-Werte zurückgegeben nil :(

Falls Sie die Bibliothek ausprobieren möchten, müssen Sie eine globale Variable definieren, um sie zum Laufen zu bringen (wie im Dokument erklärt, aber hm...:/)

BOOL gLogging = FALSE;

UPDATE 2
Antwort hier: iPhone – Zugriff auf Standortinformationen von einem Foto Ein UIImage kapselt die Metainformationen nicht, also stecken wir fest:Natürlich werden über diese Schnittstelle keine EXIF-Informationen ausgegeben.

ENDGÜLTIGES UPDATE
Ok, ich habe es geschafft, es zum Laufen zu bringen, zumindest die vom Picker zurückgegebenen Bilder richtig mit Geotags zu versehen.
Bevor Sie den UIImagePickerController auslösen, müssen Sie den CLLocationManager verwenden, um die aktuelle CLocation abzurufen
Sobald Sie es haben, können Sie diese Methode verwenden, die die exif-iPhone-Bibliothek verwendet, um das UIImage aus dem CLLocation mit einem Geotag zu versehen:


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

Das mit iOS5 funktioniert (Beta 4) und der Kamerarolle (Sie müssen Typen defs für die Blöcke in der .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];
  }
}

Es gibt einen Weg in iOS 8

Ohne unter Verwendung eines beliebige dritte Partei EXIF Bibliothek.

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

Apple hat ein Bild I / O-Framework in iOS4 hinzugefügt, die verwendet werden können, EXIF-Daten aus Bildern zu lesen. Ich weiß nicht, ob das UIImagePickerController ein Bild mit dem EXIF-Daten eingebettet obwohl zurück.

Edit: In iOS4 können Sie die EXIF-Daten holen, indem Sie den Wert des UIImagePickerControllerMediaMetadata Schlüssels im Info Wörterbuch greifen, die an den UIImagePickerControllerDelegate Delegierten übergeben wird.

Ich hatte eine ähnliche Frage, wo ich ein Bild nur das Datum wollte genommen und keine der oben genannten scheint mein Problem auf einfache Art und Weise zu lösen (zB keine externen Bibliotheken), so ist hier alle Daten, die ich finden konnte die Sie können aus einem Bild extrahieren, nachdem es mit dem Picker Auswahl:

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

Also, wenn Sie ein Bild auswählen, erhalten Sie Ausgabe, die wie folgt aussieht (inklusive Datum!):

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

Wie auch immer, hoffentlich erspart, dass jemand anderes etwas Zeit.

Ich gebe auf diese als auch für eine Anwendung eines während der Arbeit ich wurde beauftragt, zu bauen. Im Grunde genommen wie die API zur Zeit steht, ist es nicht möglich. Das Grundproblem ist die UIImage Klasse STRIPS alle EXIF-Daten mit Ausnahme der Orientierung aus. Auch die Funktion der Kamerarolle Streifen dieser Daten heraus zu speichern. Also im Grunde der einzige Weg zu greifen und zu halten jede zusätzliche EXIF-Daten ist es in einem privaten „Camera Roll“ speichern in Ihrer Anwendung. Ich habe auch diesen Fehler mit Apfel abgelegt und betonte die Notwendigkeit, auf die App Rezensenten Wiederholungen mit denen wir in Kontakt sind. Hoffentlich ein Tag werden sie es in hinzufügen .. Ansonsten macht es mit GEO völlig nutzlos Tagging, da es nur in der „Lager“ Kamera-Anwendung funktioniert.

Hinweis Einige Anwendungen im App Store Hack , um diesen. Mit dem, was ich gefunden habe, direkt auf die Kamerarolle Zugriff und Speichern von Fotos direkt auf das GEO-Daten zu speichern. Allerdings funktioniert dies nur mit der Kamera Roll / gespeicherten Fotos und nicht den Rest der Fotobibliothek. Die Fotos „synchronisiert“ auf Ihr Telefon von Ihrem Computer haben alle EXIF-Daten mit Ausnahme der Orientierung beraubt.

Ich kann immer noch nicht verstehen, warum diese Anwendungen zugelassen wurden (heck sie sogar von der Kamerarolle DELETE) und unsere Anwendung, die nichts davon tut immer noch zurückgehalten wird.

Für iOS 8 und später können Sie verwenden Fotos 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)
            }
    }

Mit der UIImagePickerControllerMediaURL Wörterbuch, um die Datei-URL in die ursprüngliche Datei zu erhalten. Trotz allem, was die Dokumentation sagt, können Sie die Datei-URL für die Fotos bekommen und nicht nur Filme.

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

könnten Sie in der Lage sein, die Bilddaten durch die UIImagePickerController und jedes der Bilder in dem Verzeichnis, und vergleichen sie zurück Hash.

Nur ein Gedanke, aber haben Sie versucht TTPhotoViewController im Three20 Projekt auf GitHub?

Das liefert einen Bild-Picker, die aus mehreren Quellen lesen. Sie können in der Lage sein, es als Alternative zu verwenden, um UIImagePickerController, oder die Quelle Ihnen einen Hinweis geben könnte, wie zu erarbeiten, wie die Informationen, die Sie benötigen.

Gibt es einen bestimmten Grund, warum Sie die Standortdaten aus dem Bild extrahieren möchten? Eine Alternative könnte sein, den Standort separat zu erhalten, das Corelocation-Framework. Wenn es nur die Geodaten Sie brauchen, könnte dies einige Kopfschmerzen speichern.

es das Foto scheint durch UIImagePickerControllerMediaURL erreicht nicht EXIF-Tags überhaupt haben

Um diese Metadaten zu erhalten Sie den unteren Ebene Rahmen AVFoundation verwenden.

Werfen Sie einen Blick auf Apples Squarecam Beispiel (http://developer.apple.com/library/ios/#samplecode/SquareCam/Introduction/Intro.html)

Hier finden Sie die Methode unten und fügen Sie die Zeile, habe ich zum Code hinzugefügt. Das Wörterbuch Metadaten zurückgegeben enthält auch eine Diagnose NSDictionary Objekt.

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

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

}

Ich verwende diese für Kamerarolle Bilder

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

Es funktioniert offensichtlich, wenn das Bild GPS-Meta-Informationen

Hoffe, es hilft

Dies ist in Swift 3, wenn Sie noch Support für die 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!)
        })
    }

}
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top