Skip to content

pixtreme.filter

filter

Blur, derivative, sharpening, and convolution filters.

__all__ = ('gaussian_blur', 'box_blur', 'median_blur', 'bilateral_blur', 'directional_blur', 'zoom_blur', 'spin_blur', 'vector_blur', 'lens_blur', 'sobel', 'laplacian', 'difference_of_gaussians', 'canny', 'sharpen', 'unsharp_mask', 'convolve_box') module-attribute

bilateral_blur(frame, *, sigma_space, sigma_value, border='mirror', border_value=None)

Apply a bilateral blur using one all-channel value distance per neighbor.

Kernel radius is fixed as radius = ceil(3 * sigma_space). Border defaults to mirror (edge-excluding reflection); replicate clamps to the edge and wrap uses periodic indices. sigma_value is measured in the Frame's working-value scale: changing scene-linear exposure changes its meaning, and this function does not normalize values automatically. constant uses border_value for every virtual pixel outside the image; border_value is required with constant and forbidden for every other border. The fp32 calculation does not clamp negative values or values above 1 and returns new storage with unchanged metadata.

box_blur(frame, *, size, border='mirror', border_value=None)

Replace each channel value with its square-window mean.

size is a positive odd integer. Border defaults to mirror (edge-excluding reflection); replicate clamps to the edge and wrap uses periodic indices. constant uses border_value for every virtual pixel outside the image; border_value is required with constant and forbidden for every other border. Calculation is fp32 per channel. It does not clamp scene values; negative values and values above 1 pass through. Size 1 is an identity in new storage.

canny(frame, *, threshold_low, threshold_high, border='mirror', border_value=None)

Detect binary edges in a float32 Frame with a deterministic Canny pipeline.

threshold_low and threshold_high are required nonnegative absolute scene-gradient strengths on the non-normalized 3x3 Sobel scale. The pipeline applies Sobel L2 magnitude, four-sector NMS, double thresholding, and 8-connected hysteresis with complete convergence and no iteration cap. NMS retains a sample exactly when current > magnitude(-v) and current >= magnitude(+v); equal thresholds select a single-threshold mode with no weak edge set. No Gaussian smoothing, normalization, or grayscale conversion is implicit.

Border defaults to mirror; replicate clamps to the edge, wrap is periodic, and constant requires a finite border_value. The same border rule applies to Sobel source samples and NMS magnitude samples, but hysteresis never connects through virtual pixels. Processing is per channel without using channel labels. The result is a private C-contiguous float32 Frame containing only 0.0 and 1.0, with metadata preserved and the input storage unchanged.

Convert non-float32 storage according to its value meaning with px.values.cast_dtype, px.values.recode_dtype, or px.values.dequantize before calling this operation.

convolve_box(frame, *, size, normalize, border='mirror', border_value=None)

Apply a rectangular moving sum or moving mean independently per channel.

size is one positive odd integer or (height, width). normalize must be passed explicitly: true returns the window mean and false returns the window sum. Border defaults to mirror (edge-excluding reflection); replicate clamps to the edge and wrap uses periodic indices. constant uses border_value for every virtual pixel outside the image; border_value is required with constant and forbidden for every other border. The fp32 calculation does not clamp negative values or values above 1 and always returns new storage, including size 1.

difference_of_gaussians(frame, *, sigma1, sigma2, border='mirror', border_value=None)

Subtract gaussian_blur(sigma2) from gaussian_blur(sigma1).

Each Gaussian uses the public gaussian_blur contract, including radius = ceil(3 * sigma). sigma1 and sigma2 must be positive finite real values. Their order is unrestricted and determines the sign; equal sigmas produce a zero Frame without error.

Border defaults to mirror; replicate clamps to the edge, wrap is periodic, and constant requires a finite border_value. The float32 calculation applies independently and uniformly to all channels, preserves Frame metadata and input storage, and does not clamp negative values or values above 1. Convert non-float32 storage with px.values.cast_dtype, px.values.recode_dtype, or px.values.dequantize.

laplacian(frame, *, border='mirror', border_value=None)

Apply the fixed non-normalized 3x3 Laplacian to a float32 Frame.

The kernel is [[0, 1, 0], [1, -4, 1], [0, 1, 0]]. No Gaussian smoothing or LoG behavior is built in. Border defaults to mirror; replicate clamps to the edge, wrap is periodic, and constant requires a finite border_value. A uniform image is zero at every pixel when a constant border uses the same uniform value.

The calculation applies independently and uniformly to all channels, preserves Frame metadata and input storage, and does not clamp negative values or values above 1. Convert non-float32 storage with px.values.cast_dtype, px.values.recode_dtype, or px.values.dequantize.

sobel(frame, *, direction='magnitude', border='mirror', border_value=None)

Apply the standard non-normalized 3x3 Sobel derivative to a float32 Frame.

x uses derivative [-1, 0, 1] horizontally and smoothing [1, 2, 1] vertically; it responds to vertical edges. y is the transpose and responds to horizontal edges. magnitude is the default and equals sqrt(x**2 + y**2) per channel. The scale is not normalized: a unit horizontal ramp has an interior x response of 8.

Border defaults to mirror; replicate clamps to the edge, wrap is periodic, and constant requires a finite border_value. The calculation applies independently and uniformly to all channels, preserves Frame metadata and input storage, and does not clamp negative values or values above 1. Convert non-float32 storage with px.values.cast_dtype, px.values.recode_dtype, or px.values.dequantize according to its meaning.

directional_blur(frame, *, angle, length, border='mirror', border_value=None)

Average a symmetric straight path through each pixel.

The path is p + t * (cos(angle), -sin(angle)) for t in [-length / 2, +length / 2]. Angles use degrees: 0 degrees is +x and positive is visually counterclockwise. Sampling uses max(2, ceil(path length) + 1) uniformly weighted points and fixed bicubic interpolation with Keys a = -0.5.

Border defaults to mirror; replicate and wrap are also accepted and apply independently to every bicubic tap. constant uses border_value for taps outside the image; border_value is required with constant and forbidden for every other border. Calculation is fp32 per channel, does not clamp scene values, and returns new storage.

spin_blur(frame, *, angle, center=None, border='mirror', border_value=None)

Average a symmetric circular arc around center through each pixel.

The circular arc rotates p from -angle / 2 through +angle / 2. Angles use degrees: 0 degrees is +x and positive is visually counterclockwise. Center defaults to the geometric center and may lie outside the image. Sampling uses max(2, ceil(path length) + 1) uniformly weighted points with fixed bicubic interpolation using Keys a = -0.5. Path length is center distance times angle in radians, so cost grows with center distance.

Border defaults to mirror; replicate and wrap are also accepted and apply independently to every bicubic tap. constant uses border_value for taps outside the image; border_value is required with constant and forbidden for every other border. Calculation is fp32 per channel, does not clamp scene values, and returns new storage.

zoom_blur(frame, *, amount, center=None, border='mirror', border_value=None)

Average a symmetric radial scale path through each pixel.

The path is center + (p - center) * s for s from 1 - amount / 2 through 1 + amount / 2. Center defaults to the geometric center and may lie outside the image. Sampling uses max(2, ceil(path length) + 1) uniformly weighted points with fixed bicubic interpolation using Keys a = -0.5. Path length is center distance times amount, so cost grows with center distance.

Border defaults to mirror; replicate and wrap are also accepted and apply independently to every bicubic tap. constant uses border_value for taps outside the image; border_value is required with constant and forbidden for every other border. Calculation is fp32 per channel, does not clamp scene values, and returns new storage.

gaussian_blur(frame, *, sigma, border='mirror', border_value=None)

Apply an isotropic Gaussian blur without changing Frame metadata.

Kernel radius is fixed as radius = ceil(3 * sigma); discrete 2D Gaussian weights are normalized by their sum. Border defaults to mirror (edge-excluding reflection); replicate clamps to the edge and wrap uses periodic indices. constant uses border_value for every virtual pixel outside the image; border_value is required with constant and forbidden for every other border. Calculation is fp32 per channel. It does not clamp scene values; negative values and values above 1 pass through. The result always owns a new allocation.

lens_blur(frame, *, radius, blades=None, rotation=None, border='mirror', border_value=None)

Convolve with a flat uniform aperture for spatially invariant bokeh.

This is the optical flat-aperture counterpart of lens blur: with scene-linear input, highlights above 1.0 open into bokeh shaped like the aperture. radius is the circle radius or a regular polygon's circumradius. At the same radius, changing blades changes aperture area. blades=None selects a circle; an integer of at least 3 selects a regular polygon.

rotation is available only with blades. A vertex is at +x at 0 degrees, and positive rotation is visually counterclockwise. Every kernel pixel uses partial coverage from a fixed 16 x 16 center-subsample grid, then all nonzero coverage weights are normalized for convolution.

Border defaults to mirror; replicate clamps to the edge and wrap uses periodic indexing. constant uses border_value outside the image. border_value is required with constant and forbidden for every other border. Calculation is fp32 independently per channel and does not clamp negative scene values or values above 1. The result has new storage and unchanged Frame metadata.

radius = 0 is an exact identity in new storage. A positive radius whose fixed grid has zero aperture coverage follows the same exact identity rule. Small apertures use direct convolution, whose cost grows in proportion to radius squared; larger apertures use equivalent FFT convolution.

median_blur(frame, *, size, border='mirror', border_value=None)

Replace each channel value with its square-window median.

size is a positive odd integer from 1 through 7. Border defaults to mirror (edge-excluding reflection); replicate clamps to the edge and wrap uses periodic indices. constant uses border_value for every virtual pixel outside the image; border_value is required with constant and forbidden for every other border. Median selection is independent per channel, uses fp32 values, and does not clamp negative values or values above 1. The result always owns new storage, including size 1.

sharpen(frame, *, amount, border='mirror', border_value=None)

Sharpen a float32 Frame with input - amount * laplacian(input).

The basis is the fixed non-normalized 3x3 Laplacian [[0, 1, 0], [1, -4, 1], [0, 1, 0]] with no Gaussian smoothing. amount is required, accepts any finite real except bool, and may be negative. Zero returns a private bit-exact copy. Border defaults to mirror; replicate clamps to the edge, wrap is periodic, and constant requires a finite border_value. Other borders forbid border_value.

The float32 calculation applies independently and uniformly to all channels and does not clamp negative values, values above 1, or sharpening halos. Frame metadata passes through and the input remains unchanged. Convert non-float32 storage with px.values.cast_dtype, px.values.recode_dtype, or px.values.dequantize according to its value meaning.

unsharp_mask(frame, *, sigma, amount, border='mirror', border_value=None)

Sharpen a float32 Frame with input + amount * (input - G(input)).

G is the same isotropic Gaussian as :func:gaussian_blur: its kernel radius is fixed as radius = ceil(3 * sigma) and its discrete weights are normalized by their sum. amount may be negative; zero returns a private bit-exact copy. Border defaults to mirror (edge-excluding reflection), replicate clamps to the edge, and wrap uses periodic indices. constant uses border_value outside the image; border_value is required with constant and forbidden for every other border.

The fp32 calculation applies independently and uniformly to all channels, including alpha, and does not clamp negative values, values above 1, or sharpening halos. Frame metadata passes through and the input remains unchanged. Convert float16 literal values first with px.values.cast_dtype(frame, dtype="float32"); use px.values.recode_dtype or px.values.dequantize for integer storage according to its value meaning.

vector_blur(frame, *, vector, shutter='centered', border='mirror', border_value=None)

Average a per-pixel straight line selected by one gather vector.

For output pixel p, the path is p + t * v(p): v(p) is read once at p, and the vector field is not followed again along the path. This gather contract cannot create the scatter-style contribution of a moving object across a motion boundary; that visual limitation is intentional.

Vector channel 0 = x and channel 1 = y by position, independent of labels. Coordinates are measured in pixels without y inversion: +x is right; +y is down. centered integrates t in [-1/2, +1/2], forward uses [0, 1], and backward uses [-1, 0]. Every interval has length 1.

The uniformly weighted endpoint-inclusive sample count is max(2, ceil(|v(p)|) + 1). Sampling uses fixed bicubic interpolation with Keys a = -0.5. mirror is the default border; replicate and wrap use edge clamp and periodic indexing. constant uses border_value for every out-of-image bicubic tap. border_value is required with constant and forbidden for every other border.

Geometry, samples, and accumulation use fp32 independently per channel and the result does not clamp negative values or values above 1. Vector values are assumed finite and are not checked; output for non-finite vectors is undefined. The result owns new storage with frame metadata unchanged, and computational cost grows in proportion to |v|.