55 votes

Problème de configuration des données exif pour une image

J'utilise le nouveau framework ImageIO dans iOS 4.1. J'ai réussi à récupérer les métadonnées exif en utilisant les éléments suivants:

 CFDictionaryRef metadataDict = CMGetAttachment(sampleBuffer, kCGImagePropertyExifDictionary , NULL);
 

En le lisant, il semble valide. L'enregistrement d'une image sur fonctionne, mais il n'y a jamais aucune donnée exif dans l'image.

     CGImageDestinationRef myImageDest = CGImageDestinationCreateWithURL((CFURLRef) docurl, kUTTypeJPEG, 1, NULL);

    // Add the image to the destination using previously saved options. 
    CGImageDestinationAddImage(myImageDest, iref, NULL);

    //add back exif
    NSDictionary *props = [NSDictionary dictionaryWithObjectsAndKeys:
                            [NSNumber numberWithFloat:.1], kCGImageDestinationLossyCompressionQuality,
                           metadataDict, kCGImagePropertyExifDictionary, //the exif metadata
                                                        nil];

                          //kCGImagePropertyExifAuxDictionary

    CGImageDestinationSetProperties(myImageDest, (CFDictionaryRef) props);

    // Finalize the image destination. 
    bool status = CGImageDestinationFinalize(myImageDest);
 

84voto

Steve McFarlin Points 2303

Le billet de blog suivant est l'endroit où j'ai obtenu ma réponse lorsque j'ai eu des problèmes avec la modification et la sauvegarde de cacao caféiné Exif Data. Cela fonctionne sur iOS.

Voici mon code de test pour l'écriture de données Exif et GPS. C'est à peu près un copier-coller du code du blog ci-dessus. J'utilise ceci pour écrire des données exif sur une image capturée.

 NSData *jpeg = [AVCaptureStillImageOutput jpegStillImageNSDataRepresentation:imageDataSampleBuffer] ;

CGImageSourceRef  source ;
    source = CGImageSourceCreateWithData((CFDataRef)jpeg, NULL);

    //get all the metadata in the image
    NSDictionary *metadata = (NSDictionary *) CGImageSourceCopyPropertiesAtIndex(source,0,NULL);

    //make the metadata dictionary mutable so we can add properties to it
    NSMutableDictionary *metadataAsMutable = [[metadata mutableCopy]autorelease];
    [metadata release];

    NSMutableDictionary *EXIFDictionary = [[[metadataAsMutable objectForKey:(NSString *)kCGImagePropertyExifDictionary]mutableCopy]autorelease];
    NSMutableDictionary *GPSDictionary = [[[metadataAsMutable objectForKey:(NSString *)kCGImagePropertyGPSDictionary]mutableCopy]autorelease];
    if(!EXIFDictionary) {
        //if the image does not have an EXIF dictionary (not all images do), then create one for us to use
        EXIFDictionary = [NSMutableDictionary dictionary];
    }
    if(!GPSDictionary) {
        GPSDictionary = [NSMutableDictionary dictionary];
    }

    //Setup GPS dict


    [GPSDictionary setValue:[NSNumber numberWithFloat:_lat] forKey:(NSString*)kCGImagePropertyGPSLatitude];
    [GPSDictionary setValue:[NSNumber numberWithFloat:_lon] forKey:(NSString*)kCGImagePropertyGPSLongitude];
    [GPSDictionary setValue:lat_ref forKey:(NSString*)kCGImagePropertyGPSLatitudeRef];
    [GPSDictionary setValue:lon_ref forKey:(NSString*)kCGImagePropertyGPSLongitudeRef];
    [GPSDictionary setValue:[NSNumber numberWithFloat:_alt] forKey:(NSString*)kCGImagePropertyGPSAltitude];
    [GPSDictionary setValue:[NSNumber numberWithShort:alt_ref] forKey:(NSString*)kCGImagePropertyGPSAltitudeRef]; 
    [GPSDictionary setValue:[NSNumber numberWithFloat:_heading] forKey:(NSString*)kCGImagePropertyGPSImgDirection];
    [GPSDictionary setValue:[NSString stringWithFormat:@"%c",_headingRef] forKey:(NSString*)kCGImagePropertyGPSImgDirectionRef];

    [EXIFDictionary setValue:xml forKey:(NSString *)kCGImagePropertyExifUserComment];
    //add our modified EXIF data back into the image's metadata
    [metadataAsMutable setObject:EXIFDictionary forKey:(NSString *)kCGImagePropertyExifDictionary];
    [metadataAsMutable setObject:GPSDictionary forKey:(NSString *)kCGImagePropertyGPSDictionary];

    CFStringRef UTI = CGImageSourceGetType(source); //this is the type of image (e.g., public.jpeg)

    //this will be the data CGImageDestinationRef will write into
    NSMutableData *dest_data = [NSMutableData data];

    CGImageDestinationRef destination = CGImageDestinationCreateWithData((CFMutableDataRef)dest_data,UTI,1,NULL);

    if(!destination) {
        NSLog(@"***Could not create image destination ***");
    }

    //add the image contained in the image source to the destination, overidding the old metadata with our modified metadata
    CGImageDestinationAddImageFromSource(destination,source,0, (CFDictionaryRef) metadataAsMutable);

    //tell the destination to write the image data and metadata into our data object.
    //It will return false if something goes wrong
    BOOL success = NO;
    success = CGImageDestinationFinalize(destination);

    if(!success) {
        NSLog(@"***Could not create data from image destination ***");
    }

    //now we have the data ready to go, so do whatever you want with it
    //here we just write it to disk at the same path we were passed
    [dest_data writeToFile:file atomically:YES];

    //cleanup

    CFRelease(destination);
    CFRelease(source);
 

14voto

CC L. Points 656

J'ai essayé de Steve réponse, cela fonctionne, mais je pense que c'est lent pour des images de grande taille parce que c'est la duplication de l'ensemble de l'image.

Vous pouvez définir les propriétés directement sur la CMSampleBuffer à l'aide de CMSetAttachments. Viens de le faire avant d'appeler jpegStillImageNSDataRepresentation

CFDictionaryRef metaDict = CMCopyDictionaryOfAttachments(NULL, imageSampleBuffer, kCMAttachmentMode_ShouldPropagate);
CFMutableDictionaryRef mutable = CFDictionaryCreateMutableCopy(NULL, 0, metaDict);

NSMutableDictionary * mutableGPS = [self getGPSDictionaryForLocation:self.myLocation];
CFDictionarySetValue(mutable, kCGImagePropertyGPSDictionary, mutableGPS);

// set the dictionary back to the buffer
CMSetAttachments(imageSampleBuffer, mutable, kCMAttachmentMode_ShouldPropagate);

Et la méthode getGPSDictionaryForLocation: peut être trouvé ici:

Enregistrement Geotag info avec photo sur iOS4.1

3voto

Gustavo Ambrozio Points 389

J'ai créé une catégorie MSMutableDictionary pour aider à enregistrer les balises géographiques et autres métadonnées dans une image. Découvrez mon blog ici:

http://blog.codecropper.com/2011/05/adding-metadata-to-ios-images-the-easy-way/

1voto

Philip Points 244

Un élément implique la création du dictionnaire de métadonnées GPS pour les données EXIF. Voici une catégorie de CLLocation pour faire exactement cela:

https://gist.github.com/phildow/6043486

0voto

Code Roadie Points 185

[Revisiter cette réponse parce que de downvotes sans explications.]

Apple a mis à jour son article sur cette question (Technique Q&A QA1622). Si vous utilisez une ancienne version de Xcode, vous pouvez encore avoir de l'article qui dit, plus ou moins, pas de chance, vous ne pouvez pas faire cela sans bas-niveau de l'analyse des données de l'image.

La version mise à jour est ici:

https://developer.apple.com/library/ios/#qa/qa1622/_index.html

J'ai adapté le code comme suit:

- (void) saveImage:(UIImage *)imageToSave withInfo:(NSDictionary *)info
{
    // Get the image metadata (EXIF & TIFF)
    NSMutableDictionary * imageMetadata = [[info objectForKey:UIImagePickerControllerMediaMetadata] mutableCopy];

    // add (fake) GPS data
    CLLocationCoordinate2D coordSF = CLLocationCoordinate2DMake(37.732711,-122.45224);

    // arbitrary altitude and accuracy
    double altitudeSF = 15.0;
    double accuracyHorizontal = 1.0;
    double accuracyVertical = 1.0;
    NSDate * nowDate = [NSDate date];
    // create CLLocation for image
    CLLocation * loc = [[CLLocation alloc] initWithCoordinate:coordSF altitude:altitudeSF horizontalAccuracy:accuracyHorizontal verticalAccuracy:accuracyVertical timestamp:nowDate];

    // this is in case we try to acquire actual location instead of faking it with the code right above
    if ( loc ) {
        [imageMetadata setObject:[self gpsDictionaryForLocation:loc] forKey:(NSString*)kCGImagePropertyGPSDictionary];
    }

    // Get the assets library
    ALAssetsLibrary *library = [[ALAssetsLibrary alloc] init];

    // create a completion block for when we process the image
   ALAssetsLibraryWriteImageCompletionBlock imageWriteCompletionBlock =
    ^(NSURL *newURL, NSError *error) {
        if (error) {
            NSLog( @"Error writing image with metadata to Photo Library: %@", error );
        } else {
            NSLog( @"Wrote image %@ with metadata %@ to Photo Library",newURL,imageMetadata);
        }
    };

    // Save the new image to the Camera Roll, using the completion block defined just above
    [library writeImageToSavedPhotosAlbum:[imageToSave CGImage]
                                 metadata:imageMetadata
                          completionBlock:imageWriteCompletionBlock];
}

et ce que j'appelle de

imagePickerController:didFinishPickingMediaWithInfo:

qui est le délégué de la méthode pour le sélecteur d'images. (C'est là où j'ai mis de la logique pour voir si il y a une image à enregistrer, etc.)

Pour être complet, voici la méthode d'assistance pour obtenir les données du GPS comme un dictionnaire:

- (NSDictionary *) gpsDictionaryForLocation:(CLLocation *)location
{
    CLLocationDegrees exifLatitude  = location.coordinate.latitude;
    CLLocationDegrees exifLongitude = location.coordinate.longitude;

    NSString * latRef;
    NSString * longRef;
    if (exifLatitude < 0.0) {
        exifLatitude = exifLatitude * -1.0f;
        latRef = @"S";
    } else {
        latRef = @"N";
    }

    if (exifLongitude < 0.0) {
        exifLongitude = exifLongitude * -1.0f;
        longRef = @"W";
    } else {
        longRef = @"E";
    }

    NSMutableDictionary *locDict = [[NSMutableDictionary alloc] init];

    // requires ImageIO
    [locDict setObject:location.timestamp forKey:(NSString*)kCGImagePropertyGPSTimeStamp];
    [locDict setObject:latRef forKey:(NSString*)kCGImagePropertyGPSLatitudeRef];
    [locDict setObject:[NSNumber numberWithFloat:exifLatitude] forKey:(NSString *)kCGImagePropertyGPSLatitude];
    [locDict setObject:longRef forKey:(NSString*)kCGImagePropertyGPSLongitudeRef];
    [locDict setObject:[NSNumber numberWithFloat:exifLongitude] forKey:(NSString *)kCGImagePropertyGPSLongitude];
    [locDict setObject:[NSNumber numberWithFloat:location.horizontalAccuracy] forKey:(NSString*)kCGImagePropertyGPSDOP];
    [locDict setObject:[NSNumber numberWithFloat:location.altitude] forKey:(NSString*)kCGImagePropertyGPSAltitude];

    return locDict;

}

Voir aussi Écrire UIImage, avec des métadonnées (EXIF, GPS, TIFF) dans la bibliothèque Photo de l'iPhone

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X