scene_flow_lk — 3D motion op

• Data kinds: voxel × voxel → flow_dense

• Call: import fullseye as fs; fs.ledger.scene_flow_lk(vol0, vol1, device='cpu', win=3, levels=3, iters=3, reg=0.001) (to call the implementation directly, import match3d; match3d.scene_flow_lk(vol0, vol1, device='cpu', win=3, levels=3, iters=3, reg=0.001); from the registry, ops3d.get("scene_flow_lk"))

• GPU: this op has a GPU path (device="cuda")

Usage

Lucas-Kanade scene flow (the 3-D version of 2-D optical flow). A per-voxel motion field d = (dz, dy, dx).

Unlike template matching, this estimates dense motion/deformation. Brightness constancy ∇I·d + I_t = 0 is solved by least squares within a window per voxel (3x3 structure tensor A=Σ∇I∇Iᵀ, b=-Σ∇I·I_t, via window sums with conv3d). Coarse-to-fine with pyramid + warp handles large displacements (at each level I1 is warped back by the current flow and the residual is corrected iteratively = Gauss-Newton). vol1(x) ≈ vol0(x - d). Returns flow (3,D,H,W). Captures translation, expansion (divergence) and rotation (vortex) fields.

Measured: a uniform translation [1.5,-2,1] has an error of 0.044 voxel averaged over the central region; for an expansion field the outward divergence is detected correctly. grad_scale=32 is the measured scale of sobel3d (deriv[-1,0,1]×smooth[1,2,1]²). GPU-capable (all conv3d).

Arguments: vol0, vol1 are 3-D arrays of the same shape (otherwise ValueError; NaN/Inf or float32 overflow also give ValueError). win is the half-width of the window (side length of the window 2·win+1), levels is the number of pyramid levels (each level is reduced 2× by avg_pool3d; use more levels for larger displacements), iters is the number of warp iterations per level, reg is the regularisation added to the structure tensor diagonal (avoids division by 0 in flat regions; the larger it is, the more the flow in flat regions is pulled toward 0). The update of 1 iteration is clamped to ±2 voxel per axis. Returns (3, D, H, W) float32 numpy, flow[0]=dz, flow[1]=dy, flow[2]=dx (in voxels). vol1(x) ≈ vol0(x − d), i.e. the structure of vol0 moves by +d to become vol1. Borders are filled by border interpolation, so do not trust the values within 1-2 voxel of the border. If you want R,t of a rigid motion, convert to point clouds and use icp_point2point_3d / fit_rigid.

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.

Runnable examples (verified samples that actually call this op)

• motion_scene — py -3.11 examples_3d/motion_scene.py

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

fuse_to_voxel

Same category (motion)

—


*Provenance: match3d.py — 3D 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.