# vid.stab smooths handheld shake with an L1 optimal camera path

> vid.stab is a video stabilization library that plugs into ffmpeg as two filters, one to detect the camera motion and one to remove it. The interesting part is the path estimator, which optimises the camera trajectory as a linear program so the result looks like a dolly shot rather than a smoothed one, and the interesting part of the documentation is the list of ways you can silently break it.

**georgmartius/vid.stab** — Video stabilization library

- Repository: https://github.com/georgmartius/vid.stab
- Website: http://public.hronopik.de/vid.stab/
- Stars: 966 · Forks: 124
- Language: C
- License: NOASSERTION
- Published: 2026-10-20 · Updated: 2026-10-20 · Language: en
- Canonical page: https://hysenlabs.com/projects/georgmartius-vid-stab

## The problem it solves

Vidstab is a video stabilization library which can be plugged in with ffmpeg, and the reason it is needed is stated plainly. A video acquired using a hand-held camera or a camera mounted on a vehicle typically suffers from undesirable shakes and jitters, and activities such as surfing, skiing, riding and walking while shooting video are especially prone to erratic camera shakes. The library targets those video contents to help create smoother and stable videos. The two halves of the problem are therefore separated by design. Detecting the motion is a measurement problem, done once over the footage, and removing it is a correction problem, done with knowledge of the whole trajectory. A single pass cannot do both well, which is why the library does not try.

## The feature set, and the two smoothing strategies

The feature list covers detection and correction in turn. Detection is fast and finds the subsequent transformations, such as translation and rotations, up to a given extent, using a multi measurement field detection algorithm with contrast selection, so the fields are placed where there is something to track. Correction comes in two strategies. The first is low pass filtered smoothing with an adjustable horizon, which is the obvious approach. The second is the L1-optimal camera path following Grundmann and colleagues at CVPR 2011, where the stabilised path is composed of static, linear and parabolic segments, giving a result that looks like it came off a dolly or a crane rather than off a low pass filter. The path is steered by two familiar knobs, with zoom and optzoom giving it the crop budget it may spend and smoothing setting the horizon over which the path should stay rigid. The rest of the list covers clipping options, keeping blank or keeping from previous frames, optional drawing of the measurement fields and detected transformations for visual analysis, zooming to get rid of jiggly borders in automatic mode, interpolated results with different algorithms, and a virtual tripod mode.

## The linear program and its two solvers

The path estimation is solved as a linear program with a built-in solver, so it needs no extra dependency, and the solver is a built-in interior point solver. The alternative is GLPK, which is selected at configure time with cmake -DVIDSTAB_LPSOLVER=glpk, and the fact that both are supported is not incidental, because the test suite runs against either one and cross-checks both against a reference implementation in docs/l1campath-reference.py. That is a good way to think about the design. The optimiser is the part of the system where a subtle difference in the objective function produces a plausible but different video, so the project does not trust one implementation, it checks two of them against a third written in a different language, and a test failure means one of the three changed behaviour rather than that the machine is broken. The cost of that rigour is that the library has a linear programming solver inside it.

## Building the library and ffmpeg against it

The system requirements are a Linux based system, the ffmpeg source code and cmake. The library itself is built and installed with a three step sequence:

```shell
cd path/to/vid.stab/dir/
cmake .
make
sudo make install
```

and ffmpeg has to be configured to use it:

```shell
cd path/to/ffmpeg/dir/
./configure --enable-gpl --enable-libvidstab <other configure options>
make
sudo make install
```

The gpl flag deserves a note, because it looks alarming. vidstab itself is LGPL-2.1-or-later and does not require it, but ffmpeg's configure still gates the --enable-libvidstab option behind --enable-gpl, so it remains necessary when building ffmpeg. The README is explicit that this is an ffmpeg packaging decision and not a licence requirement of the library. There is also a route to a custom install directory, where the install prefix is passed to cmake, the ffmpeg configure then needs PKG_CONFIG_PATH pointing at the custom pkgconfig directory, and before running ffmpeg for the first time the dynamic linker has to be told where the library is:

```shell
export LD_LIBRARY_PATH=path/to/install_dir/lib:$LD_LIBRARY_PATH
```

## Two passes, and the rule that breaks them

With ffmpeg, the library must run in two pass mode, where the first pass employs the vidstabdetect filter and the second uses vidstabtransform. The detect pass generates a file with relative translation and rotation transform information about subsequent frames, and that file is read by the transform pass to compensate for the jerky motions. If you need a single pass, ffmpeg's own deshake filter can do that, though the vidstab two pass filters give superior results. The README then explains the failure mode that catches everybody, in unusually blunt terms. The transform file holds one entry per frame, in order, and the second pass consumes them in that order. It has no way to tell which frame it is looking at, so if the two passes are fed different frames the transforms silently stop lining up with the picture. The rule is that no filter which changes the number, order or rate of frames goes before vidstabtransform unless the exact same filter also ran before vidstabdetect, and the usual culprits are named: fps, minterpolate, select, mpdecimate, trim and -r. The wrong and right invocations are shown side by side:

```shell
# WRONG: detect sees 30 fps, transform sees 48 -- transforms run out early
ffmpeg -i in.mp4 -vf vidstabdetect -f null -
ffmpeg -i in.mp4 -vf minterpolate=fps=48,vidstabtransform out.mp4
```

and the fix is to change the rate first, in both passes:

```shell
# RIGHT: change the rate first, in both passes
ffmpeg -i in.mp4 -vf minterpolate=fps=48,vidstabdetect -f null -
ffmpeg -i in.mp4 -vf minterpolate=fps=48,vidstabtransform out.mp4
```

When it goes wrong you get a warning naming the problem, that not enough transforms were found and the last transformation is used, and everything after that point is unstabilized.

## Duplicated frames and 10-bit video

Two related traps are documented. Duplicated frames in the source, such as a 25 fps recording stored as 30 fps, are detected as exactly zero motion, which shows up as a regular jerk in the stabilised result, and the fix is to drop them with mpdecimate in both passes, or to work at the true frame rate. Concatenated input with the concat demuxer can present irregular timestamps, and the advice is to check that both passes report the same frame count before blaming the stabilisation. Then there is the format issue, where 10-bit 4:2:2 video must be downsampled to 8-bit 4:2:0 to avoid distortions like chroma shift or colour bleed and smearing, with the format=yuv420p filter given as the example. These are the kinds of detail that make the difference between a stabilised file and a file that looks broken, and the README also recommends the unsharp filter in the second pass for the best results.

## Interlaced input has to be deinterlaced first

The section on interlaced video is a good example of a library documenting its own limits precisely. Vidstab has no notion of fields, it treats every input frame as one progressive image, and feeding it interlaced material means the two fields, which are half a frame period apart and so show different motion, are analysed as a single picture. The consequence is that the comb artefacts are measured as image content, and the detected motion becomes a meaningless average of the two fields. Transforming then shifts both fields together, which does not undo the shake and smears the combing across the frame. The fix is to put a deinterlacer before vidstabdetect in both passes, with yadif given as the example. The reasoning is worth internalising, because the same argument applies to any motion estimator that assumes progressive frames.

## Where the project stands

The repository is a C and C++ project with a GitHub Actions workflow for the C and C++ build, and the top level shows the shape of it, with a libavfilter/ directory for the ffmpeg filters, src/ for the library sources, CMakeModules/ for the build helpers, a version.cmake file, a bench/ directory, tests/, docs/ and a Changelog, a Todo file, an Install file and a COPYING.LESSER licence text, plus a RELICENSE.md and a mktags.sh script. The homepage is at public.hronopik.de/vid.stab, and the release history lists a single version, v1.1.2 with the description that it contains minor fixes, published on 2026-07-29, with the last push on 2026-08-14. The README also notes that it covers using vidstab with ffmpeg specifically, and gives an email address for questions, which is the level of formality a project of this size usually operates at. The licence situation is the one to check before shipping: the library is LGPL-2.1-or-later, while the repository listing records a custom licence that GitHub cannot classify, so the COPYING.LESSER file and the RELICENSE.md in the tree are the documents to read.

## Conclusion

vid.stab fits someone who shoots handheld video and wants a stabilised file without a dedicated editor, and the L1 path is the reason the output looks like a crane move rather than a smeared low pass. It does not fit a single pass workflow, because the design requires two passes over the same frames, and it does not fit material you have not deinterlaced, since the library has no notion of fields. Before you run it, read the section on both passes seeing the same frames, because a frame rate filter applied to only one of them produces a file that looks stabilised at the start and then stops being stabilised, with a warning that names the problem exactly.

## FAQ

### What is vid.stab?

It is a video stabilization library that plugs into ffmpeg as the vidstabdetect and vidstabtransform filters, and it targets the shake in handheld or vehicle mounted footage, where activities such as surfing, skiing, riding and walking produce erratic camera motion.

### Why does vid.stab need two passes?

The first pass detects the transformations between frames and writes them to a file, and the second pass reads that file and applies the correction. The transform file holds one entry per frame in order, so both passes must see the same frames or the transforms stop lining up with the picture.

### What is the L1 optimal camera path in vid.stab?

It is the smoothing strategy from Grundmann and colleagues at CVPR 2011, where the stabilised path is composed of static, linear and parabolic segments so the result looks like it came off a dolly or crane. It is solved as a linear program with a built-in interior point solver, and GLPK can be used instead with the cmake option, with the test suite checking both against a reference implementation.

### How do I build ffmpeg with vid.stab support?

Build and install vid.stab with cmake and make, then configure ffmpeg with --enable-gpl --enable-libvidstab and build that. The gpl flag is an ffmpeg packaging requirement, not a licence requirement of the library, which is LGPL-2.1-or-later.

### What should I do about interlaced or 10-bit video?

Deinterlace before stabilizing, in both passes, for example with yadif, because the library treats each frame as progressive and would measure comb artefacts as motion. 10-bit 4:2:2 material must be downsampled to 8-bit 4:2:0 with the format filter, and duplicated frames should be removed with mpdecimate in both passes or the result jerks regularly.

## Sources

- [georgmartius/vid.stab on GitHub](https://github.com/georgmartius/vid.stab)
- [Issues](https://github.com/georgmartius/vid.stab/issues)
- [Project website](http://public.hronopik.de/vid.stab/)
- [README](https://github.com/georgmartius/vid.stab/blob/master/README.md)
- [Releases](https://github.com/georgmartius/vid.stab/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/georgmartius-vid-stab
