capacitor-plugins
capacitor-plugins copied to clipboard
[Bug]: Wrong iOS photo exif data
Capacitor Version
💊 Capacitor Doctor 💊
Latest Dependencies:
@capacitor/cli: 6.1.2 @capacitor/core: 6.1.2 @capacitor/android: 6.1.2 @capacitor/ios: 6.1.2
Installed Dependencies:
@capacitor/cli: 6.1.2 @capacitor/core: 5.5.1 @capacitor/android: 5.5.1 @capacitor/ios: 5.5.1
[success] iOS looking great! 👌 [success] Android looking great! 👌
Other API Details
npm version: 10.8.2
node version: v20.17.0
pod version: 1.13.0
iOS version: 17.4.1
ios device: iPhone SE 2nd generation
Platforms Affected
- [X] iOS
- [ ] Android
- [ ] Web
Current Behavior
I am developing a data collection app where data is collected primarily in the form of photos which are later processed. To make postprocessing more straightforward it is required that they are taken in portrait mode (not in landscape). So, I am trying to figure out a way to restrict taking pictures which are landscape-oriented. I have not found any suitable setting for that, so now I am trying to disable saving photos that are wider than higher (landscape by this logic) based on the photo metadata. Photos are taken by the capacitor camera plugin when taking the photos I have found out that information about the photos taken in the iOS app is incorrect.
Portrait photo taken in the ios app:
And the related exif metadata about this photo which are returned by the plugin:
{
"Flash": 16,
"SubsecTimeOriginal": "675",
"PixelXDimension": 4032,
"MeteringMode": 5,
"ApertureValue": 1.6959938131099002,
"BrightnessValue": -3.0617721648310905,
"ColorSpace": 65535,
"FocalLenIn35mmFilm": 28,
"SceneType": 1,
"ExposureBiasValue": 0,
"PixelYDimension": 3024,
"Orientation": 1,
"ExposureTime": 0.06666666666666667,
"CompositeImage": 2,
"ExposureProgram": 2,
"WhiteBalance": 0,
"ShutterSpeedValue": 3.9085506496196545,
"LensModel": "iPhone SE (2nd generation) back camera 3.99mm f/1.8",
"ExifVersion": "0232",
"ISOSpeedRatings": [
800
],
"LensSpecification": [
3.99,
3.99,
1.8,
1.8
],
"LensMake": "Apple",
"SensingMethod": 2,
"DateTimeDigitized": "2024:10:09 08:08:04",
"ExposureMode": 0,
"FNumber": 1.8,
"DateTimeOriginal": "2024:10:09 08:08:04",
"FocalLength": 3.99,
"SubsecTimeDigitized": "675",
"OffsetTimeOriginal": "+02:00",
"SubjectArea": [
2013,
1511,
2116,
1330
],
"OffsetTimeDigitized": "+02:00",
"OffsetTime": "+02:00"
},
Landscape photo taken in the ios app:
And the related exif metadata about this photo which are returned by the plugin:
{
"Flash": 16,
"SubsecTimeOriginal": "524",
"PixelXDimension": 4032,
"MeteringMode": 5,
"ApertureValue": 1.6959938131099002,
"BrightnessValue": -3.343055762488874,
"ColorSpace": 65535,
"FocalLenIn35mmFilm": 28,
"SceneType": 1,
"ExposureBiasValue": 0,
"PixelYDimension": 3024,
"Orientation": 1,
"ExposureTime": 0.06666666666666667,
"CompositeImage": 2,
"ExposureProgram": 2,
"WhiteBalance": 0,
"ShutterSpeedValue": 3.9085506496196545,
"LensModel": "iPhone SE (2nd generation) back camera 3.99mm f/1.8",
"ExifVersion": "0232",
"ISOSpeedRatings": [
1250
],
"LensSpecification": [
3.99,
3.99,
1.8,
1.8
],
"LensMake": "Apple",
"SensingMethod": 2,
"DateTimeDigitized": "2024:10:09 08:09:01",
"ExposureMode": 0,
"FNumber": 1.8,
"DateTimeOriginal": "2024:10:09 08:09:01",
"FocalLength": 3.99,
"SubsecTimeDigitized": "524",
"OffsetTimeOriginal": "+02:00",
"SubjectArea": [
2013,
1511,
2116,
1330
],
"OffsetTimeDigitized": "+02:00",
"OffsetTime": "+02:00"
}
In my code, I am trying to compare PixelXDimension and PixelYDimension from the metadata and calculate if the photo is landscape or portrait. The problem is that these values are the same for both photos although they have different orientations.
On the contrary, the same photos taken with the android app return the expected result. Landscape photo metadata from an android app:
"Orientation": "0",
"PixelXDimension": "4624",
"PixelYDimension": "3472",
Portrait photo metadata from an android app:
"Orientation": "0",
"PixelXDimension": "3472",
"PixelYDimension": "4624",
Expected Behavior
I expect to get proper photo dimensions based on the photo orientation. So if the photo is taken landscape I expect to get the XDimension wider then the YDimension. And if the photo is taken as a portrait than the YDimension should be bigger than XDimension.
Project Reproduction
https://github.com/sakonn/probable-octo-invention
Additional Information
No response
This issue relates to Camera functionality, which is part of @capacitor/camera, and as such as been transferred to the appropriate repository.
Will there by a solution for this?
This issue has been labeled as type: bug. This label is added to issues that that have been reproduced and are being tracked in our internal issue tracker.
Hey @sakonn, Can you share the entire Android JSONs as you did for iOS? BR.
Hello @OS-ricardomoreirasilva, thanks for your interest. Here is the output from the Android phone. The photos were taken in the app build from the example repository which is linked in the issue description.
Here is the outcome of the photo taken as a portrait. The ImageLength and PixelYDimension are bigger as expected.
{
"format": "jpeg",
"exif": {
"ApertureValue": "167/100",
"BrightnessValue": "0/100",
"ColorSpace": "1",
"ComponentsConfiguration": "???",
"Compression": "6",
"DateTime": "2024:12:06 18:50:20",
"DateTimeDigitized": "2024:12:06 18:50:20",
"DateTimeOriginal": "2024:12:06 18:50:20",
"ExifVersion": "0220",
"ExposureBiasValue": "0/1",
"ExposureMode": "0",
"ExposureProgram": "0",
"Flash": "16",
"FlashpixVersion": "0100",
"FocalLength": "4730/1000",
"FocalLengthIn35mmFilm": "0",
"FNumber": "1.79",
"ImageLength": "4624",
"ImageWidth": "3472",
"InteroperabilityIndex": "R98",
"JPEGInterchangeFormat": "812",
"JPEGInterchangeFormatLength": "39323",
"LightSource": "21",
"Make": "XXXXX",
"MaxApertureValue": "167/100",
"MeteringMode": "2",
"Model": "XXXXX",
"Orientation": "0",
"PhotographicSensitivity": "125",
"PixelXDimension": "3472",
"PixelYDimension": "4624",
"ResolutionUnit": "2",
"SceneCaptureType": "0",
"SceneType": "0",
"SensingMethod": "0",
"ShutterSpeedValue": "5058/1000",
"SubSecTime": "609213",
"SubSecTimeDigitized": "609213",
"SubSecTimeOriginal": "609213",
"WhiteBalance": "0",
"XResolution": "72/1",
"YCbCrPositioning": "1",
"YResolution": "72/1"
},
"path": "file:///storage/emulated/0/Android/data/io.ionic.starter/files/Pictures/JPEG_20241206_185014_2081679850067582195.jpg",
"webPath": "http://192.168.1.7:8100/_capacitor_file_/storage/emulated/0/Android/data/io.ionic.starter/files/Pictures/JPEG_20241206_185014_2081679850067582195.jpg",
"saved": false
}
And here is the outcome of the photo taken as a landscape. The ImageWidth and PixelXDimension are bigger as expected.
{
"format": "jpeg",
"exif": {
"ApertureValue": "167/100",
"BrightnessValue": "0/100",
"ColorSpace": "1",
"ComponentsConfiguration": "???",
"Compression": "6",
"DateTime": "2024:12:06 18:50:51",
"DateTimeDigitized": "2024:12:06 18:50:51",
"DateTimeOriginal": "2024:12:06 18:50:51",
"ExifVersion": "0220",
"ExposureBiasValue": "0/1",
"ExposureMode": "0",
"ExposureProgram": "0",
"Flash": "16",
"FlashpixVersion": "0100",
"FocalLength": "4730/1000",
"FocalLengthIn35mmFilm": "0",
"FNumber": "1.79",
"ImageLength": "3472",
"ImageWidth": "4624",
"InteroperabilityIndex": "R98",
"JPEGInterchangeFormat": "812",
"JPEGInterchangeFormatLength": "34979",
"LightSource": "21",
"Make": "XXXXXX",
"MaxApertureValue": "167/100",
"MeteringMode": "2",
"Model": "XXXXX",
"Orientation": "0",
"PhotographicSensitivity": "125",
"PixelXDimension": "4624",
"PixelYDimension": "3472",
"ResolutionUnit": "2",
"SceneCaptureType": "0",
"SceneType": "0",
"SensingMethod": "0",
"ShutterSpeedValue": "5058/1000",
"SubSecTime": "467467",
"SubSecTimeDigitized": "467467",
"SubSecTimeOriginal": "467467",
"WhiteBalance": "0",
"XResolution": "72/1",
"YCbCrPositioning": "1",
"YResolution": "72/1"
},
"path": "file:///storage/emulated/0/Android/data/io.ionic.starter/files/Pictures/JPEG_20241206_185045_8653160463096438101.jpg",
"webPath": "http://192.168.1.7:8100/_capacitor_file_/storage/emulated/0/Android/data/io.ionic.starter/files/Pictures/JPEG_20241206_185045_8653160463096438101.jpg",
"saved": false
}
In this situation, the autorotate feature was disabled on my phone.
So I did a bit more digging into the issue itself and would like to share you with some findings.
Comparing the JSONs you share for both Android and iOS, my initial suspicions were confirmed: the issue is not a difference between Android and iOS but between the different EXIF versions those cameras use (as you can see by the ExifVersion fields, Android is using v0220 and iOS v0232). From what I found, the issue of swapped PixelXDimension and PixelYDimension in portrait pictures with EXIF 0232 compared to EXIF 0220 is likely due to the way EXIF 0232 handles image orientation metadata versus how EXIF 0220 does. Why?
- In EXIF 0220, the
PixelXDimensionandPixelYDimensionfields often directly reflect the raw dimensions of the image data as stored in the file. TheOrientationtag is included but is sometimes ignored or inconsistently used by older software. However, in EXIF 0232, theOrientationtag is more rigorously utilised, and modern software or devices often adjust metadata accordingly to reflect the image’s intended display orientation. - Consequently, applications reading EXIF 0220 may treat the image as always in landscape mode, regardless of the
Orientationtag. This ensures the dimensions remain unaltered. Opposite to this, in EXIF 0232, for portrait images, theOrientationtag specifies that the image should be rotated (e.g., 90° clockwise for portrait mode). Some software adjusts thePixelXDimensionandPixelYDimensionvalues to reflect how the image is meant to be displayed, rather than its raw storage orientation.
Considering all this, and regarding that the values are expected from an EXIF perspective, the presented values do make sense and changing them might lead to unexpected issues.
Thank you for your answer. So, the solution would be to write the alternative image handling based on the EXIF version the device is using? Do you have any sources to read more about how the EXIF handles the Orientation metadata value?
That approach appears correct, but I'd dig a bit into understanding how tools like ExifTool or ImageMagick work with EXIF data to have a bigger picture.
Here you have some "official" sources on EXIF:
- https://www.loc.gov/preservation/digital/formats/fdd/fdd000146.shtml
- https://www.loc.gov/preservation/digital/formats/fdd/fdd000618.shtml
- https://exiftool.org/TagNames/EXIF.html
Okay, thank you very much for the answer. I will take a look and see if the implementation is worth the efford.
Thanks for the issue! This issue is being locked to prevent comments that are not relevant to the original issue. If this is still an issue with the latest version of the plugin, please create a new issue and ensure the template is fully filled out.