{
  "schemaVersion": 1,
  "contentVersion": "ca42e61ede0a24f789d7287a0001244ad22f4abf",
  "id": "docs/tools/filtering/basics",
  "collection": "docs",
  "title": "Basics of Filtering",
  "description": "A short orientation to filtering video before encoding - when to filter, in what order, and the basic crop, resize and trim steps in VapourSynth.",
  "canonicalUrl": "https://encode.wiki/tools/filtering/basics/",
  "apiUrl": "https://encode.wiki/api/v1/documents/docs/tools/filtering/basics.json",
  "markdownUrl": "https://encode.wiki/api/v1/markdown/docs/tools/filtering/basics.md",
  "status": "current",
  "tags": [
    "migrated"
  ],
  "license": "CC BY-SA 4.0",
  "dates": {
    "lastVerified": "2026-10-08T00:00:00.000Z"
  },
  "authors": [],
  "sources": [
    {
      "title": "vs-jetpack v2.2.6",
      "url": "https://github.com/Jaded-Encoding-Thaumaturgy/vs-jetpack/tree/v2.2.6",
      "accessedAt": "2026-10-08T00:00:00.000Z"
    },
    {
      "title": "vs-jetpack documentation",
      "url": "https://jaded-encoding-thaumaturgy.github.io/vs-jetpack/",
      "accessedAt": "2026-10-08T00:00:00.000Z"
    },
    {
      "title": "VapourSynth resize functions",
      "url": "https://www.vapoursynth.com/doc/functions/video/resize.html",
      "accessedAt": "2026-10-08T00:00:00.000Z"
    },
    {
      "title": "FFmpeg cropdetect filter",
      "url": "https://ffmpeg.org/ffmpeg-filters.html#cropdetect",
      "accessedAt": "2026-10-08T00:00:00.000Z"
    }
  ],
  "provenance": {
    "originUrl": "https://github.com/av1-community-contributors/codec-wiki/blob/37c3317fd058b23c71a87679bed5619a98f77752/docs/filtering/basics.mdx",
    "originCommit": "37c3317fd058b23c71a87679bed5619a98f77752",
    "migratedAt": "2026-08-21T00:00:00.000Z",
    "modified": true,
    "originPath": "docs/filtering/basics.mdx"
  },
  "headings": [
    {
      "depth": 2,
      "text": "FFmpeg or VapourSynth",
      "anchor": "ffmpeg-or-vapoursynth"
    },
    {
      "depth": 2,
      "text": "Filter order",
      "anchor": "filter-order"
    },
    {
      "depth": 2,
      "text": "Starting script",
      "anchor": "starting-script"
    },
    {
      "depth": 2,
      "text": "Cropping",
      "anchor": "cropping"
    },
    {
      "depth": 2,
      "text": "Resizing",
      "anchor": "resizing"
    },
    {
      "depth": 2,
      "text": "Trimming",
      "anchor": "trimming"
    }
  ],
  "sections": [
    {
      "id": "_preamble",
      "title": "Page introduction",
      "depth": 0,
      "canonicalUrl": "https://encode.wiki/tools/filtering/basics/",
      "apiUrl": "https://encode.wiki/api/v1/sections/docs/tools/filtering/basics/_preamble.json",
      "markdownUrl": "https://encode.wiki/api/v1/sections/docs/tools/filtering/basics/_preamble.md",
      "checksum": "bc7069103f07980f1b4d93a3854b88142d5882dfad5640ca414db31ee70df8b6",
      "bytes": 403
    },
    {
      "id": "ffmpeg-or-vapoursynth",
      "title": "FFmpeg or VapourSynth",
      "depth": 2,
      "anchor": "ffmpeg-or-vapoursynth",
      "canonicalUrl": "https://encode.wiki/tools/filtering/basics/#ffmpeg-or-vapoursynth",
      "apiUrl": "https://encode.wiki/api/v1/sections/docs/tools/filtering/basics/ffmpeg-or-vapoursynth.json",
      "markdownUrl": "https://encode.wiki/api/v1/sections/docs/tools/filtering/basics/ffmpeg-or-vapoursynth.md",
      "checksum": "42f0443f771b2c3aa30fbb9b23922a38d9b498617d96e14c40207448d3eb5a76",
      "bytes": 521
    },
    {
      "id": "filter-order",
      "title": "Filter order",
      "depth": 2,
      "anchor": "filter-order",
      "canonicalUrl": "https://encode.wiki/tools/filtering/basics/#filter-order",
      "apiUrl": "https://encode.wiki/api/v1/sections/docs/tools/filtering/basics/filter-order.json",
      "markdownUrl": "https://encode.wiki/api/v1/sections/docs/tools/filtering/basics/filter-order.md",
      "checksum": "21e145078225df7fba6bf2a65c661feb4580e985fca638955ab87d03741b1b2b",
      "bytes": 1035
    },
    {
      "id": "starting-script",
      "title": "Starting script",
      "depth": 2,
      "anchor": "starting-script",
      "canonicalUrl": "https://encode.wiki/tools/filtering/basics/#starting-script",
      "apiUrl": "https://encode.wiki/api/v1/sections/docs/tools/filtering/basics/starting-script.json",
      "markdownUrl": "https://encode.wiki/api/v1/sections/docs/tools/filtering/basics/starting-script.md",
      "checksum": "f10dff6fcbda0ccd2f9bf92fed6ec6fbf228473c90345cdb6a301fdf0f905c93",
      "bytes": 1509
    },
    {
      "id": "cropping",
      "title": "Cropping",
      "depth": 2,
      "anchor": "cropping",
      "canonicalUrl": "https://encode.wiki/tools/filtering/basics/#cropping",
      "apiUrl": "https://encode.wiki/api/v1/sections/docs/tools/filtering/basics/cropping.json",
      "markdownUrl": "https://encode.wiki/api/v1/sections/docs/tools/filtering/basics/cropping.md",
      "checksum": "3223d28030fa82f1d7030e5fc53bed8bb3fbbbb1b8a0d7fc24493d367354cb5e",
      "bytes": 566
    },
    {
      "id": "resizing",
      "title": "Resizing",
      "depth": 2,
      "anchor": "resizing",
      "canonicalUrl": "https://encode.wiki/tools/filtering/basics/#resizing",
      "apiUrl": "https://encode.wiki/api/v1/sections/docs/tools/filtering/basics/resizing.json",
      "markdownUrl": "https://encode.wiki/api/v1/sections/docs/tools/filtering/basics/resizing.md",
      "checksum": "333ce7039ea4db5a28e88781af1a4494ab3cb6a59ae0f0b91a9fd3e95bf126e3",
      "bytes": 1675
    },
    {
      "id": "trimming",
      "title": "Trimming",
      "depth": 2,
      "anchor": "trimming",
      "canonicalUrl": "https://encode.wiki/tools/filtering/basics/#trimming",
      "apiUrl": "https://encode.wiki/api/v1/sections/docs/tools/filtering/basics/trimming.json",
      "markdownUrl": "https://encode.wiki/api/v1/sections/docs/tools/filtering/basics/trimming.md",
      "checksum": "c85d69dbefa20b668fcb6bd9e7b6a604b67cff724bf48a4ff89c91694876adca",
      "bytes": 444
    }
  ],
  "outboundReferences": [
    "/guides/audio-and-subtitles/",
    "/img/vs2.webp",
    "/reference/colorimetry/intro/",
    "/tools/filtering/antialiasing/",
    "/tools/filtering/deband/",
    "/tools/filtering/dehalo/",
    "/tools/filtering/deinterlace/",
    "/tools/filtering/denoise/",
    "/tools/filtering/graining/",
    "/tools/filtering/ivtc/",
    "/tools/filtering/vapoursynth/",
    "https://github.com/Jaded-Encoding-Thaumaturgy/vs-jetpack",
    "https://slow.pics/"
  ],
  "checksum": "a85fe8ecb8d6d0cf56b590d45649868e19d55c255c8cf4e62b47d92de2fbaac7",
  "contentFormat": "markdown",
  "markdown": "Filtering is everything you do to the picture between decoding the source and handing it to the encoder: cropping borders, fixing interlacing, removing noise or banding. Most clean modern sources need little or none of it. Filter when you can point at a specific defect, and compare the result with the untouched source, because every filter that repairs something also discards or invents some detail.\n\n## FFmpeg or VapourSynth\n\nFFmpeg's `-vf` filters are fine for one-step jobs: cropping, scaling, a plain deinterlace, light denoising. [VapourSynth](/tools/filtering/vapoursynth/) is the better choice once you chain several filters, want higher internal precision, need masks or per-scene settings, or want to preview frames while you tune. This section uses VapourSynth with the [vs-jetpack](https://github.com/Jaded-Encoding-Thaumaturgy/vs-jetpack) collection and gives the FFmpeg equivalent wherever it is good enough.\n\n## Filter order\n\n![Flowchart of a typical filter chain: load, deinterlace or IVTC, convert to 16-bit, resize, denoise, dehalo, antialias, deband, add grain, dither to 10-bit](/img/vs2.webp)\n\nThe usual order, skipping every step the source doesn't need:\n\n1. [Inverse telecine](/tools/filtering/ivtc/) or [deinterlace](/tools/filtering/deinterlace/), on the source exactly as decoded. Anything that resizes or blends first mixes the fields and ruins the match.\n2. Crop.\n3. Convert to 16-bit, so later filters round less.\n4. Resize.\n5. [Denoise](/tools/filtering/denoise/), so noise doesn't confuse the edge masks of the filters after it.\n6. [Dehalo](/tools/filtering/dehalo/).\n7. [Antialias](/tools/filtering/antialiasing/).\n8. [Deband](/tools/filtering/deband/), late because earlier filters can create banding.\n9. [Grain](/tools/filtering/graining/), if you want it.\n10. Dither down to the output bit depth, usually 10-bit.\n\nCheck that the problem exists before you filter for it. Antialiasing a source with no aliasing only blurs it.\n\n## Starting script\n\n```python\nimport vapoursynth as vs\nfrom vssource import BestSource\nfrom vstools import finalize_clip\n\ncore = vs.core\n\nclip = BestSource.source(\"input.mkv\", bits=16)\n\n# filters go here\n\nclip = finalize_clip(clip, bits=10)\nclip.set_output()\n```\n\n`BestSource.source` loads the file frame-accurately (the first load indexes the whole file, so it is slow once) and runs vstools' `initialize_clip` on it. That converts to the requested bit depth with dithering and fills in any missing colour properties (matrix, primaries, transfer, range). Without `bits` it converts to 32-bit float, which many plugins don't accept, so pass `bits=16`.\n\nMissing properties are guessed from the resolution, which is right for most sources and wrong for, say, a 1080p BT.709 master upscaled to 4K. When you know better, say so:\n\n```python\nfrom vstools import Matrix, Primaries, Transfer\n\nclip = BestSource.source(\n    \"input.mkv\", bits=16,\n    matrix=Matrix.BT709, primaries=Primaries.BT709, transfer=Transfer.BT709,\n)\n```\n\nThese tags only label the samples; they don't convert anything, so don't tag HDR or unknown material as BT.709 to make a warning go away. See [colorimetry](/reference/colorimetry/intro/) for what each property means.\n\n`finalize_clip` dithers down to 10-bit by default. Pass `clamp_tv_range=True` to also clip limited-range video to the legal range.\n\n[VFM](/tools/filtering/ivtc/) only accepts 8-bit input, so for IVTC load with `bits=None`, do the field matching, and convert afterwards.\n\n## Cropping\n\nRemove black bars so you don't spend bits and encoder effort on them. FFmpeg's `cropdetect` finds them; run it over a minute or so of bright footage, since dark scenes fool it:\n\n```sh\nffmpeg -ss 300 -t 60 -i input.mkv -vf cropdetect=round=2 -f null -\n```\n\nThe last `crop=w:h:x:y` it prints is the answer. Look at the edges in several scenes before trusting it; bars are not always symmetric. For 4:2:0 video all four crop values must be even.\n\n```python\nclip = clip.std.Crop(top=138, bottom=138)\n```\n\nThe FFmpeg equivalent is `-vf crop=1920:804:0:138`.\n\n## Resizing\n\nDon't upscale unless you have to. A larger file at the same quality is not a better encode, and the extra pixels are invented. When you do resize, the kernel matters.\n\nThe built-in resizers are the simplest:\n\n```python\nclip = clip.resize.Spline36(1280, 720)\n```\n\nBicubic is fast and soft, Lanczos is sharper but rings more, and Spline36 sits between them. FFmpeg's default `scale` is bicubic; use `scale=-2:720:flags=lanczos` for the sharper option.\n\nvskernels offers more control, including resizing in linear light or with a sigmoid curve, which reduces ringing:\n\n```python\nfrom vskernels import Catrom, Hermite\n\nclip = Catrom().scale(clip, 1280, 720)\nclip = Catrom(sigmoid=True).scale(clip, 1280, 720)\nclip = Hermite(linear=True).scale(clip, 1280, 720)\n```\n\nCatrom (Catmull-Rom) is a sharp bicubic and a good default for downscaling. Hermite in linear light rings very little but thins fine lines, which some people dislike. Linear-light scaling only gives correct results when the transfer is tagged correctly.\n\nFor upscaling, `EwaLanczos` is a sharp general-purpose kernel, and NNEDI3 doubles resolution with a neural-network interpolator that keeps edges clean:\n\n```python\nfrom vskernels import EwaLanczos\nfrom vsaa import NNEDI3\n\nclip = EwaLanczos().scale(clip, 3840, 2160)\nclip = NNEDI3().scale(clip, 3840, 2160)  # alternative\n```\n\nMachine-learning upscalers such as Waifu2x and ArtCNN are available through `vsscale`, but they need ONNX models (`vsscale onnx download`) and an inference backend, and they can invent texture, so compare them with a plain kernel first. [slow.pics](https://slow.pics/) is the usual place to put screenshots side by side.\n\n## Trimming\n\nVapourSynth clips use Python slices. The end frame is exclusive:\n\n```python\nclip = clip[1805:3185]              # frames 1805-3184\nclip = clip[:1805] + clip[3185:]    # cut that section out instead\nclip = clip[3185:] + clip[:1805]    # reorder: second part first\n```\n\nTrimming only affects the video. Apply the same cuts to audio and subtitles, and check sync after muxing; see [audio and subtitles](/guides/audio-and-subtitles/).\n"
}
