2014-09-07 11 views
8

Sto modificando le foto tramite PhotoKit ma ho scoperto che questo non preserva i metadati della foto originale. Ciò si verifica anche con SamplePhotosApp fornito da Apple quando applicano i filtri Seppia o Chrome. La mia domanda è, come si fa a garantire che tutti i metadati della foto originale vengano conservati?Come conservare i metadati delle foto originali durante la modifica di PHAssets?

Ho scoperto come è possibile ottenere i metadati dell'immagine originale e sono riuscito a salvare i metadati nell'ultimo CIImage che ho creato, ma viene comunque rimosso quando viene eseguita la modifica. Ci deve essere un problema nel modo in cui converto lo CIImage in uno CGImage in uno UIImage in NSData, o come lo sto scrivendo su disco.

asset.requestContentEditingInputWithOptions(options) { (input: PHContentEditingInput!, _) -> Void in 
    //Get full image 
    let url = input.fullSizeImageURL 
    let orientation = self.input.fullSizeImageOrientation 
    var inputImage = CIImage(contentsOfURL: url) 
    inputImage = inputImage.imageByApplyingOrientation(orientation) 

    //do some processing on original photo here and create a CGImage... 

    //save the original photo's metadata to a new CIImage: 
    let originalMetadata = inputImage.properties() 
    let newImage = CIImage(CGImage: editedCGImage, options: [kCIImageProperties: originalMetadata]) 

    println(newImage.properties()) //correctly prints all metadata! 

    //commit changes to disk - somewhere after this line the metadata is lost 
    let eaglContext = EAGLContext(API: .OpenGLES2) 
    let ciContext = CIContext(EAGLContext: eaglContext) 
    let outputImageRef = ciContext.createCGImage(newImage, fromRect: newImage.extent()) 
    let uiImage = UIImage(CGImage: outputImageRef, scale: 1.0, orientation: UIImageOrientation.Up) 
    let jpegNSData = UIImageJPEGRepresentation(uiImage, 0.75) 

    let contentEditingOutput = PHContentEditingOutput(contentEditingInput: input) 
    let success = jpegData.writeToURL(contentEditingOutput.renderedContentURL, options: NSDataWritingOptions.AtomicWrite, error: _) 

    PHPhotoLibrary.sharedPhotoLibrary().performChanges({() -> Void in 
     let request = PHAssetChangeRequest(forAsset: asset) 
     request.contentEditingOutput = contentEditingOutput 
    }, completionHandler: { (success: Bool, error: NSError!) -> Void in 
     if success == false { println('failed to commit image edit: \(error)') } 
    }) 
}) 

originale - notare la scheda GPS:
Original photo's metadata

Dopo aver modificato la foto:
enter image description here

risposta

4

Secondo il documento di Apple per i CIImage.properties:

Se l'oggetto CIImage è l'output di un filtro (o una catena di filtri), thi Il metodo s restituisce i metadati dall'immagine di input originale del filtro.

Con ciò detto, questo è come lo farei se la documentazione non è corretta.

È necessario salvare le proprietà da soli. Per fare questo è necessario leggere il contenuto della URL, NSData

NSURL *url = @"http://somewebsite.com/path/to/some/image.jpg"; 
NSData *imageData = [NSData dataWithContentsOfURL:url]; 
CIImage *image = [CIImage imageWithData:imageData]; 

// Save off the properties 
CGImageSourceRef imageSource = CGImageSourceCreateWithData((__bridge CFDataRef) imageData); 
NSDictionary* metadata = (__bridge NSDictionary *) CGImageSourceCopyProperties(imageSource, NULL); 

// process your image 
// ... 
NSData *outputData = // create data from altered image. 

// Save the image 
CGImageSourceRef outputImageSource = CGImageSourceCreateWithData((__bridge CFDataRef) outputData); 
CFMutableDataRef jpegData = CFDataCreateMutable(NULL, 0); 
CGImageDestinationRef outputDestination = CGImageDestinationCreateWithData(jpegData, CGImageSourceGetType(outputImageSource), 1, NULL); 

// add the image data to the destination 
CGImageDestinationAddImageFromSource(jpegData, outputImageSource, 0, (__bridge CFDictionaryRef) metadata); 

if (CGImageDestinationFinalize(outputDestination)) { 
    NSLog(@"Successful image creation."); 
    // process the image rendering, adjustment data creation and finalize the asset edit. 
} else { 
    NSLog(@"Image creation failed."); 
} 

Se non è necessario modificare i metadati, non c'è bisogno di lanciare il CFDataRef a un NSDictionary *, ma l'ho fatto qui solo per completezza.

+0

Questo è probabilmente un bug, come hai sottolineato, 'properties' dovrebbe sempre restituire i metadati dall'immagine di input ** originale **. Sto lavorando per convertire il tuo codice in Swift, sembra promettente. – Joey

+0

Sei riuscito a portarlo su Swift? –

+0

Hm, sto trovando l'unico oggetto in 'metadata' è Filesize = #. – Joey

4

Mentre ciò era possibile fare in Objective-C come mostrato da Bradley, non funzionava correttamente in Swift e causava il fallimento del blocco performChanges (vedere il mio other question). Questo problema è stato risolto nelle ultime versioni: Swift 1.2 in Xcode 6.3. Per conservare i metadati, è questo semplice:

let metadata = CGImageSourceCopyPropertiesAtIndex(imageSource, 0, nil) as NSDictionary 
CGImageDestinationAddImage(destination, cgImage, metadata) 

O per Objective-C:

NSMutableDictionary *imageMetadata = [(NSDictionary *) CFBridgingRelease(CGImageSourceCopyPropertiesAtIndex(imageSource, 0, NULL)) mutableCopy]; 
CGImageDestinationAddImageFromSource(destination, imageSource, 0, (__bridge CFDictionaryRef)(imageMetadata)); 

EDIT: Anche se questo funziona nel simulatore iOS, non ha funzionato su un dispositivo reale che esegue lo Swift codice.

Problemi correlati