Quickly change exif information in images captured from a mobile camera

I use UIImagePickerController to search for images in my iOS application, and I know that exif information can be obtained using the [UIImagePickerControllerMediaMetadata] information. But when I upload my image to my UIImage server, most of the exif information was striped. I wonder if I can add exif information to my image in the Http request (after that the image loaded as jpg). If not, how can I solve this problem? I want to change the Make, Model attributes (in other words, which device was used for shooting)

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!) } } 
+6
source share
3 answers

Yes! Finally, I did the trick to change the EXIF ​​information. First you can get EXIF ​​information from [UIImagePickerControllerMediaMetadata] and NSData without EXIF ​​information from the selected UIImage by UIImageJPEGRepresentation. You can then create a new NSDictionary with the modified EXIF ​​information. After that, call my function in the following, you can get the NSData image with the 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! ") } 
+2
source

Using and merging some information from other posts, I approached the problem using the dictionary in Swift. I used it in the captureOutput 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 create the final image (using CGImageDestinationAddImage in my case)

It seems to work (used in a project with Swift 4.0)

Hope this helps!

+1
source

SWIFT 3

If you shoot a video and get a CMSampleBuffer, there is a way to update the EXIF ​​metadata. In my case, in iOS9 I did not get a DateTimeOriginal, although in iOS10 there was already a DataTimeOriginal. Thus, I had to add some additional key values.

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

All Articles