【问题标题】:Swift how to modify exif info in images taken from mobile cameraSwift如何修改从移动相机拍摄的图像中的exif信息
【发布时间】:2016-10-25 20:18:44
【问题描述】:

我使用UIImagePickerController 在我的 iOS 应用程序中挑选图像,我知道 exif 信息可以通过info[UIImagePickerControllerMediaMetadata] 获得。但是当我通过UIImage 将我的图片上传到我的服务器时,大部分exif 信息都被删除了。我想知道我是否可以在 Http 请求中将 exif 信息添加到我的图像中(之后图像上传为 jpg)。如果没有,我应该如何解决这个问题?我想更改 Make、Model 属性(也就是说,这张照片是用什么设备拍摄的)

下面是我的代码sn-ps:

func Tapped() {
    let myPickerController = UIImagePickerController()

    myPickerController.delegate = self
    myPickerController.sourceType = UIImagePickerControllerSourceType.Camera
    myPickerController.allowsEditing = false
    self.presentViewController(myPickerController, animated: true, completion: nil)
}
func imagePickerController(picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : AnyObject]) {
    let image = info[UIImagePickerControllerOriginalImage] as? UIImage
    myImageView.image = image
    UIImageWriteToSavedPhotosAlbum(image!, self, #selector(ViewController.image(_:didFinishSavingWithError:contextInfo:)), nil)
    self.dismissViewControllerAnimated(true, completion: nil)
}

func myImageUploadRequest()
{

    let myUrl = NSURL(string: "http://XXXXXX/Uploadfile")

    let request = NSMutableURLRequest(URL:myUrl!)
    request.HTTPMethod = "POST"

    let param = [
        "userId"    : "7"
    ]

    let boundary = generateBoundaryString()

    request.setValue("multipart/form-data; boundary=\(boundary)", forHTTPHeaderField: "Content-Type")


    let imageData = UIImageJPEGRepresentation(myImageView.image!, 1)

    if(imageData == nil)  { return; }

    request.HTTPBody = createBodyWithParameters(param, filePathKey: "file", imageDataKey: imageData!, boundary: boundary)



    myActivityIndicator.startAnimating()

    let task = NSURLSession.sharedSession().dataTaskWithRequest(request) {
        data, response, error in

        if error != nil {
            print("error=\(error)")
            return
        }

        // You can print out response object
        print("******* response = \(response)")

        // Print out response body
        let responseString = NSString(data: data!, encoding: NSUTF8StringEncoding)
        print("****** response data = \(responseString!)")

        do{
            let json = try NSJSONSerialization.JSONObjectWithData(data!, options: .MutableContainers) as? NSDictionary

        }catch{
            print(error)
        }


        dispatch_async(dispatch_get_main_queue(),{
            self.myActivityIndicator.stopAnimating()
            self.myImageView.image = nil
        })

    }

    task.resume()
}

func createBodyWithParameters(parameters: [String: String]?, filePathKey: String?, imageDataKey: NSData, boundary: String) -> NSData {
    let body = NSMutableData();

    if parameters != nil {
        for (key, value) in parameters! {
            body.appendString("--\(boundary)\r\n")
            body.appendString("Content-Disposition: form-data; name=\"\(key)\"\r\n\r\n")
            body.appendString("\(value)\r\n")
        }
    }

    let filename = "test.jpg"

    let mimetype = "image/jpg"

    body.appendString("--\(boundary)\r\n")
    body.appendString("Content-Disposition: form-data; name=\"\(filePathKey!)\"; filename=\"\(filename)\"\r\n")
    body.appendString("Content-Type: \(mimetype)\r\n\r\n")
    body.appendData(imageDataKey)
    body.appendString("\r\n")



    body.appendString("--\(boundary)--\r\n")

    return body
}

func generateBoundaryString() -> String {
    return "Boundary-\(NSUUID().UUIDString)"
}

extension NSMutableData {
    func appendString(string: String) {
        let data = string.dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: true)
        appendData(data!)
    }
}

【问题讨论】:

  • 这方面有什么更新吗?我也找不到这样做的简单方法。

标签: ios swift image metadata exif


【解决方案1】:

已接受答案的 Swift 5 版本 -

func saveImageWithImageData(data: NSData, properties: NSDictionary, completion: (_ data: NSData, _ path: NSURL) -> Void) {

    let imageRef: CGImageSource = CGImageSourceCreateWithData((data as CFData), nil)!
    let uti: CFString = CGImageSourceGetType(imageRef)!
    let dataWithEXIF: NSMutableData = NSMutableData(data: data as Data)
    let destination: CGImageDestination = CGImageDestinationCreateWithData((dataWithEXIF as CFMutableData), uti, 1, nil)!

    CGImageDestinationAddImageFromSource(destination, imageRef, 0, (properties as CFDictionary))
    CGImageDestinationFinalize(destination)

    let paths: [String] = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)
    let savePath: String = paths[0].appending("exif.jpg")

    let manager: FileManager = FileManager.default
    manager.createFile(atPath: savePath, contents: dataWithEXIF as Data, attributes: nil)

    completion(dataWithEXIF,NSURL(string: savePath)!)

    print("image with EXIF info converting to NSData: Done! Ready to upload! ")

}

【讨论】:

    【解决方案2】:

    使用和合并其他帖子中的一些信息,我在 Swift 中使用 Dictionary 解决了问题。我在 AVCapturePhoto 的 AVFounfation 回调的 captureOutput 中使用了它:

    func photoOutput(_ output: AVCapturePhotoOutput,
                     didFinishProcessingPhoto photo: AVCapturePhoto,
                     error: Error?) {
    
        //retrieve exif information
        var photoFormatDescription: CMFormatDescription?
        CMVideoFormatDescriptionCreateForImageBuffer(kCFAllocatorDefault, photoPixelBuffer, &photoFormatDescription)
    
        var metadataAttachments: Dictionary = photo.metadata as Dictionary
    
        if var exifData = metadataAttachments["{Exif}"] as? [String: Any] {
            exifData[kCGImagePropertyExifUserComment as String] = "<whatever you want to write>"
    
        metadataAttachments[kCGImagePropertyExifDictionary as String] = exifData
        }
    
    }
    

    之后“metadataAttachments”用于构建最终图像(在我的情况下使用 CGImageDestinationAddImage)

    它似乎有效(在使用 Swift 4.0 构建的项目中尝试过)

    希望对你有帮助!

    【讨论】:

    • 如何使用 metadataAttachments 构建最终镜像?
    【解决方案3】:

    SWIFT 3

    如果您正在捕获视频并获取 CMSampleBuffer,则有一种方法可以更新 EXIF 元数据。在 iOS9 中,我没有得到 DateTimeOriginal,但在 iOS10 中,DataTimeOriginal 已经存在。因此我不得不添加一些额外的键值对。

    self.stillCameraOutput.captureStillImageAsynchronously(from: connectionVideo) { (sampleBuffer, err) in
            if let err = err {
                blockCompletion(nil, err as NSError?)
            }
            else {
                if let sampleBuffer = sampleBuffer {
                    let rawMetadata = CMCopyDictionaryOfAttachments(nil, sampleBuffer, CMAttachmentMode(kCMAttachmentMode_ShouldPropagate))
                    let metadata = CFDictionaryCreateMutableCopy(nil, 0, rawMetadata) as NSMutableDictionary
    
                    let exifData = metadata.value(forKey: "{Exif}") as? NSMutableDictionary
    
                    print("EXIF DATA: \(exifData)")
    
                    if let dateTime = exifData?["DateTimeOriginal"] as? String {
                        print("DateTime exists \(dateTime)")
                    }
                    else {
                        exifData?.setValue(Date().exifDate(), forKey: "DateTimeOriginal")
                    }
    
                    if let dateTime = exifData?["DateTimeDigitized"] as? String {
                        print("DateTime exists \(dateTime)")
                    }
                    else {
                        exifData?.setValue(Date().exifDate(), forKey: "DateTimeDigitized")
                    }
    
                    metadata.setValue(exifData, forKey: "{Exif}")
    
                    CMSetAttachments(sampleBuffer, metadata as CFDictionary, CMAttachmentMode(kCMAttachmentMode_ShouldPropagate))
    
                    let rawMetadata2 = CMCopyDictionaryOfAttachments(nil, sampleBuffer, CMAttachmentMode(kCMAttachmentMode_ShouldPropagate))
                    let metadata2 = CFDictionaryCreateMutableCopy(nil, 0, rawMetadata2) as NSMutableDictionary
    
                    let exifData2 = metadata2.value(forKey: "{Exif}") as? NSMutableDictionary
    
                    print("EXIF DATA: \(exifData2)")
    
                    if let dataImage = AVCaptureStillImageOutput.jpegStillImageNSDataRepresentation(sampleBuffer) {
                        blockCompletion(dataImage, nil)
                    }
                    else {
                        blockCompletion(nil, nil)
                    }
                }
                else {
                    blockCompletion(nil, nil)
                }
            }
        }
    

    【讨论】:

    【解决方案4】:

    是的!最后我做了一个修改EXIF信息的技巧。首先,您可以从 info[UIImagePickerControllerMediaMetadata] 和 NSData 中获取 EXIF 信息,而无需从 UIImageJPEGRepresentation 选择的 UIImage 中获取 EXIF。然后,您可以使用修改后的 EXIF 信息创建一个新的 NSDictionary。之后,在下面调用我的函数,就可以得到修改EXIF的图片NSData了!

    func saveImageWithImageData(data: NSData, properties: NSDictionary, completion: (data: NSData, path: NSURL) -> Void) {
    
        let imageRef: CGImageSourceRef = CGImageSourceCreateWithData((data as CFDataRef), nil)!
        let uti: CFString = CGImageSourceGetType(imageRef)!
        let dataWithEXIF: NSMutableData = NSMutableData(data: data)
        let destination: CGImageDestinationRef = CGImageDestinationCreateWithData((dataWithEXIF as CFMutableDataRef), uti, 1, nil)!
    
        CGImageDestinationAddImageFromSource(destination, imageRef, 0, (properties as CFDictionaryRef))
        CGImageDestinationFinalize(destination)
    
        var paths: [AnyObject] = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true)
        let savePath: String = paths[0].stringByAppendingPathComponent("exif.jpg")
    
        let manager: NSFileManager = NSFileManager.defaultManager()
        manager.createFileAtPath(savePath, contents: dataWithEXIF, attributes: nil)
    
        completion(data: dataWithEXIF,path: NSURL(string: savePath)!)
    
        print("image with EXIF info converting to NSData: Done! Ready to upload! ")
    
    }
    

    【讨论】:

    • 打印时,一切都完美无缺。但是在保存后检索图像时,新的元数据没有显示。请帮忙! if let imageSource = CGImageSourceCreateWithData(imageData as CFData, nil) { let imageProperties = CGImageSourceCopyPropertiesAtIndex(imageSource, 0, nil) if let dict = imageProperties as? [String: Any] { print(dict) } }
    • @DeviOS 我有同样的问题,我现在正试图缩小范围。我相信文件本身是在没有 EXIF 的情况下保存的,它会以某种方式在某处丢失
    • @ADProgress 你查到为什么保存图片后没有检索到元数据吗?
    • @MadhavThakker 是的。至少在我的代码中是什么。保存文件时,我将数据转换为 jpegData 并创建了一个没有 EXIF 信息的全新数据容器。但是数据已经是 jpegData,所以我只需要保存它而不是重新创建它。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-07-15
    • 1970-01-01
    • 1970-01-01
    • 2018-09-15
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多