You could add the VapourSynth code version to the first post. I'll remove it from my code but don't want this to get lost.
In VapourSynth, it currently only works with the 32-bit library. The dubhater library has a bug.
Code:
# SpotLess denoising method (m1=4) EXPERIMENTAL
def SpotLess(c: vs.VideoNode, radt: int = 1, thsad: int = 10000, thsad2: Optional[int] = None, pel: int = 2, chroma: bool = True, blksize: int = 8,
overlap: Optional[int] = None, truemotion: bool = True, pglobal: bool = True, blur: float = 0.0, ref: Optional[vs.VideoNode] = None) -> vs.VideoNode:
if radt < 1 or radt > 3:
raise ValueError("Spotless: radt must be between 1 and 3")
if not pel in [1, 2, 4]:
raise ValueError("Spotless: pel must be 1, 2 or 4")
thsad2 = thsad2 or thsad
icalc = c.format.bits_per_sample < 32
S = core.mv.Super if icalc else core.mvsf.Super
A = core.mv.Analyse if icalc else core.mvsf.Analyse
C = core.mv.Compensate if icalc else core.mvsf.Compensate
pad = max(blksize, 8)
sup = ref or (c.std.Convolution(matrix=[1, 2, 1, 2, 4, 2, 1, 2, 1]) if blur > 0.0 else c)
sup = S(sup, hpad=pad, vpad=pad, pel=pel, sharp=2)
sup_rend = S(c, hpad=pad, vpad=pad, pel=pel, sharp=2, levels=1) if ref or blur > 0.0 else sup
th1 = thsad
th2 = (thsad + thsad2)/2 if radt==3 else thsad2
th3 = thsad2
bv1 = A(sup, isb=True, delta=1, blksize=blksize, overlap=overlap, chroma=chroma, truemotion=truemotion, pglobal=pglobal)
fv1 = A(sup, isb=False, delta=1, blksize=blksize, overlap=overlap, chroma=chroma, truemotion=truemotion, pglobal=pglobal)
if radt >= 2:
bv2 = A(sup, isb=True, delta=2, blksize=blksize, overlap=overlap, chroma=chroma, truemotion=truemotion, pglobal=pglobal)
fv2 = A(sup, isb=False, delta=2, blksize=blksize, overlap=overlap, chroma=chroma, truemotion=truemotion, pglobal=pglobal)
if radt >= 3:
bv3 = A(sup, isb=True, delta=3, blksize=blksize, overlap=overlap, chroma=chroma, truemotion=truemotion, pglobal=pglobal)
fv3 = A(sup, isb=False, delta=3, blksize=blksize, overlap=overlap, chroma=chroma, truemotion=truemotion, pglobal=pglobal)
bc1 = C(c, sup_rend, bv1, thsad=th1)
fc1 = C(c, sup_rend, fv1, thsad=th1)
if radt >= 2:
bc2 = C(c, sup_rend, bv2, thsad=th2)
fc2 = C(c, sup_rend, fv2, thsad=th2)
if radt >= 3:
bc3 = C(c, sup_rend, bv3, thsad=th3)
fc3 = C(c, sup_rend, fv3, thsad=th3)
ic = core.std.Interleave([bc1, c, fc1]) if radt == 1 else \
core.std.Interleave([bc2, bc1, c, fc1, fc2]) if radt == 2 else \
core.std.Interleave([bc3, bc2, bc1, c, fc1, fc2, fc3])
output = core.tmedian.TemporalMedian(ic, radius=radt)
return output.std.SelectEvery(radt*2+1, radt) # Return middle frame