Open davehearne opened 3 years ago
Facing the same issue. EXIF data not available on iOS
You need to manually add the metadata in
Someone can probably provide a better example than me but incase it helps something like this.
Grab the metadata
self.cameraController.captureImage { (image, metadata, depthData, error) in
then pass it through to your save function. Something like
let options = metadata.mutableCopy() as! NSMutableDictionary
CGImageDestinationAddImage(destination,
cgImage,
options)
Hi @jcroucher , thanks for the reply.
I have tried this in CameraController.swift
but I get an error on metadata
and depthData
.
I tried updating the function captureImage
to pass 4 parameters however metadata
and depthData
were not recognized.
Do these values need to initialized as a specific type?
Thanks in advance, Dave
Modify the captureImage function in CameraController file to be something like this. I have made a number of changes so I would recommend using it just as reference rather than copying the blocks as it may cause more issues
func captureImage(completion: @escaping (UIImage?, NSDictionary?, AVDepthData?, Error?) -> Void) {
guard let captureSession = captureSession, captureSession.isRunning else {
completion(nil, nil, nil, CameraControllerError.captureSessionIsMissing); return
}
let settings = AVCapturePhotoSettings(format: [AVVideoCodecKey: AVVideoCodecType.hevc])
settings.isDepthDataDeliveryEnabled = photoOutput?.isDepthDataDeliverySupported ?? false
settings.embedsDepthDataInPhoto = photoOutput?.isDepthDataDeliverySupported ?? false
settings.flashMode = self.flashMode
settings.isHighResolutionPhotoEnabled = self.highResolutionOutput
self.photoOutput?.capturePhoto(with: settings, delegate: self)
self.photoCaptureCompletionBlock = completion
}
Note in my version I have the codec set to
[AVVideoCodecKey: AVVideoCodecType.hevc]
Leave this as jpeg if you don't want to use the heic format.
And a bit more code as an example. That method above is called from
self.cameraController.captureImage { (image, metadata, depthData, error) in
guard let image = image else {
print(error ?? "Image capture error")
guard let error = error else {
call.reject("Image capture error")
return
}
call.reject(error.localizedDescription)
return
}
let imageDataHiec: Data?
imageDataHiec = image.heic(metadata: metadata!, depthData: depthData!)
.......
I save it out as a hiec, but the same would work for jpg
func heic(metadata: NSDictionary, depthData: AVDepthData) -> Data? {
guard
let mutableData = CFDataCreateMutable(nil, 0),
let destination = CGImageDestinationCreateWithData(mutableData, "public.heic" as CFString, 1, nil),
let cgImage = cgImage
else { return nil }
let options = metadata.mutableCopy() as! NSMutableDictionary
options[kCGImagePropertyOrientation] = cgImageOrientation.rawValue
CGImageDestinationAddImage(destination,
cgImage,
options)
var auxDataType :NSString?
let auxData = depthData.dictionaryRepresentation(forAuxiliaryDataType: &auxDataType)
// Add auxiliary data to the image destination.
if (auxData != nil && auxDataType != nil) {
CGImageDestinationAddAuxiliaryDataInfo(destination, auxDataType!, auxData! as CFDictionary)
}
guard CGImageDestinationFinalize(destination) else { return nil }
return mutableData as Data
}
Remove all the depth data bits if you don't need it. I need it for what I am doing, but it will just make your files bigger for no reason if you don't use it.
Finally revisiting the above issue regarding getting EXIF data for iOS Images. I have been following the information above provided by @jcroucher. However I have it a snag when trying to build it.
s func captureImage(completion: @escaping (UIImage?, NSDictionary?, Error?) -> Void) {
guard let captureSession = captureSession, captureSession.isRunning else { completion(nil, nil, CameraControllerError.captureSessionIsMissing); return }
let settings = AVCapturePhotoSettings()
settings.flashMode = self.flashMode
settings.isHighResolutionPhotoEnabled = self.highResolutionOutput;
let currentDevice: UIDevice = .current
let deviceOrientation: UIDeviceOrientation = currentDevice.orientation
let statusBarOrientation = UIApplication.shared.statusBarOrientation
if deviceOrientation == .portrait {
self.photoOutput?.connection(with: AVMediaType.video)?.videoOrientation = AVCaptureVideoOrientation.portrait
}else if (deviceOrientation == .landscapeLeft){
self.photoOutput?.connection(with: AVMediaType.video)?.videoOrientation = AVCaptureVideoOrientation.landscapeRight
}else if (deviceOrientation == .landscapeRight){
self.photoOutput?.connection(with: AVMediaType.video)?.videoOrientation = AVCaptureVideoOrientation.landscapeLeft
}else if (deviceOrientation == .portraitUpsideDown){
self.photoOutput?.connection(with: AVMediaType.video)?.videoOrientation = AVCaptureVideoOrientation.portraitUpsideDown
}else if (deviceOrientation == .faceUp || deviceOrientation == .faceDown){
switch (statusBarOrientation) {
case .portrait:
self.photoOutput?.connection(with: AVMediaType.video)?.videoOrientation = AVCaptureVideoOrientation.portrait
case .landscapeRight:
self.photoOutput?.connection(with: AVMediaType.video)?.videoOrientation = AVCaptureVideoOrientation.landscapeRight
case .landscapeLeft:
self.photoOutput?.connection(with: AVMediaType.video)?.videoOrientation = AVCaptureVideoOrientation.landscapeLeft
case .portraitUpsideDown:
self.photoOutput?.connection(with: AVMediaType.video)?.videoOrientation = AVCaptureVideoOrientation.portraitUpsideDown
default:
self.photoOutput?.connection(with: AVMediaType.video)?.videoOrientation = AVCaptureVideoOrientation.portrait
}
}else {
self.photoOutput?.connection(with: AVMediaType.video)?.videoOrientation = AVCaptureVideoOrientation.portrait
}
self.photoOutput?.capturePhoto(with: settings, delegate: self)
self.photoCaptureCompletionBlock = completion
I get an error for the last line of this function, 'Type of expression is ambiguous without more context'
The only other changes I have made are in the Plugin:
@objc func capture(_ call: CAPPluginCall) {
DispatchQueue.main.async {
let quality: Int? = call.getInt("quality", 85)
self.cameraController.captureImage { (image, metadata, error) in
guard let image = image else {
print(error ?? "Image capture error")
guard let error = error else {
call.reject("Image capture error")
return
}
call.reject(error.localizedDescription)
return
}
var imageData: Data?
imageData = image.testFunction(metadata: metadata!)
if (self.cameraPosition == "front") {
let flippedImage = image.withHorizontallyFlippedOrientation()
imageData = flippedImage.jpegData(compressionQuality: CGFloat(quality!/100))
} else {
imageData = image.jpegData(compressionQuality: CGFloat(quality!/100))
}
if (self.storeToFile == false){
let imageBase64 = imageData?.base64EncodedString()
call.resolve(["value": imageBase64!])
}else{
do{
let fileUrl=self.getTempFilePath()
try imageData?.write(to:fileUrl)
call.resolve(["value":fileUrl.absoluteString])
}catch{
call.reject("error writing image to file")
}
}
}
}
}
I also added this function to the extension UIImage:
func testFunction(metadata: NSDictionary) -> Data? {
guard
let mutableData = CFDataCreateMutable(nil, 0),
let destination = CGImageDestinationCreateWithData(mutableData, "public.jpeg" as CFString, 1, nil),
let cgImage = cgImage
else { return nil }
let options = metadata.mutableCopy() as! NSMutableDictionary
CGImageDestinationAddImage(destination,
cgImage,
options)
guard CGImageDestinationFinalize(destination) else { return nil }
return mutableData as Data
}
Any help appreciated. I am no swift developer, only being following what information is available.
Does anyone have a working solution for this?
I am not sure if this is related to this issue, as the original message does not mention which EXIF metadata is missing. However, I also faced similar problem, where the GPS coordinates are missing in EXIF metadata of the photos taken on both iOS and Android platforms. In my case, it seems that the camera API does does not have direct access to location information, so if you want EXIF metadata with location in the final JPEG, you have to add it yourself. For example, here is similar feature added to cordova-plugin-camera
plugin to enforce the EXIF metadata presence - see https://github.com/apache/cordova-plugin-camera/pull/525
However, I am not sure if this should be within the scope of this plugin so I decided to take another approach and created the capacitor plugin to add/read the required EXIF metadata - see https://github.com/capacitor-community/exif
I believe that this is better approach because:
Hi, It looks like there is no EXIF data available for iOS, is there a way to turn this on?
EXIF data is available for android.