Log in

View Full Version : Trying to port CQTGMC to Vapoursynth


Selur
15th January 2023, 12:02
I'm trying to port CQTGMC (https://forum.doom9.org/showthread.php?t=183823) to Vapoursynth, but got a few issues:
here's what I got

# Imports
import vapoursynth as vs
import os
import sys
from typing import Optional, Union, Sequence
# getting Vapoursynth core
core = vs.core


def CQTGMC(clip: vs.VideoNode, Sharpness: float=0.25, thSAD1: int=192, thSAD2: int=320, thSAD3: int=128, thSAD4: int=320) -> vs.VideoNode:

flip = core.std.FlipHorizontal(clip)
flip = Padding(clip=clip,top=clip.width%64)
flip = Padding(clip=clip,right=clip.height%64)
padded = core.std.StackHorizontal([clip,flip])
# todo: optional use nnedi3CL
bobbed = core.znedi3.nnedi3(clip=padded, field=2)
denoised = core.rgvs.RemoveGrain(clip=bobbed,mode=12)
denoised = core.fmtc.resample(clip=denoised, kernel="gauss", w=denoised.width, h=denoised.height, scaleh=denoised.width+0.0001, interlaced=False, interlacedd=False)
denoised = core.resize.Bicubic(clip=denoised, format=bobbed.format, dither_type="error_diffusion") # adjust format after fmtc
denoised = core.std.Merge(clipa=denoised, clipb=bobbed,weight=0.25)

srchClip = denoised
csuper = core.mv.Super(clip=denoised);
bvec = core.mv.Analyse(csuper,isb=True,blksize=64,overlap=32)
fvec = core.mv.Analyse(csuper,isb=False,blksize=64,overlap=32)
Comp1 = core.mv.Compensate(denoised,csuper,bvec,thsad=thSAD2)
Comp2 = core.mv.Compensate(denoised,csuper,fvec,thsad=thSAD2)
denoised = core.std.Interleave([Comp1,denoised,Comp2])
csuper = core.mv.Super(clip=denoised)
bvec = core.mv.Analyse(csuper,isb=True,blksize=64,overlap=32)
fvec = core.mv.Analyse(csuper,isb=False,blksize=64,overlap=32)
Inter = core.mv.FlowInter(denoised, csuper, bvec,fvec,blend=False)
a = core.std.SelectEvery(clip=Inter, cycle=2, offsets=0)
b = core.std.SelectEvery(clip=Inter, cycle=2, offsets=1)
P = core.std.PlaneStats(srchClip, srchClip[0] + srchClip)
N = core.std.Trim(srchClip,1)
N = core.std.PlaneStats(N, N[0] + N)

srchClip = a # N < P doesn't work
#srchClip = (N < P) ? a : b

csuper = core.mv.Super(srchClip)
bVec1 = core.mv.Analyse(csuper,isb=True,overlap=4,delta=1)
fVec1 = core.mv.Analyse(csuper,isb=False,overlap=4,delta=1)
csuper = core.mv.Super(bobbed,levels=1)
bComp1 = core.mv.Compensate(bobbed,csuper,bVec1,thsad=thSAD3)
fComp1 = core.mv.Compensate(bobbed,csuper,fVec1,thsad=thSAD3)
X=1 # no clue how to get field order
Inter = core.std.Interleave([
bobbed.std.SetFrameProp(prop="_FieldBased", intval=X).std.SeparateFields().std.SelectEvery(4,0),
fComp1.std.SetFrameProp(prop="_FieldBased", intval=X).std.SeparateFields().std.SelectEvery(4,1),
bComp1.std.SetFrameProp(prop="_FieldBased", intval=X).std.SeparateFields().std.SelectEvery(4,2),
bobbed.std.SetFrameProp(prop="_FieldBased", intval=X).std.SeparateFields().std.SelectEvery(4,3)
])
weaved = core.std.DoubleWeave(Inter)[::2]
csuper = core.mv.Super(weaved,levels=1)
bComp1 = core.mv.Compensate(weaved, csuper, bVec1,thsad=thSAD4)
fComp1 = core.mv.Compensate(weaved, csuper, fVec1,thsad=thSAD4)
tMax = mt_logic(fComp1,mode="max").mt_logic(bComp1,mode="max") # does not work, missing c1&c2 parameters
tMin = mt_logic(fComp1,mode="min").mt_logic(bComp1,"min") # does not work, missing c1&c2 parameters
degrained = core.std.Degrain1(csuper,bVec1,fVec1,thsad=thSAD1)
sharpen = mt_adddiff(degrained, mt_makediff(degrained.rgvs.RemoveGrain(mode=20)))
sharpen = mt_clamp(sharpen,tMax,tMin,Sharpness,Sharpness,3,3)
csuper = core.mv.Super(sharpen, levels=1)
degrained = core.std.Degrain1(csuper,bVec1,fVec1,thsad=thSAD1)
degrained = core.std.Crop(0,0,-(clip.width%64),-(clip.width%64))
return degrained

def mt_adddiff(self, c1, c2, planes=[0]):
expr = ('x y + {mid} -').format(mid=self.mid)
expr = [(i in planes) * expr for i in range(3)]
return self.std.Expr([c1, c2], expr)


#from dehalo_alpha.py https://github.com/darealshinji/vapoursynth-plugins/blob/master/scripts/dehalo_alpha.py
def mt_logic(self, c1, c2, mode, th1=0, th2=0, planes=0):
if mode == 'min':
expr = lambda x, y: min(x + th1, y + th2)
elif mode == 'max':
expr = lambda x, y: max(x + th1, y + th2)
else:
raise InvalidArgument('%s is not a valid mode for mt_logic' % mode)
return self.mt_lutxy(c1, c2, expr, planes)

# from havsfunc
def Padding(clip: vs.VideoNode, left: int = 0, right: int = 0, top: int = 0, bottom: int = 0) -> vs.VideoNode:
if not isinstance(clip, vs.VideoNode):
raise vs.Error('Padding: this is not a clip')

if left < 0 or right < 0 or top < 0 or bottom < 0:
raise vs.Error('Padding: border size to pad must not be negative')

width = clip.width + left + right
height = clip.height + top + bottom

return clip.resize.Point(width, height, src_left=-left, src_top=-top, src_width=width, src_height=height)

# from havsfunc
def mt_clamp(
clip: vs.VideoNode,
bright_limit: vs.VideoNode,
dark_limit: vs.VideoNode,
overshoot: int = 0,
undershoot: int = 0,
planes: Optional[Union[int, Sequence[int]]] = None,
) -> vs.VideoNode:
if not (isinstance(clip, vs.VideoNode) and isinstance(bright_limit, vs.VideoNode) and isinstance(dark_limit, vs.VideoNode)):
raise vs.Error('mt_clamp: this is not a clip')

if bright_limit.format.id != clip.format.id or dark_limit.format.id != clip.format.id:
raise vs.Error('mt_clamp: clips must have the same format')

plane_range = range(clip.format.num_planes)

if planes is None:
planes = list(plane_range)
elif isinstance(planes, int):
planes = [planes]

return core.std.Expr([clip, bright_limit, dark_limit], expr=[f'x y {overshoot} + min z {undershoot} - max' if i in planes else '' for i in plane_range])


Issue 1:
a = core.std.SelectEvery(clip=Inter, cycle=2, offsets=0)
b = core.std.SelectEvery(clip=Inter, cycle=2, offsets=1)
P = core.std.PlaneStats(srchClip, srchClip[0] + srchClip)
N = core.std.Trim(srchClip,1)
N = core.std.PlaneStats(N, N[0] + N)
srchClip = (N < P) ? a : b
doesn't work since there is a syntax error in N < P, no clue which :(

Issue 2:

Inter = core.std.Interleave([
bobbed.std.SeparateFields().std.SelectEvery(4,0),
fComp1.std.SeparateFields().std.SelectEvery(4,1),
bComp1.std.SeparateFields().std.SelectEvery(4,2),
bobbed.std.SeparateFields().std.SelectEvery(4,3)
])
fails since field isn't set.
I could change it to:

X = 1
Inter = core.std.Interleave([
bobbed.std.SetFrameProp(prop="_FieldBased", intval=X).std.SeparateFields().std.SelectEvery(4,0),
fComp1.std.SetFrameProp(prop="_FieldBased", intval=X).std.SeparateFields().std.SelectEvery(4,1),
bComp1.std.SetFrameProp(prop="_FieldBased", intval=X).std.SeparateFields().std.SelectEvery(4,2),
bobbed.std.SetFrameProp(prop="_FieldBased", intval=X).std.SeparateFields().std.SelectEvery(4,3)
])

but I don't know how to set X from the input clip.

Issue 3:
tMax = mt_logic(fComp1,mode="max").mt_logic(bComp1,mode="max")
tMin = mt_logic(fComp1,mode="min").mt_logic(bComp1,"min")
the mt_logic calls are missing c1 and c2, and I have no clue how to set them.

Would be nice if someone could help.

-----
Thanks to all that helped think I got a working (but still buggy) version https://github.com/Selur/VapoursynthScriptsInHybrid/blob/master/cqtgmc.py now.

Cu Selur

kedautinh12
15th January 2023, 12:13
In issue 1, why don't change to cycle=3?

Selur
15th January 2023, 12:19
a = core.std.SelectEvery(clip=Inter, cycle=3, offsets=0)
b = core.std.SelectEvery(clip=Inter, cycle=3, offsets=1)
P = core.std.PlaneStats(srchClip, srchClip[0] + srchClip)
N = core.std.Trim(srchClip,1)
N = core.std.PlaneStats(N, N[0] + N)
srchClip = (N < P) ? a : b
still fails. ( suspect N&P are not int or floats,..)

Cu Selur

kedautinh12
16th January 2023, 09:01
a = core.std.SelectEvery(clip=Inter, cycle=3, offsets=0)
b = core.std.SelectEvery(clip=Inter, cycle=3, offsets=1)
P = core.std.PlaneStats(srchClip, srchClip[0] + srchClip)
N = core.std.Trim(srchClip,1)
N = core.std.PlaneStats(N, N[0] + N)
srchClip = (N < P) ? a : b
still fails. ( suspect N&P are not int or floats,..)

Cu Selur

can change to (maybe)

a = core.std.SelectEvery(clip=Inter, cycle=3, offsets=0)
b = core.std.SelectEvery(clip=Inter, cycle=3, offsets=1)
diffclip = core.std.PlaneStats(srchClip, srchClip[0] + srchClip)
P = f.props['PlaneStatsDiff']
N = P.core.std.Trim(srchClip,1)
def selectQTGMC(n, f):
if N < P:
return a
else:
return b

srchClip = core.std.FrameEval(srchClip, selectQTGMC, prop_src=diffclip)
return srchClip

Selur
16th January 2023, 16:18
P = f.props['PlaneStatsDiff']
Where does 'f' come from ?

kedautinh12
16th January 2023, 16:50
Oh, my mistake

a = core.std.SelectEvery(clip=Inter, cycle=3, offsets=0)
b = core.std.SelectEvery(clip=Inter, cycle=3, offsets=1)
diffclip = core.std.PlaneStats(srchClip, srchClip[0] + srchClip)
def selectQTGMC(n, f):
P = f.props['PlaneStatsDiff']
N = P.core.std.Trim(srchClip,1)
if N < P:
return a
else:
return b

srchClip = core.std.FrameEval(srchClip, selectQTGMC, prop_src=diffclip)
return srchClip

_Al_
16th January 2023, 20:17
'''
srchClip
ScriptClip("""
P = YDifferenceFromPrevious()
N = Trim(1,0).YDifferenceFromPrevious()
N < P ? CQTGMC_A : CQTGMC_B
""")
'''
that part might go like this:

a = core.std.SelectEvery(clip=Inter, cycle=3, offsets=0)#trusting your code
b = core.std.SelectEvery(clip=Inter, cycle=3, offsets=1)
srchClip = srchClip.std.PlaneStats(plane=0, prop='Y')
srchClip_next = srchClip[1:]+srchClip[-1:] #delete first frame and double last frame to have same frame count
srchClip_next = srchClip_next.std.PlaneStats(plane=0, prop='Y')
def Y_preference(n,f,a,b):
if f[0].props['YAverage'] < f[0].props['YAverage']:
return a
else:
return b
result = a.std.FrameEval(functools.partial(Y_preference, a=a,b=b), prop_src=[srchClip,srchClip_next])

_Al_
16th January 2023, 20:38
actually, it is just based on luma difference per pixel, so using Expr and Overlay:
srchClip_next = srchClip[1:]+srchClip[-1:] #delete first frame and double last frame to have same frame count
srchClip_Y = srchClip.std.ShufflePlanes(0, vs.GRAY)
srchClip_Y_next = srchClip_Y_next.std.ShufflePlanes(0, vs.GRAY)
mask = core.std.Expr([srchClipY, srchClip_Y_next], 'x y > 255 0 ?')#255 or max size value for bitdepth
result = havsfunc.Overlay(a, b, mask)
If it is a pixel difference, what actually YDifferenceFromPrevious() does?

_Al_
16th January 2023, 21:35
well, isn't like this, there is two differences going and one trimming as well:

Y = srchClip.std.ShufflePlanes(0, vs.GRAY)
diffclip = core.std.PlaneStats(Y, Y[0]+Y[0:-1])
Y_trimmed =Y[1:]+Y[-1:]
diffclip_trimmed = core.std.PlaneStats(Y_trimmed,Y_trimmed[0]+Y_trimmed[0:-1])
def selectQTGMC(n, f, a, b):
P = f[0].props['PlaneStatsDiff']
N = f[1].props['PlaneStatsDiff']
if N < P:
return a
else:
return b
result = core.std.FrameEval(srchClip, partial(selectQTGMC,a=a,b=b), prop_src=diffclip,diffclip_trimmed)

Selur
17th January 2023, 05:00
Thanks that works.
# Imports
import vapoursynth as vs
import os
import sys
from functools import partial
from typing import Optional, Union, Sequence
# getting Vapoursynth core
core = vs.core


def CQTGMC(clip: vs.VideoNode, Sharpness: float=0.25, thSAD1: int=192, thSAD2: int=320, thSAD3: int=128, thSAD4: int=320) -> vs.VideoNode:

flip = core.std.FlipHorizontal(clip)
flip = Padding(clip=clip,top=clip.width%64)
flip = Padding(clip=clip,right=clip.height%64)
padded = core.std.StackHorizontal([clip,flip])
# todo: optional use nnedi3CL
bobbed = core.znedi3.nnedi3(clip=padded, field=2)
denoised = core.rgvs.RemoveGrain(clip=bobbed,mode=12)
denoised = core.fmtc.resample(clip=denoised, kernel="gauss", w=denoised.width, h=denoised.height, scaleh=denoised.width+0.0001, interlaced=False, interlacedd=False)
denoised = core.resize.Bicubic(clip=denoised, format=bobbed.format, dither_type="error_diffusion") # adjust format after fmtc
denoised = core.std.Merge(clipa=denoised, clipb=bobbed,weight=0.25)

srchClip = denoised
csuper = core.mv.Super(clip=denoised);
bvec = core.mv.Analyse(csuper,isb=True,blksize=64,overlap=32)
fvec = core.mv.Analyse(csuper,isb=False,blksize=64,overlap=32)
Comp1 = core.mv.Compensate(denoised,csuper,bvec,thsad=thSAD2)
Comp2 = core.mv.Compensate(denoised,csuper,fvec,thsad=thSAD2)
denoised = core.std.Interleave([Comp1,denoised,Comp2])
csuper = core.mv.Super(clip=denoised)
bvec = core.mv.Analyse(csuper,isb=True,blksize=64,overlap=32)
fvec = core.mv.Analyse(csuper,isb=False,blksize=64,overlap=32)
Inter = core.mv.FlowInter(denoised, csuper, bvec,fvec,blend=False)
a = core.std.SelectEvery(clip=Inter, cycle=3, offsets=0)
b = core.std.SelectEvery(clip=Inter, cycle=3, offsets=1)
Y = srchClip.std.ShufflePlanes(0, vs.GRAY)
diffclip = core.std.PlaneStats(Y, Y[0]+Y[0:-1])
Y_trimmed =Y[1:]+Y[-1:]
diffclip_trimmed = core.std.PlaneStats(Y_trimmed,Y_trimmed[0]+Y_trimmed[0:-1])
def selectQTGMC(n, f, a, b):
P = f[0].props['PlaneStatsDiff']
N = f[1].props['PlaneStatsDiff']
if N < P:
return a
else:
return b

srchClip = core.std.FrameEval(clip=srchClip, eval=partial(selectQTGMC,a=a,b=b), prop_src=diffclip, clip_src=diffclip_trimmed)

csuper = core.mv.Super(srchClip)
bVec1 = core.mv.Analyse(csuper,isb=True,overlap=4,delta=1)
fVec1 = core.mv.Analyse(csuper,isb=False,overlap=4,delta=1)
csuper = core.mv.Super(bobbed,levels=1)
bComp1 = core.mv.Compensate(bobbed,csuper,bVec1,thsad=thSAD3)
fComp1 = core.mv.Compensate(bobbed,csuper,fVec1,thsad=thSAD3)
X=1 # no clue how to get field order
Inter = core.std.Interleave([
bobbed.std.SetFrameProp(prop="_FieldBased", intval=X).std.SeparateFields().std.SelectEvery(4,0),
fComp1.std.SetFrameProp(prop="_FieldBased", intval=X).std.SeparateFields().std.SelectEvery(4,1),
bComp1.std.SetFrameProp(prop="_FieldBased", intval=X).std.SeparateFields().std.SelectEvery(4,2),
bobbed.std.SetFrameProp(prop="_FieldBased", intval=X).std.SeparateFields().std.SelectEvery(4,3)
])
weaved = core.std.DoubleWeave(Inter)[::2]
csuper = core.mv.Super(weaved,levels=1)
bComp1 = core.mv.Compensate(weaved, csuper, bVec1,thsad=thSAD4)
fComp1 = core.mv.Compensate(weaved, csuper, fVec1,thsad=thSAD4)
tMax = mt_logic(fComp1,mode="max").mt_logic(bComp1,mode="max")
tMin = mt_logic(fComp1,mode="min").mt_logic(bComp1,"min")
degrained = core.std.Degrain1(csuper,bVec1,fVec1,thsad=thSAD1)
sharpen = mt_adddiff(degrained, mt_makediff(degrained.rgvs.RemoveGrain(mode=20)))
sharpen = mt_clamp(sharpen,tMax,tMin,Sharpness,Sharpness,3,3)
csuper = core.mv.Super(sharpen, levels=1)
degrained = core.std.Degrain1(csuper,bVec1,fVec1,thsad=thSAD1)
degrained = core.std.Crop(0,0,-(clip.width%64),-(clip.width%64))
return degrained

def mt_adddiff(self, c1, c2, planes=[0]):
expr = ('x y + {mid} -').format(mid=self.mid)
expr = [(i in planes) * expr for i in range(3)]
return self.std.Expr([c1, c2], expr)


#from dehalo_alpha.py https://github.com/darealshinji/vapoursynth-plugins/blob/master/scripts/dehalo_alpha.py
def mt_logic(self, c1, c2, mode, th1=0, th2=0, planes=0):
if mode == 'min':
expr = lambda x, y: min(x + th1, y + th2)
elif mode == 'max':
expr = lambda x, y: max(x + th1, y + th2)
else:
raise InvalidArgument('%s is not a valid mode for mt_logic' % mode)
return self.mt_lutxy(c1, c2, expr, planes)

# from havsfunc
def Padding(clip: vs.VideoNode, left: int = 0, right: int = 0, top: int = 0, bottom: int = 0) -> vs.VideoNode:
if not isinstance(clip, vs.VideoNode):
raise vs.Error('Padding: this is not a clip')

if left < 0 or right < 0 or top < 0 or bottom < 0:
raise vs.Error('Padding: border size to pad must not be negative')

width = clip.width + left + right
height = clip.height + top + bottom

return clip.resize.Point(width, height, src_left=-left, src_top=-top, src_width=width, src_height=height)

# from havsfunc
def mt_clamp(
clip: vs.VideoNode,
bright_limit: vs.VideoNode,
dark_limit: vs.VideoNode,
overshoot: int = 0,
undershoot: int = 0,
planes: Optional[Union[int, Sequence[int]]] = None,
) -> vs.VideoNode:
if not (isinstance(clip, vs.VideoNode) and isinstance(bright_limit, vs.VideoNode) and isinstance(dark_limit, vs.VideoNode)):
raise vs.Error('mt_clamp: this is not a clip')

if bright_limit.format.id != clip.format.id or dark_limit.format.id != clip.format.id:
raise vs.Error('mt_clamp: clips must have the same format')

plane_range = range(clip.format.num_planes)

if planes is None:
planes = list(plane_range)
elif isinstance(planes, int):
planes = [planes]

return core.std.Expr([clip, bright_limit, dark_limit], expr=[f'x y {overshoot} + min z {undershoot} - max' if i in planes else '' for i in plane_range])

Now I'm at:
bVec1 = core.mv.Analyse(csuper,isb=True,overlap=4,delta=1)
File "src\cython\vapoursynth.pyx", line 2612, in vapoursynth.Function.__call__
vapoursynth.Error: Analyse: failed to retrieve first frame from super clip. Error message: 'memoryview' object has no attribute 'props'
:(

Cu Selur

_Al_
17th January 2023, 21:31
I tested it and I could go thru that line but
I used:
srchClip = core.std.FrameEval(clip=srchClip, eval=partial(selectQTGMC,a=a,b=b), prop_src=[diffclip,diffclip_trimmed])
I forgot to put prop_src clips in a list, you used clip_src , not sure if that is correct and was introcuded in API4 anyway

my test on your last script ended up on this line:
tMax = mt_logic(fComp1,mode="max").mt_logic(bComp1,mode="max"), but that needs some fixing, I'm sure you know ...

Selur
18th January 2023, 19:24
my test on your last script ended up on this line:
tMax = mt_logic(fComp1,mode="max").mt_logic(bComp1,mode="max"), but that needs some fixing, I'm sure you know ...
That's issue 3 in my first post. :)

There's also the issue that I use:
X=1 # no clue how to get field order
Inter = core.std.Interleave([
bobbed.std.SetFrameProp(prop="_FieldBased", intval=X).std.SeparateFields().std.SelectEvery(4,0),
fComp1.std.SetFrameProp(prop="_FieldBased", intval=X).std.SeparateFields().std.SelectEvery(4,1),
bComp1.std.SetFrameProp(prop="_FieldBased", intval=X).std.SeparateFields().std.SelectEvery(4,2),
bobbed.std.SetFrameProp(prop="_FieldBased", intval=X).std.SeparateFields().std.SelectEvery(4,3)
])
since I'm not certain how to get the field order.

Selur
21st January 2023, 11:31
Now the script:
# Imports
import vapoursynth as vs
import os
import sys
from functools import partial
from typing import Optional, Union, Sequence
# getting Vapoursynth core
core = vs.core


def CQTGMC(clip: vs.VideoNode, Sharpness: float=0.25, thSAD1: int=192, thSAD2: int=320, thSAD3: int=128, thSAD4: int=320, tff: bool=True) -> vs.VideoNode:

padded = core.std.AddBorders(clip, left=0, top=clip.height%4, right=clip.width%64, bottom=0)
# alternative:
#padded = core.std.StackHorizontal([clip, clip.std.FlipHorizontal().std.Crop(0,0,clip.width+clip.width()%64,0)])
#padded = core.std.StackVertical([padded, padded.std.FlipVertical().std.Crop(0,0,0,clip.height+clip.height%64)])

bobbed = core.znedi3.nnedi3(clip=padded, field=2)
denoised = core.rgvs.RemoveGrain(clip=bobbed,mode=12)
denoised = core.fmtc.resample(clip=denoised, kernel="gauss", w=denoised.width, h=denoised.height+0.0001, scaleh=denoised.width+0.0001, interlaced=False, interlacedd=False)
denoised = core.resize.Bicubic(clip=denoised, format=bobbed.format, dither_type="error_diffusion") # adjust format after fmtc
denoised = core.std.Merge(clipa=denoised, clipb=bobbed,weight=0.25)

srchClip = denoised
csuper = core.mv.Super(clip=denoised);
bvec = core.mv.Analyse(csuper,isb=True,blksize=64,overlap=32)
fvec = core.mv.Analyse(csuper,isb=False,blksize=64,overlap=32)
Comp1 = core.mv.Compensate(clip=denoised,super=csuper,vectors=bvec,thsad=thSAD2)
Comp2 = core.mv.Compensate(clip=denoised,super=csuper,vectors=fvec,thsad=thSAD2)
denoised = core.std.Interleave([Comp1,denoised,Comp2])
csuper = core.mv.Super(clip=denoised)
bvec = core.mv.Analyse(csuper,isb=True,blksize=64,overlap=32)
fvec = core.mv.Analyse(csuper,isb=False,blksize=64,overlap=32)
Inter = core.mv.FlowInter(denoised, csuper, bvec,fvec,blend=False)
a = core.std.SelectEvery(clip=Inter, cycle=3, offsets=0)
b = core.std.SelectEvery(clip=Inter, cycle=3, offsets=1)
Y = core.std.ShufflePlanes([srchClip],[0], vs.GRAY)
diffclip = core.std.PlaneStats(Y, Y[0]+Y[0:-1])
Y_trimmed =Y[1:]+Y[-1:]
diffclip_trimmed = core.std.PlaneStats(Y_trimmed,Y_trimmed[0]+Y_trimmed[0:-1])
def selectQTGMC(n, f, a, b):
P = f[0].props['PlaneStatsDiff']
N = f[1].props['PlaneStatsDiff']
if N < P:
return a
else:
return b

srchClip = core.std.FrameEval(clip=srchClip, eval=partial(selectQTGMC,a=a,b=b), prop_src=[diffclip,diffclip_trimmed])

csuper = core.mv.Super(clip=srchClip)
bVec1 = core.mv.Analyse(super=csuper, isb=True, overlap=4, delta=1)
fVec1 = core.mv.Analyse(super=csuper, isb=False, overlap=4, delta=1)
csuper = core.mv.Super(clip=bobbed,levels=1)
bComp1 = core.mv.Compensate(clip=bobbed, super=csuper, vectors=bVec1, thsad=thSAD3)
fComp1 = core.mv.Compensate(clip=bobbed, super=csuper, vectors=fVec1, thsad=thSAD3)
X = 2 if tff else 1 # no clue how to get field order
Inter = core.std.Interleave([
bobbed.std.SetFrameProp(prop="_FieldBased", intval=X).std.SeparateFields().std.SelectEvery(4,0),
fComp1.std.SetFrameProp(prop="_FieldBased", intval=X).std.SeparateFields().std.SelectEvery(4,1),
bComp1.std.SetFrameProp(prop="_FieldBased", intval=X).std.SeparateFields().std.SelectEvery(4,2),
bobbed.std.SetFrameProp(prop="_FieldBased", intval=X).std.SeparateFields().std.SelectEvery(4,3)
])
weaved = core.std.DoubleWeave(clip=Inter)[::2]
csuper = core.mv.Super(clip=weaved, levels=1)
bComp1 = core.mv.Compensate(clip=weaved, super=csuper, vectors=bVec1, thsad=thSAD4)
fComp1 = core.mv.Compensate(clip=weaved, super=csuper, vectors=fVec1, thsad=thSAD4)
tMax = core.std.Expr(clips=[weaved, bComp1], expr=['x y max'])
tMax = core.std.Expr(clips=[tMax, fComp1], expr=['x y max'])
tMin = core.std.Expr(clips=[weaved, bComp1], expr=['x y min'])
tMin = core.std.Expr(clips=[tMin, fComp1], expr=['x y min'])

degrained = core.mv.Degrain1(clip=weaved, super=csuper, mvbw=bVec1, mvfw=fVec1, thsad=thSAD1)
sharpen = core.std.MergeDiff(degrained, core.std.MakeDiff(degrained,degrained.rgvs.RemoveGrain(mode=20)))

sharpen = mt_clamp(sharpen,tMax,tMin,Sharpness,Sharpness,[0])
csuper = core.mv.Super(sharpen, levels=1)
degrained = core.mv.Degrain1(degrained, csuper,bVec1,fVec1,thsad=thSAD1)
degrained = core.std.Crop(degrained, left=0, top=clip.height%4, right=clip.width%64, bottom=0)
return degrained

# from havsfunc
def mt_clamp(
clip: vs.VideoNode,
bright_limit: vs.VideoNode,
dark_limit: vs.VideoNode,
overshoot: int = 0,
undershoot: int = 0,
planes: Optional[Union[int, Sequence[int]]] = None,
) -> vs.VideoNode:
if not (isinstance(clip, vs.VideoNode) and isinstance(bright_limit, vs.VideoNode) and isinstance(dark_limit, vs.VideoNode)):
raise vs.Error('mt_clamp: this is not a clip')

if bright_limit.format.id != clip.format.id or dark_limit.format.id != clip.format.id:
raise vs.Error('mt_clamp: clips must have the same format')

plane_range = range(clip.format.num_planes)

if planes is None:
planes = list(plane_range)
elif isinstance(planes, int):
planes = [planes]

return core.std.Expr([clip, bright_limit, dark_limit], expr=[f'x y {overshoot} + min z {undershoot} - max' if i in planes else '' for i in plane_range])

gets processed, but the output isn't properly deinerlaced.
Must have mixed up something somewhere.

=> does anyone see where I went wrong?

Cu Selur

Ceppo
23rd January 2023, 22:15
Just wondering? Does the avisynth version deinterlaces it correctly? You have to think like a much strong lossless of QTGMC, so combed pixels might show up.

I don't speak vapoursynth language but I can't catch something that looks like this code, but I might be wrong.


bobbed
super = MSuper(levels=1)
bComp1 = MCompensate(super,bVec1,thSAD=thSAD3)
fComp1 = MCompensate(super,fVec1,thSAD=thSAD3)
Interleave(\
SeparateFields(bobbed).SelectEvery(4,0),\
SeparateFields(fComp1).SelectEvery(4,1),\
SeparateFields(bComp1).SelectEvery(4,2),\
SeparateFields(bobbed).SelectEvery(4,3))
Weave()


My bad I spot it :\ :)

Ceppo
24th January 2023, 00:03
padded = core.std.AddBorders(clip, left=0, top=clip.height%4, right=clip.width%64, bottom=0)
Note: AddBorders is bad.

Y = core.std.ShufflePlanes([srchClip],[0], vs.GRAY)
diffclip = core.std.PlaneStats(Y, Y[0]+Y[0:-1])
Y_trimmed =Y[1:]+Y[-1:]
diffclip_trimmed = core.std.PlaneStats(Y_trimmed,Y_trimmed[0]+Y_trimmed[0:-1])
What this piece of code does? Je ne comprends pas. It is YDifferenceFromPrevious()?

X = 2 if tff else 1 # no clue how to get field order

You are working on a bobbed clip, you don't need the field order, and why you set it to field based? I'm missing something?

degrained = core.std.Crop(degrained, left=0, top=clip.height%4, right=clip.width%64, bottom=0)


Nothing else catches my eyes with 1% brain usage as processing power and 64kbit of RAM. :cool:

_Al_
24th January 2023, 03:46
HolyWu is working with YDifferenceFromPrevious here:
https://github.com/HomeOfVapourSynthEvolution/havsfunc/blob/7f0a9a7a37b60a05b9f408024d203e511e544a61/havsfunc.py#L3321

Selur
24th January 2023, 19:31
Note: AddBorders is bad.
Fixed mod in crop&addborders

You are working on a bobbed clip, you don't need the field order, and why you set it to field based? I'm missing something?
SeparateFields requires FieldBased info .
Without the 'std.SetFrameProp(prop="_FieldBased", intval=X)' part I get
bComp1 = core.mv.Compensate(clip=weaved, super=csuper, vectors=bVec1, thsad=thSAD4)
File "src\cython\vapoursynth.pyx", line 2612, in vapoursynth.Function.__call__
vapoursynth.Error: Compensate: failed to retrieve first frame from super clip. Error message: SeparateFields: no field order provided
later on.
the

global CQTGMC_A = Inter.SelectEvery(3,0)
global CQTGMC_B = Inter.SelectEvery(3,1)
srchClip
ScriptClip("""
P = YDifferenceFromPrevious()
N = Trim(1,0).YDifferenceFromPrevious()
N < P ? CQTGMC_A : CQTGMC_B
""")

part currently is translated to:
a = core.std.SelectEvery(clip=Inter, cycle=3, offsets=0)
b = core.std.SelectEvery(clip=Inter, cycle=3, offsets=1)
Y = core.std.ShufflePlanes([srchClip],[0], vs.GRAY)
diffclip = core.std.PlaneStats(Y, Y[0]+Y[0:-1])
Y_trimmed =Y[1:]+Y[-1:]
diffclip_trimmed = core.std.PlaneStats(Y_trimmed,Y_trimmed[0]+Y_trimmed[0:-1])
def selectQTGMC(n, f, a, b):
P = f[0].props['PlaneStatsDiff']
N = f[1].props['PlaneStatsDiff']
if N < P:
return a
else:
return b

srchClip = core.std.FrameEval(clip=srchClip, eval=partial(selectQTGMC,a=a,b=b), prop_src=[diffclip,diffclip_trimmed])
but I have no clue whether this is correct.
-> if someone can translate this to Vapoursynth in a working way, please do.

Selur
4th February 2023, 14:20
Thanks to all that helped think I got a working version now:
# Imports
import vapoursynth as vs
import os
import sys
from functools import partial
from typing import Optional, Union, Sequence
# getting Vapoursynth core
core = vs.core

# Vapoursynth port of CQTMC from https://forum.doom9.org/showthread.php?p=1963693
#
# dependencies:
#
# fmtc when boxed=False https://github.com/EleonoreMizo/fmtconv
# mvtools https://github.com/dubhater/vapoursynth-mvtools
# NNEDI3CL when openCL=True https://github.com/HomeOfVapourSynthEvolution/VapourSynth-NNEDI3CL
# NNEDI3 when openCL=False https://github.com/sekrit-twc/znedi3


def CQTGMC(clip: vs.VideoNode, Sharpness: float=0.25, thSAD1: int=192, thSAD2: int=320, thSAD3: int=128, thSAD4: int=320, tff: bool=True, openCL: bool=False, boxed: bool=False) -> vs.VideoNode:

# pad to mod64
padded = core.std.AddBorders(clip, left=0, top=clip.height%64, right=clip.width%64, bottom=0)

# deinterlace
X = 2 if tff else 1
if openCL:
bobbed = core.znedi3.nnedi3(clip=padded, field=X)
else:
bobbed = core.nnedi3cl.NNEDI3CL(clip=padded, field=X)

# denoise
denoised = core.rgvs.RemoveGrain(clip=bobbed,mode=12)
if boxed:
denoised = core.std.BoxBlur(clip=denoised)
else:
denoised = core.fmtc.resample(clip=denoised, kernel="gauss", w=denoised.width, h=denoised.height+0.0001, scaleh=denoised.width+0.0001, interlaced=False, interlacedd=False)
denoised = core.resize.Bicubic(clip=denoised, format=bobbed.format, dither_type="error_diffusion") # adjust format after fmtc
denoised = core.std.Merge(clipa=denoised, clipb=bobbed,weight=0.25)

srchClip = denoised
csuper = core.mv.Super(clip=denoised);
bvec = core.mv.Analyse(csuper,isb=True,blksize=64,overlap=32)
fvec = core.mv.Analyse(csuper,isb=False,blksize=64,overlap=32)
Comp1 = core.mv.Compensate(clip=denoised,super=csuper,vectors=bvec,thsad=thSAD2)
Comp2 = core.mv.Compensate(clip=denoised,super=csuper,vectors=fvec,thsad=thSAD2)
denoised = core.std.Interleave([Comp1,denoised,Comp2])
csuper = core.mv.Super(clip=denoised)
bvec = core.mv.Analyse(csuper,isb=True,blksize=64,overlap=32)
fvec = core.mv.Analyse(csuper,isb=False,blksize=64,overlap=32)
Inter = core.mv.FlowInter(denoised, csuper, bvec,fvec,blend=False)

a = core.std.SelectEvery(clip=Inter, cycle=3, offsets=0)
b = core.std.SelectEvery(clip=Inter, cycle=3, offsets=1)
Y = core.std.ShufflePlanes([srchClip],[0], vs.GRAY)
diffclip = core.std.PlaneStats(Y, Y[0]+Y[0:-1])
Y_trimmed =Y[1:]+Y[-1:]
diffclip_trimmed = core.std.PlaneStats(Y_trimmed,Y_trimmed[0]+Y_trimmed[0:-1])
def selectQTGMC(n, f, a, b):
P = f[0].props['PlaneStatsDiff']
N = f[1].props['PlaneStatsDiff']
if N < P:
return a
else:
return b

srchClip = core.std.FrameEval(clip=srchClip, eval=partial(selectQTGMC,a=a,b=b), prop_src=[diffclip,diffclip_trimmed])

csuper = core.mv.Super(clip=srchClip)
bVec1 = core.mv.Analyse(super=csuper, isb=True, overlap=4, delta=1)
fVec1 = core.mv.Analyse(super=csuper, isb=False, overlap=4, delta=1)
csuper = core.mv.Super(clip=bobbed,levels=1)
bComp1 = core.mv.Compensate(clip=bobbed, super=csuper, vectors=bVec1, thsad=thSAD3)
fComp1 = core.mv.Compensate(clip=bobbed, super=csuper, vectors=fVec1, thsad=thSAD3)



Inter = core.std.Interleave([
bobbed.std.SeparateFields(tff=X).std.SelectEvery(4,0),
fComp1.std.SeparateFields(tff=X).std.SelectEvery(4,1),
bComp1.std.SeparateFields(tff=X).std.SelectEvery(4,2),
bobbed.std.SeparateFields(tff=X).std.SelectEvery(4,3)
])


weaved = core.std.DoubleWeave(clip=Inter)[::2]
csuper = core.mv.Super(clip=weaved, levels=1)


bComp1 = core.mv.Compensate(clip=weaved, super=csuper, vectors=bVec1, thsad=thSAD4)
fComp1 = core.mv.Compensate(clip=weaved, super=csuper, vectors=fVec1, thsad=thSAD4)
tMax = core.std.Expr(clips=[weaved, bComp1], expr=['x y max'])
tMax = core.std.Expr(clips=[tMax, fComp1], expr=['x y max'])
tMin = core.std.Expr(clips=[weaved, bComp1], expr=['x y min'])
tMin = core.std.Expr(clips=[tMin, fComp1], expr=['x y min'])

degrained = core.mv.Degrain1(clip=weaved, super=csuper, mvbw=bVec1, mvfw=fVec1, thsad=thSAD1)
sharpen = core.std.MergeDiff(degrained, core.std.MakeDiff(degrained,degrained.rgvs.RemoveGrain(mode=20)))
sharpen = mt_clamp(sharpen,tMax,tMin,Sharpness,Sharpness,[0])

csuper = core.mv.Super(sharpen, levels=1)
degrained = core.mv.Degrain1(clip=degrained, super=csuper, mvbw=bVec1,mvfw=fVec1,thsad=thSAD1)
degrained = core.std.Crop(degrained, left=0, top=clip.height%64, right=clip.width%64, bottom=0)
return degrained

# from havsfunc
# https://github.com/HomeOfVapourSynthEvolution/havsfunc/blob/7f0a9a7a37b60a05b9f408024d203e511e544a61/havsfunc.py#L5911
def mt_clamp(
clip: vs.VideoNode,
bright_limit: vs.VideoNode,
dark_limit: vs.VideoNode,
overshoot: int = 0,
undershoot: int = 0,
planes: Optional[Union[int, Sequence[int]]] = None,
) -> vs.VideoNode:
if not (isinstance(clip, vs.VideoNode) and isinstance(bright_limit, vs.VideoNode) and isinstance(dark_limit, vs.VideoNode)):
raise vs.Error('mt_clamp: this is not a clip')

if bright_limit.format.id != clip.format.id or dark_limit.format.id != clip.format.id:
raise vs.Error('mt_clamp: clips must have the same format')

plane_range = range(clip.format.num_planes)

if planes is None:
planes = list(plane_range)
elif isinstance(planes, int):
planes = [planes]

return core.std.Expr([clip, bright_limit, dark_limit], expr=[f'x y {overshoot} + min z {undershoot} - max' if i in planes else '' for i in plane_range])

also under: https://github.com/Selur/VapoursynthScriptsInHybrid/blob/master/cqtgmc.py

Cu Selur

Selur
19th February 2023, 18:35
Nope, the padding and cropping is wrong.
Using:
# Imports
import vapoursynth as vs
import os
import sys
from functools import partial
from typing import Optional, Union, Sequence
# getting Vapoursynth core
core = vs.core

# Vapoursynth port of CQTMC from https://forum.doom9.org/showthread.php?p=1963693
#
# dependencies:
#
# fmtc when boxed=False https://github.com/EleonoreMizo/fmtconv
# mvtools https://github.com/dubhater/vapoursynth-mvtools
# NNEDI3CL when openCL=True https://github.com/HomeOfVapourSynthEvolution/VapourSynth-NNEDI3CL
# NNEDI3 when openCL=False https://github.com/sekrit-twc/znedi3


def CQTGMC(clip: vs.VideoNode, Sharpness: float=0.25, thSAD1: int=192, thSAD2: int=320, thSAD3: int=128, thSAD4: int=320, tff: bool=True, openCL: bool=False, boxed: bool=False) -> vs.VideoNode:

# pad to mod64
padded = core.std.AddBorders(clip, left=0, top=clip.height%4, right=clip.width%64, bottom=0)

# spatial deinterlace
# field: 2: Double rate, start with bottom field, 3: Double rate, start with top field.
X = 3 if tff else 2
if openCL:
spatial = core.nnedi3cl.NNEDI3CL(clip=padded, field=X)
else:
spatial = core.znedi3.nnedi3(clip=padded, field=X)

# temporal deint
# order: 0 = bottom field first (bff), 1 = top field first (tff)
X = 1 if tff else 0
bobbed = core.tdm.TDeintMod(clip=padded, order=X, mode=1, edeint=spatial)

# denoise
denoised = core.rgvs.RemoveGrain(clip=bobbed,mode=12)
if boxed:
denoised = core.std.BoxBlur(clip=denoised)
else:
denoised = core.fmtc.resample(clip=denoised, kernel="gauss", w=denoised.width, h=denoised.height+0.0001, scaleh=denoised.width+0.0001, interlaced=False, interlacedd=False)
denoised = core.resize.Bicubic(clip=denoised, format=bobbed.format, dither_type="error_diffusion") # adjust format after fmtc
denoised = core.std.Merge(clipa=denoised, clipb=bobbed,weight=0.25)

srchClip = denoised
csuper = core.mv.Super(clip=denoised);
bvec = core.mv.Analyse(csuper,isb=True,blksize=64,overlap=32)
fvec = core.mv.Analyse(csuper,isb=False,blksize=64,overlap=32)
Comp1 = core.mv.Compensate(clip=denoised,super=csuper,vectors=bvec,thsad=thSAD2)
Comp2 = core.mv.Compensate(clip=denoised,super=csuper,vectors=fvec,thsad=thSAD2)
denoised = core.std.Interleave([Comp1,denoised,Comp2])
csuper = core.mv.Super(clip=denoised)
bvec = core.mv.Analyse(csuper,isb=True,blksize=64,overlap=32)
fvec = core.mv.Analyse(csuper,isb=False,blksize=64,overlap=32)
Inter = core.mv.FlowInter(denoised, csuper, bvec,fvec,blend=False)

a = core.std.SelectEvery(clip=Inter, cycle=3, offsets=0)
b = core.std.SelectEvery(clip=Inter, cycle=3, offsets=1)
Y = core.std.ShufflePlanes([srchClip],[0], vs.GRAY)
diffclip = core.std.PlaneStats(Y, Y[0]+Y[0:-1])
Y_trimmed =Y[1:]+Y[-1:]
diffclip_trimmed = core.std.PlaneStats(Y_trimmed,Y_trimmed[0]+Y_trimmed[0:-1])
def selectQTGMC(n, f, a, b):
P = f[0].props['PlaneStatsDiff']
N = f[1].props['PlaneStatsDiff']
if N < P:
return a
else:
return b

srchClip = core.std.FrameEval(clip=srchClip, eval=partial(selectQTGMC,a=a,b=b), prop_src=[diffclip,diffclip_trimmed])

csuper = core.mv.Super(clip=srchClip)
bVec1 = core.mv.Analyse(super=csuper, isb=True, overlap=4, delta=1)
fVec1 = core.mv.Analyse(super=csuper, isb=False, overlap=4, delta=1)
csuper = core.mv.Super(clip=bobbed,levels=1)
bComp1 = core.mv.Compensate(clip=bobbed, super=csuper, vectors=bVec1, thsad=thSAD3)
fComp1 = core.mv.Compensate(clip=bobbed, super=csuper, vectors=fVec1, thsad=thSAD3)



Inter = core.std.Interleave([
bobbed.std.SeparateFields(tff=tff).std.SelectEvery(4,0),
fComp1.std.SeparateFields(tff=tff).std.SelectEvery(4,1),
bComp1.std.SeparateFields(tff=tff).std.SelectEvery(4,2),
bobbed.std.SeparateFields(tff=tff).std.SelectEvery(4,3)
])


weaved = core.std.DoubleWeave(clip=Inter)[::2]
csuper = core.mv.Super(clip=weaved, levels=1)


bComp1 = core.mv.Compensate(clip=weaved, super=csuper, vectors=bVec1, thsad=thSAD4)
fComp1 = core.mv.Compensate(clip=weaved, super=csuper, vectors=fVec1, thsad=thSAD4)
tMax = core.std.Expr(clips=[weaved, bComp1], expr=['x y max'])
tMax = core.std.Expr(clips=[tMax, fComp1], expr=['x y max'])
tMin = core.std.Expr(clips=[weaved, bComp1], expr=['x y min'])
tMin = core.std.Expr(clips=[tMin, fComp1], expr=['x y min'])

degrained = core.mv.Degrain1(clip=weaved, super=csuper, mvbw=bVec1, mvfw=fVec1, thsad=thSAD1)
sharpen = core.std.MergeDiff(degrained, core.std.MakeDiff(degrained,degrained.rgvs.RemoveGrain(mode=20)))
sharpen = mt_clamp(sharpen,tMax,tMin,Sharpness,Sharpness,[0])

csuper = core.mv.Super(sharpen, levels=1)
degrained = core.mv.Degrain1(clip=degrained, super=csuper, mvbw=bVec1,mvfw=fVec1,thsad=thSAD1)
degrained = core.std.Crop(degrained, left=0, top=clip.core%64, right=clip.width%64, bottom=0)
return degrained


# from havsfunc
# https://github.com/HomeOfVapourSynthEvolution/havsfunc/blob/7f0a9a7a37b60a05b9f408024d203e511e544a61/havsfunc.py#L5911
def mt_clamp(
clip: vs.VideoNode,
bright_limit: vs.VideoNode,
dark_limit: vs.VideoNode,
overshoot: int = 0,
undershoot: int = 0,
planes: Optional[Union[int, Sequence[int]]] = None,
) -> vs.VideoNode:
if not (isinstance(clip, vs.VideoNode) and isinstance(bright_limit, vs.VideoNode) and isinstance(dark_limit, vs.VideoNode)):
raise vs.Error('mt_clamp: this is not a clip')

if bright_limit.format.id != clip.format.id or dark_limit.format.id != clip.format.id:
raise vs.Error('mt_clamp: clips must have the same format')

plane_range = range(clip.format.num_planes)

if planes is None:
planes = list(plane_range)
elif isinstance(planes, int):
planes = [planes]

return core.std.Expr([clip, bright_limit, dark_limit], expr=[f'x y {overshoot} + min z {undershoot} - max' if i in planes else '' for i in plane_range])

the padding at the beginning and cropping at the end is wrong as it is implemented atm. :(