Skip to content
dnjulek edited this page Jul 7, 2026 · 2 revisions

Edge-directed interpolation, more than twice as fast as eedi3m.

EEDI3 reconstructs a missing field by finding, for each interpolated pixel, the best non-crossing warping between the neighbouring lines: a dynamic program minimises a cost built from neighbourhood similarity (alpha), the vertical difference the interpolation creates (beta), the interpolation direction's length (the remaining 1-alpha-beta), and the change in direction from pixel to pixel (gamma).

This plugin exposes two functions sharing one numeric core:

  • EEDI3 — interpolates rows (the classic vertical deinterlacer / doubler).
  • EEDI3H — interpolates columns (the horizontal axis). It runs the identical kernel on a transposed copy, so it is bit-exact to Transpose → EEDI3 → Transpose.

Usage

vszip.EEDI3(vnode clip, int field[, bint dh=False, float alpha=0.2, float beta=0.25, float gamma=20.0, int nrad=2, int mdis=20, bint hp=False, int vcheck=2, float vthresh0=32.0, float vthresh1=64.0, float vthresh2=4.0, vnode sclip=None, vnode mclip=None])
vszip.EEDI3H(vnode clip, int field[, ... same arguments ...])

Both functions take the same arguments. For EEDI3 the interpolated axis is the height; for EEDI3H it is the width (so the mod-2 / dh-doubling rules below apply to width instead of height).

Parameters

  • clip:
    8-16 bit integer or 32-bit float. Any planar/gray color family (Gray, YUV, RGB) is supported. Half precision float (GRAYH) and 17+ bit integer are rejected. Integer output keeps the input format; cubic overshoot is clamped to [0, peak] on store.
  • field: Mode of operation and which field is kept.
    • 0 = same rate, keep bottom field
    • 1 = same rate, keep top field
    • 2 = double rate (alternates each frame), starts with bottom
    • 3 = double rate (alternates each frame), starts with top
  • dh: (Default: False)
    Doubles the size of the input along the interpolated axis (height for EEDI3, width for EEDI3H): each input line is copied to every other output line and the gaps are interpolated. field must be 0 or 1 when dh=True.
  • alpha / beta / gamma: Cost weights. alpha and beta must each be in [0, 1] and alpha + beta ≤ 1; gamma ≥ 0. Larger alpha connects more lines/edges; larger beta connects fewer; the leftover weight favours shorter (more vertical) connections; larger gamma penalises direction changes (smoother field).
  • nrad: (Default: 2)
    Radius for the neighbourhood-similarity term. Range [0, 3].
  • mdis: (Default: 20)
    Maximum connection radius (search distance). Range [1, 40]. Larger connects shallower slopes but is slower and more artifact-prone.
  • hp: (Default: False)
    Use half-pel search steps (higher angular resolution, slower). Implemented here, unlike eedi3m where it is a no-op.
  • vcheck: (Default: 2)
    Reliability check on the interpolated result: 0 off, 1 weak, 2 medium, 3 strong. When > 0, inconsistent pixels are blended toward cint (a vertical-cubic value, or the matching sclip pixel if given).
  • vthresh0 / vthresh1 / vthresh2: (Default: 32.0 / 64.0 / 4.0)
    Thresholds for vcheck. Must be > 0 when vcheck > 0.
  • sclip: (Default: None)
    Optional "smooth clip" supplying the cint value for the vcheck blend. Must match the output format and dimensions.
  • mclip: (Default: None)
    Optional Gray mask: only nonzero pixels get the full edge-directed search; zero pixels get plain cubic interpolation (mainly a speed knob). Must match the clip's width/height/length. A non-8-bit gray mask is converted internally (full-range).

Examples

out = core.vszip.EEDI3(src, field=1)                       # deinterlace, keep top field
out = core.vszip.EEDI3(src, field=1, dh=True)              # double the height (anti-aliasing base)
out = core.vszip.EEDI3(src, field=0, nrad=3, mdis=40)      # stronger (slower) search
out = core.vszip.EEDI3H(src, field=1, dh=True)             # double the width
mask = src.std.Prewitt().std.Binarize(0.1)
out = core.vszip.EEDI3(src, field=1, mclip=mask)           # edge-directed only on mask

Differences from eedi3m

The interior matches, but a thin band of border pixels differs because the two compute edge connections differently (see below).

Edge-cost handling

  • eedi3m shrinks the search at the border. At pixel x it clamps the offset to umax = min(x, width − 1 − x, mdis), so near the left/right edge only short, fully in-bounds connections are even considered. Neighbourhood terms whose taps would land outside the frame are dropped from that pixel's cost (s1/s2 simply aren't added), and the DP backtrack applies a matching edge correction (pumax) so the chosen direction never points off-frame.

  • vszip evaluates the full range over a mirror-padded line. Each line is copied into a buffer with mdis-wide mirror-reflected margins on both sides, and the cost is computed for the entire [−mdis, +mdis] offset range at every pixel. Out-of-frame taps therefore read the mirror reflection instead of being skipped, and the per-pixel umax/pumax corrections are omitted. This keeps the SIMD inner loop uniform and branch-free (the main speed win) at the cost of slightly different — but still physically valid — connections within ~mdis pixels of the edge.

So the results agree everywhere except a mdis-wide strip at the relevant borders (left/right for EEDI3, top/bottom for EEDI3H), where eedi3m forces short edge-clamped connections and vszip allows longer, mirror-fed ones. In practice the disagreement is sparse and concentrated in the first few pixels.

TODO: Test other ways to fill padded borders.

Other differences

  • EEDI3H is new. eedi3m has no horizontal variant; you would transpose around EEDI3.
  • hp works. eedi3m documents hp as having no effect; here it performs real half-pel search.
  • mclip must be Gray and the single mask is applied to every processed plane. eedi3m requires a same-format mask and uses it per-plane. (With subsampled chroma the gray mask is at luma resolution; use it with gray/4:4:4 clips or luma-only processing.)
  • All planes are always processed. There is no planes argument; eedi3m's planes (which leaves unselected planes uninitialized) is dropped.
  • Removed arguments (vs eedi3m):
    • ucubic — always uses cubic 4-point interpolation (i.e. ucubic=True).
    • cost3 — always uses the 3-neighbourhood cost (i.e. cost3=True).
    • opt — CPU dispatch is automatic (portable Zig SIMD); there is no manual override.

TODO: improve mclip.

Clone this wiki locally