Swift how to modify exif info in images taken from mobile camera
Solution 1
Yes! Finally I made a trick to modify the EXIF info. At first, you can get EXIF info from info[UIImagePickerControllerMediaMetadata] and NSData without EXIF from picked UIImage by UIImageJPEGRepresentation. Then, you can create a new NSDictionary with modified EXIF info. After that, call my function in the following, you can get image NSData with modified EXIF!
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! ")
}
Solution 2
using and mergind some info from other posts, I approached problem using Dictionary in Swift. I used it in captureOutput of AVFounfation callback for AVCapturePhoto:
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
}
}
After that "metadataAttachments" is used to build final image (using CGImageDestinationAddImage in my case)
It seems to work (tried in a project build with Swift 4.0)
Hope it can help!
Solution 3
Swift 5 Version of the accepted answer -
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! ")
}
Related videos on Youtube
SuperBi
Updated on September 15, 2022Comments
-
SuperBi over 1 year
I use
UIImagePickerController
to pick images in my iOS App and I know exif info can be got byinfo[UIImagePickerControllerMediaMetadata]
. But when I upload my image to my server byUIImage
, most of exif info has been striped. I wonder whether I can add exif info to my image in Http request(image uploaded as jpg after that). If not, how should I solve this problem? I wanna change Make, Model attributes(in other words, what device was used to take this picture)Below are my code snippets:
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!) } }
-
jonmecer almost 8 yearsAny updates on this? I too can't find a simple way of doing this.
-
-
Maury Markowitz about 7 yearsI tried this method, but my rawMetadata is nil. Does this work on image data from AVCapturePhotoOutput?
-
Subhojit Mandal about 7 yearsstackoverflow.com/questions/43489623/… can you ans this also
-
hamedazhar over 5 yearsWhile printing, its all coming perfectly. But while retrieving the image after it is saved, the new metadata is not showing. Pls help!
if let imageSource = CGImageSourceCreateWithData(imageData as CFData, nil) { let imageProperties = CGImageSourceCopyPropertiesAtIndex(imageSource, 0, nil) if let dict = imageProperties as? [String: Any] { print(dict) } }
-
AD Progress about 3 years@DeviOS I have the same problem and I am trying to narrow it down now. I believe that the file itself is being saved without the EXIF itself it somehow gets lost somewhere
-
Madhav Thakker about 3 years@ADProgress did you track it down why the metadata is not being retrieved after saving the image?
-
AD Progress about 3 years@MadhavThakker Yes. What it was at least in my code. When saving the file I was converting the data to jpegData and that created a brand new Data container without the EXIF information. But the data was already a jpegData so I only needed to save it rather than recreating it.
-
Madhav Thakker almost 3 yearsHow to use metadataAttachments to build final image?