Task 4 of 5
Task 3 returned a number for every pixel it could solve, and some of those numbers
are worthless. A useful tracker does not just answer — it says how much it should be
believed, and the material for that is already sitting in the window matrix
M = [[Sxx, Sxy], [Sxy, Syy]].
M's two eigenvalues measure how much intensity change the window sees in its
two principal directions. Three cases, and they are the whole story:
λmax λmin verdict
flat ≈ 0 ≈ 0 nothing to track
edge large ≈ 0 one direction
corner large large trustworthy
The middle row is task 2's aperture problem wearing a matrix. So the smaller eigenvalue is the confidence score: it is large only when the window is pinned down in both directions. That measure has a name — it is the Shi–Tomasi score, and thresholding it is literally what "good features to track" means. A 2×2 symmetric matrix has a closed-form spectrum, so this is one line of arithmetic, not an eigensolver:
trace = Sxx + Syy
det = Sxx·Syy − Sxy²
λmin = (trace − √(trace² − 4·det)) / 2
Sxx, Sxy, Syyoutput: [64, 64], one number per pixel(trace - Math.sqrt(disc)) / 2, where disc = trace * trace - 4 * detMath.max(0, …) before the square root — it is mathematically non-negative, but float32 rounding can nudge it below zero and hand you a NaNIt plays no part here. Confidence is a property of the
window, not of the motion — you can decide a pixel is untrackable before you
look at the second frame at all.
const trace = sxx + syy;
const det = sxx * syy - sxy * sxy;
const disc = Math.max(0, trace * trace - 4 * det);
return (trace - Math.sqrt(disc)) / 2;
Both roots share everything but a sign; + gives the larger eigenvalue,
− the smaller. Take the smaller one — an edge scores well on the larger and
is exactly what you are trying to reject.
λmin, Harris takes det − k·trace² to dodge the
square root, FAST skips the matrix entirely and tests a pixel ring instead. OpenCV's
goodFeaturesToTrack, the corner pass in ARKit and ARCore, and every
visual-odometry front end run this per-pixel score and then keep the local maxima — which is
a reduction, then a compaction, over a map you just computed in one launch.
This page is an interactive exercise — the editor, the GPU runner and your saved progress need JavaScript. The text above is the full brief.