Yesterday, 13:58
Hi Selur,
I'm using Hybrid's VIVTC handling on telecined NTSC film sources (720x480/486, ProRes 422 HQ)
and noticed something in the generated VapourSynth script that I'd like to check with you.
Settings: Filtering > (De-)Interlace/Telecine > Auto deinterlace handling: VIVTC (Vapoursynth)
VFM tab: Mode "2-way match + 3rd match on combed (p/c + n)", Deinterlacer: QTGMC (preset slow),
Order 0, Field 2, Mi 80, Block 32/32, Exclude 16/16, Scene threshold 12, Chroma matching on, Mic match 1.
Seen with Hybrid 2026.09.06.1, 2026.09.26.1 (macOS) and the 2026.10.01.1 Linux AppImage.
The generated script contains:
clip2clip = clip
clip2clip = qtgmc.QTGMC(Input=clip2clip, Preset="slow", TFF=False, ...)
yv12 = core.resize.Bicubic(clip=clip2clip, format=vs.YUV420P8, range_in_s="limited")
clip = core.vivtc.VFM(yv12, clip2=clip2clip, order=0, chroma=False)
...
clip = core.std.FrameEval(clip=clip, eval=functools.partial(postprocess, clip=clip, deinterlaced=clip2clip), prop_src=clip)
clip = core.vivtc.VDecimate(clip)
So VFM both analyses and outputs the already-deinterlaced QTGMC clip, never the original fields.
I measured this on 300 frames: VFMMatch was 'c' on 300/300 and every VFM output frame was
bit-identical to the QTGMC frame. Run on the original fields, VFM chose p on about two-thirds of
those frames, i.e. it actually re-paired fields. Over a full film, the chain's output is about
41 dB PSNR to the original fields, vs about 55 dB when VFM matches the original fields.
The vivtc docs describe clip2 as the format workaround with clip2 = the original
(yv12 = Bicubic(original); VFM(yv12, clip2=original)), and the post-processing example
applies the deinterlacer to the matched clip, used only for frames flagged _Combed.
Questions:
1. Is applying the selected Deinterlacer to every frame before VFM intended? I expected it to be
used only for frames VFM still flags as combed.
2. If it isn't intended, could VIVTC mode generate VFM on the original (clip = 8-bit copy of the
source, clip2 = source) with the deinterlacer only in the FrameEval post-processing?
3. Only order and chroma reach VFM in the script; the Block 32/32 (and other VFM tab values) don't
seem to be passed. Is that expected?
P.S. In case it's useful for other users: with 720x486 sources, the YUV420P8 match clip has 243-row
chroma planes, and vivtc's compareFieldsSlow (field=0) reads one row past the end of the plane
(curnf on row 243). On Linux this intermittently segfaults vspipe in vivtc.so (it never crashed on
macOS for us). Padding the match clip to 488 rows (std.AddBorders(bottom=2)) avoids it.
Thanks for Hybrid and for any insight!
I'm using Hybrid's VIVTC handling on telecined NTSC film sources (720x480/486, ProRes 422 HQ)
and noticed something in the generated VapourSynth script that I'd like to check with you.
Settings: Filtering > (De-)Interlace/Telecine > Auto deinterlace handling: VIVTC (Vapoursynth)
VFM tab: Mode "2-way match + 3rd match on combed (p/c + n)", Deinterlacer: QTGMC (preset slow),
Order 0, Field 2, Mi 80, Block 32/32, Exclude 16/16, Scene threshold 12, Chroma matching on, Mic match 1.
Seen with Hybrid 2026.09.06.1, 2026.09.26.1 (macOS) and the 2026.10.01.1 Linux AppImage.
The generated script contains:
clip2clip = clip
clip2clip = qtgmc.QTGMC(Input=clip2clip, Preset="slow", TFF=False, ...)
yv12 = core.resize.Bicubic(clip=clip2clip, format=vs.YUV420P8, range_in_s="limited")
clip = core.vivtc.VFM(yv12, clip2=clip2clip, order=0, chroma=False)
...
clip = core.std.FrameEval(clip=clip, eval=functools.partial(postprocess, clip=clip, deinterlaced=clip2clip), prop_src=clip)
clip = core.vivtc.VDecimate(clip)
So VFM both analyses and outputs the already-deinterlaced QTGMC clip, never the original fields.
I measured this on 300 frames: VFMMatch was 'c' on 300/300 and every VFM output frame was
bit-identical to the QTGMC frame. Run on the original fields, VFM chose p on about two-thirds of
those frames, i.e. it actually re-paired fields. Over a full film, the chain's output is about
41 dB PSNR to the original fields, vs about 55 dB when VFM matches the original fields.
The vivtc docs describe clip2 as the format workaround with clip2 = the original
(yv12 = Bicubic(original); VFM(yv12, clip2=original)), and the post-processing example
applies the deinterlacer to the matched clip, used only for frames flagged _Combed.
Questions:
1. Is applying the selected Deinterlacer to every frame before VFM intended? I expected it to be
used only for frames VFM still flags as combed.
2. If it isn't intended, could VIVTC mode generate VFM on the original (clip = 8-bit copy of the
source, clip2 = source) with the deinterlacer only in the FrameEval post-processing?
3. Only order and chroma reach VFM in the script; the Block 32/32 (and other VFM tab values) don't
seem to be passed. Is that expected?
P.S. In case it's useful for other users: with 720x486 sources, the YUV420P8 match clip has 243-row
chroma planes, and vivtc's compareFieldsSlow (field=0) reads one row past the end of the plane
(curnf on row 243). On Linux this intermittently segfaults vspipe in vivtc.so (it never crashed on
macOS for us). Padding the match clip to 488 rows (std.AddBorders(bottom=2)) avoids it.
Thanks for Hybrid and for any insight!

