Smooths a trajectory while undoing the inward "corner-cutting" bias that a plain moving average introduces on curved paths.
Arguments
- data
An aniframe in Cartesian coordinates with 2 or 3 spatial columns (set via the
variables_wheremetadata field). The curvature math is Cartesian-specific (cross products, Euclidean norms, circumradius), so polar / cylindrical / spherical aniframes are rejected.- window_width_ma
Integer width of the moving-average kernel (must be odd; even values are rounded up). Larger = more smoothing. Default
11.- window_width_cc
Integer width of the curvature-correction kernel (must be odd). Larger = smoother correction but uses curvature info from further away. Default
7.- kernel
Kernel shape for both stages. One of
"hanning"(default; raised cosine) or"uniform"(boxcar).- boundary
Edge-handling strategy. Currently only
"padding"(repeat the first and last point so output length equals input length).- cc_mode
If
FALSE, returns just the moving-average result without curvature correction. Useful for comparison.- na_action
How to fill
NAvalues in the spatial columns before filtering. One of"linear"(default),"spline","stine","locf","value", or"error"(abort if anyNAs are present). Seereplace_na().- keep_na
If
TRUE, restoreNAs at their original positions in the output. Defaults toFALSE.- ...
Additional arguments passed to
replace_na()(e.g.value,min_gap,max_gap).
Details
A plain moving average pulls each point toward the chord between its neighbours, which lies inside the curve — so smoothed circles shrink inward. CCMA (Steinecker & Wuensche, 2023) estimates how much shrinkage the moving average caused at each point — from the local curvature and the kernel — and pushes the result back outward by exactly that amount.
The algorithm has two stages:
Moving average of the spatial coordinates with a kernel of width
window_width_ma.Curvature correction: at each output position, sum a kernel of width
window_width_ccof curvature-derived shifts and apply them outward in the curve's plane.
Because curvature is intrinsically multi-dimensional, this filter
operates on all spatial coordinates jointly (unlike the per-column
filters dispatched through filter_aniframe()). It is most useful
for smoothing curved 2D or 3D trajectories where a plain moving
average visibly cuts corners; for general-purpose time-series
smoothing reach for filter_gaussian() or filter_sgolay().
Smoothing is applied within the aniframe's existing grouping (driven
by variables_what), so each individual / track / keypoint is
smoothed as its own trajectory.
References
Steinecker, T. & Wuensche, H.-J. (2023). A Simple and Model-Free Path Filtering Algorithm for Smoothing and Accuracy. 2023 IEEE Intelligent Vehicles Symposium (IV).
Reference Python implementation: https://github.com/UniBwTAS/ccma