Log in

View Full Version : Very Effective Field Matching script


Ceppo
2nd October 2023, 23:56
Sorry, I disappeared, but soon I will again. However, today I was playing with avisynth and I found a nice way to make field matching thanks to the VMAF2 plugin. Basically you make a c/n/p match clips, then you deinterlace them with a simple vertical blur, then you make a comparison using the PSNR metric. The highest wins, of course. Works in 99.9% of the cases on my source. I don't have time to make a plugin so, if someone likes it, please do.


mpeg2Source("VTS_01_1.d2v")
global c = last
global n = DoubleWeave(c).SelectOdd()
global p = DoubleWeave(c).SelectOdd().DuplicateFrame(0)
global c = VMAF2(c,Blur(c,0,1),feature = 0)
global n = VMAF2(n,Blur(n,0,1),feature = 0)
global p = VMAF2(p,Blur(p,0,1),feature = 0)
c
ScriptClip("""
propGetFloat(n,"psnr_y") > propGetFloat(c,"psnr_y") ? n : \
propGetFloat(p,"psnr_y") > propGetFloat(c,"psnr_y") ? p : c
""")



The PSNR metric can be also used to detect duplicates to do decimation, since duplicate have a very high PSNR score.

kedautinh12
3rd October 2023, 02:23
any chance in this situation for the first part??
https://forum.doom9.org/showthread.php?p=1992102#post1992102

Ceppo
3rd October 2023, 07:07
Not sure what you mean. :confused:

kedautinh12
3rd October 2023, 08:05
Not sure what you mean. :confused:

Sr for my bad English, edited :D

Ceppo
3rd October 2023, 13:26
I have no idea.

BTW, never used vapoursynth but I did a version for it too.

import vapoursynth as vs
core = vs.core

c = core.d2v.Source("VTS_01_1.d2v")
n = core.std.SeparateFields(c)
n = core.std.DoubleWeave(n)
n = core.std.SelectEvery(n,2,1)
p = core.std.DuplicateFrames(n,0)

c = core.vmaf.Metric(c,core.std.BoxBlur(c,hradius=0),0)
n = core.vmaf.Metric(n,core.std.BoxBlur(n,hradius=0),0)
p = core.vmaf.Metric(p,core.std.BoxBlur(p,hradius=0),0)

def SelectFrame(n, f):
A = f[0].props["psnr_y"]
B = f[1].props["psnr_y"]
C = f[2].props["psnr_y"]
if B > A:
return f[1]
elif C > A:
return f[2]
else:
return f[0]

core.std.ModifyFrame(clip=c,clips=[c, n, p],selector=SelectFrame).set_output()

kedautinh12
3rd October 2023, 13:46
Selur will happy for it :D

Ceppo
3rd October 2023, 16:19
With vinverse we are at the 100% of correct match on my source.

mpeg2Source("VTS_01_1.d2v")
global c = last
global n = DoubleWeave(c).SelectOdd()
global p = DoubleWeave(c).SelectOdd().DuplicateFrame(0)
global c = VMAF2(c,vinverse(c),feature = 0)
global n = VMAF2(n,vinverse(n),feature = 0)
global p = VMAF2(p,vinverse(p),feature = 0)
c
ScriptClip("""
propGetFloat(n,"psnr_y") > propGetFloat(c,"psnr_y") ? n : \
propGetFloat(p,"psnr_y") > propGetFloat(c,"psnr_y") ? p : c
""")

SaurusX
3rd October 2023, 20:45
Very interesting. I've got just the project to test this on.

Ceppo
3rd October 2023, 22:07
This should be more precise.


global c = last
global n = DoubleWeave(c).SelectOdd()
global p = DoubleWeave(c).SelectOdd().DuplicateFrame(0)
global c = VMAF2(c,vinverse(c),feature = 0)
global n = VMAF2(n,vinverse(n),feature = 0)
global p = VMAF2(p,vinverse(p),feature = 0)
c
ScriptClip("""
n_metric = propGetFloat(n,"psnr_y") + propGetFloat(n,"psnr_cb") + propGetFloat(n,"psnr_cr")
c_metric = propGetFloat(c,"psnr_y") + propGetFloat(c,"psnr_cb") + propGetFloat(c,"psnr_cr")
p_metric = propGetFloat(p,"psnr_y") + propGetFloat(p,"psnr_cb") + propGetFloat(p,"psnr_cr")
n_metric > c_metric && n_metric > p_metric ? n : \
p_metric > c_metric && p_metric > n_metric ? p : c
""")

Selur
15th October 2023, 10:39
Can't get the Vapoursynth version working.
I used the code from post #5, packed it into a function and adjusted SelectFrame, where:
if B > A:
return f[1]
elif C > A:
return f[2]
else:
return f[0]
to:
if B > A:
if B > C:
return f[1]
return f[2]
elif C > A:
return f[2]
else:
return f[0]
I ended up with:
# FieldMatching from https://forum.doom9.org/showthread.php?t=185104 by Ceppo
# requires:
# havsfunc https://github.com/Selur/VapoursynthScriptsInHybrid/blob/master/havsfunc.py when Vinverse is enabled (!not working atm!)
# VMAF: https://github.com/HomeOfVapourSynthEvolution/VapourSynth-VMAF
def cFieldMatch(clip: vs.VideoNode, chroma: bool=False, vinverse: bool=False):
# prepare
n = clip
n = core.std.SeparateFields(clip=n) # separate frames
n = core.std.DoubleWeave(clip=n) # double weave
n = core.std.SelectEvery(clip=n, cycle=2, offsets=1) # select odd
p = core.std.DuplicateFrames(clip=n, frames=[0]) # duplicate first frame

# calculate PSNR
if vinverse:
import havsfunc
c = core.vmaf.Metric(reference=clip, distorted=havsfunc.Vinverse(clip), feature=[0])
n = core.vmaf.Metric(reference=n, distorted=havsfunc.Vinverse(n), feature=[0])
p = core.vmaf.Metric(reference=p, distorted=havsfunc.Vinverse(c), feature=[0])
else:
c = core.vmaf.Metric(reference=clip, distorted=core.std.BoxBlur(clip,hradius=0), feature=[0])
n = core.vmaf.Metric(reference=n, distorted=core.std.BoxBlur(n,hradius=0), feature=[0])
p = core.vmaf.Metric(reference=p, distorted=core.std.BoxBlur(p,hradius=0), feature=[0])

def SelectFrameChroma(n, f):
A = f[0].props["psnr_y"] + f[0].props["psnr_cb"] + f[0].props["psnr_cr"] # c
B = f[1].props["psnr_y"] + f[1].props["psnr_cb"] + f[1].props["psnr_cr"] # n
C = f[2].props["psnr_y"] + f[2].props["psnr_cb"] + f[2].props["psnr_cr"] # p
# return max
if B > A:
if B > C:
return f[1]
return f[2]
elif C > A:
return f[2]
else:
return f[0]
def SelectFrame(n, f):
A = f[0].props["psnr_y"] # c
B = f[1].props["psnr_y"] # n
C = f[2].props["psnr_y"] # p
# return max
if B > A:
if B > C:
return f[1]
return f[2]
elif C > A:
return f[2]
else:
return f[0]
# select
if chroma:
return core.std.ModifyFrame(clip=c, clips=[c, n, p], selector=SelectFrameChroma)
return core.std.ModifyFrame(clip=c, clips=[c, n, p], selector=SelectFrame)
The problem is: It does not work. At least here, it does not field match at all.

Does anyone have an idea why it does not work?

Cu Selur

Ceppo
16th October 2023, 19:01
Let me change the problem. Is there a tutorial/sample for making vapoursynth plugins?

Selur
17th October 2023, 18:13
https://forum.doom9.org/showthread.php?t=182961 maybe ?

kedautinh12
19th October 2023, 15:41
Did you try with IT, Ceppo??
https://github.com/Asd-g/AviSynthPlus-Scripts/issues/15#issuecomment-1771105416

kedautinh12
2nd February 2024, 16:47
Asd-g ask you license to update Ctools, Ceppo
https://github.com/Asd-g/AviSynthPlus-Scripts/issues/15#issuecomment-1923731799

takla
3rd February 2024, 13:30
This should be more precise.


global c = last
global n = DoubleWeave(c).SelectOdd()
global p = DoubleWeave(c).SelectOdd().DuplicateFrame(0)
global c = VMAF2(c,vinverse(c),feature = 0)
global n = VMAF2(n,vinverse(n),feature = 0)
global p = VMAF2(p,vinverse(p),feature = 0)
c
ScriptClip("""
n_metric = propGetFloat(n,"psnr_y") + propGetFloat(n,"psnr_cb") + propGetFloat(n,"psnr_cr")
c_metric = propGetFloat(c,"psnr_y") + propGetFloat(c,"psnr_cb") + propGetFloat(c,"psnr_cr")
p_metric = propGetFloat(p,"psnr_y") + propGetFloat(p,"psnr_cb") + propGetFloat(p,"psnr_cr")
n_metric > c_metric && n_metric > p_metric ? n : \
p_metric > c_metric && p_metric > n_metric ? p : c
""")



Much better then:
-vf "dejudder, fps=30000/1001, fieldmatch=mode=pcn_ub:combmatch=full, yadif=deint=interlaced, decimate"
Thanks!

takla
14th February 2024, 13:13
Any way to deinterlace unmatchable fields with this?

Selur
24th March 2026, 14:49
In case anyone is interested: I revisited this and fixed the Vapoursynth script port (https://github.com/Selur/VapoursynthScriptsInHybrid/blob/e72ba8f5c1b6e26262ea5c9844c0e351bef15cfb/fieldmatch.py#L78).