Problem z ustawianiem danych exif dla obrazu

[2]} używam nowego frameworka ImageIO w iOS 4.1. Z powodzeniem odzyskuję metadane exif używając następującego:

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

Odczytując to, wydaje się ważne. Zapisywanie obrazu działa, ale nigdy nie ma żadnych danych exif w obrazie.

    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);
Author: akaru, 2011-02-26

7 answers

W poniższym wpisie na blogu otrzymałem odpowiedź, gdy miałem problemy z modyfikacją i zapisywaniem danych Exif. To działa na iOS.

Oto Mój kod testowy do zapisu danych Exif i GPS. To prawie kopiowanie i wklejanie kodu z powyższego bloga. Używam tego do zapisu danych exif do przechwyconego obrazu.

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);
 78
Author: Steve McFarlin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-07-14 16:39:56

Próbowałem odpowiedzi Steve ' a i działa, ale myślę, że jest wolny dla dużych obrazów, ponieważ duplikuje cały obraz.

Właściwości można ustawić bezpośrednio w Cmsamplebufferze za pomocą CMSetAttachments. Po prostu zrób to przed wywołaniem 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);

Oraz metodę getGPSDictionaryForLocation: można znaleźć tutaj:

Zapisywanie informacji Geotag ze zdjęciem na iOS4.1

 14
Author: Morty,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:10:35

Stworzyłem kategorię MSMutableDictionary, aby pomóc zapisać geotag i inne metadane do obrazu. Sprawdź mój wpis na blogu tutaj:

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

 3
Author: Gustavo Ambrozio,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-05-14 22:44:21

Fragment polega na stworzeniu słownika metadanych GPS dla danych EXIF. Oto Kategoria CLLocation, aby to zrobić:

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

 1
Author: Philip,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-07-20 01:45:02

Ponieważ wciąż się uczę, Zajęło mi trochę czasu, aby poskładać odpowiedzi Steve 'a i Marty' ego, aby dodać metadane do danych obrazu, a następnie zapisać go. Poniżej wykonałem szybką implementację ich odpowiedzi, która nie wykorzystuje metod ImageIO.

Biorąc pod uwagę bufor próbki CMSampleBuffer buffer, Niektóre CLLocation location i używając sugestii Morty ' ego aby uniknąć powielania obrazu, możemy wykonać następujące czynności. Metoda gpsMetadata rozszerzająca Pblokację można znaleźć tutaj (również Swift).

if let location = location {
    // Get the existing metadata dictionary (if there is one)
    var metaDict = CMCopyDictionaryOfAttachments(nil, buffer, kCMAttachmentMode_ShouldPropagate) as? Dictionary<String, Any> ?? [:]

    // Append the GPS metadata to the existing metadata
    metaDict[kCGImagePropertyGPSDictionary as String] = location.gpsMetadata()

    // Save the new metadata back to the buffer without duplicating any data
    CMSetAttachments(buffer, metaDict as CFDictionary, kCMAttachmentMode_ShouldPropagate)
}

// Get JPG image Data from the buffer
guard let imageData = AVCaptureStillImageOutput.jpegStillImageNSDataRepresentation(buffer) else {
    // There was a problem; handle it here
}
W tym momencie możesz zapisać dane obrazu do pliku lub użyć interfejsu API zdjęcia, aby zapisać obraz w rolce aparatu (używając PHAssetCreationRequest's addResource:with:data:options dla systemu iOS 9+ lub starszych wersji systemu iOS, zapisując dane imageData do pliku tymczasowego, a następnie wywołując PHAssetChangeRequest.creationRequestForAssetFromImage:atFileURL). Zauważ, że ALAssertLibrary jest przestarzały dla iOS 9. Więcej szczegółów implementacji podaję w odpowiedzi tutaj .
 1
Author: Undrea,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:26:12

[[3]] [Powrót do tej odpowiedzi z powodu downvotes bez wyjaśnień.]

Apple zaktualizowało swój artykuł dotyczący tego problemu (techniczne Q&A QA1622). Jeśli używasz starszej wersji Xcode, może nadal mieć artykuł, który mówi, mniej więcej, pech, nie można tego zrobić bez niskopoziomowego parsowania danych obrazu.

Zaktualizowana wersja jest tutaj:

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

Zaadaptowałem kod tam jak następuje:

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

I nazywam to z

imagePickerController:didFinishPickingMediaWithInfo:

Która jest metodą delegowania dla selektora obrazów. (Tam umieszczam logikę, aby sprawdzić, czy jest obraz do zapisania itp.)

Dla kompletności, oto metoda pomocnicza, aby uzyskać dane GPS jako słownik:

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

}

Zobacz także zapisywanie interfejsu użytkownika wraz z metadanymi (EXIF, GPS, TIFF) w bibliotece zdjęć iPhone ' a

 0
Author: Code Roadie,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 10:31:25

W ten sposób ustawiłem dane EXIF, również możesz skompresować zdjęcie w razie potrzeby, to rozwiązało problem dla mnie: Mam nadzieję, że pomoże

// Get your image.
NSURL *url = @"http://somewebsite.com/path/to/some/image.jpg";
UIImage *loImgPhoto = [NSData dataWithContentsOfURL:url];

// Get your metadata (includes the EXIF data).
CGImageSourceRef loImageOriginalSource = CGImageSourceCreateWithData(( CFDataRef) loDataFotoOriginal, NULL);
NSDictionary *loDicMetadata = (__bridge NSDictionary *) CGImageSourceCopyPropertiesAtIndex(loImageOriginalSource, 0, NULL);

// Set your compression quality (0.0 to 1.0).
NSMutableDictionary *loDicMutableMetadata = [loDicMetadata mutableCopy];
[loDicMutableMetadata setObject:@(lfCompressionQualityValue) forKey:(__bridge NSString *)kCGImageDestinationLossyCompressionQuality];

// Create an image destination.
NSMutableData *loNewImageDataWithExif = [NSMutableData data];
CGImageDestinationRef loImgDestination = CGImageDestinationCreateWithData((__bridge CFMutableDataRef)loNewImageDataWithExif, CGImageSourceGetType(loImageOriginalSource), 1, NULL);


// Add your image to the destination.
CGImageDestinationAddImage(loImgDestination, loImgPhoto.CGImage, (__bridge CFDictionaryRef) loDicMutableMetadata);

// Finalize the destination.
if (CGImageDestinationFinalize(loImgDestination))
   {
       NSLog(@"Successful image creation.");                   
       // process the image rendering, adjustment data creation and finalize the asset edit.


       //Upload photo with EXIF metadata
       [self myUploadMethod:loNewImageDataWithExif];

    }
    else
    {
          NSLog(@"Error -> failed to finalize the image.");                         
    }

CFRelease(loImageOriginalSource);
CFRelease(loImgDestination);
 0
Author: CGR,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-11-27 23:40:40