Log in

View Full Version : FizzKiller - Death to High Contrast Noise


Jawed
21st January 2008, 03:44
[See post 30 for current version]

I've been using MVDegrain2 for the cleaning up of "noise" for a while now. I've noticed that there's one type of noise, high-contrast noise, that utterly defeats MVD2.

I have provided two clips for the purposes of this thread, both from the same film, Concursante:


Smoke - this clip features a night scene with smoke blowing across the face of a building. The smoke is brightly lit, showing the kind of fizzy noise I dislike. Additionally other brightly lit surfaces feature this fizz, a great example of this is the man's face at the end of the clip. Smoke and MVD2 tend not to mix well, producing some swimming motion artefacts, too. The flashing lights also create a rather entertaining motion artefact - this can be seen most clearly on the dark diagonal line on the front corner of the ambulance in the latter half of the clip. The full-screen white flashes also turn out to be quite useful tests for obscure reasons…

Blackboard - several faces in this clip show how annoying this fizzy noise is. The blackboard, with its low contrast scribblings, provides a fairly sensitive test for ghosting and swimming motion artefacts.

http://www.megaupload.com/?d=R0XDV4MT

This is how I've been using MVD2 for a while now:

function MVDegrain2p(clip source, int "blksize", int "overlap", int "sharp", int "thSAD", int "idx")
{ # Motion compensated denoiser for progressive source clip, adapted from http://avisynth.org.ru/mvtools/mvtools.html
# uses MVTools plugin

blksize = default(blksize, 8) # blksize value (4, 8 or 16)
overlap = default(overlap, blksize/2) # overlap value (0 to half blksize)
sharp = default(sharp, 1) # 0=bilinear softest, 1=bicubic, 2=Wiener sharpest
thSAD = default(thSAD, 400) # higher risks motion ghosting and swimming, lower risks blotchy denoising
idx = default(idx, 1) # use various idx for different sources in same script

backward_vec2 = source.MVAnalyse(isb = true, delta = 2, pel = 2, blksize=blksize, overlap=overlap, sharp=sharp, idx = idx)
backward_vec1 = source.MVAnalyse(isb = true, delta = 1, pel = 2, blksize=blksize, overlap=overlap, sharp=sharp, idx = idx)
forward_vec1 = source.MVAnalyse(isb = false, delta = 1, pel = 2, blksize=blksize, overlap=overlap, sharp=sharp, idx = idx)
forward_vec2 = source.MVAnalyse(isb = false, delta = 2, pel = 2, blksize=blksize, overlap=overlap, sharp=sharp, idx = idx)
source.MVDegrain2(backward_vec1, forward_vec1, backward_vec2, forward_vec2, thSAD=thSAD, idx=idx)
}

Normally I call this with:

MVDegrain2p(last, blksize = 16, sharp = 2)

as it's considerably faster than running blksize 8, and the "extra" cleaning power of 16 is acceptable to me. The motion artefacts are reduced with blksize 8 and lowered further with blksize 4 - but on balance I can live with what I get at 16.

For cleaning this high contrast noise I've been inspired by various discussions of "prefiltered" MVD2 using a "calm" clip. I've tried using FFT3DFilter/FFT3DGPU (various block sizes up to 128, bt 1, 3 and 5, sigma from 2 to 64 and various strengths of sigma2/3/4), then tried combining this with RemoveGrain(mode=17), luma only or luma+chroma, in various orderings and counts.

So, this is where I've got to:

function FizzKiller(clip source, int "blksize", int "overlap", int "sharp", int "thSAD")
{ # Motion compensated denoiser for progressive source clip with prefiltering optimised to reduce high-contrast noise
# Uses MVTools and RemoveGrain

blksize = default(blksize,8) # blksize value (4, 8 or 16)
overlap = default(overlap,blksize/2) # overlap value (0 to half blksize)
sharp = default(sharp,1) # 0=bilinear softest, 1=bicubic, 2=Wiener sharpest
thSAD = default(thSAD,400) # higher risks motion ghosting and swimming, lower risks blotchy denoising

idx = 20 # "safe" idx for use within this function only

# Prefilter the clip
calm = source.invert("Y").levels(0, 0.5, 255, 0, 255, coring = false)
calm = calm.removegrain(mode = 17)
calm = calm.removegrain(mode = 17)
calm = calm.removegrain(mode = 17)

backward_vec2 = calm.MVAnalyse(isb = true, delta = 2, pel = 2, blksize=blksize, overlap=overlap, sharp=sharp, idx = idx)
backward_vec1 = calm.MVAnalyse(isb = true, delta = 1, pel = 2, blksize=blksize, overlap=overlap, sharp=sharp, idx = idx)
forward_vec1 = calm.MVAnalyse(isb = false, delta = 1, pel = 2, blksize=blksize, overlap=overlap, sharp=sharp, idx = idx)
forward_vec2 = calm.MVAnalyse(isb = false, delta = 2, pel = 2, blksize=blksize, overlap=overlap, sharp=sharp, idx = idx)
source.MVDegrain2(backward_vec1, forward_vec1, backward_vec2, forward_vec2, thSAD = thSAD, idx = idx + 1)
}

I call it like so:

last.FizzKiller(blksize = 16, sharp = 2)

So, any ideas for tweaking this to improve it? Less motion artefacts would always be welcome (my CPU makes blksize 8 uncomfortable, halving encode rate)? Also perhaps it could be made a bit stronger on "flat" areas of the picture, but not at the cost of making the motion artefacts worse. I haven't finished playing with this, but I've been playing so long I thought I'd share the results so far.

I'd be interested in clips that cause problems. I've tested other clips and I have yet more clips to test with...

Jawed

Sagekilla
21st January 2008, 17:22
This sounds very much like the degraining filter (http://forum.doom9.org/showthread.php?p=1076276#post1076276) created by Didee that I turned into a function (http://forum.doom9.org/showpost.php?p=1078534&postcount=116). I'll test yours against mine, I'm curious as to how different the output is from the two methods. No doubt yours is much faster though, since Temporal Degrain calls FFT3DFilter and then MVDegrain2 twice. Will post results soon, see if there's anything I can do to help improve our functions.

Edit: By the way, try out mediafire next time :) Much better site to use for files.

Terranigma
21st January 2008, 17:27
Yes, but the function you came up with, doesn't fare well against hot pixels or Mosquito noise, so maybe you shouldn't be passing it off as a miracle function. :p

Sagekilla
21st January 2008, 17:35
Found a bug by the way. You have to change the source from clip "source", to clip source. No quotations for your source video! Here's the fixed function until you come around to fix it:

function FizzKiller(clip source, int "blksize", int "overlap", int "sharp", int "thSAD")
{ # Motion compensated denoiser for progressive source clip with prefiltering optimised to reduce high-contrast noise
# Uses MVTools and RemoveGrain

blksize = default(blksize,8) # blksize value (4, 8 or 16)
overlap = default(overlap,blksize/2) # overlap value (0 to half blksize)
sharp = default(sharp,1) # 0=bilinear softest, 1=bicubic, 2=Wiener sharpest
thSAD = default(thSAD,400) # higher risks motion ghosting and swimming, lower risks blotchy denoising

idx = 20 # "safe" idx for use within this function only

# Prefilter the clip
calm = source.invert("Y").levels(0, 0.5, 255, 0, 255, coring = false)
calm = calm.removegrain(mode = 17)
calm = calm.removegrain(mode = 17)
calm = calm.removegrain(mode = 17)

backward_vec2 = calm.MVAnalyse(isb = true, delta = 2, pel = 2, blksize=blksize, overlap=overlap, sharp=sharp, idx = idx)
backward_vec1 = calm.MVAnalyse(isb = true, delta = 1, pel = 2, blksize=blksize, overlap=overlap, sharp=sharp, idx = idx)
forward_vec1 = calm.MVAnalyse(isb = false, delta = 1, pel = 2, blksize=blksize, overlap=overlap, sharp=sharp, idx = idx)
forward_vec2 = calm.MVAnalyse(isb = false, delta = 2, pel = 2, blksize=blksize, overlap=overlap, sharp=sharp, idx = idx)
source.MVDegrain2(backward_vec1, forward_vec1, backward_vec2, forward_vec2, thSAD = thSAD, idx = idx + 1)
}

Terranigma
21st January 2008, 17:42
otoh, mc_spuds handles hot pixels very well, but only with frames above 2. But sometimes, frames 3 & 4 gives unpleasing results to the eyes. I wonder if spuds noticed anything. :(

Jawed
21st January 2008, 17:48
This sounds very much like the degraining filter (http://forum.doom9.org/showthread.php?p=1076276#post1076276) created by Didee that I turned into a function (http://forum.doom9.org/showpost.php?p=1078534&postcount=116). I'll test yours against mine, I'm curious as to how different the output is from the two methods.
I tried Didee's function, on the way...

No doubt yours is much faster though, since Temporal Degrain calls FFT3DFilter and then MVDegrain2 twice. Will post results soon, see if there's anything I can do to help improve our functions.
I've been ildly wondering if I should recode in mask tools, maybe that's faster.

Edit: By the way, try out mediafire next time :) Much better site to use for files.
The 100MB limit might be problematic for general usage (it has certainly tripped me up on other big files I've tried to share), though wouldn't have been an issue here. Spanned-RARs yeah, sure, but inertia's powerful.

Jawed

Morte66
21st January 2008, 17:56
[Jawed is an old friend of mine.]

What prompted you to use the inverted/levelled clip for motion analysis? What do you reckon to gain by it?

Jawed
21st January 2008, 17:58
Found a bug by the way. You have to change the source from clip "source", to clip source.
Sigh, forgot that the quotes make an argument "optional".

Thanks,
Jawed

Jawed
21st January 2008, 18:02
otoh, mc_spuds handles hot pixels very well, but only with frames above 2. But sometimes, frames 3 & 4 gives unpleasing results to the eyes. I wonder if spuds noticed anything. :(

I tried:

last.MC_Spuds(frames=2,strength=5)

but didn't go any higher as I don't have MVDegrain3 and decided that if MVD2 doesn't solve the problem, a longer temporal filter wouldn't be any better.

Jawed

Didée
21st January 2008, 18:05
Yes, but the function you came up with, doesn't fare well against hot pixels or Mosquito noise, so maybe you shouldn't be passing it off as a miracle function. :p
This function here isn't a miracle, either. Truth is, there're no big surprises to expect. Whether MVDegrain "bites" something or not depends mostly on the SAD of a compensated block. When using a pre-denoised search clip, consequently it depends mostly on the quality of the pre-denoising.

Here, the pre-denoising is done pure spatially by a RG(17) cascade. It works out quite OK for this source, but: like already told in Zep's thread, the caveat is that pure spatial pre-denoising leaves too much flicker in flat areas. That's why Jawed gets those "swimming" artefacts.

Also, the gamma-adjustment done for the search clip isn't a bad idea, but it's not well balanced. gamma=2.0 between 0->0 & 255->255 will map 16->64 ... one quarter of the available gammut isn't used anymore at all, and the bright parts lose information.

Jawed
21st January 2008, 18:08
[Jawed is an old friend of mine.]

What prompted you to use the inverted/levelled clip for motion analysis? What do you reckon to gain by it?
I played with various gamma changes to see what happens and reasoning that this noise is problematic due to contrast reasoned that an inverted gamma might do the trick better than just gamma.

Jawed

Terranigma
21st January 2008, 18:23
I tried:

last.MC_Spuds(frames=2,strength=5)

but didn't go any higher as I don't have MVDegrain3 and decided that if MVD2 doesn't solve the problem, a longer temporal filter wouldn't be any better.

Jawed

Well, if you don't have access to mvdegrain 3, like me, then instead use flow=true.
So like:
MC_Spuds(frames=3,strength=5,flow=true)

Here, the pre-denoising is done pure spatially by a RG(17) cascade. It works out quite OK for this source, but: like already told in Zep's thread, the caveat is that pure spatial pre-denoising leaves too much flicker in flat areas. That's why Jawed gets those "swimming" artefacts.
I've noticed that a few weeks ago. Since then, i've been using very weak pre-filtering settings. Just enough so that the remaining noise (if any) is tolerable.

Jawed
21st January 2008, 19:05
This function here isn't a miracle, either. Truth is, there're no big surprises to expect. Whether MVDegrain "bites" something or not depends mostly on the SAD of a compensated block.
I think the trick here is that I'm biasing the input data for MVAnalyse so that the SAD in MVDegrain doesn't cause the high contrast noise to be ignored. Though I admit this is justification after the fact.

When using a pre-denoised search clip, consequently it depends mostly on the quality of the pre-denoising.

Here, the pre-denoising is done pure spatially by a RG(17) cascade. It works out quite OK for this source, but: like already told in Zep's thread, the caveat is that pure spatial pre-denoising leaves too much flicker in flat areas. That's why Jawed gets those "swimming" artefacts.
I think I've got less motion artefacts with this function than all other variants of pre-filtered MVD2 I've tried. All forms of pre-filtering that I've tested cause an increase in motion artefacts, when compared with MVD2 used in the default fashion.

Also, the gamma-adjustment done for the search clip isn't a bad idea, but it's not well balanced. gamma=2.0 between 0->0 & 255->255 will map 16->64 ... one quarter of the available gammut isn't used anymore at all, and the bright parts lose information.
It looks like I've done gamma 2 but the two are not the same :eek: If I take out Invert("Y") and simply use:

levels(0, 2, 255, 0, 255, coring = false)

I get worse motion artefacts - the bitrate is notably lower, about 3.5% on the Smoke clip. Clearly I could tweak that "2" to be something else, after all the whole thing is empirical... it's just where I landed.

I started experimenting with a gamma change (I set about changing the image into linear space) because I'm always suspicious that image processing software handles gamma incorrectly:

http://www.4p8.com/eric.brasseur/gamma.html

The asymmetry between Invert("Y").gamma(0.5) and gamma(2) makes me suspicious. The error might be in Invert or Levels or it might be in MVTools. Or everywhere.

I've got more experimentation to do...

Jawed

Jawed
22nd January 2008, 06:54
It looks like I've done gamma 2 but the two are not the same
Where's that :oops: smiley?

I don't know why, but for some reason I convinced myself that Invert.gamma(0.5) is the same as gamma(2). ARGH. ARGH ARGH.

Apologies for that ruse :confused:

Jawed

Morte66
22nd January 2008, 18:28
don't have MVDegrain3 and decided that if MVD2 doesn't solve the problem, a longer temporal filter wouldn't be any betterWell, I donated my GBP 13.66 and got the beta of mvdegrain3. So, for your reference:

MVDegrain2 (http://www.mediafire.com/?9jnrmdv3xjm) 17.95fps
MVDegrain3 (http://www.mediafire.com/?bn2utwmim09) 13.66fps
FizzKiller (http://www.mediafire.com/?bjtmtm1j392) 16.91fps (replaces borked version (http://www.mediafire.com/?cvdgmnbbz4g))

The script:
SetMTMode(5)
DGDecode_mpeg2source("smoke.d2v",idct=4,info=3)
SetMTMode(2)

Tweak(bright=-16, cont=1.164, coring=false)

DeBlock_QED_mt2( quant1=20, quant2=24, aOff1=2, bOff1=4, aOff2=4, bOff2=8 )

#crop( 10, 80, -6, -80) #original, erroneous
crop( 10, 80, -6, -80, align=true)

#mvdegrain2p (source=last,blksize=16,overlap=8,sharp=2,thSAD=400,idx=1)
#mvdegrain3p (source=last,blksize=16,overlap=8,sharp=2,thSAD=400,idx=1)
FizzKiller (blksize=16,overlap=8,sharp=2,idx=1)

gradfunkmirror(1.2)

Jawed
23rd January 2008, 00:24
Well, I donated my GBP 13.66 and got the beta of mvdegrain3.

Thanks for posting those clips Joel, nice work. Prepare to SCREAM (hmm, maybe you've already started).

But first, comparing MVD2 and MVD3 I can discern a useful reduction in the strength of the noise in the sky at the beginning of the clip. The rest of the clip makes this comparison very difficult. MVD3 only seems to have very slightly worse motion artefacts, judging by the black line on the ambulance (frames 1352 and 1365) - I'd say ignorable, which is a relief. Hopefully FizzKiller will be quite happy working with MVD3...

Now, to that scream. FK is completely borked on frames 7, 72, 356+357, 435, 508, 610, 678, 713, 753, 818, 991, 1059, 1201, 1237, 1490. I can't reproduce that problem here. The error is very similar to errors I've encountered when doing silly extreme things to the calm clip. I can't find anything "special" about the corresponding frames in the source clip, or about the neighbouring pairs of frames on both sides.

I suppose it's either MVTools beta (did you use it for the FK encode?) or multi-threading. Or some horrid combination of the two. Or, erm...

For what it's worth, FK encodes to 34MB here, at ~2.8fps on my lickle A64 3500+, single core. This is for a slightly larger frame, 704x424 versus 704x416. Here's my script:

global MeGUI_darx = 2361
global MeGUI_dary = 1000
DGDecode_mpeg2source("Smoke.d2v")

crop(0,64,0,-64)

tweak(bright=-16, cont=1.164, coring=false)
DeBlock_QED_mt2( quant1=20, quant2=24, aOff1=2, bOff1=4, aOff2=4, bOff2=8 )
last.FizzKiller(blksize=16,sharp=2)

crop( 4, 10, -12, -14)

gradfunkmirror()


and x264 command line:

--crf 16 --ref 5 --mixed-refs --no-fast-pskip --bframes 8 --b-pyramid --b-rdo --bime --weightb --subme 6 --analyse p8x8,b8x8,i4x4,i8x8 --8x8dct --me umh --merange 32 --no-dct-decimate --aq-strength 0.3 --aq-sensitivity 5

Jawed

Morte66
23rd January 2008, 00:53
Now, to that scream. FK is completely borked on frames 7, 72, 356+357, 435, 508, 610, 678, 713, 753, 818, 991, 1059, 1201, 1237, 1490.

The flashgun effect?

I suppose it's either MVTools beta (did you use it for the FK encode?) or multi-threading. Or some horrid combination of the two. Or, erm...

Rebooted, ran it again, problem still there.

Switched to script with FizzKiller single-threaded, problem gone.

I may have mixed single and multi-threaded this afternoon during the smoke sequence. I dimly remember forgetting to switch it on at and "fixing" it some time. I've done a lot of clips today between mvd3 and variance AQ, they run together in my mind...

I've uploaded a new version, single-threaded, no flashgun. You can compare now.

I've been running multithreaded mvd3 since this afternoon on various stuff without problems. But not FizzKiller.

Just did multithreaded mvd3 and mvd2 again on the smoke clip, they work fine.

But FizzKiller seems to have a problem with MT, at least here it does. I note that it is a meaningfully different use of mvtools from mvdegrain2p/3p, e.g. it uses two values for idx instead of one, so one script might expose issues that the other doesn't.

I will work on this some more tomorrow, and try to either find where I screwed up or work up a useful bug report for somebody.

Jawed
23rd January 2008, 01:44
The flashgun effect?
Yeah. I was like WTF is that when I first watched the clip, I don't remember that happening.

I've uploaded a new version, single-threaded, no flashgun. You can compare now.
Cool, yeah, that looks fine.

But FizzKiller seems to have a problem with MT, at least here it does. I note that it is a meaningfully different use of mvtools from mvdegrain2p/3p, e.g. it uses two values for idx instead of one, so one script might expose issues that the other doesn't.
I suppose the obvious thing to try is someone else's known-good prefiltering MVD2 script. Or null FizzKiller by setting calm=source. Or edit a regular MVD2 script so that it uses two values of idx (or remove all assignments to idx so that each MV function gets a unique idx - though that seems extremely unlikely - actually these all seem unlikely).

I will work on this some more tomorrow, and try to either find where I screwed up or work up a useful bug report for somebody.
Are encodes made with, say, MVD2, identical if using MVTools released versus MVTools beta?

With single-threaded FK, see how frames 1395+1396 look, with gamma set to 0.05. That is much like the problem with MT-FK. The error simply looks like the same blocks (mostly bright ones) being used all over the place - on frame 1396 you can see this in action by changing blksize to 8 or 4.

You didn't scream - I guess that's your hardened beta tester skin coming into its own...

Jawed

Sagekilla
23rd January 2008, 16:35
It seems odd that FizzKiller is having issues with MT. The function I use, I always use SetMTMode(2) to (mostly) double the speed of the function. Only difference is you're using RemoveGrain as a prefilter -- perhaps that's what has issues with MT?

Morte66
23rd January 2008, 16:38
*sigh*
Is seems you're meant to add "align=true" to the crop command when using MT, or stuff sometimes goes wonky. It further seems that I never encountered anything that provoked the problem until I tried your FizzKiller script. It's all OK now.

Jawed
23rd January 2008, 18:17
*sigh*
Is seems you're meant to add "align=true" to the crop command when using MT, or stuff sometimes goes wonky. It further seems that I never encountered anything that provoked the problem until I tried your FizzKiller script. It's all OK now.
Woah, that was simpler but stranger than expected.

Seems there are performance implications to align=true, but not necessarily negative... Hmm, it appears I should benchtest this for myself, I could get a speed-up.

Jawed

ankurs
24th January 2008, 15:59
testing it atm , will let you know shortly >_<

Jawed
22nd August 2008, 21:24
Earlier in this thread I bemoaned an increased sensitivity to certain kinds of movement artefacts with FizzKiller - artefacts that are inherent to MVDegrain.

Well I've found a way to mitigate them, often substantially. It's boringly, brutishly, simple. Simply increase the frame dimensions :eek:

In the tests I've been doing I've multiplied the frame's width and height by 2, passed it to FK, then returned it back to its original dimensions.

What's interesting about this is that MVDegrain3, used conventionally, doesn't benefit from this hack - there is sometimes a small benefit but nothing like as strong as with FizzKiller. I don't really understand why...

So I'm now finding that 2x-scaled FK processed video has less movement artefacts than MVDegrain3 with or without this hack.

So far I've not found an appreciable difference in the quality of "grain" denoising rendered by FK alone or FK with this doubling hack. I've experimented with some very noisy stuff including cheaply made 1950s b+w footage. This footage has a huge amount of dirt, spots and general damage on each frame. The doubling hack definitely reduces the threshold for the size of noise that MVDegrain3 attacks. The result with this film is that more dirt spots survive.

The other downside is the processing time - particularly if you have, like me, just a single core CPU.

I could experiment with factors other than 2, but well, I have a suspicion you get into per-clip tweaking and I tend to find that boring.

For the sake of completeness, here's the current version of FizzKiller I'm using, renamed FizzKiller3 since it uses 3 prior and 3 following frames, i.e. MVDegrain3:

function FizzKiller3(clip source, int "blksize", int "overlap", int "sharp", int "thSAD")
{ # Motion compensated denoiser for progressive source clip with prefiltering optimised to reduce high-contrast noise
# Uses MVTools and RemoveGrain

blksize = default(blksize,8) # blksize value (4, 8 or 16)
overlap = default(overlap,blksize/2) # overlap value (0 to half blksize)
sharp = default(sharp,1) # 0=bilinear softest, 1=bicubic, 2=Wiener sharpest
thSAD = default(thSAD,400) # higher risks motion ghosting and swimming, lower risks blotchy denoising

idx = 20 # "safe" idx for use within this function only

# Prefilter the clip
calm = source.invert("Y").levels(0, 0.5, 255, 0, 255, coring = false)
calm = calm.removegrain(mode = 17)
calm = calm.removegrain(mode = 17)
calm = calm.removegrain(mode = 17)

backward_vec3 = calm.MVAnalyse(isb = true, delta = 3, pel = 2, blksize=blksize, overlap=overlap, sharp=sharp, idx = idx)
backward_vec2 = calm.MVAnalyse(isb = true, delta = 2, pel = 2, blksize=blksize, overlap=overlap, sharp=sharp, idx = idx)
backward_vec1 = calm.MVAnalyse(isb = true, delta = 1, pel = 2, blksize=blksize, overlap=overlap, sharp=sharp, idx = idx)
forward_vec1 = calm.MVAnalyse(isb = false, delta = 1, pel = 2, blksize=blksize, overlap=overlap, sharp=sharp, idx = idx)
forward_vec2 = calm.MVAnalyse(isb = false, delta = 2, pel = 2, blksize=blksize, overlap=overlap, sharp=sharp, idx = idx)
forward_vec3 = calm.MVAnalyse(isb = false, delta = 3, pel = 2, blksize=blksize, overlap=overlap, sharp=sharp, idx = idx)
source.MVDegrain3(backward_vec1, forward_vec1, backward_vec2, forward_vec2, backward_vec3, forward_vec3, thSAD = thSAD, idx = idx + 1)
}

I normally call it with blksize=16 and sharp=2. A smaller blksize usually worsens movement artefacts.

Jawed

Nightshiver
22nd August 2008, 22:55
Man, talk about gravedigging. Anyway, seems interesting.

Sagekilla
23rd August 2008, 03:58
Jawed, what you basically did was do a second level of subpixel refinement -- Normally with MVAnalyse(pel=1) you're working fullpel, and pel=2/4 enables halfpel refinement by virtue of doubling the width and height of the clip. I believe the only difference between 2 and 4 is that 4 uses a sharper refinement (i.e. lanczos instead of billinear)

But, onto the point.. Since you're introducing subpixel refinement for RG, it's going to clean up a bit better I believe. I don't know the exact theoretical basis for this, but almost always the increased resolution tends to help filters. Neat though, that you decided to go the step further like that. You might be better off only upsampling before removegrain works, then downsampling afterwards so MVAnalyse + MVDegrain isn't working with 4x the number of pixels.

Jawed
23rd August 2008, 11:55
Hmm, your comments on "subpixel refinement for RG" might explain why I was seeing no useful benefit for MVDegrain3 on its own.

In truth this was a brutish experiment - I didn't come to this by thinking about subpixels, I just thought "I wonder what happens if..."

So, I shall certainly try out your suggestion for just processing RG this way.

Jawed

Sagekilla
23rd August 2008, 15:49
I've just (informally) tested this myself, and it seems like the difference is as so:

MVDegrain3 : fast and sharp
RemoveGrain + MVDegrain3: slow and softest
Removegrain + MVDegrain3 + upsample: slowest and soft

Upsampling offers a softness between MVD and MVD + RG, but it's much slower.

I could be completely wrong, I've only tested this on one source because I had no others on hand.

Jawed
23rd August 2008, 18:31
I'm not sure if doubled-MVDegrain3-halved is softer than MVD3 alone - I haven't tried that. Using spline36resize I expect the difference is essentially down to the fact that doubled-MVD3-halved will not bite on larger-scaled detail - so it should look sharper if anything.

Bear in mind that FK doesn't apply RG to the image - it's used solely as part of generating the calm clip for MVAnalyse - it's much better at this than FFT3DFilter/FFT3DGPU. I rejected those in the development of FK.

My initial tests show that doubled-RG-halved-FK has slightly worse movement artefacts than doubled-FK-halved. But still better than FK alone.

Jawed

Jawed
23rd August 2008, 18:57
Hmm, reading MVTools page I notice two things:


MVAnalyse and MVDegrain3 can both make use of an upsampled calm clip - so I don't need to halve calm after I've generated it.

pelclip has been broken from 1.8.5 until 1.9.3. I guess this means I should upgrade from 1.9.2

So I suppose I should try that to see what happens. Presumably it'll be slower again though...

Also I notice that pel=4 is not a sharper version of pel=2, but is in fact quarter-pixel instead of half-pixel.

Jawed

Jawed
19th January 2011, 11:58
I'm posting my current version of FizzKiller:

function FizzKiller(clip source, int temporal, int "blksize", int "overlap", int "sharp", int "thSAD", bool "RefineMotion")
{ # Motion compensated denoiser for progressive source clip with prefiltering optimised to reduce high-contrast noise
# Uses MVTools2, ReduceFlicker and RemoveGrain/Repair

blksize = default(blksize,16) # blksize value (4, 8 or 16)
overlap = default(overlap,blksize/2) # overlap value (0 to half blksize)
sharp = default(sharp,2) # 0=bilinear softest, 1=bicubic, 2=Wiener sharpest
thSAD = default(thSAD,300) # higher risks motion ghosting and swimming, lower risks blotchy denoising
RefineMotion = default(RefineMotion,true) # true means MRecalculate will be used to improve motion vectors

halfblksize = blksize/2 # MRecalculate works with half block size
halfoverlap = overlap/2 # Halve the overlap to suit the halved block size
halfthSAD = thSAD/2 # MRecalculate uses a more strict thSAD, which defaults to 150 (half of function's default of 300)

dct=5

source = source.assumeframebased() # MSuper pel=2 is faster with this

# Prefilter the clip
calm = source.reduceflicker(strength=3,aggressive=true)
calm = calm.repair(source,mode=1)
calm = calm.levels(0, 2, 255, 0, 255, coring = false)
calm = calm.removegrain(mode = 17)
calm = calm.removegrain(mode = 17)
calm = calm.removegrain(mode = 17)

calm_super = calm.MSuper(pel=2, hpad=blksize, vpad=blksize, sharp=sharp)
source_super = source.MSuper(pel=2, hpad=blksize, vpad=blksize, sharp=sharp,levels=1)
recalculate = calm.MSuper(pel=2, hpad=blksize, vpad=blksize, sharp=sharp,levels=1)

backward_vec3 = MAnalyse(calm_super, blksize=blksize, isb = true, delta = 3, overlap=overlap, dct=dct)
backward_vec3 = RefineMotion ? MRecalculate(recalculate, backward_vec3, blksize=halfblksize, overlap=halfoverlap, thSAD=halfthSAD) : backward_vec3
backward_vec2 = MAnalyse(calm_super, blksize=blksize, isb = true, delta = 2, overlap=overlap, dct=dct)
backward_vec2 = RefineMotion ? MRecalculate(recalculate, backward_vec2, blksize=halfblksize, overlap=halfoverlap, thSAD=halfthSAD) : backward_vec2
backward_vec1 = MAnalyse(calm_super, blksize=blksize, isb = true, delta = 1, overlap=overlap, dct=dct)
backward_vec1 = RefineMotion ? MRecalculate(recalculate, backward_vec1, blksize=halfblksize, overlap=halfoverlap, thSAD=halfthSAD) : backward_vec1

forward_vec1 = MAnalyse(calm_super, blksize=blksize, isb = false, delta = 1, overlap=overlap, dct=dct)
forward_vec1 = RefineMotion ? MRecalculate(recalculate, forward_vec1, blksize=halfblksize, overlap=halfoverlap, thSAD=halfthSAD) : forward_vec1
forward_vec2 = MAnalyse(calm_super, blksize=blksize, isb = false, delta = 2, overlap=overlap, dct=dct)
forward_vec2 = RefineMotion ? MRecalculate(recalculate, forward_vec2, blksize=halfblksize, overlap=halfoverlap, thSAD=halfthSAD) : forward_vec2
forward_vec3 = MAnalyse(calm_super, blksize=blksize, isb = false, delta = 3, overlap=overlap, dct=dct)
forward_vec3 = RefineMotion ? MRecalculate(recalculate, forward_vec3, blksize=halfblksize, overlap=halfoverlap, thSAD=halfthSAD) : forward_vec3

temporal == 3 ? MDegrain3(source, source_super, backward_vec1, forward_vec1, backward_vec2, forward_vec2, backward_vec3, forward_vec3, thSAD=thSAD) : \
temporal == 2 ? MDegrain2(source, source_super, backward_vec1, forward_vec1, backward_vec2, forward_vec2, thSAD=thSAD) : \
MDegrain1(source, source_super, backward_vec1, forward_vec1, thSAD=thSAD)
}

Note the new arguments, "temporal" and "RefineMotion". Temporal is not optional and should be 1, 2 or 3.

I also have a variant, which does not use a gamma-change for the calm-clip. It also offers the option to post-filter the result, by blending some of the original image:

function Killer(clip source, int temporal, int "blksize", int "overlap", int "sharp", int "thSAD", bool "RefineMotion", bool "Full")
{ # Motion compensated denoiser for progressive source clip with prefiltering for strength and repair for de-artefacting
# Uses MVTools2, ReduceFlicker and RemoveGrain/Repair

blksize = default(blksize,16) # blksize value (4, 8 or 16)
overlap = default(overlap,blksize/2) # overlap value (0 to half blksize)
sharp = default(sharp,2) # 0=bilinear softest, 1=bicubic, 2=Wiener sharpest
thSAD = default(thSAD,300) # higher risks motion ghosting and swimming, lower risks blotchy denoising
RefineMotion = default(RefineMotion,true) # true means MRecalculate will be used to improve motion vectors
Full = default(Full, false) # full strength, false for repair at end

halfblksize = blksize/2 # MRecalculate works with half block size
halfoverlap = overlap/2 # Halve the overlap to suit the halved block size
halfthSAD = thSAD/2 # MRecalculate uses a more strict thSAD, which defaults to 150 (half of function's default of 300)

dct=5

source = source.assumeframebased() # MSuper pel=2 is faster with this

# Prefilter the clip
calm = source.reduceflicker(strength=3,aggressive=true)
calm = calm.repair(source,mode=1)
calm = calm.removegrain(mode = 17)
calm = calm.removegrain(mode = 17)
calm = calm.removegrain(mode = 17)

calm_super = calm.MSuper(pel=2, hpad=blksize, vpad=blksize, sharp=sharp)
source_super = source.MSuper(pel=2, hpad=blksize, vpad=blksize, sharp=sharp,levels=1)
recalculate = calm.MSuper(pel=2, hpad=blksize, vpad=blksize, sharp=sharp,levels=1)

backward_vec3 = MAnalyse(calm_super, blksize=blksize, isb = true, delta = 3, overlap=overlap, dct=dct)
backward_vec3 = RefineMotion ? MRecalculate(recalculate, backward_vec3, blksize=halfblksize, overlap=halfoverlap, thSAD=halfthSAD) : backward_vec3
backward_vec2 = MAnalyse(calm_super, blksize=blksize, isb = true, delta = 2, overlap=overlap, dct=dct)
backward_vec2 = RefineMotion ? MRecalculate(recalculate, backward_vec2, blksize=halfblksize, overlap=halfoverlap, thSAD=halfthSAD) : backward_vec2
backward_vec1 = MAnalyse(calm_super, blksize=blksize, isb = true, delta = 1, overlap=overlap, dct=dct)
backward_vec1 = RefineMotion ? MRecalculate(recalculate, backward_vec1, blksize=halfblksize, overlap=halfoverlap, thSAD=halfthSAD) : backward_vec1

forward_vec1 = MAnalyse(calm_super, blksize=blksize, isb = false, delta = 1, overlap=overlap, dct=dct)
forward_vec1 = RefineMotion ? MRecalculate(recalculate, forward_vec1, blksize=halfblksize, overlap=halfoverlap, thSAD=halfthSAD) : forward_vec1
forward_vec2 = MAnalyse(calm_super, blksize=blksize, isb = false, delta = 2, overlap=overlap, dct=dct)
forward_vec2 = RefineMotion ? MRecalculate(recalculate, forward_vec2, blksize=halfblksize, overlap=halfoverlap, thSAD=halfthSAD) : forward_vec2
forward_vec3 = MAnalyse(calm_super, blksize=blksize, isb = false, delta = 3, overlap=overlap, dct=dct)
forward_vec3 = RefineMotion ? MRecalculate(recalculate, forward_vec3, blksize=halfblksize, overlap=halfoverlap, thSAD=halfthSAD) : forward_vec3

temporal == 3 ? MDegrain3(source, source_super, backward_vec1, forward_vec1, backward_vec2, forward_vec2, backward_vec3, forward_vec3, thSAD=thSAD) : \
temporal == 2 ? MDegrain2(source, source_super, backward_vec1, forward_vec1, backward_vec2, forward_vec2, thSAD=thSAD) : \
MDegrain1(source, source_super, backward_vec1, forward_vec1, thSAD=thSAD)
full == false ? repair(source,mode=17): last
}

The parameter "Full" determines the output result. When true the unadulterated result from Killer will be output. When false Killer blends the original image with the filtered result. This parameter defaults to false.

Vitaliy Gorbatenko
19th January 2011, 12:35
repair with mode = 17 not for blend! It recover little detail from source video.

More advanced version of killer:


function Killer(clip source, int temporal, clip "ref", int "calm", int "blksize", int "overlap", int "sharp", int "thSAD", bool "oversharp", clip "sharpclip", bool "RefineMotion", int "limit", int "search", int "dct", bool "dark", int "pel")
{ # Motion compensated denoiser for progressive source clip with prefiltering for strength and repair for de-artefacting
# Uses MVTools2, ReduceFlicker and RemoveGrain/Repair

blksize = default(blksize,16) # blksize value (4, 8 or 16)
overlap = default(overlap,blksize/2) # overlap value (0 to half blksize)
sharp = default(sharp,2) # 0=bilinear softest, 1=bicubic, 2=Wiener sharpest
thSAD = default(thSAD,300) # higher risks motion ghosting and swimming, lower risks blotchy denoising
RefineMotion = default(RefineMotion,true) # true means MRecalculate will be used to improve motion vectors
limit = default(limit, 255) # Limits maximum change of a pixel. Default means no limit
ref = default(ref, source) # Reference (calmed) clip
presharpclip = source.KillerSharp() # Pre sharp if need
sharpclip = default(sharpclip, presharpclip) # sharp clip
calm = default(calm, 1) # use calm() for predenoising as default
dct = default(dct, 5) # SATD instead of SAD for brightness
search = default(search, 5) # Search Uneven multi hexagons (UMH), searchparam parameter determines the range. (like the x264).
oversharp = default(oversharp, true) # Pre sharp the clip
dark = default(dark, false) # mode for dark movies
pel = default(pel, 2) # Value can only be 1, 2 or 4. 1 means a precision to the pixel. 2 means a precision to half a pixel, 4 means a precision to quarter a pixel
halfblksize = blksize/2 # MRecalculate works with half block size
halfoverlap = overlap/2 # Halve the overlap to suit the halved block size
halfthSAD = thSAD/2 # MRecalculate uses a more strict thSAD, which defaults to 150 (half of function's default of 300)
#Also, the default values thSCD1=400, thSCD2=130 often do not reckognize some scenechanges,
#especially in dimmed/dark scenery. lowering to sth like 350,90 is more safe in reckognizing scenechanges ...
#however, the more you lower these values, the more there is danger that the detection
#erroneously triggers during bright/complex scenery with lots of motion.
thSCD1 = (dark==true) ? 350 : 400
thSCD2 = (dark==true) ? 90 : 130

#pel = (temporal>3) ? 1 : pel
source = source.assumeframebased() # MSuper pel=2 is faster with this

# Prefilter the clip
calm=(calm==1) ? ref.KillerCalm(): ref
# Presharp the clip
sharp0 = (oversharp==true) ? sharpclip : source

calm_super = calm.MSuper(pel=pel, hpad=blksize, vpad=blksize, sharp=sharp)
source_super = sharp0.MSuper(pel=pel, hpad=blksize, vpad=blksize, sharp=sharp,levels=1)
recalculate = calm.MSuper(pel=pel, hpad=blksize, vpad=blksize, sharp=sharp,levels=1)

bv9 = MAnalyse(calm_super, blksize=blksize, isb = true, delta = 9, overlap=overlap, dct=dct, search=search)
bv9 = RefineMotion ? MRecalculate(recalculate, bv9, blksize=halfblksize, overlap=halfoverlap, thSAD=halfthSAD, search=search) : bv9
bv8 = MAnalyse(calm_super, blksize=blksize, isb = true, delta = 8, overlap=overlap, dct=dct, search=search)
bv8 = RefineMotion ? MRecalculate(recalculate, bv8, blksize=halfblksize, overlap=halfoverlap, thSAD=halfthSAD, search=search) : bv8
bv7 = MAnalyse(calm_super, blksize=blksize, isb = true, delta = 7, overlap=overlap, dct=dct, search=search)
bv7 = RefineMotion ? MRecalculate(recalculate, bv7, blksize=halfblksize, overlap=halfoverlap, thSAD=halfthSAD, search=search) : bv7
bv6 = MAnalyse(calm_super, blksize=blksize, isb = true, delta = 6, overlap=overlap, dct=dct, search=search)
bv6 = RefineMotion ? MRecalculate(recalculate, bv6, blksize=halfblksize, overlap=halfoverlap, thSAD=halfthSAD, search=search) : bv6
bv5 = MAnalyse(calm_super, blksize=blksize, isb = true, delta = 5, overlap=overlap, dct=dct, search=search)
bv5 = RefineMotion ? MRecalculate(recalculate, bv5, blksize=halfblksize, overlap=halfoverlap, thSAD=halfthSAD, search=search) : bv5
bv4 = MAnalyse(calm_super, blksize=blksize, isb = true, delta = 4, overlap=overlap, dct=dct, search=search)
bv4 = RefineMotion ? MRecalculate(recalculate, bv4, blksize=halfblksize, overlap=halfoverlap, thSAD=halfthSAD, search=search) : bv4
bv3 = MAnalyse(calm_super, blksize=blksize, isb = true, delta = 3, overlap=overlap, dct=dct, search=search)
bv3 = RefineMotion ? MRecalculate(recalculate, bv3, blksize=halfblksize, overlap=halfoverlap, thSAD=halfthSAD, search=search) : bv3
bv2 = MAnalyse(calm_super, blksize=blksize, isb = true, delta = 2, overlap=overlap, dct=dct, search=search)
bv2 = RefineMotion ? MRecalculate(recalculate, bv2, blksize=halfblksize, overlap=halfoverlap, thSAD=halfthSAD, search=search) : bv2
bv1 = MAnalyse(calm_super, blksize=blksize, isb = true, delta = 1, overlap=overlap, dct=dct, search=search)
bv1 = RefineMotion ? MRecalculate(recalculate, bv1, blksize=halfblksize, overlap=halfoverlap, thSAD=halfthSAD, search=search) : bv1

fv1 = MAnalyse(calm_super, blksize=blksize, isb = false, delta = 1, overlap=overlap, dct=dct, search=search)
fv1 = RefineMotion ? MRecalculate(recalculate, fv1, blksize=halfblksize, overlap=halfoverlap, thSAD=halfthSAD, search=search) : fv1
fv2 = MAnalyse(calm_super, blksize=blksize, isb = false, delta = 2, overlap=overlap, dct=dct, search=search)
fv2 = RefineMotion ? MRecalculate(recalculate, fv2, blksize=halfblksize, overlap=halfoverlap, thSAD=halfthSAD, search=search) : fv2
fv3 = MAnalyse(calm_super, blksize=blksize, isb = false, delta = 3, overlap=overlap, dct=dct, search=search)
fv3 = RefineMotion ? MRecalculate(recalculate, fv3, blksize=halfblksize, overlap=halfoverlap, thSAD=halfthSAD, search=search) : fv3
fv4 = MAnalyse(calm_super, blksize=blksize, isb = false, delta = 4, overlap=overlap, dct=dct, search=search)
fv4 = RefineMotion ? MRecalculate(recalculate, fv4, blksize=halfblksize, overlap=halfoverlap, thSAD=halfthSAD, search=search) : fv4
fv5 = MAnalyse(calm_super, blksize=blksize, isb = false, delta = 5, overlap=overlap, dct=dct, search=search)
fv5 = RefineMotion ? MRecalculate(recalculate, fv5, blksize=halfblksize, overlap=halfoverlap, thSAD=halfthSAD, search=search) : fv5
fv6 = MAnalyse(calm_super, blksize=blksize, isb = false, delta = 6, overlap=overlap, dct=dct, search=search)
fv6 = RefineMotion ? MRecalculate(recalculate, fv6, blksize=halfblksize, overlap=halfoverlap, thSAD=halfthSAD, search=search) : fv6
fv7 = MAnalyse(calm_super, blksize=blksize, isb = false, delta = 7, overlap=overlap, dct=dct, search=search)
fv7 = RefineMotion ? MRecalculate(recalculate, fv7, blksize=halfblksize, overlap=halfoverlap, thSAD=halfthSAD, search=search) : fv7
fv8 = MAnalyse(calm_super, blksize=blksize, isb = false, delta = 8, overlap=overlap, dct=dct, search=search)
fv8 = RefineMotion ? MRecalculate(recalculate, fv8, blksize=halfblksize, overlap=halfoverlap, thSAD=halfthSAD, search=search) : fv8
fv9 = MAnalyse(calm_super, blksize=blksize, isb = false, delta = 9, overlap=overlap, dct=dct, search=search)
fv9 = RefineMotion ? MRecalculate(recalculate, fv9, blksize=halfblksize, overlap=halfoverlap, thSAD=halfthSAD, search=search) : fv9

temporal == 9 ? Eval("""
dg123 = source.MDegrain3(source_super, bv1,fv1,bv2,fv2,bv3,fv3, thSAD=thSAD+thSAD/10, limit=limit, thSCD1=thSCD1, thSCD2=thSCD2)
dg456 = source.MDegrain3(source_super, bv4,fv4,bv5,fv5,bv6,fv6, thSAD=thSAD, limit=limit, thSCD1=thSCD1, thSCD2=thSCD2)
dg789 = source.MDegrain3(source_super, bv7,fv7,bv8,fv8,bv9,fv9, thSAD=thSAD-thSAD/10, limit=limit, thSCD1=thSCD1, thSCD2=thSCD2)
#
# Average( dg123, 1./3., dg456, 1./3., dg789, 1./3.)
#OR
# merge(dg456,dg789).merge(dg123,0.3334)
#OR
Mt_lutxyz(dg123, dg456, dg789,"x 3 / y 3 / + z 3 / +")
#
""") :\
temporal == 5 ? Eval("""
mdg3 = source.MDegrain3(source_super,bv1,fv1,bv2,fv2,bv3,fv3,thSAD=thSAD, limit=limit, thSCD1=thSCD1, thSCD2=thSCD2)
mdg3.MDegrain2(source_super,bv4,fv4,bv5,fv5,thSAD=thSAD, limit=limit, thSCD1=thSCD1, thSCD2=thSCD2)
\ .Merge(mdg3, 0.455) # correct weightings (hopefully)
""") :\
temporal == 3 ? MDegrain3(source, source_super, bv1, fv1, bv2, fv2, bv3, fv3, thSAD=thSAD, limit=limit, thSCD1=thSCD1, thSCD2=thSCD2) :\
temporal == 2 ? MDegrain2(source, source_super, bv1, fv1, bv2, fv2, thSAD=thSAD, limit=limit, thSCD1=thSCD1, thSCD2=thSCD2) :\
MDegrain1(source, source_super, bv1, fv1, thSAD=thSAD, limit=limit, thSCD1=thSCD1, thSCD2=thSCD2)

repair(sharp0,mode=17)
}

function KillerCalm (clip ref)
{
calm = ref.reduceflicker(strength=3,aggressive=true)
calm = calm.repair(ref,mode=1)
calm = calm.removegrain(mode = 17)
calm = calm.removegrain(mode = 17)
calm = calm.removegrain(mode = 17)
return calm
}

function KillerSharp (clip ref)
{
return ref.Seesaw(nrlimit=0, nrlimit2=0, sstr=2.0, sootheT=0, sootheS=0, Slimit=50, Sdamplo=29, Spower=1, SdampHi=35).mergechroma(ref)
#return ref.Seesaw(nrlimit=0, nrlimit2=99, bias=49, sstr=1.24, Spower=3, Szp=12, Sdamplo=4, SdampHi=19, Slimit=99, sootheT=0, sootheS=0)
}


I'm use like this:
sharpCLIP=limited_toon(level=32).lite_dark()
Killer(3, calm=0, sharpclip=sharpCLIP)

Jawed
19th January 2011, 13:19
Ooh, very interesting to see someone else's ideas. Temporal =9 :eek: :cool:

You appear to be not using the calming concept with the sample call you gave (calm=0).

I normally have Full=true, i.e. I don't use the repair option.

I actually prefer to use FizzKiller and use Killer only when noise in dark areas is the biggest problem. I should really make FizzKiller's gamma-change to the calm clip an option, but I'm a bit lazy.

I normally adjust thSAD, typically 50-200 and I prefer temporal=3.

Mug Funky
20th January 2011, 03:58
this looks nice.

i've tinkered with a lot of Mtools based denoising, always looking to find something that tackles high contrast noise well.

i missed this thread in 2008 i think... but i find it interesting that the noise-in-the-whites that you refer to is indeed colloquially referred to as "fizz", and was the bane of my existence when using a very old telecine that could only run properly in low beam (ie. less light hitting the pickup = more grain).

the noisy white comes not from the film itself, but the machine that did the scanning - white pictures means black negative, and black neg can be quite hard to see through, meaning it hits the noise floor of the sensor used to look through it.

i like the idea of gamma correcting the mv search clip a lot. however, each shot in a film is likely to have different gamma settings depending on how badly shot it was or limitations in certain scenes. changing the gamma of course biases the noise either to the blacks or the whites. on good-looking film or digitally shot stuff, you'll mostly get noise in the blacks. in some cases you'll get the opposite (like here).

what would be good is some kind of decision that adapts the gamma correction to distribute the noise evenly across the luma range. it'd need to be scenechange aware and adaptive.

perhaps if a heavy spatial soften were subtracted from the source, then the result was split into two clips - an "upper noise" and a "lower noise", which are then analysed (averageluma?) and the results used to derive a meaningful gamma value that makes them equal. some experimenting or just inspired maths would be required to figure out how to derive a gamma value from this that reliably equalises the two noise metrics.

i'm not sure how useful this would end up being, but it's something worth thinking about.

Vitaliy Gorbatenko
20th January 2011, 06:02
Calming concept gives ghost on dark areas. I'm don't like this. Temporal modes 5 and 9 useful sometimes, but very slow. In my version main concept set on Motion Compensated Sharpening with denoising. The results is pretty good!

tormento
20th January 2011, 07:47
More advanced version of killer:
Why don't you put any pel control in switches? Sometimes I find that pel=1 is enough and improves speed a lot.

Vitaliy Gorbatenko
20th January 2011, 08:31
tormento:
Done. See above.

tormento
20th January 2011, 18:33
tormento:
Done. See above.
Thank you.

P.S: It's a pity ReduceFlicker won't exist in x64 world. It's the only plugin that prevents some speed enhancement.

Didée
20th January 2011, 19:51
You could exchange ReduceFlicker with FluxSmoothT. Flux is available in x64, is (apparently) better suited for grain calming than ReduceFlicker, while having less artifacts on motion.

tormento
23rd January 2011, 09:45
You could exchange ReduceFlicker with FluxSmoothT. Flux is available in x64, is (apparently) better suited for grain calming than ReduceFlicker, while having less artifacts on motion.
I remember your last x64 answer in another thread, I don't dare to ask you to port Vitaliy's script to x64 world even if I'd like to...

Vitaliy Gorbatenko
23rd January 2011, 11:40
If you want use FluxSmoothT a'm think it will be like this:

function Killer(clip source, int temporal, clip "ref", int "calm", int "blksize", int "overlap", int "sharp", int "thSAD"\
, bool "oversharp", clip "sharpclip", bool "RefineMotion", int "limit", int "search", int "dct", bool "dark"\
, int "pel", int "flux", bool "gamma", bool "Full")
{ # Motion compensated denoiser for progressive source clip with prefiltering for strength and repair for de-artefacting
# Uses MVTools2, ReduceFlicker and RemoveGrain/Repair

blksize = default(blksize,16) # blksize value (4, 8 or 16)
overlap = default(overlap,blksize/2) # overlap value (0 to half blksize)
sharp = default(sharp,2) # 0=bilinear softest, 1=bicubic, 2=Wiener sharpest
thSAD = default(thSAD,300) # higher risks motion ghosting and swimming, lower risks blotchy denoising
RefineMotion = default(RefineMotion,true) # true means MRecalculate will be used to improve motion vectors
limit = default(limit, 255) # Limits maximum change of a pixel. Default means no limit
ref = default(ref, source) # Reference (calmed) clip
presharpclip = source.KillerSharp() # Pre sharp if need
sharpclip = default(sharpclip, presharpclip) # sharp clip
calm = default(calm, 1) # use calm() for predenoising as default
dct = default(dct, 5) # SATD instead of SAD for brightness
search = default(search, 5) # Search Uneven multi hexagons (UMH), searchparam parameter determines the range. (like the x264).
oversharp = default(oversharp, true) # Pre sharp the clip
dark = default(dark, false) # mode for dark movies
pel = default(pel, 2) # Value can only be 1, 2 or 4. 1 means a precision to the pixel. 2 means a precision to half a pixel, 4 means a precision to quarter a pixel
flux = default(flux, 5) # default calm mode for flux for killer
gamma = default(gamma, false) # mode for dark movies
Full = default(Full, false) # full strength, false for repair at end
halfblksize = blksize/2 # MRecalculate works with half block size
halfoverlap = overlap/2 # Halve the overlap to suit the halved block size
halfthSAD = thSAD/2 # MRecalculate uses a more strict thSAD, which defaults to 150 (half of function's default of 300)
#Also, the default values thSCD1=400, thSCD2=130 often do not reckognize some scenechanges,
#especially in dimmed/dark scenery. lowering to sth like 350,90 is more safe in reckognizing scenechanges ...
#however, the more you lower these values, the more there is danger that the detection
#erroneously triggers during bright/complex scenery with lots of motion.
thSCD1 = (dark==true) ? 350 : 400
thSCD2 = (dark==true) ? 90 : 130

pel = (temporal>3) ? 1 : pel
source = source.assumeframebased() # MSuper pel=2 is faster with this

# Prefilter the clip
calm=(calm==1) ? ref.KillerCalm(flux=flux, gamma=gamma): ref
# Presharp the clip
sharp0 = (oversharp==true) ? sharpclip : source

calm_super = calm.MSuper(pel=pel, hpad=blksize, vpad=blksize, sharp=sharp)
source_super = sharp0.MSuper(pel=pel, hpad=blksize, vpad=blksize, sharp=sharp,levels=1)
recalculate = calm.MSuper(pel=pel, hpad=blksize, vpad=blksize, sharp=sharp,levels=1)

bv9 = MAnalyse(calm_super, blksize=blksize, isb = true, delta = 9, overlap=overlap, dct=dct, search=search)
bv9 = RefineMotion ? MRecalculate(recalculate, bv9, blksize=halfblksize, overlap=halfoverlap, thSAD=halfthSAD, search=search) : bv9
bv8 = MAnalyse(calm_super, blksize=blksize, isb = true, delta = 8, overlap=overlap, dct=dct, search=search)
bv8 = RefineMotion ? MRecalculate(recalculate, bv8, blksize=halfblksize, overlap=halfoverlap, thSAD=halfthSAD, search=search) : bv8
bv7 = MAnalyse(calm_super, blksize=blksize, isb = true, delta = 7, overlap=overlap, dct=dct, search=search)
bv7 = RefineMotion ? MRecalculate(recalculate, bv7, blksize=halfblksize, overlap=halfoverlap, thSAD=halfthSAD, search=search) : bv7
bv6 = MAnalyse(calm_super, blksize=blksize, isb = true, delta = 6, overlap=overlap, dct=dct, search=search)
bv6 = RefineMotion ? MRecalculate(recalculate, bv6, blksize=halfblksize, overlap=halfoverlap, thSAD=halfthSAD, search=search) : bv6
bv5 = MAnalyse(calm_super, blksize=blksize, isb = true, delta = 5, overlap=overlap, dct=dct, search=search)
bv5 = RefineMotion ? MRecalculate(recalculate, bv5, blksize=halfblksize, overlap=halfoverlap, thSAD=halfthSAD, search=search) : bv5
bv4 = MAnalyse(calm_super, blksize=blksize, isb = true, delta = 4, overlap=overlap, dct=dct, search=search)
bv4 = RefineMotion ? MRecalculate(recalculate, bv4, blksize=halfblksize, overlap=halfoverlap, thSAD=halfthSAD, search=search) : bv4
bv3 = MAnalyse(calm_super, blksize=blksize, isb = true, delta = 3, overlap=overlap, dct=dct, search=search)
bv3 = RefineMotion ? MRecalculate(recalculate, bv3, blksize=halfblksize, overlap=halfoverlap, thSAD=halfthSAD, search=search) : bv3
bv2 = MAnalyse(calm_super, blksize=blksize, isb = true, delta = 2, overlap=overlap, dct=dct, search=search)
bv2 = RefineMotion ? MRecalculate(recalculate, bv2, blksize=halfblksize, overlap=halfoverlap, thSAD=halfthSAD, search=search) : bv2
bv1 = MAnalyse(calm_super, blksize=blksize, isb = true, delta = 1, overlap=overlap, dct=dct, search=search)
bv1 = RefineMotion ? MRecalculate(recalculate, bv1, blksize=halfblksize, overlap=halfoverlap, thSAD=halfthSAD, search=search) : bv1

fv1 = MAnalyse(calm_super, blksize=blksize, isb = false, delta = 1, overlap=overlap, dct=dct, search=search)
fv1 = RefineMotion ? MRecalculate(recalculate, fv1, blksize=halfblksize, overlap=halfoverlap, thSAD=halfthSAD, search=search) : fv1
fv2 = MAnalyse(calm_super, blksize=blksize, isb = false, delta = 2, overlap=overlap, dct=dct, search=search)
fv2 = RefineMotion ? MRecalculate(recalculate, fv2, blksize=halfblksize, overlap=halfoverlap, thSAD=halfthSAD, search=search) : fv2
fv3 = MAnalyse(calm_super, blksize=blksize, isb = false, delta = 3, overlap=overlap, dct=dct, search=search)
fv3 = RefineMotion ? MRecalculate(recalculate, fv3, blksize=halfblksize, overlap=halfoverlap, thSAD=halfthSAD, search=search) : fv3
fv4 = MAnalyse(calm_super, blksize=blksize, isb = false, delta = 4, overlap=overlap, dct=dct, search=search)
fv4 = RefineMotion ? MRecalculate(recalculate, fv4, blksize=halfblksize, overlap=halfoverlap, thSAD=halfthSAD, search=search) : fv4
fv5 = MAnalyse(calm_super, blksize=blksize, isb = false, delta = 5, overlap=overlap, dct=dct, search=search)
fv5 = RefineMotion ? MRecalculate(recalculate, fv5, blksize=halfblksize, overlap=halfoverlap, thSAD=halfthSAD, search=search) : fv5
fv6 = MAnalyse(calm_super, blksize=blksize, isb = false, delta = 6, overlap=overlap, dct=dct, search=search)
fv6 = RefineMotion ? MRecalculate(recalculate, fv6, blksize=halfblksize, overlap=halfoverlap, thSAD=halfthSAD, search=search) : fv6
fv7 = MAnalyse(calm_super, blksize=blksize, isb = false, delta = 7, overlap=overlap, dct=dct, search=search)
fv7 = RefineMotion ? MRecalculate(recalculate, fv7, blksize=halfblksize, overlap=halfoverlap, thSAD=halfthSAD, search=search) : fv7
fv8 = MAnalyse(calm_super, blksize=blksize, isb = false, delta = 8, overlap=overlap, dct=dct, search=search)
fv8 = RefineMotion ? MRecalculate(recalculate, fv8, blksize=halfblksize, overlap=halfoverlap, thSAD=halfthSAD, search=search) : fv8
fv9 = MAnalyse(calm_super, blksize=blksize, isb = false, delta = 9, overlap=overlap, dct=dct, search=search)
fv9 = RefineMotion ? MRecalculate(recalculate, fv9, blksize=halfblksize, overlap=halfoverlap, thSAD=halfthSAD, search=search) : fv9

temporal == 9 ? Eval("""
dg123 = source.MDegrain3(source_super, bv1,fv1,bv2,fv2,bv3,fv3, thSAD=thSAD+thSAD/10, limit=limit, thSCD1=thSCD1, thSCD2=thSCD2)
dg456 = source.MDegrain3(source_super, bv4,fv4,bv5,fv5,bv6,fv6, thSAD=thSAD, limit=limit, thSCD1=thSCD1, thSCD2=thSCD2)
dg789 = source.MDegrain3(source_super, bv7,fv7,bv8,fv8,bv9,fv9, thSAD=thSAD-thSAD/10, limit=limit, thSCD1=thSCD1, thSCD2=thSCD2)
#
# Average( dg123, 1./3., dg456, 1./3., dg789, 1./3.)
#OR
# merge(dg456,dg789).merge(dg123,0.3334)
#OR
Mt_lutxyz(dg123, dg456, dg789,"x 3 / y 3 / + z 3 / +")
#
""") :\
temporal == 5 ? Eval("""
mdg3 = source.MDegrain3(source_super,bv1,fv1,bv2,fv2,bv3,fv3,thSAD=thSAD, limit=limit, thSCD1=thSCD1, thSCD2=thSCD2)
mdg3.MDegrain2(source_super,bv4,fv4,bv5,fv5,thSAD=thSAD, limit=limit, thSCD1=thSCD1, thSCD2=thSCD2)
\ .Merge(mdg3, 0.455) # correct weightings (hopefully)
""") :\
temporal == 3 ? MDegrain3(source, source_super, bv1, fv1, bv2, fv2, bv3, fv3, thSAD=thSAD, limit=limit, thSCD1=thSCD1, thSCD2=thSCD2) :\
temporal == 2 ? MDegrain2(source, source_super, bv1, fv1, bv2, fv2, thSAD=thSAD, limit=limit, thSCD1=thSCD1, thSCD2=thSCD2) :\
MDegrain1(source, source_super, bv1, fv1, thSAD=thSAD, limit=limit, thSCD1=thSCD1, thSCD2=thSCD2)

Full == false ? repair(sharp0,mode=17) : last
}

function KillerCalm (clip ref, int "flux", bool "gamma")
{
flux = default(flux, 7) # calm mode
gamma = default(gamma, false) # mode for dark movies
calm = (flux >= 0) ? ref.FluxSmoothT(temporal_threshold=flux) : ref.reduceflicker(strength=3,aggressive=true)
calm = calm.repair(ref,mode=1)
calm = (gamma == true) ? calm.levels(0, 2, 255, 0, 255, coring = false) : calm
calm = calm.removegrain(mode = 17)
calm = calm.removegrain(mode = 17)
calm = calm.removegrain(mode = 17)
return calm
}

function KillerSharp (clip ref)
{
return ref.Seesaw(nrlimit=0, nrlimit2=0, sstr=2.0, sootheT=0, sootheS=0, Slimit=50, Sdamplo=29, Spower=1, SdampHi=35).mergechroma(ref)
#return ref.Seesaw(nrlimit=0, nrlimit2=99, bias=49, sstr=1.24, Spower=3, Szp=12, Sdamplo=4, SdampHi=19, Slimit=99, sootheT=0, sootheS=0)
}

Didée
23rd January 2011, 11:56
Seems good, but one suggestion:

FluxsSmoothT(temporal_threshold=3)

A thresh of 3 is really, really small. This will be ineffective on lots of noisy/grainy sources.

What about making the "flux" parameter an integer?

flux = -1 => deaktivate FluxSmooth, use reduceflicker for calming
flux = 0...255 => use FluxSmoothT with temporal_threshold=flux

Vitaliy Gorbatenko
23rd January 2011, 14:39
Good idea! I'm try to do this now!
Done! temporal_threshold default now is 5.

egrimisu
23rd January 2011, 15:08
some screens? (before and after)

SilaSurfer
23rd January 2011, 15:27
Didée gave me this script not long ago:


function Flux5framesT(clip c, int "th", int "thC") {
th = default(th,7)
thC = default(thC,th)
med = c.TMedian2()
avg = c.temporalsoften(2,th,thC,24,2)
medD = mt_makediff(c,med,U=3,V=3)
avgD = mt_makediff(c,avg,U=3,V=3)
DD = mt_lutxy(medD,avgD,"x 128 - y 128 - * 0 < 128 x 128 - abs y 128 - abs < x y ? ?",U=3,V=3)
c.mt_makediff(DD,U=3,V=3)
}

Very good for removing LF flicker.

Also try it like this:

a = last
b = a.RemoveGrain(11)
f = b.Flux5framesT.merge(b,0.49)

a.mt_makediff(mt_makediff(b,f,U=3,V=3),U=3,V=3)

If there is any LF flicker left in your video apply come filtering with FFt3DFilter in bt5 mode and with tweaking sigma1/2/3/4 values, sigma4 meaning the lowest frequency.:)

Vitaliy Gorbatenko
24th January 2011, 09:22
You can replace Calm function with anything you like.
Or use follow:

function Flux5framesT(clip c, int "th", int "thC") {
th = default(th,7)
thC = default(thC,th)
med = c.TMedian2()
avg = c.temporalsoften(2,th,thC,24,2)
medD = mt_makediff(c,med,U=3,V=3)
avgD = mt_makediff(c,avg,U=3,V=3)
DD = mt_lutxy(medD,avgD,"x 128 - y 128 - * 0 < 128 x 128 - abs y 128 - abs < x y ? ?",U=3,V=3)
c.mt_makediff(DD,U=3,V=3)
}


a = last
b = a.RemoveGrain(11)
f = b.Flux5framesT.merge(b,0.49)
ref = a.mt_makediff(mt_makediff(b,f,U=3,V=3),U=3,V=3)

Killer(3, calm=0, ref=ref, oversharp=false)

this set external calm mode =D

Note: if you have a ghosts in calm clip http://forum.doom9.org/showthread.php?p=1472648#post1472648 you will have it in result too.