Skip to content

better support for radiometric panels#2030

Draft
maurerle wants to merge 4 commits into
OpenDroneMap:masterfrom
brainergylab:radiometric_panels
Draft

better support for radiometric panels#2030
maurerle wants to merge 4 commits into
OpenDroneMap:masterfrom
brainergylab:radiometric_panels

Conversation

@maurerle

@maurerle maurerle commented Jun 4, 2026

Copy link
Copy Markdown

Summary

For usage with a Micasense RedEdge-P, we are currently using the DLS reflectance from camera+sun, however it is more accurate when preprocessing the Data with the micasense library: https://github.com/brainergylab/micasense_imageprocessing and using the QR panel adjustment from there.
This PR adds the capability to ODM as well.

This PR is assisted by opus-4.8 and is currently untested.
I would like some early feedback and improve based on it, after testing that it works as intended.
I am aware of https://github.com/OpenDroneMap/documents/blob/master/CONTRIBUTING.md - which is why this is a draft.

Adds --radiometric-calibration camera+panel, deriving per-band reflectance
from in-field MicaSense (and compatible) calibration reflectance panel captures.
This closes the long-standing # TODO: support for calibration panels in
opendm/multispectral.py.
The implementation is fully native: panel metadata is parsed directly from
XMP as Micasense already handles QR Code panel reading and exposes results in MetaData.

How it works

  1. Metadata parsing (opendm/photo.py): reads the panel XMP tags
    CalibrationPicture, Albedo, ReflectArea, PanelSerial and exposes
    get_panel_albedo(), get_panel_region(), get_panel_serial(),
    is_calibration_picture(), and is_panel_image().
  2. Detection & irradiance (stages/dataset.py + opendm/multispectral.py):
    panel captures are auto-detected, per-band irradiance is computed once
    (irradiance = mean_panel_radiance * π / albedo, robust median across panel
    captures), persisted to panels.json, and the panel frames are excluded from
    the reconstruction (they are ground shots that would pollute SfM).
  3. Application (stages/run_opensfm.py): the per-band irradiance is loaded
    and passed into dn_to_reflectance. compute_irradiance now checks panel
    irradiance before the stored HorizontalIrradiance, so it is actually used
    on DLS cameras.
    Fallback order per band: panel → stored HorizontalIrradiance → DLS estimate → 1.0.

New CLI options

  • --radiometric-calibration camera+panel
  • --panel-reflectance — override panel albedo when metadata is missing;
    accepts a single float (e.g. 0.49) or per-band JSON
    (e.g. '{"Red": 0.49, "Green": 0.49}').

Robustness

  • Per-band graceful fallback if a panel isn't detected for some bands.
  • Saturation guard rejects panels with too many saturated pixels.
  • LWIR/thermal bands are skipped (no panel).
  • No new dependencies; behavior is unchanged unless camera+panel is selected.

Test plan

  • Add unit tests for panel XMP parsing and parse_panel_reflectance
    (tests/test_photo.py).
  • RedEdge-P dataset with panel captures: panel region reflectance ≈ catalog
    albedo within tolerance.
  • Dataset without panels still processes (DLS/stored irradiance path).
  • --panel-reflectance 0.49 override path.
  • Confirm panel frames are excluded from the reconstruction.

Notes / limitations

  • Panel detection/exclusion happens in the dataset stage; rerun with
    --rerun-from dataset if enabling camera+panel on an already-loaded dataset.
  • Exclusion is metadata-driven (camera-flagged calibration pictures).
  • Docs (OpenDroneMap/docs) to be updated separately.

maurerle added 4 commits June 4, 2026 09:27
Parse CalibrationPicture/Albedo/ReflectArea/PanelSerial directly from XMP
(no exiftool dependency) and expose panel getters plus is_calibration_picture()
and is_panel_image() helpers on ODM_Photo.

Assisted-by: Cursor:opus-4.8
New opendm/panel.py provides polygon region statistics (mean/std and saturated
pixel fraction) over a panel active area, used to derive panel irradiance.

Assisted-by: Cursor:opus-4.8
Add compute_irradiance_from_panels() (median across panel captures, saturation
rejection, graceful per-band fallback) and parse_panel_reflectance() for the
--panel-reflectance override. Thread panel_irradiance through dn_to_reflectance
and check it in compute_irradiance before the stored HorizontalIrradiance early
return, so panel irradiance is actually used for DLS cameras.

Assisted-by: Cursor:opus-4.8
Add the camera+panel choice and --panel-reflectance flag. The dataset stage
detects calibration panel captures, computes and persists per-band irradiance
to panels.json, and excludes panel frames from the reconstruction. run_opensfm
loads that irradiance and passes it per band into dn_to_reflectance.

Assisted-by: Cursor:opus-4.8
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant