Horn-Schunck Optical Flow | VitaVision
Back to atlas

Horn-Schunck Optical Flow

4 min readIntermediateView in graph
Based on
Determining optical flow
Horn, Schunck · Artificial Intelligence 1981
DOI ↗

Goal

Compute a dense 2-D optical flow field (u,v)(u, v) at every pixel from two consecutive grayscale frames. Input: a spatiotemporal brightness function E(x,y,t)E(x, y, t) sampled on a square spatial grid with uniform time steps. Output: velocity vectors (ui,j,vi,j)(u_{i,j}, v_{i,j}) in units of picture cells per frame interval at every pixel. The algorithm minimises a global variational energy combining a brightness-constancy fit term with a spatial smoothness penalty on the flow field, then recovers the flow via per-pixel Gauss-Seidel relaxation. The smoothness prior resolves the aperture problem — one brightness-constancy equation, two unknowns per pixel — by propagating boundary velocity information across uniform regions through iterated local averaging.

Algorithm

Let E(x,y,t)E(x, y, t) denote the image brightness function on the spatiotemporal domain. Let Ex,Ey,EtE_x, E_y, E_t denote the partial derivatives of EE with respect to xx, yy, and tt. Let (u,v)(u, v) denote the 2-D optical flow velocity at a pixel. Let α2\alpha^2 denote the smoothness weight balancing fit against regularisation. Let uˉ,vˉ\bar{u}, \bar{v} denote the weighted local averages of uu and vv over the 3×3 neighbourhood stencil. Let KK denote the proportionality factor in the discrete Laplacian approximation.

Definition
Brightness-constancy equation

Linear constraint on (u,v)(u, v) derived from the assumption that image brightness is conserved along a moving point's trajectory.

Exu+Eyv+Et=0.E_x u + E_y v + E_t = 0.
Definition
Variational energy

Global functional minimised over the full image domain, combining the squared brightness-constancy residual Eb\mathcal{E}_b with the squared flow-gradient magnitude Es\mathcal{E}_s.

E2= ⁣(α2Eb2+Es2)dxdy,\mathcal{E}^2 = \iint \!\left(\alpha^2 \mathcal{E}_b^2 + \mathcal{E}_s^2\right) dx\, dy,

where Eb=Exu+Eyv+Et\mathcal{E}_b = E_x u + E_y v + E_t and Es=u2+v2\mathcal{E}_s = \|\nabla u\|^2 + \|\nabla v\|^2.

Definition
Discrete Laplacian approximation

Finite-difference form of the Laplacian used after the Euler-Lagrange equations are discretised on the pixel grid.

2uK(uˉu),K=3,\nabla^2 u \approx K(\bar{u} - u), \quad K = 3,

where K=3K = 3 corresponds to the weighted 3×3 stencil with unit grid spacing.

Definition
Iterative flow update

Per-pixel closed-form relaxation step obtained by substituting the discrete Laplacian approximation into the Euler-Lagrange equations.

un+1=uˉnEx ⁣(Exuˉn+Eyvˉn+Et)α2+Ex2+Ey2,vn+1=vˉnEy ⁣(Exuˉn+Eyvˉn+Et)α2+Ex2+Ey2.\begin{aligned} u^{n+1} &= \bar{u}^n - \frac{E_x\!\left(E_x \bar{u}^n + E_y \bar{v}^n + E_t\right)}{\alpha^2 + E_x^2 + E_y^2}, \\[4pt] v^{n+1} &= \bar{v}^n - \frac{E_y\!\left(E_x \bar{u}^n + E_y \bar{v}^n + E_t\right)}{\alpha^2 + E_x^2 + E_y^2}. \end{aligned}

Procedure

Algorithm
Horn-Schunck dense flow
Input: Two consecutive grayscale frames; smoothness weight α2\alpha^2; iteration count NN or convergence tolerance.
Output: Dense flow field (ui,j,vi,j)(u_{i,j}, v_{i,j}) at every pixel, in picture cells per frame interval.
  1. Estimate ExE_x, EyE_y, and EtE_t at every pixel as the average of four first differences along the four parallel edges of the 2×2×2 spatiotemporal cube formed by adjacent frame samples.
  2. Initialise u0=v0=0u^0 = v^0 = 0 at every pixel.
  3. Compute the weighted local averages uˉn\bar{u}^n and vˉn\bar{v}^n at every pixel using the 3×3 stencil.
  4. Apply the iterative flow update at every pixel to obtain un+1u^{n+1} and vn+1v^{n+1}.
  5. At image boundary pixels, copy velocities from the adjacent interior pixel to enforce zero normal derivative.
  6. Repeat steps 3–5 until the maximum per-pixel change falls below the tolerance or the iteration count reaches NN.

Implementation

The per-pixel flow update in Rust, showing the weighted local-average helper and the inner iteration kernel:

/// Weighted 3×3 local average for one flow component (u or v).
/// Stencil: cardinal neighbours weight 1/6, diagonal neighbours weight 1/12.
fn local_avg(flow: &[f32], w: usize, h: usize, x: usize, y: usize) -> f32 {
    let get = |cx: usize, cy: usize| flow[cy * w + cx];
    let xm = x.saturating_sub(1);
    let xp = (x + 1).min(w - 1);
    let ym = y.saturating_sub(1);
    let yp = (y + 1).min(h - 1);
    (1.0 / 6.0)  * (get(xm, y) + get(xp, y) + get(x, ym) + get(x, yp))
    + (1.0 / 12.0) * (get(xm, ym) + get(xp, ym) + get(xm, yp) + get(xp, yp))
}

/// One Horn-Schunck iteration over all pixels.
/// ex, ey, et: spatial–temporal gradient images (row-major, w×h).
/// u, v:       current flow (read); u_new, v_new: updated flow (write).
fn hs_iteration(
    ex: &[f32], ey: &[f32], et: &[f32],
    u: &[f32], v: &[f32],
    u_new: &mut [f32], v_new: &mut [f32],
    w: usize, h: usize, alpha2: f32,
) {
    for y in 0..h {
        for x in 0..w {
            let i = y * w + x;
            let u_bar = local_avg(u, w, h, x, y);
            let v_bar = local_avg(v, w, h, x, y);
            let num = ex[i] * u_bar + ey[i] * v_bar + et[i];
            let denom = alpha2 + ex[i] * ex[i] + ey[i] * ey[i];
            u_new[i] = u_bar - ex[i] * num / denom;
            v_new[i] = v_bar - ey[i] * num / denom;
        }
    }
}

Vectorised one iteration in Python:

import numpy as np

def hs_iteration(ex, ey, et, u, v, alpha2: float):
    """One Horn-Schunck iteration. Arrays are 2-D float32, shape (H, W)."""
    u_bar = (
        (np.roll(u, 1, 1) + np.roll(u, -1, 1)
         + np.roll(u, 1, 0) + np.roll(u, -1, 0)) / 6.0
        + (np.roll(np.roll(u, 1, 0), 1, 1) + np.roll(np.roll(u, 1, 0), -1, 1)
           + np.roll(np.roll(u, -1, 0), 1, 1) + np.roll(np.roll(u, -1, 0), -1, 1)) / 12.0
    )
    v_bar = (
        (np.roll(v, 1, 1) + np.roll(v, -1, 1)
         + np.roll(v, 1, 0) + np.roll(v, -1, 0)) / 6.0
        + (np.roll(np.roll(v, 1, 0), 1, 1) + np.roll(np.roll(v, 1, 0), -1, 1)
           + np.roll(np.roll(v, -1, 0), 1, 1) + np.roll(np.roll(v, -1, 0), -1, 1)) / 12.0
    )
    num   = ex * u_bar + ey * v_bar + et
    denom = alpha2 + ex**2 + ey**2
    return u_bar - ex * num / denom, v_bar - ey * num / denom

Remarks

  • Per-iteration cost is O(NM)O(NM) on an N×MN \times M image; total iteration count must exceed the diameter of the largest uniform region in pixels, which sets the effective spatial range of information propagation.
  • α2\alpha^2 trades fit against smoothness; the paper recommends setting it comparable to the expected noise in Ex2+Ey2E_x^2 + E_y^2. Low α2\alpha^2 preserves gradients but destabilises uniform regions; high α2\alpha^2 over-smooths discontinuities.
  • Global smoothness over-regularises flow at occlusion boundaries; the worst errors occur there and shrink with image resolution but are never eliminated.
  • Brightness constancy fails under illumination changes, specular reflection, and non-Lambertian shading; the failure is silent — incorrect EtE_t produces systematically wrong flow without any signal of error.
  • The linearised brightness constraint is valid only for sub-pixel to approximately 1-pixel inter-frame displacement; coarse-to-fine Gaussian pyramids extend the operating range.
  • The Jacobi update structure is parallel-safe: each pixel's new estimate depends only on the previous iteration's local averages, not on the same pixel's previous value.
  • Replacing the quadratic Eb2\mathcal{E}_b^2 and Es2\mathcal{E}_s^2 terms with redescending M-estimators (Lorentzian or Geman-McClure) yields a piecewise-smooth robust variant that tolerates motion discontinuities and brightness-constancy outliers without an explicit line-process layer; see black-anandan-robust-flow. Robustifying the smoothness term alone is insufficient — the data term must also be robust.

References

  1. B. K. P. Horn, B. G. Schunck. Determining Optical Flow. Artificial Intelligence 17 (1–3), 1981. hdl.handle.net/1721.1/6337
  2. B. D. Lucas, T. Kanade. An Iterative Image Registration Technique with an Application to Stereo Vision. IJCAI, 1981. ri.cmu.edu

Parallel foundation with

  • Lucas-Kanade Image Registration

    Dense variational vs sparse local LSQ — co-founded optical flow in 1981; pick by problem (dense flow field vs sparse displacement of features).

Extended by

  • Black-Anandan Robust Optical Flow

    Robust M-estimator extension of the quadratic data and smoothness terms; non-convex but more tolerant of outliers and motion discontinuities.