Metadata for Camera and Pose Extraction#

weitsicht turns EXIF/XMP tags into usable camera models and image parameters. The package is prepared to support multiple metadata wrappers by defining a small tag interface (MetaTagsBase). Currently, weitsicht ships with PyExifToolTags, which works with metadata dictionaries produced by Phil Harvey’s exiftool (via CLI JSON output or Python wrappers).

Use the helpers as building blocks:

  • retrieve IOR (intrinsics) for a camera model,

  • extract EOR (position/orientation + CRS),

  • build a ready-to-use ImagePerspective from metadata.

Note

There is no universal metadata standard: vendors mix EXIF, XMP, and MakerNotes inconsistently. Standard GPS tags are often present but are insufficient without orientation tags. Always sanity-check by mapping an image footprint and comparing it with a known ortho or satellite layer before running large batches.

Minimal example#

Extract tags (e.g. with Phil Harvey’s exiftool) and wrap the resulting dictionary with PyExifToolTags.

from pathlib import Path
import json

# Option A: Python helper (needs exiftool on PATH)
from exiftool import ExifToolHelper
with ExifToolHelper() as et:
    meta_list = et.get_metadata([Path("DJI_0001.JPG")])

# Option B: CLI + JSON
# import subprocess
# raw = subprocess.check_output(["exiftool", "-json", "DJI_0001.JPG"])
# meta_list = json.loads(raw)

from weitsicht import PyExifToolTags
tags = PyExifToolTags(meta_list[0])

Parse data (PyExifToolTags)#

PyExifToolTags provides convenient accessors that return typed dataclasses. Other metadata sources can be supported by implementing MetaTagsBase and returning the same tag dataclasses.

# image dimensions (width, height)
tags.image_shape()

# group all resolved tags into one object
tags_all = tags.get_all()

# camera/intrinsics related tags
tags.get_ior_base()       # -> MetaTagIORBase
tags.get_ior_extended()   # -> MetaTagIORExtended

# GNSS and orientation tags
tags.get_standard_gps()         # -> MetaTagGPS
tags.get_orientation_values()   # -> MetaTagOrientation

# optional vendor alternatives
tags.get_z_alternatives()  # -> MetaTagZalternatives (e.g. XMP:RelativeAltitude)
tags.get_crs()             # -> MetaTagCRS (custom HorizCS/VertCS tags)

Camera IOR options#

Option 1 (baseline): estimate minimal intrinsics#

Estimate camera intrinsics from standard tags: estimate_camera(tags.get_ior_base()) returns width, height, focal_px, cx, cy.

from weitsicht import CameraOpenCVPerspective, estimate_camera

width, height, focal_px, cx, cy = estimate_camera(tags.get_ior_base())
camera = CameraOpenCVPerspective(
    width=width,
    height=height,
    fx=focal_px,
    fy=focal_px,
    cx=cx,
    cy=cy,
)

Option 2: use ior_from_meta (preferred)#

ior_from_meta wraps estimation and also checks for extended/vendor calibration tags when present.

from weitsicht import ior_from_meta

ior_res = ior_from_meta(
    tags_ior=tags.get_ior_base(),
    tags_ior_extended=tags.get_ior_extended(),
)
if ior_res.ok:
    camera = ior_res.camera
    width = ior_res.width
    height = ior_res.height
else:
    # ResultFailure[MetadataIssue]
    print(ior_res.error, ior_res.issues)

Exterior orientation (EOR)#

from weitsicht import eor_from_meta

eor_res = eor_from_meta(tags.get_all())
if eor_res.ok:
    position = eor_res.position
    orientation = eor_res.orientation
    crs = eor_res.crs
else:
    print(eor_res.error, eor_res.issues)

Complete image from metadata#

from weitsicht import image_from_meta

img_res = image_from_meta(tags)  # accepts MetaTagsBase or MetaTagAll
if img_res.ok:
    image = img_res.image
    # Assign a mapper later, e.g. image.mapper = MappingHorizontalPlane(...)
else:
    print(img_res.error, img_res.issues)

Return structures#

The metadata helpers return small result objects:

  • Success results are dataclasses with ok=True (e.g. IORFromMetaResultSuccess).

  • Failures are ResultFailure with ok=False and the fields: error (human-readable message) and issues (a set of MetadataIssue).

The concrete unions are:

  • IORFromMetaResult = IORFromMetaResultSuccess | ResultFailure[MetadataIssue]

  • EORFromMetaResult = EORFromMetaResultSuccess | ResultFailure[MetadataIssue]

  • ImageFromMetaResult = ImageFromMetaResultSuccess | ResultFailure[MetadataIssue]

Keeping IOR and EOR decoupled#

If you want to handle IOR and EOR separately (e.g. use a default IOR but read EOR from IMU), use the builder:

from weitsicht.metadata.image_from_meta import ImageFromMetaBuilder

builder = ImageFromMetaBuilder(tags)
ior_res = builder.ior()
eor_res = builder.eor()

# Optionally build an image only if both are available
img_res = builder.image()

Notes & tips#

  • Sensor database fallback (src/weitsicht/metadata/camera_database.py) supplies sensor sizes for common models when EXIF lacks focal-plane resolutions.

  • CRS is separate: metadata rarely contains a complete CRS definition; pass a pyproj.CRS explicitly if needed.

  • Validation: after building an image, map a footprint and compare it to known GIS layers to detect convention issues.