Swift: пользовательская камера сохраняет измененные метаданные с изображением

Я пытаюсь сохранить НЕКОТОРЫЕ из метаданных из буфера образца изображения вместе с изображением.

Мне нужно:

  • Поверните изображение в сторону ориентации из метаданных
  • Удалить ориентацию из метаданных
  • Сохранить дату, указанную в метаданных
  • Сохраните это изображение с метаданными в каталоге документов

Я попытался создать UIImage из данных, но это удаляет метаданные. Я попытался использовать CIImage из данных, который хранит метаданные, но я не могу повернуть его, а затем сохранить его в файл.

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) } } 

Вот мой код для сохранения изображения.

 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) } 

Я в конечном итоге выяснил, как заставить все работать так, как мне было нужно. То, что помогло мне больше всего, заключалось в том, что CFDictionary можно отличить как NSMutableDictionary.

Вот мой последний код:

Как вы можете видеть, я добавляю свойство в словарь EXIF ​​для даты оцифровки и изменяю значение ориентации.

 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) } } 

И мой последний код для сохранения изображения с метаданными:

Множество охранных заявлений, которые я ненавижу, но это лучше, чем развертывание силы.

 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) } 

Вот мой обновленный метод save (не то же самое, что я использовал при написании этого вопроса, так как я обновил Swift 2.3, но концепция такая же):

 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]) } 

Я сделал значительно упрощенную версию вышеприведенного кода. Он делает файл изображения, но, как заметил Карлос, никакие пользовательские метаданные не находятся в файле при повторной загрузке. По другим темам это просто невозможно.

 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) } 
  • Разрешить пользователям фоновую музыку в swift 2.0
  • Неоднозначная ссылка на члена Swift 3
  • Swift3: добавить кнопку с кодом
  • CALayerInvalidGeometry crash на подслое iOS9 с не конечным положением "
  • Как создать сгруппированный TableView без использования TableViewController
  • Динамические дополнительные свойства в Swift 2.0
  • Использование инициализаторов объектов в Swift для замены AllocWithZone
  • Группа захвата регулярных выражений
  • Почему ViewForAnnotation не вызывается?
  • Правильный способ создания асинхронного запроса в Swift 2.0
  • UIButton в Swift 2.2. Как добавить другую цель?
  • Давайте будем гением компьютера.