Zastin

eedi3.py (bugfix, eedi3aa, DogaKoboAA)

Aug 2nd, 2017
323
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 11.64 KB | None | 0 0
  1. import vapoursynth as vs
  2. import mvsfunc as mvf
  3. import muvsfunc as muf
  4. import functools
  5. import math
  6.  
  7.  
  8. # "eedi3aa" - supersampled eedi3 anti-aliasing
  9. #
  10. # ss  = supersampling
  11. # ssh = supersampling (height)
  12. #      numbers below 10 are read as supersampling factor, else they work as width/height parameters
  13. #      since ssh defaults to ss, setting ss to the width only will use aspect ratio to set the equivalent ssh
  14. #
  15. # kernel_u: specify resizer for supersampling.
  16. #      0 - Point (eedi3_rpow2 is used) **not actually implemented, it just uses taa's eedi3 setting
  17. #      1 - Bilinear - pls don't
  18. #      2 - Bicubic  [2, b(0), c(0.5)]
  19. #      3 - Lanczos  [3, taps(4)]
  20. #      4 - Spline16
  21. #      5 - Spline36 (default)
  22. #      string: any fmtc ['kernel', taps or a1, a2, a3] or ['nnedi3', nsize, nns, qual]
  23. # kernel_d: specify resizer for supersampling.
  24. #      1 - Bilinear - Please note, the AVS wiki was written like 80 years ago. It's a low-complexity resizer, not an AA filter
  25. #      2 - Bicubic  [2, b(-0.5), c(0.25)]
  26. #      3 - Lanczos  [3, taps(ss*4)]
  27. #      4 - Spline16
  28. #      5 - Spline36 (default)
  29. #      string: any fmtc ['kernel', taps or a1, a2, a3], and no, you can't set invks
  30. #
  31. # a, b, g: alpha beta, & gamma eedi3 parameters
  32. # nrad, mdis: eedi3 parameters. defaults to 2, 20 for anything at or below 720p^2, scaling to 3, 30 at 1080p^2 and so on
  33. #
  34. # exmask: provide external aa mask. default uses the taa default mask: tcanny.TCanny(1.2)
  35.  
  36. def eedi3aa(clip, ss=2., ssh=None, kernel_u=[5], kernel_d=[5], a=0.5, b=0.2, g=None, nrad=None, mdis=None, exmask=None)
  37.     core = vs.core
  38.     # TODO
  39.     # Downscale output
  40.     # Force internal resizers when string inputs correspond w/ the core kernels
  41.    
  42.     f = clip.format
  43.     st = f.sample_type
  44.     bits = f.bits_per_sample
  45.     isGRAY = f.color_family==vs.GRAY
  46.    
  47.     if bits!=32 and st==vs.FLOAT:
  48.         raise TypeError('eedi3.eedi3aa: 16 bit float unsupported.')
  49.    
  50.     iw = clip.width
  51.     ih = clip.height
  52.    
  53.     ssw = ss
  54.     ssh = ssh if ssh is not None else ss if ss<10 else round( ssw / (iw/ih) )
  55.    
  56.     ssw = round(iw*ssw) if ssw<10 else ssw
  57.     ssh = round(ih*ssh) if ssh<10 else ssh
  58.    
  59.     rfac = max(ssw/128, ssh/72)
  60.     mdis = mdis if mdis is not None else nrad*10 if isinstance(nrad, int) else min(20, round(rfac) )
  61.     nrad = nrad if nrad is not None else min(2, rfac//10)
  62.    
  63.     alpha = min(1, max(0, a) )
  64.     beta = min(1, max(0, b) )
  65.     beta = beta if (alpha+beta)<=1 else 1-alpha
  66.     gamma = g
  67.     nrad = max(0, min(4, nrad) )
  68.     mdis = max(1, min(40, mdis) )
  69.  
  70.     kernel_u = [kernel_u] if not isinstance(kernel_u, list) else kernel_u
  71.     kernel_d = [kernel_d] if not isinstance(kernel_d, list) else kernel_d
  72.  
  73.     if kernel_u[0]<=0:
  74.         try:
  75.             import vsTAAmbk as taa
  76.             return taa.TAAmbk(clip, aatype=2, alpha=alpha, beta=beta, gamma=gamma, nrad=nrad, mdis=mdis, mclip=exmask)
  77.         except ModuleNotFoundError:
  78.             import taa
  79.             return taa.TAAmbkX(clip, aatype='eedi3', alpha=alpha, beta=beta, gamma=gamma, nrad=nrad, mdis=mdis, mclip=exmask)
  80.    
  81.     src = togray(clip)
  82.    
  83.     if exmask is None:
  84.         tc = y8(src, 1).tcanny.TCanny(sigma=1.2, t_h=8.0, mode=0).std.Maximum()
  85.         mclip = None if if hasattr(core, 'eedi3m') else tc.std.Maximum()
  86.         aamask = tc.std.Convolution([1]*9)
  87.         aamask = togray(aamask, bits, range=1, dmode=1)
  88.     else:
  89.         exmask = togray(exmask)
  90.         mclip = None if if hasattr(core, 'eedi3m') else y8(exmask, 1, 1).std.Maximum()
  91.         aamask = togray(exmask, bits, 1, 1)
  92.    
  93.     mclipv = None if if hasattr(core, 'eedi3m') else togray(mclip, resize=[ssw,ssh], kernel=2)
  94.     mcliph = None if if hasattr(core, 'eedi3m') else mclipv.std.Transpose()
  95.    
  96.     if kernel_u[0]=='nnedi3':
  97.         aa = togray(src, min(16, bits))
  98.         aa = aa.nnedi3.nnedi3(1, True, 0, kernel_u[1], kernel_u[2], kernel_u[3])
  99.         aa = aa.std.Transpose()
  100.         aa = aa.nnedi3.nnedi3(1, True, 0, kernel_u[1], kernel_u[2], kernel_u[3])
  101.         aa = togray(aa, 8, resize=[ssh, ssw, 0.5, -0.5])
  102.     else:
  103.         aa = src.std.Transpose()
  104.         if isinstance(kernel_u[0], int):
  105.             aa = togray(aa, 8, resize=[ssh, ssw], kernel=kernel_u)
  106.         else:
  107.             aa = aa.fmtc.resample(ssh, ssw, kernel=kernel_u[0], taps=kernel_u[1], a1=kernel_u[1], a2=kernel_u[2], a3=kernel_u[3], flt=0)
  108.     aa = eedi3(aa, dh=False, alpha=alpha, beta=beta, gamma=gamma, nrad=nrad, mdis=mdis, mclip=mcliph)
  109.     aa = aa.std.Transpose()
  110.     aa = eedi3(aa, dh=False, alpha=alpha, beta=beta, gamma=gamma, nrad=nrad, mdis=mdis, mclip=mclipv)
  111.     if isinstance(kernel_u[0], int):
  112.         aa = togray(aa, bits, resize=[iw, ih], kernel=kernel_d)
  113.     else:
  114.         aa = aa.fmtc.resample(iw, ih, kernel=kernel_d[0], taps=kernel_d[1], a1=kernel_d[1], a2=kernel_d[2], a3=kernel_d[3], flt=0)
  115.    
  116.     aa = src.std.MaskedMerge(aa, aamask)
  117.    
  118.     return aa if isGRAY else muf.MergeChroma(aa, clip)
  119.    
  120. # "DogaKoboAA" - a banquet of questionable image scaling methods all crammed into one
  121. #
  122. # rfactor: parameter of nnedi3_rpow2 (only "2" and "4" are supported)
  123. # nsize: sharpness vs interpolation window. Range is 0 (sharpest) to 3 (strongest AA)
  124. # nns, qual: speed vs quality parameters. range: 0-4 and 1-2. lower 'qual' before lowering 'nns'
  125. # mixed: when False, overlay debicubic/nnedi3/eedi3 clip over input clip.
  126. #        when True, mix pre-eedi3'ed nnedi3_rpow2 clip with a debicubic+spline64 clip and overlay eedi3'ed lines
  127. def DogaKoboAA(clip, w=1280, h=720, rfactor=4, nsize=0, nns=3, qual=2, mixed=False):
  128.     core = vs.core
  129.     # TODO
  130.     # Downscale to resolutions between (iw, ih) and (w, h)
  131.     # Support float resolutions (src_width/height parameter for descale where?)
  132.    
  133.     f = clip.format
  134.     st = f.sample_type
  135.     bits = f.bits_per_sample
  136.     isGRAY = f.color_family==vs.GRAY
  137.    
  138.     if bits!=32 and st==vs.FLOAT:
  139.         raise TypeError('eedi3.DogaKoboAA: 16 bit float unsupported.')
  140.    
  141.     y_src = togray(clip)
  142.    
  143.     tc = y8(y_src, 1).tcanny.TCanny(sigma=1.2, t_h=8.0, mode=0).std.Maximum()
  144.     aamask = tc.std.Convolution([1]*9)
  145.     aamask = togray(aamask, bits, 1, 1)
  146.    
  147.     mclip = None if if hasattr(core, 'eedi3m') else tc.std.Maximum()
  148.     mclipv = None if if hasattr(core, 'eedi3m') else mclip.resize.Bicubic(iw*2, ih*2)
  149.     mcliph = None if if hasattr(core, 'eedi3m') else mclipv.std.Transpose()
  150.    
  151.     dsc = y16(y32(y_src).descale.Debicubic(w, h, 0.2, 0.4))
  152.    
  153.     rpow = dsc.nnedi3.nnedi3(1, True, 0, nsize, nns, qual).std.Transpose().nnedi3.nnedi3(1, True, 0, nsize, nns, qual)
  154.    
  155.     if mixed:
  156.         nnclip = togray(rpow, resize=[ih, iw, 0.5, -0.5]).std.Transpose()
  157.         spclip = dsc.fmtc.resample(iw, ih, kernel='spline64', flt=0)
  158.         y_src = mvf.LimitFilter(flt=spclip, src=nnclip, thr=1, elast=1.5)
  159.         y_src = togray(y_src, bits)
  160.        
  161.     if rfactor==4:
  162.         rpow = rpow.nnedi3.nnedi3(0, True, 0, nsize, nns, qual).std.Transpose().nnedi3.nnedi3(1, True, 0, nsize, nns, qual)
  163.         ssclip = togray(rpow, 8, resize=[iw*2, ih*2, -0.5, -0.5]).std.Transpose()
  164.     else:
  165.         ssclip = togray(rpow, 8, resize=[ih*2, iw*2, 0.5, -0.5], kernel=4)
  166.     aa = eedi3(ssclip, dh=False, alpha=0.25, beta=0.6, gamma=0.15, nrad=3, mdis=30, mclip=mcliph)
  167.     aa = aa.std.Transpose()
  168.     aa = eedi3(aa, dh=False, alpha=0.25, beta=0.6, gamma=0.15, nrad=3, mdis=30, mclip=mclipv)
  169.     lines = togray(aa, bits, resize=[iw, ih])
  170.    
  171.     merged = y_src.std.MaskedMerge(lines, aamask)
  172.    
  173.     return merged if isGRAY else muf.MergeChroma(merged, clip)
  174.  
  175. # eedi3 with mclip bug work-around when dh=False
  176. def eedi3(clip, field=1, dh=False, alpha=None, beta=None, gamma=None, nrad=None, mdis=None, mclip=None)
  177.     core = vs.core
  178.    
  179.     if not isinstance(clip, vs.VideoNode):
  180.         raise TypeError('eedi3.py: clip must be a video clip!')
  181.    
  182.     if hasattr(core, 'eedi3m'):
  183.         return clip.eedi3m.EEDI3(field, dh, 0, alpha, beta, gamma, nrad, mdis)
  184.    
  185.     if not isinstance(mclip, vs.VideoNode) or mclip is not None:
  186.         raise TypeError('eedi3.py: mclip must be a video clip!')
  187.     if field!=0 and field!=1:
  188.         raise TypeError('eedi3.py: only single rate deinterlacing is supported')
  189.    
  190.     f = clip.format
  191.     range = 1 if f.bits_per_sample==16 and f.sample_type==vs.FLOAT else None
  192.    
  193.     clip = y8(clip, 3, range)
  194.    
  195.     if mclip is None:
  196.         return clip.eedi3_092.eedi3(field, True, 0, alpha, beta, gamma, nrad, mdis)
  197.    
  198.     mclip = y8(mclip, 1, 1)
  199.    
  200.     iw = clip.width
  201.     ih = clip.height
  202.     mw = mclip.width
  203.     mh = mclip.height
  204.    
  205.     if (iw, ih)!=(mw, mh)
  206.         if (iw, ih)==(mh, mw):
  207.             mclip = core.std.Transpose(mclip)
  208.         else:
  209.             raise TypeError('eedi3.py: incorrect mclip resolution. Attempt to correct with std.Transpose failed.')
  210.    
  211.     if dh==False:
  212.         mclip = mclip.std.SeparateFields(tff=False).std.SelectEvery(2, field)
  213.         clip = clip.std.SeparateFields(tff=False).std.SelectEvery(2, field)
  214.    
  215.     return clip.eedi3_092.eedi3(field, True, 0, alpha, beta, gamma, nrad, mdis, mclip=mclip)
  216.  
  217.  
  218.  
  219.  
  220. # Utility functions
  221. def togray(clip, bits=None, dmode=3, range=None, int16=True, resize=None, kernel=5):
  222.     core = vs.core
  223.    
  224.     f = clip.format
  225.     cf = f.color_family
  226.     isGRAY = cf==vs.GRAY
  227.     isYUV = cf==vs.YUV
  228.     isRGB = cf==vs.RGB
  229.     isYCOCG = cf==vs.YCOCG
  230.     in_st = f.sample_type
  231.     in_bits = f.bits_per_sample
  232.    
  233.     iw = clip.width
  234.     ih = clip.height
  235.    
  236.     bits = in_bits if bits==None else bits
  237.     st = vs.FLOAT if bits==32 else st if (bits, in_bits)==(16, 16) and not int16 else vs.INTEGER
  238.    
  239.     f16_to_i16 = (in_bits, bits, in_st, st)==(16, 16, vs.FLOAT, vs.INTEGER)
  240.    
  241.     probablyfull = isRGB or isYCOCG or f16_to_i16
  242.     range = get_range_int(range, probablyfull)
  243.    
  244.     scaling = [] if resize is None else [resize] if isinstance(resize, int) else resize
  245.     scaling = scaling+[None,None,None,None,None,None]
  246.    
  247.     ow = iw if scaling[0] is None else scaling[0]
  248.     oh = ih if scaling[1] is None else scaling[1]
  249.     dscl = (iw * ih) > (ow * oh)
  250.    
  251.     kernel = [kernel] if isinstance(kernel, int) else kernel
  252.     def_bic = ([-0.5, 0.25] if dscl else [0, 0.5]) if len(kernel)==1 else [(0-kernel[1])/2] if dscl else [(1-kernel[1])/2]
  253.     def_lan = [math.ceil((iw/ow + ih/oh)*2), None] if dscl else [4, None]
  254.  
  255.     kernel = kernel+def_bic if kernel[0]==2 else kernel+def_lan if kernel[0]==3 else kernel+[None, None]
  256.    
  257.     Resizer = core.resize.Point if kernel[0]==0 else core.resize.Bilinear if kernel[0]==1 else core.resize.Bicubic if kernel[0]==2 else core.resize.Lanczos if kernel[0]==3 else core.resize.Spline16 if kernel[0]==4 else core.resize.Spline36
  258.     res_params = {'width': scaling[0], 'height': scaling[1], 'src_left': scaling[2], 'src_top': scaling[3], 'src_width': scaling[4], 'src_height': scaling[5], 'filter_param_a': kernel[1], 'filter_param_b': kernel[2]}
  259.    
  260.     if (in_bits, in_st)==(bits, st) and isGRAY and resize is None:
  261.         pass
  262.     elif (in_bits, in_st)==(bits, st) and isYUV and resize is None:
  263.         return clip.std.ShufflePlanes(0, vs.GRAY)
  264.     else:
  265.         format = core.register_format(vs.GRAY, st, bits, 0, 0)
  266.         matrix = None if isGRAY or isYUV else mvf.GetMatrix(clip, id=True)
  267.         dither_type = dmode if isinstance(dmode, str) else 'ordered' if dmode==0 else 'none' if dmode<3 else 'error_diffusion'
  268.         return Resizer(clip, format=format.id, matrix=matrix, dither_type=dither_type, range_in=range, range=range, **res_params)
  269.  
  270. y = togray
  271. y8 = functools.partial(togray, bits=8)
  272. y16 = functools.partial(togray, bits=16)
  273. y32 = functools.partial(togray, bits=32)
Add Comment
Please, Sign In to add comment