Pure-Rust single-frame image filters for the oxideav framework — codec, container, and filter crates are implemented from the spec (no C codec libraries linked or wrapped, no *-sys crates).
This crate specialises in stateless per-frame effects — there is no frame-to-frame context. That's what separates it from sibling crates:
oxideav-audio-filtercarries streaming state (echo, resample).oxideav-pixfmtdoes colour-space conversion / palette / dither.oxideav-image-filter(this crate) takes aVideoFrameand returns a newVideoFrame— same filter, same input → same output.
[dependencies]
oxideav-core = "0.1"
oxideav-image-filter = "0.0"Separable Gaussian blur with configurable radius + sigma and an optional plane selector:
use oxideav_image_filter::{Blur, ImageFilter, Planes};
// Blur all planes with radius 3, sigma 1.5 (default).
let f = Blur::new(3).with_sigma(1.5);
let out = f.apply(&input_frame)?;
// Blur only the luma plane — leave chroma untouched.
let f = Blur::new(5).with_sigma(2.0).with_planes(Planes::Luma);3×3 Sobel edge-magnitude; output is a Gray8 frame of the same size:
use oxideav_image_filter::{Edge, ImageFilter};
let edges = Edge::new().apply(&input_frame)?;For Rgb24 / Rgba input the filter first computes a luma proxy
Y = (R + 2G + B) / 4 and applies Sobel to it. YUV inputs use plane 0
directly.
Rescale to arbitrary dimensions, nearest-neighbour or bilinear:
use oxideav_image_filter::{Interpolation, ImageFilter, Resize};
let half = Resize::new(input.width / 2, input.height / 2)
.with_interpolation(Interpolation::Bilinear)
.apply(&input)?;For planar YUV the chroma planes are resized to the matching subsampled output dimensions (e.g. 4:2:0 halves both chroma axes).
Flip— mirror vertically (top ↔ bottom).Flop— mirror horizontally (left ↔ right).Rotate— arbitrary-degree rotation with bilinear resampling and canvas grow; configurable background colour. 90°-multiples use an exact axis-aligned fast path. IM:-rotate N.Crop—(x, y, width, height)subregion extraction; chroma rects are ceil/floor-aligned for YUV subsampling. IM:-crop WxH+X+Y.Roll— circular pixel shift(dx, dy); rows / columns wrap around the borders. Chroma offsets on planar YUV are scaled by the subsampling factor so the visible image translates as a rigid block. IM:-roll +X+Y.Shave— strip a uniform(x_border, y_border)margin off every edge (centred crop). Backed byCropso YUV chroma alignment matches. IM:-shave XxY.Extent— set the output canvas to a fixed(width, height)with a placement offset, padding the gaps with a configurable background colour. Negative offsets translate the input toward the upper-left so its right / bottom edge can land inside the window. IM:-extent WxH+X+Y.Trim— auto-crop to the bounding box of pixels that differ from a reference background colour by more thanfuzzper channel (Chebyshev / L-infinity tolerance). Background defaults to the source's(0, 0)pixel. IM:-fuzz N% -trim.AutoTrim— likeTrim, but picks the dominant background colour via a 4-bit-per-channel histogram vote across the whole frame instead of trusting the(0, 0)corner sample. Noisy corners no longer poison the inferred background.GravityTranslate+Gravity— 9-point compass anchor placement on a fixed(width, height)canvas (NorthWest/North/NorthEast/West/Centre/East/SouthWest/South/SouthEast). Backed byExtentso YUV chroma subsampling alignment matches. Factory aliases:gravity-translate,gravity. IM:-gravity <anchor> -extent WxH.
Negate— invert pixel values. On YUV inverts only Y so chroma (hue/saturation) is preserved. IM:-negate.Threshold— binarise at a per-channel threshold; chroma → neutral 128 for YUV. IM:-threshold N.Gamma— power-law gamma correction. IM:-gamma G.BrightnessContrast— linear brightness + contrast in the IM range−100..100. IM:-brightness-contrast BxC.Level— remap[black, white]to[0, 255]with optional mid-tone gamma. IM:-level LOW,MID,HIGH.Normalize— two-pass auto-level stretch to use the full range. IM:-normalize.Posterize— reduce each channel toNintensity levels. IM:-posterize N.Solarize— invert pixels above a threshold. IM:-solarize N%.AdaptiveThreshold— local-mean-based binarisation with a configurable(2*radius+1)²window and signed offset; box-sum implementation isO(W*H)regardless of radius. Always emitsGray8(RGB collapsed via Rec. 601 luma). IM:-threshold local.Equalize— per-channel histogram equalisation via CDF mapping. Luma-only on YUV. IM:-equalize.AutoGamma— pick a per-channel gamma so the geometric mean lands at mid-grey 0.5 (gamma = log(mean) / log(0.5)). IM:-auto-gamma.SigmoidalContrast— sigmoid-curve contrast around a midpoint. IM:-sigmoidal-contrast CxM%.Exposure— EV-stop adjustment in linear-light space (sRGB → linear → ×2^EV → sRGB).+1.0EV doubles the captured light,-1.0EV halves it. RGB / RGBA / Gray8. IM analogue:-colorspace RGB -evaluate Multiply k -colorspace sRGBfolded into one LUT pass.ShadowHighlight— independent shadow lift + highlight recovery gated by a soft tonal mask (peaks at the extremes, zero at midtone). Mid-grey passes through unchanged. RGB / RGBA / Gray8 / YUV (luma only). Lr-style "Shadows" / "Highlights" sliders.Evaluate+EvaluateOp— per-pixel arithmetic LUT with a single scalar operand. Operators:Add,Subtract,Multiply,Divide,Pow,Max,Min,Set,And,Or,Xor,Threshold. Alpha (RGBA) and chroma (YUV) pass through. IM:-evaluate <op> N.Cycle— modular per-channel value rotation (out = (src + amount) mod 256); alpha and chroma preserved. IM analogue:-cycle N.Statistic+StatisticOp— rolling-window per-pixelMedian/Min/Max/Meanover aWxHneighbourhood (border-clamped). 1×1 window is identity. IM:-statistic <op> WxH.Clamp— clamp every tone sample into[low, high]. Alpha preserved on RGBA; YUV touches only luma. IM:-clamp(extended with explicit endpoints).AutoLevel— per-channel auto-stretch: independently fill[0, 255]for each of R / G / B (RGB / RGBA). IM:-auto-level.ContrastStretch— burn the darkestblack%and brightestwhite%of pixels then linearly stretch the rest, per channel for RGB. IM:-contrast-stretch black%xwhite%.LinearStretch— likeContrastStretchbut cut-offs are absolute pixel counts, not fractions. IM:-linear-stretch black-pixels{xwhite-pixels}.Function+FunctionOp— per-pixel mathematical-function map evaluated in normalised[0, 1]space. Operators:Polynomial(Horner-rule on descending coefficients),Sinusoid(bias + amp · sin(2π · (freq · x + phase / 360))),ArcSin,ArcTan. IM:-function <kind> args.
Modulate— brightness / saturation / hue via HSL round-trip. RGB / RGBA only (YUV returnsUnsupported). IM:-modulate B,S,H.Sepia— sepia-tone matrix with optionalthresholdmix back to grayscale. IM:-sepia-tone N%.Grayscale— Rec. 601 desaturate; optionalGray8collapse. IM:-colorspace Gray.Colorize— linear blend toward a target[R, G, B, A]colour by a0.0..=1.0amount. IM:-colorize N%.Tint— luminance-weighted blend toward a target colour; bright pixels reach the target, dark pixels stay put. IM:-tint N.Vignette— Gaussian radial darkening centred at(x*w, y*h)withradius+sigma. IM:-vignette RxS{+x{+y}}.ColorMatrix— 3×3 colour matrix with optional 3-vector offset; equivalent of an affine 3×4 matrix on R / G / B. RGB / RGBA only (YUV →Unsupported). IM:-color-matrix matrix,-recolor matrix.BlueShift— moonlight / scotopic-vision tint: per-pixel(min/factor, min/factor, max/factor). IM:+blue-shift factor.Quantize— uniform-grid colour quantizer: round each channel to one ofcbrt(N)evenly-spaced palette entries. IM:-colors N(uniform-cube variant).Frame— decorative bordered frame with a 3-D bevel (highlight on top / left, shadow on bottom / right). RGB / RGBA only. IM:-frame WxH+inner+outer-mat.HslRotate— rotate the hue channel by N degrees in HSL space (RGB ⇒ HSL ⇒ rotate ⇒ RGBround-trip). Achromatic greys (R = G = B) are passed through unchanged. RGB / RGBA only.ChannelMixer— 4×4 linear combination of source channels into destination channels (plus a 4-vector offset). Super-set ofColorMatrixbecause it also touches the alpha row. Convenience constructors:identity(),sepia_classic(),from_color_matrix(m). RGB / RGBA only.ChromaticAberration— per-channel pixel offset on R and B with G undisturbed; simulates lateral lens chromatic aberration. Convenience constructorshorizontal(n)/vertical(n)plus explicit(r_dx, r_dy, b_dx, b_dy)overrides. RGB / RGBA only.VignetteSoft— raised-cosine soft vignette with separate inner / outer normalised radii. Smoother seam than the Gaussian [Vignette] because both endpoints have zero derivative. RGB / RGBA only.HslShift— independent additive shifts for H (degrees), S, and L (each in[-1, 1]) via the same RGB ⇄ HSL round-trip as [HslRotate]. Achromatic greys keep their hue=undefined semantics — only the L shift moves them. Alpha pass-through; RGB / RGBA only.ColorBalance— three-way ASC CDL-style per-channellift/gamma/gain(out = ((in × gain) + lift)^(1/gamma)) folded into a single 256-entry per-channel LUT so the cost isO(W·H)regardless of how many adjustments are enabled. Alpha pass-through; RGB / RGBA / Gray8.Canvas— constant-colour generator: every output sample is the configured[R, G, B, A]. YUV chroma planes are painted neutral 128 (matching IM's behaviour when an RGB colour lands on a YUV pipeline). Useful as a pipeline-head generator (a flat backdrop forcomposite-over) or as a wipe step.GradientRadial— radial gradient generator: per-pixel distance from(centre_x · w, centre_y · h)is normalised againstradius(defaults to half-diagonal), then linearly interpolated between aninnerandoutercolour per channel. Gray8 / RGB / RGBA. Factory alias:radial-gradient.GradientConic— angular sweep generator parameterised by(centre_x, centre_y)+start_angle(degrees).t = 0lies along the angle ray and increases counter-clockwise round to 1. Gray8 / RGB / RGBA. Factory alias:conic-gradient.Temperature— warmth slider in[-1, 1]biasing the R / B channel multipliers (up to ±50 % at the extremes); G and alpha pass through. Per-channel 256-entry LUT,O(W·H)regardless ofwarmth. RGB / RGBA.Vibrance— Lr-style saturation boost that spares already- saturated pixels via1 - sper-pixel weighting (vsModulatewhich scalesSuniformly). RGB / RGBA only.BwMix— black-and-white conversion with per-channel weights. Convenience constructorsred_filter()/green_filter()/blue_filter(). Output defaults toGray8; opt intokeep_formatto emit grey-equalled RGB / RGBA. RGB / RGBA only.BorderedFrame— flat solid-coloured border with independent per-side widths. Distinct fromFrame, which paints a 3-D bevel. Gray8 / RGB / RGBA. IM analogue:-bordercolor C -border WxH.DropShadow— soft offset shadow composited behind opaque RGBA subject pixels. Configurable(offset_x, offset_y), blurradius,opacity, andcolour. Box-blur approximates Gaussian for cheap soft halos. RGBA only.InnerShadow— soft offset shadow rendered inside the subject coverage instead of behind it. Builds the shadow mask from the inverted alpha channel + offset + box-blur, then darkens interior pixels towardcolourweighted byopacity. RGBA only.Bloom— Gaussian-blurred highlights additively composited on top of the source for an emissive / HDR-look glow. Configurablethreshold(luma cut-off) +radius+intensity. Gray8 / RGB / RGBA. Factory alias:glow.Heatmap+HeatmapRamp— false-colour ramp applied to a luminance-reduced source. Built-in rampsJet/Viridis/Plasma/Hot/Cool/Grayscale. Input may be Gray8 / RGB / RGBA / planar YUV; output is RGB24 (or RGBA when input was RGBA, with alpha preserved). Factory alias:false-color.SplitTone— independent shadow + highlight tints driven by a triangular tonal maskw(v) = (1 - 2|v - 0.5|).max(0).balancein[-1, 1]biases shadow vs highlight emphasis;amountis the global strength. RGB / RGBA only.PosterizeChannels— per-channel posterise with independentr_levels/g_levels/b_levels(and optionalalpha_levels). Distinct from the existingPosterizewhich collapses every channel to the same level count. Gray8 / RGB / RGBA.Toon— cel-shaded cartoon look: per-channel posterise + Sobel edge overlay with configurable ink colour. Configurablelevels/edge_threshold/edge_color. RGB / RGBA only. Factory alias:cartoon.FloodFill— seeded scanline flood-fill with per-channel Chebyshev tolerance (matching ImageMagick's-fuzz). Iterative span-fill keeps the workO(W·H); out-of-bounds seeds error. Gray8 / RGB / RGBA.
BilateralBlur— edge-preserving Gaussian blur: each tap is weighted by both spatial proximity (sigma_spatial) and intensity proximity (sigma_range), so step edges survive a heavy smoothing pass. Border samples clamp to the nearest in-bounds coordinate; alpha pass-through on RGBA. Gray8 / RGB / RGBA. IM analogue:-define blur:bilateral=1on the Gaussian blur path.Sharpen— unsharp-mask (Gaussian-blurred subtract + re-addition). IM:-sharpen RxS.Unsharp— explicit unsharp-mask withthresholdgate: only high-contrast regions are sharpened. IM:-unsharp RxS+A+T.Clarity— large-radius unsharp-mask preset for mid-frequency tonal pop ("Lightroom Clarity" slider). Defaults:radius = 30,sigma = 15.0,amount = 0.5,threshold = 10. IM analogue:-unsharp 30x15+0.5+10.Emboss— 3×3 relief convolution with+128bias. IM:-emboss R.MotionBlur— 1-D Gaussian blur alongangle_degrees. IM:-motion-blur RxS+A.Implode— radial pinch / explode with bilinear resampling. IM:-implode N.Swirl— radius-decaying rotational distortion. IM:-swirl N.Despeckle— median-window edge-preserving noise reduction (alpha pass-through). IM:-despeckle.Wave— sinusoidal vertical pixel displacement (amplitude / wavelength in pixels). IM:-wave AxL.Spread— random pixel-position perturbation inside a[-radius, radius]²neighbourhood, with a deterministic seed for reproducibility. IM:-spread N.Charcoal— non-photorealistic stylise: optional Gaussian pre-blur (radius, default0) ⇒ Sobel-on-luma ⇒ invert ⇒Gray8sketch. Larger pre-blur radii thicken the strokes by suppressing fine texture before the edge pass. IM:-charcoal R.Paint— oil-paint stylise: per-pixel modal-bucket vote in a(2*radius+1)²window then mean-of-mode RGB. IM:-paint radius.Pixelate— block-average spatial mosaic; eachN×Ntile collapses to its mean colour. Distinct fromQuantizewhich coarsens the colour axis only. Gray8 / RGB / RGBA + planar YUV (luma plane only). Factory aliases:pixelate,mosaic.Shade— directional Lambertian relief shading from an(azimuth, elevation)light vector. Optional colour pass-through mode (+shade). IM:-shade az,el.Convolve— user-supplied squareN×Nkernel (oddN); optional bias / divisor; alpha pass-through on RGBA. IM:-convolve "...".Laplacian— 3×3 Laplacian (second-derivative) edge filter. Output is|response|clamped to[0, 255]as aGray8frame. IM:-laplacian.Canny— full Canny edge pipeline: Gaussian pre-blur → Sobel gradient + direction → non-maximum suppression → hysteresis thresholding. Output is binaryGray8(0 / 255). IM:-canny RxS+L%+H%.EdgeDetect+EdgeKernel— runtime-selectable gradient kernel (Sobel/Prewitt/Scharr/Roberts). Complements the fixed-SobelEdge; sameGray8output shape and pixel-format coverage. Factory aliases:edge-detect,edge-multi.HoughCircles— circle detection via a 3-D Hough accumulator indexed by(radius, cx, cy). Sobel-magnitude voters cast votes along Bresenham circles of each candidate radius into the accumulator; top-K peaks abovevote_thresholdare rendered onto aGray8canvas. Complement (radius axis) toHoughLines.Polar/PolarDirection::DePolar— Cartesian ⇄ polar coordinate distortion (-distort Polar/-distort DePolar). Bends an image into a fan or unrolls a fan back to a rectangle. r7 surfaces optionalcx/cy/max_radiusoverrides through both the builder API and the JSON schema.Morphology+MorphologyChain— N-iteration greyscale dilate / erode with a 3×3 square or cross structuring element; plusopen(erode → dilate) andclose(dilate → erode) compositions. IM:-morphology Dilate|Erode|Open|Close.MorphologyEdge— morphological edge / gradient operators built on the dilate / erode primitives:EdgeIn=src - erode(src)— inner boundary.EdgeOut=dilate(src) - src— outer boundary.EdgeMagnitude=dilate - erode— full per-pixel gradient. IM:-morphology EdgeIn|EdgeOut|Edge.
Perspective— 4-corner perspective warp; solves the 3×3 homography from src/dst quads with 8×8 Gauss-Jordan elimination, then inverse-maps each output pixel viaH⁻¹with bilinear sampling. Optionaloutput_size: (w, h)(JSON keysoutput_widthoutput_height) emits a custom canvas size — useful when the dst quad escapes the source rectangle. IM:-distort Perspective "...".
Affine— 2-D affine warp with bilinear resampling. Six coefficients in(sx, ry, rx, sy, tx, ty)order; supplied either asmatrix: [...]or per-named keys on the JSON factory. Optionaloutput_sizefor translation off-canvas. IM:-distort Affine "sx,ry,rx,sy,tx,ty".Srt— Scale / Rotate / Translate composite warp. CollapsesT(tx,ty) · R(θ) · S(sx,sy) · T(-ox,-oy)into a single 2×3 affine matrix and reuses the [Affine] machinery. Origin defaults to the image centre on the JSON factory; uniformscaleoverridessx/sy. IM:-distort SRT "ox,oy sx[,sy] angle tx,ty".Distort— full Brown-Conrady lens distortion model: radial (k1quadratic +k2quartic) plus optional tangential (p1+p2, default0) decentering coefficients. Pure-radial mode (p1 = p2 = 0) is bit-identical to the legacy r7 output. IM:-distort barrel "k1 k2 ...".TiltShift— selective Gaussian blur masked by an in-focus band (miniature-photography depth-of-field). Configurablefocus_centre,focus_height,falloff_height, plus the underlying blurradius/sigma. Optionalangle_degrees(default0= horizontal band;90= vertical band) rotates the focus band around the image centre.
ChannelExtract+Channel— pull one channel out of a multi-channel frame as a single-planeGray8frame. AcceptsR/G/B/Aon packed RGB / RGBA andY/U/Von planar YUV (chroma channels return at the subsampled grid size). IM rough analogue:-channel <ch> -separate.
Clut— 1-D Colour Look-Up Table.srcis the image;dstis the CLUT (read row-major). Per-channel index lookup; alpha pass-through. IM:-clut.HaldClut— Hald CLUT image-as-LUT colour grading.dstis a(L²)×(L²)Hald cube; trilinear sampling per pixel. RGB / RGBA only. IM:-hald-clut.Difference— two-input pixel-wise absolute difference (out = |src - dst|). Cheap change-detection / motion mask, no Porter–Duff coverage algebra. Gray8 / RGB / RGBA / planar YUV.Watermark— two-input over-place of a secondary image (dst) onto the source (src) at(offset_x, offset_y)with a[0, 1]opacitymultiplier. The overlay shape is independent of the source — clipped on negative or oversize placements. Straight- alpha on RGBA; opacity scales the per-pixel alpha. Gray8 / RGB / RGBA (the two ports must sharePixelFormat).Composite+CompositeOp— Porter–Duff and arithmetic blends of a foreground (src) over a background (dst) frame. Sixteen operators registered ascomposite-<op>factories:- Porter–Duff coverage:
over,in,out,atop,xor. - Arithmetic / per-channel:
plus(clamped sum),multiply,screen,overlay(multiply-or-screen ondst < 128),darken(per-channel min),lighten(per-channel max),difference(|src - dst|). - Overlay family (r11):
hardlight(overlay driven bysrcinstead ofdst),softlight(Pegtop continuous formula),colordodge(dst / (1 - src/255)),colorburn(255 - (255 - dst) / (src/255)). - Operates on
Gray8/Rgb24/Rgbawith straight (non- premultiplied) alpha throughout. The newTwoInputImageFiltertrait formalises the two-port shape; theTwoInputImageFilterAdaptershim buffers per-port frames and emits whenever both ports have a frame in hand. - IM analogue:
-compose <op> -composite.
- Porter–Duff coverage:
All single-input filters listed above share the ImageFilter trait — chain them
manually with repeated .apply() calls, or feed them through
oxideav-pipeline as video.<name> filter nodes in a JSON job.
Supported in v0:
Gray8,Rgb24,RgbaYuv420P,Yuv422P,Yuv444P
Other formats return Error::unsupported. Planar higher-bit formats
(Yuv420P10Le etc.) will land in a later release as filters grow
per-format specialisations.
MIT. See LICENSE.