Hello. I'm working with animated content and am trying to do some prefiltering with McTemporalDenoise. I am trying to avoid all lines and edges, which my current script seems to be doing quite well. I am using mt_edge("prewitt). My problem is that, as you can see in my sample images, although it seems like I have the right idea with my current script, the filter does not seem to cover 100% of the image (which, as I stated, I wish to filter minus the lines). Please see my image samples---I used white as a color test for a visual of what I'm trying to do here. Is there any way I can tweak this to cover the entire area but still avoiding the lines?
[Attachment 49472 - Click to enlarge]
[Attachment 49473 - Click to enlarge]
I am using the following script:
Attached also is a sample video.Code:MPEG2Source("blah.d2v", cpu=0) TFM(d2v="blah.d2v") TDecimate(mode=1) #Include a sharpener FIRST so that your lines can be very dark, thus the mask protecting them. Toonlite() Santiag(). TurnRight(). Santiag().TurnLeft() a=last g=blankclip(a, color=color_white) #mask: mask = mt_edge("prewitt"). mt_invert() # Overlay of clip, filter, and mask Overlay(a,g,mask=mask)
Thanks.
Try StreamFab Downloader and download from Netflix, Amazon, Youtube! Or Try DVDFab and copy Blu-rays! or rip iTunes movies!
+ Reply to Thread
Results 1 to 10 of 10
Thread
-
-
Use mt_expand() to thicken the edges, mt_inpand() to thin them. Something like:
mask = mt_edge("prewitt").mt_inpand().mt_expand().mt_expa nd().mt_inpand().mt_invert()
The first mt_inpand() eliminates a lot of the noise areas (or use a noise reduction filter before building the mask). mt_expand() expands the edges to fill in the "holes". The final mt_inpand() thins the edges. You'll probably want to soften the edges of the mask with a blur.
Or use a noise reduction filter that's better at retaining edges, like TemporalDegrain2().Last edited by jagabo; 27th Jun 2019 at 09:54.
-
Or use a noise reduction filter that's better at retaining edges, like TemporalDegrain2()
[Attachment 49475 - Click to enlarge] -
Is there something in my script order that shouldn't be there that's probably making it do that?
I use crop(), addborders(), then I IVTC and then do some color tweaking. Then I add temporaldegrain2(). -
It's not, but it's still part of the clip. Nevertheless, temporaldegrain2() is still causing that type of result in the whole clip. Maybe I have an old version? Do you have an updated script that perhaps I can use? I'd appreciate it.
-
You must be using 32 bit AviSynth? I didn't see a 64 bit version of ToonLite. Here's the TemporalDegrain2.avsi I have for 32 bit AviSynth:
Code:############################################################################# # Temporal Degrain v2.1.2 Updated by ErazorTT # # # # Function v1.23 by Sagekilla, idea + original script created by Didee # # # # Works as a simple temporal degraining function that'll remove # # MOST or even ALL grain and noise from video sources, # # including dancing grain, like the grain found on 300. # # Also note, the parameters don't need to be tweaked much. # # # # Required plugins: # # FFT3DFilter: https://github.com/pinterf/fft3dfilter/releases # # MaskTools2: https://github.com/pinterf/masktools/releases # # MVtools2 (mt_*): https://github.com/pinterf/mvtools/releases # # rgTools (RemoveGrain,Repair): https://github.com/pinterf/RgTools/releases # # # # Optional plugins: # # dfttest: https://github.com/DJATOM/dfttest/releases # # FFT3DGPU: http://avisynth.nl/index.php/AviSynth%2B_x64_plugins # # KNLMeansCL: https://github.com/Khanattila/KNLMeansCL/releases # # Dither tools: http://avisynth.nl/index.php/Dither_tools # ############################################################################# # Changelog: # May 15, 2018: v2.0 # - use the exeptional motion estimation from QTGMC # - use MDegrainN for unbound temporal radius # - automatic tuning of default parameters based on input size # - add optional motion compensated post processing FFT filtering # Oct 19, 2018: v2.0.1 # - rename function to TemporalDegrain2 # - expose TrueMotion parameter through meTM, and let it default to false # Oct 22, 2018: v2.0.2 # - use power of 2 blocksizes for MAnalyse which have much better performance # - check that Undot exists before calling it # Oct 23, 2018: v2.0.3 # - use RemoveGrain(1) instead of Undot # Oct 26, 2018: v2.1 # - tune motion estimation # Oct 27, 2018: v2.1.1 # - allow for a controlled over-sharpen through extraSharp # - correct parameter types # Oct 30, 2018: v2.1.2 # - correct usage of KNLMeansCL # - update comments # recommendation: # 1. start with default settings # 2a.if there is too much denoising for your taste use degrainTR=1 # 2b.if more denoising is needed try postFFT=1 with postSigma=1, then tune postSigma (obvious blocking and banding of the sky are indications of a value which is at least a factor 2 too high) # 3. do not increase degrainTR above 1/8th of the fps (at 24fps up to 3) # 4. if there are any issues with banding switch to postFFT=3 # 5. if there are still issues with banding also use postDither=-1 # use only the following knobs (all other settings should already be were they need to be): # - degrainTR, temporal radius of degrain, usefull range: min=1, default=2, max=fps/8. Higher values do clean the video more, but also increase probability of wrongly identified motion search which leads to washed out regions # - postFFT, if you want to remove absolutely all remaining noise suggestion is to use 3 (dfttest) for its quality or 1 (ff3dfilter) is much faster but can introduce banding. 2 and 4 are GPU based versions. # - postSigma, increase it to remove all the remaining noise you want removed, but do not increase too much since unnecessary high values have severe negative impact on either banding and/or sharpness # - postDither, positive values are used as parameter for dfttest (values above 2 can remove preexinsting banding), negative values indicate 16 bits FFT processing followd by diterhing (to be used only in case of strong paranoia against introducing banding) # - degrainPlane, if you just want to denoise the chroma use 3 (helps with compressability with the clip being almost identical to the original) function TemporalDegrain2 ( clip input, int "degrainTR", int "degrainPlane", int "meAlg", int "meAlgPar", int "meSubpel", int "meBlksz", bool "meTM", \ int "limitFFT", float "limitSigma", int "limitBlksz", int "fftThreads", int "postFFT", int "postTR", float "postSigma", int "postDither", \ float "ppSAD1", float "ppSAD2", float "ppSCD1", int "thSCD2", int "DCT", float "gausParam", bool "extraSharp") { longlat = (input.Width() > input.Height()) ? input.Width() : input.Height() autoTune = (longlat<1000) ? 0 : \ (longlat<1300) ? 1 : \ (longlat<2100) ? 2 : 3 degrainTR = default( degrainTR,2 ) # MDegrain with 1-6 temporal radius. degrainPlane = default( degrainPlane, 4 ) # which planes to degrain: 0=luma, 3=chroma, 4=luma+chroma. meAlg = default( meAlg, 4 ) # Motion estimation algorithm (5 might be a little better but is significantly slower, 2 will be a little faster while missing some vectors), https://forum.doom9.org/showthread.php?p=693742 meAlgPar= default( meAlgPar, Select( meAlg,2,2,2,2,16,24,2,2 ) ) # radius/range parameter for the motion estimation algorithms meSubpel= default( meSubpel, Select( autoTune, 4, 2, 2, 1 ) ) # higher values increase motion vector quality at the cost of speed meBlksz = default( meBlksz, Select( autoTune, 8, 8,16,32 ) ) # higher values for more speed, but higher probability of smearing of fine details, do also not go too small so that blocks still have enough content to be distinguishable meTM = default( meTM, false ) # this is the default of QTGMC and prefered by Didee (https://forum.doom9.org/showthread.php?p=1407409&highlight=truemotion#post1407409) limitFFT= default( limitFFT, 1 ) # wether to use the limit clip. Do never ever disable this, thats part of the very core of the functionality limitSigma= default( limitSigma, Select( autoTune, 8,12,16,32 ) ) # strength of filtering for limit clip limitBlksz= default( limitBlksz, Select( autoTune,16,24,32,64 ) ) # FFT3D blocksize postFFT = default( postFFT, 0 ) # Whether the FFT post processing takes place, deminishes remaining noise, 1 is fast but produces some banding, 3 is of high quality but slow postTR = default( postTR, 1 ) # Temporal radius of FFT processing, increasing to 2 does not help much, but is much slower postSigma=default( postSigma,1.0) # Strength of the filtering postDither=default(postDither,1 ) # Dithering strength for dfttest, negative enables 16bit post FFT with a final call to ditherpost fftThreads = default( fftThreads, 1 ) # Number of threads to be used for FFTs extraSharp = default( extraSharp, false) # enable for a moderate over-sharpen, do not use this if you want the output to be as close to the original as possible, only to be used by sharpness junkies postTR = (postFFT > 0) ? postTR : 0 postTD = postTR * 2 + 1 maxTR = (degrainTR > postTR) ? degrainTR : postTR LumaNoise = (degrainPlane == 0) || (degrainPlane == 4) ChromaNoise = (degrainPlane > 0) ? true : false ChromaMotion = true #fix! GlobalNames = "TD2" ReuseGlobals = false ReplaceGlobals = false SubPelInterp = 2 #fix? SubPel = meSubpel Blocksize = meBlksz Overlap = Blocksize/2 Search = meAlg SearchParam = meAlgPar PelSearch = SubPel TrueMotion = meTM GlobalMotion = true #fix! SrchClipPP = 3 Lambda = ((TrueMotion) ? 1000 : 100 ) * (BlockSize*BlockSize)/(8*8) LSAD = (TrueMotion) ? 1200 : 400 PNew = (TrueMotion) ? 50 : 25 PLevel = (TrueMotion) ? 1 : 0 DCT = default(DCT, 0) thSAD1 = int(default(ppSAD1, 10) *8*8) #here the per-pixel measure is converted to the per-8x8-Block measure MVTools is using thSAD2 = int(default(ppSAD2, 5) *8*8) #here the per-pixel measure is converted to the per-8x8-Block measure MVTools is using thSCD1 = int(default(ppSCD1, 4) *8*8) #here the per-pixel measure is converted to the per-8x8-Block measure MVTools is using thSCD2 = default(thSCD2, 98) gausParam = default(gausParam, 3.0) #--------------------------------------- # Pre-Processing yuy2 = input.IsYUY2() input = yuy2 ? input.ConvertToYV16() : input w = input.Width() h = input.Height() epsilon = 0.0001 # Reverse "field" dominance for progressive repair mode 3 (only difference from mode 2) compl = Input # Pad vertically during processing (to prevent artefacts at top & bottom edges) clip = compl # Calculate padding needed for MVTools super clips to avoid crashes [fixed in latest MVTools, but keeping this code for a while] hpad = w - (Int((w - Overlap) / (Blocksize - Overlap)) * (Blocksize - Overlap) + Overlap) vpad = h - (Int((h - Overlap) / (Blocksize - Overlap)) * (Blocksize - Overlap) + Overlap) hpad = (hpad > 8) ? hpad : 8 # But match default padding if possible vpad = (vpad > 8) ? vpad : 8 #--------------------------------------- # Motion Analysis # >>> Planar YUY2 for motion analysis, interleaved whilst blurring search clip planarClip = clip # Bob the input as a starting point for motion search clip bobbed = planarClip # If required, get any existing global clips with a matching "GlobalNames" setting. Unmatched values get NOP (= 0) srchClip = DT_GetUserGlobal( GlobalNames, "srchClip", ReuseGlobals ) srchSuper = DT_GetUserGlobal( GlobalNames, "srchSuper", ReuseGlobals ) vmulti = DT_GetUserGlobal( GlobalNames, "vmulti", ReuseGlobals ) CMmt = ChromaMotion ? 3 : 1 CMts = ChromaMotion ? 255 : 0 CMrg = ChromaMotion ? 12 : -1 # Remove areas of difference between temporal blurred motion search clip and bob that are not due to bob-shimmer - removes general motion blur repair0 = bobbed # Blur image and soften edges to assist in motion matching of edge blocks. Blocks are matched by SAD (sum of absolute differences between blocks), but even # a slight change in an edge from frame to frame will give a high SAD due to the higher contrast of edges spatialBlur = (IsClip(srchClip) || SrchClipPP == 0) ? NOP() : \ (SrchClipPP == 1) ? repair0.BilinearResize( w/2, h/2 ).RemoveGrain( 12,CMrg ).BilinearResize( w, h ) : \ repair0.RemoveGrain( 12,CMrg ).GaussResize( w,h, 0,0, w+epsilon,h+epsilon, p=gausParam ) spatialBlur = (IsClip(spatialBlur) && SrchClipPP > 1) ? (ChromaMotion ? spatialBlur.Merge( repair0, 0.1 ) : spatialBlur.MergeLuma( repair0, 0.1 )) : spatialBlur tweaked = repair0 srchClip = IsClip(srchClip) ? srchClip : \ (SrchClipPP == 0) ? repair0 : \ (SrchClipPP < 3) ? spatialBlur : \ spatialBlur.mt_lutxy( tweaked, "x 7 scalef + y < x 2 scalef + x 7 scalef - y > x 2 scalef - x 51 * y 49 * + 100 / ? ?", U=CMmt,V=CMmt ) # Calculate forward and backward motion vectors from motion search clip srchSuper = IsClip(srchSuper) ? srchSuper : \ (maxTR > 0) ? srchClip.MSuper( pel=SubPel, sharp=SubPelInterp, hpad=hpad, vpad=vpad, chroma=ChromaMotion ) : NOP() vmulti = IsClip(vmulti) ? vmulti : \ (maxTR > 0) ? srchSuper.MAnalyse( isb=true, multi=true, delta=maxTR, blksize=BlockSize, overlap=Overlap, search=Search, searchparam=SearchParam, \ pelsearch=PelSearch, truemotion=TrueMotion, lambda=Lambda, lsad=LSAD, pnew=PNew, plevel=PLevel, \ global=GlobalMotion, DCT=DCT, chroma=ChromaMotion ) : NOP() #return(MShow(srchSuper,vmulti.SelectEvery(maxTR*2,0))) # Expose search clip, motion search super clip and motion vectors to calling script through globals DT_SetUserGlobal( GlobalNames, "srchClip", srchClip, ReplaceGlobals ) DT_SetUserGlobal( GlobalNames, "srchSuper", srchSuper, ReplaceGlobals ) DT_SetUserGlobal( GlobalNames, "vmulti", vmulti, ReplaceGlobals ) #--------------------------------------- # Degrain o = planarClip s2 = floor( limitSigma * 0.625 ) # See sigma s3 = floor( limitSigma * 0.375 ) # See sigma s4 = floor( limitSigma * 0.250 ) # See sigma filter = (limitFFT==1) ? o.FFT3DFilter(plane=degrainPlane, sigma=limitSigma, sigma2=s2, sigma3=s3, sigma4=s4, bt=3, bw=limitBlksz, bh=limitBlksz, ncpu=fftThreads) : \ o.FFT3DGPU(plane=degrainPlane, sigma=limitSigma, sigma2=s2 , sigma3=s3, sigma4=s4, bt=3, bw=limitBlksz, bh=limitBlksz) # "spat" is a prefiltered clip which is used to limit the effect of the 1st MV-denoise stage. # For simplicity, we just use the FFT3DFilter. There's lots of other possibilities. spat = filter spatD = mt_makediff(o,spat) # First MV-denoising stage. Usually here's some temporal-medianfiltering going on. # For simplicity, we just use MVDegrain. vmultiDegrain = (degrainTR>0) ? vmulti.SelectRangeEvery(maxTR*2,degrainTR*2) : NOP() o_super = o.MSuper(pel=SubPel, levels=1, chroma=ChromaNoise) NR1 = (degrainTR>0) ? o.MDegrainN(o_super, vmultiDegrain, degrainTR, plane=degrainPlane, thSAD=thSAD1, thSCD1=thSCD1, thSCD2=thSCD2) : o # Limit NR1 to not do more than what "spat" would do. NR1D = mt_makediff(o,NR1) DD = mt_lutxy(spatD,NR1D,"x range_half - abs y range_half - abs < x y ?") NR1x = mt_makediff(o,DD,U=2,V=2) # Second MV-denoising stage. We use MVDegrain2. NR1x_super = NR1x.MSuper(pel=SubPel, levels=1, chroma=ChromaNoise) NR2 = (degrainTR>0) ? NR1x.MDegrainN(NR1x_super, vmultiDegrain, degrainTR, plane=degrainPlane, thSAD=thSAD2, thSCD1=thSCD1, thSCD2=thSCD2) : o NR2 = NR2.RemoveGrain(mode=1) #aka Undot, Filter to remove last bits of dancing pixels, YMMV. #--------------------------------------- # post FFT fullClip = NR2 fullSuper = (postTR > 0) ? fullClip.MSuper( pel=subpel, levels=1, chroma=ChromaNoise ) : NOP() noiseWindow = (postTR == 0) ? fullClip : \ fullClip.MCompensate( fullSuper, vmulti.SelectRangeEvery(maxTR*2,postTR*2), tr=postTR, center=true, thSAD=thSAD2, thSCD1=thSCD1, thSCD2=thSCD2 ) dftDither = (postDither > 0) ? postDither : 0 lsbd = (postDither < 0) ? true : false dnWindow = (postFFT == 0) ? noiseWindow : \ (postFFT == 1) ? FFT3DFilter( lsbd ? noiseWindow.ConvertTo16bit() : noiseWindow, plane=degrainPlane, sigma=postSigma, bt=postTD, ncpu=fftThreads ) : \ (postFFT == 2) ? FFT3DGPU( noiseWindow, plane=degrainPlane, sigma=postSigma, bt=postTD ) : \ (postFFT == 3) ? dfttest(noiseWindow, Y=LumaNoise, U=ChromaNoise, V=ChromaNoise, sigma=postSigma*4, tbsize=postTD, threads=fftThreads, lsb=lsbd, dither=dftDither ) : \ DT_KNLMeansCL( lsbd ? noiseWindow.Dither_convert_8_to_16() : noiseWindow, a=2, d=postTR, h=postSigma*0.5, Luma = LumaNoise, Chroma = ChromaNoise, stacked=lsbd, device_type="GPU") dnWindow = dnWindow.SelectEvery( postTD, postTR ) NR3 = lsbd && (postFFT == 1) ? dnWindow.ConvertTo8bit(dither=1) : \ lsbd && (postFFT == 3 || postFFT == 4) ? dnWindow.ditherpost(mode=6, Y=LumaNoise?3:2, U=ChromaNoise?3:2, V=ChromaNoise?3:2, slice=false) : dnWindow sharpened = DT_ContraSharpening(NR3, o, extraSharp) # Crop off temporary vertical padding cropped = sharpened return(yuy2 ? cropped.ConvertToYUY2() : cropped) } # contra-sharpening: sharpen the denoised clip, but don't add more to any pixel than what was removed previously. # script function from Didee from the VERY GRAINY thread function DT_ContraSharpening(clip denoised, clip original, bool "extraSharp") { # Damp down remaining spots of the denoised clip. s = denoised.DT_MinBlur(1,1) # The difference achieved by the denoising. allD = mt_makediff(original,denoised) # The difference of a simple kernel blur. ssD = mt_makediff(s,extraSharp?s.removegrain(20,-1).removegrain(20,-1):s.removegrain(11,-1)) # Limit the difference to the max of what the denoising removed locally. ssDD = ssD.repair(extraSharp?ssD.repair(allD,1):allD,extraSharp?12:1) # abs(diff) after limiting may not be bigger than before. ssDD = SSDD.mt_lutxy(ssD,"x range_half - abs y range_half - abs < x y ?") # Apply the limited difference. (Sharpening is just inverse blurring.) denoised.mt_adddiff(ssDD,U=2,V=2) return(last) } # MinBlur by Didee, http://avisynth.org/mediawiki/MinBlur # Nifty Gauss/Median combination function DT_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 range_half - y range_half - * 0 < range_half x range_half - abs y range_half - abs < x y ? ?",U=uv2,V=uv2) clp.mt_makediff(DD,U=uv,V=uv) return(last) } function DT_KNLMeansCL(clip input, String "device_type", int "device_id", bool "Luma", bool "Chroma", bool "stacked", float "h", int "d", int "a") { uplane = input.ExtractU() vplane = input.ExtractV() cw = uplane.Width() ch = uplane.Height() yw = input.Width() yh = input.Height() channels = Select( (Luma ? 1 : 0) + (Chroma ? 2 : 0), 0, "Y", "UV", "YUV" ) strength = (Luma && Chroma) ? h*9/16 : Luma ? h : h/4.7 temp = (Luma && Chroma) ? YToUV(uplane.BilinearResize(yw,yh), vplane.BilinearResize(yw,yh), input.ExtractY()) : Luma ? input.ExtractY() : input output = KNLMeansCL(temp, a=a, d=d, h=strength, channels = channels, stacked=stacked, device_type="GPU") (Luma && Chroma) ? YToUV(output.ExtractU().BilinearResize(cw,ch), output.ExtractV().BilinearResize(cw,ch), output.ExtractY()) : Luma ? YToUV(uplane, vplane, output) : output } # Set global variable called "Prefix_Name" to "Value". Throws exception if global already exists unless Replace=true, in which case the global is overwritten function DT_SetUserGlobal( string Prefix, string Name, val Value, bool "Replace" ) { Replace = default( Replace, false ) globalName = Prefix + "_" + Name # Tricky logic to check global: enter catch block if Replace=true *or* globalName doesn't exist (i.e. need to set the global), the exception is not rethrown # Not entering catch block means that Replace=false and global exists - so it throws an exception back to AviSynth try { Assert( !Replace && defined(Eval(globalName)) ) } catch (e) { Eval( "global " + globalName + " = Value" ) Replace = true } Assert( Replace, """Multiple calls to QTGMC, set PrevGlobals="Replace" or read documentation on 'Multiple QTGMC Calls'""" ) } # Return value of global variable called "Prefix_Name". Returns NOP() if it doesn't exist or Reuse is false function DT_GetUserGlobal( string Prefix, string Name, bool "Reuse" ) { Reuse = default( Reuse, false ) globalName = Prefix + "_" + Name try { ret = Reuse ? Eval( globalName ) : NOP() } catch (e) { ret = NOP() } return ret }
-
Yeah, I'm getting the same results. I use smdegrain() anyway as my staple denoiser, which is fantastic. I use Mctemporaldenoise as a prefilter to clean bright pixels which smdegrain can't reach.
-
Again, thanks for your help jagabo. I'll continue to fiddle with the mask, as it's the only sure way to denoise without damaging edges.
Similar Threads
-
masking or background selection
By Kracov in forum EditingReplies: 6Last Post: 5th May 2019, 00:02 -
Help with masking an added cut
By solkap in forum EditingReplies: 4Last Post: 28th Dec 2018, 11:41 -
Moving Masking Points
By liviathisbe in forum EditingReplies: 1Last Post: 2nd Sep 2017, 14:24 -
Masking 1920x1080 16:9 footage to conform to a theatrical aspect ratio
By SameSelf in forum Video ConversionReplies: 36Last Post: 13th Nov 2016, 09:13