fix(video): scale LK's temporal difference to the Scharr gain - #150
Merged
Merged
Conversation
- test_lk_single_newton_step_is_exact_on_quadratic_bowl: on the quadratic bowl translated by an integer (dx, dy), one Newton step over a symmetric window is exactly (dx, dy) in closed form (32 * 128 / 4096). It is currently (dx, dy) / 32 = 0.03125 px. - test_lk_recovers_subpixel_shift_with_opencv_defaults: a smooth texture shifted by (0.6, -0.4) must be recovered to within 0.02 px with OpenCV's calcOpticalFlowPyrLK defaults. It currently comes out as (0.35, -0.23). - test_lk_pure_translation_x: tighten the tolerance from +/-1.5 px to +/-0.05 px. The loose bound hid this bug (the estimate is 2.84 for a 3 px shift). README unit test count: 357 -> 359. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
H and b are built from Scharr derivatives, which are 32x the true image gradient, but the temporal difference It = I2 - I1 in b was the raw intensity difference. So H scaled by 32^2 and b by 32, and every Newton step H^-1 b was 1/32 of the true step: with OpenCV's defaults a clean 1 px shift was tracked as ~0.70 px. The tiny steps also tripped the oscillation fallback (#131) on ordinary same-direction steps. OpenCV keeps its window samples at x32 fixed-point (CV_DESCALE(..., W_BITS1-5)), so its diff carries the same factor and it cancels. Do the equivalent here: scale the accumulated mismatch vector b by SCHARR_GAIN = 32 in both the scalar and SIMD closures (scaling the sum once instead of It per sample). H is untouched, so min_eigen keeps its OpenCV-parity scale (#130, #138). Closes #149. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
PR Summary by QodoScale LK temporal differences to the Scharr derivative gain
AI Description
Diagram
High-Level Assessment
Files changed (3)
|
Code Review by Qodo🐞 Bugs (0) 📘 Rule violations (0) 📎 Requirement gaps (0)
Great, no issues found!Qodo reviewed your code and found no material issues that require reviewTip of the day💡 Did you know, you can keep summaries lean with Finding overflow, which tucks the rest behind 'View more' |
Merged
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #149.
Problem
Handbare built from Scharr derivatives, which are 32× the true image gradient. But the temporal differenceIt = I2 − I1inbwas the raw intensity difference, soH ∝ 32²whileb ∝ 32, and every Newton stepH⁻¹bwas 1/32 of the true step. With OpenCV'scalcOpticalFlowPyrLKdefaults, a clean 1 px shift was tracked as ≈0.70 px. The tiny steps also set off the oscillation fallback (#131) on ordinary same-direction steps, which stopped the solve early. Measurements are in #149.Fix
OpenCV keeps its window samples at ×32 fixed-point (
CV_DESCALE(..., W_BITS1-5)), so itsdiffcarries the same factor as the derivatives and the factors cancel. The equivalent here is a newSCHARR_GAIN = 32constant (documented next toFLT_SCALE) that scales the accumulated mismatch vectorb, in both the scalar and SIMD closures. Scaling the sum once is equivalent to scalingItin every sample, and costs 2 multiplies per iteration instead of one per sample.His untouched, somin_eigenkeeps its OpenCV-parity scale (#130, #138), andlk_iterate(with its synthetic-mismatch tests from #131) is unchanged.Behaviour change: every caller of
calc_optical_flow_pyramid_lknow gets noticeably different, correct flow estimates. It usually converges in fewer iterations, too.Tests
devtest_lk_single_newton_step_is_exact_on_quadratic_bowl: one Newton step on the quadratic bowl translated by (1,0), (0,−1), (2,1); exact closed form32·128/4096·(dx,dy) = (dx,dy)(dx,dy)/32, e.g. 0.03125test_lk_recovers_subpixel_shift_with_opencv_defaults: smooth texture shifted by (0.6, −0.4), 21×21 window, maxLevel 3, 30 iterations / 0.01, 1e-4; tolerance 0.02 pxtest_lk_pure_translation_x(existing): tolerance tightened from ±1.5 to ±0.05 px; the loose bound had hidden this bugResults are identical with and without
simd. No other test, example or doc tolerance needed retuning.Checks
cargo fmt --checkpasses.cargo clippy -D warningsis clean with default features,--no-default-featuresand--features simd,parallel.cargo test: 359 unit tests and 40 doc-tests; 415 withsimd,parallel; 359 with--no-default-features --features std.🤖 Generated with Claude Code