############################################################################# # Temporal Degrain V1.18 (Feb 14, 2008) # # IDX BUG FIXED AT: April 5, 2008 -- Didée # # # # Function by Sagekilla, original script created by Didee # # # # Works as a simple temporal degraining function that'll remove MOST grain # # from video sources, including dancing grain, like the grain found on 300. # # Is currently capable of utilizing MVDegrain 1, 2, or 3 for degraining # # Also note, the parameters don't need to be tweaked much. # # # # Required plugins: FFT3DFilter.dll / mt_masktools / MVtools.dll # # HQdn3D.dll / RemoveGrain.dll / Repair.dll # # # # FFT3DFilter: http://bag.hotmail.ru/fft3dfilter/fft3dfilter.dhtml # # mt_masktools: http://manao4.free.fr/mt_masktools.html # # MVtools: http://avisynth.org.ru/mvtools/mvtools.html # # RemoveGrain: http://www.removegrain.de.tf/ # # Repair: See above # # HQdn3D: http://akuvian.org/src/avisynth/hqdn3d/ # ############################################################################# ######################################################## # Description of functions # # denoise = Denoised clip for detecting motion vectors # # sigma = FFT3D filtering strength # # bw = FFT3D block width # # bh = FFT3D block height # # pel = MVAnalyse Subpixel accuracy # # blksize = MVAnalyse block size # # ov = MVAnalyse block overlap # # SAD1 = MVDegrain thSAD # # SAD2 = MVDegrain thSAD # # degrain = MVDegrain with 1, 2, or 3 vectors # # HQ = Enables prepass for motion vector search # # threads = Number of threads for FFT3DFilter # ######################################################## function TemporalDegrain ( clip input, clip "denoise", int "threads", int "sigma", int "bw", int "bh", int "pel", \ int "blksize", int "ov", int "degrain", int "limit", int "SAD1", int "SAD2", int "HQ", string "ppass" ) { o = input ncpu = default( threads, 1 ) # Threads for FFT3DFilter sigma = default( sigma, 16 ) # Default seems to work fine -- Higher values don't help much bw = default( bw, 16 ) # FFT3D block width bh = default( bh, 16 ) # FFT3D block height pel = default( pel, 2 ) # Higher values increase motion vector quality at the cost of speed blksize = default( blksize, 8 ) # use 16 for more speed, or for HD resolutions like 1080p ov = default( ov, blksize/2) # Increase for better motion vectors but slower speed. Max is blksize/2 degrain = default( degrain, 2 ) # MVDegrain 1, 2 or 3 limit = default( limit, 255 ) # Limits maximum change of a pixel. Default means no limit SAD1 = default( SAD1, 400 ) # Threshold for degraining. Decrease if you suffer from ghosting SAD2 = default( SAD2, 300 ) # See above HQ = default( HQ, 1 ) # How much to clean up clip for motion vector searching ppass = default( ppass, "fft" ) # Use FFT3DFilter or dfttest for MV search cleanup s2 = floor ( sigma * 0.625 ) # See sigma s3 = floor ( sigma * 0.375 ) # See sigma s4 = floor ( sigma * 0.250 ) # See sigma ow = bw / 2 # Don't adjust unless you need speed oh = bh / 2 # See above ov = (ov*2>blksize) ? blksize/2 : ov # "srch" is a prefiltered clip on which the motion serach is done. # Here, we simply use FFT3DFilter. You can also use your own. Basically, you shouldn't use # a clip with "a tiny bit of filtering". The search clip should ideally be "dead calm." filter = o filter = defined(denoise) ? denoise : filter filter = (ppass=="fft") ? filter.fft3dfilter(ncpu=ncpu,sigma=sigma,sigma2=s2,sigma3=s3,sigma4=s4,bt=5,bw=bw,bh=bh,ow=ow,oh=oh) : filter filter = (ppass=="dft") ? filter.dfttest(sigma=2.5,tbsize=1,sbsize=8,sosize=6,u=false,v=false,swin=7,twin=7) : filter filter = (HQ>=2) ? filter.HQdn3D(4,3,6,3) : filter srch = filter # "spat" is a prefiltered clip which is used to limit the effect of the 1st MV-denoise stage. # For simplicity, we just use the same FFT3DFilter. There's lots of other possibilities. spat = filter spatD = mt_makediff(o,spat) # Motion vector search (With very basic parameters. Add your own parameters as needed.) b3vec = (degrain==3) ? \ srch.MVAnalyse(isb=true, delta=3, pel=pel, overlap=ov, idx=1, blksize=blksize) : BlankClip b2vec = (degrain>=2) ? \ srch.MVAnalyse(isb=true, delta=2, pel=pel, overlap=ov, idx=1, blksize=blksize) : BlankClip b1vec = srch.MVAnalyse(isb=true, delta=1, pel=pel, overlap=ov, idx=1, blksize=blksize) f1vec = srch.MVAnalyse(isb=false, delta=1, pel=pel, overlap=ov, idx=1, blksize=blksize) f2vec = (degrain>=2) ? \ srch.MVAnalyse(isb=false, delta=2, pel=pel, overlap=ov, idx=1, blksize=blksize) : BlankClip f3vec = (degrain==3) ? \ srch.MVAnalyse(isb=false, delta=3, pel=pel, overlap=ov, idx=1, blksize=blksize) : BlankClip # First MV-denoising stage. Usually here's some temporal-medianfiltering going on. # For simplicity, we just use MVDegrain. NR1 = (degrain==3) ? o.MVDegrain3(b1vec,f1vec,b2vec,f2vec,b3vec,f3vec,thSAD=SAD1,idx=2,limit=limit) : \ (degrain==2) ? o.MVDegrain2(b1vec,f1vec,b2vec,f2vec,thSAD=SAD1,idx=2,limit=limit) : \ o.MVDegrain1(b1vec,f1vec,thSAD=SAD1,idx=2,limit=limit) NR1D = mt_makediff(o,NR1) # Limit NR1 to not do more than what "spat" would do. DD = mt_lutxy(spatD,NR1D,"x 128 - abs y 128 - abs < x y ?") NR1x = o.mt_makediff(DD,U=2,V=2) # Second MV-denoising stage. We use MVDegrain2. NR2 = (degrain==3) ? NR1x.MVDegrain3(b1vec,f1vec,b2vec,f2vec,b3vec,f3vec,thSAD=SAD2,idx=3,limit=limit) : \ (degrain==2) ? NR1x.MVDegrain2(b1vec,f1vec,b2vec,f2vec,thSAD=SAD1,idx=3,limit=limit) : \ NR1x.MVDegrain1(b1vec,f1vec,thSAD=SAD1,idx=3,limit=limit) NR2 = (HQ>=2) ? NR2.HQDn3D(1,1,4,1) : NR2 # Temporal filter to remove last bits of dancing pixels # Contra-sharpening: sharpen the denoised clip, but don't add more than what was removed previously. # Here: A simple area-based version with relaxed restriction. The full version is more complicated. s = NR2.minblur(1,1) # Damp down remaining spots of the denoised clip. allD = mt_makediff(o,NR2) # The difference achieved by the denoising. ssD = mt_makediff(s,s.removegrain(11,-1)) # The difference of a simple kernel blur. ssDD = ssD.repair(allD,1) # Limit the difference to the max of what the denoising removed locally. ssDD = ssDD.mt_lutxy(ssD,"x 128 - abs y 128 - abs < x y ?") # abs(diff) after limiting may not be bigger than before. NR2.mt_adddiff(ssDD,U=2,V=2) # Apply the limited difference. (Sharpening is just inverse blurring.) output = last return(output) } # From Didee's MCBob script function MinBlur(clip clp, int r, int "uv") { uv = default(uv,3) uv2 = (uv==2) ? 1 : uv rg4 = (uv==3) ? 4 : -1 rg11 = (uv==3) ? 11 : -1 rg20 = (uv==3) ? 20 : -1 medf = (uv==3) ? 1 : -200 RG11D = (r==1) ? mt_makediff(clp,clp.removegrain(11,rg11),U=uv2,V=uv2) \ : (r==2) ? mt_makediff(clp,clp.removegrain(11,rg11).removegrain(20,rg20),U=uv2,V=uv2) \ : mt_makediff(clp,clp.removegrain(11,rg11).removegrain(20,rg20).removegrain(20,rg20),U=uv2,V=uv2) RG4D = (r==1) ? mt_makediff(clp,clp.removegrain(4,rg4),U=uv2,V=uv2) \ : (r==2) ? mt_makediff(clp,clp.medianblur(2,2*medf,2*medf),U=uv2,V=uv2) \ : mt_makediff(clp,clp.medianblur(3,3*medf,3*medf),U=uv2,V=uv2) DD = mt_lutxy(RG11D,RG4D,"x 128 - y 128 - * 0 < 128 x 128 - abs y 128 - abs < x y ? ?",U=uv2,V=uv2) clp.mt_makediff(DD,U=uv,V=uv) return(last) }