Fixes incorrect camera orientation EXIF/XMP tags on DJI drone JPEGs that cause orthomosaic and 3D reconstruction software (iTwin Capture Modeler, Pix4D, Metashape, etc.) to badly misalign or reject the majority of photos in a block.
DJI drones log two separate rotations per photo:
- Airframe attitude (
Pitch/Yaw/Roll) — the aircraft body's orientation relative to the world/horizon, from the IMU. - Camera/gimbal orientation (
CameraPitch/CameraYaw/CameraRoll, mirrored in XMP asGimbalPitchDegree/GimbalYawDegree/GimbalRollDegreeandFlightPitchDegree/FlightYawDegree/FlightRollDegree) — how the camera points, but on many DJI models/gimbal modes this is logged relative to the airframe, not the true absolute orientation in world space.
Reconstruction software needs the camera's absolute pointing direction in world space for every frame to back-project pixels correctly and match overlap between photos. If the airframe-relative camera value is read as if it were already absolute, every frame flown off-level (turns, wind correction, oblique gimbal angles) gets seeded with the wrong initial pose. The matcher can't find correspondences between neighboring photos, and most of the block fails to reconstruct — even though individual photos look fine.
This is a known, recurring issue reported across DJI/Pix4D/Mavic forums (foreshortened orthos, point clouds curved ~90° out of true, "hosed" orientation in rayCloud views), but no clean fix seems to be published.
Absolute camera orientation = airframe attitude + camera-relative angle, per axis:
corrected_pitch = camera_pitch + drone_pitch
corrected_yaw = camera_yaw + drone_yaw
corrected_roll = camera_roll + drone_roll
fix_drone_exif.py reads both sets of tags via ExifTool, computes the
corrected absolute orientation, and writes it back into both the standard
EXIF fields and the DJI XMP gimbal/flight fields, so any tool reading either
tag set sees a consistent, correct value.
Tested on 268 JPEGs from a DJI Mavic Mini (camera model FC7203), processed
in iTwin Capture Modeler:
| Before fix | After fix | |
|---|---|---|
| Photos in main component | 22 / 270 | 263 / 268 |
| Automatic tie points | — | 63,990 |
| Resolution | — | 0.011–0.13 m |
Before the fix, only photos from near-level sections of the flight (where airframe-relative ≈ absolute by coincidence) reconstructed at all. After the fix, nearly the entire block bundles successfully.
- ExifTool
- Python 3
python fix_drone_exif.py --dir "C:\path\to\photos" --exiftool "C:\tools\exiftool.exe"
python fix_drone_exif.py --exiftool exiftool # if exiftool is on your PATH
python fix_drone_exif.py --exiftool exiftool # no --dir -> opens a folder picker
python fix_drone_exif.py --help
--exiftool is required every run. It processes every .jpg in the target
directory in place (originals are overwritten — back up first if you want to
keep the uncorrected files).
- Verify the correction on your own drone/gimbal combo before trusting it
blindly: diff
Pitch/Yaw/RollagainstCameraPitch/Yaw/Rollon a few frames from a level pass vs. a turn. If the camera fields stay roughly constant across airframe attitude changes, they're airframe-relative and this fix applies. - Not every drone/firmware logs orientation the same way — some already report absolute camera orientation, in which case this correction would be wrong to apply. Check before running on a new dataset.