Skip to content

trafo: restore the x-only scx affine model; make the inverses exact - #230

Open
alexlib wants to merge 1 commit into
masterfrom
fix/brown-affine-scx-x-only
Open

alexlib wants to merge 1 commit into
masterfrom
fix/brown-affine-scx-x-only

Conversation

@alexlib

@alexlib alexlib commented Oct 9, 2026

Copy link
Copy Markdown
Contributor

Problem

Commit 8321647 (2025-03-21) rewrote distort_brown_affin so that scx scales both axes and the shear term:

*x1 = ap.scx * (x_dist - sin_she * y_dist);
*y1 = ap.scx * cos_she * y_dist;

Until then liboptv, like the Tcl/Tk 3DPTV trafo.c it was ported from, used:

*x1 = ap.scx * x - sin(ap.she) * y;
*y1 = cos(ap.she) * y;

scx is the x-only scale factor of the El-Hakim (1986) / Grün & Beyer (1986) affine pair. It compensated the pixel-clock vs. frame-grabber mismatch, which stretches only the scan-line direction. Applied to both axes it duplicates cc. orientation.c's Jacobian (∂y/∂scx = 0) still assumes the original model, so the fit and the projection disagree.

The same commit left correct_brown_affine_exact (used by dist_to_flat) inverting the original model. flat_to_dist → dist_to_flat therefore misses by 0.04 mm (≈ 8 px at 5 µm pixels) at scx = 1.02. Every round-trip test used scx = 1, where the two models coincide, so nothing caught it.

Change

  • Restore the original affine step in distort_brown_affin.
  • Make correct_brown_affin and correct_brown_affine_exact exact inverses of it, in both the initial guess and the iteration target.
  • Decentering stays evaluated at one undistorted point (Brown 1971), as in 8321647. The pre-2025 code updated x in place before the y line; that difference is second order.

Tests

check_trafo.c gains three tests:

  • affine_matches_3dptv: forward equals the Tcl/Tk formula.
  • scx_scales_x_only: changing scx leaves y unchanged.
  • affine_round_trip: scx = 1.02, she = 0.01, radial + decentering, through both correct_brown_affin and dist_to_flat.

Verified locally with a standalone harness compiled against trafo.c over 5 (scx, she) combinations × 3 points:

master this branch
forward vs. 3DPTV formula 0.04–0.06 mm off when scx ≠ 1 ≤ 9e-16
dist_to_flat round trip 0.04–0.06 mm 2e-13
failing cases 12 / 15 0 / 15

The check suite itself was not run locally (no check/cmake on this machine), so CI is the first full run. Note that tests/testing_fodder/track/cal/cam{1,2}.tif.addpar have scx ≈ 1.0016–1.0020. Their tracking expectations predate 2025, so they should agree with the restored model, but CI needs to confirm that.

The same fix is in openptv2 (fix/brown-affine-scx-x-only), where the formula was also inlined in the tracking kernels.

🤖 Generated with Claude Code

Commit 8321647 (2025-03-21) rewrote distort_brown_affin with scx applied
to both axes and to the shear term:
    x' = scx*(x_d - sin(she)*y_d),  y' = scx*cos(she)*y_d
Until then liboptv, like the Tcl/Tk 3DPTV trafo.c it was ported from, used
    x' = scx*x_d - sin(she)*y_d,    y' = cos(she)*y_d
scx is the x-only scale factor of the El-Hakim (1986) / Gruen & Beyer (1986)
affine pair; on both axes it duplicates cc. orientation.c's Jacobian
(dy/dscx = 0) still assumes the original model.

The same commit left correct_brown_affine_exact (used by dist_to_flat)
inverting the original model, so flat_to_dist -> dist_to_flat missed by
0.04 mm at scx = 1.02. Every round-trip test used scx = 1, where the two
models coincide.

Restored the original affine step and made correct_brown_affin and
correct_brown_affine_exact exact inverses of it. Decentering stays
evaluated at one undistorted point (Brown 1971), as in 8321647.

New tests in check_trafo.c: affine_matches_3dptv, scx_scales_x_only,
affine_round_trip (scx = 1.02, she = 0.01 with radial and decentering).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Copilot AI balanced review requested due to automatic review settings October 9, 2026 14:49

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟢 Approval recommended

The forward and inverse formulas are algebraically consistent and adequately covered by targeted tests.

0 open findings

What changed in this PR

Restores the x-only Brown affine scale model and aligns inverse transformations with it.

Changes:

  • Applies scx only to the x-coordinate.
  • Corrects both inverse affine calculations.
  • Adds regression and round-trip tests.
File Description
liboptv/​src/​trafo.c Restores and exactly inverts the original affine model.
liboptv/​tests/​check_trafo.c Adds affine-model and inverse regression tests.

🧠 Review effort: Balanced


💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

@alexlib

alexlib commented Oct 9, 2026

Copy link
Copy Markdown
Contributor Author

Local run of the full C test suite (all 17 check_*.c compiled against liboptv/src with a minimal stand-in for check.h; libcheck isn't installed here): master 84 ok / 3 fail, this branch 87 ok (+3 new trafo tests) / the same 3 fail (test_volumedimension, track3d_test_cavity, track3d_test_burgers, all already failing on master). In particular check_track, which uses the scx ≈ 1.0016–1.0020 cameras, passes with the restored model. CI with the real libcheck is still the authority.

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.

2 participants