tb_lf_epi_slope — 2D typed op

• Data kinds: lightfield → image

• Call: fullseye.apply(img, "tb_lf_epi_slope", a=0.5, b=0.5) (the 2-D model is one image plus two scalar knobs a,b∈[0,1])

tb_lf_epi_slope: input → output

*The figure is the actual output on a synthetic 128×128 input. Left: input, right: output. Point clouds are drawn as a top-down scatter (brightness = z), 1-D series as a line plot, volumes as the maximum-intensity projection along z, videos as the middle frame, complex images as magnitude; return values that are not pictures are shown as the values themselves.*

*Knob a does not change the output (measured: identical at 0.1 / 0.5 / 0.9).*

*Knob b does not change the output (measured: identical at 0.1 / 0.5 / 0.9).*

Stages (the ops that come before → this op, left to right):

▸ tb_lf_epi_slope: stages (docs site)

On other images (synthetic scene / photo / coins. Top row: inputs, bottom row: their outputs. Knobs at default):

▸ tb_lf_epi_slope: other inputs (docs site)

Usage

Per-pixel slope from the EPI line orientation (structure tensor, one pass).

A scene point traces a straight line in the epipolar-plane image (lf_epi), so along that line the intensity is constant:

E_u + s * E_x = 0. Accumulating that constraint over the whole angular grid and a window x window spatial neighbourhood gives the closed-form least-squares slope s = -(J_ux + J_vy) / (J_xx + J_yy) with J_ab = sum(E_a * E_b) — one pass over the light field, no sweep, both the horizontal and vertical EPI directions pooled.

This estimator is biased, and the bias is the reason to also run lf_depth_from_focus. It is ordinary (not total) least squares on finite differences, so it needs the EPI line to advance less than roughly one texture correlation length per view. Measured 2026-09-01 on 5x5x64x64 synthetic fields, median over the interior: with texture sigma = 1.5 px, true +1.00 -> +1.0004, +0.50 -> +0.5285, +1.50 -> +1.3018, +2.00 -> +1.4614; with sigma = 5.0 px the same slopes give +1.0003, +0.5029, +1.4827, +1.9482. Integer slopes on a wrapped field come back within 4e-4 and s = 0 is exact; |s| > 1 is under-estimated, by 27% at s = 2 on the roughest texture. Use it as a fast dense initialiser, not as the final word.

Returns (slope_map, energy): the (H, W) slope map and the (H, W) gradient energy J_xx + J_yy that was the denominator. Pixels whose energy is below *min_energy* have no measurable parallax (a flat patch of sky); their slope is set to 0 and their energy reported as-is, so you threshold on energy instead of being handed a plausible-looking number divided by ~0.

Raises ValueError: *lf* not a valid light field, an angular/spatial shape where *neither* EPI direction carries information (the horizontal EPI needs U >= 2 and W >= 2, the vertical needs V >= 2 and H >= 2), an even or non-positive *window*, a non-positive *min_energy*.

Typed bridge of the lightfield op lf_epi_slope into the 2-D evolution registry: the same implementation, called under the op(v, a, b) convention. This op has no tunable parameter; a and b are unused.

References (sample data, literature)

• Sample-data catalog (download URLs / licences) — 2-D uses skimage.data (BSD/public domain) plus synthetic images; 3-D lists download URLs for real data sources (Stanford, PDS, …).

• Operator provenance and references — the sources of the research/methods this op family came from.

Try it in Studio

The program below has been verified to run (same input as the figure). In Studio's help this block becomes buttons that load and run it on the spot.

img_to_lightfield 0.50 0.50
tb_lf_epi_slope 0.50 0.50

▸ Load this pipeline  ·  Load & run

Runnable examples (verified samples that actually call this op)

The examples below call the underlying ledger op lf_epi_slope. This bridge op is the same implementation adapted to the fn(v, a, b) convention, so the behaviour carries over unchanged (only the call form differs).

• lightfield_depth — py -3.11 examples/lightfield_depth.py

• poc_lightfield_depth — py -3.11 examples/poc_lightfield_depth.py

Ops the type connects to (they accept image as input)

identity · gaussian · mean_box · bilateral · unsharp · median · min_filter · max_filter

Same category (typed)

tb_points_to_voxel · tb_estimate_point_normals · tb_iss_keypoints · tb_project_points · tb_render_point_depth · tb_statistical_outlier_removal · tb_radius_outlier_removal · tb_voxel_grid_downsample


*Provenance: ops.py — 2D operator registry. This per-op note is generated by tools/opdocs.py md (do not hand-edit).*

© 2026 Kazufumi Furuse — Fullseye operator documentation. Licensed under Apache-2.0.