Swift how to modify exif info in images taken from mobile camera
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! ")
}
EXIF data read and write
I'm using this to get EXIF infos from an image file:
import ImageIO
let fileURL = theURLToTheImageFile
if let imageSource = CGImageSourceCreateWithURL(fileURL as CFURL, nil) {
let imageProperties = CGImageSourceCopyPropertiesAtIndex(imageSource, 0, nil)
if let dict = imageProperties as? [String: Any] {
print(dict)
}
}
It gives you a dictionary containing various informations like the color profile - the EXIF info specifically is in dict["{Exif}"]
.
Modifying Image Metadata
I’m answering this myself since I found out why it wasn’t saving the data and it looks like this question can help others.
My code is correct, the only issue is that I wasn’t formatting the date properly. Since the date wasn’t in the correct format it was getting pruned by the frameworks. I formatted the date like this and it saved and displayed properly:
let formatter = DateFormatter()
formatter.dateFormat = "yyyy:MM:dd HH:mm:ss"
exif[ImagePropertyExifDateTimeDigitized] = formatter.string(from: Date())
This was in place of the line:
exif[ImagePropertyExifDateTimeDigitized] = Date() as CFDate
The output is now (again pruned to only the relevant properties):
unmodified properties
{
"{Exif}" = {
DateTimeDigitized = "2007:07:31 17:42:01";
DateTimeOriginal = "2007:07:31 17:42:01";
};
}
modified properties
{
"{Exif}" = {
DateTimeDigitized = "2017:05:12 01:04:14";
DateTimeOriginal = "2007:07:31 17:42:01";
};
}
saved properties
{
"{Exif}" = {
DateTimeDigitized = "2017:05:12 01:04:14";
DateTimeOriginal = "2007:07:31 17:42:01";
};
}
UIImagePickerController and extracting EXIF data from existing photos
The naughty way to do this is to traverse the UIImagePickerViewController's views and pick out the selected image in the delegate callback.
- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info {
id thumbnailView = [[[[[[[[[[picker.view subviews]
objectAtIndex:0] subviews]
objectAtIndex:0] subviews]
objectAtIndex:0] subviews]
objectAtIndex:0] subviews]
objectAtIndex:0];
NSString *fullSizePath = [[[thumbnailView selectedPhoto] fileGroup] pathForFullSizeImage];
NSString *thumbnailPath = [[[thumbnailView selectedPhoto] fileGroup] pathForThumbnailFile];
NSLog(@"%@ and %@", fullSizePath, thumbnailPath);
}
That will give you the path to the full size image, which you can then open with an EXIF library of your choice.
But, this calls a Private API and these method names will be detected by Apple if you submit this app. So don't do this, OK?
Save CUSTOM metadata in an image taken from AVFoundation in iOS
UIImage
and CIImage
don't hold all the metadata. In fact, converting it from NSData
to either of those two formats will strip a lot of that metadata out, so IMHO, UIImage
and CIImage
should only be used if you are planning on displaying it in the UI or passing it to CoreImage.
You can read the image properties like this:
- (NSDictionary *)getImageProperties:(NSData *)imageData {
// get the original image metadata
CGImageSourceRef imageSourceRef = CGImageSourceCreateWithData((__bridge CFDataRef) imageData, NULL);
CFDictionaryRef properties = CGImageSourceCopyPropertiesAtIndex(imageSourceRef, 0, NULL);
NSDictionary *props = (__bridge_transfer NSDictionary *) properties;
CFRelease(imageSourceRef);
return props;
}
You can then convert your NSDictionary
to a NSMutableDictionary
:
NSDictionary *props = [self getImageProperties:imageData];
NSMutableDictionary *mutableDictionary = [NSMutableDictionary dictionaryWithDictionary:props];
After that, add whatever fields you would like. Note that EXIF data is found by grabbing the kCGImagePropertyExifDictionary
, GPS data by the kCGImagePropertyGPSDictionary
, and so on:
NSMutableDictionary *exifDictionary = properties[(__bridge NSString *) kCGImagePropertyExifDictionary];
Look at the CGImageProperties
header file for all the keys available.
Okay, so now that you have made whatever changes you needed to the metadata, adding it back takes a couple of more steps:
// incoming image data
NSData *imageData;
// create the image ref
CGDataProviderRef imgDataProvider = CGDataProviderCreateWithCFData((__bridge CFDataRef) imageData);
CGImageRef imageRef = CGImageCreateWithJPEGDataProvider(imgDataProvider, NULL, true, kCGRenderingIntentDefault);
// the updated properties from above
NSMutableDictionary *mutableDictionary;
// create the new output data
CFMutableDataRef newImageData = CFDataCreateMutable(NULL, 0);
// my code assumes JPEG type since the input is from the iOS device camera
CFStringRef type = UTTypeCreatePreferredIdentifierForTag(kUTTagClassMIMEType, (__bridge CFStringRef) @"image/jpg", kUTTypeImage);
// create the destination
CGImageDestinationRef destination = CGImageDestinationCreateWithData(newImageData, type, 1, NULL);
// add the image to the destination
CGImageDestinationAddImage(destination, imageRef, (__bridge CFDictionaryRef) mutableDictionary);
// finalize the write
CGImageDestinationFinalize(destination);
// memory cleanup
CGDataProviderRelease(imgDataProvider);
CGImageRelease(imageRef);
CFRelease(type);
CFRelease(destination);
// your new image data
NSData *newImage = (__bridge_transfer NSData *)newImageData;
And there you have it.
I want to read exif info in image in android. I can read exif from image in gallery but i cannot read exif photo taken from the camera
You cannot read exif data from a Stream using the android api. ExifInterface doesn't have a constructor with an InputStream.
However, You can use the metadata-extractor
and use the constructor with an InputStream to build an InputStream backed by your byte[] using a ByteArrayInputStream
Reading the GPS data from the image returned by the camera in iOS iphone
One possibility is to leaving CoreLocation running when the camera is visible. Record each CCLocation into an array along with the time of the sample. When the photo comes back, find its time, then match the closest CClocation from the array.
Sounds kludgy but it will work.
Correctly set the right picture orientation when shooting photo
You may set your shot orientation to whatever you like by setting videoOrientation
of your AVCapturePhotoOutput
.
To match it with the current device orientation, you may use UIDevice.current.orientation
manually converted to AVCaptureVideoOrientation
.
let photoOutput = AVCapturePhotoOutput()
func takeShot() {
// set whatever orientation you like
let myShotOrientation = UIDevice.current.orientation.asCaptureVideoOrientation
if let photoOutputConnection = self.photoOutput.connection(with: .video) {
photoOutputConnection.videoOrientation = myShotOrientation
}
photoOutput.capturePhoto(...)
}
Conversion from UIDeviceOrientation
to AVCaptureVideoOrientation
:
extension UIDeviceOrientation {
///
var asCaptureVideoOrientation: AVCaptureVideoOrientation {
switch self {
// YES, that's not a mistake
case .landscapeLeft: return .landscapeRight
case .landscapeRight: return .landscapeLeft
case .portraitUpsideDown: return .portraitUpsideDown
default: return .portrait
}
}
}
Related Topics
Becomefirstresponder Not Working in iOS 8
How to Disable Arc for a Single File in Xcode 5
iOS 6 Streaming Player Com.Apple.Coremedia.Networkbuffering Bug
Xcode 8 Swift 3 Uitableview Space Between Cells
Marker Click Event on React Native Maps Not Working in React iOS
Set a Passwordfield to Securetextentry Give Me a Strange Behaviour
Cannot Show Automatic Strong Passwords for App Bundleid
Using Apple's Reachability to Check Remote Server Reachability in Swift
Header Displaced in Tableview with Uirefreshcontrol
#Import <Libxml/Tree.H> File Not Found After Xcode Update
iOS - Wkwebview Cross Origin Requests Are Only Supported for Http
View Controller Responds to App Delegate Notifications in iOS 12 But Not in iOS 13
iOS Swift Converting Calendar Component Int Month to Medium Style String Month
Alamofire No Such Module (Cocoapods)