webentwicklung-frage-antwort-db.com.de

rahmen für iOS 8-Fotos. Greifen Sie auf Foto-Metadaten zu

Ich ersuche, ALAssetsLibrary durch Photos framework in meiner App zu ersetzen.

Ich kann Fotos, Sammlungen und Asset-Quellen problemlos abrufen (sogar zurückschreiben), aber ich sehe keinen Ort, um auf die Metadaten der Fotos zuzugreifen (die Wörterbücher wie {Exif}, {TIFF}, {GPS}, usw...).

ALAssetsLibrary hat einen Weg. UIImagePickerController hat einen Weg. Photos muss auch einen Weg haben. 

Ich sehe, dass PHAsset eine location -Eigenschaft hat, die für das GPS-Wörterbuch geeignet ist, aber ich möchte auf alle Metadaten zugreifen, darunter Gesichter, Orientierung, Belichtung, ISO und vieles mehr. 

Derzeit befindet sich Apple in der Betaversion 2. Vielleicht werden noch weitere APIs folgen?

UPDATE

Es gibt keine offizielle Möglichkeit, dies zu tun, indem nur Photos-APIs verwendet werden.

Sie können jedoch die Metadaten lesen, nachdem Sie die Bilddaten heruntergeladen haben. Dazu gibt es mehrere Methoden, die entweder PHImageManager oder PHContentEditingInput verwenden. 

Die PHContentEditingInput-Methode benötigte weniger Code und erfordert nicht den Import von ImageIO. Ich habe es in eine PHAsset-Kategorie eingepackt

25
VaporwareWolf

Wenn Sie eine Eingabe zur Bearbeitung des Inhalts anfordern, können Sie das vollständige Bild als CIImage abrufen. CIImage verfügt über die Eigenschaft properties, ein Wörterbuch, das die Bildmetadaten enthält.

Swiftcode-Beispielcode:

let options = PHContentEditingInputRequestOptions()
options.networkAccessAllowed = true //download asset metadata from iCloud if needed

asset.requestContentEditingInputWithOptions(options) { (contentEditingInput: PHContentEditingInput?, _) -> Void in
    let fullImage = CIImage(contentsOfURL: contentEditingInput!.fullSizeImageURL)

    print(fullImage.properties)
}

Beispiel-C-Code:  

PHContentEditingInputRequestOptions *options = [[PHContentEditingInputRequestOptions alloc] init];
options.networkAccessAllowed = YES; //download asset metadata from iCloud if needed

[asset requestContentEditingInputWithOptions:options completionHandler:^(PHContentEditingInput *contentEditingInput, NSDictionary *info) {
    CIImage *fullImage = [CIImage imageWithContentsOfURL:contentEditingInput.fullSizeImageURL];

    NSLog(@"%@", fullImage.properties.description);
}];

Sie erhalten die gewünschten Wörterbücher {Exif}, {TIFF}, {GPS} usw.

34
Jordan H

Ich dachte, ich würde etwas Code zum Lesen der Metadaten mit dem ImageIO-Framework in Verbindung mit dem Photos-Framework freigeben. Sie müssen die Bilddaten mit einem PHCachingImageManager anfordern.

@property (strong) PHCachingImageManager *imageManager;

Fordern Sie das Bild an und verwenden Sie dessen Daten, um ein Metadatenwörterbuch zu erstellen

-(void)metadataReader{
    PHFetchResult *result = [PHAsset fetchAssetsInAssetCollection:self.myAssetCollection options:nil];
    [result enumerateObjectsAtIndexes:[NSIndexSet indexSetWithIndex:myIndex] options:NSEnumerationConcurrent usingBlock:^(PHAsset *asset, NSUInteger idx, BOOL *stop) {
        [self.imageManager requestImageDataForAsset:asset options:nil resultHandler:^(NSData *imageData, NSString *dataUTI, UIImageOrientation orientation, NSDictionary *info) {
            NSDictionary *metadata = [self metadataFromImageData:imageData];
                           NSLog(@"Metadata: %@", metadata.description);
            NSDictionary *gpsDictionary = metadata[(NSString*)kCGImagePropertyGPSDictionary];
            if(gpsDictionary){
                NSLog(@"GPS: %@", gpsDictionary.description);
            }
            NSDictionary *exifDictionary = metadata[(NSString*)kCGImagePropertyExifDictionary];
            if(exifDictionary){
                NSLog(@"EXIF: %@", exifDictionary.description);
            }

            UIImage *image = [UIImage imageWithData:imageData scale:[UIScreen mainScreen].scale];
            // assign image where ever you need...
        }];

    }];
}

Konvertieren Sie NSData in Metadaten

-(NSDictionary*)metadataFromImageData:(NSData*)imageData{
    CGImageSourceRef imageSource = CGImageSourceCreateWithData((__bridge CFDataRef)(imageData), NULL);
    if (imageSource) {
        NSDictionary *options = @{(NSString *)kCGImageSourceShouldCache : [NSNumber numberWithBool:NO]};
        CFDictionaryRef imageProperties = CGImageSourceCopyPropertiesAtIndex(imageSource, 0, (__bridge CFDictionaryRef)options);
        if (imageProperties) {
            NSDictionary *metadata = (__bridge NSDictionary *)imageProperties;
            CFRelease(imageProperties);
            CFRelease(imageSource);
            return metadata;
        }
        CFRelease(imageSource);
    }

    NSLog(@"Can't read metadata");
    return nil;
}

Dies hat den Aufwand, das Bild zu packen, also ist es bei weitem nicht so schnell wie das Auflisten Ihrer Assets oder Sammlungen, aber es ist zumindest etwas. 

7
VaporwareWolf

Eine bessere Lösung, die ich für mich gefunden habe, ist:

[[PHImageManager defaultManager] requestImageDataForAsset:photoAsset
                                                  options:reqOptions
                                            resultHandler:
         ^(NSData *imageData, NSString *dataUTI, UIImageOrientation orientation, NSDictionary *info) {
             CIImage* ciImage = [CIImage imageWithData:imageData];
             DLog(@"Metadata : %@", ciImage.properties);
         }];
5
Karthik

PhotoKit beschränkt den Zugriff auf Metadaten auf die Eigenschaften von PHAsset (Standort, Erstellungsdatum, Favorit, ausgeblendet, Änderungsdatum, Pixelbreite, Pixelhöhe). Der Grund (vermute ich) ist, dass die Bilder aufgrund der Einführung von iCloud PhotoLibrary möglicherweise nicht auf dem Gerät gespeichert sind. Daher sind nicht die gesamten Metadaten verfügbar .. Die einzige Möglichkeit, vollständige EXIF ​​/ IPTC-Metadaten zu erhalten, besteht darin, zunächst das Originalbild (falls nicht verfügbar) von iCloud herunterzuladen und dann die Metadaten mit ImageIO zu extrahieren.

5
holtmann

Ich bevorzuge keine CIImage-Lösung, sondern eine ImageIO-Lösung:

func imageAndMetadataFromImageData(data: NSData)-> (UIImage?,[String: Any]?) {
    let options = [kCGImageSourceShouldCache as String: kCFBooleanFalse]
    if let imgSrc = CGImageSourceCreateWithData(data, options as CFDictionary) {
        let metadata = CGImageSourceCopyPropertiesAtIndex(imgSrc, 0, options as CFDictionary) as! [String: Any]
        //print(metadata)
        // let image = UIImage(cgImage: imgSrc as! CGImage)
        let image = UIImage(data: data as Data)
        return (image, metadata)
    }
    return (nil, nil)
}

unten finden Sie den Code, um Daten von PHAseet zu erhalten

func getImageAndMeta(asset: PHAsset){
    let options = PHImageRequestOptions()
    options.isSynchronous = true
    options.resizeMode = .none
    options.isNetworkAccessAllowed = false
    options.version = .current
    var image: UIImage? = nil
    var meta:[String:Any]? = nil
    _ = PHCachingImageManager().requestImageData(for: asset, options: options) { (imageData, dataUTI, orientation, info) in
        if let data = imageData {
            (image, meta) = metadataFromImageData(data: data as NSData)
            //image = UIImage(data: data)
        }
    }
    // here to return image and meta
}
4
lbsweek

Sie können das PHAsset (z. B. Hinzufügen von Standortmetadaten) mithilfe von Photos Framework und der UIImagePickerControllerDelegate-Methode ändern. Kein Aufwand aus Bibliotheken von Drittanbietern, keine doppelten Fotos erstellt. Funktioniert für iOS 8.0 und höher

Rufen Sie in der Delegatenmethode didFinishPickingMediaWithInfo UIImageWriteToSavedPhotosAlbum auf, um das Bild zuerst zu speichern. Dadurch wird auch das PHAsset erstellt, dessen EXIF-GPS-Daten wir ändern werden:

func imagePickerController(picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : AnyObject]) {

    if let myImage = info[UIImagePickerControllerOriginalImage] as? UIImage  {

        UIImageWriteToSavedPhotosAlbum(myImage, self, Selector("image:didFinishSavingWithError:contextInfo:"), nil)
    }    
}

Die Beendigungsauswahlfunktion wird ausgeführt, nachdem das Speichern abgeschlossen ist oder mit einem Fehler fehlschlägt. Rufen Sie im Callback das neu erstellte PHAsset ab. Erstellen Sie anschließend ein PHAssetChangeRequest, um die Standortmetadaten zu ändern.

func image(image: UIImage, didFinishSavingWithError: NSErrorPointer, contextInfo:UnsafePointer<Void>)       {

    if (didFinishSavingWithError != nil) {
        print("Error saving photo: \(didFinishSavingWithError)")
    } else {
        print("Successfully saved photo, will make request to update asset metadata")

        // fetch the most recent image asset:
        let fetchOptions = PHFetchOptions()
        fetchOptions.sortDescriptors = [NSSortDescriptor(key: "creationDate", ascending: true)]
        let fetchResult = PHAsset.fetchAssetsWithMediaType(PHAssetMediaType.Image, options: fetchOptions)

        // get the asset we want to modify from results:
        let lastImageAsset = fetchResult.lastObject as! PHAsset

        // create CLLocation from lat/long coords:
        // (could fetch from LocationManager if needed)
        let coordinate = CLLocationCoordinate2DMake(myLatitude, myLongitude)
        let nowDate = NSDate()
        // I add some defaults for time/altitude/accuracies:
        let myLocation = CLLocation(coordinate: coordinate, altitude: 0.0, horizontalAccuracy: 1.0, verticalAccuracy: 1.0, timestamp: nowDate)

        // make change request:
        PHPhotoLibrary.sharedPhotoLibrary().performChanges({

            // modify existing asset:
            let assetChangeRequest = PHAssetChangeRequest(forAsset: lastImageAsset)
            assetChangeRequest.location = myLocation

            }, completionHandler: {
                (success:Bool, error:NSError?) -> Void in

                if (success) {
                    print("Succesfully saved metadata to asset")
                    print("location metadata = \(myLocation)")
                } else {
                    print("Failed to save metadata to asset with error: \(error!)")
}
0
kev8484