2016-06-30 27 views
6

Sto provando a salvare ALCUNI metadati da un buffer di esempio dell'immagine insieme all'immagine.Swift: la fotocamera personalizzata salva i metadati modificati con l'immagine

ho bisogno di:

  • ruotare l'immagine per l'orientamento dai metadati
  • Rimuovere l'orientamento dai metadati
  • Salva la data di presa per i metadati
  • Salva l'immagine con i metadati nella directory dei documenti

Ho provato a creare un UIImmagine dai dati, ma quello si spegne i metadati. Ho provato a utilizzare un CIImage dai dati, che mantiene i metadati, ma non posso ruotarlo e quindi salvarlo in un file.

private func snapPhoto(success: (UIImage, CFMutableDictionary) -> Void, errorMessage: String -> Void) { 
    guard !self.stillImageOutput.capturingStillImage, 
     let videoConnection = stillImageOutput.connectionWithMediaType(AVMediaTypeVideo) else { return } 

    videoConnection.fixVideoOrientation() 

    stillImageOutput.captureStillImageAsynchronouslyFromConnection(videoConnection) { 
     (imageDataSampleBuffer, error) -> Void in 
     guard imageDataSampleBuffer != nil && error == nil else { 
      errorMessage("Couldn't snap photo") 
      return 
     } 

     let data = AVCaptureStillImageOutput.jpegStillImageNSDataRepresentation(imageDataSampleBuffer) 

     let metadata = CMCopyDictionaryOfAttachments(nil, imageDataSampleBuffer, CMAttachmentMode(kCMAttachmentMode_ShouldPropagate)) 
     let metadataMutable = CFDictionaryCreateMutableCopy(nil, 0, metadata) 

     let utcDate = "\(NSDate())" 
     let cfUTCDate = CFStringCreateCopy(nil, utcDate) 
     CFDictionarySetValue(metadataMutable!, unsafeAddressOf(kCGImagePropertyGPSDateStamp), unsafeAddressOf(cfUTCDate)) 

     guard let image = UIImage(data: data)?.fixOrientation() else { return } 
     CFDictionarySetValue(metadataMutable, unsafeAddressOf(kCGImagePropertyOrientation), unsafeAddressOf(1)) 

     success(image, metadataMutable) 
    } 
} 

Ecco il mio codice per il salvataggio dell'immagine.

func saveImageAsJpg(image: UIImage, metadata: CFMutableDictionary) { 
    // Add metadata to image 
    guard let jpgData = UIImageJPEGRepresentation(image, 1) else { return } 
    jpgData.writeToFile("\(self.documentsDirectory)/image1.jpg", atomically: true) 
} 

risposta

7

Ho finito per capire come far funzionare tutto nel modo in cui ne avevo bisogno. La cosa che mi ha aiutato di più è stata scoprire che un CFDictionary può essere lanciato come NSMutableDictionary.

Ecco il mio codice finale:

Come potete vedere aggiungo una proprietà al dizionario EXIF ​​per la data digitalizzato, e ha cambiato il valore di orientamento.

private func snapPhoto(success: (UIImage, NSMutableDictionary) -> Void, errorMessage: String -> Void) { 
    guard !self.stillImageOutput.capturingStillImage, 
     let videoConnection = stillImageOutput.connectionWithMediaType(AVMediaTypeVideo) else { return } 

    videoConnection.fixVideoOrientation() 

    stillImageOutput.captureStillImageAsynchronouslyFromConnection(videoConnection) { 
     (imageDataSampleBuffer, error) -> Void in 
     guard imageDataSampleBuffer != nil && error == nil else { 
      errorMessage("Couldn't snap photo") 
      return 
     } 

     let data = AVCaptureStillImageOutput.jpegStillImageNSDataRepresentation(imageDataSampleBuffer) 

     let rawMetadata = CMCopyDictionaryOfAttachments(nil, imageDataSampleBuffer, CMAttachmentMode(kCMAttachmentMode_ShouldPropagate)) 
     let metadata = CFDictionaryCreateMutableCopy(nil, 0, rawMetadata) as NSMutableDictionary 

     let exifData = metadata.valueForKey(kCGImagePropertyExifDictionary as String) as? NSMutableDictionary 
     exifData?.setValue(NSDate().toString("yyyy:MM:dd HH:mm:ss"), forKey: kCGImagePropertyExifDateTimeDigitized as String) 

     metadata.setValue(exifData, forKey: kCGImagePropertyExifDictionary as String) 
     metadata.setValue(1, forKey: kCGImagePropertyOrientation as String) 

     guard let image = UIImage(data: data)?.fixOrientation() else { 
      errorMessage("Couldn't create image") 
      return 
     } 

     success(image, metadata) 
    } 
} 

E il mio codice finale per salvare l'immagine con i metadati:

sacco di dichiarazioni di guardia, che io odio, ma è meglio che la forza scartare.

func saveImage(withMetadata image: UIImage, metadata: NSMutableDictionary) { 
    let filePath = "\(self.documentsPath)/image1.jpg" 

    guard let jpgData = UIImageJPEGRepresentation(image, 1) else { return } 

    // Add metadata to jpgData 
    guard let source = CGImageSourceCreateWithData(jpgData, nil), 
     let uniformTypeIdentifier = CGImageSourceGetType(source) else { return } 
    let finalData = NSMutableData(data: jpgData) 
    guard let destination = CGImageDestinationCreateWithData(finalData, uniformTypeIdentifier, 1, nil) else { return } 
    CGImageDestinationAddImageFromSource(destination, source, 0, metadata) 
    guard CGImageDestinationFinalize(destination) else { return } 

    // Save image that now has metadata 
    self.fileService.save(filePath, data: finalData) 
} 

Ecco il mio aggiornato save metodo (non esattamente lo stesso che stavo usando quando ho scritto questa domanda, dal momento che ho aggiornato a Swift 2.3, ma il concetto è lo stesso):

public func save(fileAt path: NSURL, with data: NSData) throws -> Bool { 
    guard let pathString = path.absoluteString else { return false } 
    let directory = (pathString as NSString).stringByDeletingLastPathComponent 

    if !self.fileManager.fileExistsAtPath(directory) { 
     try self.makeDirectory(at: NSURL(string: directory)!) 
    } 

    if self.fileManager.fileExistsAtPath(pathString) { 
     try self.delete(fileAt: path) 
    } 

    return self.fileManager.createFileAtPath(pathString, contents: data, attributes: [NSFileProtectionKey: NSFileProtectionComplete]) 
} 
+0

Sto assumendo che posso ottenere '' filePath' da NSSearchPathForDirectoriesInDomains (.DocumentDirectory, .UserDomainMask, vero) .Prima! + "nome.png" '. Ma cos'è 'fileService'? 'FileService' è una classe personalizzata? –

+1

@ Carlos.V 'fileService' è una classe personalizzata che ho creato per avvolgere il' NSFileManager' e semplificarmi la vita. – SeanRobinson159

+0

Sono in grado di leggere i metadati effettivi e modificarli, ma non posso scrivere la nuova meta nel file. Puoi spiegare un po 'come funziona la funzione '.save()', ho provato a usare 'fileMan.createFileAtPath (filePath, contenuto: finalData, attributi: metadata come! [String: AnyObject])' ma non salva il metadati, è per altri attributi. Forse stai usando 'PHPhotoLibrary'? Sto pensando di provare in questo modo, ma non sono sicuro che funzionerebbe al 100%. –

0

Ho fatto una versione notevolmente semplificata del codice sopra. Crea un file immagine, ma come ha notato Carlos, non ci sono metadati personalizzati nel file quando lo si carica di nuovo. Secondo altri thread, questo semplicemente potrebbe non essere possibile.

func saveImage(_ image: UIImage, withMetadata metadata: NSMutableDictionary, atPath path: URL) -> Bool { 
    guard let jpgData = UIImageJPEGRepresentation(image, 1) else { 
     return false 
    } 
    // make an image source 
    guard let source = CGImageSourceCreateWithData(jpgData as CFData, nil), let uniformTypeIdentifier = CGImageSourceGetType(source) else { 
     return false 
    } 
    // make an image destination pointing to the file we want to write 
    guard let destination = CGImageDestinationCreateWithURL(path as CFURL, uniformTypeIdentifier, 1, nil) else { 
     return false 
    } 

    // add the source image to the destination, along with the metadata 
    CGImageDestinationAddImageFromSource(destination, source, 0, metadata) 

    // and write it out 
    return CGImageDestinationFinalize(destination) 
} 
+0

Funziona per me. Sto prendendo il file prodotto, caricandolo su un server e ha i nuovi metadati. Quindi è sicuramente possibile. – SeanRobinson159

+0

Ciao, come aggiungo i metadati a questa funzione? Inoltre, se voglio salvare le foto nella libreria delle foto, come farei per farlo? –

+0

Crea il dizionario e passalo. Assicurati che i tasti del dizionario siano quelli supportati da Apple, altrimenti sembreranno essere salvati, ma non verranno salvati nel file vero e proprio. –