BookNotesSlidesP-sets Syllabusdraft · v0.2.49.1
◀▲⌂▶
💬Comments welcome. To leave a note, select any text and click the note / highlight button that pops up — or open the panel with the tab at the top-right (‹). Notes are visible only inside our private review group.
💡 In a hurry? Jump to this chapter’s 2 big lessons ↓

12.4 Feature tracking⧉

A feature track links one selected location across frames. A dense two-frame flow supplies many displacements but does not itself maintain long-term identities. Both dense and sparse trackers exist; density and temporal duration are separate design choices.

💡 Big lesson (L17, recurrence)

Tracking is a sparse correspondence. Where dense flow asks "where did every pixel go?", tracking asks "where did these few physical points go?" and threads the answer across a long sequence. It is the Lagrangian instance of correspondence (follow the particle, Chapter 30.1, Motion blur, temporal sampling, and resampling) to dense flow's Eulerian leaning (watch the fixed pixel). The sparse trajectories it produces are the raw input to everything downstream that needs the same physical points over time: video stabilization, structure-from-motion, simultaneous localization and mapping (SLAM), and bundle adjustment. The rest of the section is the mechanics of doing the sparse thing well, and the rest of the part is what those sparse tracks feed.

💡 Big lesson ((L7.6), recurrence)

A learned operator swaps a hand-designed prior for one learned from data. Classical KLT tracks a hand-built brightness patch by a hand-derived least-squares update. The modern point trackers at the end of this section keep the exact same task, "where did this point go?", but replace the patch with learned features and the greedy per-point update with a learned, occlusion-aware, track-together module. The skeleton is unchanged: initialize a trajectory, iteratively refine it against appearance, predict visibility; only the operator is now fit to data. The same neuralise-the-classical-iteration move powers RAFT in Chapter 12.8, Optical flow. (L7.6 first appears in Chapter 7.3, Deep learning; this is a recurrence.)

Before any of this was automatic it was done by hand, and the hand version is worth remembering because it is still in use. Rotoscoping, patented by Max Fleischer in 1915 to trace live-action footage frame by frame for animation, and reborn in visual effects as the painstaking craft of manually outlining and following an object across every frame of a shot, is tracking done by a human, one frame at a time. A rotoscope artist threads a boundary or a control point through a sequence exactly as a tracker does, but with judgment instead of a $2\times2$ matrix, which is why it is slow, expensive, and still the fallback whenever the automatic methods fail (hair, motion blur, transparency, a low-contrast edge). Everything in this section can be read as the long effort to automate the rotoscoper: detect the points worth following, follow them by brightness constancy, and flag where the tracker, like the artist, should not be trusted. Modern tools meet in the middle, with learned trackers and segmenters proposing the track and a human correcting only where they break.

12.4.1 Tracking vs dense flow: sparse-but-long vs dense-but-short⧉

Correspondence comes in two regimes, and the distinction organizes this whole part. Dense optical flow gives a two-dimensional motion vector at every pixel, that is, a full field, but typically only between two successive frames. Feature tracking gives motion at only a sparse set of points, but threads them across a long sequence. Dense-but-short versus sparse-but-long (Figure 12.4.1). Each is the right tool in a different regime: you reach for dense flow when you need motion everywhere (to warp a whole frame, interpolate slow motion, or segment by motion), and for tracking when you need the same physical points identified over a long clip.

Why go sparse at all? Two reasons, and they are the reasons tracking has never been retired. The first is scalability: a few hundred points carry far fewer unknowns than a million pixels. That mattered enormously when the field was young and it still matters today, for real-time SLAM and on-device tracking where you cannot afford a dense solve per frame. The second is deeper and is really about conditioning: some pixels are easier to track than others. Spend effort only where the answer is reliable, at a corner, and refuse to guess in flat regions or along edges, where the motion is ambiguous (the aperture problem you met in Chapter 12.8, Optical flow). In this light, tracking is dense flow's quality-controlled sibling: it reports motion only where it is confident, and stays silent where dense flow would be forced to hallucinate.

There is a useful label for what tracking does, made precise later in Chapter 30.1, Motion blur, temporal sampling, and resampling: tracking is the Lagrangian view of motion: "this physical point, where is it now?", following a particle through the scene. Dense per-pixel flow leans Eulerian: "at this fixed pixel, what moved through?" That Lagrangian/Eulerian dichotomy organizes the whole part, and tracking is its cleanest Lagrangian instance: it literally follows the patch rather than differentiating at a fixed location.

fig-track-vs-flow
Figure 12.4.1. Two regimes of correspondence, in one strip. Dense flow (left): a single pair of frames, with a colored two-dimensional motion vector at every pixel, dense but spanning only two frames. Tracking (right): a handful of marked points threaded as trajectories across many frames, sparse but long. The contrast is exactly dense-but-short versus sparse-but-long: where dense flow fills one gap completely, tracking carries a few reliable points across the whole clip.

12.4.2 KLT = Lucas–Kanade, per feature, iterated over time⧉

The workhorse of sparse tracking is the Kanade–Lucas–Tomasi (KLT) tracker, and its definition is simple: take the Lucas–Kanade patch solve from Chapter 12.8, Optical flow, apply it to one small window around a feature, iterate it to convergence, then repeat for the next frame. That is the entire algorithm. The lineage factors the credit cleanly: Lucas & Kanade (1981) gave the iterative registration update; Tomasi & Kanade 1991 packaged that update as a feature tracker; and Shi & Tomasi (1994) settled which features to track. KLT is the assembly of those three.

The per-patch system is pure reuse: it is Lucas–Kanade restricted to a window $W$, so we will set it up but not re-derive it. Assume every pixel in the window shares one displacement $\mathbf{u}=(u,v)$, and stack the brightness-constancy constraint $I_x u + I_y v + I_t = 0$ over all the pixels in $W$. That is an over-determined linear system in two unknowns, and its least-squares normal equations are

$$ M\,\Delta\mathbf{u} = \mathbf{b}, \qquad M=\sum_{\mathbf{x}\in W}\begin{pmatrix}I_x^2 & I_xI_y\\ I_xI_y & I_y^2\end{pmatrix}, \qquad \mathbf{b}=-\sum_{\mathbf{x}\in W} I_t\begin{pmatrix}I_x\\ I_y\end{pmatrix}, $$

so the displacement update is formally

$$ \Delta\mathbf{u} = M^{-1}\mathbf{b}. $$

Now look hard at that $2\times2$ matrix $M$. It is exactly the structure tensor of Harris Harris & Stephens 1988 (the second-moment matrix of gradient products, Gaussian-weighted over the window), the very object that classifies a patch as flat, edge, or corner in Chapter 12.3, Sparse matching. The same matrix, no new math. The whole detection apparatus from corner finding turns out to be precisely the apparatus the motion solve needs (Figure 12.4.2). Lucas–Kanade imposes a shared displacement over a window; Horn–Schunck Horn & Schunck 1981 instead couples a field through a smoothness penalty. They use the same brightness linearization but solve different models.

One iteration is not enough, and the reason is built into the derivation: brightness constancy was Taylor-linearized, so a single solve is only valid for sub-pixel motion. The fix is Gauss–Newton on the patch sum-of-squared-differences: warp the second-frame patch by the current estimate $\mathbf{u}$, recompute the temporal difference $I_t$ on the warped patch, re-solve for a residual $\Delta\mathbf{u}$, add it, and repeat until the residual is negligible (the inverse-compositional account of Baker & Matthews 2004 unifies the bookkeeping).

That handles small motion; real videos move farther than a pixel between frames. The standard remedy is the same coarse-to-fine logic as dense flow, now localized to each feature: run the iteration down a pyramid (Bouguet, 2000). Solve on a heavily blurred and downsampled level, where the displacement is small enough for the linearization to hold; propagate that estimate as the initial warp for the next-finer level; refine; repeat to full resolution.

Finally, thread it through time. Having localized the feature in frame $t{+}1$, use that location as the starting guess for frame $t{+}2$, and so on down the clip. KLT is this tiny $2\times2$ solve threaded greedily through the whole video, one small matrix inverse per feature per frame, almost free. Assembled, the three layers (iterate to converge, thread to the next frame) read as one short loop:

for each active feature with current position p:
    d = predicted displacement from this frame to the next
    repeat up to max_iterations:
        compare current-frame patch at p with next-frame patch at p+d
        use valid samples to build the consistent Jacobian and residual
        if information matrix is poorly constrained: mark lost; stop
        solve the small least-squares system for delta
        d = d + delta
        if delta is small and residual is acceptable: stop
    if fit passes visibility and consistency checks:
        p = p + d
        report position p and keep d as a motion prediction
    else:
        mark track lost; retain its history without inventing a position

But "greedy and memoryless" is exactly the property that makes drift and occlusion bite, as we will see two sections on.

fig-klt-is-harris
Figure 12.4.2. One matrix, two readings: the spine of the section. The same $2\times2$ structure-tensor ellipse (axes $\lambda_1,\lambda_2$) is drawn twice. Left, labeled "is this a good corner to detect?": the Harris reading: a round ellipse (two large eigenvalues) marks a corner. Right, labeled "is this patch's motion solve well-conditioned?": the KLT reading: the same round ellipse means $M^{-1}$ is stable and the displacement is determined in both directions. A degenerate ellipse (one tiny eigenvalue) is simultaneously "not a corner" and "not trackable." Detection and trackability are the same statement about $M$.

12.4.3 Good features to track = where the structure tensor is well-conditioned⧉

Here is the hinge the whole section turns on. The update $\Delta\mathbf{u}=M^{-1}\mathbf{b}$ is only meaningful when $M$ is invertible and well-conditioned, meaning you can actually trust the inverse. And the conditioning of $M$ is read straight off its eigenvalues $\lambda_1\ge\lambda_2$, the same eigenvalues that, in the Harris derivation, sort a patch into flat, edge, or corner. So the question "is this a good feature to track?" is literally "is the tracking matrix well-conditioned?", which is in turn literally "is this a corner?" One question in three forms.

Walk through the three cases. They are exactly the Harris partition, now read as trackability:

The selection rule falls right out of this. Shi & Tomasi's (1994) "good features to track" criterion is the simple, principled test

$$ \min(\lambda_1,\lambda_2) > \lambda_{\min}, $$

read as: require the smaller eigenvalue to be large enough, that is, demand that both directions be well-constrained, not just one. This is nothing but the conditioning of $M$ stated directly, and it is the same $\min(\lambda_1,\lambda_2)$ corner response cross-referenced in Chapter 12.3, Sparse matching. The Harris response $R=\det M - k\,(\operatorname{tr} M)^2$ is a determinant-and-trace proxy for the same quantity. What matters is the direction of the logic: the detector and the tracker agree by construction, because Shi & Tomasi derived "what makes a good feature to detect" from "what makes a good feature to track", not the other way round (Figure 12.4.3).

In practice the recipe is exactly the Harris pipeline: compute $M$ over a window at every pixel (image gradients → pointwise products → Gaussian-weighted sum), score each pixel by $\min(\lambda_1,\lambda_2)$, non-maximum-suppress, and keep the top-$N$ well-spread points. Those are the features you then KLT-track.

One real tension governs the window size, and it is the recurring small-versus-large-aperture trade of every patch method. A small window localises precisely but sees little texture, so $M$ is poorly conditioned and noise fools it. A large window is better conditioned but spans multiple motions: across a depth discontinuity or an object boundary, a single displacement $\mathbf{u}$ no longer fits the whole window. The sweet spot trades localization against conditioning.

fig-good-features-to-track
Figure 12.4.3. Shi–Tomasi points land only on corners. A photograph with selected features overlaid: markers cluster on corners and texture junctions, never on the flat sky or along a single straight edge (the railing, the roofline). A few windows are annotated with their local eigenvalue pair $(\lambda_1,\lambda_2)$: large-and-large at the kept corners, large-and-near-zero on the rejected edges, near-zero-and-near-zero on the rejected flat regions. The overlay makes "good feature to track" visibly identical to "well-conditioned $M$" identical to "corner."

The minimum eigenvalue measures the weakest direction's absolute information, while the eigenvalue ratio measures conditioning. They are related but different: a matrix proportional to the identity can be perfectly conditioned and still carry almost no information if both eigenvalues are tiny. With a fixed noise and derivative convention, a minimum-eigenvalue threshold rejects that case. Large values also do not rule out a wrong correspondence on repeated texture.

12.4.4 What breaks long-term tracking: drift, appearance change, occlusion, and re-detection⧉

A single small patch tracked greedily over a long video is not enough, and the failures are predictable. There are three of them, and one standard response.

Drift: the cost of greediness. Each frame's tiny Lucas–Kanade (LK) residual is imperfect, and chained over hundreds of frames the sub-pixel errors accumulate until the window has slowly slid off the true feature, so sub-pixel bias becomes whole pixels (Figure 4a). The classic remedy is to anchor to the first frame: match against the original template from frame $t_0$ rather than the previous frame, so the error does not compound. But anchoring trades one problem for another, because over a long clip the template goes stale.

Appearance change: the template goes stale. Over many frames the patch changes: lighting shifts, the object undergoes out-of-plane rotation, its scale changes as it nears or recedes, perspective skews it. A pure-translation match against a fixed template degrades. Shi & Tomasi's answer is a two-speed scheme: track plain translation frame-to-frame (small motion, cheap) but verify against the anchor template with an affine warp, allowing the patch to rotate, scale, and shear when you check whether it is still itself. The affine dissimilarity

$$ \varepsilon=\sum_{\mathbf{x}\in W}\big[\,I_2(A\mathbf{x}+\mathbf{d})-I_1(\mathbf{x})\,\big]^2 $$

measures how well the current patch still matches its origin under the best affine warp $A$ (with translation $\mathbf{d}$). A large $\varepsilon$ means the feature is no longer the thing you started tracking.

Occlusion or leaving the frame removes a visible correspondence. A shadow or specular change can instead break the appearance model while the surface point remains visible. In both cases a tracker may drift to an unrelated patch; use validity, residual and consistency checks rather than reporting every update as a valid track.

Re-detection: keep the set populated. As features bleed away to drift, occlusion, and leaving the frame, periodically re-run Shi–Tomasi and spawn fresh features in the gaps, so the tracked set stays dense enough and well-distributed (Figure 4c). Detection and tracking become an interleaved loop rather than a one-shot detect-then-track pipeline.

Step back and the plain limitation is clear, and it is what motivates the next section. Classical KLT is per-point, greedy, and forgetful: each feature is tracked independently, with no memory of where it went and no way to recover a point once it reappears from behind an occluder. Points also drift relative to one another, with no enforced global consistency. These are exactly the failures the learned trackers were built to fix.

fig-track-drift-occlusion
Figure 12.4.4. Three ways a long track fails, one point over a long clip. (a) Drift: the tracking window slowly slides off the true feature as per-frame sub-pixel errors accumulate. (b) Occlusion: the point passes behind an object; the dissimilarity $\varepsilon$ spikes, signaling "no valid match" → drop the feature rather than lock onto background texture. (c) Re-detection: a fresh Shi–Tomasi feature is spawned to replace a lost one, keeping the tracked set populated and well-spread. Together they show why a single greedy patch is not enough over a long sequence.

12.4.5 Modern point trackers (briefly)⧉

The failures of greedy KLT have a modern answer, and it is a clean instance of L7.6: the bitter-lesson successor keeps the task, "track this physical point through a video, even through occlusion," but replaces the hand-built brightness patch with learned features and the greedy per-frame solve with a learned iterative update, trained on synthetic video with ground-truth tracks. The classical scaffolding survives as architecture: a cost volume of appearance similarity, iterative refinement of the trajectory, and now an explicit visibility / occlusion prediction. The skeleton Lucas, Kanade, and Tomasi laid down is intact; only the operator is fit to data. We sketch the family here and leave the full models to Chapter 7.3, Deep learning.

Modern point trackers use temporal context, learned appearance and visibility estimates to reduce drift and reconnect tracks. Occlusion and out-of-domain motion remain difficult. Evaluate identity errors and recovery after occlusion, not just average position error on visible easy frames.

12.4.6 An application: synthetic motion blur from a track⧉

One application shows the mechanics directly (Figure 12.4.5). A long exposure that pans with a moving subject, leaving the subject sharp and the background streaked, is hard to capture in camera. But you can manufacture it from an ordinary short clip: track a point on the subject, stabilize every frame so that point holds still (translate each by the negative of the tracked displacement), and average the stabilized frames. Content whose image motion is well described by that same translation remains sharp; rotation, deformation or depth-dependent motion can still blur the subject; everything moving relative to it integrates into a streak, which gives the panning blur, anchored wherever you chose to track. It is the temporal mirror of denoising-by-averaging from Chapter 13.2, Denoising by averaging: there you align frames to remove the differences (noise); here you align to one point so the remaining motion is kept, as blur.

fig-track-motionblur
Figure 12.4.5. Synthetic motion blur by tracking (interactive). Click a point on the moving subject in the first frame; a small patch tracker follows it through the clip (the path is drawn), every frame is stabilized so that point stays fixed, and the stabilized frames are averaged. The tracked subject comes out sharp while everything moving relative to it streaks into motion blur, a panning long exposure built after the fact. It is an illustration rather than a calibrated exposure: the frames are averaged as encoded RGB, while a camera integrates linear light, so bright moving highlights streak more strongly in a real long exposure than here. A shutter slider sets how much of the clip the "exposure" integrates. Try the built-in pan and static clips, upload a video, or use your camera; track the subject for a blurred background, or click the background to blur the subject instead.

Recap: big lessons of this chapter

💡 Big lesson (L17, recurrence)

Tracking is a sparse correspondence. Where dense flow asks "where did every pixel go?", tracking asks "where did these few physical points go?" and threads the answer across a long sequence. It is the Lagrangian instance of correspondence (follow the particle, Chapter 30.1, Motion blur, temporal sampling, and resampling) to dense flow's Eulerian leaning (watch the fixed pixel). The sparse trajectories it produces are the raw input to everything downstream that needs the same physical points over time: video stabilization, structure-from-motion, simultaneous localization and mapping (SLAM), and bundle adjustment. The rest of the section is the mechanics of doing the sparse thing well, and the rest of the part is what those sparse tracks feed.

💡 Big lesson ((L7.6), recurrence)

A learned operator swaps a hand-designed prior for one learned from data. Classical KLT tracks a hand-built brightness patch by a hand-derived least-squares update. The modern point trackers at the end of this section keep the exact same task, "where did this point go?", but replace the patch with learned features and the greedy per-point update with a learned, occlusion-aware, track-together module. The skeleton is unchanged: initialize a trajectory, iteratively refine it against appearance, predict visibility; only the operator is now fit to data. The same neuralise-the-classical-iteration move powers RAFT in Chapter 12.8, Optical flow. (L7.6 first appears in Chapter 7.3, Deep learning; this is a recurrence.)