{
  "schemaVersion": 1,
  "contentVersion": "ca42e61ede0a24f789d7287a0001244ad22f4abf",
  "id": "docs/tools/filtering/stabilizing",
  "collection": "docs",
  "title": "Stabilizing",
  "description": "How to reduce camera shake with FFmpeg's two-pass vidstab filters, which options matter, and what stabilization costs in sharpness and resolution.",
  "canonicalUrl": "https://encode.wiki/tools/filtering/stabilizing/",
  "apiUrl": "https://encode.wiki/api/v1/documents/docs/tools/filtering/stabilizing.json",
  "markdownUrl": "https://encode.wiki/api/v1/markdown/docs/tools/filtering/stabilizing.md",
  "status": "current",
  "tags": [
    "migrated"
  ],
  "license": "CC BY-SA 4.0",
  "dates": {
    "lastVerified": "2026-10-08T00:00:00.000Z"
  },
  "authors": [],
  "sources": [
    {
      "title": "FFmpeg vidstabdetect filter documentation",
      "url": "https://ffmpeg.org/ffmpeg-filters.html#vidstabdetect",
      "accessedAt": "2026-10-08T00:00:00.000Z"
    },
    {
      "title": "FFmpeg vidstabtransform filter documentation",
      "url": "https://ffmpeg.org/ffmpeg-filters.html#vidstabtransform",
      "accessedAt": "2026-10-08T00:00:00.000Z"
    },
    {
      "title": "vid.stab v1.1.2",
      "url": "https://github.com/georgmartius/vid.stab/releases/tag/v1.1.2",
      "accessedAt": "2026-10-08T00:00:00.000Z"
    }
  ],
  "provenance": {
    "originUrl": "https://github.com/av1-community-contributors/codec-wiki/blob/37c3317fd058b23c71a87679bed5619a98f77752/docs/filtering/stabilizing.mdx",
    "originCommit": "37c3317fd058b23c71a87679bed5619a98f77752",
    "migratedAt": "2026-08-21T00:00:00.000Z",
    "modified": true,
    "originPath": "docs/filtering/stabilizing.mdx"
  },
  "headings": [
    {
      "depth": 2,
      "text": "Two passes with vid.stab",
      "anchor": "two-passes-with-vidstab"
    },
    {
      "depth": 2,
      "text": "Options that matter",
      "anchor": "options-that-matter"
    },
    {
      "depth": 2,
      "text": "What it can't fix",
      "anchor": "what-it-cant-fix"
    },
    {
      "depth": 2,
      "text": "Checking the result",
      "anchor": "checking-the-result"
    }
  ],
  "sections": [
    {
      "id": "_preamble",
      "title": "Page introduction",
      "depth": 0,
      "canonicalUrl": "https://encode.wiki/tools/filtering/stabilizing/",
      "apiUrl": "https://encode.wiki/api/v1/sections/docs/tools/filtering/stabilizing/_preamble.json",
      "markdownUrl": "https://encode.wiki/api/v1/sections/docs/tools/filtering/stabilizing/_preamble.md",
      "checksum": "61e190f2a277b1eab9e2e9750989df4a015a50906ba18094dab693437b1566f9",
      "bytes": 508
    },
    {
      "id": "two-passes-with-vidstab",
      "title": "Two passes with vid.stab",
      "depth": 2,
      "anchor": "two-passes-with-vidstab",
      "canonicalUrl": "https://encode.wiki/tools/filtering/stabilizing/#two-passes-with-vidstab",
      "apiUrl": "https://encode.wiki/api/v1/sections/docs/tools/filtering/stabilizing/two-passes-with-vidstab.json",
      "markdownUrl": "https://encode.wiki/api/v1/sections/docs/tools/filtering/stabilizing/two-passes-with-vidstab.md",
      "checksum": "b653ebb860407fca45c8f219fd88d5eeb40d06dce3aadf89934705f41528b179",
      "bytes": 1154
    },
    {
      "id": "options-that-matter",
      "title": "Options that matter",
      "depth": 2,
      "anchor": "options-that-matter",
      "canonicalUrl": "https://encode.wiki/tools/filtering/stabilizing/#options-that-matter",
      "apiUrl": "https://encode.wiki/api/v1/sections/docs/tools/filtering/stabilizing/options-that-matter.json",
      "markdownUrl": "https://encode.wiki/api/v1/sections/docs/tools/filtering/stabilizing/options-that-matter.md",
      "checksum": "1dc55509402c173a3a98295a98b103153274ab6e901f581212d0cce783997550",
      "bytes": 2514
    },
    {
      "id": "what-it-cant-fix",
      "title": "What it can't fix",
      "depth": 2,
      "anchor": "what-it-cant-fix",
      "canonicalUrl": "https://encode.wiki/tools/filtering/stabilizing/#what-it-cant-fix",
      "apiUrl": "https://encode.wiki/api/v1/sections/docs/tools/filtering/stabilizing/what-it-cant-fix.json",
      "markdownUrl": "https://encode.wiki/api/v1/sections/docs/tools/filtering/stabilizing/what-it-cant-fix.md",
      "checksum": "222f1c35164fbb6d7e4617263c928df6c8cbb8c082917cd76aae97821cb90b72",
      "bytes": 488
    },
    {
      "id": "checking-the-result",
      "title": "Checking the result",
      "depth": 2,
      "anchor": "checking-the-result",
      "canonicalUrl": "https://encode.wiki/tools/filtering/stabilizing/#checking-the-result",
      "apiUrl": "https://encode.wiki/api/v1/sections/docs/tools/filtering/stabilizing/checking-the-result.json",
      "markdownUrl": "https://encode.wiki/api/v1/sections/docs/tools/filtering/stabilizing/checking-the-result.md",
      "checksum": "50497ac940bd0415dc00cdbde5e16c35effc1d908c0dd9b0aaea18b5054f657f",
      "bytes": 481
    }
  ],
  "outboundReferences": [
    "https://github.com/gyroflow/gyroflow"
  ],
  "checksum": "d4f9520bc2c7b9ee8d09f2bc3f496041994cb2d64f83e81d36be39a5128d8cf0",
  "contentFormat": "markdown",
  "markdown": "Stabilization estimates how the camera moved from frame to frame and warps each frame to cancel the shake. It suits handheld and action-camera footage. It cannot add information that wasn't captured, so it always costs something: the warp needs interpolation, and the borders exposed by the movement are cropped or zoomed away.\n\nStabilizing can help compression, since removing erratic global motion leaves the encoder less to predict. The saving is secondary; the reason to stabilize is a steadier picture.\n\n## Two passes with vid.stab\n\nFFmpeg's `vidstabdetect` and `vidstabtransform` filters wrap the vid.stab library, and your FFmpeg build needs `--enable-libvidstab`. The first pass analyses motion and writes it to a file; the second applies it.\n\n```sh\nffmpeg -i input.mp4 -vf vidstabdetect -f null -\nffmpeg -i input.mp4 -vf \"vidstabtransform,unsharp=5:5:0.8:3:3:0.4\" -c:v libx264 -crf 18 output.mp4\n```\n\nThe first command writes `transforms.trf` in the current directory, which you can delete afterwards. The `unsharp` filter is recommended by the FFmpeg documentation because the interpolation in the transform softens the picture. Both passes must see the same frames, so don't trim or filter differently between them. You cannot use `-c:v copy`, since the video is being changed.\n\nFor very shaky, fast footage, such as action-camera video, detect with a higher `shakiness`, write to a named file, and smooth more with a small zoom-out:\n\n```sh\nffmpeg -i input.mp4 -vf \"vidstabdetect=shakiness=8:result=a.trf\" -f null -\nffmpeg -i input.mp4 -vf \"vidstabtransform=input=a.trf:smoothing=30:zoom=-5,unsharp=5:5:0.8:3:3:0.4\" -c:v libx264 -crf 18 output.mp4\n```\n\n## Options that matter\n\n| Option      | Filter    | What it does                                                                                                                                                                                                               |\n| ----------- | --------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| `shakiness` | detect    | How shaky the video is and how fast the camera moves, 1-10 (default 5). It tunes detection; it is not a stabilization strength.                                                                                            |\n| `accuracy`  | detect    | Detection accuracy, 1-15 (default 15). Lower is faster.                                                                                                                                                                    |\n| `smoothing` | transform | Frames on each side used to smooth the camera path (default 15). Larger is steadier but fights real pans and tilts. 0 simulates a static camera.                                                                           |\n| `optzoom`   | transform | Automatic zoom to hide borders: 1 (default) picks one static zoom, 2 adapts it per frame, 0 disables it.                                                                                                                   |\n| `zoom`      | transform | Extra zoom in percent, added to `optzoom`. Negative values zoom out and expose more border.                                                                                                                                |\n| `crop`      | transform | What to do with exposed borders: `keep` the previous frame's content (default) or fill with `black`.                                                                                                                       |\n| `interpol`  | transform | `bilinear` (default), `bicubic` (slower, sharper) or `no`.                                                                                                                                                                 |\n| `tripod`    | both      | Virtual tripod for a camera that should be locked off: every frame is compared to a reference frame. In `vidstabdetect` the value is the reference frame number (counted from 1); in `vidstabtransform` it is 1 to enable. |\n\n## What it can't fix\n\nvid.stab estimates translation and rotation of the whole frame. It will not fix rolling-shutter wobble (\"jello\"), parallax between near and far objects, or motion blur from the shake, and low-contrast or very dark footage can confuse the detection. FFmpeg also has a single-pass `deshake` filter that needs no extra library. If your camera records gyroscope data, [Gyroflow](https://github.com/gyroflow/gyroflow) stabilizes from that data instead of from the image.\n\n## Checking the result\n\nWatch the intended pans and any foreground subject, since a large `smoothing` makes them lag or drift. Check the edges: stronger stabilization means more zoom or more visible border, and the picture will be a little softer. If the result wobbles in a way the original did not, reduce `smoothing` or `shakiness`, or give up on that clip. Stabilization is lossy and cannot be repeated without further loss, so do it once, from the original, and encode after.\n"
}
