Deflicker Runner
Fix LED flicker in video footage caused by LED PWM drivers beating against rolling shutter cameras.
Quick Start
# Auto-detect flicker type and fix python3 deflicker.py auto input.MP4 output.MP4 --duration=5 # Whole-frame LED flicker (best approach) python3 deflicker.py temporal-median input.MP4 output.MP4 --radius=2 # Small flickering bulbs / fairy lights python3 deflicker.py spot-replace input.MP4 output.MP4 --start=128 --duration=5 # Diagnose flicker type before fixing python3 diagnose.py input.MP4 # Run tests python3 test_deflicker.py verify
Two Video Types
Whole-frame flicker
LED panels reflect off walls, ceiling, floors. The entire frame pulses because the reflected light covers large surfaces. Dark surfaces show it most — bright LED panels are saturated and don't flicker visibly.
Detection: Row brightness at 540p varies 1-3% frame-to-frame with 3-frame repeating pattern.
Spot flicker
Small flickering light sources — exposed filament bulbs, fairy lights, candles. Only a few pixels flicker while the rest of the frame is stable. Standard 540p approaches miss these (2-5 pixels at 4K = sub-pixel at 540p).
Detection: Bright pixels (>150) with high temporal std (>40) in small clusters.
Approaches
| Approach | Type | Best For | Performance |
|---|---|---|---|
auto | auto | Default — detects and selects | Quick scan |
temporal-median | 2D | Whole-frame LED flicker (recommended) | ~15 fps compute, ~20 fps apply |
running-mean | 1D | General-purpose deflicker | Fast compute |
spot-replace | spot | Small filament bulbs, fairy lights | ~8 fps apply |
pixel-smooth | 2D | Scene changes, motion-preserving | ~2 fps compute |
bcc | 1D | Scene changes (per-row) | Medium |
fft-notch | 1D | Precise LED beat removal only (~2%) | Fast |
physical-model | 1D | Known LED frequency, physics-based | Slow (fitting) |
global-row | 1D | Global frame brightness variation | Fast |
hybrid | 1D | Experimental (not recommended) | Medium |
Type key
- •1D: Per-row corrections at 540p, interpolated to full resolution
- •2D: Per-pixel corrections at 540p, bilinearly upscaled to full resolution (uses cv2.resize, 96x faster than scipy)
- •spot: Full-resolution detection + per-box correction (works at native 4K)
Common Parameters
--qp=10 Output quality (lower = better, 10 = near-lossless) --start=30 Start time in seconds --duration=5 Process N seconds --dark-floor=25 Suppress corrections below this brightness --radius=2 Temporal radius (temporal-median, spot-replace) --window=300 Temporal window for running-mean --bright-threshold=150 Spot detection: min pixel brightness --std-threshold=40 Spot detection: min temporal std
Performance Notes
- •Uses CUDA-accelerated ffmpeg decode + NVENC encode
- •cv2.resize for 2D correction upscale: 96x faster than scipy.ndimage.zoom
- •QP=10 needed to preserve subtle 2% corrections
- •Per-box delta storage: ~50MB vs 3.2GB full-frame for spot-replace
Results
C1608 (whole-frame, temporal-median r=2)
- •Ceiling: 57% std reduction, 85% 3-frame oscillation removal
- •Wall: 66% std reduction, 76% 3-frame oscillation removal
C1595 (spot flicker, spot-replace r=2)
- •Both filament bulbs detected and corrected at 4K resolution
- •Fixed detection: bright>150, std>40 (was broken with adaptive threshold)
Integration with color-grade-ai
Deflicker BEFORE applying color correction LUTs:
raw clip → deflicker.py → color-corrected clip (in Premiere/Resolve)
Physics
- •Australian mains: 50Hz → LED PWM at 100Hz
- •Camera: 59.94fps rolling shutter (Sony A7IV)
- •Beat: 100Hz mod 59.94fps = 19.88Hz aliased (3-frame cycle)
- •Amplitude: ~2% on ceiling, ~1.8% on walls (subtle but perceptible at 20Hz)