Choosing camera axes: aerial vs terrestrial (and YPR vs OPK)¶
- Status: unverified
- Applies to: Metashape Pro 2.x and Standard 2.x (GUI Camera axes). The Python Sensor.axes attribute and Sensor.Axes enum were added in 2.3.
- Edition: Pro / Standard
- Diátaxis: explanation
- Confidence: medium
- Last reviewed: 2026-08-20
Confidence: medium. The
Sensor.axesattribute, theSensor.Axesenum (Aerial,Terrestrial, defaultAerial), and the version in which they were added are Tier 1 introspection-confirmed on Metashape 2.3.1 and stated in the Python API Reference change log. The axis-direction and platform framing are quoted from the Pro 2.3 user manual. Gimbal lock at a middle angle of ±90° is general Tait–Bryan math. What is synthesised here — and so hedged — is the link from the terrestrial axes setting to gimbal-lock avoidance, and whether the choice perturbs Metashape's internal bundle adjustment (which may use rotation matrices) versus only reference entry, display, and per-axis priors.
When you set camera-orientation references, two separate things control how a rotation is interpreted:
- Camera axes — Aerial or Terrestrial (
Sensor.axes). This selects the camera's local axis directions, and it is the primary aerial-vs-terrestrial control. - Angle elements — YPR / OPK / POK / ANK (
Chunk.euler_angles). This only selects which angle triple expresses the rotation.
For a terrestrial, horizontal-looking capture there are two ways to avoid the ill-conditioning explained below:
- Theoretically correct: set the sensor's camera axes to
Terrestrial (
Sensor.axes) and keep YPR. This re-centers the parameterization so a horizontal camera is well-conditioned. - Recommended in practice today: use OPK reference angles
instead. The camera-axes setting is new in 2.3 and, as of 2.3.1, has
a serialization bug that silently drops it when a project is saved to
.psz(see Caveats), so OPK remains the safe workaround for now.
The two controls¶
- Camera Calibration dialog → Camera axes (Aerial / Terrestrial);
Python
Sensor.axes. Selects the camera's axis directions:
"Metashape allows to specify camera axes in the Camera Calibration dialog window for aerial and terrestrial data. For aerial data the Z-axis is directed backward, and the Y-axis is directed upward. For terrestrial data, the Y-axis is directed backward and the Z-axis is directed upward." — Metashape Pro User Manual 2.3, ch. "General workflow" § "Camera orientation and angles conventions" (Pro 2.3, p. 35)
The Python API Reference documents Sensor.axes as the "Local camera
coordinate system orientation," of type Metashape.Sensor.Axes
(Aerial or Terrestrial). It is per-sensor and defaults to
Aerial.
- Reference Settings dialog → angle elements; Python
Chunk.euler_angles. Selects which triple expresses the rotation:
"For images system of angular elements of exterior orientation ([yaw,pitch,roll], [omega, phi, kappa] or [alpha, nu, kappa]) can be switched in the Reference Settings dialog." — Metashape Pro User Manual 2.3, § "Camera axes for aerial and terrestrial data" (Pro 2.3, p. 36)
What the YPR angles describe¶
YPR angles describe the platform (aircraft) that carries the camera, not the optical axis directly. Under the default (aerial) axes, a zero triple is a level platform whose camera looks straight down:
"Rotation angles for the camera coordinates in Metashape are defined around the following axes: yaw axis runs from top to bottom, pitch axis runs from left to right wing of the drone, roll axis runs from tail to nose of the drone. Zero values of the rotation angle triple define the following camera position aboard: camera looks down to the ground, frames are taken in landscape orientation, and horizontal axis of the frame is perpendicular to the central (tail-nose) axis of the drone." — Metashape Pro User Manual 2.3, § "Camera axes for aerial and terrestrial data" (Pro 2.3, p. 36)
That aerial framing is the default because it is what an airborne IMU reports:
"The more common scenario is to load [yaw, pitch, roll] measurements directly from the airborne IMU system and, hence, default setting in Metashape is to tackle the input values as [yaw, pitch, roll] data." — Metashape Pro User Manual 2.3, § "INS offset for terrestrial data"
Why a horizontal camera is awkward under aerial axes: gimbal lock¶
Every three-angle (Tait–Bryan) parameterization has one orientation where the first and third axes align and the triple loses a degree of freedom — gimbal lock. For yaw-pitch-roll this is at the middle angle, pitch = ±90°.
Under the aerial axes the zero pose is a down-looking camera, so to
make that camera look at the horizon the platform must pitch by
±90° — right on the singularity. There, yaw and roll rotate about the
same axis, the (yaw, pitch, roll) triple is non-unique, small pose
changes swing yaw and roll wildly, and the per-axis yaw/roll accuracies
you set as priors lose independent meaning. A nadir aerial camera, by
contrast, sits at pitch ≈ 0° — nowhere near the singularity.
Switching the sensor to Terrestrial axes changes the local axis directions (Y backward, Z up rather than Z backward, Y up), which re-centers the parameterization so the horizontal-looking pose — the normal terrestrial case — is the well-conditioned zero region rather than the ±90° singularity. That is why terrestrial camera axes is the principled fix for ground-based capture — although, as of 2.3.1, a serialization bug (see Caveats) means OPK references remain the safer choice in practice.
Note (unverified). The clear, math-level effect is on representing the orientation near pitch ±90° — entering, importing, displaying, and setting per-axis priors. Whether the axes choice also changes Metashape's internal bundle adjustment (which may parameterize rotations as matrices/quaternions) is not established here; treat that as pending Tier 3 verification.
Setting it in Python¶
import Metashape
chunk = Metashape.app.document.chunk
# PRIMARY control: aerial vs terrestrial camera axes (Metashape 2.3+).
# Per-sensor; the default is Sensor.Axes.Aerial.
for sensor in chunk.sensors:
sensor.axes = Metashape.Sensor.Axes.Terrestrial # ground-based capture
# SEPARATE, largely independent: which angle triple expresses the
# rotation when you import / display / export references.
chunk.euler_angles = Metashape.EulerAnglesYPR # or EulerAnglesOPK / POK / ANK
Sensor.axes (and the Sensor.Axes enum) were added in Metashape
2.3; on 2.2.x and earlier the attribute does not exist, and the
aerial/terrestrial choice is only reachable in the GUI (or not at all,
depending on version). Chunk.euler_angles has been available across
2.x.
Warning —
.pszserialization bug (Metashape 2.3.1). Saving a project to.pszsilently drops a non-defaultsensor.axes: set Terrestrial,doc.save()to.psz, reopen, and the sensor is back toAerialwith nothing logged. The.psxformat andexportCameras(..., CamerasFormatXML)write it correctly, and the.pszreader honours it — only the.pszwriter omits it. Until this is fixed, do not rely onsensor.axesin any workflow that round-trips through.psz; use OPK reference angles instead. The bug is logged against 2.3.1 and may be resolved in 2.3.2 or later — check the current status in the linked report before relying on either path.
Choosing¶
- Nadir / near-nadir aerial (drone) survey → Aerial axes (default), YPR. It matches the IMU data and sits far from the pitch-±90° singularity.
- Terrestrial / façade / any horizontal-looking capture → OPK
reference angles (recommended today). Terrestrial camera axes with
YPR is the cleaner solution in principle, but it is new in 2.3 and the
2.3.1
.pszwriter silently drops it (see Caveats), so prefer OPK references until that is fixed (possibly 2.3.2+). - YPR vs OPK is a secondary, representation-level choice. Pick whichever your reference data already uses (drone IMU/EXIF → YPR; classical aerotriangulation / survey exports → OPK) so you do not convert through the singular region.
Caveats¶
Sensor.axesis per-sensor. Set it on every sensor whose cameras need the terrestrial convention, not once on the chunk.- Version floor.
Sensor.axes/Sensor.Axesare 2.3+. Scripts targeting 2.2.x cannot set the axes convention through Python. .pszsilently dropssensor.axes(2.3.1). A non-defaultsensor.axesis not written by the.pszwriter, so saving to.pszand reopening reverts the sensor toAerialwith no warning (.psxandexportCamerasXML are unaffected). This is why OPK references are recommended over terrestrial axes today. The bug is logged against 2.3.1 and may be fixed in 2.3.2 or later — verify the current status in the linked report before relying onsensor.axeswith.psz.- Axes and angle elements are independent.
Sensor.axeschooses the axis directions;Chunk.euler_angleschooses the angle triple. Changing either re-interprets yourcamera.reference.rotationvalues. - YPR sign conventions are separately subtle. For the math-vs-drone pitch-sign flip when round-tripping YPR through the API, see YPR rotation conventions.
See also¶
- YPR rotation conventions:
ypr2matvscamera.reference.rotation— the pitch-sign convention and round-trip recipe within YPR. - Importing camera orientation: EXIF, omega-phi-kappa, and yaw/pitch/roll
— how to populate
camera.reference.rotationfrom each source. - Chunk frame vs camera frame: per-axis priors
— the per-axis
rotation_accuracysurface that the singularity makes ambiguous. - Bundle-adjustment quality: variance factor, overfit testing, and reference detectability — where camera rotation references enter as weighted observations.
References¶
- Metashape Pro User Manual 2.3 (and Standard edition), ch. "General workflow" § "Camera orientation and angles conventions" (Pro 2.3, p. 35) — the Camera axes (aerial / terrestrial) setting and the axis directions it selects.
- Metashape Pro User Manual 2.3 (and Standard edition), ch. "General workflow" § "Camera axes for aerial and terrestrial data" (Pro 2.3, p. 36) — the YPR / OPK / POK / ANK axis assignments and the "camera looks down at zero" platform convention.
- Metashape Pro User Manual 2.3, § "INS offset for terrestrial data" — YPR is the default because it is what the airborne IMU provides.
- Metashape Python API Reference (2.3.1):
Sensor.axes("Local camera coordinate system orientation"), theSensor.Axesenum (Aerial,Terrestrial) — both added in 2.3 per the reference's change log — andChunk.euler_angles,Metashape.EulerAngles. - Forum thread, Z axis convention for aerial datasets, 2016 — the drone-Z-axis-pointing-to-ground canonical pose.
- Forum bug report, [2.3.1] doc.save() to .psz silently drops sensor.axes, 2026
— Metashape 2.3.1: the
.pszwriter omitssensor.axeswhile.psx,exportCamerasXML, and the.pszreader all handle it; may be addressed in 2.3.2 or later.