Skip to main content

Inverse-telecine plugin for VapourSynth — Zig port of IT, with the original Avisynth parameters restored

Project description

zit — Inverse Telecine for VapourSynth (Zig port)

A Zig port of the VapourSynth-IT plugin (3:2-pulldown removal for NTSC), with the Avisynth-original parameters restored, high-bit-depth and 4:2:2/4:4:4 support, full frame-property support, and @Vector-based SIMD.

Verified bit-exact against the upstream --c reference path across the integration test grid (8-bit 4:2:0, the only format upstream supports) and on real-world telecined NTSC VOB samples.

Status

Item State
Algorithm port (~5200 LoC Zig)
Bit-exact vs upstream C path ✅ 8-bit 4:2:0 (198 fixture + 720 real-VOB frames)
10/12/16-bit, 4:2:2, 4:4:4 ✅ decisions bit-depth-deterministic; no external oracle exists (upstream is YV12-only) — guarded by consistency + golden tests
All Avisynth params (ref, blend, diMode)
Frame properties ✅ standard + diagnostic
SIMD via @Vector ✅ ~2× over scalar, ~4× over VIVTC VFM
Cross-compile Linux / macOS (x86_64 + aarch64), Windows x86_64
CI workflow ✅ lint + unit (Debug & ReleaseFast) + cross + gating integration suite
AI-assisted port ✅ Anthropic's Claude — verified byte-for-byte against the upstream C reference

Quick start

import vapoursynth as vs
core = vs.core

clip = core.bs.VideoSource("source.vob")
clip = core.zit.IT(clip)          # default: fps=24, ref="TOP", diMode=3
clip.set_output()

Plugin reference

Plugin namespace: zit. Function: IT.

Full signature:

core.zit.IT(
    clip,
    fps=24,
    threshold=20,
    pthreshold=75,
    ref="TOP",
    blend=0,
    diMode=3,
)

Parameters

Name Type Default Description
clip vnode Input clip. Integer YUV, 8/10/12/16-bit, 4:2:0 / 4:2:2 / 4:4:4. Width a multiple of 16 and ≤ 8192; height even (a multiple of 4 for 4:2:0), ≤ 8192; constant format and frame rate.
fps int 24 24 = inverse telecine (decimate 5→4, output 24000/1001 fps), 30 = field-matching only (input fps preserved).
threshold int 20 Field-match decision sensitivity. Lower = more aggressive matching. Valid range 0–100000.
pthreshold int 75 Progressive-classification threshold for _Combed. Adjusted internally for resolution. Below: frame is ip='P' (clean match); above: ip='I' (deinterlaced). Valid range 0–100000.
ref data "TOP" Field-order / match-search direction (case-insensitive). One of: TOP, BOTTOM, ALL, NONE (see below).
blend int (0/1) 0 When 1 and fps=24, blends adjacent post-matched frames with a triangular kernel for smoother 24p output. Motion-gated — only fires on high-motion 5-frame blocks. Ignored when fps=30.
diMode int 3 Deinterlace strategy applied when a frame is classified ip='I'. See below.

ref values

Value Avisynth name Effect
"TOP" REF_PREV Field match looks at the previous frame as the bottom-field source. Standard for TFF source. Same as the VapourSynth upstream's behaviour.
"BOTTOM" REF_NEXT Field match looks at the next frame. Use for BFF source.
"ALL" REF_ALL Evaluates both prev and next, picks the one with stronger evidence.
"NONE" REF_NONE Skips field-matching entirely; every frame is treated as interlaced and dispatched to the deinterlacer.

diMode values

Value Avisynth name Behaviour
0 DI_MODE_NONE No deinterlace. Just field-copy from the chosen match (same as the clean-match path).
1 DI_MODE_DEINTERLACE The full Avisynth deinterlacer: per-pixel scores C, P, N, avg(C,P), avg(C,N) and picks the lowest interlace score, with motion-gated vertical-average fallback.
2 DI_MODE_SIMPLE_BLUR Vertical (T + 2·C + B) / 4 blur on pixels flagged by the motion map.
3 DI_MODE_ONE_FIELD Default. Field-interpolation using motion + simple-blur maps. The VapourSynth upstream hardcodes this mode.

Frame properties on output

Standard (always set)

Key Type Description
_FieldBased int Always 0. Output is progressive after IVTC.
_Combed int 0 if the algorithm matched cleanly (ip='P'), 1 if it had to deinterlace (ip='I').
_DurationNum, _DurationDen int Per-frame duration derived from the output framerate. At fps=24 mode this becomes 1001 / 24000 per frame instead of the source's 1001 / 30000.
_Matrix, _Transfer, _Primaries, _ChromaLocation, _Range/_ColorRange, _SARNum/_SARDen int Inherited from the source frame via propSrc. Pass-through unchanged.

Diagnostic (set for inspection/scripting)

Key Type Description
ITMatch utf8 (1 char) Match decision: 'C', 'P', 'N' (uppercase = strong, lowercase = weak), 'U' if not evaluated.
ITMflag utf8 (1 char) Decimation code in fps=24 mode: 'D'/'d'/'x'/'y'/'z'/'+'/'.'. 'U' in fps=30 mode.
ITIpFlag utf8 (1 char) 'P' (progressive) or 'I' (interlaced); 'U' if not run.
ITIvC, ITIvP, ITIvN, ITIvM int Interlace-evidence counters from EvalIV against C, P, N, and the chosen match M.
ITDiffP0, ITDiffP1, ITDiffS0, ITDiffS1 int Motion-map stats: rough motion (P0/P1) and saturated motion (S0/S1) on even/odd field rows.
ITBlended int (0/1) 1 if the blend=true code path produced this output frame.

Convention follows VFM/VDecimate (camelCase, plugin-name prefix, no dots — VS API 4 silently rejects keys with dots).

Building from source

Requires Zig 0.16.0+. The VapourSynth API 4 bindings come from the vapoursynth-zig package pinned in build.zig.zon.

zig build --release=fast            # native shared library -> zig-out/lib/libzit.so
zig build test                       # unit tests (also run with --release=fast in CI)
zig build cross                      # release artefacts for all five targets

Cross-compile produces (ReleaseFast, stripped):

  • zig-out/linux-x86_64/libzit.so, zig-out/linux-aarch64/libzit.so
  • zig-out/macos-x86_64/libzit.dylib, zig-out/macos-aarch64/libzit.dylib
  • zig-out/windows-x86_64/zit.dll

Installation

Via pip (recommended)

pip install vapoursynth-zit

The wheel installs the platform-matched binary into VapourSynth's auto-discovery path (site-packages/vapoursynth/plugins/), so core.zit.IT(...) is available without any further LoadPlugin calls.

Pre-built wheels are available for Linux (x86_64, aarch64), macOS (x86_64, aarch64), and Windows (x86_64).

Manual install (zip from a release)

If you'd rather not pull in pip, grab the matching *.zip from the Releases page and drop the binary into a VapourSynth plugin directory:

  • Linux: /usr/local/lib/vapoursynth/
  • macOS: ~/Library/ApplicationSupport/VapourSynth/plugins/
  • Windows: vapoursynth64/plugins/ next to vsedit.exe

Performance

500 frames of a 720×480 NTSC telecined VOB, ReleaseFast build:

Pipeline fps
core.zit.IT(clip, fps=30) 2697
core.zit.IT(clip, fps=24) 2236
core.zit.IT(clip, fps=24, diMode=1) 2355
core.zit.IT(clip, fps=24, diMode=2) 2459
core.zit.IT(clip, fps=24, blend=1) 1725
core.vivtc.VFM(clip, order=1) 596
core.vivtc.VFMcore.vivtc.VDecimate 1271

zit fps=30 is ~4.5× faster than vivtc.VFM; zit fps=24 is ~1.8× faster than vivtc.VFM + VDecimate.

Differences from the VapourSynth upstream

The VapourSynth upstream (HomeOfVapourSynthEvolution/VapourSynth-IT) ported only a subset of the Avisynth original. This port reintroduces everything plus fixes a few latent bugs:

  1. Avisynth-original parameters back: ref (TOP/BOTTOM/ALL/NONE), blend, diMode (0/1/2/3). The upstream hardcoded ref="TOP"/blend=false/diMode=3 and removed the parameters.
  2. Frame properties (_FieldBased, _Combed, _Duration*, inheritance of source props, plus IT* diagnostics). Upstream calls newVideoFrame(propSrc=null) so output frames carry no metadata, which breaks downstream core.resize.* colorspace handling.
  3. Threading: registered as fmParallelRequests. Upstream uses fmParallel despite sharing mutable per-instance state across calls — a latent race condition that VS R55+ exposes more often.
  4. Frame request range: widened to cover what the algorithm actually reads ([base-2, base+6] for fps=24, [n-2, n+2] for fps=30 — plus [base-3, base+7] when blend=true). Upstream relied on the API 3 sync getFrame to retrieve neighbours, which is no longer permitted from inside getFrameFilter under API 4.
  5. Edge clamping of frame indices passed to getFrameFilter. The upstream algorithm fetches n-1 even at n=0; under API 3 the core silently clamped, under API 4 it returns null and dereferences crash.

The reference build under reference/vapoursynth-cpp-api4/ applies the same framework-level fixes (otherwise it would segfault under VS R76) and is what the bit-exact comparison test runs against.

Repository layout

src/
├── plugin.zig    # VapourSynth plugin entry (VapourSynthPluginInit2)
├── filter.zig    # Filter instance + getFrame lifecycle + frame-prop setting
├── state.zig     # CFrameInfo, CTFblockInfo, CallState
├── plane.zig     # syp/dyp accessors, adjPara, clipFrame/X/Y
├── edge.zig      # makeDeMap                (SIMD)
├── eval_iv.zig   # evalIv                    (SIMD)
├── motion.zig    # makeMotionMap, makeMotionMap2Max/Min, makeSimpleBlurMap (SIMD)
├── scene.zig     # checkSceneChange          (SIMD)
├── decide.zig    # compCp / compCn / decide / setFt
├── output.zig    # copyCPNField / deintOneField / simpleBlur / deinterlace
├── blend.zig     # BlendFrame_YV12 port
├── simd.zig      # @Vector helpers (pavgb, absDiff, subSat, expandPairs)
└── scalar.zig    # scalar twins of the SIMD helpers (pavgbScore, toMapByte)

reference/
├── avisynth/                # original IT_YV12 0.1.03 source (read-only)
├── vapoursynth-cpp/         # upstream VS-IT @ 6fc9be8 (read-only)
└── vapoursynth-cpp-api4/    # mechanical API3→API4 port of upstream; build for bit-exact comparison

tests/integration/           # pytest suite — properties, golden hashes, upstream-compare, determinism, bit depth
scripts/                     # gen_testclip / param_grid / regen_golden / compare_upstream / compare_vivtc / ...
docs/upstream_reference.md   # design notes

Credits

  • Original IT 0.051 — thejam79 (2002)
  • Avisynth IT_YV12 0.1.03 — minamina (2003)
  • 64-bit / 8k mod — poodle
  • VapourSynth port — msg7086 (2014)
  • Zig port — this repo

License

GPL-2.0-or-later (inherited from upstream). See LICENSE.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distributions

No source distribution files available for this release.See tutorial on generating distribution archives.

Built Distributions

If you're not sure about the file name format, learn more about wheel file names.

vapoursynth_zit-1.4.0-py3-none-win_amd64.whl (945.4 kB view details)

Uploaded Python 3Windows x86-64

vapoursynth_zit-1.4.0-py3-none-manylinux2014_x86_64.whl (872.0 kB view details)

Uploaded Python 3

vapoursynth_zit-1.4.0-py3-none-macosx_11_0_arm64.whl (616.7 kB view details)

Uploaded Python 3macOS 11.0+ ARM64

vapoursynth_zit-1.4.0-py3-none-macosx_10_9_x86_64.whl (842.1 kB view details)

Uploaded Python 3macOS 10.9+ x86-64

File details

Details for the file vapoursynth_zit-1.4.0-py3-none-win_amd64.whl.

File metadata

File hashes

Hashes for vapoursynth_zit-1.4.0-py3-none-win_amd64.whl
Algorithm Hash digest
SHA256 44c937c5b5dfc4298c3f35f9624e3cd9d5a6fbada87c6171325a67d078f37755
MD5 392b561ecd1370389f3d9d3ee0c43162
BLAKE2b-256 e175e03df899718ec6bf4f53b09384ec090e8d5e3d37a84810dbb23249646030

See more details on using hashes here.

Provenance

The following attestation bundles were made for vapoursynth_zit-1.4.0-py3-none-win_amd64.whl:

Publisher: release.yml on theChaosCoder/vapoursynth-it

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file vapoursynth_zit-1.4.0-py3-none-manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for vapoursynth_zit-1.4.0-py3-none-manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 5ddd818c247d7c28ce9c348ecfd683b52ae90efa2514a8be38ac43adf53e00bd
MD5 be39be898521753b8a0446202726e6e4
BLAKE2b-256 5e866a7aa87c69913049a2633ba6138cf86b3c72173956732c869cb80343d72d

See more details on using hashes here.

Provenance

The following attestation bundles were made for vapoursynth_zit-1.4.0-py3-none-manylinux2014_x86_64.whl:

Publisher: release.yml on theChaosCoder/vapoursynth-it

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file vapoursynth_zit-1.4.0-py3-none-manylinux2014_aarch64.whl.

File metadata

File hashes

Hashes for vapoursynth_zit-1.4.0-py3-none-manylinux2014_aarch64.whl
Algorithm Hash digest
SHA256 c513c47f9721f670852662b322a78e398b8c81a252026f1777e7bee1fdd9f737
MD5 6dc77c627e716b9d94309fc9ea0ce711
BLAKE2b-256 033bf355f6f2a7b1b5ace6f0d11bd12981908df8b6d1b6e3dabcb1a40ee423fa

See more details on using hashes here.

Provenance

The following attestation bundles were made for vapoursynth_zit-1.4.0-py3-none-manylinux2014_aarch64.whl:

Publisher: release.yml on theChaosCoder/vapoursynth-it

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file vapoursynth_zit-1.4.0-py3-none-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for vapoursynth_zit-1.4.0-py3-none-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 7908cd3c1eadababb570663466b7a848def4b2298e5e81ae6a01134b66fbc033
MD5 02f4c06ffc70139bc06842ede4e42bbe
BLAKE2b-256 b5587de1da6f3f28d93c4500c3773337aca6ec84158b13d0b567045aa954f4ae

See more details on using hashes here.

Provenance

The following attestation bundles were made for vapoursynth_zit-1.4.0-py3-none-macosx_11_0_arm64.whl:

Publisher: release.yml on theChaosCoder/vapoursynth-it

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file vapoursynth_zit-1.4.0-py3-none-macosx_10_9_x86_64.whl.

File metadata

File hashes

Hashes for vapoursynth_zit-1.4.0-py3-none-macosx_10_9_x86_64.whl
Algorithm Hash digest
SHA256 20f9473759ac7f26ef759a1a1ff7521c472fcb157c23b93f367afb443e7dec76
MD5 113febeb81453f825405622f4ddef624
BLAKE2b-256 a994bb73b93547de05e3f0ae38c404570f0a65b004e04d3c13cf64ebb5e79b4a

See more details on using hashes here.

Provenance

The following attestation bundles were made for vapoursynth_zit-1.4.0-py3-none-macosx_10_9_x86_64.whl:

Publisher: release.yml on theChaosCoder/vapoursynth-it

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page