Wednesday, January 1, 2020

Two different solutions for Denoising video with AVISynth

Last year, I worked on a couple of projects that involved not just deinterlacing, but denoising. Some scenes had a fine, natural noise that I preferred to keep intact. Other scenes had so much noise it was difficult to make out the faces of the actors. Figuring out how to minimize the noise without sacrificing too much detail or looking jarringly different from the surrounding shots turned out to be a real challenge.

Here are some notes from my experimenting.

Commercial tools


There are some commercial tools for denoising, each with their own strengths and weaknesses. Here's three of the most common:

I've used Neat Video in the past, and it has some real advantages in terms of denoising strength, being able to manually select what to consider noise (if there's a large enough area of just noise in frame), and parameters to tweak. In particular, it's great at dealing with blotchy chroma (color) noise. It's really easy to overdo it, however, and sometimes it takes a lot of tweaking to remove only the noise you want.

There's also Red Giant's Denoiser III, which has a much simpler interface, and is better for even noise reduction, although not as great for dealing with truly terrible noise.

Then there's the Studio version of DaVinci Resolve. I don't own this and can't get a trial version, so I don't know how well it would perform.

If you're looking to denoise HD or higher resolution footage, I'd recommend one of the above. They're all fully GPU accelerated, focus on modern camera sensor noise, and they don't have a chance of introducing gamma or color shifts.

However, for SD interlaced video, I think AVISynth has some better options.

TemporalDegrain2


Let's start with TemporalDegrain2. With low to medium grain/noise, the default settings are usually fine. It uses many of the same requirement filters as QTGMC, so if you've already got QTGMC set up, you should be able to use TemporalDegrain2 at defaults. The one setting to pay attention to is grainLevel=True. Setting this to False may give better performance on some footage, so try it both ways to check.

Here's a brightened capture of some noisy SD footage (Note that I haven't done a pixel aspect ratio correction, so the image is slightly squashed):


And here's the same footage passed through TemporalDegrain2 at grainLevel=False:


And grainLevel=True:



The difference is subtle in this case, but definitely there.

More detailed instructions are included in the .avsi script if you want to play around with things, but in my experience, the defaults do the best job at denoising without undesirable artifacts.

Oh, and one more important thing: TemporalDegrain2 has a 64-bit version for AVISynth.

SMDegrain


Next, let's look at SMDegrain. If you thought QTGMC has a lot of options...

Using the recommendations for a starting point from the documentation:

SMDegrain(tr=2,thSAD=250,contrasharp=true,refinemotion=true,lsb=true)
Gives this result:


Not very impressive in this case. Let's try the recommendation for "dark scenes", and trigger the interlaced switch so the denoising can be done prior to calling QTGMC:

 SMDegrain(tr=3,thSAD=300,contrasharp=true,str=2.0,refinemotion=true,lsb=true,interlaced=true)
Which gives us this:


Better, but still not great. Again, this is a fairly noisy clip. Depending on the type/amount of noise, the defaults might be much more effective.

Now, let's look at the option for "grainy sources". I like to call this the "kitchen sink" option:

pre=fluxsmootht(3).removegrain(11)
SMDegrain(tr=6,thSAD=500,contrasharp=30,prefilter=pre,str=1.2,refinemotion=true,lsb=true)
What's happening here is that in addition to increasing the strength of denoising, we've added a prefilter. This is designed to blur the image first when calculating what detail to preserve, so the denoise can be that much more aggressive. If you just use a number here, SMDegrain will do some variation of a simple blur for it's initial calculations. In this case, we're using more intensive solutions. Here's the end result:


Now we're talking. However, you should know some things about the Kitchen Sink method:
  • If you use the interlaced option it'll distort, so don't do that. You could use SeparateFields() first and AssumeFieldBased() plus Weave() after to try to preserve the interlacing while reducing the amount of time it takes to denoise, but in my experience, that makes the processing look crappier/lower resolution. Either use it after QTGMC, or use it before and accept that the motion may not always look quite right.
  • You can do multiple passes, but since SMDegrain is 32-bit only, you're realistically limited to 2 passes without render freezes/crashes due to memory issues.
  • Yes, SMDegrain is available in 64-bit for VapourSynth (via havsfunc, which also contains a port of QTGMC). There, you're more likely to be able to squeeze in 3 passes, but it'll be excruciatingly slow if you do that, and can end up overprocessing the image. Also, you'll need to pay attention to the documentation for the various options, as they sometimes use different capitalization than on AVISynth. Why? Basically, VapourSynth scripts use Python, and Python is case-sensitive. Also, you have to re-write the fluxsmootht and removegrain entries in a different way, and remove the lsb=true option (because VapourSynth doesn't need it).
  • In footage with significant motion, you may notice more of a smearing effect, similar to the old-school electronic denoising on the laserdisk versions of the original Star Wars trilogy.
  • If you use multiple passes, you can end up with a dithering effect that's very noticeable on dark footage.
Basically, there are some drawbacks. The good news is that you can get very close to the same results with a line like this:

SMDegrain(tr=6,thSAD=500,contrasharp=0,str=1.2,refinemotion=true,lsb=true,interlaced=true)
Which gives this:


This is easier to run, has less of a smearing effect, works better with interlaced footage, and I've removed the built-in sharpening (Contrasharp) so I can use LimitedSharpenFaster later instead.

For those interested, here's a condensed (AVISynth+) version of my .avs script using the above options:

SetFilterMTMode("QTGMC", 2)
SetFilterMTMode("f3kdb", 2)
AVISource("DV Noise Test intro.avi", audio=true)
ConvertToYV12()

SMDegrain(tr=6,thSAD=500,contrasharp=0,str=1.2,refinemotion=true,lsb=true,interlaced=True,Globals=2) 
QTGMC( Preset="Slower", EdiThreads=3 ) 
#--------------(Optional) Reduce chroma noise----------------------
#f3kdb(grainY=0, grainC=0, sample_mode=1)  
Prefetch(10) 
Since the original clip is DV video, I've done a colorspace conversion to YV12. If you're using a Digibeta file or ProRes capture, you might be able to get away without it, or you could convert to YUY2 instead.


Some other info that might be worth knowing:

SMDegrain has a variable called "globals" that allows you to change the way motion vectors are used. Basically, the globals are motion vector calculations, which can be either generated each time SMDegrain is called (default), passed through, or used from previous passes. In other words, if you want the same patterns of noise to be denoised in subsequent passes, you can use:

SMDegrain(tr=6,thSAD=500,contrasharp=0,str=1.2,refinemotion=true,lsb=true,interlaced=true,Globals=2)

for the first pass and:

SMDegrain(tr=6,thSAD=500,contrasharp=0,str=1.2,refinemotion=true,lsb=true,interlaced=true,Globals=1)

for the second. While doing so can reduce processor load a bit, it's still generally not a good idea to do three or more passes, even with globals=1.

From the documentation, here's how each number option works:
  • 0: Ignore globals, just process
  • 1: Read globals and Process
  • 2: Process and output globals
  • 3: Output globals only, don't process 

  • Doing a globals=2 pass and then a globals=1 pass results in the following:



    Which is pretty darn clean. You may still notice some artifacts on fast-moving objects:



    but that's true of any SMDegrain setting that I've tried. TemporalDegrain2 does better with these artifacts, but not so well with overall noise in noisy footage, at least at defaults:



    Oh, and if you still have chroma noise after this, you might want to try messing with something like f3kdb.

    Other notes


    For comparison's sake, here's the best results I could get using Magic Bullet Denoiser III and Neat Video 5, applied to a version of the video already deinterlaced with QTGMC. Yes, both have ugly watermarks due to them being trial versions.



    I could probably get Neat Video looking a bit better with some more tweaking, but this is what I would consider an acceptable denoise from it. If you look closely, you'll notice that Denoiser III retains some noise in this case, while Neat Video removes more, but has a slightly plastic look. This is the tradeoff in denoising - removing *all* grain often removes surface details you might want to keep. For an extreme example of this, check out the Predator Ultimate Edition Blu-Ray.



    I may update this post later, but I think that's it for now. Post a comment if you have a question or correction.

    Which deinterlacing algorithm is the best? Part 1 - HD interlaced footage

    Before I began, I'd like to give a special thanks to Aleksander Kozak for his help in testing and providing the HD footage used in this ...