Log in

View Full Version : Smart deinterlace script


Mystery Keeper
22nd September 2016, 21:42
I wanted a way to detect combing in frames and only deinterlace frames that contain it. As result, this script was born. It applies local contrast enhancement to clip before combing detection - works great even on those dark crossfades or where combing is barely seen. You can also use the local contrast enhancement function for your needs.

Requires TDeintMod (https://github.com/HomeOfVapourSynthEvolution/VapourSynth-TDeintMod/releases) and Retinex (https://github.com/HomeOfVapourSynthEvolution/VapourSynth-Retinex/releases) plugins.

import vapoursynth as vs

def retinex_y(clip, out_format = None):
#-------------------------------------------------------------------------------------------------------------------------------------

core = vs.get_core()

if out_format is None:
out_format = clip.format

#-------------------------------------------------------------------------------------------------------------------------------------

intformat = core.register_format(clip.format.color_family, vs.INTEGER, 16, 0, 0)

clip_in = core.resize.Spline36(clip, format=intformat.id)
retinex = core.retinex.MSRCP(clip_in)
retinex_out = core.resize.Spline36(retinex, format=out_format.id)
clip_out = core.resize.Spline36(clip, format=out_format.id)
shuffled = core.std.ShufflePlanes([retinex_out, clip_out, clip_out], [0,1,2], colorfamily=out_format.color_family)

return shuffled

# END OF retinex_y(clip, out_format = None)
#=========================================================================================================================================

def cond_deint(clip, deinterlaced, cthresh = None, blockx = None, blocky = None, chroma = None, mi = None, metric = None):
core = vs.get_core()

if not isinstance(clip, vs.VideoNode):
raise TypeError('''cond_deint: 'clip' argument is not a clip''')

if not isinstance(deinterlaced, vs.VideoNode):
raise TypeError('''cond_deint: 'deinterlaced' argument is not a clip''')

is_combed_arguments = {
'cthresh': cthresh,
'blockx': blockx,
'blocky': blocky,
'chroma': chroma,
'mi': mi,
'metric': metric,
}

old_format = clip.format
new_format = core.register_format(old_format.color_family, vs.INTEGER, 16, old_format.subsampling_w, old_format.subsampling_h)

refOriginal = core.resize.Point(clip, format=new_format.id)
refOriginal = core.tdm.IsCombed(refOriginal, **is_combed_arguments)
refEnhanced = retinex_y(clip, new_format)
refEnhanced = core.tdm.IsCombed(refEnhanced, **is_combed_arguments)

def cond_deint_transfer_property(n, f):
fout = f[1].copy()
if f[0].props._Combed:
fout.props._Combed = True
return fout

combProps = core.std.ModifyFrame(refEnhanced, [refOriginal, refEnhanced], cond_deint_transfer_property)

final = core.std.FrameEval(clip, lambda n, f: deinterlaced if f.props._Combed else clip, combProps)

return final

# END OF cond_deint(clip, deinterlaced, cthresh = None, blockx = None, blocky = None, chroma = None, mi = None, metric = None)
#=========================================================================================================================================

brucethemoose
22nd September 2016, 22:55
Sweet! I've been banging my head against the wall trying to tune VFM's combing detection, this looks like what I need.

EDIT: What are some good default values?

Mystery Keeper
22nd September 2016, 23:20
I've found mi=8 working good for me.

Upd: Updated the script with more neat version. Thanks to TheFluff.

brucethemoose
23rd September 2016, 01:01
I get the error "Python exception: 'NoneType' object has no attribute 'set_output'" on my "clip.set_output()" line.

Which means it's not outputting a clip, I think. This is using the new code, I haven't tried the old code.

Mystery Keeper
23rd September 2016, 02:02
I get the error "Python exception: 'NoneType' object has no attribute 'set_output'" on my "clip.set_output()" line.

Which means it's not outputting a clip, I think. This is using the new code, I haven't tried the old code.Works alright for me. Paste your script?

brucethemoose
23rd September 2016, 02:57
It's basically this:

clip = core.ffms2.Source(r"C:\ReMasteringATLA\Book3\AVATAR\title06.mkv")
clip = core.vivtc.VFM(clip, mode = 0, order = 1, micmatch = 1) #IVTC
deint = clip #for debugging
clip = cond_deint.cond_deint(clip = clip, deinterlaced = deint)
clip.set_output()

With a bunch of imports and commented out code.

EDIT2: Thought I fixed it, was wrong.

Mystery Keeper
23rd September 2016, 03:05
Ugh. I do it like this. No errors.
from __future__ import print_function

import vapoursynth as vs
core = vs.get_core(threads=8)
core.set_max_cache_size(12000)

import sys
sys.path.append('D:\\vapoursynth-plugins\\py\\')
import platform
architecture = platform.architecture()
if architecture[0] == '64bit':
vapoursynth_plugins_path = 'D:\\vapoursynth-plugins\\64bit\\'
else:
vapoursynth_plugins_path = 'D:\\vapoursynth-plugins\\32bit\\'
print('Plugins folder: ', vapoursynth_plugins_path, end='\n', file=sys.stderr)
import os
for filename in os.listdir(vapoursynth_plugins_path):
if filename[-4:] != '.dll':
continue
try:
core.std.LoadPlugin(vapoursynth_plugins_path + filename)
except Exception as e:
print('Error: ', e, end='\n', file=sys.stderr)

#===========================================================================================================

d2vfile = r'D:\Video to Process\KOTOKO - Tsukiyo no Butoukai\VTS_01_1.d2v'
src = core.d2v.Source(input=d2vfile, rff=False)
src = core.std.CropRel(src, left=0, top=66, right=0, bottom=62)
src = core.vivtc.VFM(src, order=1, mode=5)
src = core.vivtc.VDecimate(src)
src.set_output()

#========================================================================
# Denoising

import MCDenoise as mcd

tlamcArguments = {
'radius': 5,
'search': 5,
'pelsearch': 2,
'subpelinterp': 2,
'searchparam': 5,
'truemotion': True,
'chromamotion': False,
'thsad': 200,
'thscd1': 150,
'thscd2': 150,
'dct': 5,
'refine': True,
}

separated = core.std.SeparateFields(src, True)

even = core.std.SelectEvery(separated, 2, 0)
even_denoised = mcd.TempLinearApproximateMC(even, **tlamcArguments)
#even_denoised = mcd.TempLinearApproximateMC(even_denoised, **tlamcArguments)

odd = core.std.SelectEvery(separated, 2, 1)
odd_denoised = mcd.TempLinearApproximateMC(odd, **tlamcArguments)
#odd_denoised = mcd.TempLinearApproximateMC(odd_denoised, **tlamcArguments)

interleaved = core.std.Interleave([even_denoised, odd_denoised])
double_weaved = core.std.DoubleWeave(interleaved, True)
src = core.std.SelectEvery(double_weaved, 2, 0)

#========================================================================

import helpers as h

deint = core.nnedi3.nnedi3(src, field=0, nsize=3, nns=4, qual=2)
deint = core.sub.Subtitle(deint, 'X')
cdeint = h.cond_deint(src, deint, mi=8)
cdeint.set_output()

retinex_src = h.retinex_y(src)
retinex_deint = h.retinex_y(cdeint)
top_row = core.std.StackHorizontal([src, cdeint])
bottom_row = core.std.StackHorizontal([retinex_src, retinex_deint])
cmp = core.std.StackVertical([top_row, bottom_row])
#cmp.set_output()

brucethemoose
23rd September 2016, 03:09
Might have something to do with my source, let me try another file.

I'll also try importing it like you did.


Sorry for the ninja edit. Thought I fixed it, but 1 line I forgot to comment out meant it was outputting another clip.

brucethemoose
23rd September 2016, 03:56
Works great, thanks! Maybe I was tuning IsCombed() and VFM wrong, but this seems to be better at detecting combing:
http://u.cubeupload.com/brucethemoose/B1v2vpy182709.png
http://u.cubeupload.com/brucethemoose/B1v2vpy182710.png
http://u.cubeupload.com/brucethemoose/B1v2vpy182711.png

EDIT: This is mi=64, btw.

EDIT2: Another note, I get alot of false positives without a really high CThresh.

Mystery Keeper
23rd September 2016, 05:39
EDIT2: Another note, I get alot of false positives without a really high CThresh.Remember: miracles don't happen.
Play with parameters and pray. Sacrifice few uncombed frames and deinterlace them anyway.

brucethemoose
23rd September 2016, 06:23
Remember: miracles don't happen.
Play with parameters and pray. Sacrifice few uncombed frames and deinterlace them anyway.

Actually, cthresh=70 and mi=16 is working almost perfectly.

Proper decimation is another story. AFAIK VS has no equivalent to
"TDecimate(mode = 1, hybrid = 1)", so motion is not as smooth as it should be.

Mystery Keeper
12th December 2016, 17:52
Updated. Now checks both original clip and contrast enhanced clip and combines the results.