View Full Version : mClean spatio/temporal denoiser v3.2 (01 March 2018)


burfadel
15th August 2017, 04:15
mClean by burfadel

Changelog: https://forum.doom9.org/showpost.php?p=1815046&postcount=3
Dependencies: https://forum.doom9.org/showpost.php?p=1834698&postcount=334

# mClean spatio/temporal denoiser
# Version: 3.2 (01 March 2018)
# By burfadel

# +++ Description +++
# Typical spatial filters work by removing large variations in the image on a small scale, reducing noise but also making the image less
# sharp or temporally stable. mClean removes noise whilst retaining as much detail as possible, as well as provide optional image enhancement

# mClean works primarily in the temporal domain, although there is some spatial limiting
# Chroma is processed a little differently to luma for optimal results
# Input must be 8-bit Planar type (YV12, YV16, YV24) or their equivalents in 10, 12, 14, or 16 bits
# Chroma processing can be disabled with chroma=false

# +++ Artifacts +++
# Spatial picture artifacts may remain as removing them is a fine balance between removing the unwanted artifact whilst not removing detail
# Additional dering/dehalo/deblock filters may be required, but should ONLY be uses if required due the detail loss/artifact removal balance

# +++ Sharpening +++
# Applies a modified unsharp mask to edges and major detected detail. Range of normal sharpening is 0-20, the default 10. There are 4 additional
# settings, 21-24 that provide 'overboost' sharpening. Overboost sharpening is only suitable typically for high definition, high quality sources.
# Actual sharpening calculation is scaled based on resolution.

# +++ ReNoise +++
# ReNoise adds back some of the removed luma noise. Re-adding original noise would be counterproductive, therefore ReNoise modifies this noise
# both spatially and temporally. The result of this modification is the noise becomes much nicer and it's impact on compressibility is greatly
# reduced. It is not applied on areas where the sharpening occurs as that would be counterproductive. Settings range from 1 to 20, default
# value is 14. The strength of renoise is affected by the the amount of original noise removed and how this noise varies between frames. It's
# main purpose is to reduce the 'flatness' that occurs with any form of effective denoising.

# +++ Deband +++
# This will perceptibly improve the quality of the image by reducing banding effect and adding a small amount of temporally stabilised grain to
# both luma and chroma. The settings are not adjustable as the default settings are suitable for most cases without having a large effect on
# compressibility. Auto balance uses Autoadjust, it calculates statistics of the clip, stabilises temporally and adjusts luminance gain & colour
# balance of the noise reduced clip.
# 0=disabled, 1=deband only, 2=auto balance only, 3=both deband and auto balance, 4=deband and veed, 5=all

# +++ Depth +++
# This applies a modified warp sharpening on the image that may be useful for certain things, and can improve the perception of image depth. Default
# is 0 (disabled), and ranges up to 5. This function will distort the image, for animation a setting of 1 or 2 can be beneficial to improve lines. The
# effect

# +++ Strength +++
# The strength of the denoising effect can be adjusted using this parameter. It ranges from 20 percent denoising effect with strength 1, up to the
# 100 percent of the denoising with strength 20 (default). This function works by blending a scaled percentage of the original image with the processed
# image.

# +++ Outbits +++
# Specifies the bits per component (bpc) for the output for processing by additional filters. It will also be the bpc that mClean will process.
# By default, mClean processes as 12 bits if the input is 8 bit, and converts back to 8 bit. If the input is 10 bits or higher no conversion is
# done unless outbits is specified and is different to the input bpc. If you output at a higher bpc keep in mind that there may be limitations
# to what subsequent filters and the encoder may support.

# +++ Required plugins +++
# Latest RGTools, MVTools2, Masktools2, f3kdb, Modplus, AutoAdjust
# Refer to https://forum.doom9.org/showpost.php?p=1834698&postcount=334

function mClean(clip c, int "thSAD", bool "chroma", int "sharp", int "rn", int "deband", int "depth", float "strength", int "outbits")
{

defH = Max (C.Height, C.Width/4*3) # Resolution calculation for auto blksize settings
thSAD = Default (thSAD, 400) # Denoising threshold
chroma = Default (chroma, true) # Process chroma
sharp = Default (sharp, 10) # Sharp multiplier
rn = Default (rn, 14) # Luma ReNoise strength from 0 (disabled) to 20
deband = Default (deband, 4) # Apply deband/veed and/or auto balance
depth = Default (depth, 0) # Depth enhancement
strength = Default (strength, 20) # Strength of denoising.
outbits = Default (outbits, BitsPerComponent(c)) # Output bits, default input depth
calcbits = BitsPerComponent(c) == 8 ? 12 : outbits

Assert(isYUV(c)==true, """mClean: Supports only YUV formats (YV12, YV16, YV24)""")
Assert(isYUY2(c)==false, """mClean: Supports only YUV formats (YV12, YV16, YV24)""")
Assert(isYV411(c)==false, """mClean: Supports only YUV formats (YV12, YV16, YV24)""")
Assert(sharp>=0 && sharp<=24, """mClean: "sharp" ranges from 0 to 24""")
Assert(rn>=0 && rn<=20, """mClean: "rn" ranges from 0 to 20""")
Assert(deband>=0 && deband<=5, """mClean: deband options 0 (disabled) to 5. Refer to description""")
Assert(depth>=0 && depth<=5, """mClean: depth ranges from 0 (disabled) to 5""")
Assert(strength>0 && depth<=20, """mClean: strength ranges from 1 (20%) to 20 (100%, default)""")
Assert(outbits>=8 && outbits<=16, """mClean: "outbits" ranges from 8 to 16""")

padX = c.width%8 == 0 ? 0 : (16 - c.width%8)
padY = c.height%8 == 0 ? 0 : (16 - c.height%8)
c = padX+padY<>0 ? c.addborders(0, 0, padX, padY) : c
cy = ExtractY(c)
sc = defH>2800 ? 8 : defH>1400 ? 4 : defH>720 ? 2 : 1
blksize = sc==8 ? 8 : ((defH/sc)/360)>1.5 ? 16 : ((defH/sc)/360)>0.8 ? 12 : 8
overlap = blksize>=12 ? 6 : 2
lambda = 775*(blksize*blksize)/64
sharp = sharp>20 ? sharp+30 : DefH<=2600 ? 16+round(defH*(34/2600)*sharp/20) : 50
depth = depth*2
depth2 = -(depth+(depth/2))


# Denoise preparation
c = chroma ? Median (c, yy=false, uu=true, vv=true) : c

# Temporal luma noise filter
fvec1 = bitspercomponent(c)>8 ? convertbits(c, 8) : undefined()
bvec1 = bitspercomponent(cy)>8 ? convertbits(cy, 8) : undefined()
super = MSuper (BicubicResize(chroma ? defined(fvec1) ? fvec1 : c : defined(bvec1) ? bvec1 : cy, c.Width/sc, c.Height/sc),
\ hpad=16/sc, vpad=16/sc, rfilter=4)
super2 = MSuper (chroma ? defined(fvec1) ? fvec1 : c : defined(bvec1) ? bvec1 : cy, hpad=16, vpad=16, levels=1)

# --> Analysis
bvec4 = MRecalculate(super2, MscaleVect (MAnalyse (super, isb = true, delta = 4, blksize=blksize, overlap=overlap), sc),
\ blksize=blksize, overlap=overlap, lambda=lambda, thSAD=180)
bvec3 = MRecalculate(super2, MscaleVect (MAnalyse (super, isb = true, delta = 3, blksize=blksize, overlap=overlap), sc),
\ blksize=blksize, overlap=overlap, lambda=lambda, thSAD=180)
bvec2 = MRecalculate(super2, MscaleVect (MAnalyse (super, isb = true, delta = 2, blksize=blksize, overlap=overlap,
\ badSAD=1100, lsad=1120), sc), searchparam=3, blksize=blksize, overlap=overlap, lambda=lambda, thSAD=180)
bvec1 = MRecalculate(super2, MscaleVect (MAnalyse (super, isb = true, delta = 1, blksize=blksize, overlap=overlap, badSAD=1500, badrange=27,
\ search=5, lsad=980), sc), blksize=blksize, overlap=overlap, search=5, searchparam=3, lambda=lambda, thSAD=180)
fvec1 = MRecalculate(super2, MscaleVect (MAnalyse (super, isb = false, delta = 1, blksize=blksize, overlap=overlap, badSAD=1500, badrange=27,
\ search=5, lsad=980), sc), blksize=blksize, overlap=overlap, search=5, searchparam=3, lambda=lambda, thSAD=180)
fvec2 = MRecalculate(super2, MscaleVect (MAnalyse (super, isb = false, delta = 2, blksize=blksize, overlap=overlap,
\ badSAD=1100, lsad=1120), sc), searchparam=3, blksize=blksize, overlap=overlap, lambda=lambda, thSAD=180)
fvec3 = MRecalculate(super2, MscaleVect (MAnalyse (super, isb = false, delta = 3, blksize=blksize, overlap=overlap), sc),
\ blksize=blksize, overlap=overlap, lambda=lambda, thSAD=180)
fvec4 = MRecalculate(super2, MscaleVect (MAnalyse (super, isb = false, delta = 4, blksize=blksize, overlap=overlap), sc),
\ blksize=blksize, overlap=overlap, lambda=lambda, thSAD=180)

# --> Bit depth conversion
c = chroma ? calcbits != BitsPerComponent(c) ? ConvertBits(c, calcbits) : c : c
super2 = calcbits != BitsPerComponent(super2) ? ConvertBits(super2, calcbits) : super2
cy = calcbits != BitsPerComponent(cy) ? ConvertBits(cy, calcbits) : cy

# --> Applying cleaning
clean = MDegrain4(chroma ? c : cy, super2, bvec1, fvec1, bvec2, fvec2, bvec3, fvec3, bvec4, fvec4, thSAD=thSAD)
u = chroma ? ExtractU(clean) : nop ()
v = chroma ? ExtractV(clean) : nop ()
filt_chroma = chroma ? CombinePlanes(c, mt_adddiff(u, clense(mt_makediff(ExtractU(c), u), reduceflicker=true)), mt_adddiff(v,
\ clense(mt_makediff(ExtractV(c), v), reduceflicker=true)), planes="yuv", source_planes="yyy", sample_clip=c) : c
clean = chroma ? ExtractY(clean) : clean

# Post clean, pre-process deband
filt_chroma_bits = BitsPerComponent(filt_chroma)
clean2 = deband==0 ? nop() : ConvertBits(clean, 8)
noise_diff = deband==0 ? nop() : BitsPerComponent(c)==8 ? nop() : mt_makediff(convertbits(clean2, calcbits), clean)
depth_calc = deband==0 ? nop() : CombinePlanes (clean2, filt_chroma_bits>8 ? ConvertBits(filt_chroma, 8) : filt_chroma, planes="YUV",
\ source_planes="YUV", pixel_type="YV12")
depth_calc = deband==0 ? nop() : deband>1 ? deband==4 ? depth_calc : AutoAdjust (depth_calc, auto_gain=true, bright_limit=1.09, dark_limit=1.11,
\ gamma_limit=1.045, auto_balance=true, chroma_limit=1.13, chroma_process=115, balance_str=0.85) : depth_calc
depth_calc = deband==0 ? undefined() : deband<>2 ? f3kdb (depth_calc, preset=chroma?"high":"luma", range=16, grainY=38*(defH/540),
\ grainC=chroma?37*(defH/540):0) :depth_calc
clean = deband==0 ? clean : BitsPerComponent(c)==8 ? ExtractY (depth_calc) : mt_adddiff(ConvertBits(ExtractY
\ (depth_calc), calcbits), noise_diff)
depth_calc = deband==0 ? nop() : BitsPerComponent(depth_calc)<>filt_chroma_bits ? ConvertBits(depth_calc, filt_chroma_bits) : depth_calc
filt_chroma = deband==0 ? filt_chroma : deband>4 ? veed(depth_calc) : depth_calc

# Spatial luma denoising
clean2 = removegrain(clean, 18)

# Unsharp filter for spatial detail enhancement
clsharp = sharp>0 ? sharp>=51<=54 ? mt_makediff(clean, gblur(clean2, (sharp-50), sd=3)) :
\ mt_makediff(clean, blur(clean2, 1.58*(0.03+(0.97/50)*sharp))) : nop()
clsharp = mt_adddiff(clean2, repair(clense(clsharp), clsharp, 12))

# If selected, combining ReNoise
noise_diff = mt_makediff (clean2, cy)
clean2 = rn>0<=20 ? mt_merge(clean2, mergeluma (clean2, mt_adddiff(clean2, tweak(clense(noise_diff, reduceflicker=true), cont=1.008+(0.0032*(rn/20)))),
\ 0.3+(rn*0.035)), mt_lut (overlay(clean, invert(clean), mode="darken"), "x 32 scaleb < 0 x 45 scaleb > range_max 0 x 35 scaleb - range_max 32
\ scaleb 65 scaleb - / * - ? ?")) : clean2

# Combining spatial detail enhancement with spatial noise reduction using prepared mask
noise_diff = mt_invert(mt_binarize(noise_diff))
clean2 = sharp>0 ? mt_merge (clean2, clsharp, overlay(noise_diff, mt_edge(clean, "prewitt"), mode="lighten")) :
\ mt_merge (clean2, clean, overlay(noise_diff, mt_edge(clean, "prewitt"), mode="lighten"))

# Converting bits per channel and luma format
filt_chroma = outbits < BitsPerComponent(filt_chroma) ? ConvertBits(filt_chroma, outbits, dither=1) : ConvertBits(filt_chroma, outbits)
clean2 = outbits < BitsPerComponent(clean2) ? ConvertBits(clean2, outbits, dither=1) : ConvertBits(clean2, outbits)
c = BitsPerComponent(c) <> BitsPerComponent(clean2) ? ConvertBits(c, BitsPerComponent(clean2)) : c

# Combining result of luma and chroma cleaning
output = CombinePlanes(clean2, filt_chroma, planes="YUV", source_planes="YUV", sample_clip=c)
output = strength<20 ? Merge(c, output, 0.2+(0.04*strength)) : output
depth_calc = depth>0 ? defh>640 ? bicubicresize(output, 720, 480) : output : nop()
output = depth>0 ? mt_adddiff(output, spline36resize(mt_makediff(awarpsharp2(depth_calc, depth=depth2, blur=3),
\ awarpsharp2(depth_calc, depth=depth, blur=2)), output.width, output.height)) : output
output = padX+padY<>0 ? output.crop(0, 0, -padX, -padY) : output

return output
}

MysteryX
15th August 2017, 04:27
Besides smart block size selection, is this different than the first version?

As I said, I'd be very interested in a script that includes optional correction of other types of defects. I don't know what order of execution gives best results.

Here's a question for you.

How would you define, in technical terms:
- noise
- ringing
- blocking
- banding

... and where do you draw the line between what is and isn't each of the above?

burfadel
15th August 2017, 05:21
Changes:

v3.2
- small update to to make use of changes in MvTools2 2.7.25 *** Please update dependencies ****
- analysis will always be done in 8 bit regardless of input depth. This will give a small speed bump with no quality loss for videos with input bitdepth greater than 8
- fixed deband=0 issue

v3.1
- minor tweaks to analysis
- minor tweaks to luma and chroma renoise

v3.0
- considerable amount changed:
- reverted to MDegrain chroma denoising with different handling of chroma
- heavily revised luma cleaning
- sharp scales to resolution based on the multiplier (now 0-24)
- renoise tweaked
- new masks
- remove cstr setting
- added 'depth' function (post-processing)
- added strength function; remixes a portion of the original image back with the processed imaged, scaled from 20 percent to 100 percent at strength 20 (default)
- no longer requires FFT3DFilter or fftw dependency
- requires modplus for chroma processing (in addition to existing features) http://www.avisynth.nl/users/vcmohan/modPlus/modPlus.html
- some other tweaks and changes

v2.3
- modified deband features so that luma bit depth difference is only applied when source material is greater than 8 bit
- resolved minor artifacts that were occasionally produced when there were high contrast differences and flat surfaces
- repurposed dctfilter

v2.2
- corrected an oversight regarding chroma processing of deband features, it was applied to luma but not to chroma
- resolved an associated script bug that didn't affect anything since chroma deband wasn't applied
- luma bit depth detail now retained even when using deband features

v2.1
- processing of denoising now undertaken in 12 bits (or whatever is specified for outbits if greater than 8), analysis still processed at source depth.
- added the use of veed by VCMohan. Deband options 4 is now default (deband+veed), deband option 5 is to use deband, veed, and level adjustment (autoadjust)
- modified renoise
- slight adjustment to the motion mask
- made changes to the temporal stabilisation of renoise and sharpening (and made a correction to the sharpening stabilisation)
- added non-8 bit workaround for modern high bit depth incompatible filters (deband from f3kdb and autoadjust)
- tweaked several parameters
- resolved bit depth issue when using different combinations of input depths and outbits

v2.0
- improved debanding feature, now ranges 0-3. 0=disabled, 1=deband (default), 2=levels/saturation auto balance adjustment only, 3=both
- levels/saturation is automatically adjusted using the Autolevels plugin, only required if manually enabled - https://forum.doom9.org/showthread.php?t=167573
- changed sharpening setting from 'enh' to 'sharp' to better distinguish what it is. 'enh' will be used later for another name appropriate feature
- refractored sharpening, it now increases a little less with higher resolutions
- added chroma renoise when chroma is enabled (default), non-adjustable
- fixed issue with block sizes on higher resolutions
- fixed issue with the passmask used as part of processing; appears Masktool2 may have a bug with the value scaling feature
- tweaked noise processing parameters

v1.9
- added option to disable chroma processing, default is to process chroma
- added an option to change the strength of chroma processing
- added debanding, default is enabled
- adjusted blocksize parameters
- tweaked some other settings

v1.8
- speed increase and reduced memory use for all but the lowest resolutions
- improved quality
- removed cpu option for FFT3DFilter, as any more than 4 threads proves no faster an for high thread counts, appears to run slower

v1.7c
- slight adjustments and slightly better speed

v1.7b
- modified analysis for MDegrain for performance and quality

MysteryX
15th August 2017, 06:14
mt_lut evaluates an expression on pixels. It basically allows implementing algorithms without having to write a DLL nor write assembly code. It uses a LUT table for optimization, but that doesn't work for 16-bit videos. Because noise reduction algorithms deal with subtleties and then affect the rest of the script, I'd recommend running it in 16-bit, and mt_lut then isn't a good option. If you need custom algorithms, creating a DLL is always a good option, like I did with FrameRateConverter to detect stripe patterns.

Banding is not related to blocking. Banding is due to rounding where each value appears as a distinct band. To avoid banding, we normally use dithering. No dithering leads to banding.

burfadel
15th August 2017, 07:52
Original third post:
The basic noise filtering is similar to the original script, although I did make a small mistake that limited part of the effectiveness of that original script. The difference is what this version of the script does with the outputs of the different filtering. It could also potentially allow for deringing and dehalo reutilising some of the calculations, and this to some extent can be done for deblocking as well, but I'd have to work out the best way of making it effective. Banding is the hard one though, there would be probably no benefit to include that in the script over running a separate filter.

I would describe temporal noise as small variations between each frame on the scale of a few pixels. Spatial noise is small variations on the scale of a few pixels compared to adjacent pixels, that doesn't change too much between frames. This is much harder to remove without affecting actual detail, because it's a math based solution, not an perceptual based where we look at it and determine that it shouldn't be there. Temporal denoising is therefore IMO potentially much more useful out of the two, historially though temporal denoising was considerably slower and not practical. A small amount of spatial denoising I think can be beneficial though, if you can work out how it should be applied.

Ringing is the small 'ring like' artifacts typically next to areas of large contrast difference, caused by resizing or compression. Halo's are a brightening of the edge, typically outside edge due to contrast changes of an object that may be present. This can be the result of oversharpening. Blocking is the visible edges of a block typically caused by not enough bandwidth or the use of an inefficient (by modern standard) codec. In effect it is large scale pixellation. Banding is related to blocking, it's the visualisation of the block boundaries on a flat area that has gradient, again caused by encoder or transfer inefficiencies.

No promises on a timeframe for the ringing, halo, deblocking filter, or whether it can be done related to a concept I have in mind. I think for best results I might have to use mt_lut functions, and to be honest I don't know how to use that. The documentation for masktools is a little lacking regarding most of its power features. There's a function called mt_gradient(), but not sure whether that actually does what it sounds like... and again, no idea how to use it!

-------------------

Ah ok. Makes sense about the banding, I thought that rounding occurs on a per block case causing the edges of the blocks to become pronounced over flat areas that have a gradient. Does the banding occur mid block? As for blocking, do you know if DCTFilter could be used for that, to ascertain block boundaries etc?

Updated version here:
https://github.com/chikuzen/DCTFilter/releases

I do have an idea for deringing and dehalo, however it would have to wait until the weekend to even contemplate sitting down and nutting it out :). I'd set it to enable it as an option, likewise with any deblocking.

feisty2
15th August 2017, 11:08
mt_lut evaluates an expression on pixels. It basically allows implementing algorithms without having to write a DLL nor write assembly code. It uses a LUT table for optimization, but that doesn't work for 16-bit videos. Because noise reduction algorithms deal with subtleties and then affect the rest of the script, I'd recommend running it in 16-bit, and mt_lut then isn't a good option. If you need custom algorithms, creating a DLL is always a good option, like I did with FrameRateConverter to detect stripe patterns.

Banding is not related to blocking. Banding is due to rounding where each value appears as a distinct band. To avoid banding, we normally use dithering. No dithering leads to banding.

no, pixel-wise evaluations are just literally, "pixel-wise", u got no access to the neighbor pixels and that renders it much less useful than a dynamic library

a Gaussian blur with a radius of 1 is simply like

dstp[y][x] = (srcp[y-1][x-1] + 2 * srcp[y-1][x] + srcp[y-1][x+1] + 2 * srcp[y][x-1] + 4 * srcp[y][x] + 2 * srcp[y][x+1] + srcp[y+1][x-1] + 2 * srcp[y+1][x] + srcp[y+1][x+1]) / (1 + 2 + 1 + 2 + 4 + 2 + 1 + 2 + 1);

for a c++ plugin

now how is that gonna work for ur fancy LUT or whatever?

well, another fun fact is that it's actually possible in vaporsynth with Expr

topleft = core.std.AddBorders(core.std.CropRel(clp, 0, 1, 0, 1), 1, 0, 1, 0)
topcenter = ...
topright = ...
adjacentleft = ...
center = clp
adjacentright = ...
bottomleft = ...
bottomcenter = ...
bottomright = ...

clp = core.std.Expr([topleft, topcenter, topright, adjacentleft, center, adjacentright, bottomleft, bottomcenter, bottomright],
"x y 2 * + z + a 2 * + b 4 * + c 2 * + d + e 2 * + f + 1 2 + 1 + 2 + 4 + 2 + 1 + 2 + 1 + /")

ain't that pretty, eh? that's why it's only possible but not practical

and I'm damn sure it's not even possible in avisynth
edit:
or maybe possible with y8rpn, but you see the point, it's nasty

Mounir
15th August 2017, 11:49
there is no function named veed that's what i get, any idea?
i can't find the plugin veed anywhere
nevermind, i found modplus(which contain veed i think)

now i get:
manalyse blocks must be 4x4, 8x4, 16x2 blabla

feisty2
15th August 2017, 11:58
why not use mt_convolution() / mt_luts() / core.std.Convolution()?

mt_lut is a pixel-wise evaluator, std.Convolution is NOT
the toy in vaporsynth corresponding to mt_lut(xyz) should be std.Expr (function-wise, they do things differently tho)

feisty2
15th August 2017, 12:05
Your code is the same as
core.std.Convolution(matrix=[1, 2, 1, 2, 4, 2, 1, 2, 1])
or simply
core.rgvs.RemoveGrain(11)
right?

So why you write such complicated code?

to show that a pixel-wise evaluator is far from enough to code any sophisticated algorithm

std.Convolution is NOT A PIXEL-WISE EVALUATOR, it is NOT CORRESPONDING TO mt_lut, stop distracting me

the point is there're limitations for pixel-wise evaluators, not how to perform a Gaussian blur quick and fast, that Gaussian blur thing is just a demonstration of the point

feisty2
15th August 2017, 12:14
I never said that. Maybe I should stop speaking because this is an avs thread and it seems that we both misunderstand each other.


the point is there're limitations for pixel-wise evaluators, not how to perform a Gaussian blur quick and fast, that Gaussian blur thing is just a demonstration of the point

did you even read my previous posts?

feisty2
15th August 2017, 12:31
How would you define, in technical terms:
- noise
- ringing
- blocking
- banding

... and where do you draw the line between what is and isn't each of the above?

noise: any unwanted components in the signal, concretely, noise is generally modeled as a random signal that follows Gaussian distribution in most denosing algorithms, this random signal could be canceled out by various approaches, bilateral assumes that pixel-wise weighted averaging could cancel out the signal, DFT assumes that if you extract a piece of pattern from the image, you will notice something fishy in that pattern and it's an intra-pattern based approach, pixel and block matching assume that noise could be canceled by averaging similar patterns, it's an inter-pattern based approach

ringing: https://en.wikipedia.org/wiki/Gibbs_phenomenon

blocking: has definitely nothing to do with low-bitrate, the real reason it happens with some obsolete codecs is that, macroblocks in those codecs do not share any overlap

banding: lack of quantizing precision, 8bit sucks, it won't happen if the entire process chain has a higher precision, say, 32bit float

burfadel
15th August 2017, 13:42
that's what i get, any idea?
i can't find the plugin veed anywhere
nevermind, i found modplus(which contain veed i think)

now i get:
manalyse blocks must be 4x4, 8x4, 16x2 blabla

What version of Avisynth and MVTools are you using? The script was written under AviSynth+ r2508, and the latest Pinterf's updated MVtools and Masktools. Are you using any custom options for blocksize? The auto blocksize calculation was from Mysteryx's Framerateconverter.

MysteryX
15th August 2017, 19:24
that's what i get, any idea?
i can't find the plugin veed anywhere
nevermind, i found modplus(which contain veed i think)

now i get:
manalyse blocks must be 4x4, 8x4, 16x2 blabla
Support for additional block sizes was added in one of Pinterf's latest version of MvTools2.

no, pixel-wise evaluations are just literally, "pixel-wise", u got no access to the neighbor pixels and that renders it much less useful than a dynamic library
I didn't say you could write C++ plugins with mt_lut. I said that anything you write with mt_lut can be written as a plugin.

Ah ok. Makes sense about the banding, I thought that rounding occurs on a per block case causing the edges of the blocks to become pronounced over flat areas that have a gradient. Does the banding occur mid block? As for blocking, do you know if DCTFilter could be used for that, to ascertain block boundaries etc?
I just realized banding mostly occurs for dark and bright scenes because of the 2.2 gamma curve. To preserve details, you would only work on Luma and only on value above/below a certain threshold where the difference between adjacent values is visible to the naked eye, leaving all mid-range values intact.

Banding happens only in specific scenarios:
- dark or bright scenes
- flat areas

If I was to implement a debander, I'd scan Luma horizontally line by line for flat areas that degrade by 1 or 2, and mark the division point between 2 flat areas of adjacent values, and mark the flat areas themselves. Repeat vertically.

Then I'd transform that patterns grid to detect significant zones, discarding detection on single lines. Similar to what I did with stripes detection.

Then, I could apply blurring/dithering/something to soften these edges. Since we're talking about flat Luma areas, there's not really any loss of data. In terms of order of execution, this should happen after denoising.

How does this compare to other debanding methods?


So what are the recommended plugins for each type of defect? In which order should they be run?
- Denoise: MClean is doing good so far
- Dering: I got best results with HQDeringmod.avsi (very complex script)
- Deblock: DCTFilter
- Deband: ?

MysteryX
15th August 2017, 20:01
I just tried your script. First version was good. Perhaps it was a lucky shot
https://s21.postimg.org/s2jsjbn5v/MClean1.png (http://postimg.org/image/s2jsjbn5v/)

This version is too sharp for me.
https://s21.postimg.org/hgzx7bgub/MClean2.png (http://postimg.org/image/hgzx7bgub/)

Other version you made me try was too blurry.

johnmeyer
16th August 2017, 01:26
I tried it, got the same "veed" error as everyone else, then downloaded ModPlus, but the script crashed right away with the error message: "An out-of-bounds memory access (access violation) occurred in module 'fftw3'...reading address FFFFFFFF."

So, no go here.

Even it I could get it to work, it looks to me like most of the noise reduction is simply using MDegrain in this line from the script:clean = c.MDegrain2 (super, bvec1, fvec1, bvec2, fvec2, thSAD=thSAD, plane = 0)There is also some selective (via mask) sharpening which may, or may not, be a good thing.

So, while I read what you said about your objectives, I am not sure you have created anything that is much different from what already existed.

MysteryX
16th August 2017, 02:35
Here's the original version that I got good results with. Less loss of details than with KNLMeansCL.

# MClean basic script
# Mask from bennynihon https://forum.doom9.org/showthread.php?p=1689444#post1689444
# Remaining script by burfadel altered from generic information
# Basics for this script is to remove grain whilst retaining as much information as possible
# The script should also be relatively fast, even without Masktools2 multithreading (disabled due to possible MT bug)
# Chroma is processed via a different method to luma for optimal results
# Requires RGTools, Modplus (Veed, for part of chroma filter), MVTools2, Masktools2, FFT3DFilter


function MClean(clip c, int "thSAD", int "blksize", int "blksizeV", int "overlap", int "overlapV", int "cblksize", int "cblksizeV", int "coverlap", int "coverlapV", int "cpu")
{
thSAD = Default(thSAD, 350) # Denoising threshold
blksize = Default(blksize, 16) # Horizontal block size for luma
blksizeV = Default(blksizeV, blksize) # Vertical block size for luma, default same as horizontal
overlap = Default(overlap, 4) # Block overlap
overlapV = Default(overlapV, overlap) # Overlap for vertical luma blocks, default same as horizontal

cblksize = Default(cblksize, 16) # Horizontal block size for chroma
cblksizeV = Default(cblksizeV, cblksize) # Vertical block size for chroma, default same as horizontal
coverlap = Default(coverlap, cblksize/4) # Overlap for horizontal chroma blocks, default quarter cblksize
coverlapV = Default(coverlapV, cblksizeV/4) # Overlap for vertical chroma blocks, default quarter cblksizeV
cpu = Default(cpu, 4) # Threads for FFT3DFilter


# Masks
LumaMask=mt_binarize(c, threshold=64, upper=true).greyscale().BilinearResize((c.width/16)*2, (c.height/16)*2).BilinearResize(c.width,c.height).mt_binarize(threshold=254)
EdgeMask=mt_edge(c, mode="prewitt",thy1=0,thy2=16).greyscale().mt_binarize(threshold=16, upper=true).BilinearResize((c.width/16)*2, (c.height/16)*2).BilinearResize(c.width,c.height).mt_binarize(threshold=254)
GrainMask=mt_logic(LumaMask,EdgeMask,mode="and")
DegrainMask=GrainMask.mt_invert()

# Chroma filter
filt_chroma=fft3dfilter(veed(c), plane=3, bw=cblksize, bh=cblksizeV, ow=coverlap, oh=coverlapV, bt=5, sharpen=0.5, ncpu=cpu, dehalo=0.2, sigma=2.35)

# Luma Filter
super = c.MSuper(rfilter=4, chroma=false,hpad=16, vpad=16)
bvec2 = MAnalyse(super, chroma=false, isb = true, delta = 2, blksize=blksize, blksizeV=blksizeV, overlap=overlap, overlapV=overlapV, search=5, searchparam=7)
bvec1 = MAnalyse(super, chroma=false, isb = true, delta = 1, blksize=blksize, blksizeV=blksizeV, overlap=overlap, overlapV=overlapV, search=5, searchparam=4)
fvec1 = MAnalyse(super, chroma=false, isb = false, delta = 1, blksize=blksize, blksizeV=blksizeV, overlap=overlap, overlapV=overlapV, search=5, searchparam=4)
fvec2 = MAnalyse(super, chroma=false, isb = false, delta = 2, blksize=blksize, blksizeV=blksizeV, overlap=overlap, overlapV=overlapV, search=5, searchparam=7)
Clean = c.MDegrain2(super, bvec1, fvec1, bvec2, fvec2, thSAD=thSAD, plane = 0)

#Luma mask merge
filt_luma = c.mt_merge(Clean, DegrainMask, U=1, V=1)

# Combining result of luma and chroma cleaning
output = mergechroma(filt_luma,filt_chroma)

return output
}

Now that I look at it though, it looks like SMDegrain, except that it instead uses FF3DFilter for chroma.

burfadel
16th August 2017, 05:40
Inevitably all scripts will look similar, the difference is how the results are treated afterwards. Mdegrain is purely temporal, I have detail independent spatial noise reduction added, as well as noise independent sharpening to recover detail, both temporally stabilised. A type of derainbow function can easily be implemented with very little performance cost as an option, once ideal preset settings are worked out. Deringing and dehalo could also be implemented utiising existing masks

@MysteryX, I'll add an adjustment function for the detail sharpening strength :).

Do people find the sharpening too strong for other sources? I'll reduce the defaults to make the sharpening more neutral.

I'll remove veed seeing as it's an Avisynth+ only filter, the Avisynth version is deveed. These can still be run separately. I'll also update the info regarding the need for fftw.

MysteryX
16th August 2017, 08:03
There's nothing I hate more than over-sharpened videos. Much better when it is sharp but neutral. It's often a fine line though. Also, sharpening amplifies noise and artifacts.

burfadel
16th August 2017, 10:25
There's nothing I hate more than over-sharpened videos. Much better when it is sharp but neutral. It's often a fine line though. Also, sharpening amplifies noise and artifacts.

That's true. That sharpening shouldn't really affect noise, however it could make some forms of artifacts stand out more for now until that part is sorted out :). At the moment the script almost entirely focuses on noise removal with the intention not to remove detail. I'll ease back the sharpening by half, and have it adjustable with a parameter scaled from probably 1/10 of what it is now to a bit more at 100, and have the default set at say, 40. I'll take a look at it shortly and update the first post script :).

feisty2
16th August 2017, 10:47
sharpening is not how u gonna magically resurrect the lost details, especially a cheap USM like that
I say you'd better off try some fancy new toys like denoising autoencoder and see how it goes

feisty2
16th August 2017, 11:01
also MDeGrain being one of the inter-patch (self-similarity) based approaches, is theoretically equivalent to a sparse coding unit, you can have one or many of those sparse coding layers in your denoising autoencoder and it would reasonably give you results similar to MDeGrain but better

burfadel
16th August 2017, 11:30
I've updated the script with a setting for that sharpening amount. This is optioned as 'enh' as it targets detail. A setting of 0 completely disables it. Default setting is 20, which is approximately half of what it was. I realise you can't resurrect lost details, that's not the intention. It's impossible to truly resurrect lost details regardless of how it's done, simply because if it's 'lost' then it's not there to recover. The best you can do is use algorithms to determine what is expected to be there, but that's still not recovering the true detail.

I started looking into other filters because I found KNLMeansCL, as well as other filters, had output that just wasn't as clear as it should be. I then thought of ways to circumvent this, the result being mClean. Another consiration is for it to be pretty fast, suitable for most scenarios, and also keeping things relatively simple. The future intention is to add dehalo, dering, derainbow etc as options, and hopefully deblock as well. Strong enough to be effective but not to reduce wanted detail, whilst still remaining relatively fast.

feisty2
16th August 2017, 11:57
quality and performance are on the opposite sides of the tradeoff, you simply can't have them both in general
some denosing filter kills a lot of details along with the noise, some kills less, if you're not happy with what you already got, the healthy choice would be making a new denoising filter that kills less details in the first place, not sharpening what's left of the crap

burfadel
16th August 2017, 12:36
That's why the sharpening is only targetting detail, the non-detail is actually being spatially cleaned :). Currently it's an 'adaptive' denoiser, but the intention is for it eventually to be an 'adaptive' noiser by default and an cleaner with the options set.

GMJCZP
16th August 2017, 13:28
burfadel, I'm noticing in this version of MClean no longer has this:

# Masks
LumaMask=mt_binarize(c, threshold=64, upper=true).greyscale().BilinearResize((c.width/16)*2, (c.height/16)*2).BilinearResize(c.width,c.height).mt_binarize(threshold=254)
EdgeMask=mt_edge(c, mode="prewitt",thy1=0,thy2=16).greyscale().mt_binarize(threshold=16, upper=true).BilinearResize((c.width/16)*2, (c.height/16)*2).BilinearResize(c.width,c.height).mt_binarize(threshold=254)
GrainMask=mt_logic(LumaMask,EdgeMask,mode="and")
DegrainMask=GrainMask.mt_invert()


Did you have this as planned?

burfadel
16th August 2017, 14:18
It meant the denoising was applied too weak on certain parts of the image.

MysteryX
16th August 2017, 18:40
I like this version

first MClean version / MClean 1.1 / KNLMeans(D=2, A=2, h=1.5, channels="YUV")

https://s22.postimg.org/gmi7lad59/MClean_Old.png (http://postimg.org/image/gmi7lad59/) https://s22.postimg.org/c20mk3itp/MClean11.png (http://postimg.org/image/c20mk3itp/) https://s22.postimg.org/4w4a3wkct/KNLMeans.png (http://postimg.org/image/4w4a3wkct/)

I like this one (middle) best, and it is notably better than your first version. Default Enh=20 looks good, eh=40 is too much.

SaurusX
16th August 2017, 21:05
I like this version

first MClean version / MClean 1.1 / KNLMeans(D=2, A=2, h=1.5, channels="YUV")

https://s22.postimg.org/gmi7lad59/MClean_Old.png (http://postimg.org/image/gmi7lad59/) https://s22.postimg.org/c20mk3itp/MClean11.png (http://postimg.org/image/c20mk3itp/) https://s22.postimg.org/4w4a3wkct/KNLMeans.png (http://postimg.org/image/4w4a3wkct/)

I like this one (middle) best, and it is notably better than your first version. Default Enh=20 looks good, eh=40 is too much.
The middle picture has more blocking, though less ringing around the letters compared the KNLMeansCL. The hand in the air for the woman on the right is better defined in the KNLMeansCL picture.

MysteryX
16th August 2017, 21:37
This doesn't solve blocking at all. I'll need to run a separate filter for that. You're right, hand in the air on the right is better defined for KNLMeans. The biggest difference, however, is in the curtains and roof, where KNLMeans discards the subtle details as noise giving a slight plastic or washed out effect.

Note than in my sample, I'm applying upscaling and interpolation after denoising which amplifies the denoising difference. Small variations can however cause large variations in interpolation, explaining why one hand would be clearer while other objects have less details. Sometimes it simply changes the motion estimation so that the hand appears clearer one frame earlier or later. So we really have to look at the whole picture.

burfadel
17th August 2017, 05:29
It's hard to compare spatial images when any filter has a temporal component. The temporal stability of motion and detail is something you can't judge in images but can have a huge impact on video quality. I've got another idea that I'll put in this week, many will say it's pointless but it's really simple and some may find it useful.

MysteryX
17th August 2017, 05:50
It always goes like this. First people say it's impossible. Then you achieve it anyway. Then some use it.

So don't worry what people say :) Just implement your ideas.

burfadel
19th August 2017, 16:33
Updated the script in the first post with a new feature, ReNoise. It's range is from 0 (default, disabled), to 20. This feature allows you to add back some of the luma noise that was removed, which may sound counterintuitive. However, the noise has been temporally cleaned and also spatially modified, so it's not the same as when removed and should have better compressibility. An advantage of doing the modifications to the noise is that it changes only the noise, not also the underlying picture which would be the case if it were applied to the whole picture. There is an option from 0 (disabled), 1 to 10, which adds back 10-100 percent of the modified noise, and 11-20 adds an additional 10 to 100 percent. The modified noise is much weaker, so I thought it would be good to give the option to apply it more strongly :). A setting of 5 is probably a good starting point if you wish to use it.

MysteryX
20th August 2017, 01:14
Interesting. As a denoiser before upscaling, renoising is useless.

As a prefilter on HD content... I've encoded with x264 to see the difference in encoding and taken screenshots afterwards. FrameRateConverter is with preset=Normal... preset=Slower just wasn't working with this at all performance-wise.

- RemoveGrain(21) [1.43MB)
- MClean() [1.46MB]
- MClean(rn=5) [1.46MB]
- MClean(rn=10) [1.46MB]
- MClean(rn=10), 16-bit processing [1.57MB]

https://s2.postimg.org/7d1kp9tn9/Enc_Remove_Grain.png (http://postimg.org/image/7d1kp9tn9/) https://s2.postimg.org/cjcq0b8et/Enc_MClean.png (http://postimg.org/image/cjcq0b8et/) https://s2.postimg.org/4rw01r49h/Enc_MClean5.png (http://postimg.org/image/4rw01r49h/) https://s2.postimg.org/800hesqj9/Enc_MClean10.png (http://postimg.org/image/800hesqj9/) https://s2.postimg.org/8qt7kkswl/Enc_MClean16.png (http://postimg.org/image/8qt7kkswl/)

MClean gives a plastic effect as a prefilter, or perhaps it could work with lower settings, but with rn=10 it looks better than RemoveGrain. Note that there are considerable rounding errors in 8-bit. If I do the whole processing in 16-bit, quality is much better. I think MClean should internally work in 16-bit if the source is 8-bit, otherwise there are rounding differences on subtle details applied several times in a row.

For some reason, I'm unable to convert to 16-bit and back to 8-bit for the prefilter.

Pref=last.ConvertBits(16).MClean(rn=10).ConvertBits(8, dither=1)
FrameRateConverter(NewNum=60, NewDen=1, Prefilter=Pref)

MRecalculate: wrong pixel type in FrameRateConverter line 145

In terms of performance, this is using MvTools2 which has MT performance issues. With FRC Preset=Normal which uses DCT=0, the encoding won't start at all, it jams, even though MT normally works with DCT=0. With FRC Preset=Slower (non-MT), it drags extremely slowly.

MysteryX
20th August 2017, 03:35
As for running MClean separately instead of as a prefilter, it considerably decrades the quality.

Prefilter / Denoise (both in 16-bit)
https://s2.postimg.org/x4w35yfdh/Clean1.png (http://postimg.org/image/x4w35yfdh/) https://s2.postimg.org/jchoabolx/Clean2.png (http://postimg.org/image/jchoabolx/)

I'm getting best results with MClean(rn=10) in 16-bit, but am unable to use it as a prefilter in 16-bit and can't figure out why. Additionally, I'm unable to get a decent encoding done with FRC Preset=slower combined with MClean. But ultimately, that would give the best.

burfadel
20th August 2017, 10:55
I've updated the script again, just note that rn strength may need to be adjusted :). You would think that it would work okay as a prefilter, but I suspect using a tempoeral filter for temporal analysis may be the issue? For the down dither, I'm using dither=0 as it would be better for compressibility and for running before framerateconverter.

MysteryX
20th August 2017, 16:35
This would cause frame requests to come in a weird order, and MT is known to have a limitation requiring frame to be requested in the right order.

With ST, however, it shouldn't be an issue.

burfadel
20th August 2017, 16:48
The whole script doesn't seem to run as a prefilter though, as the script instructs it to convert back to 8 bits. It's as if the MAnalyse of FrameRateConverter is pulling the MSuper/MAnalyse data directly from mClean, hence mixing 16 bit with 8 bit and the error.

MysteryX
20th August 2017, 16:52
What's concerning is that it looks like a bug in the core. Perhaps try AVS 2.6?

Duh, there's no ConvertBits in 2.6, never mind!

MysteryX
21st August 2017, 22:05
Interesting bug.

This freezes when opening in VirtualDub. Later on I ran this through the encoder and it went just fine...

file="Video.mp4"
LWLibavVideoSource(file, cache=False)
ConvertBits(16).MClean(rn=10).ConvertBits(8, dither=0)
Prefetch(8)


This works.

ColorBarsHD()
ConvertBits(16).MClean(rn=10).ConvertBits(8, dither=0)
Prefetch(8)


This also works.

file="Video.mp4"
LWLibavVideoSource(file, cache=False)
MClean(rn=10)
Prefetch(8)


Importing in this way still gives "wrong pixel type"

Pref=AviSource("PreviewPref.avs")
FrameRateConverter(NewNum=60, NewDen=1, Prefilter=Pref)


You're however able to run the prefilter as a first pass as an AVI file and then use that as a source for the 2nd pass. Not ideal but at least we can try and compare quality. Then you're also able to use Preset="slower".

MysteryX
21st August 2017, 22:43
OK I've done some comparison tests as a prefilter, running MClean in 16-bit and FRC in 8-bit. These tests take longer because I need to encode the prefilter output as an AVI file and then use that interim file. It gives the quality comparison though.

What did you change in this version? Renoise seems softer. Any settings you recommend for better results as a prefilter?

Here I didn't test H264 encoding which may show additional benefits.

RemoveGrain(21) / previous MClean(rn=10) / new MClean(rn=10) / new MClean(rn=12)

https://s2.postimg.org/wem8w0c0l/Remove_Grain.png (http://postimg.org/image/wem8w0c0l/) https://s2.postimg.org/iie0k4frp/MClean_B.png (http://postimg.org/image/iie0k4frp/) https://s2.postimg.org/3npf5y66t/MClean_C.png (http://postimg.org/image/3npf5y66t/) https://s2.postimg.org/lt2dk05p1/MClean_C12.png (https://postimg.org/image/lt2dk05p1/)

The prefilter AVI interm file is 392MB for previous MClean and 400MB for this version, which indicates there are more details.

Performance-wise, MClean in 16-bit encodes into AVI at 6fps on 1080p content. It's a bit slow for a prefilter unless it gives very clear benefits.

Burdafel, you don't want to convert back to 8-bit at the end if the source isn't 8-bit.

burfadel
22nd August 2017, 00:00
I did adjust the noise alteration, do you prefer the new one or old one? I was thinking about the going back to 8 bits, how do you detect the source bit depth so you can go back to it?

MysteryX
22nd August 2017, 00:23
how do you detect the source bit depth so you can go back to it?
BitPerComponent

burfadel
22nd August 2017, 00:39
The non-detail spatial noise reduction has also changed, it's now removegrain (21), was 17. Any of 2, 12, 13, 14, 17, 21 could be suitable due to how it's applied, it's figuring which one is more suitable. I can continue to adjust the renoise feature as well before going on to the next feature.

MysteryX
22nd August 2017, 02:30
I did adjust the noise alteration, do you prefer the new one or old one?
Honestly, the old one gives a more natural feel.

MysteryX
22nd August 2017, 05:43
Something else I'm thinking about. Adjusting denoising strength with THSAD doesn't actually change the strength, but rather the width of areas being affected. It's a ON/OFF denoiser where you only select where to draw the line. Renoise allows for this subtle adjustment of strength, on top of allowing for stronger denoising than would normally be acceptable. It thus has at least 2 benefits.

You can also test whether converting Luma to Linear Light makes any difference.

burfadel
22nd August 2017, 11:21
That true about THSAD :).

I've made test script with two new options for the testing only. To set the renoise variation there's options ver=1 through to ver=6. There is also spatial noise reduction option 'sn' that allows to specify any specific Removegrain mode, default 21, but 2, 4, 7, 9, 10, 17, 18 may also be worth testing. The different 'ver' settings will affect not only still images but how they look in motion, which can make it hard to tell. The intent is to have it look good in both still images and motion, whilst not causing excessive bitrate increase. Test script is YV12/YUY2/RGB32 only.

Separate question, how do you specify a script to return back to the original colour space if conversion occurs during the script? I know you can find the pixel type with the pixteltype() function, but there doesn't seem to be any way to actually use that information? Basically most filters require for instance, a planar format like YV12 or YV24. Ideally if in YV12 it can stay in YV12 (fine), what about RGB32 input? You would convert to YV24 which will (mostly) keep the chroma resolution, and convert back again, but how do you tell the script to automatically convert back to RGB32 or whatever the source was in, without guessing? Sure, you can get the pixeltype, and run the 'if' operator for every colour type based on the pixel format, but that's a considerable number of lines!

Test script for renoise variations and spatial clean type.

Note: Function mCleanT
# mClean Test Scipt
# Not for any other use than to test Renoise variations
# Function is mCleanT, T for Test.

# TEST ONLY!!!
# ver (renoise type) range 1 to 6
# sn (spatial noise reduction) test recommended values 2, 4, 7, 9, 10, 17, 18, 21 (default)


function mCleanT(clip c, int "thSAD", int "blksize", int "blksizeV", int "overlap", int "overlapV", int "enh", int "rn", int "sn", int "ver", int "cpu")
{
defH = Max (C.Height, C.Width/4*3) # Resolution calculation for auto blksize settings
thSAD = Default (thSAD, 450) # Denoising threshold
blksize = Default (blksize, defH<360 ? 8 : defH<750 ? 12 : defH<1200 ? 16 : defH<1600 ? 24 : 32) # Horizontal block size for MDegrain2
blksizeV = Default (blksizeV, blksize) # Vertical block size for MDegrain2, default same as horizontal
overlap = Default (overlap, blksize>4?(blksize/4+1)/2*2:0) # Horizontal block overlap
overlapV = Default (overlapV, blksize>4?(blksizeV/4+1)/2*2:0) # Vertical block overlap
enh = Default (enh, 20) # Detail enhancement (detail orientated sharpen) strength
rn = Default (rn, 0) # ReNoise strength from 0 (disabled) to 20
sn = Default (sn, 21) # Spatial noise type *****For TESTING ONLY*****
ver = Default (ver, 0) # Renoise variation *****For TESTING ONLY*****
cpu = Default (cpu, 4) # Threads for fft3dfilter

Assert(enh>=0 && enh<=102, """mClean: "enh" ranges from 0 to 102""")
Assert(rn>=0 && rn<=20, """mClean: "rn" ranges from 0 to 20""")
bits = bitspercomponent(c)
c = convertbits(c, 16)

# Spatio/temporal chroma noise filter
filt_chroma = fft3dfilter (c, bw=blksize*2, bh=blksizeV*2, ow=overlap*2, oh=overlapV*2, sharpen=0.12, bt=3, ncpu=cpu, dehalo=0.3, sigma=2.35, plane=3)

# Temporal luma noise filter
super = c.MSuper (chroma=false,hpad=16, vpad=16)
bvec2 = MAnalyse (super, chroma=false, isb = true, delta = 2, blksize=blksize, blksizeV=blksizeV, overlap=overlap, overlapV=overlapV, search=5, searchparam=5)
bvec1 = MAnalyse (super, chroma=false, isb = true, delta = 1, blksize=blksize, blksizeV=blksizeV, overlap=overlap, overlapV=overlapV, search=5, searchparam=3)
fvec1 = MAnalyse (super, chroma=false, isb = false, delta = 1, blksize=blksize, blksizeV=blksizeV, overlap=overlap, overlapV=overlapV, search=5, searchparam=3)
fvec2 = MAnalyse (super, chroma=false, isb = false, delta = 2, blksize=blksize, blksizeV=blksizeV, overlap=overlap, overlapV=overlapV, search=5, searchparam=5)
clean = c.MDegrain2 (super, bvec1, fvec1, bvec2, fvec2, thSAD=thSAD, plane = 0)

# Masks for spatial noise reduction and noise independent detail enhancement
noised = mt_makediff (clean, c, u=1, v=1)
noise = mt_binarize (clense(mt_makediff(mt_binarize(noised, u=1, v=1), mt_edge(sharpen(clean, 0.85), "prewitt", u=1, v=1), u=1, v=1), grey=true), u=1, v=1)

# Spatial luma denoising
clean2 = mt_merge (clean, removegrain(clean, sn, modeU=-1, modeV=-1), noise, u=1, v=1)

# Unsharp filter for spatial detail enhancement
clsharp = (enh>0<=100) ? mt_adddiff (mt_makediff(clean, blur(clean, 0.80*(enh/100), 0.50*(enh/100)), u=1, v=1), clean2, u=1, v=1) : clean
clsharp = (enh>=101<=102) ? mt_adddiff (mt_makediff(clean, gblur(clean, enh-100), u=1, v=1), clean2, u=1, v=1) : clsharp

# If selected, combining ReNoise
renoise = (ver==1) ? tweak(clense (blur(noised,1), grey=true), cont=1.010+(0.020*(rn/20)), bright=1.01+(0.04*(rn/20))) : nop
renoise = (ver==2) ? tweak(clense (noised, grey=true), cont=1.010+(0.020*(rn/20)), bright=1.01+(0.04*(rn/20))) : renoise
renoise = (ver==3) ? tweak(temporalsoften (noised, 3, 128, 0, scenechange=0, mode=2), cont=1.010+(0.020*(rn/20)), bright=1.01+(0.04*(rn/20))) : renoise
renoise = (ver==4) ? tweak(temporalsoften (noised, 4, 128, 0, scenechange=0, mode=2), cont=1.010+(0.020*(rn/20)), bright=1.01+(0.04*(rn/20))) : renoise
renoise = (ver==5) ? blur(tweak(temporalsoften (noised, 3, 128, 0, scenechange=0, mode=2), cont=1.010+(0.020*(rn/20)), bright=1.01+(0.04*(rn/20))), 1) : renoise
renoise = (ver==6) ? blur(tweak(temporalsoften (noised, 4, 128, 0, scenechange=0, mode=2), cont=1.010+(0.020*(rn/20)), bright=1.01+(0.04*(rn/20))), 1) : renoise


clean2 = (rn>0<=20) ? mergeluma (clean2, mt_adddiff(clean2, renoise, u=1, v=1), 0.3+(rn*0.035)) : clean2

# Combining spatial detail enhancement with spatial noise reduction using prepared mask
filt_luma = mt_merge (clean2, clsharp, mt_invert(mt_convolution(noise, u=1, v=1), u=1, v=1), u=1, v=1)

# Combining result of luma and chroma cleaning
mergechroma (filt_luma, filt_chroma)

return last.convertbits(bits, dither=1)
}

Renoise 'rn' is now much stronger as a minimum, seeing as it was far too weak in the lower numbers to actually be beneficial, and fractionally stronger effect at rn=20 than the previous rn=20. I can tweak that down slightly as well if found to be necessary!

EDIT: Just changed the noise again for testing :).

MysteryX
22nd August 2017, 19:13
Sure, you can get the pixeltype, and run the 'if' operator for every colour type based on the pixel format, but that's a considerable number of lines!
That's the way to do it. One line to store PixelType, and one line for conditional conversion back with ? and :

MysteryX
24th August 2017, 19:41
mClean on its own doesn't work with AVS+ MT. Prefetch(2) works, Prefetch(4) crashes after a while and Prefetch(8) freezes on startup. (with 16-bit processing)

Even without MT, it doesn't work as a prefilter for FrameRateConverter, for some strange reason.

I was thinking of using Avisynth Virtual File System to feed the prefilter into the FrameRateConverter script, but AVFS doesn't work with Windows 10 x64, the folder C:\volumes stays empty.

So many bugs!!

As it stands, mClean isn't fast. It's too heavy to use as a 1080p prefilter.

MysteryX
24th August 2017, 20:58
Just how necessary is 16-bit processing? Here are some comparison images (not using the latest version but the other one before)

Video: AOA - Like a Cat (https://www.youtube.com/watch?v=qEYOyZVWlzs)

- Original
- mClean(rn=12) # 8-bit
- ConvertBits(16).mClean(rn=12).ConvertBits(8, dither=0)
- KNLMeansCL(D=2, A=2, h=1.4, device_type="GPU")
- ConvertToYV24().KNLMeansCL(D=2, A=2, h=1.4, device_type="GPU", channels="YUV")

https://s2.postimg.org/59kb8hgb9/Original.png (http://postimg.org/image/59kb8hgb9/) https://s2.postimg.org/i4ww8zkyd/m_Clean8.png (https://postimg.org/image/i4ww8zkyd/) https://s2.postimg.org/5c8s929cl/m_Clean16.png (http://postimg.org/image/5c8s929cl/) https://s2.postimg.org/3ryuwxbkl/KNL12.png (http://postimg.org/image/3ryuwxbkl/) https://s2.postimg.org/m8t9nqrit/KNL24.png (http://postimg.org/image/m8t9nqrit/)

KNLMeansCL tends to give a plastic effect when applied on YUV planes. There is considerable difference between mClean 8 or 16 but it's subtle details.

ff3dfilter is simply applied on chroma plane and then merged back, it doesn't need to be convert to 16-bit. I did a test converting to 16-bit AFTER MDegrain2 and then converting back to 8-bit when merging luma and chroma. This will be considerably faster. Quality is good.
https://s2.postimg.org/6tu8kme39/m_Clean_Conv.png (https://postimg.org/image/6tu8kme39/)

As for bit conversions, only convert to 16-bit and back if source is 8-bit

This modified version still crashes with Prefetch(8) saying "out of memory" on startup, so there's a bug that's not in MvTools2. I still can't use it as a prefilter with FRC.

Since your method works only on Luma and applies a simple FF3DFilter on chroma, it would be fair to compare with other filters with FF3DFilter on chroma.

KnlMeansCL with FF3DFilter. This one doesn't look very good.
https://s2.postimg.org/flag6dc5x/KNL-ff3d.png (http://postimg.org/image/flag6dc5x/)

With the changes converting to 16-bit only after MDegrain2, performance is good enough. Then there can be an option whether to dither back to 8-bit or stay in 16-bit. Since this runs at the beginning of the script, it makes sense to take a 8-bit input with 16-bit output.

FPS (min | max | average): 2.348 | 70862 | 9.560
Memory usage (phys | virt): 1264 | 1381 MiB
Thread count: 34
CPU usage (average): 49%

Of the various denoiser I'm testing, I still like this one better, although it's not finished.

burfadel
25th August 2017, 01:04
I'll update the script tonight with that info (9.30 am here), thanks! Do you have a preference in terms of the renoise 'version' or spatial removegrain method 'sn' from the test script? There are very subtle differences.

MysteryX
25th August 2017, 01:24
As it is, it takes 15 minutes to encode the prefilter as lossless AVI using half the CPU (Prefetch(4)), which is very decent if I want to run a 3h encode with FRC Preset="slower". I just haven't found any way to run them both in the same script. Importing one script into the other doesn't work. AVFS doesn't work. Heck, MP_Pipeline also gives MRecalculate: wrong pixel type!!

The only kind of separation that allows it to work is to write to an interim AVI file.

Opening the AVI file with AviSource gives again "MRecalculate: wrong pixel type", but instead using LWLibavVideoSource works!?? This makes no sense

MysteryX
25th August 2017, 02:05
I haven't tested those various versions. Right now I'm still testing your previous version. Subtle improvements over RemoveGrain but just slightly too sharp with default settings.

It would be a lot easier to test various settings if I could try different mClean settings without having to encode into an interim file.

but the fact that AviSource still gives the error means there's something obvious we're missing.

MysteryX
25th August 2017, 05:19
ok, let's do some tests. First off, enh=20 is too strong. Without it it looks flat. I'd use enh=12.

Ver=1 to 6
https://s2.postimg.org/izx808wyd/Renoise1.png (http://postimg.org/image/izx808wyd/) https://s2.postimg.org/6zbrzipjp/Renoise2.png (http://postimg.org/image/6zbrzipjp/) https://s2.postimg.org/rxhxxlped/Renoise3.png (http://postimg.org/image/rxhxxlped/) https://s2.postimg.org/a8q75zdn9/Renoise4.png (http://postimg.org/image/a8q75zdn9/) https://s2.postimg.org/ne5pc37it/Renoise5.png (http://postimg.org/image/ne5pc37it/) https://s2.postimg.org/aznz8vmc5/Renoise6.png (http://postimg.org/image/aznz8vmc5/)

Difference is too minor to see. I'd rather look at how it affects interpolation and encoding; but for that, I'd need to find a way to run both in the same script.

I can see the difference at 200% zoom.

1 is good.
2 is blurry.
3 is plastic.
4 is good, slightly better than 1.
5 is good but gives a slight plastic effect.
6 is good but gives a slight blur effect.

I'd go with 1 or 4.

sn
21 is good.
2 looks cheap.
4 looks good.
7 is very slightly blurrier than 4
9 is too sharp
10 is sharper but less than 9
17 is good, I like it.
18 is even better, my favorite.

This is my favorite (you can compare with ver=4 above for sn=21
MCleanT(rn=10, ver=4, sn=18, enh=12)
https://s2.postimg.org/klhjp6dhx/sn18.png (https://postimg.org/image/klhjp6dhx/)

burfadel
25th August 2017, 05:58
I liked 18 myself, which is hardly ever used, so good to know it wasn't placebo! The noise enh setting i think depends on resolution, I'll have to look into that more. It can look good on HQ 720p and 1080p to have a higher enh setting, even 101. Version 4 is what i liked as well, the version 5 and 6 were just variations in case it was too sharp.

MysteryX
26th August 2017, 04:47
Here are some comparison of FrameRateConverter with

RemoveGrain(21) vs mClean(rn=10, enh=12, ver=4, sn=18)

It has especially great benefits on hair textures. I also suspect it will help the encoder pick more of the right details.

https://s2.postimg.org/m6s838yc5/2008_Remove_Grain.png (http://postimg.org/image/m6s838yc5/) https://s2.postimg.org/w2tb2w445/2008m_Clean.png (http://postimg.org/image/w2tb2w445/)

https://s2.postimg.org/5bi51cyxh/2636_Remove_Grain.png (http://postimg.org/image/5bi51cyxh/) https://s2.postimg.org/ot7qk6pjp/2636m_Clean.png (http://postimg.org/image/ot7qk6pjp/)

https://s2.postimg.org/5lklnl785/3002_Remove_Grain.png (http://postimg.org/image/5lklnl785/) https://s2.postimg.org/ir03tp13p/3002m_Clean.png (http://postimg.org/image/ir03tp13p/)

https://s2.postimg.org/ohvc4jff9/3525_Remove_Grain.png (http://postimg.org/image/ohvc4jff9/) https://s2.postimg.org/l5rze4hcl/3525m_Clean.png (http://postimg.org/image/l5rze4hcl/)

I suspect there will be additional benefits after encoding, getting ready to do my first encode with it.

MysteryX
26th August 2017, 15:59
Encoding result

RemoveGrain(21) file size: 157,721,628
mClean(rn=10, enh=12, ver=4, sn=18) file size: 157,741,477
Nearly identical size with Q=23

https://s2.postimg.org/vsrb01cr9/3645_Remove_Grain.png (http://postimg.org/image/vsrb01cr9/) https://s2.postimg.org/q0r6sw2xh/3645m_Clean.png (http://postimg.org/image/q0r6sw2xh/)

https://s2.postimg.org/42upz3nx1/5245_Remove_Grain.png (http://postimg.org/image/42upz3nx1/) https://s2.postimg.org/c9mpqodzp/5245m_Clean.png (http://postimg.org/image/c9mpqodzp/)

Very considerable benefits!

burfadel
26th August 2017, 16:35
Yes, the difference is even more pronounced with some videos. For noisey sources the file size actually decreases :). I've basically done the next update to the script, just need to work out the input/output pixel type conversions. The 8 to 16 and back to 8 bit conversion is only done for the luma process. You were saying there is no benefit to convert to 16 bit when in 10 or 12 bit? Would it make more sense then to convert from 8 bit to 10 or 12 bit, seeing as it would more computationally friendly?

MysteryX
26th August 2017, 17:37
10, 12, 14 and 16-bit all use the same Integer data type. Perhaps the only difference is that 10-12 bit may not need clamping and may have slightly better performance (not sure).

In my case, if I'm not using it as a prefilter, I'll give a 8-bit input and want a 16-bit output for doing further processing. Someone else may want a different output type, so it would make sense to expose that as a parameter. If outbits = 8, convert to 16-bit and back to 8-bit. If outbits > 8, convert to outbits and don't dither back.

For conversion, remember ConvertToYUV444 and ConvertToYUV420 which converts without regards to the bit depth.

burfadel
26th August 2017, 18:51
That's a good point, the output bits option is extremely easy to implement. Basically like most filters those used in the script require planar formats, it's probably only necessary to convert from and to RGB formats. Thanks to the work PinterF has done with FFT3Dfilter, Avisynth, Masktools, and MVTools, as well as VCMohan for Modplus, basically every planar format is natively supported unlike older versions of these.

MysteryX
26th August 2017, 20:21
Not sure it's worth auto-converting from RGB. You work on Luma. No point in supporting RGB for this method. If someone still wants it, they can convert to YUV manually.

Although... in terms of net result, it may still benefit those working in RGB space, so why not.

MysteryX
27th August 2017, 07:08
Woah!! My video once encoded with FrameRateConverter Preset="slower" with mClean, it's a whole other experience!! Quality is absolutely amazing
https://mega.nz/#!iEYlTRxQ!tt2h-qmy6gPuoNuRnlil3XV5ugVMJCQPGSm1JOKGRWY

original video (https://www.youtube.com/watch?v=qEYOyZVWlzs)

lansing
27th August 2017, 09:34
There's no noise in the original video, and I just don't see the point of doubling the framerate of a 30fps video, as it was pretty smooth to begin with already.

burfadel
27th August 2017, 11:03
Updated the first post with version 1.3. Added outbits function, default is the same as the input bits. If input is 8 bits, converts to 12 bits for processing for improved quality, and outputs 8 bits unless otherwise specified. If outbits is specified, script processes in that depth unless outbits is specified as 8, in which case it is processed in 12 bits.

MysteryX
27th August 2017, 18:19
There's no noise in the original video, and I just don't see the point of doubling the framerate of a 30fps video, as it was pretty smooth to begin with already.
Original is standard 23.97fps, not 30fps -- like almost every video out there. There's not much noise yet this still benefits from improved prefilter.

I'll have to do tests on noisier videos.

burfadel
27th August 2017, 20:34
Does the latest Avisynth+ build support Avisynth output in high bit depths, or is it only for processing? If I set outbits=10 such as the colour format is YUV420P10, avisynth returns an empty clip error. If I convert back to YV12 YUV420P8 it works fine. Of couse, I'm assuming 10-bit x265 (for example) can have a 10-bit input. As long as the filters support it, I would think 10-bit output along the whole chain to the encoder would be preferential over first dithering back to 8 bit?

DJATOM
27th August 2017, 20:59
burfadel
Avs+ can pass HBD clips to your software, it's only matter of that software to support desired format. For example, it's possible to write 10-bit y4m file with proper header using avs+ and my avs2yuv mod. If you want to preview HBD clip, your software for previewing avs scripts should support it.
As I know, you only can feed y4m to x265, so avs2yuv script.avs -o - | x265 --params - should work for you.

MysteryX
27th August 2017, 22:13
I'm using avs2yuv with x264 and with ffmpeg without problem

I just remembered one point: x264-10bit encodes better than x264-8bit even on 8-bit content. If I'm going to re-encode 1080p videos into H264, I might consider using x264-10bit again.

lansing
28th August 2017, 02:15
Original is standard 23.97fps, not 30fps -- like almost every video out there. There's not much noise yet this still benefits from improved prefilter.

I'll have to do tests on noisier videos.

I just found the time to read through the entire thread, your feedback are just nonsense. How on earth are you commenting/comparing on the effect of denoising filters with a sample video that has absolutely no noise at all? What is there to compare?

Like people on the thread pointed out earlier, the OP's script is nothing more than a SMDegrain + removegrain. With SMDegrain, you are bound to have visual differences(artifacts) to the original on fast moving scenes, that's just the nature of the filter, there's no way to fix it. And comparing removegrain to KNLMeanCL on spatial denoising is also pointless because KNLMeansCL is going to win every single time.

Your usage of the script as a prefilter of your own filter is also out of context of what the OP originally intended. And for your filter, using a motion compensated denoise clip as a prefilter for another motion compensation process is just weird (and you wondered why it's so slow?). The point of a prefilter before the whole mc process is to have a clip that's without noise so that you can have better motion vector search for the mc filter later, you don't need a bundled script that's doing 10 different things at once, all you need is something that can remove noise. In this case, the OP's script is no better than a fft3dfilter.

MysteryX
28th August 2017, 02:38
How about comparison images to support your points?

I'll do tests on noisier videos later.

manolito
28th August 2017, 03:31
The point of a prefilter before the whole mc process is to have a clip that's without noise so that you can have better motion vector search for the mc filter later,


This is exactly the point I would like to make (or maybe someone can correct me about how FrameRateConverter really works).

To me it was never quite clear how MClean together with FrameRateConverter is used. Do you use it BEFORE FrameRateConverter (having no prefilter in FRC), or do you use it WITHIN FrameRateConverter by defining MClean as a prefilter in FRC ?

I was under the impression so far that you use MClean WITHIN FRC. But if this is the case then I do not understand how you can say that the output quality is improved dramatically by using MClean as a FRC prefilter.

As lansing states, if you use a prefilter WITHIN FRC, the prefiltered clip will not show up in the result at all. The prefiltered clip is only used as input for MSuper and MAnalyze (which calculates motion vector data). The final clip is created with this command:
Flow = MFlowFps(C, super, bak, fwd, num=NewNum, den=NewDen, blend=false, ml=200, mask=2, thSCD2=255)
The "super" param does not have any prefiltered content in it. Only "bak" and "fwd" are based on the prefiltered clip, and these two only have motion vector data in them.

I did reread the MVTools doc and I also scrutinized the FrameRateConverter.avsi before I came to this conclusion. Please correct me if I misunderstood something here...


Cheers
manolito

MysteryX
28th August 2017, 06:35
That is how I tested it and as you can see with the sample images, it made a difference.

Applying it "before" degraded the quality in this test video.

burfadel
28th August 2017, 06:39
Like people on the thread pointed out earlier, the OP's script is nothing more than a SMDegrain + removegrain. With SMDegrain, you are bound to have visual differences(artifacts) to the original on fast moving scenes, that's just the nature of the filter, there's no way to fix it. And comparing removegrain to KNLMeanCL on spatial denoising is also pointless because KNLMeansCL is going to win every single time.

An issue with spatial noise is the filters ability to distinguish actual noise from detail. KNLMeansCL does provide strong denoising, but it also destroys fine detail. Check for yourself :). The same applies for any spatial denoiser that is applied to the whole image. If you apply KNLMeansCL or other denoiser to a noisey image, the result looks goood because you are comparing it to the noisey source. If you run them on an already clean source the detail detruction become more obvious. Another aspect I wanted for my script is something that runs relatively fast but still produces results. You might be thinking that running a denoiser on a relatively clean source is a bad thing, and in general, it is! What if your video contains scenes which are noiser than others? Are you going to manually go through and splice together the video with strong denoising on one clip, weak on another, and maybe even none on some of them in order to not lose the extra detail?

For those thinking it's just SMDegrain plus Removegrain, have you looked at the scripts? Yes, both use MDegrain which is NOT unique to SMDegrain. If it's a simple as that you might as well not even use SMDegrain and use MDegrain directly ;).

My script uses MDegrain for luma, and fft3dfilter for chroma. MDegrain does a great job on luma but not so much on chroma. fft3dfilter does a great job on chroma, but the result is too soft for luma, hence the combination. A mask is created that coincides with what we see visually, and is temporally stabilised. These areas have an unsharp mark of selectable strength applied to it (enh setting). Sure, you can apply a sharpening filter separately, however they are typically applied to the whole image, which amplifies everything in the image and not just areas of detail. For enhancement you can choose a higher enh setting, as this settings depends on clarity of the source. Areas that aren't these have removegrain applied to it. These areas also have a component of the original noise readded, however this noise is heavily modified and temporally stabilised. It helps very much with not blowing out the file size, whilst making the image not appear flat that denoisers tend to do. What do people do to overcome this flatness normally? Add grain!

In any case, it's another option that people can try that doesn't have a dozen settings there to confuse people, with the additional benefit of being relatively fast and not unintentionally destroy detail. It negates the need for filters like lsfmod (it's not the same as lsfmod and does not use its function, SMDegrain does as an option) etc with the use of enh (or a higher setting of), and can be used on basically any source as is, with maybe fine adjustment of enh for preference and rn for really noisey sources.

BTW MDegrain is a core function of MVtools, added back in version 1.4.0.0, dated 19/06/2006. Originally it was called MVDegrain.

All scripts just use existing filters in different ways. If the argument that it's all the same is true as people claim, then they're claiming literally every avisynth script is pointless because you can just call the command directly. In any case, nobody is forcing you to use any particular filter or script, it's just providing another option that does things differently that you can use based on your preferences :).

lansing
28th August 2017, 09:38
That is how I tested it and as you can see with the sample images, it made a difference.

Applying it "before" degraded the quality in this test video.

Are you talking about this post (https://forum.doom9.org/showthread.php?p=1815612#post1815612)?

You know why the difference? Because your filter is NOT doing any denoising, how do you write a filter when you don't know what it is doing?

lansing
28th August 2017, 11:24
An issue with spatial noise is the filters ability to distinguish actual noise from detail. KNLMeansCL does provide strong denoising, but it also destroys fine detail. Check for yourself :). The same applies for any spatial denoiser that is applied to the whole image. If you apply KNLMeansCL or other denoiser to a noisey image, the result looks goood because you are comparing it to the noisey source.

KNLMeansCL is a subtle denoiser, it can go strong only when you bump up the strength.


What if your video contains scenes which are noiser than others? Are you going to manually go through and splice together the video with strong denoising on one clip, weak on another, and maybe even none on some of them in order to not lose the extra detail?

What is the point of this? I don't see your script doing anything different on this either.

For those thinking it's just SMDegrain plus Removegrain, have you looked at the scripts? Yes, both use MDegrain which is NOT unique to SMDegrain. If it's a simple as that you might as well not even use SMDegrain and use MDegrain directly ;).

My script uses MDegrain for luma, and fft3dfilter for chroma. MDegrain does a great job on luma but not so much on chroma. fft3dfilter does a great job on chroma, but the result is too soft for luma, hence the combination. A mask is created that coincides with what we see visually, and is temporally stabilised. These areas have an unsharp mark of selectable strength applied to it (enh setting). Sure, you can apply a sharpening filter separately, however they are typically applied to the whole image, which amplifies everything in the image and not just areas of detail. For enhancement you can choose a higher enh setting, as this settings depends on clarity of the source. Areas that aren't these have removegrain applied to it. These areas also have a component of the original noise readded, however this noise is heavily modified and temporally stabilised. It helps very much with not blowing out the file size, whilst making the image not appear flat that denoisers tend to do. What do people do to overcome this flatness normally? Add grain!


The main core of denoising of your script are smdegrain and removegrain, smdegrain for temporal noise and removegrain for spatial noise. Nothing in your post processing is going to change that. Basically you're just taking 2 filters that everyone uses and said to provide a better result, which to me is an overstatement. And the quality of your denoise can only go what these 2 filters can go, along you are also bounded to the issues these 2 filters create. Using smdegrain for an objective to "retain as much detail as possible" does not make sense either, because the filter removes details on every motion scene. It will do even more harms on cleaner source.

burfadel
28th August 2017, 11:30
I think people are missing the point. I can assure you 100 percent it is NOT the core of SMDegrain, it's just the case the Luma part of the denoising in my script uses a similar method to SMDegrain from MVTools. The difference in any of these denoisers is how the denoising is handled, post-processing, and everything else. My intention is to keep things as simple as possible, and that includes the actual script. Some of the SMDegrain script could actually be trimmed back now with Avisynth+. mClean isn't finished either, although that doesn't mean the additional things I add will make it to a posted script version, it depends on how effective they are versus alternatives. As for KNLMeansCL, sure you can use a strong setting to remove more noise, but it removes details when achieving the same denoising amount.

On a clean, high resolution source you can go a higher enh like 80-100 or even 101, as well as a high RN, which can actually enhance the image. That high of enh setting is quite strong sharpening though :). There is still a little modification I can do for these setings, but it's only minor and I may not even apply it :). Chroma is the next thing I'll be looking at.

Remember the RN setting adds back cleaned, altered, and temporally stabilised versions of the existing noise on non high detail areas. enh applies a termporally stabilised unsharp mask on detailed areas. These two things do separate it from other filters. Again, if you claim my script is no different to SMDegrain then that's kind of a compliment, since SMDegrain is much, much longer script and runs slower when trying to apply things like the sharpening etc :). The separate chroma denoiser is necessary, in the SMDegrain description is even states that it can smear chroma. FFT3dFilter does a better job of chroma filtering. If you want to update SMDegrain to make it simpler (which can be done with Avisynth) go ahead :).

MysteryX
28th August 2017, 15:18
lansing, I'll repeat a similar comment I made to someone else before. If you're just here to tear things apart, you're wasting your time.

If you don't find personal use for this, then just let us do our work.

If you have constructive feedback on how to make things better, of can pin-point flaws, then we're all ears. Just try to keep a constructive attitude instead of trying to tear things down.

I posted comparison of mClean vs KNLMeans here (https://forum.doom9.org/showthread.php?p=1816190#post1816190). Sure, most of the image doesn't have much noise but the leather and some of the textures have. It's actually a good test for detail preservation: a mix of no-noise with light-noise.

lansing
28th August 2017, 18:26
lansing, I'll repeat a similar comment I made to someone else before. If you're just here to tear things apart, you're wasting your time.

If you don't find personal use for this, then just let us do our work.

If you have constructive feedback on how to make things better, of can pin-point flaws, then we're all ears. Just try to keep a constructive attitude instead of trying to tear things down.

I posted comparison of mClean vs KNLMeans here (https://forum.doom9.org/showthread.php?p=1816190#post1816190). Sure, most of the image doesn't have much noise but the leather and some of the textures have. It's actually a good test for detail preservation: a mix of no-noise with light-noise.

I'm commenting because you are misleading the OP by giving him false feedback. You are the one that's hijacking his post by using his script on your own filter, when you were told multiple times not to.

Your link about the comparison between OP's script and KNLMeansCL is one example of your false feedback. Your screenshots clearly showed that KNLMeansCL is better, it's removing noise while really not touching anything on the image. On the other hand, mClean, having smdegrain being the core, is exerting the flaws of smdegrain as expected, that is removing details on motion areas, the problem was clearly shown on the losing details of the leather jacket. And yet in your conclusion of the post, you talked as if mClean came out to be the better one, which is completely wrong.

MysteryX
28th August 2017, 18:26
I did some tests on a noisier source, only mClean to start with.

Source: KARA - Jumping (https://www.youtube.com/watch?v=SweXhOQPMdM)

Here I get better results with higher thSAD such as 550. Enh=20 is unwatcheable. I again get best results with enh=12, rn=10. Because of the renoise, I think it's safer to go with higher thSAD.

Screenshots:
- Original
- RemoveGrain(21)
- KnlMeansCL(D=2, A=2, h=1.4, channels="YUV")
- mClean(550, enh=12, rn=10)
- mClean(450, enh=20, rn=12)


https://s2.postimg.org/x71tl7yz9/2026_original.png (http://postimg.org/image/x71tl7yz9/) https://s2.postimg.org/nns4rrbh1/2026_removegrain.png (http://postimg.org/image/nns4rrbh1/) https://s2.postimg.org/xfzeb5brp/2026_knlmeans.png (http://postimg.org/image/xfzeb5brp/) https://s2.postimg.org/43dlozavp/2026_mclean12.png (http://postimg.org/image/43dlozavp/) https://s2.postimg.org/ry6sncgk5/2026_mclean20.png (http://postimg.org/image/ry6sncgk5/)

https://s2.postimg.org/log4e36j9/2395_original.png (http://postimg.org/image/log4e36j9/) https://s2.postimg.org/dufisoyqd/2395_removegrain.png (http://postimg.org/image/dufisoyqd/) https://s2.postimg.org/6d1pzqjth/2395_knlmeans.png (http://postimg.org/image/6d1pzqjth/) https://s2.postimg.org/ivmwu266t/2395_mclean12.png (http://postimg.org/image/ivmwu266t/) https://s2.postimg.org/zd0ptgrth/2395_mclean20.png (https://postimg.org/image/zd0ptgrth/)

burfadel
28th August 2017, 18:54
It makes sense about the lower settings, those sources are more low quality sources than grainey sources. Renoise only works with temporal noise removed (which is then altered), so it is obviously making changes if a different renoise value is required :). How does each look in motion though? mClean is designed to be temporally stabilised in the renoise and sharpening.

MysteryX
28th August 2017, 18:59
I'd need to encode both to see how it looks in motion

Taurus
28th August 2017, 19:58
@ burfadel:
1.:I really like your script!:thanks:
2.:Bad news.
Your first version (1.1) of the function is working flawless on my side.
The new versions (1.2+1.3) getting stucked at line 54 (outbits).
------------------------------------
Avisynth 2.60 MT
All necessary plugins uptodate.
YV12 8bit video.
-----------------------------------
Can you give me a hint for troubleshooting?

MysteryX
28th August 2017, 20:07
His script is designed for AVS+ only.

If you want to use with Avisynth 2.6, you need to remove ConvertBits, and you will only process in 8-bit, which will considerably degrade the results because of applying a series of actions each having a subtle effect. The rounding errors will add up.

lansing
29th August 2017, 03:19
I did some tests on a noisier source, only mClean to start with.

Source: KARA - Jumping (https://www.youtube.com/watch?v=SweXhOQPMdM)

Here I get better results with higher thSAD such as 550. Enh=20 is unwatcheable. I again get best results with enh=12, rn=10. Because of the renoise, I think it's safer to go with higher thSAD.

Screenshots:
- Original
- RemoveGrain(21)
- KnlMeansCL(D=2, A=2, h=1.4, channels="YUV")
- mClean(550, enh=12, rn=10)
- mClean(450, enh=20, rn=12)

I'm not seeing any difference in sharpness between the two mclean screenshots, heck I don't even see any difference between them as a whole, except that tiny bit of artifact variation around the fingers on the 2nd comparison. Saying that one is unwatchable compares to the other is just absurd.

MysteryX
29th August 2017, 03:32
I guess I developed a sense for the details.

MysteryX
29th August 2017, 03:51
Burdafel, you did the bitrate conversion wrong. FF3DFilter and SMDegrain were unnecessarily processing in HBD resulting in poor performance. Here I fixed your script.

Testing back my first video, thSAD is good at 450 (difference between 450 and 550 is extremely minimal). Here I've set enh=13 and rn=10, but it will take more tests on various sources. But so far, I wouldn't go higher on low-noise source, and sources with high noise can't take any more sharpening. The only case I can see where I'd set those higher are for videos that are a little blurry.


# mClean spatio/temporal denoiser
# Version: 1.3 (27 August 2017)
# By burfadel


# +++ Description +++
# This script is intended to remove noise whilst retaining as much detail as possible.
# Typical spatial filters work by removing large variations in the image on a small scale, reducing noise but also making the image less
# sharp or temporally stable.

# mClean works primarily in the temporal domain, although there is some spatial limiting.
# Chroma is processed via a different method to luma for optimal results.
# Input must be

# +++ Artifacts +++
# Spatial picture artifacts may remain as removing them is a fine balance between removing the unwanted artifact whilst not removing detail
# Additional dering/dehalo/deblock filters may be required, but should ONLY be uses if required due the detail lose/artifact removal balance
# A deblocking filter such as Deblock_QED can be applied after, as MClean currently does not deblock; this may be provided as an option later

# +++ Sharpening +++
# Additional sharpening filters may not be required, mClean does some light detail enhancement. Any additional sharpening filters may require
# a little less strength. Alternatively use a higher 'enh' setting. Range of normal sharpening is 0-100. There are two extra options, 101 and
# 102. These are for 'overboost' sharpening, suitable only for high quality, high resolution sources. Overboost sharpening requires the modplus
# plugin, this is only required if overboost is used.

# +++ ReNoise +++
# ReNoise adds back some of the removed luma noise. Re-adding original noise would be counterproductive, therefore ReNoise modifies this noise
# both spatially and temporally. The result of this modification is the noise becomes much nicer and it's impact on compressibility is greatly
# reduced. It is not applied on areas where the sharpening occurs as that would be counterproductive. Settings range from 1 to 20, default
# value is 12. The strength of renoise is affected by the the amount of original noise removed and how this noise varies between frames.

# +++ Outbits +++
# Specifies the bits per component (bpc) for the output for processing by additional filters. It will also be the bpc that mClean will process.
# By default, mClean processes as 12 bits if the input is 8 bit, and converts back to 8 bit. If the input is 10 bits or higher no conversion is
# done unless outbits is specified and is different to the input bpc. If you output at a higher bpc keep in mind that there may be limitations
# to what subsequent filters and the encoder may support.

# +++ Required plugins +++
# Latest RGTools, MVTools2, Masktools2, FFT3DFilter
# Latest Modplus, only required if using sharpening overboost (enh settings 101 and 102)
# Requires latest fftw.dll to be installed as instructed on the website - http://www.fftw.org


function mClean(clip c, int "thSAD", int "blksize", int "blksizeV", int "overlap", int "overlapV", int "enh", int "rn", int "outbits", int "cpu")
{
defH = Max (C.Height, C.Width/4*3) # Resolution calculation for auto blksize settings
thSAD = Default (thSAD, 450) # Denoising threshold
blksize = Default (blksize, defH<360 ? 8 : defH<750 ? 12 : defH<1200 ? 16 : defH<1600 ? 24 : 32) # Horizontal block size for MDegrain2
blksizeV = Default (blksizeV, blksize) # Vertical block size for MDegrain2, default same as horizontal
overlap = Default (overlap, blksize>4?(blksize/4+1)/2*2:0) # Horizontal block overlap
overlapV = Default (overlapV, blksize>4?(blksizeV/4+1)/2*2:0) # Vertical block overlap
enh = Default (enh, 13) # Detail enhancement (detail orientated sharpen) strength
rn = Default (rn, 10) # ReNoise strength from 0 (disabled) to 20
outbits = Default (outbits, c.BitsPerComponent) # Output bits, default input depth
calcbits = c.BitsPerComponent == 8 ? 12 : c.BitsPerComponent
calcbits = outbits > calcbits ? calcbits : outbits
cpu = Default (cpu, 4) # Threads for fft3dfilter


Assert(isYUV(c)==true, """mClean: Supports only YUV formats (YV12, YV16, YV24)""")
Assert(isYUY2(c)==false, """mClean: Supports only YUV formats (YV12, YV16, YV24)""")
Assert(isYV411(c)==false, """mClean: Supports only YUV formats (YV12, YV16, YV24)""")
Assert(enh>=0 && enh<=102, """mClean: "enh" ranges from 0 to 102""")
Assert(rn>=0 && rn<=20, """mClean: "rn" ranges from 0 to 20""")
Assert(outbits>=8 && outbits<=16, """mClean: "outbits" ranges from 8 to 16""")

# Spatio/temporal chroma noise filter
filt_chroma = fft3dfilter (c, bw=blksize*2, bh=blksizeV*2, ow=overlap*2, oh=overlapV*2, sharpen=0.12, bt=3, ncpu=cpu, dehalo=0.3, sigma=2.35, plane=3)

# Temporal luma noise filter
super = c.MSuper (chroma=false,hpad=16, vpad=16)
bvec2 = MAnalyse (super, chroma=false, isb = true, delta = 2, blksize=blksize, blksizeV=blksizeV, overlap=overlap, overlapV=overlapV, search=5, searchparam=5)
bvec1 = MAnalyse (super, chroma=false, isb = true, delta = 1, blksize=blksize, blksizeV=blksizeV, overlap=overlap, overlapV=overlapV, search=5, searchparam=3)
fvec1 = MAnalyse (super, chroma=false, isb = false, delta = 1, blksize=blksize, blksizeV=blksizeV, overlap=overlap, overlapV=overlapV, search=5, searchparam=3)
fvec2 = MAnalyse (super, chroma=false, isb = false, delta = 2, blksize=blksize, blksizeV=blksizeV, overlap=overlap, overlapV=overlapV, search=5, searchparam=5)
clean = c.MDegrain2 (super, bvec1, fvec1, bvec2, fvec2, thSAD=thSAD, plane = 0)
clean = calcbits != clean.BitsPerComponent ? clean.ConvertBits(calcbits) : clean
c = calcbits != c.BitsPerComponent ? c.ConvertBits(calcbits) : c

# Masks for spatial noise reduction and noise independent detail enhancement
noised = mt_makediff (clean, c, u=1, v=1)
noise = mt_binarize (clense(mt_makediff(mt_binarize(noised, u=1, v=1), mt_edge(sharpen(clean, 0.85), "prewitt", u=1, v=1), u=1, v=1), grey=true), u=1, v=1)

# Spatial luma denoising
clean2 = mt_merge (clean, removegrain(clean, 18, modeU=-1, modeV=-1), noise, u=1, v=1)

# Unsharp filter for spatial detail enhancement
clsharp = (enh>0<=100) ? mt_adddiff (mt_makediff(clean, blur(clean, 0.80*(enh/100), 0.50*(enh/100)), u=1, v=1), clean2, u=1, v=1) : clean
clsharp = (enh>=101<=102) ? mt_adddiff (mt_makediff(clean, gblur(clean, enh-100), u=1, v=1), clean2, u=1, v=1) : clsharp

# If selected, combining ReNoise
renoise = (rn==0) ? nop : tweak(temporalsoften (noised, 4, 128, 0, scenechange=0, mode=2), cont=1.010+(0.020*(rn/20)), bright=1.01+(0.04*(rn/20)))
clean2 = (rn>0<=20) ? mergeluma (clean2, mt_adddiff(clean2, renoise, u=1, v=1), 0.3+(rn*0.035)) : clean2

# Combining spatial detail enhancement with spatial noise reduction using prepared mask
filt_luma = mt_merge (clean2, clsharp, mt_invert(mt_convolution(noise, u=1, v=1), u=1, v=1), u=1, v=1)

# Converting bits per channel
filt_luma = outbits < filt_luma.BitsPerComponent ? ConvertBits(filt_luma, outbits, 1) : filt_luma

# Combining result of luma and chroma cleaning
return mergechroma (filt_luma, filt_chroma)
}

lansing
29th August 2017, 03:56
I guess I developed a sense for the details.

I literally grab the two mclean screenshots from the first comparison, stacked them into photoshop, set layer overlay to "difference", and I got a complete black image. You need an eye checkup.

MysteryX
29th August 2017, 03:57
The "super" param does not have any prefiltered content in it. Only "bak" and "fwd" are based on the prefiltered clip, and these two only have motion vector data in them.
You have a good point. It produces more clearly-defined motion vectors ... but how does it result in clearer hair textures?

The added grain won't make it to the output to benefit the encoder. This explains why I saw improvement in quality but no difference in file size.

MysteryX
29th August 2017, 04:08
I literally grab the two mclean screenshots from the first comparison, stacked them into photoshop, set layer overlay to "difference", and I got a complete black image. You need an eye checkup.
I checked back with the script to make sure I have the right images.

Oddly enough, first screenshot has 0 difference between the 2 settings.

2nd screenshot however has considerable difference and doesn't look good with enh=20

I was about to upload 3 screenshots but the 3rd has files missing after upload, perhaps because postimage detected some images as being exactly the same.

MysteryX
29th August 2017, 04:26
Here are some tests on another source that definitely has more noise

KARA - Dazzling Red (https://www.youtube.com/watch?v=YOnAVn6iRf8)

- Original
- RemoveGrain(21)
- KnlMeansCL(D=2, A=2, h=1.8, channels="YUV") # raised from 1.4 to 1.8 here
- mClean(450, enh=12, rn=10)
- mClean(450, enh=20, rn=12)

https://s26.postimg.org/hk1176zd1/1917_original.png (http://postimg.org/image/hk1176zd1/) https://s26.postimg.org/uvu8ge2dh/1917_removegrain.png (http://postimg.org/image/uvu8ge2dh/) https://s26.postimg.org/985a5y1z9/1917_knlmeans.png (http://postimg.org/image/985a5y1z9/) https://s26.postimg.org/9ap5ss5mt/1917_mclean12.png (http://postimg.org/image/9ap5ss5mt/) https://s26.postimg.org/6lprp0arp/1917_mclean20.png (http://postimg.org/image/6lprp0arp/)

https://s26.postimg.org/alwx1918l/4528_original.png (http://postimg.org/image/alwx1918l/) https://s26.postimg.org/3u6hyec91/4528_removegrain.png (http://postimg.org/image/3u6hyec91/) https://s26.postimg.org/6hvy8r5ad/4528_knlmeans.png (http://postimg.org/image/6hvy8r5ad/) https://s26.postimg.org/pbhr5r3id/4528_mclean12.png (http://postimg.org/image/pbhr5r3id/) https://s26.postimg.org/k3wo1amx1/4528_mclean20.png (http://postimg.org/image/k3wo1amx1/)

Here we see more clearly the characteristics of each method. RemoveGrain does little, KNLMeans blurs out details, and mClean does a pretty good job in both cases.

manolito
29th August 2017, 06:54
You have a good point. It produces more clearly-defined motion vectors ... but how does it result in clearer hair textures?

My next question would be:
If you do get better results with mClean instead of RemoveGrain, then what is the major difference between the two?
mClean works primarily in the temporal domain, although there is some spatial limiting.

RemoveGrain is purely spatial, so the next test I would do is replacing RemoveGrain with a (fast) purely temporal denoiser like TTempSmooth (or the even faster TTempSmoothF) as the prefilter in FRC. This should be way faster than using mClean, and maybe you will get more precise motion vectors compared to RemoveGrain, too.


Cheers
manolito

lansing
29th August 2017, 07:37
Here are some tests on another source that definitely has more noise

KARA - Dazzling Red (https://www.youtube.com/watch?v=YOnAVn6iRf8)

- Original
- RemoveGrain(21)
- KnlMeansCL(D=2, A=2, h=1.8, channels="YUV") # raised from 1.4 to 1.8 here
- mClean(450, enh=12, rn=10)
- mClean(450, enh=20, rn=12)


Here we see more clearly the characteristics of each method. RemoveGrain does little, KNLMeans blurs out details, and mClean does a pretty good job in both cases.

What? How on earth did you come up with that conclusion?

First of all, your video has no grain, so the smdegrain inside mclean is basically doing nothing except creating artifacts on motion objects by its nature, you can see the artifacts in the fog which doesn't exist in the original. Then what you have left in the script is a simple removegrain(18). So right now you're basically comparing a removegrain(21) against KNLMeansCL and a removegrain(18), and you're saying removegrain(18) wins, I am speechless.

Oddly enough, first screenshot has 0 difference between the 2 settings.
There's nothing odd about it because we can visually SEE it ourselves that there're no difference. The only one that insisted that one is better than the other is you.

I have a feeling that you're just trolling the OP with all these nonsense.

burfadel
29th August 2017, 08:31
Burdafel, you did the bitrate conversion wrong. FF3DFilter and SMDegrain were unnecessarily processing in HBD resulting in poor performance. Here I fixed your script.

Testing back my first video, thSAD is good at 450 (difference between 450 and 550 is extremely minimal). Here I've set enh=13 and rn=10, but it will take more tests on various sources. But so far, I wouldn't go higher on low-noise source, and sources with high noise can't take any more sharpening. The only case I can see where I'd set those higher are for videos that are a little blurry.


Thanks, I've updated the first post :). I also added in a line for filt_chroma, seeing as it needs to be the same bit depth for merging. There will be adjustments with that later as I update the chroma filtering.

Do you think there should be an auto adjustment for default enh and rn values based on resolution like there is for blocksize? Hopefully people won't have too many low quality 720P and 1080P videos.

MysteryX
29th August 2017, 17:30
First of all, your video has no grain
There's a big difference between a denoiser and a degrainer. I'm testing this as a denoiser. Noise is generally encoding artifacts. Grain is something else.

Do you think there should be an auto adjustment for default enh and rn values based on resolution like there is for blocksize? Hopefully people won't have too many low quality 720P and 1080P videos.
What kind of auto-adjustment are you thinking about? So far I tested enh=13, rn=0 for 288p content and enh=13, rn=10 for 1080p content.

Here's my feedback/observations so far

1. I like the results of this script

2. When it comes to using it as a prefilter for FRC, it does bring some benefits: better motion vectors

3. The re-added grain to help the encoder isn't one of those benefits

When it comes to FRC with preset="slower", the performance of mClean isn't really an issue, but perhaps not all parts of it are necessary in that case, it has to be tested. Does renoise contribute to better motion vectors at all? My suspicion is that motion vectors are better defined for having more clearly-defined edges.

The main issue is that it's hard to do each test because it can't yet be run in the same script.

burfadel
29th August 2017, 17:40
Yes, renoise probably would affect further analysis. Ideally in that case you would interpolate the renoise and add it back after framerateconverter.

MysteryX
29th August 2017, 17:52
Does it really make a difference on textures or are my eyes bad? Had to test for sure. I took an image of a full head and compared RemoveGrain(21) with mClean as a FRC prefilter. Then I put it in Photoshop, do a diff, use magic wand to select all with tolerence set to 0, 1 and 2.

There *IS* a difference. Not sure I can explain it though. This is a mostly static scene, differences are larger on high-animation scenes.

https://s26.postimg.org/eksslhnk5/Tol0.png (http://postimg.org/image/eksslhnk5/) https://s26.postimg.org/bf86va4xx/Tol1.png (http://postimg.org/image/bf86va4xx/) https://s26.postimg.org/jm06muv0l/Tol2.png (http://postimg.org/image/jm06muv0l/)

MysteryX
29th August 2017, 18:06
This test shows clear differences

- No prefilter
- RemoveGrain(18)
- RemoveGrain(21)
- TTempSmooth
- mClean(rn=10)
- mClean(rn=0)

https://s26.postimg.org/c65ieswp1/3728_original.png (http://postimg.org/image/c65ieswp1/) https://s26.postimg.org/4rg6mfath/3728_removegrain18.png (http://postimg.org/image/4rg6mfath/) https://s26.postimg.org/u7onclqpx/3827_removegrain21.png (http://postimg.org/image/u7onclqpx/) https://s26.postimg.org/dnqyqd1fp/3728_ttemp.png (http://postimg.org/image/dnqyqd1fp/) https://s26.postimg.org/5d55ij5vp/3728_mclean.png (http://postimg.org/image/5d55ij5vp/) https://s26.postimg.org/uq26bf9tx/3728_mclean0.png (https://postimg.org/image/uq26bf9tx/)

TempSmooth gives no gain and in fact degrades picture. mClean wins. RemoveGrain on its own... 21 is more balance than 18, but within mClean 18 works.

Yes, renoise helps.

You can't "add things back" after interpolation because source and destination frames count don't match

I have a feeling that you're just trolling the OP with all these nonsense.
I think I've settled the case.

burfadel
29th August 2017, 19:30
You can't "add things back" after interpolation because source and destination frames count don't match.

That's true, I didn't explain what I meant very well, but thinking of what I wrote it woudn't make sense anyway :).

lansing
29th August 2017, 21:50
Does it really make a difference on textures or are my eyes bad? Had to test for sure. I took an image of a full head and compared RemoveGrain(21) with mClean as a FRC prefilter. Then I put it in Photoshop, do a diff, use magic wand to select all with tolerence set to 0, 1 and 2.

There *IS* a difference. Not sure I can explain it though. This is a mostly static scene, differences are larger on high-animation scenes.


Dude, you'll have to pull that FRC thing of yours out of your arse, because you're confusing the f out of everybody.

with mClean as a FRC prefilter
What does this even mean?

This test shows clear differences

- Original
- RemoveGrain(18)
- RemoveGrain(21)
- TTempSmooth
- mClean(rn=10)
- mClean(rn=0)

...

I think I've settled the case.

Your test is a fake, the first screenshot is not the original, that's the result of you putting the script inside your FRC thing, which does NOTHING on denoising. You faked the results.

You're not helping the OP in any way, such that you tested his denoising script on videos that have no noise at all, you gave him false feedback on setting comparison, you exaggerated invisible differences that photoshop can't even see, and you hijacked his thread and forced his script onto your own little filter, which in itself is a big confusing mess that doesn't make sense.

MysteryX
29th August 2017, 23:52
Watch your language. If you don't like what we're doing, just get the heck out.


that's the result of you putting the script inside your FRC thing

Yes that's what I meant by original, perhaps I should have called it "no prefilter", but I think anyone with a brain understood that. I can't compare with original because generated frames don't match source frames.

Please, if you got nothing constructive to say, shut up.

Edit: I renamed it to "no prefilter" to avoid confusing anyone else

If someone else views any screenshot other than mClean(rn=10) to give better results, I'm listening, but please have something to back up what you say. This test is pretty simple: comparing 6 versions of a script to see which gives better results. I don't care about how "things should be done", I only care about concrete results.

StainlessS
29th August 2017, 23:57
Guys, please be nice.
Both of you are valued members of the community and both totally respected, if something aint working quite right, then
maybe in time it will, it dont need no negs.
Consider this a good slap on the arse for both :)

(Soon be Christmas :) )

MysteryX
30th August 2017, 00:12
Something I've noticed around here is that whenever I'm working on something, there are always some members not just disagreeing but aggressively trying to destroy it.

I worked on SuperRes to improve the upscaling quality and many were saying it was impossible and that it was impossible to get better results than NNEDI3. Turns out it's now the best upscaling method available in Avisynth.

I worked on FrameRateConverter and people were saying it was impossible to accurately detect artifacts to give an output that works most of the time, and that I shouldn't waste any time even trying. I did it and it's working pretty well.

Now I'm hearing the same thing all over again with this. I'm getting used to the pattern. Yes, I know it's impossible. Yes, I know it doesn't make sense based on old concepts. We're doing it anyway. Because we don't operate from the same mental container. I think me and Burdafel have this in common. We don't think outside the box. We just didn't know of the box to begin with. That's more the space we should be thinking from.

MysteryX
30th August 2017, 02:28
Getting back on topic, what I'm seeing is that with MvTools, the vector motion data is a lot more important than I thought. Tiny changes to motion vectors data can result in very different results. To the point where RemoveGrain(18) and RemoveGrain(21) produce a considerably different output -- which I know, doesn't make logical sense.

lansing
30th August 2017, 04:10
Watch your language. If you don't like what we're doing, just get the heck out.


Yes that's what I meant by original, perhaps I should have called it "no prefilter", but I think anyone with a brain understood that. I can't compare with original because generated frames don't match source frames.

Please, if you got nothing constructive to say, shut up.

Edit: I renamed it to "no prefilter" to avoid confusing anyone else

If someone else views any screenshot other than mClean(rn=10) to give better results, I'm listening, but please have something to back up what you say. This test is pretty simple: comparing 6 versions of a script to see which gives better results. I don't care about how "things should be done", I only care about concrete results.
What in the blue hell does that have to do with this thread?

Since when does this thread gone from comparing OP's script to other denoisers, to comparing how every denoiser perform inside YOUR filter?

I'm pretty certain that I said more constructive things than someone who have hijacked the thread for the last 5 pages talking about his own stuffs. I didn't see you saying a single word about the obvious problem that is underlying in OP's core smdegrain. To evaluate the quality of filter, you have to look it in both the pros and cons, if the cons exceeded a certain point, then the filter is a no go.

Here's a short scene emphasizing the problem I'm talking about.
snow (http://www.mediafire.com/file/p9jgodk3mwf9loo/snow.avi)

MysteryX
30th August 2017, 04:50
The conversation of mClean began within the conversation of FrameRateConverter. Budafel stated that he wanted to improve the source to feed into the interpolation. That's definitely not its only use.

Here's a short scene emphasizing the problem I'm talking about.
snow (http://www.mediafire.com/file/p9jgodk3mwf9loo/snow.avi)
Finally something constructive. You never mentioned this before unless I missed a few posts.

I found previously that KNLMeansCL worked well with artifacts but SMDegrain worked better with grainy videos, kind of like the one you have.

I was wondering whether mClean would replace SMDegrain, and I think that your post answers the question. I suppose SMDegrain is what gives the best result on that clip?

Burdafel, I'm testing your latest version with my 288p VCDs. enh=13, rn=10 is working good. enh=20, rn=12 is too much, in similar proportion to the HD videos. There's little difference between rn=0 and rn=10 but for heavy artifacts rn=0 works better. It seems this filter performs equally well on heavy artifacts or on no-artifacts clips. Which is a good thing when both are in the same frame.

Can you post a sample frame where you say it benefits from high enh?

MysteryX
30th August 2017, 05:12
288p clip with denoise, upscale and frame interpolation.

- Deblock_QED
- Deblock_QED + mClean(rn=0)
- KnlMeansCL(D=2, A=2, h=1.8, channels="YUV")

https://s26.postimg.org/6982h80p1/5483_no.png (http://postimg.org/image/6982h80p1/) https://s26.postimg.org/6ai0an2it/5483_mclean.png (http://postimg.org/image/6ai0an2it/) https://s26.postimg.org/4swjz2xs5/5483_knl.png (http://postimg.org/image/4swjz2xs5/)

KnlMeans removes a lot more artifacts but kills a lot of details, especially in the curtains.

mClean is again my favorite version, BUT artifact removal is a bit weak. Raising thSAD removes real details instead of removing more artifacts. Is there a way to make it just a little bit stronger? Perhaps lansing's clip has the same issue: mClean is a bit too weak for the job. I'm sure you'll come up with a new feature idea that will fix this.

lansing
30th August 2017, 06:52
The conversation of mClean began within the conversation of FrameRateConverter. Budafel stated that he wanted to improve the source to feed into the interpolation. That's definitely not its only use.

If that was his original intention then he should change the title to something like "a denoiser for better motion vector search for mvtools".

Finally something constructive. You never mentioned this before unless I missed a few posts.
Because I wasn't expecting someone who're writing a filter around smdegrain to not know the fault of smdegrain.


I was wondering whether mClean would replace SMDegrain, and I think that your post answers the question. I suppose SMDegrain is what gives the best result on that clip?
No! It's the complete opposite! That's the reason I don't use smdegrain, don't you see that mclean(smdegrain) is wiping out half of the snow which are details? Where is your logical sense?

For the OP, he'll eventually need to make a decision between quality and performance, just like what he had been adviced (https://forum.doom9.org/showthread.php?p=1815184#post1815184). If he is not to fix the core problem of his denoiser (like right now), then his script is going to be just another variation of smdegrain no matter how many fancy post processing he add. If he IS to fix it, he must also realize that someone had already done that 9 years ago. Why even bother reinventing the same thing...
MCTD (https://forum.doom9.org/showthread.php?t=139766)

burfadel
30th August 2017, 09:20
Yes, most often it is a tradeoff of performance and speed. You are limited by the speed of the filters used, so the best idea would be to find new ways of dealing with that. Most filter scripts use the same process for Chroma and Luma. I use the most ideal solution for these. MVtools does not do a good job on Chroma, but does on Luma. FFT3DFilter is similar on Luma as KNLMeansCL, but is great on Chroma. KNLMeansCL on chroma isn't the most efficient option.

In terms of the post processing, remoise is quite differet to the other options out there, it's perceptual based intended for motion, so still shots may not represent the trueness of it. In terms of sharpening, it is detail orientated and works on the inverse of the areas that renoise works on. The actual sharpen method itself is a unsharp filter. I haven't seen the solution I used elsewhere, it's ultra simple and super fast though :). Most other filters use external scripts. mClean doesn't deblock, unless I find a differing solution to what is out there it probably won't! Any other solutions can run separately afterwards. That said, there would be some benefit from internalising something like deblock_QED and simplifying it, because applying renoise and the sharpening after it would be beneficial. There's also another filter I would consider simplifying and incorporating it for the same reason, but ultimately any filters such as these will have simplified parameters. Most of the extra parameters or changing settings serves little or no benefit, and any change they do provide is usually in relation to another setting... so you can apply it as one setting that scales the individual settings. There are some useful things in MCTemporalDenoise, I saw those earlier :). At the moment I haven't borrowed from that script, but I probably will in the future and simplify where necessary. It's why I originally referred to it as an all-in-one script that I wanted to keep as simple as possible. Make the best script that I can do, and only then apply the best features from the other scripts and simplify their parameters. This is also inclusive of leveraging any advancements in Avisynth or respective filters, as well as accepting different solutions that people offer. I'll of course mention the respective other scripts when utilsing portions of their code.

As an all-in-one I also intended it to be a prefilter for FrameRateConverter or anything else, MysteryX knows this :).

feisty2
30th August 2017, 10:15
Since u guys been bitching about NLMeans a lot, y'all should know "h" is not the only hyperparameter? "s" is another major parameter that has to be carefully tweaked, default "s=4" is def a no go for ur shitty vcd garbage, make it 1 and gradually increase it till it feels rite

lansing
30th August 2017, 17:40
Yes, most often it is a tradeoff of performance and speed. You are limited by the speed of the filters used, so the best idea would be to find new ways of dealing with that.

Yes, that would require ideas for a new algorithm, not by using 10+ years old filters like what you're doing right now. Even then, the law between quality vs performance is not going to change, better quality is going to take longer processing time.

All the talk you said about your post processings are not reflecting on your result because your core denoising method (smdegrain) is giving you crappy input. All I'm seeing right now are the artifacts created by your smdegrain and artifacts created your mask.

MysteryX
30th August 2017, 17:48
If that was his original intention then he should change the title to something like "a denoiser for better motion vector search for mvtools".
It's not only for that, as Burdafel already explained clearly enough.

mclean(smdegrain) is wiping out half of the snow which are details
I can't even know that from the clip you sent, as I don't have the original. Next time I would suggest posting individual frames before and after so we can see the difference.

To evaluate the quality of filter, you have to look it in both the pros and cons, if the cons exceeded a certain point, then the filter is a no go.
That's where you and I fundamentally disagree. You're here to destroy. We're here to create.

If someone works on something that gives bad results, I'll point the flaws so that at the very least he will learn and improve himself from it. You'll just go in with a knife in his back to destroy him and his ideas. If you succeed, it's unlikely that member will bring anything else in the future. Luckily, there are many resilient members on this forum. I guess many are used to this dynamic (which isn't only you), as I see several people wait late in their development cycle until their script is solid before exposing it to public -- where it often gets torn apart.

So far I haven't seen any case where mClean degraded an image in any significant way. Perhaps your snow clip but I haven't seen the effect yet, nor what settings you used. If there is image deterioration, renoise should alleviate it.

--------------------------------

Putting that aside,

Burdafel, from what I've tested so far, it does a good job with subtle noise removal. Perfect as a prefilter, but a bit weak as a full-blown denoiser. Whether it works as a degrainer remains to be tested (perhaps on HD camera footage).

I'm not concerned about performance. There is however a trade-off between denoising and preserving details, and here the main focus was on preserving details, but perhaps there could be an effective "strength" setting.


Since u guys been bitching about NLMeans a lot, y'all should know "h" is not the only hyperparameter? "s" is another major parameter that has to be carefully tweaked, default "s=4" is def a no go for ur shitty vcd garbage, make it 1 and gradually increase it till it feels rite
Thanks, I'll definitely test it out!

P.S. I never bitch. Maybe if someone really crosses the line, and even then it's not bitching.

burfadel
30th August 2017, 19:32
Grain, both good and bad, is the main target. Artifacts are much harder as they're also effectively detail. I'll look for the happy balance. :)

lansing
30th August 2017, 20:42
So far I haven't seen any case where mClean degraded an image in any significant way. Perhaps your snow clip but I haven't seen the effect yet, nor what settings you used. If there is image deterioration, renoise should alleviate it.

You don't see it because you have eye problem as it was proven here (https://forum.doom9.org/showthread.php?p=1816662#post1816662), and you have a history of seeing invisible stuff (https://forum.doom9.org/showthread.php?p=1745470#post1745470). All your feedbacks are bullshit that were based on who knows what you saw.

The snow clip is a very good example exposing the problem of smdegrain.

original
http://i.imgur.com/KpvRPwp.png

mclean()
http://i.imgur.com/dDaRXar.png

The difference is so obvious, the filter is altering any moving object trying to smooth out the motion, in this case, it's wiping out the snow.


Here the filter is wiping out the detail on anything that moves:

original
http://i.imgur.com/dj0EGzX.png

mclean()
http://i.imgur.com/dIkJ94X.png

You're here to destroy. We're here to create.

If someone works on something that gives bad results, I'll point the flaws so that at the very least he will learn and improve himself from it.

This is so funny, I AM the one that's pointing out the flaws, not you! You can't even see the flaws because of your eye problem! You can't even see the artifacts that were created by the mask who know existed for how long.

MysteryX
30th August 2017, 21:23
Also post the script settings you use.

Posting on PostImage.io has the advantage of taking little space here and allowing to easily open in full-screen to compare images.

I agree that this particular clip reacts badly. Then it would be useful to see how it behaves with other denoisers. If you want to contribute, I would suggest posting sufficient relevant data:
- plugins/settings used
- screenshot
- comparison with other relevant denoisers
- your observation or conclusion in regards to the test ("this is BS" isn't a technical conclusion)

Sending the source of a difficult clip like that one could also be useful for Burdafel to work with. I developed FrameRateConverter using the worst videos I had for interpolation.

Every personal attacks might come back to you as big headaches -- better stick to a technical conversation.

MysteryX
30th August 2017, 22:19
Also Burdafel, if you need some custom algorithm written in C++, I can help with that (at least CPU version without assembly opt)

Groucho2004
30th August 2017, 22:24
BurdafelThat makes 7 times you got his moniker wrong. :D

StainlessS
30th August 2017, 22:32
That makes 7 times you got his moniker wrong. :D

I think I've been guilty of same thing a number of times, Burfadel just dont sound as good Burdafel, maybe an name change is in order :)

EDIT: To below, perhaps I have not posted wrong name, but I have definitely been using that name mentally.

Groucho2004
30th August 2017, 22:43
I think I've been guilty of same thing a number of times, Burfadel just dont sound as good Burdafel, maybe an name change is in order :)Couldn't find a single instance. Maybe someone else who's name you butchered? Like Grouchy? :rolleyes:

Actually, Grouchy is fine with me, has a nice ring to it.

MysteryX
30th August 2017, 22:50
Burfadel just dont sound as good Burdafel, maybe an name change is in order :)
Agree

Groucho, every time I see your name I'm thinking of Schtroumpf Grognon
https://vignette1.wikia.nocookie.net/schtroumpfs/images/b/ba/Schtroumpf-grognon_original_backup.jpg/revision/latest?cb=20150222140755&path-prefix=fr

burfadel
31st August 2017, 07:02
You don't see it because you have eye problem as it was proven here (https://forum.doom9.org/showthread.php?p=1816662#post1816662), and you have a history of seeing invisible stuff (https://forum.doom9.org/showthread.php?p=1745470#post1745470). All your feedbacks are bullshit that were based on who knows what you saw.

The snow clip is a very good example exposing the problem of smdegrain.

original
http://i.imgur.com/KpvRPwp.png

mclean()
http://i.imgur.com/dDaRXar.png

The difference is so obvious, the filter is altering any moving object trying to smooth out the motion, in this case, it's wiping out the snow.


Here the filter is wiping out the detail on anything that moves:
...

This is so funny, I AM the one that's pointing out the flaws, not you! You can't even see the flaws because of your eye problem! You can't even see the artifacts that were created by the mask who know existed for how long.

Dont get mixed up between SMDegrain and MDegrain, SMDegrain is a script using MDegrain as it's base :).

When removing grain you inevitably will remove what perceptually is detail. It's why adding the right type and amount of grain can make the image appear clearer and less flat. In this case, what appears to be detail that is removed due to the grain structure appearing as texture is actually most likely noise.

I've put together a test script with options for Mdegrain 3 through 6. This is adjusted by changing value of 'm'. If none is specific, the default 2 is used (MDegrain2), if you select 6 it will use MDegrain6, which is slower and should be overkill!

Additionally to that, there's an option 'recalc'. Default is false. If true, Mrecalculate will be used. This is a viable option speed wise if it has better quality :).

There's also an option for search method, applied to recalculation. Reason for this is exhaustive search may be too slow for Analysis stage, but fine for recalculation due to the recalculation only being done for bad vectors. This will only have affect if recalc=true.

Finally, there's an option for DCT. Again, this is for recalc only as this is where it would most likely be beneficial and has less of an impact on speed. If false (default) it uses the default DCT value which is 0. If true, it uses DCT value 4. Value 4 is preferable over 1 due to speed, and it maintains most of the benefits of 1.

So, play around with these test functions and let me know what you like best. The renoise function will work differently for each setting, so you may also want to use rn=0 to see just the Mdegrain process, or rn=12 (for instance) to see how it responds in relation to noise removed.

This is just a test for differences, which one performs the best for you. As an added test, I changed the rfilter value for 4, if using just mCleanT() this will be the only difference between this script and mClean.

Function is mCleanT(), T for Test.
m: 2 through 6. MDegrain function. Default is 2, which uses MDegrain2 function. Options 3-6 will use MDegrain3, MDegrain4, MDegrain5, or MDegrain6
recalc: True/false. Default is false. Recalculate vectors when set to true. Setting to true will also activate search and dct options if specified
search: True/false. Fefault is false, which uses the mClean setting of 5 (UMH search). If set to true, will enable exhaustive search (3) for the recalculation (with recalc=true)
dct: True/false. Default is false where dct 0 is used. If true dct 4 is used for recalculation (with recalc=true)

If you like feel free to adjust the searchparam values.

Those black spots when using mClean obviously shouldn't be there. Is it possible to upload a 10 second clip to Onedrive, Google drive, Mediafire etc? That way I can investigate and resolve that.

# mClean Test spatio/temporal denoiser
# Version: 1.3c_2 (29 August 2017)
# By burfadel

### TEST ONLY ###
### NOTE: Function name is mCleanT ###

# +++ Required plugins +++
# Latest RGTools, MVTools2, Masktools2, FFT3DFilter by Pinterf
# Latest Modplus, only required if using sharpening overboost (enh settings 101 and 102)
# Requires latest fftw.dll to be installed as instructed on the website - http://www.fftw.org


function mCleanT(clip c, int "thSAD", int "blksize", int "blksizeV", int "overlap", int "overlapV", int "enh", int "rn", int "outbits", int "m", bool "recalc", bool "dct", bool "search", int "cpu")
{
defH = Max (C.Height, C.Width/4*3) # Resolution calculation for auto blksize settings
thSAD = Default (thSAD, 450) # Denoising threshold
blksize = Default (blksize, defH<360 ? 8 : defH<750 ? 12 : defH<1200 ? 16 : defH<1600 ? 24 : 32) # Horizontal block size for MDegrain2
blksizeV = Default (blksizeV, blksize) # Vertical block size for MDegrain2, default same as horizontal
overlap = Default (overlap, blksize>4?(blksize/4+1)/2*2:0) # Horizontal block overlap
overlapV = Default (overlapV, blksize>4?(blksizeV/4+1)/2*2:0) # Vertical block overlap
enh = Default (enh, 13) # Detail enhancement (detail orientated sharpen) strength
rn = Default (rn, 10) # ReNoise strength from 0 (disabled) to 20
outbits = Default (outbits, c.BitsPerComponent) # Output bits, default input depth
calcbits = c.BitsPerComponent == 8 ? 12 : c.BitsPerComponent
calcbits = outbits > calcbits ? calcbits : outbits
m = Default (m, 2) # MDegrain steps
recalc = Default (recalc, false) # Process recalc
dct = Default (dct, false) # To use DCT=4 or not on recalc
search = Default (search, false) # If true uses exhaustive search on recalc
cpu = Default (cpu, 4) # Threads for fft3dfilter


Assert(isYUV(c)==true, """mClean: Supports only YUV formats (YV12, YV16, YV24)""")
Assert(isYUY2(c)==false, """mClean: Supports only YUV formats (YV12, YV16, YV24)""")
Assert(isYV411(c)==false, """mClean: Supports only YUV formats (YV12, YV16, YV24)""")
Assert(enh>=0 && enh<=102, """mClean: "enh" ranges from 0 to 102""")
Assert(rn>=0 && rn<=20, """mClean: "rn" ranges from 0 to 20""")
Assert(outbits>=8 && outbits<=16, """mClean: "outbits" ranges from 8 to 16""")
Assert(m>=2 && m<=6, """mClean Test: "m" ranges from 2 (default) to 6""")
dct = (dct==true) ? 4 : 0
search = (search==true) ? 3 : 5

# Spatio/temporal chroma noise filter
filt_chroma = fft3dfilter (c, bw=blksize*2, bh=blksizeV*2, ow=overlap*2, oh=overlapV*2, sharpen=0.12, bt=3, ncpu=cpu, dehalo=0.3, sigma=2.35, plane=3)

# Temporal luma noise filter
super = c.MSuper (chroma=false,hpad=16, vpad=16, rfilter=4)
super2 = c.MSuper (chroma=false, hpad=16, vpad=16, levels=1, rfilter=4)
bvec6 = (m==6) ? MAnalyse (super, chroma=false, delta = 6, isb = true, blksize=blksize, blksizeV=blksizeV, overlap=overlap, overlapV=overlapV, search=5, searchparam=13) :nop
bvec5 = (m>=5<=6) ? MAnalyse (super, chroma=false, delta = 5, isb = true, blksize=blksize, blksizeV=blksizeV, overlap=overlap, overlapV=overlapV, search=5, searchparam=11) :nop
bvec4 = (m>=4<=6) ? MAnalyse (super, chroma=false, delta = 4, isb = true, blksize=blksize, blksizeV=blksizeV, overlap=overlap, overlapV=overlapV, search=5, searchparam=9) :nop
bvec3 = (m>=3<=6) ? MAnalyse (super, chroma=false, delta = 3, isb = true, blksize=blksize, blksizeV=blksizeV, overlap=overlap, overlapV=overlapV, search=5, searchparam=7) :nop
bvec2 = MAnalyse (super, chroma=false, isb = true, delta = 2, blksize=blksize, blksizeV=blksizeV, overlap=overlap, overlapV=overlapV, search=5, searchparam=5)
bvec1 = MAnalyse (super, chroma=false, isb = true, delta = 1, blksize=blksize, blksizeV=blksizeV, overlap=overlap, overlapV=overlapV, search=5, searchparam=3)
fvec1 = MAnalyse (super, chroma=false, isb = false, delta = 1, blksize=blksize, blksizeV=blksizeV, overlap=overlap, overlapV=overlapV, search=5, searchparam=3)
fvec2 = MAnalyse (super, chroma=false, isb = false, delta = 2, blksize=blksize, blksizeV=blksizeV, overlap=overlap, overlapV=overlapV, search=5, searchparam=5)
fvec3 = (m>=3<=6) ? MAnalyse (super, chroma=false, isb = false, delta = 3, blksize=blksize, blksizeV=blksizeV, overlap=overlap, overlapV=overlapV, search=5, searchparam=7) :nop
fvec4 = (m>=4<=6) ? MAnalyse (super, chroma=false, isb = false, delta = 4, blksize=blksize, blksizeV=blksizeV, overlap=overlap, overlapV=overlapV, search=5, searchparam=9) :nop
fvec5 = (m>=5<=6) ? MAnalyse (super, chroma=false, isb = false, delta = 5, blksize=blksize, blksizeV=blksizeV, overlap=overlap, overlapV=overlapV, search=5, searchparam=11) :nop
fvec6 = (m==6) ? MAnalyse (super, chroma=false, isb = false, delta = 6, blksize=blksize, blksizeV=blksizeV, overlap=overlap, overlapV=overlapV, search=5, searchparam=13) :nop

bvec6 = (recalc==false) ? bvec6 : (m==6) ? MRecalculate (super2, bvec6, chroma=false, blksize=blksize/2, blksizeV=blksizeV/2, overlap=overlap/2, overlapV=overlapV/2, search=search, searchparam=13, dct) :nop
bvec5 = (recalc==false) ? bvec5 : (m>=5<=6) ? MRecalculate (super2, bvec5, chroma=false, blksize=blksize/2, blksizeV=blksizeV/2, overlap=overlap/2, overlapV=overlapV/2, search=search, searchparam=11, dct) :nop
bvec4 = (recalc==false) ? bvec4 : (m>=4<=6) ? MRecalculate (super2, bvec4, chroma=false, blksize=blksize/2, blksizeV=blksizeV/2, overlap=overlap/2, overlapV=overlapV/2, search=search, searchparam=9, dct) :nop
bvec3 = (recalc==false) ? bvec3 : (m>=3<=6) ? MRecalculate (super2, bvec3, chroma=false, blksize=blksize/2, blksizeV=blksizeV/2, overlap=overlap/2, overlapV=overlapV/2, search=search, searchparam=7, dct) :nop
bvec2 = (recalc==false) ? bvec2 : MRecalculate (super2, bvec2, chroma=false, blksize=blksize/2, blksizeV=blksizeV/2, overlap=overlap/2, overlapV=overlapV/2, search=search, searchparam=5, dct)
bvec1 = (recalc==false) ? bvec1 : MRecalculate (super2, bvec1, chroma=false, blksize=blksize/2, blksizeV=blksizeV/2, overlap=overlap/2, overlapV=overlapV/2, search=search, searchparam=3, dct)
fvec1 = (recalc==false) ? fvec1 : MRecalculate (super2, fvec1, chroma=false, blksize=blksize/2, blksizeV=blksizeV/2, overlap=overlap/2, overlapV=overlapV/2, search=search, searchparam=3, dct)
fvec2 = (recalc==false) ? fvec2 : MRecalculate (super2, fvec2, chroma=false, blksize=blksize/2, blksizeV=blksizeV/2, overlap=overlap/2, overlapV=overlapV/2, search=search, searchparam=5, dct)
fvec3 = (recalc==false) ? fvec3 : (m>=3<=6) ? MRecalculate (super2, fvec3, chroma=false, blksize=blksize/2, blksizeV=blksizeV/2, overlap=overlap/2, overlapV=overlapV/2, search=search, searchparam=7, dct) :nop
fvec4 = (recalc==false) ? fvec4 : (m>=4<=6) ? MRecalculate (super2, fvec4, chroma=false, blksize=blksize/2, blksizeV=blksizeV/2, overlap=overlap/2, overlapV=overlapV/2, search=search, searchparam=9, dct) :nop
fvec5 = (recalc==false) ? fvec5 : (m>=5<=6) ? MRecalculate (super2, fvec5, chroma=false, blksize=blksize/2, blksizeV=blksizeV/2, overlap=overlap/2, overlapV=overlapV/2, search=search, searchparam=11, dct) :nop
fvec6 = (recalc==false) ? fvec6 : (m==6) ? MRecalculate (super2, fvec6, chroma=false, blksize=blksize/2, blksizeV=blksizeV/2, overlap=overlap/2, overlapV=overlapV/2, search=search, searchparam=13, dct) :nop

clean = (m==2) ? c.MDegrain2 (super, bvec1, fvec1, bvec2, fvec2, thSAD=thSAD, plane = 0) :nop
clean = (m==3) ? c.MDegrain3 (super, bvec1, fvec1, bvec2, fvec2, bvec3, fvec3, thSAD=thSAD, plane = 0) :clean
clean = (m==4) ? c.MDegrain4 (super, bvec1, fvec1, bvec2, fvec2, bvec3, fvec3, bvec4, fvec4, thSAD=thSAD, plane = 0) :clean
clean = (m==5) ? c.MDegrain5 (super, bvec1, fvec1, bvec2, fvec2, bvec3, fvec3, bvec4, fvec4, bvec5, fvec5, thSAD=thSAD, plane = 0) :clean
clean = (m==6) ? c.MDegrain6 (super, bvec1, fvec1, bvec2, fvec2, bvec3, fvec3, bvec4, fvec4, bvec5, fvec5, bvec6, fvec6, thSAD=thSAD, plane = 0) :clean

clean = calcbits != clean.BitsPerComponent ? clean.ConvertBits(calcbits) : clean
c = calcbits != c.BitsPerComponent ? c.ConvertBits(calcbits) : c

# Masks for spatial noise reduction and noise independent detail enhancement
noised = mt_makediff (clean, c, u=1, v=1)
noise = mt_binarize (clense(mt_makediff(mt_binarize(noised, u=1, v=1), mt_edge(sharpen(clean, 0.85), "prewitt", u=1, v=1), u=1, v=1), grey=true), u=1, v=1)

# Spatial luma denoising
clean2 = mt_merge (clean, removegrain(clean, 18, modeU=-1, modeV=-1), noise, u=1, v=1)

# Unsharp filter for spatial detail enhancement
clsharp = (enh>0<=100) ? mt_adddiff (mt_makediff(clean, blur(clean, 0.80*(enh/100), 0.50*(enh/100)), u=1, v=1), clean2, u=1, v=1) : clean
clsharp = (enh>=101<=102) ? mt_adddiff (mt_makediff(clean, gblur(clean, enh-100), u=1, v=1), clean2, u=1, v=1) : clsharp

# If selected, combining ReNoise
renoise = (rn==0) ? nop : tweak(temporalsoften (noised, 4, 255, 0, scenechange=0, mode=2), cont=1.010+(0.020*(rn/20)), bright=1.01+(0.04*(rn/20)))
clean2 = (rn>0<=20) ? mergeluma (clean2, mt_adddiff(clean2, renoise, u=1, v=1), 0.3+(rn*0.035)) : clean2

# Combining spatial detail enhancement with spatial noise reduction using prepared mask
filt_luma = mt_merge (clean2, clsharp, mt_invert(mt_convolution(noise, u=1, v=1), u=1, v=1), u=1, v=1)

# Converting bits per channel
filt_luma = outbits < filt_luma.BitsPerComponent ? ConvertBits(filt_luma, outbits, 1) : filt_luma
filt_chroma = filt_chroma.BitsPerComponent <> filt_luma.BitsPerComponent ? ConvertBits(filt_chroma, BitsPerComponent(filt_luma)) : filt_chroma

# Combining result of luma and chroma cleaning
return mergechroma (filt_luma, filt_chroma)
}

Let me know how it goes :).

lansing
31st August 2017, 08:51
Also post the script settings you use.

Posting on PostImage.io has the advantage of taking little space here and allowing to easily open in full-screen to compare images.

I agree that this particular clip reacts badly. Then it would be useful to see how it behaves with other denoisers. If you want to contribute, I would suggest posting sufficient relevant data:
- plugins/settings used
- screenshot
- comparison with other relevant denoisers
- your observation or conclusion in regards to the test ("this is BS" isn't a technical conclusion)

Sending the source of a difficult clip like that one could also be useful for Burdafel to work with. I developed FrameRateConverter using the worst videos I had for interpolation.

Every personal attacks might come back to you as big headaches -- better stick to a technical conversation.

There is nothing "difficult" about the snow clip, it's just an example demonstrating the typical behavior(con) of smdegrain. This is what was expected to get from the filter, apply it on any clip and you will get the same problem.

If the OP was to build the script around smdegrain, the very first thing he should had done was to write some logic to protect against its problem, that is how you build stuff. You build a solid core first, then you add the fancy stuffs, not the other way around.

As for comparison, this is a smdegrain only problem, so any other temporal denoisers is not going to have it, just as expected. The closest one that can relate to is MCTD, since it also built around smdegrain.

original
http://i.imgur.com/KpvRPwp.png (http://imgur.com/KpvRPwp)

mctd()
http://i.imgur.com/v31a6Lc.png (http://imgur.com/v31a6Lc)

And it has done the protection.

lansing
31st August 2017, 10:44
Dont get mixed up between SMDegrain and MDegrain, SMDegrain is a script using MDegrain as it's base :).
smdegrain is a wrapper function for mvtools+mdegrain, which is what you're using.


I've put together a test script with options for Mdegrain 3 through 6. This is adjusted by changing value of 'm'. If none is specific, the default 2 is used (MDegrain2), if you select 6 it will use MDegrain6, which is slower and should be overkill!

Additionally to that, there's an option 'recalc'. Default is false. If true, Mrecalculate will be used. This is a viable option speed wise if it has better quality :).

There's also an option for search method, applied to recalculation. Reason for this is exhaustive search may be too slow for Analysis stage, but fine for recalculation due to the recalculation only being done for bad vectors. This will only have affect if recalc=true.

Finally, there's an option for DCT. Again, this is for recalc only as this is where it would most likely be beneficial and has less of an impact on speed. If false (default) it uses the default DCT value which is 0. If true, it uses DCT value 4. Value 4 is preferable over 1 due to speed, and it maintains most of the benefits of 1.

So, play around with these test functions and let me know what you like best. The renoise function will work differently for each setting, so you may also want to use rn=0 to see just the Mdegrain process, or rn=12 (for instance) to see how it responds in relation to noise removed.

This is just a test for differences, which one performs the best for you. As an added test, I changed the rfilter value for 4, if using just mCleanT() this will be the only difference between this script and mClean.

Function is mCleanT(), T for Test.
m: 2 through 6. MDegrain function. Default is 2, which uses MDegrain2 function. Options 3-6 will use MDegrain3, MDegrain4, MDegrain5, or MDegrain6
recalc: True/false. Default is false. Recalculate vectors when set to true. Setting to true will also activate search and dct options if specified
search: True/false. Fefault is false, which uses the mClean setting of 5 (UMH search). If set to true, will enable exhaustive search (3) for the recalculation (with recalc=true)
dct: True/false. Default is false where dct 0 is used. If true dct 4 is used for recalculation (with recalc=true)

If you like feel free to adjust the searchparam values.

Those black spots when using mClean obviously shouldn't be there. Is it possible to upload a 10 second clip to Onedrive, Google drive, Mediafire etc? That way I can investigate and resolve that.

Let me know how it goes :).
Man, you'll need to start doing the testing yourself, you have my sample clip, I have told you the problem and you saw the problem. You can't rely solely on the observation of others, or you'll have another MysteryX coming by trolling you for 2 weeks going nowhere.

I don't know if the problem the nature of the filter or just some lazy logic, and I don't know whether the problem existed within mvtools as a whole or just the function "mdegrain", that's going to be your job to test. If the problem only exist in "mdegrain", then there's going to be decision to make on what to replace it with. If the problem existed within mvtools as a whole, then you'll need to look for ways to protect it (MCTD), or invent your own logic.

burfadel
31st August 2017, 10:57
I have done testing myself :). What I think is great may not be the most ideal solution. The m settings are for comparison and interest. If you must know, I think recalc helps, not sure about dct, or whether there are any cases where exhaustive search is beneficial. I really don't think anyone has done that kind of testing, they're using basically what was done as an example 10 years ago, or slight variations thereof. Pinterf only recently added MDegrain 4, 5, 6. If say, 4 is useful there may be a way to limit the speed impact :). I missed your actual clip link, I'll check it up when I'm home and not using my phone :).

Home now but can't look at it right away (9.30 pm here anyway). I will do a quick look up and explaining though :)

Here is an example for Mdegrain2 when it was added... 12 years ago! In the old MVTools changelog I notice the multithreading that is causing issues was added in 2008.
backward_vec2 = source.MVAnalyse(isb = true, delta = 2, pel = 2, overlap=4, sharp=1, idx = 1)
backward_vec1 = source.MVAnalyse(isb = true, delta = 1, pel = 2, overlap=4, sharp=1, idx = 1)
forward_vec1 = source.MVAnalyse(isb = false, delta = 1, pel = 2, overlap=4, sharp=1, idx = 1)
forward_vec2 = source.MVAnalyse(isb = false, delta = 2, pel = 2, overlap=4, sharp=1, idx = 1)
source.MVDegrain2(backward_vec1,forward_vec1,backward_vec2,forward_vec2,thSAD=400,idx=1)
https://avisynth.org.ru/mvtools/mvtools.html

That will NOT work in MVTools2, it's just showing where the origins of where other scripts, including SMDegrain, came from. Note that Super was not a separate thing back then.

Here is a more 'modern' version of the above in the MVTools2 description page, the old one by Fizick:
super = MSuper(pel=2, sharp=1)
backward_vec2 = MAnalyse(super, isb = true, delta = 2, overlap=4)
backward_vec1 = MAnalyse(super, isb = true, delta = 1, overlap=4)
forward_vec1 = MAnalyse(super, isb = false, delta = 1, overlap=4)
forward_vec2 = MAnalyse(super, isb = false, delta = 2, overlap=4)
MDegrain2(super, backward_vec1,forward_vec1,backward_vec2,forward_vec2,thSAD=400)
http://avisynth.org.ru/mvtools/mvtools2.html

Sharp no longer needs to be specified as sharp=1 is now default, so if you take that out you have what basically every denoiser that uses MDegrain utilises. Only difference is they may specify blksize values etc. Sure, adding an extra line for MDegrain3 is different, but hardly so :). I thought by putting together a test script like I did, it gives people the opportunity to mess around with some of the really new stuff added (MDegrain4 through 6), as well as see if there are any benefits from the other settings. Sure, SMDegrain does expose some of these, but it's just extra options that people typically don't use. The real fact is, pretty much a single variation will be ideal for practically all situations. This is good, because it means I can keep the main script as simple as possible and provide the best results for everyone, not just when the person decides to test out all the other options.

If a high MDegrain works best, there are things to do limit the impact on speed without hopefully affecting the benefits too much.

Opinions of what looks better varies from person to person, but if there is a consensus that a certain feature provides benefits, then it makes sense to use that. I guess in many ways I'm going out of my way to try and see if the 12 year old example script can be improved upon, not just assuming it's the best because every other script that uses MDegrain basically has that same script used (differences mainly block size).

The testing is as your said, making a strong base :). MCTD is great, as I said I will incorporate the beneficial stuff from other scripts and reference them when I do. Haven't needed to do that so far because I haven't done that (the MDegrain2 use doesn't count, and especially so if improved upon the base script). The main focus was a fast as the functions allows, up to date script, utilising the most ideal filters as required. Example, FFT3Dfilter for chroma.

lansing
31st August 2017, 17:10
I have done testing myself :). What I think is great may not be the most ideal solution. The m settings are for comparison and interest. If you must know, I think recalc helps, not sure about dct, or whether there are any cases where exhaustive search is beneficial. I really don't think anyone has done that kind of testing, they're using basically what was done as an example 10 years ago, or slight variations thereof. Pinterf only recently added MDegrain 4, 5, 6. If say, 4 is useful there may be a way to limit the speed impact :). I missed your actual clip link, I'll check it up when I'm home and not using my phone :).

Home now but can't look at it right away (9.30 pm here anyway). I will do a quick look up and explaining though :)

Here is an example for Mdegrain2 when it was added... 12 years ago! In the old MVTools changelog I notice the multithreading that is causing issues was added in 2008.

https://avisynth.org.ru/mvtools/mvtools.html

That will NOT work in MVTools2, it's just showing where the origins of where other scripts, including SMDegrain, came from. Note that Super was not a separate thing back then.

Here is a more 'modern' version of the above in the MVTools2 description page, the old one by Fizick:

http://avisynth.org.ru/mvtools/mvtools2.html

Sharp no longer needs to be specified as sharp=1 is now default, so if you take that out you have what basically every denoiser that uses MDegrain utilises. Only difference is they may specify blksize values etc. Sure, adding an extra line for MDegrain3 is different, but hardly so :). I thought by putting together a test script like I did, it gives people the opportunity to mess around with some of the really new stuff added (MDegrain4 through 6), as well as see if there are any benefits from the other settings. Sure, SMDegrain does expose some of these, but it's just extra options that people typically don't use. The real fact is, pretty much a single variation will be ideal for practically all situations. This is good, because it means I can keep the main script as simple as possible and provide the best results for everyone, not just when the person decides to test out all the other options.

If a high MDegrain works best, there are things to do limit the impact on speed without hopefully affecting the benefits too much.

Opinions of what looks better varies from person to person, but if there is a consensus that a certain feature provides benefits, then it makes sense to use that. I guess in many ways I'm going out of my way to try and see if the 12 year old example script can be improved upon, not just assuming it's the best because every other script that uses MDegrain basically has that same script used (differences mainly block size).

The testing is as your said, making a strong base :). MCTD is great, as I said I will incorporate the beneficial stuff from other scripts and reference them when I do. Haven't needed to do that so far because I haven't done that (the MDegrain2 use doesn't count, and especially so if improved upon the base script). The main focus was a fast as the functions allows, up to date script, utilising the most ideal filters as required. Example, FFT3Dfilter for chroma.

I'm kind of lost at what exactly are you trying to do here. The objective of your test should be very clear: you turn off the spatial denoising and all the post processing of your test clip and plug it to the snow clip, if your temporal denoiser doesn't wipe out any snow, bingo. If not, that means it doesn't work and try another method.

And if you actually did the test yourself, you should of know that your current test script did not work.

StainlessS
31st August 2017, 17:15
Question:- Why does that Smurf thing, have his naughty tackle glued to his bum ?
https://forum.doom9.org/showthread.php?p=1816915#post1816915

Groucho2004
31st August 2017, 17:29
Groucho, every time I see your name I'm thinking of Schtroumpf Grognon[/IMG]Every time I see your name I think of dancing Asian girls in provocative outfits. Definitely better than your smurf fantasy of me.

burfadel
31st August 2017, 18:04
I'm kind of lost at what exactly are you trying to do here. The objective of your test should be very clear: you turn off the spatial denoising and all the post processing of your test clip and plug it to the snow clip, if your temporal denoiser doesn't wipe out any snow, bingo. If not, that means it doesn't work and try another method.

And if you actually did the test yourself, you should of know that your current test script did not work.

I did test the test script and it works perfectly here. Not only that, I went back, copied it from the post and tried that just to be sure :). All the options work for me, so what about it isn't working for you? Keep in mind the latest plugins are required, and like the main script requires Avisynth+. Pinterf resolved a bug I reported and released an updated MVTools earlier this week. MVTools base is quite old, PinterF has done a great job of updating it.

Now, you may be wondering why I left the post processing stuff enabled. Simple, it's easy to turn off with enh=0 and rn=0 :). More precisely though, Renoise is 100 percent dependent on what is actually removed. In terms of the most effective and most accurate (without motion issues or removing wanted detail) removal method, m=6, recalc=true, search=true, dct=true should be highest quality. That said, it is too slow and overkill anyway. The idea way would be to find the maximum settings that show a visible difference, then find a second or third 'ideal' setting, and work it from there.

An important aspect of this is that it is subjective to the individual. You will however discover with a larger dataset that there will be things that people agree on, so that's the purpose of the test. As I said earlier, it doesn't seem any real quality analysis like this has been done. You might have a clip where something stands out that is obviously better for that situation, but doesn't make a perceptible difference in the particular clips that others use. This would just mean the most ideal settings aren't found if it's only me passing each individual options and combinations thereof over different clips.

If you want to trim down the test options, m 4 through 6 are just out of interest. They are new to MVTools by PinterF, you probably haven't come across them. It's out of interest because each m higher is slower. That said, if m 4 is beneficial over 3 there are possibly ways to overcome that performance issue.

So values of m to try are 2, 3, and also 4 out of interest to see if it improves. The most ideal one out of those, proceed on to recalc=true, followed by keeping recalc and adding search=true, replacing search=true with dct=true, then using search=true and dct=true. Lets just say if you found m3, recalc=true, dct=true to be the one you like, then you can drop it to m2 and see if the quality holds. If not, then it's good.

I simply don't have every clip out there. Different clips have different noise characteristics, all of these can impact noise (grain, impulse etc) removal effectiveness of any filter. Also, I already have a general idea of which settings of these to choose. I will not say them though, as I want people to form their own opinion and corroborate my findings. By doing so it gives people the option to share their ideas and findings to find the most beneficial outcome. The added advantage with the online community is that others can also benefit for other scripts, if so desired, if there is indeed a good set of options.

burfadel
31st August 2017, 18:10
Every time I see your name I think of dancing Asian girls in provocative outfits. Definitely better than your smurf fantasy of me.

"What are you up to Groucho?"
"Just smurfing the internet"

MysteryX
31st August 2017, 19:38
"What are you up to Groucho?"
"Just smurfing the internet"
LOL!
https://www.youtube.com/watch?v=mcAW9YJ1-S8


MvTools2 is an old relic that everybody uses, yet nobody understands fully the use, nobody understand how it works and nobody wants to touch the code -- except Pinterf who works on the code without understanding how it even works. The conversation here supports that further.

The way MvTools2 works, small variations of settings can cause huge differences to the output. SMDegrain is one set of settings. MvTools2 has many functions and many settings. It's kind of an ignorant comment to say that everything that can be done with MvTools2 is one and the same, when each small variation can cascade into considerable changes.

The tests I've done with FrameRateConverter and RemoveGrain(18) vs RemoveGrain(21) support that: that tiny variation resulted into very notable changes.

MvTools2 is a complex library with tons of settings.

lansing
31st August 2017, 19:59
I did test the test script and it works perfectly here. Not only that, I went back, copied it from the post and tried that just to be sure :). All the options work for me, so what about it isn't working for you? Keep in mind the latest plugins are required, and like the main script requires Avisynth+. Pinterf resolved a bug I reported and released an updated MVTools earlier this week. MVTools base is quite old, PinterF has done a great job of updating it.

I simply don't have every clip out there. Different clips have different noise characteristics, all of these can impact noise (grain, impulse etc) removal effectiveness of any filter. Also, I already have a general idea of which settings of these to choose. I will not say them though, as I want people to form their own opinion and corroborate my findings. By doing so it gives people the option to share their ideas and findings to find the most beneficial outcome. The added advantage with the online community is that others can also benefit for other scripts, if so desired, if there is indeed a good set of options.

By "not working" I mean it's not fixing the issue. Turn on all the highest setting you want and you'll be getting the same thing. A temporal denoiser should not be touching anything that's not temporal.

burfadel
31st August 2017, 20:05
That's right :). Exposing all those options makes things too complex though for the end user. The good thing is that in terms of the analysis I suspect there is an ideal set of settings for benefit and performance that are generalistic. It's finding those settings over a wide variety of clips that is the difficult part.

burfadel
31st August 2017, 20:16
By "not working" I mean it's not fixing the issue. Turn on all the highest setting you want and you'll be getting the same thing. A temporal denoiser should not be touching anything that's not temporal.

That's true, but if it's the case it's a bug in MVTools. Without a motion clip of it I can't see myself. That said though, with a clip that has the issue and notified exactly where it is, I should be able to overcome it and importantly, maybe identify why it is happening. There's still a lot to add, I just want to 'perfect' the base.

MysteryX
31st August 2017, 20:34
So lansing, you want to help or you're just here to complain? make up your mind


BurFaDel, if you haven't figured it out yet, I wasn't sure whether he had posted the source or processed video. This is the source
Here's a short scene emphasizing the problem I'm talking about.
snow (http://www.mediafire.com/file/p9jgodk3mwf9loo/snow.avi)

burfadel
31st August 2017, 22:03
Thanks, I missed that :). MDegrain does motion compensation to find noise, that said stationary objects shouldn't be affected since there is nothing to compensate. I have some ideas of MDegrain function part improvements, whether it works it not as intended... The above test is still invaluable, the changes will be inclusive of those.

burfadel
31st August 2017, 22:53
The artifact masks from framerateconverter could overcome any remaining artifact issues, but reducing the problem is much better than trying to fix it later :).

burfadel
3rd September 2017, 19:00
Updated first post with version 1.4.
Changed to MDegrain4 as the results were nicer and the output is more efficient, the settings used should reduce the impact on speed. I also modified the sharpen function, it should produce nicer results but at the same time is also stronger. I would have done more on the script but had other things come up that needed attending to over the weekend :).

EDIT: Updated first post to version 1.5
Changed sharpen setting enh, and adjusted range from 0-24 (0-20 normal range and 21-24 overboost sharpening). Updated renoise and will now only be applied to areas with luma greater than 32(/255). Added DCTFilter, improves compressibility fractionally, main reason for its addition is for additionally cleaning of the picture in a particular way. Adding padding to mod4 (re-updated version 1.5 post to reflect this).

Please use the latest chikuzen's updated DCTFilter, which has a x64 version, from here - https://github.com/chikuzen/DCTFilter/releases

lansing
5th September 2017, 20:29
Good to see you also noticed that the renoise function was adding blown white noises around moving object in darker areas. Any update on your smdegrain issue fix/workaround?

MysteryX
5th September 2017, 20:53
Dark scenes were getting noise and getting gray-er, now it's good. If it's due to the 2.2 gamma curve, the same problem may happen with whites. Perhaps you could apply a curve that softens both edges of the gamma curve instead of applying a flat threshold?

The new version's enh doesn't work for me. This version's sharpening with enh=0 is about the same as 1.3c with enh=13, and with enh=0 it actually looks pretty good. You can see some details are better defined than in v1.3c. With enh=1, however, it goes south. enh=5 is just worse.

Note: this is my full script that denoises, upscales and interpolates.

v1.3c / v1.4 enh=0 / v1.4 enh=1 (all rn=10)
https://s26.postimg.org/pxd7el891/5017_m_Clean3.png (http://postimg.org/image/pxd7el891/) https://s26.postimg.org/chq6p4zr9/5017_m_Clean4.png (http://postimg.org/image/chq6p4zr9/) https://s26.postimg.org/4di2kedc5/5017_m_Clean4enh.png (http://postimg.org/image/4di2kedc5/)

burfadel
5th September 2017, 20:58
Remember it's MDegrain, SMDegrain is another script that uses it. It would be like calling Deblock_QED as plain Deblock, as although Deblock_QED is a wrapper function the script itself is quite different than calling Deblock directly. Basically every script does this, rely on other filters, so if another script happens to use a similar base function or function set, the base function set should be referenced, not the wrapper :)

I do know why MDegrain does that, technically it's actually doing what it's meant to do! I did think of a possible solution, I'll see if it works (or probably not!) sometime in the next couple of days.

burfadel
5th September 2017, 21:20
Dark scenes were getting noise and getting gray-er, now it's good. If it's due to the 2.2 gamma curve, the same problem may happen with whites. Perhaps you could apply a curve that softens both edges of the gamma curve instead of applying a flat threshold?

The new version's enh doesn't work for me. This version's sharpening with enh=0 is about the same as 1.3c with enh=13, and with enh=0 it actually looks pretty good. You can see some details are better defined than in v1.3c. With enh=1, however, it goes south. enh=5 is just worse.

Note: this is my full script that denoises, upscales and interpolates.


I've readjusted the defaults, now 0-54 so should give you more scale. A 0-100 scale was too high. The enh settings is fine if you are doing only a small amount of image enlargement afterwards, and definitely fine for reducing image size. If you are upsizing to a much larger resolution a slightly different approach is needed but should be relatively easy to implement. I'll have to look at that either later today or tomorrow. The added benefit of this would be that you can run (for now before implementation) other forms of artifact reduction and do an enhance after everything is done. Artifacts like ringing can be difficult to remove since it is technically detail, and the typical method of removing it is smoothing. This works great but it can also smooth wanted details or ring-esque like details. Haloing is 'easier' to reduce or remove, but again faces the same problem with detail removal. I do have some thoughts on how to do this. If I did add it, it probably won't be on or it will have a weak default setting.

In terms of scaling the application of renoise base on luma contrainsts, not sure on how to do that! Or at least, such that it can be done simply. I can though simply limit its application to a range of for instance, 32-220 if adding to high luminance areas is problematic. I might add it anyway, there's probably little point on adding it to high luminance areas.

MysteryX
5th September 2017, 23:30
Perhaps you can alter the renoise algorithm to work in linear-light instead of on a gamma curve?

MysteryX
6th September 2017, 01:15
Here's a comparison with a HD video. This clip mostly has noise in the dark leather and a little bit in the hair.

Original / mClean 1.3c / mClean 1.5c enh=0 / mClean 1.5c enh=5
https://s26.postimg.org/ncha1xekl/2204_original.png (http://postimg.org/image/ncha1xekl/) https://s26.postimg.org/z0bbwh3ph/2204_mclean3.png (http://postimg.org/image/z0bbwh3ph/) https://s26.postimg.org/8x8juigat/2204_mclean5.png (http://postimg.org/image/8x8juigat/) https://s26.postimg.org/n4y8j5szp/2204_mclean5enh.png (http://postimg.org/image/n4y8j5szp/)

Here, the leather doesn't benefit as much from renoise and looks more flat. Which is why I think working in Linear Light would be a better idea. You can't do MDegrain in Linear Light because it requires HBD to not loose a lot of data, and MDegrain in HBD is a lot slower. However, you can probably convert into Linear Light right after to do the rest of the processing.

Is this script with MDegrain4 sharper than the previous? With enh=0 it looks almost just as sharp. With enh=5, however, it's way too sharp. It might look fine now, and after encoding it will still be decent, but if you re-encode a second time it will get ugly.

The enh settings is fine if you are doing only a small amount of image enlargement afterwards, and definitely fine for reducing image size.
If it's too sharp for upscaling, then it's definitely out-of-balance, and will get ugly after encoding it twice.

MysteryX
6th September 2017, 03:16
I'm looking at your script. Instead of excluding U and V planes of a Y12 clip on all commands, why don't you just work with a Y8 clip? This would simplify the syntax quite a bit.

To convert to linear light, something like mt_lut(x, "x 2.2 ^") should do it. Not sure where it would have to go though, or whether it would work with your logic. I'm a bit confused as to what mt_binarize is doing in your script.

burfadel
6th September 2017, 04:49
I plan to change it to Y8, I just want to make sure everything is working like it should, then use Y8. The results should be identical, if not then the error can be directed at the proper filter. The mt_binarize is in there so renoise isn't applied back to areas with less than 32 luma. If there is a better way of doing it... The leather is black so it isn't being applied there. As for enh, it really depends on the basal quality of the clip. If any sharpen filter looks bad on the clip enh won't work, since it is a detail orientated unsharp mask. It's why it can be disabled with 0, it can only enhance what is there! There is something I can do to for it that can improve it though, it may make it usable at a lowered setting in those cases. It's something I always intended to do but wanted to get the base done right first :). There is also something else I can try for situations such as in those screenshots.

EDIT: Not sure if there is any benefit converting to Y8, apart from making the script look a bit neater. If in Y8 and none of the 'do not process chroma' flags are specified, do the filters work just on the Y plane, or do they 'process' blank chroma channels? I guess the only other reason would be if it were faster, but then again you have to consider the conversion to and from Y8 format to the original YV12/YV16/YV24 format.

burfadel
6th September 2017, 08:00
Updated first post with soothe function.

StainlessS
6th September 2017, 12:49
Not sure if there is any benefit converting to Y8, apart from making the script look a bit neater. If in Y8 and none of the 'do not process chroma' flags are specified, do the filters work just on the Y plane, or do they 'process' blank chroma channels? I guess the only other reason would be if it were faster, but then again you have to consider the conversion to and from Y8 format to the original YV12/YV16/YV24 format.

I think that for ConvertToY8,
Avs standard uses subframe, ie sort of forgets that it has chroma. Results of further filter ops comes only from the luma plane, chroma no longer considered part of frame by following filters [EDIT: which produce Y8 luma only].
Avs+, copies luma plane into new frame buffer (I think this may happen only if not already aligned to some optimal memory boundary, for eg SSE2/SIMD or whatever, if already aligned, then may do same as avs, not sure).
Both above methods have their advantages, and both sets of devs prefer their method.

For Convert back to YV12 (using eg MergeChroma), new frame will be created and planes copied/blitted into it.
Copying will be quite fast and not really worth considering as a bottle neck.

For non Y8 Planar, some ops in your script may not have a 'do not process chroma' type flag, and so those would process chroma even if nothing useful
exists there, conversely, some ops may not support Y8. Choose your poison.

burfadel
6th September 2017, 13:49
I guess ultimately it will come down to which method is faster. If they perform the same, then I'll probably still use Y8 for script neatness. It will be interesting to see the results of this!

MysteryX
6th September 2017, 16:08
It will be faster and use less memory to use Y8. Otherwise you're allocating and moving useless space around.

The only "downside" is a memcpy (or however they implement it) of a single plane for conversion, which is totally irrelevant performance-wise.

StainlessS
7th September 2017, 00:06
It will be faster and use less memory to use Y8. Otherwise you're allocating and moving useless space around.


I dont see nottin wrong there, I could be mistaken. (EDIT: bit pissed).

MysteryX
7th September 2017, 03:00
Frame data need to allocated and passed for every filter. Y12 clip requires twice as much memory as Y8 clips. Since allocation is no more of a performance issue than memcpy, it probably won't have any performance effect other than taking twice as much memory (3x with Y24), unless some filter in the chain doesn't exclude the chroma planes.

There's just no downside to using Y8 (unless you want to support AVS 2.5.8)

If you're wondering how it works behind the scene, Y12 stores its data in 3 separate memory areas (planes). The simplest filter would take each plane a memcpy into the destination frames. ConvertToY8 simply memcpy the first plane. Not really any conversion there; it just drops unnecessary planes.

burfadel
7th September 2017, 05:30
That makes sense, I'll change it just to Y8 on the weekend as well as the next feature:). Memory efficiency is also important.

MysteryX
7th September 2017, 07:17
To work in Linear Light. When you get the Diff mask containing the noise, reverse the 2.2 gamma curve and you're in Linear Light. Do your noise processing on that, then apply the 2.2 gamma curve back before adding the noise to the clip. This might solve a bunch of things.

StainlessS
7th September 2017, 14:27
Y12 stores its data in 3 separate memory areas (planes). The simplest filter would take each plane a memcpy into the destination frames.

Actually, I think that only 1 memory block is allocated (large enough for all three planes + padding to ensure planes aligned on some boundary) , the plane pointers are merely set to point to an offset within that memory block.

MysteryX
7th September 2017, 16:24
Correct; so the only difference is double(or triple) memory usage then; and more complex syntax.

burfadel
7th September 2017, 20:43
I used ConvertToY8 for the Luma processing, memory use seems the same, and if anything it's fractionally slower? I have to convert the Y8 only back to YV12/YV16/YV24 to merge with chroma otherwise the merge commands gives the error it must be the same format. Is there are way of doing that without conversion? Does the conversion do anything more than just add two blank chroma channels until the chroma/luminance merge?

Below is a test script with function mCleanY8. It's identical to the latest mClean apart from processing luma in Y8. If it is faster or uses less RAM that's good, but if it isn't it would be good to try and track down the reason why since it should be faster due to less memory data copied etc, or at least, absolutely not slower :).

What are the encoding speeds for mClean and everything exactly the same except using mCleanY8? Does the latest RGTools functions properly support Y8, in that if it is Y8 that it automatically doesn't even consider phantom chroma channels?

# mClean spatio/temporal denoiser
# Version: 1.5e (06 September 2017)
# By burfadel

### *** For TESTING only *** ###
### *** Function mCleanY8 *** ###


function mCleanY8(clip c, int "thSAD", int "blksize", int "blksizeV", int "overlap", int "overlapV", int "enh", int "rn", int "outbits", int "cpu")
{
defH = Max (C.Height, C.Width/4*3) # Resolution calculation for auto blksize settings
thSAD = Default (thSAD, 450) # Denoising threshold
blkSize = Default (blkSize, DefH<360 ? 8 : DefH<750 ? 12 : DefH<1200 ? 16 : DefH<1600 ? 24 : 32) # Horizontal block size for MDegrain4
blksizeV = Default (blksizeV, blksize) # Vertical block size for MDegrain4, default same as horizontal
overlap = Default (overlap, blksize>4?(blksize/4+1)/2*2:0) # Horizontal block overlap
overlapV = Default (overlapV, blksize>4?(blksizeV/4+1)/2*2:0) # Vertical block overlap
enh = Default (enh, 20) # Detail enhancement (detail orientated sharpen) strength
rn = Default (rn, 13) # ReNoise strength from 0 (disabled) to 20
outbits = Default (outbits, c.BitsPerComponent) # Output bits, default input depth
calcbits = c.BitsPerComponent == 8 ? 12 : c.BitsPerComponent
calcbits = outbits > calcbits ? calcbits : outbits
cpu = Default (cpu, 4) # Threads for fft3dfilter


Assert(isYUV(c)==true, """mClean: Supports only YUV formats (YV12, YV16, YV24)""")
Assert(isYUY2(c)==false, """mClean: Supports only YUV formats (YV12, YV16, YV24)""")
Assert(isYV411(c)==false, """mClean: Supports only YUV formats (YV12, YV16, YV24)""")
Assert(enh>=0 && enh<=54, """mClean: "enh" ranges from 0 to 54""")
Assert(rn>=0 && rn<=20, """mClean: "rn" ranges from 0 to 20""")
Assert(outbits>=8 && outbits<=16, """mClean: "outbits" ranges from 8 to 16""")

padX = c.width%4 == 0 ? 0 : (4 - c.width%4)
padY = c.height%4 == 0 ? 0 : (4 - c.height%4)
c = padX+padY<>0 ? c.pointresize(c.width+padX, c.height+padY, 0, 0, c.width+padX, c.height+padY) : c
cy = c.ConvertToY8()

# Spatio/temporal chroma noise filter
filt_chroma = fft3dfilter (c, bw=blksize*2, bh=blksizeV*2, ow=overlap*2, oh=overlapV*2, sharpen=0.12, bt=3, ncpu=cpu, dehalo=0.3, sigma=2.35, plane=3)

# Temporal luma noise filter
super = cy.MSuper (hpad=16, vpad=16)
bvec4 = MAnalyse (super, isb = true, delta = 4, blksize=blksize, blksizeV=blksizeV, overlap=overlap, overlapV=overlapV, search=5, searchparam=13)
bvec3 = MAnalyse (super, isb = true, delta = 3, blksize=blksize, blksizeV=blksizeV, overlap=overlap, overlapV=overlapV, search=5, searchparam=8)
bvec2 = MAnalyse (super, isb = true, delta = 2, blksize=blksize, blksizeV=blksizeV, overlap=overlap, overlapV=overlapV, search=5, searchparam=5)
bvec1 = MAnalyse (super, isb = true, delta = 1, blksize=blksize, blksizeV=blksizeV, overlap=overlap, overlapV=overlapV, search=5, searchparam=3)
fvec1 = MAnalyse (super, isb = false, delta = 1, blksize=blksize, blksizeV=blksizeV, overlap=overlap, overlapV=overlapV, search=5, searchparam=3)
fvec2 = MAnalyse (super, isb = false, delta = 2, blksize=blksize, blksizeV=blksizeV, overlap=overlap, overlapV=overlapV, search=5, searchparam=5)
fvec3 = MAnalyse (super, isb = false, delta = 3, blksize=blksize, blksizeV=blksizeV, overlap=overlap, overlapV=overlapV, search=5, searchparam=8)
fvec4 = MAnalyse (super, isb = false, delta = 4, blksize=blksize, blksizeV=blksizeV, overlap=overlap, overlapV=overlapV, search=5, searchparam=13)
clean = cy.MDegrain4(super, bvec1, fvec1, bvec2, fvec2, bvec3, fvec3, bvec4, fvec4, thSAD=thSAD)
clean = clean.dctfilter (1,1,1,1,1,1,0.50,0)

clean = calcbits != clean.BitsPerComponent ? clean.ConvertBits(calcbits) : clean
cy = calcbits != c.BitsPerComponent ? cy.ConvertBits(calcbits) : cy

# Masks for spatial noise reduction and noise independent detail enhancement
noised = mt_makediff (clean, cy)
noise = mt_binarize (clense(mt_makediff(mt_binarize(noised), mt_edge(sharpen(clean, 0.82), "prewitt"))))

# Spatial luma denoising
clean2 = mt_merge (clean, removegrain(clean, 18), noise)

# Unsharp filter for spatial detail enhancement
clsharp = (enh>=51<=54) ? mt_adddiff (blur(mt_makediff(clean, gblur(clean2, (enh-19), sd=3)),0.8,0), clean2) :
\ (enh>0<=50) ? mt_adddiff (blur(mt_makediff(clean, blur(clean2, 1.58*(0.03+(0.97/50)*enh))),0.5,0), clean2) : clean2
diff = (enh>0) ? mt_makediff(clean2, clsharp) : nop()
diff2 = (enh>0) ? diff.temporalsoften(1,255,0,32,2) :nop()
clsharp = (enh>0) ? mt_makediff(clean2, mt_lutxy(diff,diff2, "x 128 - y 128 - * 0 < x 128 - 100 / " + string(40)
\ + " * 128 + x 128 - abs y 128 - abs > x " + string(40) + " * y 100 " + string(40) + " - * + 100 / x ? ?")) : clsharp

# If selected, combining ReNoise
renoise = (rn==0) ? nop() : tweak(temporalsoften (noised, 3, 160, 0, scenechange=0, mode=2), cont=1.025+(0.022*(rn/20)))
clean2 = (rn>0<=20) ? mt_merge(clean2, mergeluma (clean2, mt_adddiff(clean2, renoise), 0.3+(rn*0.035)),
\ mt_logic(noised, mt_binarize(clean, 24), mode="andn")) : clean2

# Combining spatial detail enhancement with spatial noise reduction using prepared mask
filt_luma = mt_merge (clean2, clsharp, mt_invert(noise))

# Converting bits per channel and luma format
filt_luma = outbits < filt_luma.BitsPerComponent ? ConvertBits(filt_luma, outbits, 1) : filt_luma
filt_luma = c.isYV12() == true ? ConvertToYV12(filt_luma) :
\ c.isYv16() == true ? ConvertToYV16(filt_luma) : ConvertToYV24(filt_luma)
filt_chroma = filt_chroma.BitsPerComponent <> filt_luma.BitsPerComponent ? ConvertBits(filt_chroma, BitsPerComponent(filt_luma)) : filt_chroma

# Combining result of luma and chroma cleaning
output = mergechroma (filt_luma, filt_chroma)
return padX+padY<>0 ? output.pointresize(c.width-padX, c.height-padY, 0, 0, c.width-padX, c.height-padY) : output
}

StainlessS
7th September 2017, 20:52
You could try this stuff here (swap et al):- http://avisynth.nl/index.php/Swap
I cant try, not got avs+ set up.

EDIT:
Does FFT3DFilter require padding ?

Think AVS+ requires alignment mod 32, your padding might require additional blit/copy.

Would crop be quicker than point resize at end ?

burfadel
7th September 2017, 21:16
You could try this stuff here (swap et al):- http://avisynth.nl/index.php/Swap
I cant try, not got avs+ set up.

EDIT:
Does FFT3DFilter require padding ?

Think AVS+ requires alignment mod 32, your padding might require another blit.

Would crop be quicker than point resize at end ?

The swap stuff looks like it could work! I borrowed the pointresize from deblock_QED as mentioned in the first post, apparently they changed from using addborders and crop to pointresize, so I presumed it was done for a reason. I'll have to look up where the padding occurs in point resize and try it out with addborders/crop borders.

If AVS+ requries mod 32, that's easy, since mod 32 is also mod 4.

StainlessS
7th September 2017, 21:20
Didee Padding


function Padding(clip c, int left, int top, int right, int bottom) {
# Didee: http://forum.doom9.org/showthread.php?p=1596804#post1596804
# eg, Padding(32,32,0,0).Padding(0,0,32,32)
w = c.width()
h = c.height()
c.pointresize( w+left+right, h+top+bottom, -left, -top, w+left+right, h+top+bottom )
}


EDIT: Crop defo faster if at end of chain on avs standard (but your func dont work on that so is moot).

EDIT: Dont use Addborders, Padding edge mirroring [EDIT: duping] better.


Avisource("F:\v\Cabaret.avi")
Robocrop()
Padding(32,32,32,32)
return last


https://s20.postimg.org/tjzxvemm5/cabaretpadding.jpg (https://postimages.org/)

EDIT: MSuper will be padding again

MSuper

MSuper (
clip,
int hpad (8),
int vpad (8),
int pel (2),
int levels (0),
bool chroma (true),
int sharp (2),
int rfilter (2),
clip pelclip (undefined),
bool isse,
bool planar,
bool mt (true)


EDIT:

Avisource("F:\v\Cabaret.avi")
Robocrop()
MOD=64 # Or whatever
padW = ((width+MOD-1)/MOD*MOD) - Width # Required additional width mod MOD
LeftW=PadW/4*2
RightW=padW-LeftW
Padding(LeftW,0,RightW,0)
return last.Info

burfadel
7th September 2017, 21:46
That's good! I can change it so it pads to 32 on for instance, bottom and right, and crops that at the end.

StainlessS
7th September 2017, 22:01
Above EDIT, is not really padding (neither is yours, is only achieving mod MOD width).

Below will I think ensure padding and also produce mod MOD width.

Avisource("F:\v\Cabaret.avi")
Robocrop() # source has big borders
ORGW=Width
ORGH=Height
PADW=16 # Minimum Padding required either side (0 or more)
PADH=16 # Minimum Padding required top/bot (0 or more)
MODW=32 # width must be modulo MODW
MODH=2 # height must be modulo MODH (32 probably excessive, some encoders require modulo 8, I think, )
AddW = ((width+PADW*2+MODW-1)/MODW*MODW) - Width # Required additional width mod MODW
LeftW=AddW/4*2 RightW=AddW-LeftW
AddH = ((height+PADH*2+MODH-1)/MODH*MODH) - Height # Required additional height mod MODH
TopH=AddH/4*2 BotH=AddH-TopH
Padding(LeftW,TopH,RightW,BotH)
RT_debugF("PADW=%d MODW=%d PADH=%d MODH=%d LeftW=%d RightW=%d TopH=%d BotH=%d Width=%d(Orig Width=%d) Height=%d(Orig Height=%d)",
\ PADW,MODW,PADH,MODH,LeftW,RightW,TopH,BotH,Width,ORGW,Height,ORGH)
return last.Info



EDIT: Without proper padding, can result in weird disappearing reappearing things on edges of frame.
(only noticed left and right, never seen on top or bottom).

EDIT: Added ORGW

EDIT: Fiddled with it a bit more.

EDIT: Reduced MODH to 2, Fiddled with it a bit more. Can also set MSuper(hpad=0,vpad=0) as we already did it.

burfadel
10th September 2017, 11:46
Updated the first post with version 1.6.

If you extract the luminance channel such it is just the Y channel, obviously it is a different format to the YUV clip such as output from the chroma filtering. You first have to convert back to the same format before combining. This is fine, however when you are in more than 8 bits, the commands such as isYV12() etc do not work since it is expecting a YUV420Y8 clip for it to be true. Since it is a YUV420P10 clip (for example) the command isYV12() seems to give back false. If you run convertToYV12(), it keeps the higher bitdepth of 10 etc, so isYV12() still doesn't work. This, and possibly other little issues, are problematic when you want to have the output clip the same format as the input clip and it isn't 8-bit etc. This is why I put back the 'do not process' flags to the luma commands, it was either that or dropping support for YV16 and YV24, which obviously isn't desirable.

It works fine as is because the bit depths can be matched easily and the pixel type hasn't changed. I could do a convertbits() just so isYV12() etc works, but then it's an extra unnecessary bit of processing that will probably affect speed more than what was gained by converting it to Y8 and going back to the desired pixel type.

MysteryX
10th September 2017, 14:38
Use IsYUV420(), IsYUV422() and ISYUV444()

raffriff42
10th September 2017, 14:43
Updated the first post with version 1.6.

If you extract the luminance channel such it is just the Y channel, obviously it is a different format to the YUV clip such as output from the chroma filtering. You first have to convert back to the same format before combining. This is fine, however when you are in more than 8 bits, the commands such as isYV12() etc do not work since it is expecting a YUV420Y8 clip for it to be true. Since it is a YUV420P10 clip (for example) the command isYV12() seems to give back false. If you run convertToYV12(), it keeps the higher bitdepth of 10 etc, so isYV12() still doesn't work. This, and possibly other little issues, are problematic when you want to have the output clip the same format as the input clip and it isn't 8-bit etc. This is why I put back the 'do not process' flags to the luma commands, it was either that or dropping support for YV16 and YV24, which obviously isn't desirable.

It works fine as is because the bit depths can be matched easily and the pixel type hasn't changed. I could do a convertbits() just so isYV12() etc works, but then it's an extra unnecessary bit of processing that will probably affect speed more than what was gained by converting it to Y8 and going back to the desired pixel type.
Don't use IsYV12, use Is420 (http://avisynth.nl/index.php/Clip_properties#Color_Format) -- works at any bit depth.
Don't use ConvertToYV12, use ConvertToYUV420 (http://avisynth.nl/index.php/Convert).
To combine the planes again, use CombinePlanes (http://avisynth.nl/index.php/CombinePlanes)(Y-clip, U-clip, V-clip, "YUV") (for one example)

MysteryX
10th September 2017, 16:11
This issue has several issues. On 1080p content, it throws a validation error on enh. Then it crashes on gBlur value being out of range. And then, output doesn't have the right dimensions!

I used Spline36Resize(Width/2, Height/2) to test and work around the bug, so I'm testing on 960x540

Performance-wise -- Prefetch(8)

v1.3c

FPS (min | max | average): 0.239 | 9584 | 14.75
Memory usage (phys | virt): 582 | 695 MiB
Thread count: 36
CPU usage (average): 55%

v1.6

FPS (min | max | average): 0.915 | 15800 | 13.10
Memory usage (phys | virt): 1031 | 1162 MiB
Thread count: 38
CPU usage (average): 61%


It felt a lot slower, but it mostly just takes longer to initialize but then performance isn't far off v1.3c. However... memory usage is twice higher!!! And that's at HALF the resolution. In a x86 processing chain, this leaves me no room for much other processing.

As for versioning, it's bad practice to use major/minor version numbers for development versions. Agree, Microsoft did it with Windows Millenium and Vista, but it's bad practice. You'd get to v1.35 before having a script ready for production. Generally, most people use version numbers below 1 for alpha/beta/dev versions, and for FrameRateConverter, I just put the date instead of a version number until it was finalized.

Semantic Versioning Guidelines (http://semver.org/)

Major version zero (0.y.z) is for initial development. Anything may change at any time. The public API should not be considered stable.


How should I deal with revisions in the 0.y.z initial development phase?

The simplest thing to do is start your initial development release at 0.1.0 and then increment the minor version for each subsequent release.

Also keeping a development history is good practice.

So far v1.3c is what works best. I can't easily test this version side-by-side because frame dimensions don't match.


Btw could you explain this line?

noise = mt_binarize (clense(mt_makediff(mt_binarize(noised), mt_edge(sharpen(clean, 0.82), "prewitt")), grey=true))


I see you're using this to exclude blacks from renoise, but I don't think that's a good way to do it. Have you tried applying a gamma curve to the noise you want to insert back using mt_lut?
motion_mask = mmask(noised, fvec3, kind=1, ml=120).mt_binarize(32, u=1, v=1)

burfadel
10th September 2017, 19:17
A big difference between 1.3C and higher is changing from MDegrain2 to MDegrain4, which gives better output. Working in Y plane only should reduce this memory usage, I should also be able to recover some speed etc in places. The motionmask has a mt_binarize to create a hard mask, the soft mask is simply too weak for it. As for the dimensions, it's the padding and cropping afterwards, I guess that's why pointresize and not crop is used at the end in the padding script. Pointresize is used so you can add non-mod based padding, addborders requires mod based depending on the pixel type.

As for the renoise, not sure how to apply mt_lut to it such that it is only applied on non-dark areas. I'll give it another try.

DarkNite
10th September 2017, 21:22
Cranked up a test run of mClean 1.6 last night, and have been faced with two errors. First error was upon initial loading of the script: Enh reported only accepting ranges of 0-54 when using mClean(), so I defined enh as 15, and was off to the races. Come back this morning to a job that is 5 frames from completion, and an Out of memory (Unable to allocate 2992224 bytes) error from Virtualdub FilterMod. Both are firsts for me, at least as far as mClean is concerned.

burfadel
10th September 2017, 22:04
That out of memory issue is weird, a tad shy of 3 MB. Maybe I'm triggering a small memory leak somewhere since once the memory is initially allocated it shouldn't increase with time. I have to admit 1.6 does have bugs, but when it works it's good... makes testing interesting! Later today my time I'll post 1.6b with the necessary fixes. I have two different 'concepts' that may help further with speed without quality loss, unfortunately if they work it will make the code look a bit dodgy with people scratching their heads as to why i did it in such a way. The goal of course is efficacy of the concept, not how it looks in script!

MysteryX
11th September 2017, 00:16
As for the renoise, not sure how to apply mt_lut to it such that it is only applied on non-dark areas. I'll give it another try.
First of all, what is causing the extra artifacts in dark scenes? Is it a "bug" with MDegrain that causes that?

Generally, when there are issues only on dark (or bright) scenes, it's an indication that it's an issue with the gamma curve, where if you apply a value linearly, it will have a large impact on dark/bright scenes and low impact on middle values. This might be the case here.

Just try applying a 2.2 gamma curve on the noise and see what it does. Syntax is mt_lut("x 2.2 ^")

burfadel
11th September 2017, 10:39
Updated first post to version 1.7

Changes:

fixed errors introduced with 1.6
changed analysis, should be faster without motion quality loss; in testing it was either the same or fractionally better depending on the motion
luma now processed as a single plane (Y12 default)
changed application of renoise, application processes only above luma value 20, application scaled from 0 at value 20 to 100 at values 40 and above


It wasn't a linear light issue. If applied the given linear light formula it basically looks like it boosts gamma by 2.2?

Let me know how the new analysis works. Should be faster with same or better perceived quality.

MysteryX
11th September 2017, 16:32
Doing some tests. First thing I'm noticing is that it's applying a strong sharpening to scenes without noise, which is a no-no (unless I really want to run a sharpener which I don't). It uses enh=35 for 1080p. Just lowering it a bit works. 25 is too soft, 30 is too sharp, 28 is good.

Original / mClean v1.3c / mClean(enh=28) / mClean(enh=35)


https://s26.postimg.org/ouxvtt66d/3593_original2.png (https://postimg.org/image/ouxvtt66d/) https://s26.postimg.org/kgkt48685/3593_mclean13c.png (https://postimg.org/image/kgkt48685/) https://s26.postimg.org/idadwk6f9/3593_mclean28.png (https://postimg.org/image/idadwk6f9/) https://s26.postimg.org/6e3h2zq85/3593_mclean35.png (https://postimg.org/image/6e3h2zq85/)

This latest version is causing color shifts (SBS logo)
Original / mClean
https://s26.postimg.org/g33w2u3yd/colorshift1.png (https://postimg.org/image/g33w2u3yd/) https://s26.postimg.org/jnzrm28hx/colorshift2.png (https://postimg.org/image/jnzrm28hx/)

Mostly all it does with the KARA - Dazzling Red (https://www.youtube.com/watch?v=YOnAVn6iRf8) clip is over-sharpen and distort colors. Can't really compare because of color shifts.

On my 288p VCD upscaled to 768p (full script and replacing the denoiser line). enh=15 is too much but enh=7 works. Interestingly enough, renoise makes very little difference at all ... if anything, it just add a little bit of sharpening? This version is doing a good job here, keeping the right details and discarding the right things.

No denoise / mClean v1.3c / mClean(enh=7) / mClean(enh=15)
https://s26.postimg.org/ijfcrjhqd/5079_original.png (https://postimg.org/image/ijfcrjhqd/) https://s26.postimg.org/n41j6h1fp/5079_mclean13c.png (https://postimg.org/image/n41j6h1fp/) https://s26.postimg.org/bp521up39/5079_mclean7.png (https://postimg.org/image/bp521up39/) https://s26.postimg.org/86t25go79/5079_mclean15.png (https://postimg.org/image/86t25go79/)

Other types of noise, however, it's not doing anything expect sharpening the noise and distorting colors again
No denoise / mClean(enh=7)
https://s26.postimg.org/rfuq97xqd/3466_no.png (https://postimg.org/image/rfuq97xqd/) https://s26.postimg.org/en6m9am4l/3466_mclean.png (https://postimg.org/image/en6m9am4l/)

Here, it's adding several distortions compared to v1.3c, such as loosing the teeth
No denoise / mClean v1.3c / mClean(enh=7)
https://s26.postimg.org/vwroln1c5/4257_no.png (https://postimg.org/image/vwroln1c5/) https://s26.postimg.org/jthrkc0w5/4257_mclean13c.png (https://postimg.org/image/jthrkc0w5/) https://s26.postimg.org/pge4ht3et/4257_mclean.png (https://postimg.org/image/pge4ht3et/)

In terms of versioning, changing the major number from 1 to 0 would respect the conventions; then each dev build could keep incrementing the way you're doing (0.7, 0.8, etc)

burfadel
11th September 2017, 19:19
I can change enh setting, just thought of a better - and obvious in many ways, way applying the strength. I didn't think to test with VCD resolution. The colour shift is easily fixed, again it's possibly a function of resolution. I'd have to find some clips where it doesn't work as well as previous versions as it's some simple fine tuning. 1.7 should be faster?

MysteryX
11th September 2017, 20:28
download KARA - Dazzling Red (link above) and try on it

ChaosKing
12th September 2017, 15:16
@burfadel will you port this script to vapoursynth too? I find it quite effective against heavy noise, with better "color stabilising" than smdegrain. (at least for my anime source)

MysteryX
13th September 2017, 04:33
In terms of performance (with prefetch 8)

v1.6 - 540p

FPS (min | max | average): 0.915 | 15800 | 13.10
Memory usage (phys | virt): 1031 | 1162 MiB
Thread count: 38
CPU usage (average): 61%


v1.7 - 540p

FPS (min | max | average): 1.557 | 21067 | 21.87
Memory usage (phys | virt): 859 | 932 MiB
Thread count: 35
CPU usage (average): 68%


v1.7 - 1080p

FPS (min | max | average): 0.768 | 64957 | 5.620
Memory usage (phys | virt): 1702 | 2026 MiB
Thread count: 41
CPU usage (average): 69%


I think you've just designed the most expensive plugin in Avisynth in terms of memory usage. Makes it difficult to insert it into a script -- almost have to run it separately with an intermediary AVI file.

Groucho2004
13th September 2017, 06:45
In terms of performance
Can you post the script with which which you obtained these results?

burfadel
13th September 2017, 11:57
In terms of performance (with prefetch 8)

v1.6 - 540p

FPS (min | max | average): 0.915 | 15800 | 13.10
Memory usage (phys | virt): 1031 | 1162 MiB
Thread count: 38
CPU usage (average): 61%


v1.7 - 540p

FPS (min | max | average): 1.557 | 21067 | 21.87
Memory usage (phys | virt): 859 | 932 MiB
Thread count: 35
CPU usage (average): 68%


v1.7 - 1080p

FPS (min | max | average): 0.768 | 64957 | 5.620
Memory usage (phys | virt): 1702 | 2026 MiB
Thread count: 41
CPU usage (average): 69%


I think you've just designed the most expensive plugin in Avisynth in terms of memory usage. Makes it difficult to insert it into a script -- almost have to run it separately with an intermediary AVI file.

I'm guessing you're running 32-bit Avisynth? I suspect it's the recalc, great for performance but obviously not for memory when combined with multithreading! I can think of two alternatives, I'll have to figure which one is more appropriate.

Groucho2004
13th September 2017, 14:25
Ran a few tests (i5 2500K @ 4GHz / 4 cores, AVS+ 64 bit):

[Script]
#SetMemoryMax(3000)
colorbars(width = 1920, height = 1080, pixel_type = "yv12").killaudio().assumefps(25, 1).trim(0, 99)
#ConvertTo16Bit()
mclean()
#Prefetch(4)


8 bit video (YV12):
FPS (min | max | average): 0.979 | 18.07 | 4.996
Memory usage (phys | virt): 582 | 587 MiB
Thread count: 15
CPU usage (average): 27%

16 bit video (YUV420P16):
FPS (min | max | average): 0.377 | 2.532 | 1.503
Memory usage (phys | virt): 741 | 745 MiB
Thread count: 15
CPU usage (average): 26%

16 bit video with Prefetch(4):
FPS (min | max | average): 0.699 | 85227 | 3.964
Memory usage (phys | virt): 1269 | 1471 MiB
Thread count: 19
CPU usage (average): 77%

16 bit video with Prefetch(8) without using SetMemoryMax():
FPS (min | max | average): 0.029 | 325413 | 1.090
Memory usage (phys | virt): 1636 | 1954 MiB
Thread count: 35
CPU usage (average): 81%

16 bit video with Prefetch(8) and SetMemoryMax(3000):
FPS (min | max | average): 0.508 | 325413 | 5.097
Memory usage (phys | virt): 2194 | 2535 MiB
Thread count: 44
CPU usage (average): 99%


- 16 bit processing is a lot slower than 8 bit
- Using Prefetch(8) needs SetMemoryMax() to be set to ~3000 or performance drops massively

MysteryX
13th September 2017, 15:27
Can you post the script with which which you obtained these results?

sure

LWLibavVideoSource("source.mp4")
mclean()
Prefetch(8)


Using Prefetch(8) needs SetMemoryMax() to be set to ~3000 or performance drops massively
That's not even an option with x86

Groucho2004
13th September 2017, 15:29
That's not even an option with x86Why not? I just tried it, works just fine, although a bit slower than AVS+ 64 bit. Obviously that only works on a 64 bit OS.

MysteryX
13th September 2017, 16:40
Isn't x86 limited to 2GB? There's the 3GB patch but even with that I've had crashes when approaching the 2GB limit. (maybe related to AvisynthShader, haven't tested in a while)

Groucho2004
13th September 2017, 16:47
Isn't x86 limited to 2GB?
Yes, on 32 bit Windows (there are OS hacks but I wouldn't recommend them)

There's the 3GB patch but even with that I've had crashes when approaching the 2GB limit.The 3 GB patch only applies to 32 Bit Windows. So, you're on 32 bit Windows?

If the application that uses 32 Bit Avisynth is built with the LARGEADDRESSAWARE linker switch it can address up to 4 GB on a 64 Bit OS.

burfadel
13th September 2017, 17:14
I'll see what I can do regarding memory when I can, most likely tomorrow. I think I know the cause and possible useful alternative options.

MysteryX
13th September 2017, 17:21
If the application that uses 32 Bit Avisynth is built with the LARGEADDRESSAWARE linker flag it can address up to 4 GB on a 64 Bit OS.
I'll have to test this some more

Without SetMemoryMax

FPS (min | max | average): 0.762 | 28.59 | 6.270
Memory usage (phys | virt): 1710 | 2022 MiB
Thread count: 35
CPU usage (average): 77%


With SetMemoryMax

FPS (min | max | average): 0.264 | 61.48 | 6.238
Memory usage (phys | virt): 2201 | 2510 MiB
Thread count: 35
CPU usage (average): 79%


I'm not seeing much of a difference here (8-bit)

Yes, I noticed that 16-bit is a LOT slower; it's either FFT3DFilter or MvTools2 that isn't well optimized. To know for sure, we'd have to test on a Y8 clip. If it's 2x slower in 16-bit, it's FF3DFilter. If it's 5x slower, it's MvTools2.

mClean crashes on a Y8 clip!

Groucho2004
13th September 2017, 17:24
I'm not seeing much of a difference here (8-bit)Try it with a 1080p 16 bit source.

MysteryX
13th September 2017, 17:46
Try it with a 1080p 16 bit source.
Generally not much point in running MvTools2 in 16-bit anyway.

Unless your source is a 10-bit clip, this goes at the beginning and your source is 8-bit.

Unless you have a Deblocker (it doesn't support 16-bit yet) and you get that output in 16-bit; then perhaps it would be best to have that deblocking integrated within mClean.

Groucho2004
13th September 2017, 18:30
I'll see what I can do regarding memory when I can, most likely tomorrow. I think I know the cause and possible useful alternative options.Considering the results I got, I don't think the memory consumption is excessive. MCTemporalDenoise for example uses a lot more memory from what I recall.

Of course, if you run 8 threads with a temporal filter you need plenty of memory.

burfadel
15th September 2017, 12:51
Well, no luck trimming the memory down, but on x64 systems it shouldn't be an issue at all! It's a limitation of MVTools2, I think there may be a way to trim it a bit (script wise) but I'll have to test it.

In terms of testing, I've posted an updated script for people to play with. Only 'minor' changes (this term is relative depending on what you are referring to!).

MysteryX
15th September 2017, 17:24
Have you made corrections to color distortions?

It would be useful to know what the changes are so we can know what to look for.

burfadel
15th September 2017, 23:03
Difference is just heavy tweaking to the noise analysis for speed and quality. It's based on the latest MVTools2 and Avisynth by pinterf, not sure how it will perform using the old versions. The analysis is used for two different things, so the settings chosen are based on leveraging the most ideal settings for both. Fvec and bvec 2 through 4 is noise analysis purely, to a large extent at least the motion analysis is meaningless apart from determining what are moving objects and what is temporal noise. The quality of the motion therefore isn't overly important considering the temporal radius. The important motion analysis is either side of the current frame, which is why the analysis is more precise there. I believe slight colour issue is resolved, it was just an issue with tweaking in preparation for the next feature.

MysteryX
16th September 2017, 16:43
Differences with v1.7 are very minor in most cases. This frame shows more differences.

no denoise / mClean 1.3c / mClean 1.7(enh=7) / mClean 1.7b

https://s26.postimg.org/kmommrt6t/6890_no.png (https://postimg.org/image/kmommrt6t/) https://s26.postimg.org/ussms0o79/6890_mclean13.png (https://postimg.org/image/ussms0o79/) https://s26.postimg.org/6qbsx57k5/6890_mclean17.png (https://postimg.org/image/6qbsx57k5/) https://s26.postimg.org/5pg5lrfyd/6890_mclean17b.png (https://postimg.org/image/5pg5lrfyd/)

First, enh is now almost good with its default value. Just slightly too strong. However, because it's a value relative to the resolution, it's hard to tweak it manually. Let's say I want to always set it 10% weaker, I need to look at the script and calculate it based on the resolution every time.

Then, there are slight improvements over 1.7

However, I still get better results with v1.3c!

Finally, look at "Home Karaoke", it looks fine in v1.3 but appears ugly and distorted in both v1.7 and v1.7b

Does v1.7 work better in other clips such as the snowflakes clip?

There are still color distortions but it's slightly better than 1.7. It might be enough to cross the line where it's 'acceptable', although it is still an undesirable effect for a denoiser.

v1.3c is still the one that works best according to my tests. Perhaps there could be an option for an alternative mode (MDegrain2 or MDegrain4) when a clip doesn't react well.

lansing
17th September 2017, 03:59
Ok here's a good all around sample clip with temporal and spatial grain for the OP to test. It's a pain to watch MysteryX testing a denoiser with videos that have no grain and vcd that has nothing but compression artifacts.
sample (http://www.mediafire.com/file/nk5tbg5fo5stkcf/anime_low_light_thin_line_sample.264)

burfadel
17th September 2017, 16:28
Thanks, I'll check that out as soon as I am able :).

manolito
17th September 2017, 16:37
Out of curiosity I applied my usual weapon of choice (DegrainMedian plus FineSharp) to lansing's sample. I had to convert the source to DVD compliant MPEG2, my computer does not do HD.

I liked the result quite a bit, and just for fun I made another conversion using Kassandro's old DenoiseSharpen plugin in mode 22. And even this conversion doesn't look too bad.

Needless to say that I cannot use mClean on my old machine, and even on a more capable Core i5 CPU the low speed is not worth the small improvement (which almost nobody will be able to detect when watching the clip - not stills).

Get the results here:
http://www101.zippyshare.com/v/v89ieI57/file.html
https://www.sendspace.com/file/kvtmts


Cheers
manolito

burfadel
17th September 2017, 17:39
I'm not doing mClean to support older systems, I'm trying to leverage the latest versions of the plugins used without compromise if possible. Temporal denoisers work differently to spatial denoisers. Spatial denoisers work on luma/choma differences over a given area for a given frame. They can remove noise but can also wipe out finer detail. They also don't work well on temporal changes as you can still see temporal noise in the video. As a result of the way spatial denoisers work, areas of difference such as lines etc also become less clear, so the result is effectively a lower spatial resolution image in terms of the detail. This is bound to happen due to the averaging over a given area of pixels that the spatial denoiser does. The stronger the spatial denoiser (like removegrain(mode=20), the lower the effective spatial resolution.

I just tried mClean against knlmeanscl(s=5,d=1,h=2.0), removegrain etc, I have to say I like the results of mClean best using just mClean() default settings... obviously you would expect me to say that, but it's true! I'll tweak some things a little more on the luma side of things, then go to work on the chroma adjustments. I've already made some minor adjustments that result in a tad better speed.

MysteryX
17th September 2017, 18:15
It's a pain to watch MysteryX testing a denoiser with videos that have no grain and vcd that has nothing but compression artifacts.
It's good to test on a variety of formats
- SD with encoding artifacts
- HD with grain only in limited areas
- grainy video

You're testing as a degrainer, but it's doing a pretty good job as a denoiser.

Burdafel... Burfadel, anything you can do to fix the image distortions pointed above? I'd want to move on from v1.3c

burfadel
17th September 2017, 18:31
I wasn't going to post it as there are only minor changes, but try 1.7c. I'm not claiming it will fix what you described, but the output may be slightly different.

Taurus
17th September 2017, 18:54
@ lansing:
Thanks for the sample!
After all this illnoised stuff MysteryX posted, what a relief :D
I think the dancing grain is artificial generated?
Mad producers :devil:.
So far good ol TemporalDegrain seems to be the only script that can handle this stuff eyepleasing.
(Thanks to Sagekilla and Didee for this wonderful piece of work!)
I shranked your 55Mb file down to 5.05Mb downscaled with BicubicResizeMT(1280,720,-0.4,0.20) + Temporaldegrain
With mClean nearly the same (5.44Mb).
Warning! Temporaldegrain is deadslow, but worth the effort!
Sorry for being a little offtopic.

burfadel
17th September 2017, 19:19
It's not really off topic, it's an aim of mClean to not be too slow so that's good. The are still other minor improvements that can be done. There is something I can do that could be considered overkill, but if it doesn't affect speed too much and it is useful then why not :).

lansing
18th September 2017, 02:23
It's good to test on a variety of formats
- SD with encoding artifacts
- HD with grain only in limited areas
- grainy video

You're testing as a degrainer, but it's doing a pretty good job as a denoiser.

This just goes down to common sense, you don't test a vacuum cleaner on a clean floor.


then go to work on the chroma adjustments. I've already made some minor adjustments that result in a tad better speed.
Just take off the fft3dfilter filter on chroma, desaturation problem fixed.

@ lansing:
Thanks for the sample!
After all this illnoised stuff MysteryX posted, what a relief :D
I think the dancing grain is artificial generated?
Mad producers :devil:.
So far good ol TemporalDegrain seems to be the only script that can handle this stuff eyepleasing.
(Thanks to Sagekilla and Didee for this wonderful piece of work!)


MCTD surpassed it. And KNLMeansCL(s=4,h=3.0) did pretty good as well

burfadel
18th September 2017, 05:10
I found knlmeamscl to cause detail or edge sharpness loss. Denoising is ineffective if it also limits visual fidelity. The idea for mClean is to have something that's suitable for most situations, and be simple to use, and also not to be too slow. There's still a lot to do on it. As for chroma, it's just the sigma settings. There's a couple of things I haven't added for that yet also.

The texture like noise isn't removed so much in that anime image, also remember you can set renoise to 0. Noise removal doesn't have to be absolute, some noise can be visually pleasing watching at a normal distance, without those overly flat surfaces. This of course depends on the type and quality of noise. mClean is temporally stabilised which is why it is relatively bandwidth friendly with renoise.

MysteryX
18th September 2017, 07:15
This just goes down to common sense, you don't test a vacuum cleaner on a clean floor.
You have the pre-conceived idea that this is a degrainer that should be compared to TemporalDegrain and other degrainers.

The idea for mClean is to have something that's suitable for most situations, and be simple to use, and also not to be too slow.
This filter was never meant to be limited to grains. It is more meant as a replacement to KnlMeansCL denoising while preserving more details.

In fact, the tests so far show that it's giving better or more consistent results as a denoiser than as a degrainer.

Since it's meant to be suitable for most situations, it's good to test various scenarios. In my case, I'll test the videos I'm actually using it on (and the most difficult cases). I have very little videos with grain, except camera footage.

lansing
18th September 2017, 11:06
I found knlmeamscl to cause detail or edge sharpness loss. Denoising is ineffective if it also limits visual fidelity.
Here's the general order of sophistication of algorithm we have for spatial denoiser in avisynth now:
knlmeanscl>dfttest>fft3dfilter>removegrain

There's really no debate on its effectiveness, it's better than removegrain because it just has a better algorithm, end of debate.

As for chroma, it's just the sigma settings. There's a couple of things I haven't added for that yet also.
There's no point to tweak for chroma denoising because we cannot see them like 99% of the time. And I doubt that you tweak them by ExtractU/V+blown up brightness, so why even bother.

In addition to that, fft3dfilter's chroma denoise will desaturate color, people even use it as a derainbow alternative, so the best way to "fix" the problem is not use it.

The texture like noise isn't removed so much in that anime image, also remember you can set renoise to 0. Noise removal doesn't have to be absolute, some noise can be visually pleasing watching at a normal distance, without those overly flat surfaces. This of course depends on the type and quality of noise. mClean is temporally stabilised which is why it is relatively bandwidth friendly with renoise.
It's more like the removegrain inside mclean reached its limit than it chooses not to remove the grain.

Groucho2004
18th September 2017, 11:16
I'm also quite puzzled by the "loss of details" comments regarding KNLMeansCL. My experience with it (or any NLMeans algorithm) is the exact opposite. Maybe increasing the search window ("a") will yield better results.

MysteryX
18th September 2017, 17:52
Maybe increasing the search window ("a") will yield better results.
I've always been using KNLMeans with D=2, A=2

MysteryX
20th September 2017, 19:10
I did another test on a very grainy video take with my camera. In this case, KNLMeans wasn't doing a good job and I instead opted for SMDegrain.

Original / SMDegrain / mClean v1.3c / mClean v1.7c
https://s26.postimg.org/l3jjdk16t/Grain_original.png (http://postimg.org/image/l3jjdk16t/) https://s26.postimg.org/pdy79569x/Grain_smdegrain.png (http://postimg.org/image/pdy79569x/) https://s26.postimg.org/7k2p7un7p/Grain_mclean13.png (http://postimg.org/image/7k2p7un7p/) https://s26.postimg.org/ggdhbsdtx/Grain_mclean17.png (http://postimg.org/image/ggdhbsdtx/)

I lowered enh from 27 to 22. v1.3c does lose considerable details with such grains, and v1.7c does a lot better here. Much better output than SMDegrain.

I don't mind tweaking enh to what suits me best, but what is more of a problem is having to calculate its dynamic value before altering its value. You divine height by 40 for its default value. Perhaps it could set the division factor or something like that.

It's not giving the plastic effect, but it's not falling far. Without renoise, it would look like a plastic doll.

I can definitely use this instead of SMDegrain. v1.7c isn't giving as consistent results with low-quality VCDs though.

Note: the clip above is rather static. Animated scenes may give different results.

https://s26.postimg.org/ye7e72rr9/Grain2_original.png (http://postimg.org/image/ye7e72rr9/) https://s26.postimg.org/bzl4d3zlx/Grain2_mclean13.png (http://postimg.org/image/bzl4d3zlx/) https://s26.postimg.org/ycsuzx0jp/Grain2_mclean17.png (http://postimg.org/image/ycsuzx0jp/) https://s26.postimg.org/g16swihad/Grain2_mclean17.png (https://postimg.org/image/g16swihad/)

Here, unfortunately, it's blurring out my shirt.

SaurusX
20th September 2017, 19:18
I've always been using KNLMeans with D=2, A=2

I've found that changing the S value to S=1 also preserves more fine details. Star fields and the like. The higher the A value in combination with S=1, the better it seems to do.

That being said, I use D=1,A=3,S=1. Bumping up D to 2 can introduce encoding errors when you combine it with a lot of other temporal filters. The end effect seems to be minimal, especially for animated content.

EDIT FROM THE FAR FUTURE: Now I use D=3, A=1, S=1. S for the same reasons as before. A higher D does a better job of eliminating Gaussian random noise and keep details. A lower A provides less smoothing of large flat areas. I only increase A if I find that after removing the white noise with a high D that what's left behind is an uneven orange-peel or mottled look.

burfadel
21st September 2017, 00:03
If you can think if a better way to scale enh, since the greater the resolution the higher it needs to be. I've made some performance improvements to the script, and it also uses less memory. I've still got some tweaking to do on it. I've discovered search 0, 1, and 2 don't seem to be optimised, which makes sense, default 4 is practically faster. I wonder if optimisations from x265 could be incorporated, I think it will help with speed. Ideally STAR search should be ported and made default, it's as fast as UMH search in x265, sometimes faster, and provided results like exhaustive. The default search range in MVTools is quite small, a ported optimised STAR search would not only likely be faster but produce more accurate and nicer results. Remember the search algorithms in MVTools2 have been there for 10+ years, are unlikely fully optimised, and are the principle thing that is required by most functions. Other functions like MDegrain rely on it indirectly, but would still benefit hugely.

burfadel
21st September 2017, 06:06
Updated first post to v1.8. Speed improvements, memory use reduction, and better quality.

Atak_Snajpera
21st September 2017, 16:40
I did another test on a very grainy video take with my camera. In this case, KNLMeans wasn't doing a good job and I instead opted for SMDegrain.

https://s26.postimg.org/l3jjdk16t/Grain_original.png
This does not look like very grainy video to me! This is example of very grainy video
http://i.cubeupload.com/bhTQsW.png

burfadel
21st September 2017, 22:21
Is that grain changing every frame or is it consistent? If consistent the noise is basically a texture, so removal of that noise you are potentially removing detail. Spatial denoisers work on pixels or area of pixels as either a blank implementation, effectively acting as a blur, or difference between pixels to determine whether it should be removed. Temporal denoisers work by difference between frames, ideally in this case you track the movement over the temporal radius. Spatial denoisers were traditionally preferenced because they are generally fast and can be good at damping and softening static noise at the risk of losing detail. No denoiser can be perfect, because you are fundamentally changing the picture.

What did you think of v1.8? The results are 'quite different'. I might have to chase up a couple of things in MVTools2 with PinterF, the issues don't effect the output of v1.8, they're just bugs I found along the way that should be simple fixes.

burfadel
22nd September 2017, 00:10
@burfadel will you port this script to vapoursynth too? I find it quite effective against heavy noise, with better "color stabilising" than smdegrain. (at least for my anime source)

Sorry I didn't answer this question earlier, I forgot about it :D. I have no idea about Vapoursynth, the syntax is different and the workings of some of the commands is also different. mClean is still in development, it would be time consuming to maintain the changes between the two.

lansing
22nd September 2017, 09:28
What did you think of v1.8? The results are 'quite different'. I might have to chase up a couple of things in MVTools2 with PinterF, the issues don't effect the output of v1.8, they're just bugs I found along the way that should be simple fixes.

Like I said before, you should have test it yourself before posting, or you would have known that the black dot artifacts was brought back again a couple of version ago.

And I don't really notice any speed improvement, same old 14fps on my 4770k with a 1080 blankclip script
BlankClip(length=10000, width=1920, height=1080, pixel_type="yv12")
killaudio()
mclean()
prefetch(8)

AVSMeter 2.6.2 (x64) - Copyright (c) 2012-2017, Groucho2004
AviSynth+ 0.1 (r2508, MT, x86_64) (0.1.0.0)

Number of frames: 10000
Length (hh:mm:ss.ms): 00:06:56.667
Frame width: 1920
Frame height: 1080
Framerate: 24.000 (24/1)
Colorspace: YV12

Frames processed: 656 (0 - 655)
FPS (min | max | average): 1.115 | 85451 | 14.22
Memory usage (phys | virt): 1935 | 2145 MiB
Thread count: 33
CPU usage (average): 72%

Time (elapsed): 00:00:46.121

Press any key to exit...

burfadel
22nd September 2017, 09:47
I did do testing before posting, doesn't meant I can come across all issues. The speed thing you noticed is peculiar, in all testing that I did it was noticeably faster for me consistently, as well as using less memory.

Same test as you, except length is 1000. CPU usage is lower becuase I set prefetch(8) on a 16 thread machine.

mClean 1.7c:
AVSMeter 2.6.5 (x64) - Copyright (c) 2012-2017, Groucho2004
AviSynth+ 0.1 (r2508, MT, x86_64) (0.1.0.0)

Number of frames: 1000
Length (hh:mm:ss.ms): 00:00:41.667
Frame width: 1920
Frame height: 1080
Framerate: 24.000 (24/1)
Colorspace: YV12

Frames processed: 1000 (0 - 999)
FPS (min | max | average): 1.885 | 21560 | 21.32
Memory usage (phys | virt): 1792 | 2136 MiB
Thread count: 53
CPU usage (average): 41%

Time (elapsed): 00:00:46.9

mClean 1.8:
AVSMeter 2.6.5 (x64) - Copyright (c) 2012-2017, Groucho2004
AviSynth+ 0.1 (r2508, MT, x86_64) (0.1.0.0)

Number of frames: 1000
Length (hh:mm:ss.ms): 00:00:41.667
Frame width: 1920
Frame height: 1080
Framerate: 24.000 (24/1)
Colorspace: YV12

Frames processed: 1000 (0 - 999)
FPS (min | max | average): 1.783 | 26352 | 25.53
Memory usage (phys | virt): 1515 | 1536 MiB
Thread count: 37
CPU usage (average): 36%

Time (elapsed): 00:00:39.170

It's faster for me and uses less memory than mClean 1.7c. Also the CPU usage is less.

Of course it's a bit counter-intuitive to test a temporal filter, particularly one that uses MVTools2, on a blank clip :).

tebasuna51
22nd September 2017, 12:04
In previous versions (at least until 1.4) there are the line:

blkSize = Default (blkSize, DefH<360 ? 8 : DefH<750 ? 12 : DefH<1200 ? 16 : DefH<1600 ? 24 : 32) # Horizontal block size for MDegrain4

In mClean 1.8 the line is:

blkSize = Default (blkSize, defH>480 ? 12 : defH>1200 ? 16 : defH >=2600 ? 32 : 8) # Horizontal block size for MDegrain4

Are you sure about that sintax?

I think than defH>1200 ? 16 : defH >=2600 ? 32 never work, is always 12 for defH>480.

Groucho2004
22nd September 2017, 12:14
Of course it's a bit counter-intuitive to test a temporal filter, particularly one that uses MVTools2, on a blank clip :).You can always use synthetic, random grain like this:
colorbars(width = 1920, height = 1080, pixel_type = "yv12").killaudio().assumefps(24000, 1001).trim(0, 999)
addgrain(var = 100.0)

Sharc
22nd September 2017, 14:10
mClean Version 1.8, AVS+ 32 bit:
I'm getting from avisynth a "System exception - Illegal Instruction .... line 117" which is
clean = clean.dctfilter (1,1,1,1,1,1,0.50,0)
My dctfilter version is 0.5.0.0.

Any suggestion what could be wrong?

burfadel
22nd September 2017, 23:07
If you run dctfilter by itself does it do the same thing? What CPU do you have?

manolito
23rd September 2017, 01:05
What about this post by tebasuna51?
https://forum.doom9.org/showthread.php?p=1819328#post1819328

IMO he is absolutely right, the logic in the 1.8 version of the script is faulty.


Cheers
manolito

burfadel
23rd September 2017, 03:09
What about this post by tebasuna51?
https://forum.doom9.org/showthread.php?p=1819328#post1819328

IMO he is absolutely right, the logic in the 1.8 version of the script is faulty.


Cheers
manolito

Possibly? I see what you mean though, I'll fix that for the next version.

Currently I'm trying to work out the most ideal way to work a new quality feature. It's ideal in almost all situations and frames, it's trying to work the remaining situations that is the tricky part. It resolves the issue on the snow clip that was posted :). I've tried basically everything simple, so I might have to look at another approach which will still be faster than v1.7c but take some of the gains of v1.8, in addition to taking some of the gains as a result of the feature itself. Hopefully it will be worth it though :).

Sharc
23rd September 2017, 09:20
If you run dctfilter by itself does it do the same thing? What CPU do you have?
Problem solved, thanks.
By mistake I copied both the DCTFilter.dll and the DCTFilter_avx2.dll into the avisynth plugins folder.

MysteryX
23rd September 2017, 20:16
v1.8 seems to give a little bit more of a plastic effect, and it shifts the image slightly to the left, which may be the cause of the 1st problem if luma/chroma aren't aligned

burfadel
24th September 2017, 09:55
There does appear to be a slight shift. After lots of testing it appears there is a minor bug in the MScaleVect function that's causing it. It's not related to block size or anything else. I'll report it in the MVTools2 thread.
http://forum.doom9.net/showpost.php?p=1819536&postcount=399

This same bug is probably why it doesn't work very well with MFlowFPS either.

burfadel
28th September 2017, 12:03
The other day Pinterf sent me a test version of MVTools2 which basically fixed the image shift issue. It was an error in one of the algorithms that was there since day one. There was still a very slight shift or difference in some motion between fully using Mscalevect and using base code, I'm not sure whether that could be resolved though. In terms of how mClean is set up though, basically the new version fixes the issue as I don't use it on radius 1. I guess if that additional error was improved upon I could add a fast option that runs it for radius 1 as well and maybe use Mdegrain3 instead of Mdegrain4 for that mode.

cap5lock
4th October 2017, 06:24
Using v1.8 got this error
Please help
https://s1.postimg.org/3u0v7j5mq7/image.png

Groucho2004
4th October 2017, 07:53
Using v1.8 got this error
Please help
Run "AVSMeter (https://forum.doom9.org/showthread.php?t=174797) -avsinfo -log". Post the created log file (avsinfo_x86.log).

Sharc
4th October 2017, 09:18
Using v1.8 got this error
Please help
https://s1.postimg.org/5ujddatcsv/image.png
Removing DCTFilter_avx2.dll from the plugins folder has solved exactly this problem for me (https://forum.doom9.org/showpost.php?p=1819409&postcount=219).

cap5lock
4th October 2017, 10:50
Removing DCTFilter_avx2.dll from the plugins folder has solved exactly this problem for me (https://forum.doom9.org/showpost.php?p=1819409&postcount=219).
Thanks
Unfortunately after removing avx2.dll, I got this error showed up
https://s1.postimg.org/1fkh3gaesv/image.png

Run "AVSMeter (https://forum.doom9.org/showthread.php?t=174797) -avsinfo -log". Post the created log file (avsinfo_x86.log).
Here it is the log file

Edit :
SOLVED
I used old DCTFilter dll by mistake...
Thanks for helping me

MysteryX
11th October 2017, 02:35
Original / mClean 1.7 / mClean 1.8

Here the result is excellent with v1.8
https://s1.postimg.org/1iwezdlhhn/1318_original.png (https://postimg.org/image/1iwezdlhhn/) https://s1.postimg.org/6xuxhsxukb/1318_mclean17.png (https://postimg.org/image/6xuxhsxukb/) https://s1.postimg.org/7c1d8o7nfv/1318_mclean18.png (https://postimg.org/image/7c1d8o7nfv/)

Here, there is still too much color shifts.
https://s1.postimg.org/1iwezdkewr/2749_original.png (https://postimg.org/image/1iwezdkewr/) https://s1.postimg.org/1pzmut6s23/2749_mclean17.png (https://postimg.org/image/1pzmut6s23/) https://s1.postimg.org/6cl9vi5jff/2749_mclean18.png (https://postimg.org/image/6cl9vi5jff/)

On 288p content, however, I still get best results with v1.3c; v1.7 and v1.8 add distortion and amplify artifacts. Is there a specific reason for this? Perhaps SD and HD content require different settings.

burfadel
11th October 2017, 05:00
Possibly. There is a bug in MVtools2 that affects 1.8. PinterF has fixed this and it will be in the next MVtools2 release. I was waiting on this before a new mClean. I did spend quite a while working on another 'feature' that works great in some specific scenarios in making it better (ideal even), but in other scenarios it was worse. For the remainder of situation it made no difference. Since it was scene dependent it's usefulness isn't really there. There is completely different way I could potentially do it that could have most of the benefits without the downsides, I'll have to do testing.

MysteryX
11th October 2017, 06:57
Yes I have Pinterf's fixed version, that's fine.

MDegrain4 gives good quality on HD grain (small pixels). However, on SD content with gross pixels and artifacts, it's distorting stuff. Perhaps you'll find a method that works for both HD and SD. If not, perhaps you can have a version for HD and a version for SD.

Then color distortion is the other concern -- entirely separate issue.

burfadel
11th October 2017, 12:12
I think I can work something out there!

burfadel
29th October 2017, 00:25
Updated first post with v1.9. I had to shuffle around the description etc as the first post exceeded the allowed post size limit. MysteryX, If the very low resolution handling (such as the 288P example you gave) is still a little iffy I can add handling for this, but I didn't want to add it with v1.9 if it appears unnecessary.

tebasuna51
29th October 2017, 11:07
I think than your line:

blkSize = Default (blkSize, defH>480 ? 12 : defH>1200 ? 16 : defH >=2600 ? 32 : 8) # Horizontal block size for MDegrain4

is still wrong in v1.9, blkSize 16 or 32 are never used, only 12 for defH>480 or 8 for defH<=480
Must be:

blkSize = Default (blkSize, defH >=2600 ? 32 : defH>1200 ? 16 : defH>480 ? 12 : 8) # Horizontal block size for MDegrain4

EDIT:
Also the sc parameter:

sc = defH>480 ? 2 : defH>1200 ? 4 : defH >=2600 ? 8 : 1

must be:

sc = defH >=2600 ? 8 : defH>1200 ? 4 : defH>480 ? 2 : 1

burfadel
29th October 2017, 11:34
Oops, I've made that correction now :). On a separate note, something that I was going to include requires the use of scriptclip, but I can not get it working within the script. If I run it outside the script it works fine.

Are you familiar with using scriptclip? I asked MysteryX, I can forward you the message I sent him on here if you are.

StainlessS
29th October 2017, 12:02
Just post your Scriptclip requirement here, let everyone see it.

Perhaps you keep getting mixed up with masktools RPN [EDIT: multi-ganged] '?:' ternary conditional, I think it works a bit back-to-front and weird
compared with everyone else's [EDIT: multi-ganged] version ternary conditional.

burfadel
29th October 2017, 12:21
For scriptclip, it would be something like:
combined = deband ? CombinePlanes(clean, chroma?filt_chroma:c, planes="YUV", source_planes="YUV", pixel_type=pixeltype(filt_chroma)) : nop()
combined = deband ? ScriptClip(" coloryuv(combined, gain_y=LumaDifference(c, clean), chroma?gain_u=ChromaUDifference(c, filt_chroma):0, chroma?gain_v=ChromaVDifference(c, filt_chroma):0) ") : nop ()
filt_chroma = deband ? mt_adddiff (combined, TemporalSoften(mt_makediff(combined, f3kdb (combined, preset=chroma?"high":"luma", range=17,
\ grainY=33, grainC=chroma?35:0)), 1, 255, chroma?255:0, scenechange=255, mode=2)) : filt_chroma
clean = deband ? ExtractY (filt_chroma) : clean

The second line is what's added to the passage. The above can be copied over the equivalent lines in v1.9 and scriptclip line adjusted according. It works outside the script as:
c=last
mClean()
filt_chroma=last
ScriptClip("coloryuv(filt_chroma, gain_y=LumaDifference(c, filt_chroma), gain_u=ChromaUDifference(c, filt_chroma), gain_v=ChromaVDifference(c, filt_chroma))")

When I added it to the script it didn't work. When it did appear to work with some faffing around it wasn't functional. You can see the difference by adding *150 (multiplying and overblowing the change to show the base number difference at least works) at the end of one of the gains, such as gain_v=ChromaVDifference(orig, filt_chroma)*120.

StainlessS
29th October 2017, 16:22
# How it is
S= "
\ coloryuv(combined,
\ gain_y = LumaDifference(c,clean),
\ chroma ? gain_u=ChromaUDifference(c,filt_chroma) : 0, # EDIT: kind of surprised these 2 lines work at all
\ chroma ? gain_v=ChromaVDifference(c,filt_chroma) : 0 # EDIT: Assuming that they do
\ )"

# more like how it should be,
# but, when not @ main script level, combined, c, clean. chroma, filt_chroma, are not in scope (unless globals).

# Maybe below more as intended (but args still not in scope)
S= "
\ coloryuv(combined,
\ gain_y = LumaDifference(c,clean),
\ gain_u = (chroma) ? ChromaUDifference(c,filt_chroma) : 0,
\ gain_v = (chroma) ? ChromaVDifference(c,filt_chroma) : 0
\ )"

combined = deband ? ScriptClip(S) : nop # This is horrible, [EDIT: perhaps nop should be combined]

# TEST
Colorbars.killaudio # Assign to Last
a=invert # inverted Last
a=false ? blankclip : NOP
return a # If true then returns blankclip else Error, "The script return value was not a clip (is an int, 0)"


EDIT: Grunt would let you import args using eg args="combined, c, clean. chroma, filt_chroma" as args to scriptclip.

EDIT: Also, take care with ScriptClip, requires a source arg, if not supplied then is implied to be Last (should it be c or combined or what, inside Scriptclip that source arg will become Last).

MysteryX
29th October 2017, 17:46
Note that if you use ScriptClip, you can't use MT anymore -- which renders it useless for practical use in many cases.

Perhaps you'd need a custom filter for this one? Plus you might do a better job if you decide how it's being done in the details.

Also perhaps other complex command chains would be better achieved as custom filters.

burfadel
30th October 2017, 07:21
I've worked out it, I just have to build on it now :). I also found that using 'pixel_type for combineplanes instead of sample_clip was 'wrong' :).

tormento
31st October 2017, 11:20
I tried a very silly mClean() in my script, just to give it a try, and the result is:
[2017-10-31][11:15:02] Simple x264 Launcher (Build #1118), built 2017-10-04
[2017-10-31][11:15:02]
[2017-10-31][11:15:02] Job started at 2017-10-31, 11:15:02.
[2017-10-31][11:15:02]
[2017-10-31][11:15:02] Source file : E:\in\1_30 Eden Lake\eden_mclean.avs
[2017-10-31][11:15:02] Output file : E:\in\1_30 Eden Lake\eden_mclean.mkv
[2017-10-31][11:15:02]
[2017-10-31][11:15:02] --- SYSTEMINFO ---
[2017-10-31][11:15:02]
[2017-10-31][11:15:02] Binary Path : D:\eseguibili\media\x264 launcher
[2017-10-31][11:15:02] Avisynth : Yes
[2017-10-31][11:15:02] VapourSynth : No
[2017-10-31][11:15:02]
[2017-10-31][11:15:02] --- SETTINGS ---
[2017-10-31][11:15:02]
[2017-10-31][11:15:02] Encoder : x264 (AVC/H.264), 64-Bit (x64), 8-Bit
[2017-10-31][11:15:02] Source : Avisynth (avs)
[2017-10-31][11:15:02] RC Mode : CRF
[2017-10-31][11:15:02] Preset : slow
[2017-10-31][11:15:02] Tuning : <None>
[2017-10-31][11:15:02] Profile : High
[2017-10-31][11:15:02] Custom : --level 4.1 --keyint 240 --vbv-bufsize 78125 --vbv-maxrate 62500 --aq-mode 2 --sar 1:1
[2017-10-31][11:15:02]
[2017-10-31][11:15:02] --- CHECK VERSION ---
[2017-10-31][11:15:02]
[2017-10-31][11:15:02] Detect video encoder version:
[2017-10-31][11:15:02]
[2017-10-31][11:15:02] Creating process:
[2017-10-31][11:15:02] "D:\eseguibili\media\x264 launcher\toolset\x64\x264_8bit_x64.exe" --version
[2017-10-31][11:15:02]
[2017-10-31][11:15:02] x264 0.152.2851kMod ba24899
[2017-10-31][11:15:02] (libswscale 4.7.101)
[2017-10-31][11:15:02] (libavformat 57.75.100)
[2017-10-31][11:15:02] (ffmpegsource 2.23.0.0)
[2017-10-31][11:15:02] built by Komisar on Jul 2 2017, gcc: 4.9.2 (multilib.generic.Komisar)
[2017-10-31][11:15:02] x264 configuration: --bit-depth=8 --chroma-format=all
[2017-10-31][11:15:02] libx264 configuration: --bit-depth=8 --chroma-format=all
[2017-10-31][11:15:02] x264 license: GPL version 2 or later
[2017-10-31][11:15:02] libswscale/libavformat/ffmpegsource license: GPL version 2 or later
[2017-10-31][11:15:02]
[2017-10-31][11:15:02] Detect video source version:
[2017-10-31][11:15:02]
[2017-10-31][11:15:02] Creating process:
[2017-10-31][11:15:02] "D:\eseguibili\media\x264 launcher\toolset\x64\avs2yuv_x64.exe"
[2017-10-31][11:15:02]
[2017-10-31][11:15:02] Avs2YUV 0.24bm5
[2017-10-31][11:15:02]
[2017-10-31][11:15:02] > x264 revision: 2851 (core #152) - with custom patches!
[2017-10-31][11:15:02] > Avs2YUV version: 0.24.5
[2017-10-31][11:15:02]
[2017-10-31][11:15:02] --- GET SOURCE INFO ---
[2017-10-31][11:15:02]
[2017-10-31][11:15:02] Creating process:
[2017-10-31][11:15:02] "D:\eseguibili\media\x264 launcher\toolset\x64\avs2yuv_x64.exe" -frames 1 "E:\in\1_30 Eden Lake\eden_mclean.avs" NUL
[2017-10-31][11:15:02]
[2017-10-31][11:15:03] error: MAnalyse: Block sizes must be 8 or more for divide mode
[2017-10-31][11:15:03] (D:/Programmi/Media/AviSynth+/plugins64/mClean-1.9�burfadel.avsi, line 109)
[2017-10-31][11:15:03] (E:\in\1_30 Eden Lake\eden_mclean.avs, line 13)
[2017-10-31][11:15:03]
[2017-10-31][11:15:03] PROCESS EXITED WITH ERROR CODE: 1

burfadel
31st October 2017, 12:40
Are you using the latest versions of all the plugins, for example MVTools2 (pfmod)? https://github.com/pinterf/mvtools/releases/

tormento
31st October 2017, 12:43
Are you using the latest versions of all the plugins, for example MVTools2 (pfmod)? https://github.com/pinterf/mvtools/releases/
Yes and the video is Mod 16.

Groucho2004
31st October 2017, 12:49
Yes and the video is Mod 16.
You could try running the script with AVSMeter. If that works, something else in the chain is not working as it should (avs2yuv?).

burfadel
31st October 2017, 13:00
The smallest blocksize that the script uses is 8. I tried it on a clip resolution of 524,288, and no hassles.

tormento
31st October 2017, 13:32
You could try running the script with AVSMeter. If that works, something else in the chain is not working as it should (avs2yuv?).
AVSMeter 2.6.0 (x64) - Copyright (c) 2012-2017, Groucho2004
AviSynth+ 0.1 (r2508, MT, x86_64) (0.1.0.0)

MAnalyse: Block sizes must be 8 or more for divide mode
(D:/Programmi/Media/AviSynth+/plugins64/mClean-1.9ùburfadel.avsi, line 109)
(E:\in\1_30 Eden Lake\eden_mclean.avs, line 13)

Same video works with SMDegrain, KNLMeansCL, FFT3D, etc etc etc

Isn't simply that a () without parameters triggers some strange bug?

My environment:
Log created with: AVSMeter 2.6.0 (x64)

[OS/Hardware info]
Operating system: Windows 10 (x64) (Build 17025)
CPU brand string: Intel(R) Core(TM) i7-2600K CPU @ 3.40GHz
CPU features: MMX, SSE, SSE2, SSE3, SSSE3, SSE4.1, SSE4.2, AVX, POPCNT, AES


[Avisynth info]
VersionString: AviSynth+ 0.1 (r2508, MT, x86_64)
VersionNumber: 2.60
File / Product version: 0.1.0.0 / 0.1.0.0
Interface Version: 6
Multi-threading support: Yes
Avisynth.dll location: C:\WINDOWS\SYSTEM32\avisynth.dll
Avisynth.dll time stamp: 2017-06-29, 09:09:33 (UTC)
PluginDir2_5 (HKLM, x64): D:\AviSynth+\plugins64
PluginDir+ (HKLM, x64): D:\AviSynth+\plugins64+


[CPP 2.5 / 64 Bit Plugins]
D:\AviSynth+\plugins64\Dither-1.27.2.dll
D:\AviSynth+\plugins64\f3kdb-2.0•20140721—SAPikachu.dll

[CPP 2.6 / 64 Bit Plugins]
D:\AviSynth+\plugins64+\ConvertStacked.dll
D:\AviSynth+\plugins64+\DCTFilter-0.5.0—chikuzen.dll [0.5.0.0]
D:\AviSynth+\plugins64+\DirectShowSource.dll
D:\AviSynth+\plugins64+\FFT3dFilter-2.4—pinterf.dll [2.4.0.0]
D:\AviSynth+\plugins64+\ImageSeq.dll
D:\AviSynth+\plugins64+\KNLMeansCL-1.1.0.dll
D:\AviSynth+\plugins64+\MaskTools-2.2.10—pinterf.dll [2.2.10.0]
D:\AviSynth+\plugins64+\MedianBlur2-0.94—tp7.dll
D:\AviSynth+\plugins64+\MVTools-2.7.2.23—pinterf.dll [2.7.23.0]
D:\AviSynth+\plugins64+\RgTools-0.96—pinterf.dll [0.96.0.0]
D:\AviSynth+\plugins64+\Shibatch.dll
D:\AviSynth+\plugins64+\TimeStretch.dll
D:\AviSynth+\plugins64+\VDubFilter.dll

[Scripts / AVSI]
D:\AviSynth+\plugins64+\colors_rgb.avsi
D:\AviSynth+\plugins64\CompTest.avsi
D:\AviSynth+\plugins64\DeHalo_alpha—realfinder.avsi
D:\AviSynth+\plugins64\Dither-1.27.2.avsi
D:\AviSynth+\plugins64\mClean-1.9—burfadel.avsi
D:\AviSynth+\plugins64\MT_xxpand_multi.avsi
D:\AviSynth+\plugins64\SMDegrain-3.1.2·93—realfinder.avsi
D:\AviSynth+\plugins64\VHSHaloremover.avsi

[Uncategorized / Other]
D:\AviSynth+\plugins64+\AviSynth-new.css
D:\AviSynth+\plugins64+\AviSynth.css
D:\AviSynth+\plugins64+\colors_rgb.txt
D:\AviSynth+\plugins64+\DCTFilter-0.5.0—chikuzen.md
D:\AviSynth+\plugins64+\FFT3dFilter-2.4—pinterf.gif
D:\AviSynth+\plugins64+\FFT3dFilter-2.4—pinterf.htm
D:\AviSynth+\plugins64+\FFT3dFilter-2.4—pinterf.txt
D:\AviSynth+\plugins64+\MaskTools-2.0a48.htm
D:\AviSynth+\plugins64+\MaskTools-2.2.10—pinterf.md
D:\AviSynth+\plugins64+\MedianBlur-0.84.txt
D:\AviSynth+\plugins64+\MVTools-2.7.2.22—pinterf.htm
D:\AviSynth+\plugins64+\MVTools-2.7.2.23—pinterf.md
D:\AviSynth+\plugins64+\RgTools-0.96—pinterf.md
D:\AviSynth+\plugins64\AviSynth-new.css
D:\AviSynth+\plugins64\AviSynth.css
D:\AviSynth+\plugins64\Dither-1.27.2.htm
D:\AviSynth+\plugins64\f3kdb-2.0•20140721—SAPikachu.html
D:\AviSynth+\plugins64\SMDegrain-3.1.2d.htm



[DLL dependencies (x64)]
C:\WINDOWS\SYSTEM32\avisynth.dll:
AVIFIL32.dll
MSVFW32.dll
MSACM32.dll
GDI32.dll
USER32.dll
ADVAPI32.dll
ole32.dll
imagehlp.dll
MSVCP140.dll
KERNEL32.dll
VCRUNTIME140.dll
api-ms-win-crt-runtime-l1-1-0.dll
api-ms-win-crt-heap-l1-1-0.dll
api-ms-win-crt-string-l1-1-0.dll
api-ms-win-crt-stdio-l1-1-0.dll
api-ms-win-crt-math-l1-1-0.dll
api-ms-win-crt-environment-l1-1-0.dll
api-ms-win-crt-filesystem-l1-1-0.dll
api-ms-win-crt-locale-l1-1-0.dll
api-ms-win-crt-utility-l1-1-0.dll
api-ms-win-crt-convert-l1-1-0.dll
api-ms-win-crt-time-l1-1-0.dll

D:\AviSynth+\plugins64\Dither-1.27.2.dll:
KERNEL32.dll

D:\AviSynth+\plugins64\f3kdb-2.0•20140721—SAPikachu.dll:
KERNEL32.dll

D:\AviSynth+\plugins64+\ConvertStacked.dll:
KERNEL32.dll
VCRUNTIME140.dll
api-ms-win-crt-heap-l1-1-0.dll
api-ms-win-crt-runtime-l1-1-0.dll

D:\AviSynth+\plugins64+\DCTFilter-0.5.0—chikuzen.dll:
VCRUNTIME140.dll
api-ms-win-crt-heap-l1-1-0.dll
api-ms-win-crt-runtime-l1-1-0.dll
KERNEL32.dll

D:\AviSynth+\plugins64+\DirectShowSource.dll:
WINMM.dll
QUARTZ.dll
ole32.dll
USER32.dll
OLEAUT32.dll
KERNEL32.dll
VCRUNTIME140.dll
api-ms-win-crt-string-l1-1-0.dll
api-ms-win-crt-stdio-l1-1-0.dll
api-ms-win-crt-runtime-l1-1-0.dll
api-ms-win-crt-heap-l1-1-0.dll

D:\AviSynth+\plugins64+\FFT3dFilter-2.4—pinterf.dll:
KERNEL32.dll
USER32.dll
VCRUNTIME140.dll
api-ms-win-crt-heap-l1-1-0.dll
api-ms-win-crt-runtime-l1-1-0.dll
api-ms-win-crt-math-l1-1-0.dll

D:\AviSynth+\plugins64+\ImageSeq.dll:
DevIL.dll
MSVCP140.dll
KERNEL32.dll
VCRUNTIME140.dll
api-ms-win-crt-runtime-l1-1-0.dll
api-ms-win-crt-stdio-l1-1-0.dll
api-ms-win-crt-filesystem-l1-1-0.dll
api-ms-win-crt-heap-l1-1-0.dll

D:\AviSynth+\plugins64+\KNLMeansCL-1.1.0.dll:
OpenCL.dll
MSVCP140.dll
KERNEL32.dll
VCRUNTIME140.dll
api-ms-win-crt-locale-l1-1-0.dll
api-ms-win-crt-stdio-l1-1-0.dll
api-ms-win-crt-runtime-l1-1-0.dll
api-ms-win-crt-filesystem-l1-1-0.dll
api-ms-win-crt-string-l1-1-0.dll
api-ms-win-crt-heap-l1-1-0.dll

D:\AviSynth+\plugins64+\MaskTools-2.2.10—pinterf.dll:
KERNEL32.dll
MSVCP140.dll
VCRUNTIME140.dll
api-ms-win-crt-runtime-l1-1-0.dll
api-ms-win-crt-heap-l1-1-0.dll
api-ms-win-crt-convert-l1-1-0.dll
api-ms-win-crt-string-l1-1-0.dll
api-ms-win-crt-stdio-l1-1-0.dll
api-ms-win-crt-math-l1-1-0.dll

D:\AviSynth+\plugins64+\MedianBlur2-0.94—tp7.dll:
MSVCR110.dll
KERNEL32.dll

D:\AviSynth+\plugins64+\MVTools-2.7.2.23—pinterf.dll:
KERNEL32.dll
MSVCP140.dll
VCRUNTIME140.dll
api-ms-win-crt-string-l1-1-0.dll
api-ms-win-crt-runtime-l1-1-0.dll
api-ms-win-crt-heap-l1-1-0.dll
api-ms-win-crt-stdio-l1-1-0.dll
api-ms-win-crt-math-l1-1-0.dll
api-ms-win-crt-utility-l1-1-0.dll

D:\AviSynth+\plugins64+\RgTools-0.96—pinterf.dll:
VCRUNTIME140.dll
api-ms-win-crt-heap-l1-1-0.dll
api-ms-win-crt-runtime-l1-1-0.dll
KERNEL32.dll

D:\AviSynth+\plugins64+\Shibatch.dll:
MSVCP140.dll
KERNEL32.dll
VCRUNTIME140.dll
api-ms-win-crt-math-l1-1-0.dll
api-ms-win-crt-heap-l1-1-0.dll
api-ms-win-crt-runtime-l1-1-0.dll
api-ms-win-crt-stdio-l1-1-0.dll

D:\AviSynth+\plugins64+\TimeStretch.dll:
KERNEL32.dll
VCRUNTIME140.dll
api-ms-win-crt-math-l1-1-0.dll
api-ms-win-crt-heap-l1-1-0.dll
api-ms-win-crt-runtime-l1-1-0.dll

D:\AviSynth+\plugins64+\VDubFilter.dll:
USER32.dll
KERNEL32.dll
VCRUNTIME140.dll
api-ms-win-crt-stdio-l1-1-0.dll
api-ms-win-crt-heap-l1-1-0.dll
api-ms-win-crt-runtime-l1-1-0.dll

[External (plugin) functions]
D:\AviSynth+\plugins64\Dither-1.27.2.dll:
DitherPost
Dither_add16
Dither_bilateral16
Dither_box_filter16
Dither_limit_dif16
Dither_max_dif16
Dither_median16
Dither_merge16
Dither_min_dif16
Dither_out
Dither_removegrain16
Dither_repair16
Dither_resize16
Dither_sub16
SmoothGrad

D:\AviSynth+\plugins64\f3kdb-2.0•20140721—SAPikachu.dll:
f3kdb
flash3kyuu_deband

D:\AviSynth+\plugins64+\ConvertStacked.dll:
ConvertFromDoubleWidth
ConvertFromStacked
ConvertToDoubleWidth
ConvertToStacked

D:\AviSynth+\plugins64+\DCTFilter-0.5.0—chikuzen.dll:
DCTFilter
DCTFilter4
DCTFilter4D
DCTFilter8
DCTFilter8D
DCTFilterD

D:\AviSynth+\plugins64+\DirectShowSource.dll:
DirectShowSource

D:\AviSynth+\plugins64+\FFT3dFilter-2.4—pinterf.dll:
FFT3DFilter
FFT3DFilter_VersionNumber

D:\AviSynth+\plugins64+\ImageSeq.dll:
ImageReader
ImageSource
ImageSourceAnim
ImageWriter

D:\AviSynth+\plugins64+\KNLMeansCL-1.1.0.dll:
KNLMeansCL

D:\AviSynth+\plugins64+\MaskTools-2.2.10—pinterf.dll:
mt_adddiff
mt_average
mt_binarize
mt_circle
mt_clamp
mt_convolution
mt_deflate
mt_diamond
mt_edge
mt_ellipse
mt_expand
mt_freeellipse
mt_freelosange
mt_freerectangle
mt_gradient
mt_hysteresis
mt_infix
mt_inflate
mt_inpand
mt_invert
mt_logic
mt_losange
mt_lut
mt_lutf
mt_luts
mt_lutspa
mt_lutsx
mt_lutxy
mt_lutxyz
mt_lutxyza
mt_makediff
mt_mappedblur
mt_merge
mt_motion
mt_polish
mt_rectangle
mt_square

D:\AviSynth+\plugins64+\MedianBlur2-0.94—tp7.dll:
MedianBlur
MedianBlurTemporal

D:\AviSynth+\plugins64+\MVTools-2.7.2.23—pinterf.dll:
MAnalyse
MBlockFps
MCompensate
MDegrain1
MDegrain2
MDegrain3
MDegrain4
MDegrain5
MDegrain6
MDegrainN
MDepan
MFlow
MFlowBlur
MFlowFps
MFlowInter
MMask
MRecalculate
MRestoreVect
MSCDetection
MScaleVect
MShow
MStoreVect
MSuper

D:\AviSynth+\plugins64+\RgTools-0.96—pinterf.dll:
BackwardClense
Clense
ForwardClense
RemoveGrain
Repair
VerticalCleaner

D:\AviSynth+\plugins64+\Shibatch.dll:
SSRC
SuperEQ

D:\AviSynth+\plugins64+\TimeStretch.dll:
TimeStretch

D:\AviSynth+\plugins64+\VDubFilter.dll:
LoadVirtualdubPlugin

Groucho2004
31st October 2017, 14:42
I get the same error.
Script:
colorbars(width = 1024, height = 1024, pixel_type = "yv12").killaudio().assumefps(50, 1).trim(0, 499)
mclean()


Also, with a resolution of 1024 x 1024, I get this error:
Script error: f3kdb does not have a named argument "preset"
(E:/Apps/VideoTools/AVSPlugins/AutoLoad/mclean.avsi, line 140)
Edit: It seems that the experimental f3kdb 2.0pre2 is required.

tormento
31st October 2017, 20:16
Edit: It seems that the experimental f3kdb 2.0pre2 is required.
I think your comment refers only to second error, as I already had the experimental version and the first error occurred.

burfadel
31st October 2017, 20:59
2.0.0.1 for f3kdb is basically the 'final'. Work stopped on it, source code is available on github. https://github.com/SAPikachu/flash3kyuu_deband/releases

The other error is a result of a fixing a syntax error. I'll fix it for version 2.0, which will be released shortly. That one will require the autoadjust plugin for one of the settings change, allowing for basically harmonising colour balance, deflicker etc (more appropriate custom settings). Yes, another plugin, but it's super fast and applied at the appropriate location. It's also much more effective than my attempt that I got working with scriptclip but was a little finnicky stability wise. Basically there are four deband settings, 0=disabled, 1=deband only, 2=auto balance only, 3=deband and auto balance. Deband 1 is the default (so the extra filter won't be required), unless non 8 bit where 0 is default (f3kdb limitation), with auto balance option 2 will be still available.

Groucho2004
31st October 2017, 22:31
I think your comment refers only to second errorThat's correct.

Groucho2004
31st October 2017, 22:40
2.0.0.1 for f3kdb is basically the 'final'. Work stopped on it, source code is available on github. https://github.com/SAPikachu/flash3kyuu_deband/releasesI have been using f3kdb 1.5.1 for years and it has served me well. I'm a bit reluctant to use the 2.0 version because of SAPikachu's "experimental" and "use with caution" notes in the first post of the f3kdb thread. Maybe I should give it a try...

MysteryX
1st November 2017, 01:48
2.0.0.1 for f3kdb is basically the 'final'. Work stopped on it, source code is available on github. https://github.com/SAPikachu/flash3kyuu_deband/releases
There's no pre-compiled version of it?

and it requires Python AND GCC to build!? I won't spent half an hour just to try to build something that should be ready to use.

pinterf
1st November 2017, 14:36
For divide error message: maybe the problem is that 8x8 block size becomes 4x4 for yv12 chroma. Try with yv24

tebasuna51
1st November 2017, 16:22
About the tormento error: MAnalyse: Block sizes must be 8 or more for divide mode
in line 109: MRecalculate(...MAnalyse(...blksize=blksizeL/sc...), blksize=blksize/sc)

Using the lines in v1.9 13:41, 31 Oct 2017:

defH = Max (C.Height, C.Width/4*3) # always Width/4*3 for AR >= 4:3 (most the times)
blkSize = Default (blkSize, defH >=2600 ? 32 : defH>1200 ? 16 : defH>480 ? 12 : 8)
sc = defH >=2600 ? 8 : defH>1200 ? 4 : defH>480 ? 2 : 1
blksizeL = blksize>8<=16 ? 24 : blksize>16<=32 ? 32 : sc==8 ? 48 : 16

I obtain:

Width Range blkSize sc blksize/sc blksizeL blksizeL/sc
----------- ------- -- ---------- -------- -----------
<644 8 1 8 16 16
644-1603 12 2 6 24 12
1604-3467 24 4 6 24 (32) 6 (8)
>3467 32 8 4 24 (48) 3 (6)

Like you see blksizeL never ground from 24
maybe you need change this line:

blksizeL = sc==8 ? 48 : blksize>16 ? 32 :blksize>8 ? 24 : 16

with the new (values)

BTW the values passed to MRecalculate and MAnalyse can be less than 8...

burfadel
9th November 2017, 06:11
Updated first post with version 2.0.

StainlessS
9th November 2017, 09:29
burfadel,

blksizeL = blksize>8<=16 ? 24 : blksize>16<=32 ? 32 : sc==8 ? 48 : 16

I think you misunderstand how this bit "blksize>8<=16" works,
what that is saying is:- if blksize is greater than 8 AND if 8 is smaller or equal to 16 then ...

what I think you intend is this, " 8 < blksize <= 16 " or in non shorthand version " 8 < blksize && blksize <= 16 ".

The "blksize>16<=32" is likewise a little screwy I think.

RieGo
9th November 2017, 09:56
trying V2.0 with default settings i get an error:
f3kdb Initialization failed (code: 3). Invalid parameter sample_mode, must be between 1 and 2.
tested f3kdb 2.0 and the older one.

burfadel
9th November 2017, 14:08
burfadel,

blksizeL = blksize>8<=16 ? 24 : blksize>16<=32 ? 32 : sc==8 ? 48 : 16

I think you misunderstand how this bit "blksize>8<=16" works,
what that is saying is:- if blksize is greater than 8 AND if 8 is smaller or equal to 16 then ...

what I think you intend is this, " 8 < blksize <= 16 " or in non shorthand version " 8 < blksize && blksize <= 16 ".

The "blksize>16<=32" is likewise a little screwy I think.

That makes sense :). I'll change it for the next version.

trying V2.0 with default settings i get an error:
f3kdb Initialization failed (code: 3). Invalid parameter sample_mode, must be between 1 and 2.
tested f3kdb 2.0 and the older one.

That's weird. I've uploaded the version used here. It's the latest version (for now). 7-Zip archive.
https://1drv.ms/u/s!AmGuHbW3zvrBmb4tmBP2-KUToKUpVQ

RieGo
9th November 2017, 15:31
That's weird. I've uploaded the version used here. It's the latest version (for now). 7-Zip archive.
https://1drv.ms/u/s!AmGuHbW3zvrBmb4tmBP2-KUToKUpVQ

thanks, with this one it works... i guess i'll just use this one while i figure out my mistake

edit: i'm sorry it was my mistake. the version which i used before, thinking it's 1.x actually was 2.0
so it doesn't work with flash3kyuu 2.0. any chance you could add compatibility?

StainlessS
9th November 2017, 22:34
That makes sense :). I'll change it for the next version.


I was not contradicting Tebasuna51 at all (was about to tumble into bed when I wrote it, and have not really tried to figure out what it should be). My only intent was that you were more informed about how that shortcut style condition works.

See Here:- https://forum.doom9.org/showthread.php?p=1783132#post1783132

MysteryX
13th November 2017, 21:07
This mClean thing is just teasing me. Compared to KnlMeansCL, I'm getting better results with mClean 1.3c with my 288p VCDs. On 1080p camera footage with noise, I get much better results with mClean 1.8 or 2.0 than with KnlMeansCL or SMDegrain. Then v1.8 and v2.0 give considerable distortion on SD sources, and all versions cause severe color distortions on some videos. Sometimes it works great, sometimes it works bad.

Then I can't easily adjust "enh" because it's default value is dynamic based on the video resolution, so if I want to increase or decrease by 10%, I have to look at the source, take my calculator, and determine the value from there.

I *want* to implement this within my video encoder, but for now it's too unreliable, so what am I supposed to do? I still get better results with either v1.3c or v1.8 on most sources. v2.0 seems to give a slightly more plastic effect, but also seems slightly closer to the original.

I guess the first step would be in properly identifying and diagnosing why the shape distortions and color distortions are happening in the first place.

burfadel
14th November 2017, 06:24
I'm currently time limited, but in early/mid December I should have some time to really sit down and work through those issues. As for the sharpening (old enh setting), I do have a solution but will require some time to balance it across all resolutions.

MysteryX
14th November 2017, 15:35
Could you explain why those issues are happening in the first place?

burfadel
14th November 2017, 18:23
It's all about the settings for each resolution type, and adding a small amount of code. The sharpness setting changes the default number automatically based on the resolution. I'll change this so the number remains the same but applies the sharpness based on the resolution, but keeping it comparative to other resolutions.

MysteryX
14th November 2017, 18:38
'enh' setting is pretty simple; just needs minor code to translate the numbers.

Color distortions and shape distortions, however, are more complex issues.

lansing
15th November 2017, 23:17
'enh' setting is pretty simple; just needs minor code to translate the numbers.

Color distortions and shape distortions, however, are more complex issues.

So you finally realized the major problem that I talked about like 10 pages ago...

The shape distortion came from mdegrain and I told burfadel the solution too. That is don't use mdegrain, or look at the code of MCTD.

The desaturation problem came from fft3dfilter and I also told him not to use it. But then another 2 months passed and he still stuck with the same problems.

MysteryX
4th December 2017, 05:30
Are there cases where MDegrain gives better results than mClean? I'd guess this works better than MDegrain in 95% of cases for degraining.

In my encoder, I could replace MDegrain with mClean, and stick to other methods for denoising for now.

MysteryX
4th December 2017, 18:54
I'll also note that the issues of color/shape distortion have no impact when used as a prefilter for FrameRateConverter so it can be used there; but due to technical restrictions it's hard to run them both at the same time. So far I need to encode prefilter into an intermediary AVI file. Shape distortions "may" distort motion vector in certain cases, but in most cases, this will result in better motion vectors and superior quality, so I'd say it's safe to use. Color desaturation should have little to no impact on motion vectors.

burfadel
5th December 2017, 02:32
Luma noise is unwanted difference on a small scale of localised brightness (common) or darkness variation. If you remove this lightness variation (aka, the noise) you would technically be making it darker over a given area of similar luminance but it's not perceived this way. Let's apply this same principle to chroma as if it were a luma channel (easy to do in avisynth), the same applies in making it darker. Let's apply that back to the chroma channels and removal of that noise and localised luminance difference means for a chroma channel a colour or saturation variation.

I have thought of a means around this, whether it works as intended I won't know until I do it. It will likely be the weekend at the soonest. I've also got a simple perceptual depth enhancement that works, but will need to find the appropriate way of scaling it based on strength. It will probably be ridiculed as well, but it's an option only so if you don't like it, don't use it! Running it internally makes sense as it should be applied before renoise. Depth enhancement will only be suitable for non-cruddy (that's the technical term :D) sources. I've also made changes to renoise already since version 2.0.

Khun_Doug
7th December 2017, 08:22
Hey bufadel, I really like this script. I have some SD sources from DVD, and a few HD sources from BD, that are really noisy. Really noisy! I had been using fluxsmooth and that did a good job at removing the noise. But even then the film still had some noise, and suffered a small amount of detail loss because of the high settings needed to deal with the amount of noise. I avoided a lot of the other denoisers because they just removed too much detail. So much so that the loss was very obvious. I am using your version 2.0 script with defaults. I am amazed at how much of the noise is removed and the detailed is retained. This even solved the problem of not being able to use sharpening. The built-in processing really produces a good result on these noisy sources.

One note that I did to move things along with encoding. In my AVISynth testing scripts I added prefect(8). In StaxRip I added a new PreFetch in the Misc section, and set it to prefetch(8) . The CPU is an I7-6850K so I have 12 threads. Using the higher prefetch really makes a drastic difference in encode speed.

zub35
7th December 2017, 17:28
That's weird. I've uploaded the version used here. It's the latest version (for now). 7-Zip archive.
https://1drv.ms/u/s!AmGuHbW3zvrBmb4tmBP2-KUToKUpVQ

http://i069.radikal.ru/1712/d5/df8f8e805e41.png

Perhaps compile with use of sse4 or avx2. Is it possible compile for sse2/3 CPUs?

UPD: if use Intel Software Development Emulator (https://software.intel.com/en-us/articles/pre-release-license-agreement-for-intel-software-development-emulator-accept-end-user-license-agreement-and-download) (sde) - all good.
but it's not the best solution for performance :|

ChaosKing
17th December 2017, 23:05
I get always this error when I set outbits > 8. using ver. v2.0 (09 November 2017), AVS+ r2544 mClean(thSAD = 300, rn=10, sharp=0, outbits=16,deband=1)

CombinePlanes: source bit depth is different from 8

ConvertTo16bit() works without problems. Am I missing something?

burfadel
19th December 2017, 18:30
That's probably an issue with the script, I've got some changes in the pipeline for bit related stuff, I'll fix that error then. I've got some other changes to make as well, and a mild depth enhancement feature to add. That will require a little testing in terms of strength application and possible affect of different resolutions. It's something that needs to be done before renoise application, and also before a texture enhancement feature. Both aren't suitable for truly low quality source, but they're perfectly fine for even average quality DVDs of old shows. They'll be optional and adjustable of course. There are a couple of other minor things to change.

I may have also found a bug in one of main filters or avisynth but that will require more testing. I won't have time until after the New Years when I get back from holidays. I'll be able to check on here though for any queries. I do have a minor update with adjusted renoise and Luma range of appliation, I'll post that later today as a separate post to the first one so people can compare it if they wish. It should produce a nicer effect and be milder on bright surfaces. It's where I got up to in the changes before I ran out of time for the year :). It won't include the bit fix or the enh setting resolution scale.

If there's any bad weather when I'm away or I find myself with a little share time I might be able to make a start on these on another rig/laptop, but no promises!

burfadel
21st December 2017, 08:52
Here's the 'test' update for revised renoise settings. No other changes applied, however there are several that will likely go in the next proper version as explained above. The use of it is exactly the same as version 2.0, but the results will be different as a result of the change to renoise. It may require tweaking up in strength (try say, rn=16 or something), if so let me know :). Also if it is less favourable than the previous version let me know also. It should handle heavy noise/renoise situations more effectively whilst sill producing the desired affect on less noisy sources.

# mClean spatio/temporal denoiser
# Version: 2.0B Interim Testing Update (21 December 2017)
# By burfadel

# +++ Description +++
# This script is intended to remove noise whilst retaining as much detail as possible.
# Typical spatial filters work by removing large variations in the image on a small scale, reducing noise but also making the image less
# sharp or temporally stable.

# mClean works primarily in the temporal domain, although there is some spatial limiting.
# Chroma is processed via a different method to luma for optimal results.
# Input must be 8-bit Planar type (YV12, YV16, YV24) or their equivalents in 10, 12, 14, or 16 bits.
# Chroma processing can be disabled with chroma=false.

# +++ Artifacts +++
# Spatial picture artifacts may remain as removing them is a fine balance between removing the unwanted artifact whilst not removing detail
# Additional dering/dehalo/deblock filters may be required, but should ONLY be uses if required due the detail lose/artifact removal balance
# A deblocking filter such as Deblock_QED can be applied after, as MClean currently does not deblock; this may be provided as an option later

# +++ Sharpening +++
# Additional sharpening filters may not be required, mClean does some light detail enhancement. Any additional sharpening filters may require
# a little less strength. Alternatively use a higher 'sharp' setting. Range of normal sharpening is 0-50. There are four extra options, 51
# through 54. These are for 'overboost' sharpening, suitable only for high quality, high resolution sources. Overboost sharpening requires the
# modplus plugin, this is only required if overboost is used. Default setting is scaled mild sharpening based on resolution.

# +++ ReNoise +++
# ReNoise adds back some of the removed luma noise. Re-adding original noise would be counterproductive, therefore ReNoise modifies this noise
# both spatially and temporally. The result of this modification is the noise becomes much nicer and it's impact on compressibility is greatly
# reduced. It is not applied on areas where the sharpening occurs as that would be counterproductive. Settings range from 1 to 20, default
# value is 13. The strength of renoise is affected by the the amount of original noise removed and how this noise varies between frames. It's
# main purpose is to reduce the 'flatness' that occurs with any form of effective denoising.

# +++ Chroma Strength +++
# This function allows you to adjust the strength of the chroma filtering. The values for the different sigmas are variably adjusted with the
# 'cstr' setting. The default is 5, and up to 60 is allowed. Higher values may allow for removal of rainbows etc., however there will be a
# degradation in the chroma quality. Decimal point setting are allowed for fine tuning, for instance a cstr value of 6.3. Chroma processing can
# be bypassed with chroma=false or setting a cstr strenghth of 0. Further adjustments are to be made to the script in regards to strong cstr
# will be completed later.

# +++ Deband +++
# This will perceptibly improve the quality of the image by reducing banding effect and adding a small amount of temporally stabilised grain to
# both luma and chroma. The settings are not adjustable as the default settings are suitable for most cases without having a large effect on
# compressibility. Only works on 8-bit sources. This is a limitation with the f3kdb, the inbuilt high bit-depth support is old and doesn't appear
# to work properly with higher bit depths. A later version of the script will reflect any changes in regards to updates allowing for greater than
# 8-bit support. Deband can be set as 0, 1, 2 or 3. Auto balance uses Autoadjust, it calculates statistics of the source clip, stabilises them
# temporally and uses them to adjust luminance gain & colour balance of the noise reduced clip.
# 0=disabled (default, >8-bit), 1=deband only (default, 8-bit), 2=level adjustment only, 3=both deband and auto balance

# +++ Outbits +++
# Specifies the bits per component (bpc) for the output for processing by additional filters. It will also be the bpc that mClean will process.
# By default, mClean processes as 12 bits if the input is 8 bit, and converts back to 8 bit. If the input is 10 bits or higher no conversion is
# done unless outbits is specified and is different to the input bpc. If you output at a higher bpc keep in mind that there may be limitations
# to what subsequent filters and the encoder may support.

# +++ Required plugins +++
# Latest RGTools, MVTools2, Masktools2, FFT3DFilter, f3kdb
# Latest Modplus, only required if using sharpening overboost (sharp settings 51 through 54)
# Latest DCTFilter, chikuzen update - https://github.com/chikuzen/DCTFilter
# Latest AutoAdjust - https://forum.doom9.org/showthread.php?t=167573
# Requires latest fftw.dll to be installed as instructed on the website - http://www.fftw.org


function mClean(clip c, int "thSAD", bool "chroma", int "sharp", int "rn", float "cstr", int "deband", int "outbits")
{

defH = Max (C.Height, C.Width/4*3) # Resolution calculation for auto blksize settings
thSAD = Default (thSAD, 420) # Denoising threshold
chroma = Default (chroma, true) # Process chroma
sharp = Default (sharp, DefH<2400 ? 10+int(DefH/60) : 50) # Detail orientated sharpen strength
rn = Default (rn, 14) # Luma ReNoise strength from 0 (disabled) to 20
cstr = chroma ? Default (cstr, 5) : Default (cstr, 0) # Chroma denoise strength from 0 (disabled) to 60. Not actioned if chroma is false
deband = bitspercomponent(c)==8 ? Default (deband, 1) : Default (deband, 0) # Apply deband and/or auto balance
outbits = Default (outbits, BitsPerComponent(c)) # Output bits, default input depth
calcbits = BitsPerComponent(c) == 8 ? 12 : outbits

Assert(isYUV(c)==true, """mClean: Supports only YUV formats (YV12, YV16, YV24)""")
Assert(isYUY2(c)==false, """mClean: Supports only YUV formats (YV12, YV16, YV24)""")
Assert(isYV411(c)==false, """mClean: Supports only YUV formats (YV12, YV16, YV24)""")
Assert(sharp>=0 && sharp<=54, """mClean: "sharp" ranges from 0 to 54""")
Assert(rn>=0 && rn<=20, """mClean: "rn" ranges from 0 to 20""")
Assert(cstr>=0 && cstr<=60, """mClean: Chroma denoise strength "cstr" ranges from 0 to 60""")
Assert(deband>=0 && deband<=3, """mClean: deband 0 (disabled), 1 (deband only), 2 (levels only), 3 (both)""")
deband<>0 ? deband<>2 ? Assert(bitspercomponent(c)==8, """mClean: Deband available only on an 8 bit source""") : nop() : nop()
Assert(outbits>=8 && outbits<=16, """mClean: "outbits" ranges from 8 to 16""")

padX = c.width%8 == 0 ? 0 : (8 - c.width%8)
padY = c.height%8 == 0 ? 0 : (8 - c.height%8)
c = padX+padY<>0 ? c.pointresize(c.width+padX, c.height+padY, 0, 0, c.width+padX, c.height+padY) : c
cy = ExtractY(c)
sc = defH >=2600 ? 8 : defH>1200 ? 4 : defH>480 ? 2 : 1
blksize = defH >=2600 ? 32 : defH>1200 ? 24 : defH>480 ? 12 : 8
blksizeL = blksize*2
overlap = blksize==32 ? 16 : blksize==24 ? 8 : blksize==12 ? 4 : 4
overlapL = blksize==64 ? 32 : blksize==48 ? 16 : blksize==24 ? 8 : 4
lambda = 750*(blksize*blksize)/64
lambdasc = 750*(blksizeL*blksizeL)/96
chroma = (cstr==0) ? false : true


# Spatio/temporal chroma noise filter
filt_chroma = chroma ? fft3dfilter (c, bw=blksizeL, bh=blksizeL, ow=overlapL, oh=overlapL, sharpen=0.28, bt=0, ncpu=1, kratio=2.15, dehalo=0.32,
\ sigma=1.05+(.2*cstr), sigma2=0.8+(0.56*cstr), sigma3=1.05+(.18*cstr), sigma4=1.2+(0.06*cstr), plane=3) : c
chroma_diff = chroma ? temporalsoften(removegrain(mt_makediff(c, filt_chroma, y=1, u=3, v=3), mode=-1, modeU=19), 1, 0, 160, scenechange=25, mode=2) : nop()

# Temporal luma noise filter
super = cy.MSuper (hpad=16, vpad=16)
supersc = sc<>1 ? MSuper(cy.BicubicResize(cy.Width/sc, cy.Height/sc), hpad=16/sc, vpad=16/sc) : super

# --> Analysis
bvec4 = MRecalculate(supersc, MAnalyse (supersc, isb = true, delta = 4, searchparam=3, blksize=blksizeL/sc, overlap=overlapL/sc, badSAD=1700, badrange=32+sc*4,
\ divide=2, lambda=lambdasc, lsad=1600), searchparam=4, blksize=blksize/sc, thSAD=int(thSAD-(thSAD/(sc*6))), overlap=overlap/sc, lambda=lambda)
bvec4 = sc<>1 ? MscaleVect (bvec4, sc) : bvec4
bvec3 = MRecalculate(supersc, MAnalyse (supersc, isb = true, delta = 3, searchparam=4, blksize=blksizeL/sc, overlap=overlapL/sc, badSAD=1500, badrange=32+sc*4,
\ divide=2, lambda=lambdasc, lsad=1500), searchparam=4, blksize=blksize/sc, thSAD=int(thSAD-(thSAD/(sc*6))), overlap=overlap/sc, lambda=lambda)
bvec3 = sc<>1 ? MscaleVect (bvec3, sc) : bvec3
bvec2 = MRecalculate(supersc, MAnalyse (supersc, isb = true, delta = 2, searchparam=4, blksize=blksizeL/sc, overlap=overlapL/sc, badSAD=1300, badrange=32+sc*4,
\ divide=2, lambda=lambdasc, lsad=1300), searchparam=4, blksize=blksize/sc, thSAD=int(thSAD-(thSAD/(sc*6))), overlap=overlap/sc, lambda=lambda)
bvec2 = sc<>1 ? MscaleVect (bvec2, sc) : bvec2
bvec1 = MRecalculate(super, MAnalyse (super, isb = true, delta = 1, blksize=blksizeL, overlap=overlapL, badSAD=800, badrange=32+sc*6,
\ searchparam=5, trymany=true, lsad=1150, lambda=lambda+200), blksize=blksize, overlap=overlap, search=5, searchparam=5, lambda=lambda)
fvec1 = MRecalculate(super, MAnalyse (super, isb = false, delta = 1, blksize=blksizeL, overlap=overlapL, badSAD=800, badrange=32+sc*6,
\ searchparam=5, trymany=true, lsad=1150, lambda=lambda+200), blksize=blksize, overlap=overlap, search=5, searchparam=5, lambda=lambda)
fvec2 = MRecalculate(supersc, MAnalyse (supersc, isb = false, delta = 2, searchparam=4, blksize=blksizeL/sc, overlap=overlapL/sc, badSAD=1300, badrange=32+sc*4,
\ divide=2, lambda=lambdasc, lsad=1300), searchparam=4, blksize=blksize/sc, thSAD=int(thSAD-(thSAD/(sc*6))), overlap=overlap/sc, lambda=lambda)
fvec2 = sc<>1 ? MscaleVect (fvec2, sc) : fvec2
fvec3 = MRecalculate(supersc, MAnalyse (supersc, isb = false, delta = 3, searchparam=4, blksize=blksizeL/sc, overlap=overlapL/sc, badSAD=1500, badrange=32+sc*4,
\ divide=2, lambda=lambdasc, lsad=1500), searchparam=4, blksize=blksize/sc, thSAD=int(thSAD-(thSAD/(sc*6))), overlap=overlap/sc, lambda=lambda)
fvec3 = sc<>1 ? MscaleVect (fvec3, sc) : fvec3
fvec4 = MRecalculate(supersc, MAnalyse (supersc, isb = false, delta = 4, searchparam=3, blksize=blksizeL/sc, overlap=overlapL/sc, badSAD=1700, badrange=32+sc*4,
\ divide=2, lambda=lambdasc, lsad=1600), searchparam=4, blksize=blksize/sc, thSAD=int(thSAD-(thSAD/(sc*6))), overlap=overlap/sc, lambda=lambda)
fvec4 = sc<>1 ? MscaleVect (fvec4, sc) : fvec4

# --> Applying cleaning
clean = cy.MDegrain4(super, bvec1, fvec1, bvec2, fvec2, bvec3, fvec3, bvec4, fvec4, thSAD=thSAD)
clean = clean.dctfilter (1,1,1,1,1,1,0.50,0)


# Post clean, pre-process deband
combined = deband==0 ? nop() : CombinePlanes (clean, chroma?filt_chroma:c, planes="YUV", source_planes="YUV", sample_clip=c)
combined = deband==0 ? nop() : deband>1 ? AutoAdjust (combined, external_clip=c, auto_gain=true, bright_limit=1.13, dark_limit=1.13, gamma_limit=1.055, auto_balance=true,
\ chroma_limit=1.10, chroma_process=112, balance_str=0.82) : combined
filt_chroma = deband==0 ? filt_chroma : deband<>2 ? mt_adddiff (combined, TemporalSoften(mt_makediff(combined, f3kdb (combined, preset=chroma?"high":"luma", range=17,
\ grainY=35, grainC=chroma?38:0)), 1, 255, chroma?255:0, 255, 2)) : combined
clean = deband<>0 ? ExtractY (filt_chroma) : clean

# Creating pass mask and applying chroma renoise
passmask = mt_lut (c, "x "+string(32)+" < 0 x "+string(45)+" > "+string(255)+" 0 x "+string(35)+" - "+string(255)+" "+string(32)+" "+string(65)+" - / * - ? ?")
filt_chroma = chroma ? mt_merge(filt_chroma, mt_adddiff(filt_chroma, chroma_diff, y=1, u=3, v=3), passmask, y=1, u=3, v=3) : filt_chroma

# --> Bit depth conversion
clean = calcbits != BitsPerComponent(clean) ? ConvertBits(clean, calcbits) : clean
cy = calcbits != BitsPerComponent(c) ? ConvertBits(cy, calcbits) : cy
passmask = calcbits != BitsPerComponent(passmask) ? ExtractY(convertbits(passmask, calcbits)) : ExtractY(passmask)

# Masks for spatial noise reduction and noise independent detail enhancement
noised = mt_makediff (blur(clean, 0.08), cy)
motion_mask = mmask(noised, fvec1, kind=1, ml=120).mt_binarize(34)
noise = mt_binarize (clense(mt_makediff(mt_binarize(noised), mt_edge(blur(clean, 0.12), "prewitt"))))

# Repairing areas of motion
clean_m = mt_merge (clean, repair(clean, removegrain(cy, mode=17)), motion_mask)

# Spatial luma denoising
clean2 = mt_merge (clean_m, removegrain(clean, 18), noise)

# Unsharp filter for spatial detail enhancement
clsharp = (sharp>=51<=54) ? mt_adddiff (mt_makediff(clean, gblur(clean2, (sharp-50), sd=3)), clean2) :
\ (sharp>0<=50) ? mt_adddiff (mt_makediff(clean, blur(clean2, 1.58*(0.03+(0.97/50)*sharp))), clean2) : clean2
diff = (sharp>0) ? mt_makediff(clean2, clsharp) : nop()
diff2 = (sharp>0) ? diff.temporalsoften(0,255,0,32,2) :nop()
clsharp = (sharp>0) ? mt_makediff(clean2, mt_lutxy(diff,diff2, "x 128 - y 128 - * 0 < x 128 - 100 / " + string(40)
\ + " * 128 + x 128 - abs y 128 - abs > x " + string(40) + " * y 100 " + string(40) + " - * + 100 / x ? ?")) : clsharp

# If selected, combining ReNoise
renoise = (rn==0) ? nop() : tweak(temporalsoften (noised, 2, 165, 0, 32, 2), cont=1.010+(0.008*(rn/20)))
clean2 = (rn>0<=20) ? mt_merge(clean2, mergeluma (clean2, mt_adddiff(clean2, renoise), 0.3+(rn*0.035)), passmask) : clean2

# Combining spatial detail enhancement with spatial noise reduction using prepared mask
filt_luma = mt_merge (clean2, clsharp, mt_invert(noise))

# Converting bits per channel and luma format
filt_luma = outbits < BitsPerComponent(filt_luma) ? ConvertBits(filt_luma, outbits, dither=1) : convertbits(filt_luma, outbits)
filt_chroma = BitsPerComponent(filt_chroma) <> BitsPerComponent(filt_luma) ? ConvertBits(filt_chroma, BitsPerComponent(filt_luma)) : filt_chroma

# Combining result of luma and chroma cleaning
output = CombinePlanes(filt_luma, filt_chroma, planes="YUV", source_planes="YUV", sample_clip=c)
output = padX+padY<>0 ? output.crop(0, 0, -padX, -padY) : output
return output
}

Micheal813
22nd December 2017, 11:18
Does mClean work with interlaced video?

burfadel
22nd December 2017, 13:26
It can be changed to :). Are you intending to encode as interlaced or will it be deinterlaced? There's pro's and cons if you intend to deinterlace afterwards, mostly with the amount processed.

Micheal813
22nd December 2017, 20:46
It can be changed to :). Are you intending to encode as interlaced or will it be deinterlaced? There's pro's and cons if you intend to deinterlace afterwards, mostly with the amount processed.

I wanted to deinterlace after denoising. Also, I can't get mClean 1.8 to work at all. VDub and it says something about an illegal instruction line 117. Line 117 is "clean = clean.dctfilter (1,1,1,1,1,1,0.50,0)" I was also trying to use it with Staxrip and could not get it to work.

Micheal813
22nd December 2017, 21:16
I removed "DCTFilter_avx2.dll" from my plugins folder and now VDub just show a green image and error.

Error reading source frame 0: Avisynth read error: CAVIStreamSynth: System exception - Illegal Instruction at 0x00007FFFE1A9306E

burfadel
23rd December 2017, 02:58
It sounds like your processor may not support AVX2. Have you tried the non-AVX2 version of DCTFilter?

Micheal813
23rd December 2017, 03:44
It sounds like your processor may not support AVX2. Have you tried the non-AVX2 version of DCTFilter?

Where can I find that? Sorry, I'm more of an intermediate user.

Also, how do I enable interlaced or is that in a later version? Thanks for the help.

burfadel
23rd December 2017, 07:05
It should be available in this archive from the author: https://github.com/chikuzen/DCTFilter/releases/download/0.5.0/DCTFilter-0.5.0.zip

For interlaced material, some of the filters may not support it directly. There is a work around by calling mclean for each field.

Separatefields().Assumeframebased()
Odd=selectodd().mclean()
Even=selecteven().mclean()
Interleave(odd, even)
Assumefieldbased()
Weave()

I'm using my phone as I'm away so can't check that, but I believe it should work! You can add the commands like deband=3 to mclean of course, just apply it to both lines.

Micheal813
23rd December 2017, 07:29
It should be available in this archive from the author: https://github.com/chikuzen/DCTFilter/releases/download/0.5.0/DCTFilter-0.5.0.zip



Oh, I've already tried that. Still not working.

Micheal813
23rd December 2017, 08:16
OK. I tried with the v2.0B above and get:

f3kdb does not have a named argument "preset"
line 143

Line 143 is:

\ grainY=35, grainC=chroma?38:0)), 1, 255, chroma?255:0, 255, 2)) : combined

ChaosKing
23rd December 2017, 11:59
Only version 2pre of f3kdb supports presets.

Micheal813
23rd December 2017, 13:16
Only version 2pre of f3kdb supports presets.

Sorry I'm not familiar with that. Where can I find the dll?

ChaosKing
23rd December 2017, 13:24
https://forum.doom9.org/showthread.php?t=161411

2.0pre2 with native VapourSynth support: http://nmm.me/tr

Micheal813
23rd December 2017, 13:37
https://forum.doom9.org/showthread.php?t=161411

Thanks. I found the x64 version since I'm using VDub x64.

Now when I open the avs script, VDub just shows a green image and error.

Error reading source frame 0: Avisynth read error: CAVIStreamSynth: System exception - Illegal Instruction at 0x00007FFFE1A9306E

pinterf
23rd December 2017, 15:18
Thanks. I found the x64 version since I'm using VDub x64.

Now when I open the avs script, VDub just shows a green image and error.

Error reading source frame 0: Avisynth read error: CAVIStreamSynth: System exception - Illegal Instruction at 0x00007FFFE1A9306E
I have run the basic script through Intel SDE emulator, simulating an SSE2-only architecture. Turned out that TemporalSoften in 10-14 bits mode contains an SSE4.1 instruction. Internally 'calcbits' variable is 12 in mClean, and since Micheal813 has an Athlon w/o SSE4.1 support, it fails. I'll fix it on my side (avisynth.dll) and send a test version to Micheal813.
Edit: test version link sent

burfadel
23rd December 2017, 16:54
That would have been a hard one to track down! I have noticed that on rare occasions encodes fail to initialise and it says unable to open input file. Unfortunately being away I'm unable to track the issue down, and if I did test it here it may not trigger it being on a completely different system. I'm sure it's something simple, anyone else come across that?

Micheal813
23rd December 2017, 21:56
I have run the basic script through Intel SDE emulator, simulating an SSE2-only architecture. Turned out that TemporalSoften in 10-14 bits mode contains an SSE4.1 instruction. Internally 'calcbits' variable is 12 in mClean, and since Micheal813 has an Athlon w/o SSE4.1 support, it fails. I'll fix it on my side (avisynth.dll) and send a test version to Micheal813.
Edit: test version link sent

It now works. Thanks a lot for your time. There's no way I would have been able to fix it on my own.

StainlessS
23rd December 2017, 22:38
For interlaced material, some of the filters may not support it directly. There is a work around by calling mclean for each field.

Separatefields().Assumeframebased()
Odd=selectodd().mclean()
Even=selecteven().mclean()
Interleave(odd, even)
Assumefieldbased()
Weave()

I'm using my phone as I'm away so can't check that, but I believe it should work! You can add the commands like deband=3 to mclean of course, just apply it to both lines.

Hi Burf, was above snippit correct ?
[should it have been Interleave(even, odd)]

Micheal813
23rd December 2017, 22:49
Hi Burf, was above snippit correct ?
[should it have been Interleave(even, odd)]

Also, I normally have to put AssumeTFF() to get things like QTGMC to work for me. Do I have to do the same here?

StainlessS
23rd December 2017, 22:59
Also, I normally have to put AssumeTFF() to get things like QTGMC to work for me. Do I have to do the same here?

I would think so, along with suggested change above
(but guess we should wait for Burf [Bourguignon] :) )

EDIT: If you TEMP change to

AssumeTFF
Separatefields().Assumeframebased()
Even=selecteven().mclean()
Odd=selectodd().mclean()
Interleave(even, odd)
BilinearResize(width,height*2)


And play through some sequence with motion, then should play reasonably smoothly, not jump backwards and forwards.

Then if OK restore to

AssumeTFF
Separatefields().Assumeframebased()
Even=selecteven().mclean()
Odd=selectodd().mclean()
Interleave(even, odd)
Assumefieldbased()
Weave()


Above assuming that even,odd was wrongly transposed.

EDIT: I've also changed order of the lines above in BLUE, just makes a bit more sense to me [but no real effect].

Micheal813
24th December 2017, 00:24
I would think so, along with suggested change above
(but guess we should wait for Burf [Bourguignon] :) )

EDIT: If you TEMP change to

AssumeTFF
Separatefields().Assumeframebased()
Even=selecteven().mclean()
Odd=selectodd().mclean()
Interleave(even, odd)
BilinearResize(width,height*2)


And play through some sequence with motion, then should play reasonably smoothly, not jump backwards and forwards.

Then if OK restore to

AssumeTFF
Separatefields().Assumeframebased()
Even=selecteven().mclean()
Odd=selectodd().mclean()
Interleave(even, odd)
Assumefieldbased()
Weave()


Above assuming that even,odd was wrongly transposed.

EDIT: I've also changed order of the lines above in BLUE, just makes a bit more sense to me [but no real effect].

It looks like you are correct.

AssumeTFF()
Separatefields().Assumeframebased()
Even=selecteven().mclean()
Odd=selectodd().mclean()
Interleave(even, odd)
Assumefieldbased()
Weave()

burfadel
24th December 2017, 04:54
Oops! Thanks for those corrections, it was thinking on the fly typing on the phone :). Was the speed okay? It's running everything twice, but half the processing on each run. Multithreading would help!

Micheal813
24th December 2017, 05:23
Oops! Thanks for those corrections, it was thinking on the fly typing on the phone :). Was the speed okay? It's running everything twice, but half the processing on each run. Multithreading would help!

Well I'm using Staxrip to encode a 45 minute avi (Lagarith) to h.264 and it will take about 12 hours. That's with mClean and QTGMC on my slow PC. Staxrip shows about 3.5fps I think. I'll have to look into mulithreading.

I also tested a 1 minute long Lagarith avi in VDubx64 with this script:

Avisource("F:\Capture\Test.avi")
ConvertToYV12()
AssumeTFF()
Separatefields().Assumeframebased()
Even=selecteven().mclean()
Odd=selectodd().mclean()
Interleave(even, odd)
Assumefieldbased()
Weave()

I used "fast recompress" and set compression to Lagarith YV12. It took about 9 minutes at about 3.5fps.

burfadel
25th December 2017, 02:06
That's probably not too bad of a speed? If it isn't too much of an issue I might do something like that internally for compatibility.

StainlessS
25th December 2017, 02:45
Micheal813,

As is Interlaced, then I would say you need this mod

Avisource("F:Capture\Test.avi")
ConvertToYV12(Interlaced=True)
AssumeTFF()
Separatefields().Assumeframebased()
Even=selecteven().mclean()
Odd=selectodd().mclean()
Interleave(even, odd)
Assumefieldbased()
Weave()

Micheal813
25th December 2017, 08:15
Micheal813,

As is Interlaced, then I would say you need this mod

Avisource("F:Capture\Test.avi")
ConvertToYV12(Interlaced=True)
AssumeTFF()
Separatefields().Assumeframebased()
Even=selecteven().mclean()
Odd=selectodd().mclean()
Interleave(even, odd)
Assumefieldbased()
Weave()


Stupid mistake on my part. Thanks for pointing it out!

Boulder
26th December 2017, 19:15
If you have truly interlaced material, you should not use a simple field separation. Use a smart bobber such as QTGMC or something lighter (someone else can come up with a good alternative), then use mClean on that and reinterleave the fields. Using temporal operations on separated fields alone is a no-no. There are also some alternatives to smart bobbing like (Un)FoldFieldsVertical which are safer than a simple field separation.

Gser
13th January 2018, 14:44
When using outbits=10 with an 8bit source file I get this error "CombinePlanes: source bit depth is different from 8"

burfadel
14th January 2018, 13:41
When using outbits=10 with an 8bit source file I get this error "CombinePlanes: source bit depth is different from 8"

I'll try fixing that for the next version. There are some major changes I'll be making for that, so it may be a few days and I'll probably do a test version in the meantime for one of the concept changes. :). Basically it's for people to decide which way is perceptively more congruent, the new or old, and I'll use that one. Having both methods is 'possible', but little point if one version proves no better, or is worse in most cases than the other.

Micheal813
18th January 2018, 04:28
MClean is working fin with 64 bit Avisynth+. When I try to use 32 bit with Avisynth 2.6, I get:

Avisynth open failure:
Script error: there is no function named "bitspercomponent"
mClean.avsi, line 72

StainlessS
18th January 2018, 04:33
bitspercomponent is AVS+. [If using 32bit Avs+, then maybe need update]

drizzit
18th January 2018, 10:50
Edited, Never mind problem is fixed, found an older post by burfadel with the version he uses and it now works!
(did go to https://forum.doom9.org/showthread.php?t=161411 for the ones I tried but had no luck with those)


That's weird. I've uploaded the version used here. It's the latest version (for now). 7-Zip archive.
https://1drv.ms/u/s!AmGuHbW3zvrBmb4tmBP2-KUToKUpVQ

-----------------------Problem Fixed but leaving incase it helps anyone else-----------------
Trying to update to the latest version while using staxrip I get the following error

System exception - Access Violation
(D:\StaxRip-x64\Apps\Plugins\avs\mClean\mClean.avsi, line 143)
(D:\Temp movies\Stax\random.file.name_temp\random.file.name, line 13)


Line 142 and 143 being the following

filt_chroma = deband==0 ? filt_chroma : deband<>2 ? mt_adddiff (combined, TemporalSoften(mt_makediff(combined, f3kdb (combined, preset=chroma?"high":"luma", range=17,
\ grainY=35, grainC=chroma?38:0)), 1, 255, chroma?255:0, 255, 2)) : combined

I saw Michael813 got an error on the same line but mine does not mention preset and I did make sure I have the 64bit 2pre version of f3kdb.
could it still be the same problem @pinterf found? And if so any chance you could send me that test version too?


My Basic specs
-------------------------- System Environment --------------------------

StaxRip : 1.7.0.4
Windows : Windows 7 Professional
Language : Swedish (Sweden)
CPU : Intel(R) Core(TM) i7-4790K CPU @ 4.00GHz
GPU : NVIDIA GeForce GTX 980, VNC Mirror Driver
Resolution : 2560 x 1440
DPI : 96

burfadel
19th January 2018, 11:22
It can be downloaded from here:
http://nmm.me/10z

It doesn't have 'modern' high bit depth support along with autolevels, although there is a workaround for that which I'll use. This is just in case it doesn't get updates. Autoadjust is an issue though, since it appears closed source it's up to LaTo to fix it and it appears he may not longer be active on this forum. Ideally f3kdb and autoadjust should be combined. I did try the major change to renoise, but it wasn't any better in 90 percent of cases so I scrapped that particular concept, but there are some other changes I can make to improve it

burfadel
27th January 2018, 11:50
Updated first post to version 2.1. There are lots of changes :). I still haven't added the depth and detail enhancement options, I will need to look into the most optimal base settings for these. Additionally, I need to change the sharpness parameter scaling :).

I probably should also add a list linking the latest required filters, at some stage. In any case, hope you like the new version and it would be great to know what people think of the changes.

ChaosKing
27th January 2018, 14:43
It produces artifacts for me. Tested version 2.1 and also some previous versions.
Lower thSAD dosen't seem to affect (or reduce) the "artifacts".


https://i.imgur.com/IHuiy2m.png

burfadel
27th January 2018, 15:07
The source look pretty clean apart from the aliasing and 'clean' pixellation, so in general the denoising probably isn't necessary. Were those artifacts in areas where there is motion? I can look into it, but on the test clips I have I didn't notice them. Do you have a clip of a few seconds including the above frame? On a side note, it may be possible another feature I will be adding could remove the aliasing. I'll also make it so you can run it independently from denoising, as Avisynth+ allows scripts to contain more than one function. That way, I can call the function from within the mClean part of the script, whilst also allowing the other functions to be applied independently.

ChaosKing
27th January 2018, 16:22
I used PointResize to better show the problem hence the pixelation.
I think the artifacts mostly appear on large flat areas with strong edges.

Here's a sample https://www.dropbox.com/s/ktizz9vxwtkwq26/cut_for_mclean.mkv?dl=1

burfadel
27th January 2018, 17:21
Thanks, I'll look in to that!

burfadel
28th January 2018, 09:33
Updated to v2.2. Fixed chroma not being processed with deband, resolved an issue with chroma debanding features (which didn't affect anything since it wasn't applied!), and added a luma diff so higher bitdepth detail isn't lost when using deband, since it can only be done in 8 bit.

@ Chaosking
I was intending to look into that issue of yours, but instead resolved the issues above. I'll look into soon :).

MysteryX
28th January 2018, 18:57
Can debanding be enabled/disabled?

ChaosKing
28th January 2018, 19:22
# +++ Deband +++
# This will perceptibly improve the quality of the image by reducing banding effect and adding a small amount of temporally stabilised grain to
# both luma and chroma. The settings are not adjustable as the default settings are suitable for most cases without having a large effect on
# compressibility. Auto balance uses Autoadjust, it calculates statistics of the clip, stabilises temporally and adjusts luminance gain & colour
# balance of the noise reduced clip.
# 0=disabled, 1=deband only, 2=auto balance only, 3=both deband and auto balance, 4=deband and veed, 5=all

burfadel
29th January 2018, 06:56
Updated first post to v2.3.

I think the artifacts mostly appear on large flat areas with strong edges.

Issue should be resolved now!

Can debanding be enabled/disabled?

As Chaosking highlighted, yes, deband etc can be disabled.

lansing
29th January 2018, 08:19
Issue should be resolved now!


No it still doesn't. Seriously have you ever test out your script on actual videos? I mean the issue existed for 4+ months and it was so obvious to spot.

UPDATE:
I double checked on every plugin I have for the script and looks like I have an outdated RGtools.dll. Artifacts are gone now.

ChaosKing
29th January 2018, 10:05
Unfortunately the artifacts are still present. I also updated all plugins just in case.
The only older version I have on my drive is 1.3c_2 (29 August 2017), and it produces artifacts too.

EDIT

Sorry, I forgot the dehalo filter in my script. It seems like the problem is fixed now!

Taurus
29th January 2018, 12:46
@burfadel
You should really make it clear in the first post of your thread, that your script is only running on Avisynth+.
Would have saved me a lot of messing around with plugins and error messages...
Groucho2004's AVSMeter finally sorted it out for me.
And an exact list of all the needed plugins with download list would really be appreciated.
Thank you.:thanks:

zub35
3rd February 2018, 13:45
There is a great plugin chroma denoise for VirtualDub - "Camcorder color denoise" http://acobw.narod.ru
If use it before mClean, on the digital record / very-lossy encode, gives excellent results. Alas, only 32bit and rgb

burfadel
3rd February 2018, 15:52
Yes, chroma is probably more difficult than luma. Currently making some changes, although there will likely be a delay as I may have found an issue with MVTools that affects one of the changes.

Selur
4th February 2018, 09:14
You should really make it clear in the first post of your thread, that your script is only running on Avisynth+.
I agree, since a few users asked me about adding support for the filter in Hybrid, but I had to decline that request due to the Avisynth+ dependency. (I only use AvisynthMT and Vapoursynth in Hybrid.)

That said, are there any plans to port this to Vapoursynth?

Cu Selur

burfadel
6th February 2018, 07:59
I agree, since a few users asked me about adding support for the filter in Hybrid, but I had to decline that request due to the Avisynth+ dependency. (I only use AvisynthMT and Vapoursynth in Hybrid.)

That said, are there any plans to port this to Vapoursynth?

Cu Selur

I'll make a list of required depenedencies and versions with the next release. No plans for vapoursynth conversion, as not sure the actual syntax. Also aren't the plugins a little different now, such that the output wouldn't be the same?

Taurus
6th February 2018, 13:13
I'll make a list of required depenedencies and versions with the next release.

Thank You :thanks:!

ryrynz
8th February 2018, 01:14
Anyone done any comparisons vs FluxSmooth?

Khun_Doug
16th February 2018, 18:34
I noticed something peculiar with v2.3. I have not done regression testing so the problem may exist in older versions. I am attempting to clean a B&W HD rip. The source is cropped 220 on the left and 220 on the right. I believe the HD source is VC1. I am using an I7-6850K with 12 threads. I use a prefetch of 8, and later I tried 10. What I notice is the speed starts at 22 fps. Inevitably at around the 6 minute point into the film the speed slows to around 15 fps and CPU also drops significantly. The script to pre-render the lossless video is now running 5 hours and the speed is 5.6 fps. This is only reading the source, cropping, and using mClean. I also noticed that ffmpeg went from 2.5 GB up to 3.3 GB of memory.

Does this sound like some type of memory leak? Why would it only seem to occur on this particular video? I have used mClean on several other sources, some of those also B&W, and I have not seen this happen. Any thoughts? Perhaps something with AviSynth+?

burfadel
18th February 2018, 08:38
Are all the filters used the latest? I'll release a major update to the script shortly. It's kind of ready now apart from one thing I want in the release.

Khun_Doug
21st February 2018, 19:24
As far as I know, the filters are the latest versions. I began to think the problem was the source video. The ripping software identified the soundtrack incorrectly and nothing could detect any playable audio in the rip. My thought is the odd audio frames confused the readers, causing them to slow down as they got further into the source. I did a second rip of the BD and forced the audio to AC3, and the problem went away.

However, I did notice something else. I have seen this happen on two different rips so I know the problem is not a unique thing. On some of the older sources the borders are not completely clean. I've see this on Summer Lovers and Forbidden Planet, so far. The result is at the edge where the source isn't clean the border seems to get amplified, or widened. The rest of the image is clean, just not at the edge. And it can be very noticeable depending on the scenes. Is there a setting that will have the script to be less aggressive on the borders? If not, is there that such a thing could be added? For instance, a parameter to be less aggressive on the right, bottom, etc. I can get a screen shot if needed.

burfadel
21st February 2018, 19:42
It might need a bit more padding. Still working on the next version, there are considerable changes.

Khun_Doug
21st February 2018, 21:57
If you would like some testing to see how the new version handles this, I am willing to do some testing. These are examples of what I see.

burfadel
24th February 2018, 10:43
Just posted a new version in the first post with considerable changes. There's still some minor tweaking and another feature I want to add to it, but that's to come later.

burfadel
24th February 2018, 10:46
I'll make a list of required depenedencies and versions with the next release. No plans for vapoursynth conversion, as not sure the actual syntax. Also aren't the plugins a little different now, such that the output wouldn't be the same?

Forgot about this post :). I don't have time currently to do this, I'll post it within the next few days.

If you have the very latest RGTools, Avisynth+ build, modplus, MVTools, Masktools, f3kdb, and chikuzen DCTFilter update, you should be fine :)

Please note I just updated the first post with the correct v3.0 version, not the earlier test version as previously.

zub35
24th February 2018, 22:18
On 3.0 again compatibility issues. Only works with "Intel SDE"
Maybe problem in masktools. First MT-mode, second ST-mode.
http://images2.imagebam.com/f2/65/15/c93acb760586423.png

burfadel
25th February 2018, 03:21
Are you using the latest MaskTools and MVTools by Pinterf?

zub35
25th February 2018, 03:30
Yeah. I tried not only the latter, alas, everywhere this problem.
Try to check yourself through the flag -p4p (sde.exe -p4p -- AVSMeter64.exe test.avs)
This will be emulation Pentium4-Prescott and Core2Quad-Kentsfield even on new CPUs

burfadel
25th February 2018, 09:32
Yeah. I tried not only the latter, alas, everywhere this problem.
Try to check yourself through the flag -p4p (sde.exe -p4p -- AVSMeter64.exe test.avs)
This will be emulation Pentium4-Prescott and Core2Quad-Kentsfield even on new CPUs

If it's a problem with one of the tools, it would be best to post it in the appropriate thread. Most of the tools were updated by Pinterf, who did a great job :), but it may have caused problems with older processors with certain functions.

For the relevant versions:
Masktools: https://forum.doom9.org/showthread.php?t=174333
MVTools: https://forum.doom9.org/showthread.php?t=173356
RGTools: http://forum.doom9.net/showpost.php?p=1809183&postcount=239 (although it's not a specific thread for it)
Avisynth+: https://forum.doom9.org/showthread.php?t=168856&page=199

I've updated the first post with v3.1 that has some tweaking. The changes won't resolve the issue you mentioned.

burfadel
25th February 2018, 09:45
Recommended versions (or later):

Avisynth+ r2636: https://github.com/pinterf/AviSynthPlus/releases
RGTools 0.96: https://github.com/pinterf/RgTools/releases
Masktools2 2.2.14: https://github.com/pinterf/masktools/releases/
MVTools2 2.7.25: https://github.com/pinterf/mvtools/releases/
Modplus: http://www.avisynth.nl/users/vcmohan/modPlus/modPlus.html (link down the bottom)
f3kdb 2.0pre2: https://forum.doom9.org/showthread.php?t=161411 (link at top of code window for 2.0pre2, http://nmm.me/tr)
AutoAdjust v2.60 : https://forum.doom9.org/showthread.php?t=167573

pinterf
25th February 2018, 10:39
Masktools, rgtools, mvtools2 and avs+ should work with sse2 and up. Earlier there were a couple of issues with sse2 only computers with specific filter parameters, but they were fixed (I was aware of).

zub35
25th February 2018, 13:28
I've updated the first post with v3.1 that has some tweaking. The changes won't resolve the issue you mentioned.

This will sound very strange, but version 3.1 has worked without compatibility issues. While the 3.0 version gives a problem.

pinterf, Let's try to figure it out. I will send you in private messages script version 3.0 and we will try to find out why this happens, so that in the future there was no such.

burfadel
25th February 2018, 14:01
That's weird, I can't think of a valid reason for that. The changes were with MAnalyse thSAD, and using reduceflicker function of clense only if it proved beneficial.

pinterf
25th February 2018, 14:12
There was a non-guarded SSE4.1 instruction in the convolution code in masktools2. Interactive test is on the way.

zub35
25th February 2018, 16:06
burfadel, if deband=0 BitsPerComponent error

phazer11
26th February 2018, 03:41
Love the filter burfadel, really the only thing saving one of my encodes atm. Perhaps you could help me eliminate a filter?

Currently I'm having to use the following script (after a solid few weeks of working on it) to get good results for this aggravating source. I had hoped mClean by itself might be able to do everything I needed but the defaults aren't quite good enough and I was having trouble sorting through for the syntax so had to combine it with my previous best script to have it eliminate the final resistance of noise.


SMDegrain(tr = 12, thSAD = 800, thSADC = 400, contrasharp = false, refinemotion = false, interlaced = false, pel = 1, subpixel = 3, prefilter = 0, blksize = 16, search = 5, Truemotion = false, thSCD1 = 2400, lsb = false)
mClean()


Also would anyone of you fine people be able to help burfadel port his beautiful filter over to Vapoursynth?

burfadel
26th February 2018, 10:09
Could you upload a short few seconds clip? It's hard to make suggestions without knowing what you're working with :D :)

phazer11
2nd March 2018, 10:58
When I can. Rig is going full bore on a batch job atm. A bit about the source. it's an anime from the 90's on BD it's from film stock not sure what kind can't find a description of the type film used. At any rate, hoping to cut out SMDegrain as it's bogging things down, it's also not completely able to get rid of the grain on it's own. In order to get any results with SMDegrain I had to use the thSCD1 parameter at 2400 then add in the settings from my next best setup. When I thought to add in your filter (at default settings mind you) it fixed all the remaining imperfections, well the ones that can be fixed without destroying details. Though there are some super fine details the script can't help but do something to
SMDegrain(tr = 12, thSAD = 800, thSADC = 400, contrasharp = false, refinemotion = false, interlaced = false, pel = 1, subpixel = 3, prefilter = 0, blksize = 16, search = 5, Truemotion = false, thSCD1 = 2400, lsb = false)
mClean()

My laptop isn't great so it'll take several hours overnight to get to the 7-9% point in the first episode (my benchmark as it's easily the worst section I've seen so far). If you could give me a better run down on the syntax for your filter I could try things out. From reading it looked like your filter shares several of the functions as I'd expect though I'm having trouble with syntax. It's been a while since I looked at syntax or code, especially for filters as I didn't have a computer capable for a while, just got some help with a deinterlacing issue (man I'm glad things are progressive these days, though the one I needed help with was fairly nasty).

Here's a before and after shot with the current settings on one of my test frames. The grain is quite dancy and changes pretty much every frame, the script works it just has a lot of overhead from running two filters that I'd like to cut out by cutting SMDegrain out.
http://screenshotcomparison.com/comparison/133368

Edit: Also, updated to your latest version and am getting an error through MVtools (clip and super clip have different bit depths) no idea why.

zub35
3rd March 2018, 22:15
burfadel Is it possible to add a multiplier or passes. For example, with ultra high compression (periscope streams), have to apply mClean twice, which worsens the speed.
Example

ccd(20,1) #Camcorder color denoise
mClean(deband=0,rn=0)
mClean(rn=0)
aWarpSharp2(100)

two adjacent frames before and after (apng) http://images2.imagebam.com/76/ec/3d/6b32f7769202563.png

UPD: After my requests,the author (Sergey 1400) of plugin "Camcorder color denoise" updated and made a 64-bit version. http://acobw.narod.ru
I Propose to add/replace chroma denoise to him in your script mClean.

burfadel
6th March 2018, 04:02
I can work something out there! There's also something what I want to do, I won't have much time until the weekend.

javidial
6th March 2018, 09:05
Where can I download the avs file for this plugin. I've been using the version 1.8 in staxrip and it works very well. I'm impressed. Thanks

Taurus
6th March 2018, 11:35
Where can I download the avs file for this plugin. I've been using the version 1.8 in staxrip and it works very well. I'm impressed. Thanks
Heh:confused:
Just look at the first page of this thread :sly:!
It's not a plugin, it's a script!
Just copy and paste the content of the code to a new avsi.
Make clear you name this script mClean.avsi and you should be done.....

burfadel
6th March 2018, 15:25
Check the dependencies, v3.2 requires recent versions and the output should be a bit nicer as well.

ryrynz
4th April 2018, 11:19
Link to flash3kyuu_deband 2.0pre2 is down, someone up it for me?
Found it (https://www.mediafire.com/folder/2izh9abzep52o/Video_works)

Stereodude
1st May 2018, 14:59
How does mClean compare to MCTD or QTGMC for SD and HD content?

(yes I know QTGMC is a deinterlacer, but InputType=1 works pretty well on progressive content)

Stereodude
2nd May 2018, 16:08
Link to flash3kyuu_deband 2.0pre2 is down, someone up it for me?
Found it (https://www.mediafire.com/folder/2izh9abzep52o/Video_works)
That's the 32-bit version. Do you have a link to a 64-bit one?

Also, this 32-bit one reports that it doesn't have an argument called presets.

Edit: A 64-bit one with a preset argument is available here:
https://1drv.ms/u/s!AmGuHbW3zvrBmb4tmBP2-KUToKUpVQ

lansing
2nd May 2018, 21:18
How does mClean compare to MCTD or QTGMC for SD and HD content?

(yes I know QTGMC is a deinterlacer, but InputType=1 works pretty well on progressive content)

It suffers the same problem as smdegrain that they altered details on moving objects.

Stereodude
2nd May 2018, 22:09
It suffers the same problem as smdegrain that they altered details on moving objects.
FWIW, I played with it a bit on a concert DVD I'm messing with. mClean seems to inferior to MCTD based on my examinations of still frames from it. I will pick a segment and compare it in motion, but I'm not holding out a lot of hope.

Based on what I've seen it might be better suited for animation, but that's just a guess.

Edit: In motion it's ugly. MCTD clobbers it.

Khun_Doug
9th May 2018, 06:29
Out of curiosity, and a test of my tenacity to get something to function, I grabbed the latest scripts for MCTD. Not to get off topic on this forum, can anyone report on it working properly with sharp=true? I believe this is the default and MCTD scripts I found all crash with sharp=true but will function with sharp=false. There is something about the parameters to mt_clamp being incorrect.

I bring it up here since MCTD is being compared to MClean. I don't believe the comparison is accurate or valid if the script is crippled.

I will try to post something on a forum for MCTD to see if anyone there has any suggestions. I really would like to give it a test. I have some really REALLY noisy sources that thrive on these filters.

lansing
9th May 2018, 17:03
Out of curiosity, and a test of my tenacity to get something to function, I grabbed the latest scripts for MCTD. Not to get off topic on this forum, can anyone report on it working properly with sharp=true? I believe this is the default and MCTD scripts I found all crash with sharp=true but will function with sharp=false. There is something about the parameters to mt_clamp being incorrect.

I bring it up here since MCTD is being compared to MClean. I don't believe the comparison is accurate or valid if the script is crippled.

I will try to post something on a forum for MCTD to see if anyone there has any suggestions. I really would like to give it a test. I have some really REALLY noisy sources that thrive on these filters.

You probably have some outdated plugin. This is the latest mctd from taro:
https://forum.doom9.org/showthread.php?p=1559222#post1559222

For the requirement filters you'll need to find them elsewhere as the download link was outdated.

Khun_Doug
10th May 2018, 05:25
Hi lansing, I tried the version of the script you showed and I also found one slightly newer version dated back to 2013. The plug-in for mt_clamp is in masktools2 and the version I used for testing is 2.2.14, dated February 2018. I tried both versions of the X86 plug-in, but not the XP version since I am not using XP. Keep in mind that up until now, none of the other scripts I use have had or have any trouble with this version of masktools2, and even the previous version. I even went so far as moving the masktools2.dll to a different folder just to be sure there wasn't one somewhere else, and that caused a slew of other errors, so I am sure the version of masktools2 is not some older version that sneaked in somewhere else.

I get nowhere with this filter if sharpening is used. Set sharp=false and it works. In looking at the code, sharpening seems to be the only section that uses the mt_clamp function. The error remains the same, no matter what version of masktools2, incorrect arguments to mt_clamp.

I'm dead in the water as for comparing this to MClean until I can get past this error. And since the last update was back in 2013 I wonder if the author is available, or even if there is anyone else that can understand the script or help me diagnose why it fails on my system. I did as much debugging as I am able to but I am not clear on what argument or arguments are wrong.

I admit I am bummed about this. Anytime I can add another tool, it's a good thing. And being able to compare tools is also a good thing. Neither is happening for me right now.

lansing
10th May 2018, 22:12
Hi lansing, I tried the version of the script you showed and I also found one slightly newer version dated back to 2013. The plug-in for mt_clamp is in masktools2 and the version I used for testing is 2.2.14, dated February 2018. I tried both versions of the X86 plug-in, but not the XP version since I am not using XP. Keep in mind that up until now, none of the other scripts I use have had or have any trouble with this version of masktools2, and even the previous version. I even went so far as moving the masktools2.dll to a different folder just to be sure there wasn't one somewhere else, and that caused a slew of other errors, so I am sure the version of masktools2 is not some older version that sneaked in somewhere else.

I get nowhere with this filter if sharpening is used. Set sharp=false and it works. In looking at the code, sharpening seems to be the only section that uses the mt_clamp function. The error remains the same, no matter what version of masktools2, incorrect arguments to mt_clamp.

I'm dead in the water as for comparing this to MClean until I can get past this error. And since the last update was back in 2013 I wonder if the author is available, or even if there is anyone else that can understand the script or help me diagnose why it fails on my system. I did as much debugging as I am able to but I am not clear on what argument or arguments are wrong.

I admit I am bummed about this. Anytime I can add another tool, it's a good thing. And being able to compare tools is also a good thing. Neither is happening for me right now.
What is the exact error you're getting?

Khun_Doug
11th May 2018, 07:14
The error is invalid arguments to mt_clamp. I tried another script/filter that uses mt_clamp and the filter functions, no errors. From that I conclude that masktools2 is good. Today I tried updating to the latest AviSynth+, and that had no effect on this. Tried masktools2 2.2.13 and 2.2.14, and the one listed in comments of the script, version 2.0a48. Same error message. Other scripts working fine, including MClean.

My system has an I7-6850K, Windows 10 64 bit 64GB RAM. It is possible I am hitting something that is CPU dependent. What confuses me is that others are using the script without errors. I don't know how to diagnose or debug this.

There is a thread on this on the Doom9 MCTD forum. I am not sure the moderator wants discussion about this error on this forum. It doesn't matter to me where I get help on this. I am stuck as it stands and I would really like to have another tool and be able to compare it against MClean.

lansing
11th May 2018, 07:53
The error is invalid arguments to mt_clamp. I tried another script/filter that uses mt_clamp and the filter functions, no errors. From that I conclude that masktools2 is good. Today I tried updating to the latest AviSynth+, and that had no effect on this. Tried masktools2 2.2.13 and 2.2.14, and the one listed in comments of the script, version 2.0a48. Same error message. Other scripts working fine, including MClean.

My system has an I7-6850K, Windows 10 64 bit 64GB RAM. It is possible I am hitting something that is CPU dependent. What confuses me is that others are using the script without errors. I don't know how to diagnose or debug this.

There is a thread on this on the Doom9 MCTD forum. I am not sure the moderator wants discussion about this error on this forum. It doesn't matter to me where I get help on this. I am stuck as it stands and I would really like to have another tool and be able to compare it against MClean.

I'm suspecting that you may have put a 64 bit dll into the 32 bit plugin folder or vise versa. The most basic way to find the problematic file is remove all dll from the plugin folder, run the mctd script, and then put the files back in one by one according to the error message.

Khun_Doug
11th May 2018, 09:43
I went through all the plugins and installed a 32 bit version to be sure I didn't make that error, and still get the same error. Remember that if I set sharp to false, the script operates properly. Also, I tried a different script that uses the mt_clamp function and that script runs correctly. Whatever this is, it is isolated to the MCTD script and only the section that invokes the mt_clamp function.

Just as a test, I slipped in a 64 bit dll in place of a 32 bit dll and AvsPmod immediately detected the error and will not process the script. I am now sure that all plugins are indeed 32 bit. And I only testing 32 bit at this point. Once I had that working I planned to move to 64 bit. I have always been under the impression that 32 bit was more stable than 64 bit. That may not be true anymore, and once I get sleep I will try this using 64 bit plugins. The struggle continues.

ChaosKing
11th May 2018, 09:58
Easiest way to check for plugin problems is avsmeter: https://forum.doom9.org/showthread.php?t=174797

AVSMeter(64).exe -avsinfo

Khun_Doug
12th May 2018, 05:06
I downloaded avsmeter and ran avsmeter -avsinfo. All the plugins list as 32 bit and look correct. I can attach the listing here if the information would be considered useful. Bottom line is the same results and error. This filter simply refuses to function with the defaults (no parameters), or any parameter as long as sharp is true. The error is the same, invalid arguments to function mt_clamp. But I tested other filters that use mt_clamp and there are no faults or script crashes. I have even gone so far as changing dgdecode.dll to ffms2.dll, and then trying a different source. Results are the same.

Any other suggestions or debugging tips are welcome.

StainlessS
12th May 2018, 09:41
Just a simple tip, can use RT_Stats RT_Debug() or RT_DebugF() to show progress,
eg

IntVar=42
RT_DebugF("OK here at step 1 (some IntVar = %d)",IntVar)
IntVar=SomeFunc()
RT_DebugF("OK here at step 2 (some IntVar = %d)",IntVar)


Need DebugView (Google) to view debug output.
or here:- https://docs.microsoft.com/en-us/sysinternals/downloads/debugview

Khun_Doug
13th May 2018, 06:53
StainlessS, the problem is resolved. I posted the information on the MCTD forum in the event someone else hits that error in the future. Now I can get back to the original thing; that was to compare mClean to MCTD. And now I have another tool in the grab bag.

MysteryX
13th May 2018, 20:42
The latest version is throwing me: "MDegrainX: clip and super clip have different bit depths" at line 128

MysteryX
14th May 2018, 03:28
I'm also getting much better results with MCTemporalDenoise than with mClean. It accomplishes the same goal, packing the various cleaning functions together to give optimal results, and doesn't have the shape and color distortion problems I had with mClean.

MysteryX
14th May 2018, 14:51
If used as a prefilter with FrameRateConverter, mClean was working nicely while MCTemporalDenoise introduces some artifacts. I think MCTemporalDenoise gives better image stabilization and output quality, at the cost of slightly shifting some motions causing artifacts with FRC, while mClean seems to better respect the vectors of the source image.

Khun_Doug
17th May 2018, 00:51
Now that I finally have an MCTD that is functional I have been able to do some comparisons between MCTD and mClean, and also included tests with SMDegrain. When using MCTD I chose the setting of medium. On my worst test cases, the material with the highest amount of noise, only mClean does a good job of cleaning and denoising while maintaining detail. Is it perfect? No. But MCTD and SMDegrain could not clean those worst cases and carried along a noticeable amount of noise.

The largest drawback I found with MCTD was the way it added a milky or smokey haze to the output. At first I ignored it and kept watching the output looking for artifacts, distortion, etc. But that haze becomes some obvious in certain scenes that there is no avoiding it. I purposely did side by side tests (with StackHorizontal) between the source and mClean, and the source and MCTD.

What I gleaned from the tests, so far, is that material that is not overly noisy, SMDegrain was my best choice. With some variations of its parameters I was able to significantly remove noise. But as I already stated, in the worse examples, mClean handled the task, and do so very well.

One other point worth mentioning is that mClean and SMDegrain work very well with SetFilterMTMode. The very best I was ever able to get with MCTD was SetFilterMTMode (4, force=true).

lansing
17th May 2018, 12:03
only mClean does a good job of cleaning and denoising while maintaining detail.
No it doesn't. With mdegrain being the core for temporal denoising, both mclean and smdegrain will alter details on moving object, that's how it is.


The largest drawback I found with MCTD was the way it added a milky or smokey haze to the output.
MCTD doesn't produce haze, you may have another issue with outdated filters.


What I gleaned from the tests, so far, is that material that is not overly noisy, SMDegrain was my best choice. With some variations of its parameters I was able to significantly remove noise. But as I already stated, in the worse examples, mClean handled the task, and do so very well.

For videos with heavy spatial noise, you will need to look at KnlmeansCL or the commercial Neat Video.

MysteryX
17th May 2018, 15:20
I find KnlMeansCL to give a lot blurrier results than mClean or MCTD. I guess we have to choose: blurrier or slight distortions.

Truth is various types of noise and videos will look better with either one of these 4: KnlMeansCL, MCTD, mClean or SMDegrain. So in my software I'm probably best to allow the user to select any one of them.

Khun_Doug
17th May 2018, 17:02
I agree on the blurred results from KNMeansCL. I tried FluxSmooth but it only handles the lightest amount of noise. The thing that caught my eye, quite by surprise actually, was the way mClean handles old B&W films where the brightness would flicker. I have one film that is really bad where the faces of the actors actually has brightness fluctuations. The mClean helped smooth that away and really stabilized the entire video. But I think having all of these as user choices rather than force just one is a wise decision.

The one thing to consider also is whether there needs to be sharpening after the noise filter. I haven't found the need for sharpening with mClean. With SMDegrain I set ContraSharp to true. I have too little experience with MCTD to know whether it needs additional sharpening. FluxSmooth definitely does need sharpening. My favorite choice is mSharpen. The option on that filter to have the areas highlighted so you can see what the filter is seeing is extremely helpful.

SaurusX
17th May 2018, 18:04
I’d be curious to see the knlmeanscl settings they people are using and having blurred results. From my testing the “a” parameter value plays the largest part in the blurring by removing low frequency noise, mottling, or subtle color differences in the video. Anything higher than 1 is going to smudge out the finer subtle details. But maybe that’s desired in situations. That value is a smoother. An “s” value greater than 1 tends to wink out pinpoint noise like star fields similar to the filter dedot(). That said, it’s my go-to denoiser due to its preservation of details with the correct settings for the source.

MysteryX
6th June 2018, 04:41
Does the latest version work for anyone? I still get this error:

"MDegrainX : clip and super clip have different bit depths"

Khun_Doug
6th June 2018, 05:43
I use mClean 3.2 quite a lot on older SD and HD material that have a goodly amount of grain and noise. I never hit that error with mClean. It could be that you have an outdated plug-in. I only ever processed 8 bit material so it is possible that higher bit-depth material may have a problem.

The best tool I found for checking plug-in information is AVSmeter. Look for duplicate plug-ins and check the versions.

Khun_Doug
28th June 2018, 18:17
Burfadel, do you have any plans to further tweak or modify this script? I keep hitting a problem where cropped sources have a visible line of distortion on the right side of the frames/clips. I have verified that if I crop further in, the distortion disappears. But I shouldn't need to crop off borders beyond the black (or noise).

Just to be sure the cropping was correct I tried a different filter that does a similar job. The line of distortion is not created in the output.

Could this be something related to divisible width? For example, does the source need to be a multiple of 2, 4, 8?

StainlessS
28th June 2018, 22:21
You could probably say what dimensions cause problem, and what fixes it.

This looks a little strange to me

padX = c.width%8 == 0 ? 0 : (16 - c.width%8)
padY = c.height%8 == 0 ? 0 : (16 - c.height%8)

Try changing all 8's to 16's and see what happens.

Khun_Doug
30th June 2018, 00:19
I think those modulo divisions are exactly where the fault lies. I was using 1436X1080. Everything else works fine but there is this line of corrupted pixels near the entire right edge. I changed the 8's to 16's and the corrupted pixels are gone. Just to be on the safe side I left the original lines but commented out, and added corrected versions of the lines.

padX = c.width%16 == 0 ? 0 : (16 - c.width%16)
padY = c.height%16 == 0 ? 0 : (16 - c.height%16)

If burfadel is around I would feel happy if he could confirm this as the culprit and fix.

Thanks for help!

Mawazi
3rd July 2018, 03:13
The latest version is throwing me: "MDegrainX: clip and super clip have different bit depths" at line 128

I was getting the same error. I updated mvtools2 to https://github.com/pinterf/mvtools/releases/download/2.7.31/mvtools-2.7.31-with-depans.7z and that cleared up the problem.

ChaosKing
16th August 2018, 13:53
Thanks to Wolfberry there is now a Vapoursynth version of mClean https://forum.doom9.org/showthread.php?t=175614

Selur
18th August 2018, 10:42
blksize = sc==8 ? 8 : ((defH/sc)/360)>1.5 ? 16 : ((defH/sc)/360)>0.8 ? 12 : 8
Is blksize 12 correct?
Analyze: the block size must be 2x2, 4x4, 8x4, 8x8, 16x2, 16x8, 16x16, 32x16, 32x32, 64x32, 64x64, 128x64, 128x128, 256x128, or 256x256
no mention of 12x...

Cu Selur

StainlessS
18th August 2018, 12:04
Changelog from Version 2.7.31
2.7.19.22 (20170525)

New: [MMask] Support any planar input video formats e.g. greyscale, Planar RGB.
Input clip can even be of different bit depth or format from vector's original format
For kind==5 where U and V is filled, the greyscale option is not allowed
Mod: [MMask] Faster: request source frame only for kind=5.
Fix: [MxxxxFPS,MMask]: MakeVectorOcclusionMaskTime garbage in bottom blocks (30 hrs of debugging)
Fix: [MMask] bottom padding garbage for padded frame dimension
Fix: [MMask] proper 10+ bits scene change values (for default: 1023, 4095, 16383, 65535. Was: 65535)
Parameter is still in 8-bit range
Fix: [MRecalculate] prevent overflow during thSAD scaling in 16 bits or large block sizes (32, 48...)
Fix: [DepanEstimate] Sometimes giving wrong motion instead of scene change detection
Fix: [MAnalyze] Possible overflow in MAnalyze 8 bit, block size 48x48 and above.
Overflow-safe predictor recalc for big block sizes
New: [General] Add block size 12x3 for SAD, allow 6x24
List of available block sizes
64x64, 64x48, 64x32, 64x16
48x64, 48x48, 48x24, 48x12
32x64, 32x32, 32x24, 32x16, 32x8
24x48, 24x24, 24x32, 24x12, 24x6
16x64, 16x32, 16x16, 16x12, 16x8, 16x4, 16x2
12x48, 12x24, 12x16, 12x12, 12x6, 12x3 <<<<<<<<<<<<<<<<<<<<<
8x32, 8x16, 8x8, 8x4, 8x2, 8x1
6x24, 6x12, 6x6, 6x3
4x8, 4x4, 4x2
3x6, 3x3
2x4, 2x2
Mod: [Internal] Reorganized 10-16 bit SAD simd intrinsics, faster 8-12% for BlkSize 12-32



EDIT: Selur, where did you find this
128x64, 128x128, 256x128, or 256x256

Wolfberry
18th August 2018, 13:08
It is from the single precision mvtools in vapoursynth, which is based on dubhater's mvtools port.

The new block sizes: 2x2, 64x64, 64x32, 128x128, 128x64, 256x256, 256x128 is added in r6.

StainlessS
18th August 2018, 13:33
It is from the single precision mvtools in vapoursynth, which is based on dubhater's mvtools port.

The new block sizes: 2x2, 64x64, 64x32, 128x128, 128x64, 256x256, 256x128 is added in r6.

Thanks Wolfberry.

Selur
18th August 2018, 14:47
Ah okay, wasn't aware that the values for SAD differed :)

Revan654
29th December 2018, 20:34
Not sure if anyone can help but I was wonder if what everyone uses for there values with this filter. I having some issue with it on Vapoursynth side(Avisynth always had issues and could never get it to work correctly)

With either avisynth or VapourSynth anyone what are good values to use without destroying the text/font in the picture. My issue currently with mclean it fixes the picture very nicely however warps the text(Like if the picture has a logo or something like that), it looks like it oversharpens it(Even if I set sharpen to 0).

creeve4
12th May 2021, 22:10
I am using mClean as a plugin with Staxrip. When using Avisynth+ and 4k video, I am getting the following error:

Script Error

Resize: Planar destination height must be a multiple of 2.
(C:\StaxRip-v2.5.0-x64\Apps\Plugins\AVS\mClean\mClean.avsi, line 100)

I am not resizing the videos and the dimensions of the videos are all divisible by 2.

StainlessS
12th May 2021, 22:41
Problem causer

sc = defH>2800 ? 8 : defH>1400 ? 4 : defH>720 ? 2 : 1

and this

super = MSuper (BicubicResize(chroma ? defined(fvec1) ? fvec1 : c : defined(bvec1) ? bvec1 : cy, c.Width/sc, c.Height/sc),
\ hpad=16/sc, vpad=16/sc, rfilter=4)


Maybe try mod [EDIT: Line 100]

super = MSuper (BicubicResize(chroma ? defined(fvec1) ? fvec1 : c : defined(bvec1) ? bvec1 : cy, c.Width/(sc*2)*2, c.Height/(sc*2)*2),
\ hpad=16/sc, vpad=16/sc, rfilter=4)


EDIT: I have not tried it, it may work, or may not, Burfadel needs to look at it.

EDIT: if say your height is, defH>2800, then sc=8, and then your height has to be a multiple of (8 * 2), else error.

sc = defH>2800 ? 8 : defH>1400 ? 4 : defH>720 ? 2 : 1


EDIT: Above fix probably will not work, sc is a bit more invloved later in script, author needs to fix it.

creeve4
13th May 2021, 01:28
Thank you for digging into this!

I'll stick with Vaporsynth for now. It seems the the port of mClean for Vaporsynth does not have this bug.

kedautinh12
13th May 2021, 05:33
Can you fix it now, Stainless?? I think development don't online now

Frank62
13th May 2021, 13:12
You may also simply use:

addborders(0,0,0,2) (or 4, or 6)
filter
crop(0,0,-0,-2) (or -4 or -6)

StainlessS
13th May 2021, 15:32
Suggest as Frank62 says above, but with slight mod.


function Padding(clip c, int left, int top, int right, int bottom) {
# Didee: http://forum.doom9.org/showthread.php?p=1596804#post1596804
# eg, Padding(32,32,0,0).Padding(0,0,32,32)
w = c.width()
h = c.height()
c.pointresize( w+left+right, h+top+bottom, -left, -top, w+left+right, h+top+bottom )
}

c=Colorbars.convertToYV12
WMOD=(8*2) # substitute whatever for 8
HMOD=(8*2) # Ditto
P_RGT=(c.Width % WMOD == 0) ? 0 : (WMOD - (c.Width % WMOD))
P_BOT=(c.Height % HMOD == 0) ? 0 : (HMOD - (c.Height % HMOD))
c=c.Padding(0,0,P_RGT,P_BOT)

#c=c.YourFilter

c=c.crop(0,0,-P_RGT,-P_BOT)
c

Better using Padding rather than black addborders, untested (other than not crash) but should be about right.

Frank62
13th May 2021, 16:34
More complicated but also more elegant. :)

JKyle
14th May 2021, 07:05
Suggest as Frank62 says above, but with slight mod.


function Padding(clip c, int left, int top, int right, int bottom) {
# Didee: http://forum.doom9.org/showthread.php?p=1596804#post1596804
# eg, Padding(32,32,0,0).Padding(0,0,32,32)
w = c.width()
h = c.height()
c.pointresize( w+left+right, h+top+bottom, -left, -top, w+left+right, h+top+bottom )
}

c=Colorbars.convertToYV12
WMOD=(8*2) # substitute whatever for 8
HMOD=(8*2) # Ditto
P_RGT=(c.Width % WMOD == 0) ? 0 : (WMOD - (c.Width % WMOD))
P_BOT=(c.Height % HMOD == 0) ? 0 : (HMOD - (c.Height % HMOD))
c=c.Padding(0,0,P_RGT,P_BOT)

#c=c.YourFilter

c=c.crop(0,0,-P_RGT,-P_BOT)
c

Better using Padding rather than black addborders, untested (other than not crash) but should be about right.

Thanks for the idea, @StainlessS. :)

Based on your suggestion, I modified the script source so that padX and padY should be based on mod 16, not mod 8 as in the original script.

And instead of using addborders, I adopted Padding and attached its definition at the bottom of the script source.

Here's the source.

# mClean spatio/temporal denoiser
# Version: 3.2 (01 March 2018)
# By burfadel

# Version: 3.2 mod J (2021-05-13)
# By JKyle
# Original idea by StainlessS
# https://forum.doom9.org/showthread.php?p=1942788#post1942788
# Replaced addborders with Padding
# Fixed the bug that source should be mod 16 if defH>2800

# +++ Description +++
# Typical spatial filters work by removing large variations in the image on a small scale, reducing noise but also making the image less
# sharp or temporally stable. mClean removes noise whilst retaining as much detail as possible, as well as provide optional image enhancement

# mClean works primarily in the temporal domain, although there is some spatial limiting
# Chroma is processed a little differently to luma for optimal results
# Input must be 8-bit Planar type (YV12, YV16, YV24) or their equivalents in 10, 12, 14, or 16 bits
# Chroma processing can be disabled with chroma=false

# +++ Artifacts +++
# Spatial picture artifacts may remain as removing them is a fine balance between removing the unwanted artifact whilst not removing detail
# Additional dering/dehalo/deblock filters may be required, but should ONLY be uses if required due the detail loss/artifact removal balance

# +++ Sharpening +++
# Applies a modified unsharp mask to edges and major detected detail. Range of normal sharpening is 0-20, the default 10. There are 4 additional
# settings, 21-24 that provide 'overboost' sharpening. Overboost sharpening is only suitable typically for high definition, high quality sources.
# Actual sharpening calculation is scaled based on resolution.

# +++ ReNoise +++
# ReNoise adds back some of the removed luma noise. Re-adding original noise would be counterproductive, therefore ReNoise modifies this noise
# both spatially and temporally. The result of this modification is the noise becomes much nicer and it's impact on compressibility is greatly
# reduced. It is not applied on areas where the sharpening occurs as that would be counterproductive. Settings range from 1 to 20, default
# value is 14. The strength of renoise is affected by the the amount of original noise removed and how this noise varies between frames. It's
# main purpose is to reduce the 'flatness' that occurs with any form of effective denoising.

# +++ Deband +++
# This will perceptibly improve the quality of the image by reducing banding effect and adding a small amount of temporally stabilised grain to
# both luma and chroma. The settings are not adjustable as the default settings are suitable for most cases without having a large effect on
# compressibility. Auto balance uses Autoadjust, it calculates statistics of the clip, stabilises temporally and adjusts luminance gain & colour
# balance of the noise reduced clip.
# 0=disabled, 1=deband only, 2=auto balance only, 3=both deband and auto balance, 4=deband and veed, 5=all

# +++ Depth +++
# This applies a modified warp sharpening on the image that may be useful for certain things, and can improve the perception of image depth. Default
# is 0 (disabled), and ranges up to 5. This function will distort the image, for animation a setting of 1 or 2 can be beneficial to improve lines. The
# effect

# +++ Strength +++
# The strength of the denoising effect can be adjusted using this parameter. It ranges from 20 percent denoising effect with strength 1, up to the
# 100 percent of the denoising with strength 20 (default). This function works by blending a scaled percentage of the original image with the processed
# image.

# +++ Outbits +++
# Specifies the bits per component (bpc) for the output for processing by additional filters. It will also be the bpc that mClean will process.
# By default, mClean processes as 12 bits if the input is 8 bit, and converts back to 8 bit. If the input is 10 bits or higher no conversion is
# done unless outbits is specified and is different to the input bpc. If you output at a higher bpc keep in mind that there may be limitations
# to what subsequent filters and the encoder may support.

# +++ Required plugins +++
# Latest RGTools, MVTools2, Masktools2, f3kdb, Modplus, AutoAdjust
# Refer to https://forum.doom9.org/showpost.php?p=1834698&postcount=334

function mClean(clip c, int "thSAD", bool "chroma", int "sharp", int "rn", int "deband", int "depth", float "strength", int "outbits")
{

defH = Max (C.Height, C.Width/4*3) # Resolution calculation for auto blksize settings
thSAD = Default (thSAD, 400) # Denoising threshold
chroma = Default (chroma, true) # Process chroma
sharp = Default (sharp, 10) # Sharp multiplier
rn = Default (rn, 14) # Luma ReNoise strength from 0 (disabled) to 20
deband = Default (deband, 4) # Apply deband/veed and/or auto balance
depth = Default (depth, 0) # Depth enhancement
strength = Default (strength, 20) # Strength of denoising.
outbits = Default (outbits, BitsPerComponent(c)) # Output bits, default input depth
calcbits = BitsPerComponent(c) == 8 ? 12 : outbits

Assert(isYUV(c)==true, """mClean: Supports only YUV formats (YV12, YV16, YV24)""")
Assert(isYUY2(c)==false, """mClean: Supports only YUV formats (YV12, YV16, YV24)""")
Assert(isYV411(c)==false, """mClean: Supports only YUV formats (YV12, YV16, YV24)""")
Assert(sharp>=0 && sharp<=24, """mClean: "sharp" ranges from 0 to 24""")
Assert(rn>=0 && rn<=20, """mClean: "rn" ranges from 0 to 20""")
Assert(deband>=0 && deband<=5, """mClean: deband options 0 (disabled) to 5. Refer to description""")
Assert(depth>=0 && depth<=5, """mClean: depth ranges from 0 (disabled) to 5""")
Assert(strength>0 && depth<=20, """mClean: strength ranges from 1 (20%) to 20 (100%, default)""")
Assert(outbits>=8 && outbits<=16, """mClean: "outbits" ranges from 8 to 16""")

# padX, padY modified to be based on mod 16, not mod 8 as in the original script
# modified by JKyle
padX = c.width%16 == 0 ? 0 : (16 - c.width%16)
padY = c.height%16 == 0 ? 0 : (16 - c.height%16)
c = padX+padY<>0 ? c.Padding(0, 0, padX, padY) : c
cy = ExtractY(c)
sc = defH>2800 ? 8 : defH>1400 ? 4 : defH>720 ? 2 : 1
blksize = sc==8 ? 8 : ((defH/sc)/360)>1.5 ? 16 : ((defH/sc)/360)>0.8 ? 12 : 8
overlap = blksize>=12 ? 6 : 2
lambda = 775*(blksize*blksize)/64
sharp = sharp>20 ? sharp+30 : DefH<=2600 ? 16+round(defH*(34/2600)*sharp/20) : 50
depth = depth*2
depth2 = -(depth+(depth/2))


# Denoise preparation
c = chroma ? Median (c, yy=false, uu=true, vv=true) : c

# Temporal luma noise filter
fvec1 = bitspercomponent(c)>8 ? convertbits(c, 8) : undefined()
bvec1 = bitspercomponent(cy)>8 ? convertbits(cy, 8) : undefined()
super = MSuper (BicubicResize(chroma ? defined(fvec1) ? fvec1 : c : defined(bvec1) ? bvec1 : cy, c.Width/sc, c.Height/sc),
\ hpad=16/sc, vpad=16/sc, rfilter=4)
super2 = MSuper (chroma ? defined(fvec1) ? fvec1 : c : defined(bvec1) ? bvec1 : cy, hpad=16, vpad=16, levels=1)

# --> Analysis
bvec4 = MRecalculate(super2, MscaleVect (MAnalyse (super, isb = true, delta = 4, blksize=blksize, overlap=overlap), sc),
\ blksize=blksize, overlap=overlap, lambda=lambda, thSAD=180)
bvec3 = MRecalculate(super2, MscaleVect (MAnalyse (super, isb = true, delta = 3, blksize=blksize, overlap=overlap), sc),
\ blksize=blksize, overlap=overlap, lambda=lambda, thSAD=180)
bvec2 = MRecalculate(super2, MscaleVect (MAnalyse (super, isb = true, delta = 2, blksize=blksize, overlap=overlap,
\ badSAD=1100, lsad=1120), sc), searchparam=3, blksize=blksize, overlap=overlap, lambda=lambda, thSAD=180)
bvec1 = MRecalculate(super2, MscaleVect (MAnalyse (super, isb = true, delta = 1, blksize=blksize, overlap=overlap, badSAD=1500, badrange=27,
\ search=5, lsad=980), sc), blksize=blksize, overlap=overlap, search=5, searchparam=3, lambda=lambda, thSAD=180)
fvec1 = MRecalculate(super2, MscaleVect (MAnalyse (super, isb = false, delta = 1, blksize=blksize, overlap=overlap, badSAD=1500, badrange=27,
\ search=5, lsad=980), sc), blksize=blksize, overlap=overlap, search=5, searchparam=3, lambda=lambda, thSAD=180)
fvec2 = MRecalculate(super2, MscaleVect (MAnalyse (super, isb = false, delta = 2, blksize=blksize, overlap=overlap,
\ badSAD=1100, lsad=1120), sc), searchparam=3, blksize=blksize, overlap=overlap, lambda=lambda, thSAD=180)
fvec3 = MRecalculate(super2, MscaleVect (MAnalyse (super, isb = false, delta = 3, blksize=blksize, overlap=overlap), sc),
\ blksize=blksize, overlap=overlap, lambda=lambda, thSAD=180)
fvec4 = MRecalculate(super2, MscaleVect (MAnalyse (super, isb = false, delta = 4, blksize=blksize, overlap=overlap), sc),
\ blksize=blksize, overlap=overlap, lambda=lambda, thSAD=180)

# --> Bit depth conversion
c = chroma ? calcbits != BitsPerComponent(c) ? ConvertBits(c, calcbits) : c : c
super2 = calcbits != BitsPerComponent(super2) ? ConvertBits(super2, calcbits) : super2
cy = calcbits != BitsPerComponent(cy) ? ConvertBits(cy, calcbits) : cy

# --> Applying cleaning
clean = MDegrain4(chroma ? c : cy, super2, bvec1, fvec1, bvec2, fvec2, bvec3, fvec3, bvec4, fvec4, thSAD=thSAD)
u = chroma ? ExtractU(clean) : nop ()
v = chroma ? ExtractV(clean) : nop ()
filt_chroma = chroma ? CombinePlanes(c, mt_adddiff(u, clense(mt_makediff(ExtractU(c), u), reduceflicker=true)), mt_adddiff(v,
\ clense(mt_makediff(ExtractV(c), v), reduceflicker=true)), planes="yuv", source_planes="yyy", sample_clip=c) : c
clean = chroma ? ExtractY(clean) : clean

# Post clean, pre-process deband
filt_chroma_bits = BitsPerComponent(filt_chroma)
clean2 = deband==0 ? nop() : ConvertBits(clean, 8)
noise_diff = deband==0 ? nop() : BitsPerComponent(c)==8 ? nop() : mt_makediff(convertbits(clean2, calcbits), clean)
depth_calc = deband==0 ? nop() : CombinePlanes (clean2, filt_chroma_bits>8 ? ConvertBits(filt_chroma, 8) : filt_chroma, planes="YUV",
\ source_planes="YUV", pixel_type="YV12")
depth_calc = deband==0 ? nop() : deband>1 ? deband==4 ? depth_calc : AutoAdjust (depth_calc, auto_gain=true, bright_limit=1.09, dark_limit=1.11,
\ gamma_limit=1.045, auto_balance=true, chroma_limit=1.13, chroma_process=115, balance_str=0.85) : depth_calc
depth_calc = deband==0 ? undefined() : deband<>2 ? f3kdb (depth_calc, preset=chroma?"high":"luma", range=16, grainY=38*(defH/540),
\ grainC=chroma?37*(defH/540):0) :depth_calc
clean = deband==0 ? clean : BitsPerComponent(c)==8 ? ExtractY (depth_calc) : mt_adddiff(ConvertBits(ExtractY
\ (depth_calc), calcbits), noise_diff)
depth_calc = deband==0 ? nop() : BitsPerComponent(depth_calc)<>filt_chroma_bits ? ConvertBits(depth_calc, filt_chroma_bits) : depth_calc
filt_chroma = deband==0 ? filt_chroma : deband>4 ? veed(depth_calc) : depth_calc

# Spatial luma denoising
clean2 = removegrain(clean, 18)

# Unsharp filter for spatial detail enhancement
clsharp = sharp>0 ? sharp>=51<=54 ? mt_makediff(clean, gblur(clean2, (sharp-50), sd=3)) :
\ mt_makediff(clean, blur(clean2, 1.58*(0.03+(0.97/50)*sharp))) : nop()
clsharp = mt_adddiff(clean2, repair(clense(clsharp), clsharp, 12))

# If selected, combining ReNoise
noise_diff = mt_makediff (clean2, cy)
clean2 = rn>0<=20 ? mt_merge(clean2, mergeluma (clean2, mt_adddiff(clean2, tweak(clense(noise_diff, reduceflicker=true), cont=1.008+(0.0032*(rn/20)))),
\ 0.3+(rn*0.035)), mt_lut (overlay(clean, invert(clean), mode="darken"), "x 32 scaleb < 0 x 45 scaleb > range_max 0 x 35 scaleb - range_max 32
\ scaleb 65 scaleb - / * - ? ?")) : clean2

# Combining spatial detail enhancement with spatial noise reduction using prepared mask
noise_diff = mt_invert(mt_binarize(noise_diff))
clean2 = sharp>0 ? mt_merge (clean2, clsharp, overlay(noise_diff, mt_edge(clean, "prewitt"), mode="lighten")) :
\ mt_merge (clean2, clean, overlay(noise_diff, mt_edge(clean, "prewitt"), mode="lighten"))

# Converting bits per channel and luma format
filt_chroma = outbits < BitsPerComponent(filt_chroma) ? ConvertBits(filt_chroma, outbits, dither=1) : ConvertBits(filt_chroma, outbits)
clean2 = outbits < BitsPerComponent(clean2) ? ConvertBits(clean2, outbits, dither=1) : ConvertBits(clean2, outbits)
c = BitsPerComponent(c) <> BitsPerComponent(clean2) ? ConvertBits(c, BitsPerComponent(clean2)) : c

# Combining result of luma and chroma cleaning
output = CombinePlanes(clean2, filt_chroma, planes="YUV", source_planes="YUV", sample_clip=c)
output = strength<20 ? Merge(c, output, 0.2+(0.04*strength)) : output
depth_calc = depth>0 ? defh>640 ? bicubicresize(output, 720, 480) : output : nop()
output = depth>0 ? mt_adddiff(output, spline36resize(mt_makediff(awarpsharp2(depth_calc, depth=depth2, blur=3),
\ awarpsharp2(depth_calc, depth=depth, blur=2)), output.width, output.height)) : output
output = padX+padY<>0 ? output.crop(0, 0, -padX, -padY) : output

return output
}

function Padding(clip c, int left, int top, int right, int bottom) {
# Didée: http://forum.doom9.org/showthread.php?p=1596804#post1596804
w = c.width()
h = c.height()
c.pointresize( w+left+right, h+top+bottom, -left, -top, w+left+right, h+top+bottom )
}

I think it has no problem with 4K inputs now.

kedautinh12
14th May 2021, 07:20
Thanks, but need replace f3kdb to neo-f3kdb for speed and i think Vcmohan rename modplus to manyPlus
http://www.avisynth.nl/users/vcmohan/manyPlus/manyPlus.html

JKyle
14th May 2021, 07:43
Thanks, but need replace f3kdb to neo-f3kdb for speed and i think Vcmohan rename modplus to manyPlus
http://www.avisynth.nl/users/vcmohan/manyPlus/manyPlus.html

Thanks for the info.

Added as a note in the script.

# mClean spatio/temporal denoiser
# Version: 3.2 (01 March 2018)
# By burfadel

# Version: 3.2 mod J (2021-05-13)
# By JKyle
# Original idea by StainlessS
# https://forum.doom9.org/showthread.php?p=1942788#post1942788
# Replaced addborders with Padding
# Fixed the bug that the source should be processed based on mod 16 if defH>2800
# Replaced f3kdb with neo_f3kdb

# +++ Description +++
# Typical spatial filters work by removing large variations in the image on a small scale, reducing noise but also making the image less
# sharp or temporally stable. mClean removes noise whilst retaining as much detail as possible, as well as provide optional image enhancement

# mClean works primarily in the temporal domain, although there is some spatial limiting
# Chroma is processed a little differently to luma for optimal results
# Input must be 8-bit Planar type (YV12, YV16, YV24) or their equivalents in 10, 12, 14, or 16 bits
# Chroma processing can be disabled with chroma=false

# +++ Artifacts +++
# Spatial picture artifacts may remain as removing them is a fine balance between removing the unwanted artifact whilst not removing detail
# Additional dering/dehalo/deblock filters may be required, but should ONLY be uses if required due the detail loss/artifact removal balance

# +++ Sharpening +++
# Applies a modified unsharp mask to edges and major detected detail. Range of normal sharpening is 0-20, the default 10. There are 4 additional
# settings, 21-24 that provide 'overboost' sharpening. Overboost sharpening is only suitable typically for high definition, high quality sources.
# Actual sharpening calculation is scaled based on resolution.

# +++ ReNoise +++
# ReNoise adds back some of the removed luma noise. Re-adding original noise would be counterproductive, therefore ReNoise modifies this noise
# both spatially and temporally. The result of this modification is the noise becomes much nicer and it's impact on compressibility is greatly
# reduced. It is not applied on areas where the sharpening occurs as that would be counterproductive. Settings range from 1 to 20, default
# value is 14. The strength of renoise is affected by the the amount of original noise removed and how this noise varies between frames. It's
# main purpose is to reduce the 'flatness' that occurs with any form of effective denoising.

# +++ Deband +++
# This will perceptibly improve the quality of the image by reducing banding effect and adding a small amount of temporally stabilised grain to
# both luma and chroma. The settings are not adjustable as the default settings are suitable for most cases without having a large effect on
# compressibility. Auto balance uses Autoadjust, it calculates statistics of the clip, stabilises temporally and adjusts luminance gain & colour
# balance of the noise reduced clip.
# 0=disabled, 1=deband only, 2=auto balance only, 3=both deband and auto balance, 4=deband and veed, 5=all

# +++ Depth +++
# This applies a modified warp sharpening on the image that may be useful for certain things, and can improve the perception of image depth. Default
# is 0 (disabled), and ranges up to 5. This function will distort the image, for animation a setting of 1 or 2 can be beneficial to improve lines. The
# effect

# +++ Strength +++
# The strength of the denoising effect can be adjusted using this parameter. It ranges from 20 percent denoising effect with strength 1, up to the
# 100 percent of the denoising with strength 20 (default). This function works by blending a scaled percentage of the original image with the processed
# image.

# +++ Outbits +++
# Specifies the bits per component (bpc) for the output for processing by additional filters. It will also be the bpc that mClean will process.
# By default, mClean processes as 12 bits if the input is 8 bit, and converts back to 8 bit. If the input is 10 bits or higher no conversion is
# done unless outbits is specified and is different to the input bpc. If you output at a higher bpc keep in mind that there may be limitations
# to what subsequent filters and the encoder may support.

# +++ Required plugins +++
# Latest RGTools, MVTools2, Masktools2, neo_f3kdb, Modplus(renamed to manyPlus as it is merged with other plugins, 2021-04-21), AutoAdjust
# Refer to https://forum.doom9.org/showpost.php?p=1834698&postcount=334
# For neo_f3kdb, visit https://github.com/HomeOfAviSynthPlusEvolution/neo_f3kdb
# For Modplus(manyPlus), visit http://www.avisynth.nl/users/vcmohan/manyPlus/manyPlus.html (info by kedautinh12)

function mClean(clip c, int "thSAD", bool "chroma", int "sharp", int "rn", int "deband", int "depth", float "strength", int "outbits")
{

defH = Max (C.Height, C.Width/4*3) # Resolution calculation for auto blksize settings
thSAD = Default (thSAD, 400) # Denoising threshold
chroma = Default (chroma, true) # Process chroma
sharp = Default (sharp, 10) # Sharp multiplier
rn = Default (rn, 14) # Luma ReNoise strength from 0 (disabled) to 20
deband = Default (deband, 4) # Apply deband/veed and/or auto balance
depth = Default (depth, 0) # Depth enhancement
strength = Default (strength, 20) # Strength of denoising.
outbits = Default (outbits, BitsPerComponent(c)) # Output bits, default input depth
calcbits = BitsPerComponent(c) == 8 ? 12 : outbits

Assert(isYUV(c)==true, """mClean: Supports only YUV formats (YV12, YV16, YV24)""")
Assert(isYUY2(c)==false, """mClean: Supports only YUV formats (YV12, YV16, YV24)""")
Assert(isYV411(c)==false, """mClean: Supports only YUV formats (YV12, YV16, YV24)""")
Assert(sharp>=0 && sharp<=24, """mClean: "sharp" ranges from 0 to 24""")
Assert(rn>=0 && rn<=20, """mClean: "rn" ranges from 0 to 20""")
Assert(deband>=0 && deband<=5, """mClean: deband options 0 (disabled) to 5. Refer to description""")
Assert(depth>=0 && depth<=5, """mClean: depth ranges from 0 (disabled) to 5""")
Assert(strength>0 && depth<=20, """mClean: strength ranges from 1 (20%) to 20 (100%, default)""")
Assert(outbits>=8 && outbits<=16, """mClean: "outbits" ranges from 8 to 16""")

# padX, padY modified to be based on mod 16, not mod 8 as in the original script
# modified by JKyle
padX = c.width%16 == 0 ? 0 : (16 - c.width%16)
padY = c.height%16 == 0 ? 0 : (16 - c.height%16)
c = padX+padY<>0 ? c.Padding(0, 0, padX, padY) : c
cy = ExtractY(c)
sc = defH>2800 ? 8 : defH>1400 ? 4 : defH>720 ? 2 : 1
blksize = sc==8 ? 8 : ((defH/sc)/360)>1.5 ? 16 : ((defH/sc)/360)>0.8 ? 12 : 8
overlap = blksize>=12 ? 6 : 2
lambda = 775*(blksize*blksize)/64
sharp = sharp>20 ? sharp+30 : DefH<=2600 ? 16+round(defH*(34/2600)*sharp/20) : 50
depth = depth*2
depth2 = -(depth+(depth/2))


# Denoise preparation
c = chroma ? Median (c, yy=false, uu=true, vv=true) : c

# Temporal luma noise filter
fvec1 = bitspercomponent(c)>8 ? convertbits(c, 8) : undefined()
bvec1 = bitspercomponent(cy)>8 ? convertbits(cy, 8) : undefined()
super = MSuper (BicubicResize(chroma ? defined(fvec1) ? fvec1 : c : defined(bvec1) ? bvec1 : cy, c.Width/sc, c.Height/sc),
\ hpad=16/sc, vpad=16/sc, rfilter=4)
super2 = MSuper (chroma ? defined(fvec1) ? fvec1 : c : defined(bvec1) ? bvec1 : cy, hpad=16, vpad=16, levels=1)

# --> Analysis
bvec4 = MRecalculate(super2, MscaleVect (MAnalyse (super, isb = true, delta = 4, blksize=blksize, overlap=overlap), sc),
\ blksize=blksize, overlap=overlap, lambda=lambda, thSAD=180)
bvec3 = MRecalculate(super2, MscaleVect (MAnalyse (super, isb = true, delta = 3, blksize=blksize, overlap=overlap), sc),
\ blksize=blksize, overlap=overlap, lambda=lambda, thSAD=180)
bvec2 = MRecalculate(super2, MscaleVect (MAnalyse (super, isb = true, delta = 2, blksize=blksize, overlap=overlap,
\ badSAD=1100, lsad=1120), sc), searchparam=3, blksize=blksize, overlap=overlap, lambda=lambda, thSAD=180)
bvec1 = MRecalculate(super2, MscaleVect (MAnalyse (super, isb = true, delta = 1, blksize=blksize, overlap=overlap, badSAD=1500, badrange=27,
\ search=5, lsad=980), sc), blksize=blksize, overlap=overlap, search=5, searchparam=3, lambda=lambda, thSAD=180)
fvec1 = MRecalculate(super2, MscaleVect (MAnalyse (super, isb = false, delta = 1, blksize=blksize, overlap=overlap, badSAD=1500, badrange=27,
\ search=5, lsad=980), sc), blksize=blksize, overlap=overlap, search=5, searchparam=3, lambda=lambda, thSAD=180)
fvec2 = MRecalculate(super2, MscaleVect (MAnalyse (super, isb = false, delta = 2, blksize=blksize, overlap=overlap,
\ badSAD=1100, lsad=1120), sc), searchparam=3, blksize=blksize, overlap=overlap, lambda=lambda, thSAD=180)
fvec3 = MRecalculate(super2, MscaleVect (MAnalyse (super, isb = false, delta = 3, blksize=blksize, overlap=overlap), sc),
\ blksize=blksize, overlap=overlap, lambda=lambda, thSAD=180)
fvec4 = MRecalculate(super2, MscaleVect (MAnalyse (super, isb = false, delta = 4, blksize=blksize, overlap=overlap), sc),
\ blksize=blksize, overlap=overlap, lambda=lambda, thSAD=180)

# --> Bit depth conversion
c = chroma ? calcbits != BitsPerComponent(c) ? ConvertBits(c, calcbits) : c : c
super2 = calcbits != BitsPerComponent(super2) ? ConvertBits(super2, calcbits) : super2
cy = calcbits != BitsPerComponent(cy) ? ConvertBits(cy, calcbits) : cy

# --> Applying cleaning
clean = MDegrain4(chroma ? c : cy, super2, bvec1, fvec1, bvec2, fvec2, bvec3, fvec3, bvec4, fvec4, thSAD=thSAD)
u = chroma ? ExtractU(clean) : nop ()
v = chroma ? ExtractV(clean) : nop ()
filt_chroma = chroma ? CombinePlanes(c, mt_adddiff(u, clense(mt_makediff(ExtractU(c), u), reduceflicker=true)), mt_adddiff(v,
\ clense(mt_makediff(ExtractV(c), v), reduceflicker=true)), planes="yuv", source_planes="yyy", sample_clip=c) : c
clean = chroma ? ExtractY(clean) : clean

# Post clean, pre-process deband
filt_chroma_bits = BitsPerComponent(filt_chroma)
clean2 = deband==0 ? nop() : ConvertBits(clean, 8)
noise_diff = deband==0 ? nop() : BitsPerComponent(c)==8 ? nop() : mt_makediff(convertbits(clean2, calcbits), clean)
depth_calc = deband==0 ? nop() : CombinePlanes (clean2, filt_chroma_bits>8 ? ConvertBits(filt_chroma, 8) : filt_chroma, planes="YUV",
\ source_planes="YUV", pixel_type="YV12")
depth_calc = deband==0 ? nop() : deband>1 ? deband==4 ? depth_calc : AutoAdjust (depth_calc, auto_gain=true, bright_limit=1.09, dark_limit=1.11,
\ gamma_limit=1.045, auto_balance=true, chroma_limit=1.13, chroma_process=115, balance_str=0.85) : depth_calc
depth_calc = deband==0 ? undefined() : deband<>2 ? neo_f3kdb (depth_calc, preset=chroma?"high":"luma", range=16, grainY=38*(defH/540),
\ grainC=chroma?37*(defH/540):0) :depth_calc
clean = deband==0 ? clean : BitsPerComponent(c)==8 ? ExtractY (depth_calc) : mt_adddiff(ConvertBits(ExtractY
\ (depth_calc), calcbits), noise_diff)
depth_calc = deband==0 ? nop() : BitsPerComponent(depth_calc)<>filt_chroma_bits ? ConvertBits(depth_calc, filt_chroma_bits) : depth_calc
filt_chroma = deband==0 ? filt_chroma : deband>4 ? veed(depth_calc) : depth_calc

# Spatial luma denoising
clean2 = removegrain(clean, 18)

# Unsharp filter for spatial detail enhancement
clsharp = sharp>0 ? sharp>=51<=54 ? mt_makediff(clean, gblur(clean2, (sharp-50), sd=3)) :
\ mt_makediff(clean, blur(clean2, 1.58*(0.03+(0.97/50)*sharp))) : nop()
clsharp = mt_adddiff(clean2, repair(clense(clsharp), clsharp, 12))

# If selected, combining ReNoise
noise_diff = mt_makediff (clean2, cy)
clean2 = rn>0<=20 ? mt_merge(clean2, mergeluma (clean2, mt_adddiff(clean2, tweak(clense(noise_diff, reduceflicker=true), cont=1.008+(0.0032*(rn/20)))),
\ 0.3+(rn*0.035)), mt_lut (overlay(clean, invert(clean), mode="darken"), "x 32 scaleb < 0 x 45 scaleb > range_max 0 x 35 scaleb - range_max 32
\ scaleb 65 scaleb - / * - ? ?")) : clean2

# Combining spatial detail enhancement with spatial noise reduction using prepared mask
noise_diff = mt_invert(mt_binarize(noise_diff))
clean2 = sharp>0 ? mt_merge (clean2, clsharp, overlay(noise_diff, mt_edge(clean, "prewitt"), mode="lighten")) :
\ mt_merge (clean2, clean, overlay(noise_diff, mt_edge(clean, "prewitt"), mode="lighten"))

# Converting bits per channel and luma format
filt_chroma = outbits < BitsPerComponent(filt_chroma) ? ConvertBits(filt_chroma, outbits, dither=1) : ConvertBits(filt_chroma, outbits)
clean2 = outbits < BitsPerComponent(clean2) ? ConvertBits(clean2, outbits, dither=1) : ConvertBits(clean2, outbits)
c = BitsPerComponent(c) <> BitsPerComponent(clean2) ? ConvertBits(c, BitsPerComponent(clean2)) : c

# Combining result of luma and chroma cleaning
output = CombinePlanes(clean2, filt_chroma, planes="YUV", source_planes="YUV", sample_clip=c)
output = strength<20 ? Merge(c, output, 0.2+(0.04*strength)) : output
depth_calc = depth>0 ? defh>640 ? bicubicresize(output, 720, 480) : output : nop()
output = depth>0 ? mt_adddiff(output, spline36resize(mt_makediff(awarpsharp2(depth_calc, depth=depth2, blur=3),
\ awarpsharp2(depth_calc, depth=depth, blur=2)), output.width, output.height)) : output
output = padX+padY<>0 ? output.crop(0, 0, -padX, -padY) : output

return output
}

function Padding(clip c, int left, int top, int right, int bottom) {
# Didée: http://forum.doom9.org/showthread.php?p=1596804#post1596804
w = c.width()
h = c.height()
c.pointresize( w+left+right, h+top+bottom, -left, -top, w+left+right, h+top+bottom )
}

kedautinh12
14th May 2021, 07:50
Thanks

JKyle
14th May 2021, 11:43
Instead of using the original Padding function by Didée, I adopted sh_Padding (an MT version of Padding) in Zs_RF_Shared.avsi in order to improve the processing speed of the script when the source is not mod 16.

So you need to import Zs_RF_Shared.avsi along with other dependencies before calling this mClean script.

Here's the updated code:

# mClean spatio/temporal denoiser
# Version: 3.2 (01 March 2018)
# By burfadel

# Version: 3.2 mod J (2021-05-14)
# By JKyle
# Original idea by StainlessS
# https://forum.doom9.org/showthread.php?p=1942788#post1942788
# Replaced addborders with sh_Padding in Zs_RF_Shared.avsi
# Fixed the bug that the source should be processed based on mod 16 if defH>2800
# Replaced f3kdb with neo_f3kdb

# +++ Description +++
# Typical spatial filters work by removing large variations in the image on a small scale, reducing noise but also making the image less
# sharp or temporally stable. mClean removes noise whilst retaining as much detail as possible, as well as provide optional image enhancement

# mClean works primarily in the temporal domain, although there is some spatial limiting
# Chroma is processed a little differently to luma for optimal results
# Input must be 8-bit Planar type (YV12, YV16, YV24) or their equivalents in 10, 12, 14, or 16 bits
# Chroma processing can be disabled with chroma=false

# +++ Artifacts +++
# Spatial picture artifacts may remain as removing them is a fine balance between removing the unwanted artifact whilst not removing detail
# Additional dering/dehalo/deblock filters may be required, but should ONLY be uses if required due the detail loss/artifact removal balance

# +++ Sharpening +++
# Applies a modified unsharp mask to edges and major detected detail. Range of normal sharpening is 0-20, the default 10. There are 4 additional
# settings, 21-24 that provide 'overboost' sharpening. Overboost sharpening is only suitable typically for high definition, high quality sources.
# Actual sharpening calculation is scaled based on resolution.

# +++ ReNoise +++
# ReNoise adds back some of the removed luma noise. Re-adding original noise would be counterproductive, therefore ReNoise modifies this noise
# both spatially and temporally. The result of this modification is the noise becomes much nicer and it's impact on compressibility is greatly
# reduced. It is not applied on areas where the sharpening occurs as that would be counterproductive. Settings range from 1 to 20, default
# value is 14. The strength of renoise is affected by the the amount of original noise removed and how this noise varies between frames. It's
# main purpose is to reduce the 'flatness' that occurs with any form of effective denoising.

# +++ Deband +++
# This will perceptibly improve the quality of the image by reducing banding effect and adding a small amount of temporally stabilised grain to
# both luma and chroma. The settings are not adjustable as the default settings are suitable for most cases without having a large effect on
# compressibility. Auto balance uses Autoadjust, it calculates statistics of the clip, stabilises temporally and adjusts luminance gain & colour
# balance of the noise reduced clip.
# 0=disabled, 1=deband only, 2=auto balance only, 3=both deband and auto balance, 4=deband and veed, 5=all

# +++ Depth +++
# This applies a modified warp sharpening on the image that may be useful for certain things, and can improve the perception of image depth. Default
# is 0 (disabled), and ranges up to 5. This function will distort the image, for animation a setting of 1 or 2 can be beneficial to improve lines. The
# effect

# +++ Strength +++
# The strength of the denoising effect can be adjusted using this parameter. It ranges from 20 percent denoising effect with strength 1, up to the
# 100 percent of the denoising with strength 20 (default). This function works by blending a scaled percentage of the original image with the processed
# image.

# +++ Outbits +++
# Specifies the bits per component (bpc) for the output for processing by additional filters. It will also be the bpc that mClean will process.
# By default, mClean processes as 12 bits if the input is 8 bit, and converts back to 8 bit. If the input is 10 bits or higher no conversion is
# done unless outbits is specified and is different to the input bpc. If you output at a higher bpc keep in mind that there may be limitations
# to what subsequent filters and the encoder may support.

# +++ Required plugins +++
# Latest RGTools, MVTools2, Masktools2, neo_f3kdb, Modplus(renamed to manyPlus as it is merged with other plugins, 2021-04-21), AutoAdjust
# and Zs_RF_Shared.avsi
# Refer to https://forum.doom9.org/showpost.php?p=1834698&postcount=334
# For neo_f3kdb, visit https://github.com/HomeOfAviSynthPlusEvolution/neo_f3kdb
# For Modplus(manyPlus), visit http://www.avisynth.nl/users/vcmohan/manyPlus/manyPlus.html (info by kedautinh12)
# For Zs_RF_Shared.avsi, visit https://github.com/realfinder/AVS-Stuff/blob/Community/avs%202.5%20and%20up/Zs_RF_Shared.avsi

function mClean(clip c, int "thSAD", bool "chroma", int "sharp", int "rn", int "deband", int "depth", float "strength", int "outbits")
{

defH = Max (C.Height, C.Width/4*3) # Resolution calculation for auto blksize settings
thSAD = Default (thSAD, 400) # Denoising threshold
chroma = Default (chroma, true) # Process chroma
sharp = Default (sharp, 10) # Sharp multiplier
rn = Default (rn, 14) # Luma ReNoise strength from 0 (disabled) to 20
deband = Default (deband, 4) # Apply deband/veed and/or auto balance
depth = Default (depth, 0) # Depth enhancement
strength = Default (strength, 20) # Strength of denoising.
outbits = Default (outbits, BitsPerComponent(c)) # Output bits, default input depth
calcbits = BitsPerComponent(c) == 8 ? 12 : outbits

Assert(isYUV(c)==true, """mClean: Supports only YUV formats (YV12, YV16, YV24)""")
Assert(isYUY2(c)==false, """mClean: Supports only YUV formats (YV12, YV16, YV24)""")
Assert(isYV411(c)==false, """mClean: Supports only YUV formats (YV12, YV16, YV24)""")
Assert(sharp>=0 && sharp<=24, """mClean: "sharp" ranges from 0 to 24""")
Assert(rn>=0 && rn<=20, """mClean: "rn" ranges from 0 to 20""")
Assert(deband>=0 && deband<=5, """mClean: deband options 0 (disabled) to 5. Refer to description""")
Assert(depth>=0 && depth<=5, """mClean: depth ranges from 0 (disabled) to 5""")
Assert(strength>0 && depth<=20, """mClean: strength ranges from 1 (20%) to 20 (100%, default)""")
Assert(outbits>=8 && outbits<=16, """mClean: "outbits" ranges from 8 to 16""")

# padX, padY modified to be based on mod 16, not mod 8 as in the original script
# modified by JKyle
padX = c.width%16 == 0 ? 0 : (16 - c.width%16)
padY = c.height%16 == 0 ? 0 : (16 - c.height%16)
c = padX+padY<>0 ? c.sh_Padding(0, 0, padX, padY) : c
cy = ExtractY(c)
sc = defH>2800 ? 8 : defH>1400 ? 4 : defH>720 ? 2 : 1
blksize = sc==8 ? 8 : ((defH/sc)/360)>1.5 ? 16 : ((defH/sc)/360)>0.8 ? 12 : 8
overlap = blksize>=12 ? 6 : 2
lambda = 775*(blksize*blksize)/64
sharp = sharp>20 ? sharp+30 : DefH<=2600 ? 16+round(defH*(34/2600)*sharp/20) : 50
depth = depth*2
depth2 = -(depth+(depth/2))


# Denoise preparation
c = chroma ? Median (c, yy=false, uu=true, vv=true) : c

# Temporal luma noise filter
fvec1 = bitspercomponent(c)>8 ? convertbits(c, 8) : undefined()
bvec1 = bitspercomponent(cy)>8 ? convertbits(cy, 8) : undefined()
super = MSuper (BicubicResize(chroma ? defined(fvec1) ? fvec1 : c : defined(bvec1) ? bvec1 : cy, c.Width/sc, c.Height/sc),
\ hpad=16/sc, vpad=16/sc, rfilter=4)
super2 = MSuper (chroma ? defined(fvec1) ? fvec1 : c : defined(bvec1) ? bvec1 : cy, hpad=16, vpad=16, levels=1)

# --> Analysis
bvec4 = MRecalculate(super2, MscaleVect (MAnalyse (super, isb = true, delta = 4, blksize=blksize, overlap=overlap), sc),
\ blksize=blksize, overlap=overlap, lambda=lambda, thSAD=180)
bvec3 = MRecalculate(super2, MscaleVect (MAnalyse (super, isb = true, delta = 3, blksize=blksize, overlap=overlap), sc),
\ blksize=blksize, overlap=overlap, lambda=lambda, thSAD=180)
bvec2 = MRecalculate(super2, MscaleVect (MAnalyse (super, isb = true, delta = 2, blksize=blksize, overlap=overlap,
\ badSAD=1100, lsad=1120), sc), searchparam=3, blksize=blksize, overlap=overlap, lambda=lambda, thSAD=180)
bvec1 = MRecalculate(super2, MscaleVect (MAnalyse (super, isb = true, delta = 1, blksize=blksize, overlap=overlap, badSAD=1500, badrange=27,
\ search=5, lsad=980), sc), blksize=blksize, overlap=overlap, search=5, searchparam=3, lambda=lambda, thSAD=180)
fvec1 = MRecalculate(super2, MscaleVect (MAnalyse (super, isb = false, delta = 1, blksize=blksize, overlap=overlap, badSAD=1500, badrange=27,
\ search=5, lsad=980), sc), blksize=blksize, overlap=overlap, search=5, searchparam=3, lambda=lambda, thSAD=180)
fvec2 = MRecalculate(super2, MscaleVect (MAnalyse (super, isb = false, delta = 2, blksize=blksize, overlap=overlap,
\ badSAD=1100, lsad=1120), sc), searchparam=3, blksize=blksize, overlap=overlap, lambda=lambda, thSAD=180)
fvec3 = MRecalculate(super2, MscaleVect (MAnalyse (super, isb = false, delta = 3, blksize=blksize, overlap=overlap), sc),
\ blksize=blksize, overlap=overlap, lambda=lambda, thSAD=180)
fvec4 = MRecalculate(super2, MscaleVect (MAnalyse (super, isb = false, delta = 4, blksize=blksize, overlap=overlap), sc),
\ blksize=blksize, overlap=overlap, lambda=lambda, thSAD=180)

# --> Bit depth conversion
c = chroma ? calcbits != BitsPerComponent(c) ? ConvertBits(c, calcbits) : c : c
super2 = calcbits != BitsPerComponent(super2) ? ConvertBits(super2, calcbits) : super2
cy = calcbits != BitsPerComponent(cy) ? ConvertBits(cy, calcbits) : cy

# --> Applying cleaning
clean = MDegrain4(chroma ? c : cy, super2, bvec1, fvec1, bvec2, fvec2, bvec3, fvec3, bvec4, fvec4, thSAD=thSAD)
u = chroma ? ExtractU(clean) : nop ()
v = chroma ? ExtractV(clean) : nop ()
filt_chroma = chroma ? CombinePlanes(c, mt_adddiff(u, clense(mt_makediff(ExtractU(c), u), reduceflicker=true)), mt_adddiff(v,
\ clense(mt_makediff(ExtractV(c), v), reduceflicker=true)), planes="yuv", source_planes="yyy", sample_clip=c) : c
clean = chroma ? ExtractY(clean) : clean

# Post clean, pre-process deband
filt_chroma_bits = BitsPerComponent(filt_chroma)
clean2 = deband==0 ? nop() : ConvertBits(clean, 8)
noise_diff = deband==0 ? nop() : BitsPerComponent(c)==8 ? nop() : mt_makediff(convertbits(clean2, calcbits), clean)
depth_calc = deband==0 ? nop() : CombinePlanes (clean2, filt_chroma_bits>8 ? ConvertBits(filt_chroma, 8) : filt_chroma, planes="YUV",
\ source_planes="YUV", pixel_type="YV12")
depth_calc = deband==0 ? nop() : deband>1 ? deband==4 ? depth_calc : AutoAdjust (depth_calc, auto_gain=true, bright_limit=1.09, dark_limit=1.11,
\ gamma_limit=1.045, auto_balance=true, chroma_limit=1.13, chroma_process=115, balance_str=0.85) : depth_calc
depth_calc = deband==0 ? undefined() : deband<>2 ? neo_f3kdb (depth_calc, preset=chroma?"high":"luma", range=16, grainY=38*(defH/540),
\ grainC=chroma?37*(defH/540):0) :depth_calc
clean = deband==0 ? clean : BitsPerComponent(c)==8 ? ExtractY (depth_calc) : mt_adddiff(ConvertBits(ExtractY
\ (depth_calc), calcbits), noise_diff)
depth_calc = deband==0 ? nop() : BitsPerComponent(depth_calc)<>filt_chroma_bits ? ConvertBits(depth_calc, filt_chroma_bits) : depth_calc
filt_chroma = deband==0 ? filt_chroma : deband>4 ? veed(depth_calc) : depth_calc

# Spatial luma denoising
clean2 = removegrain(clean, 18)

# Unsharp filter for spatial detail enhancement
clsharp = sharp>0 ? sharp>=51<=54 ? mt_makediff(clean, gblur(clean2, (sharp-50), sd=3)) :
\ mt_makediff(clean, blur(clean2, 1.58*(0.03+(0.97/50)*sharp))) : nop()
clsharp = mt_adddiff(clean2, repair(clense(clsharp), clsharp, 12))

# If selected, combining ReNoise
noise_diff = mt_makediff (clean2, cy)
clean2 = rn>0<=20 ? mt_merge(clean2, mergeluma (clean2, mt_adddiff(clean2, tweak(clense(noise_diff, reduceflicker=true), cont=1.008+(0.0032*(rn/20)))),
\ 0.3+(rn*0.035)), mt_lut (overlay(clean, invert(clean), mode="darken"), "x 32 scaleb < 0 x 45 scaleb > range_max 0 x 35 scaleb - range_max 32
\ scaleb 65 scaleb - / * - ? ?")) : clean2

# Combining spatial detail enhancement with spatial noise reduction using prepared mask
noise_diff = mt_invert(mt_binarize(noise_diff))
clean2 = sharp>0 ? mt_merge (clean2, clsharp, overlay(noise_diff, mt_edge(clean, "prewitt"), mode="lighten")) :
\ mt_merge (clean2, clean, overlay(noise_diff, mt_edge(clean, "prewitt"), mode="lighten"))

# Converting bits per channel and luma format
filt_chroma = outbits < BitsPerComponent(filt_chroma) ? ConvertBits(filt_chroma, outbits, dither=1) : ConvertBits(filt_chroma, outbits)
clean2 = outbits < BitsPerComponent(clean2) ? ConvertBits(clean2, outbits, dither=1) : ConvertBits(clean2, outbits)
c = BitsPerComponent(c) <> BitsPerComponent(clean2) ? ConvertBits(c, BitsPerComponent(clean2)) : c

# Combining result of luma and chroma cleaning
output = CombinePlanes(clean2, filt_chroma, planes="YUV", source_planes="YUV", sample_clip=c)
output = strength<20 ? Merge(c, output, 0.2+(0.04*strength)) : output
depth_calc = depth>0 ? defh>640 ? bicubicresize(output, 720, 480) : output : nop()
output = depth>0 ? mt_adddiff(output, spline36resize(mt_makediff(awarpsharp2(depth_calc, depth=depth2, blur=3),
\ awarpsharp2(depth_calc, depth=depth, blur=2)), output.width, output.height)) : output
output = padX+padY<>0 ? output.crop(0, 0, -padX, -padY) : output

return output
}

kedautinh12
14th May 2021, 14:58
Thanks for your hardwork

JKyle
15th May 2021, 01:10
I fixed another error that the script does not work when the source is YUV422 or YUV444.

I hope this will be the final version.

3.2 Jmod 4

/*
# mClean spatio/temporal denoiser
# Version: 3.2 (01 March 2018)
# By burfadel

# Version: 3.2 Jmod 4 (2021-05-14)
# By JKyle
# Original idea by StainlessS (https://forum.doom9.org/showthread.php?p=1942788#post1942788)

# Fixed error when the source is 4K and cropped
# Replaced `f3kdb` with `neo_f3kdb`
# Replaced `addborders` with `sh_Padding` in `Zs_RF_Shared.avsi`
# Fixed error when the source is YUV422 or YUV444

# +++ Description +++
# Typical spatial filters work by removing large variations in the image on a small scale, reducing noise but also making the image less
# sharp or temporally stable. mClean removes noise whilst retaining as much detail as possible, as well as provide optional image enhancement

# mClean works primarily in the temporal domain, although there is some spatial limiting
# Chroma is processed a little differently to luma for optimal results
# Input must be 8-bit Planar type (YV12, YV16, YV24) or their equivalents in 10, 12, 14, or 16 bits
# Chroma processing can be disabled with chroma=false

# +++ Artifacts +++
# Spatial picture artifacts may remain as removing them is a fine balance between removing the unwanted artifact whilst not removing detail
# Additional dering/dehalo/deblock filters may be required, but should ONLY be uses if required due the detail loss/artifact removal balance

# +++ Sharpening +++
# Applies a modified unsharp mask to edges and major detected detail. Range of normal sharpening is 0-20, the default 10. There are 4 additional
# settings, 21-24 that provide 'overboost' sharpening. Overboost sharpening is only suitable typically for high definition, high quality sources.
# Actual sharpening calculation is scaled based on resolution.

# +++ ReNoise +++
# ReNoise adds back some of the removed luma noise. Re-adding original noise would be counterproductive, therefore ReNoise modifies this noise
# both spatially and temporally. The result of this modification is the noise becomes much nicer and it's impact on compressibility is greatly
# reduced. It is not applied on areas where the sharpening occurs as that would be counterproductive. Settings range from 1 to 20, default
# value is 14. The strength of renoise is affected by the the amount of original noise removed and how this noise varies between frames. It's
# main purpose is to reduce the 'flatness' that occurs with any form of effective denoising.

# +++ Deband +++
# This will perceptibly improve the quality of the image by reducing banding effect and adding a small amount of temporally stabilised grain to
# both luma and chroma. The settings are not adjustable as the default settings are suitable for most cases without having a large effect on
# compressibility. Auto balance uses Autoadjust, it calculates statistics of the clip, stabilises temporally and adjusts luminance gain & colour
# balance of the noise reduced clip.
# 0=disabled, 1=deband only, 2=auto balance only, 3=both deband and auto balance, 4=deband and veed, 5=all

# +++ Depth +++
# This applies a modified warp sharpening on the image that may be useful for certain things, and can improve the perception of image depth. Default
# is 0 (disabled), and ranges up to 5. This function will distort the image, for animation a setting of 1 or 2 can be beneficial to improve lines. The
# effect

# +++ Strength +++
# The strength of the denoising effect can be adjusted using this parameter. It ranges from 20 percent denoising effect with strength 1, up to the
# 100 percent of the denoising with strength 20 (default). This function works by blending a scaled percentage of the original image with the processed
# image.

# +++ Outbits +++
# Specifies the bits per component (bpc) for the output for processing by additional filters. It will also be the bpc that mClean will process.
# By default, mClean processes as 12 bits if the input is 8 bit, and converts back to 8 bit. If the input is 10 bits or higher no conversion is
# done unless outbits is specified and is different to the input bpc. If you output at a higher bpc keep in mind that there may be limitations
# to what subsequent filters and the encoder may support.

# +++ Required plugins +++
# Latest RGTools, MVTools2, Masktools2, neo_f3kdb, Modplus(renamed to manyPlus as it is merged with other plugins, 2021-04-21), AutoAdjust
# and Zs_RF_Shared.avsi
# Refer to https://forum.doom9.org/showpost.php?p=1834698&postcount=334
# For neo_f3kdb, visit https://github.com/HomeOfAviSynthPlusEvolution/neo_f3kdb
# For Modplus(manyPlus), visit http://www.avisynth.nl/users/vcmohan/manyPlus/manyPlus.html (info by kedautinh12)
# For Zs_RF_Shared.avsi, visit https://github.com/realfinder/AVS-Stuff/blob/Community/avs%202.5%20and%20up/Zs_RF_Shared.avsi
*/

function mClean(clip c, int "thSAD", bool "chroma", int "sharp", int "rn", int "deband", int "depth", float "strength", int "outbits")
{

defH = Max (C.Height, C.Width/4*3) # Resolution calculation for auto blksize settings
thSAD = Default (thSAD, 400) # Denoising threshold
chroma = Default (chroma, true) # Process chroma
sharp = Default (sharp, 10) # Sharp multiplier
rn = Default (rn, 14) # Luma ReNoise strength from 0 (disabled) to 20
deband = Default (deband, 4) # Apply deband/veed and/or auto balance
depth = Default (depth, 0) # Depth enhancement
strength = Default (strength, 20) # Strength of denoising.
outbits = Default (outbits, BitsPerComponent(c)) # Output bits, default input depth
calcbits = BitsPerComponent(c) == 8 ? 12 : outbits

Assert(isYUV(c)==true, """mClean: Supports only YUV formats (YV12, YV16, YV24)""")
Assert(isYUY2(c)==false, """mClean: Supports only YUV formats (YV12, YV16, YV24)""")
Assert(isYV411(c)==false, """mClean: Supports only YUV formats (YV12, YV16, YV24)""")
Assert(sharp>=0 && sharp<=24, """mClean: "sharp" ranges from 0 to 24""")
Assert(rn>=0 && rn<=20, """mClean: "rn" ranges from 0 to 20""")
Assert(deband>=0 && deband<=5, """mClean: deband options 0 (disabled) to 5. Refer to description""")
Assert(depth>=0 && depth<=5, """mClean: depth ranges from 0 (disabled) to 5""")
Assert(strength>0 && depth<=20, """mClean: strength ranges from 1 (20%) to 20 (100%, default)""")
Assert(outbits>=8 && outbits<=16, """mClean: "outbits" ranges from 8 to 16""")

# `padX`, `padY` modified to be based on mod 16, not mod 8 as in the original script (JKyle)
padX = c.width%16 == 0 ? 0 : (16 - c.width%16)
padY = c.height%16 == 0 ? 0 : (16 - c.height%16)
# Replaced `addborders` with `sh_Padding` in `Zs_RF_Shared.avsi` (JKyle)
c = padX+padY<>0 ? c.sh_Padding(0, 0, padX, padY) : c
cy = ExtractY(c)
sc = defH>2800 ? 8 : defH>1400 ? 4 : defH>720 ? 2 : 1
blksize = sc==8 ? 8 : ((defH/sc)/360)>1.5 ? 16 : ((defH/sc)/360)>0.8 ? 12 : 8
overlap = blksize>=12 ? 6 : 2
lambda = 775*(blksize*blksize)/64
sharp = sharp>20 ? sharp+30 : DefH<=2600 ? 16+round(defH*(34/2600)*sharp/20) : 50
depth = depth*2
depth2 = -(depth+(depth/2))


# Denoise preparation
c = chroma ? Median (c, yy=false, uu=true, vv=true) : c

# Temporal luma noise filter
fvec1 = bitspercomponent(c)>8 ? convertbits(c, 8) : undefined()
bvec1 = bitspercomponent(cy)>8 ? convertbits(cy, 8) : undefined()
super = MSuper (BicubicResize(chroma ? defined(fvec1) ? fvec1 : c : defined(bvec1) ? bvec1 : cy, c.Width/sc, c.Height/sc),
\ hpad=16/sc, vpad=16/sc, rfilter=4)
super2 = MSuper (chroma ? defined(fvec1) ? fvec1 : c : defined(bvec1) ? bvec1 : cy, hpad=16, vpad=16, levels=1)

# --> Analysis
bvec4 = MRecalculate(super2, MscaleVect (MAnalyse (super, isb = true, delta = 4, blksize=blksize, overlap=overlap), sc),
\ blksize=blksize, overlap=overlap, lambda=lambda, thSAD=180)
bvec3 = MRecalculate(super2, MscaleVect (MAnalyse (super, isb = true, delta = 3, blksize=blksize, overlap=overlap), sc),
\ blksize=blksize, overlap=overlap, lambda=lambda, thSAD=180)
bvec2 = MRecalculate(super2, MscaleVect (MAnalyse (super, isb = true, delta = 2, blksize=blksize, overlap=overlap,
\ badSAD=1100, lsad=1120), sc), searchparam=3, blksize=blksize, overlap=overlap, lambda=lambda, thSAD=180)
bvec1 = MRecalculate(super2, MscaleVect (MAnalyse (super, isb = true, delta = 1, blksize=blksize, overlap=overlap, badSAD=1500, badrange=27,
\ search=5, lsad=980), sc), blksize=blksize, overlap=overlap, search=5, searchparam=3, lambda=lambda, thSAD=180)
fvec1 = MRecalculate(super2, MscaleVect (MAnalyse (super, isb = false, delta = 1, blksize=blksize, overlap=overlap, badSAD=1500, badrange=27,
\ search=5, lsad=980), sc), blksize=blksize, overlap=overlap, search=5, searchparam=3, lambda=lambda, thSAD=180)
fvec2 = MRecalculate(super2, MscaleVect (MAnalyse (super, isb = false, delta = 2, blksize=blksize, overlap=overlap,
\ badSAD=1100, lsad=1120), sc), searchparam=3, blksize=blksize, overlap=overlap, lambda=lambda, thSAD=180)
fvec3 = MRecalculate(super2, MscaleVect (MAnalyse (super, isb = false, delta = 3, blksize=blksize, overlap=overlap), sc),
\ blksize=blksize, overlap=overlap, lambda=lambda, thSAD=180)
fvec4 = MRecalculate(super2, MscaleVect (MAnalyse (super, isb = false, delta = 4, blksize=blksize, overlap=overlap), sc),
\ blksize=blksize, overlap=overlap, lambda=lambda, thSAD=180)

# --> Bit depth conversion
c = chroma ? calcbits != BitsPerComponent(c) ? ConvertBits(c, calcbits) : c : c
super2 = calcbits != BitsPerComponent(super2) ? ConvertBits(super2, calcbits) : super2
cy = calcbits != BitsPerComponent(cy) ? ConvertBits(cy, calcbits) : cy

# --> Applying cleaning
clean = MDegrain4(chroma ? c : cy, super2, bvec1, fvec1, bvec2, fvec2, bvec3, fvec3, bvec4, fvec4, thSAD=thSAD)
u = chroma ? ExtractU(clean) : nop ()
v = chroma ? ExtractV(clean) : nop ()
filt_chroma = chroma ? CombinePlanes(c, mt_adddiff(u, clense(mt_makediff(ExtractU(c), u), reduceflicker=true)), mt_adddiff(v,
\ clense(mt_makediff(ExtractV(c), v), reduceflicker=true)), planes="yuv", source_planes="yyy", sample_clip=c) : c
clean = chroma ? ExtractY(clean) : clean

# Post clean, pre-process deband
filt_chroma_bits = BitsPerComponent(filt_chroma)
clean2 = deband==0 ? nop() : ConvertBits(clean, 8)
noise_diff = deband==0 ? nop() : BitsPerComponent(c)==8 ? nop() : mt_makediff(convertbits(clean2, calcbits), clean)
# Different color formats are returned for `depth_calc` depending on the source chroma subsampling scheme (JKyle)
depth_calc = deband==0 ? nop() : CombinePlanes (clean2, filt_chroma_bits>8 ? ConvertBits(filt_chroma, 8) : filt_chroma, planes="YUV",
\ source_planes="YUV", pixel_type = Is420(c) ? "YV12" : Is422(c) ? "YV16" : "YV24")
depth_calc = deband==0 ? nop() : deband>1 ? deband==4 ? depth_calc : AutoAdjust (depth_calc, auto_gain=true, bright_limit=1.09, dark_limit=1.11,
\ gamma_limit=1.045, auto_balance=true, chroma_limit=1.13, chroma_process=115, balance_str=0.85) : depth_calc
# Replaced `f3kdb` with `neo_f3kdb` (JKyle)
depth_calc = deband==0 ? undefined() : deband<>2 ? neo_f3kdb (depth_calc, preset=chroma?"high":"luma", range=16, grainY=38*(defH/540),
\ grainC=chroma?37*(defH/540):0) :depth_calc
clean = deband==0 ? clean : BitsPerComponent(c)==8 ? ExtractY (depth_calc) : mt_adddiff(ConvertBits(ExtractY
\ (depth_calc), calcbits), noise_diff)
depth_calc = deband==0 ? nop() : BitsPerComponent(depth_calc)<>filt_chroma_bits ? ConvertBits(depth_calc, filt_chroma_bits) : depth_calc
filt_chroma = deband==0 ? filt_chroma : deband>4 ? veed(depth_calc) : depth_calc

# Spatial luma denoising
clean2 = removegrain(clean, 18)

# Unsharp filter for spatial detail enhancement
clsharp = sharp>0 ? sharp>=51<=54 ? mt_makediff(clean, gblur(clean2, (sharp-50), sd=3)) :
\ mt_makediff(clean, blur(clean2, 1.58*(0.03+(0.97/50)*sharp))) : nop()
clsharp = mt_adddiff(clean2, repair(clense(clsharp), clsharp, 12))

# If selected, combining ReNoise
noise_diff = mt_makediff (clean2, cy)
clean2 = rn>0<=20 ? mt_merge(clean2, mergeluma (clean2, mt_adddiff(clean2, tweak(clense(noise_diff, reduceflicker=true), cont=1.008+(0.0032*(rn/20)))),
\ 0.3+(rn*0.035)), mt_lut (overlay(clean, invert(clean), mode="darken"), "x 32 scaleb < 0 x 45 scaleb > range_max 0 x 35 scaleb - range_max 32
\ scaleb 65 scaleb - / * - ? ?")) : clean2

# Combining spatial detail enhancement with spatial noise reduction using prepared mask
noise_diff = mt_invert(mt_binarize(noise_diff))
clean2 = sharp>0 ? mt_merge (clean2, clsharp, overlay(noise_diff, mt_edge(clean, "prewitt"), mode="lighten")) :
\ mt_merge (clean2, clean, overlay(noise_diff, mt_edge(clean, "prewitt"), mode="lighten"))

# Converting bits per channel and luma format
filt_chroma = outbits < BitsPerComponent(filt_chroma) ? ConvertBits(filt_chroma, outbits, dither=1) : ConvertBits(filt_chroma, outbits)
clean2 = outbits < BitsPerComponent(clean2) ? ConvertBits(clean2, outbits, dither=1) : ConvertBits(clean2, outbits)
c = BitsPerComponent(c) <> BitsPerComponent(clean2) ? ConvertBits(c, BitsPerComponent(clean2)) : c

# Combining result of luma and chroma cleaning
output = CombinePlanes(clean2, filt_chroma, planes="YUV", source_planes="YUV", sample_clip=c)
output = strength<20 ? Merge(c, output, 0.2+(0.04*strength)) : output
depth_calc = depth>0 ? defh>640 ? bicubicresize(output, 720, 480) : output : nop()
output = depth>0 ? mt_adddiff(output, spline36resize(mt_makediff(awarpsharp2(depth_calc, depth=depth2, blur=3),
\ awarpsharp2(depth_calc, depth=depth, blur=2)), output.width, output.height)) : output
output = padX+padY<>0 ? output.crop(0, 0, -padX, -padY) : output

return output
}

MysteryX
18th September 2021, 17:25
I'm trying mClean on GoPro 5K footage.


clip = G41Fun.mClean(clip, thSAD=400, chroma=True, sharp=21, rn=14, deband=1, depth=0, strength=20)


In certain cases it's working well, especially on low-light footage with considerable noise.

When only certain parts of the video have noise, however, waterfall gets blurred, skin and beard gets blurred, and moving background gets blurred.

KNLMeansCL doesn't blurry those parts as much; but it does make the overall image softer (plus I've set GoPro's sharpening to Low to reduce sharpening of noise). I do like mClean's overboost sharpening (21).

How can I better process my GoPro footages? Since it's struggling with low-light situations, perhaps have dynamic "str" based on Luma? Any other ideas?

Here's a sample 20-frame 5K video clip. (https://mega.nz/file/6BYnVCrS#RI5ZLsBjciatWPsvvZJSRgiS2iTzMGPhKBOENVaM-fg) It has noise at the top and on the seat, moving background, and skin/beard texture to keep. Overall image could be denoised and then sharpened a bit. If you can get something to do a good job here, then it will work much better on all sources overall.

Dogway
18th September 2021, 21:57
You can try retinex, it's designed for low light. I will give the clip a whirl. That dynamic word gave an idea to run MDegrain in runtime with variable strength.


EDIT: sorry I can't really test, mClean hangs with this clip, also I saw it's not very dark?

MysteryX
18th September 2021, 22:48
This one isn't very dark, just has light noise in some grey areas, making it more confusing to process.

Runnign MDegrain with variable per-frame strength wouldn't work on hybrid frames having noise only in some parts.

What source filter are you using? You can try a different source filter. Indeed FFVideoSource freezes, LWLibavVideoSource works.

I'm looking at a greyscale version of it to see what it would look like if overall strength was applied dynamically based on Luma. Some area at the top has noise with a light colour, but not as much as the darker parts on the left. Beard is black, but not continuous black. Discarding dark areas too small to contain noise wouldn't be difficult with SpotLess or ContinuousMask. Still -- there's a difference between areas that are dark because of low light and dark because the objects are dark.

The way Strength works

The strength of the denoising effect can be adjusted using this parameter. It ranges from 20 percent denoising effect with strength 1, up to the 100 percent of the denoising with strength 20 (default). This function works by blending a scaled percentage of the original image with the processed image.


Dynamic Strength mask would solve waterfall problem because it's bright.

As for the moving background, what would be a good way to handle it? Some highly-dynamic areas should have only spatial denoising and not temporal. In this case the moving objects are bright with small dark spots that could be discarded, but the moving background could very well be darker if filming in the evening with lights on.

OR. If mClean just isn't suitable, can run KNLMeansCL plus a sharpener. What's the recommended sharpener to use here?

Dogway
18th September 2021, 23:42
Switched from ffvideo to DGSource, but same story. I tried to optimize mClean removing some slow filters like Median(), but then saw veed, autoadjust and said, well just stop here you work enough already lol.

SMDegrain works for me but can't compare to mClean on this clip, wanted to test the function's sharpening. About blur on face details it's hard to deal with, lower tr, thSAD, then of course sharpening (temporal limitings worked for me better on the beard), luma range expansion works, local contrast too (retinex does this to some extent). Maybe try different DCTs or motion compensated denoising.
I have been looking into LTSMC where Didèe repair()ed MComped framed then added some extra sharpening. Hope to port that soon.

In any case I personally would just filter chroma for this source.

Here is an attempt:
pre=ex_smartblur(radius=6,thres=4,UV=3)
smdegrain(tr=6,thSAD=300,thSADC=300,,limit=1,limitC=255,prefilter=pre,limitS=false,contrasharp=true,refinemotion=true)

If you want to totally exclude the face I would mask out the face by its color, this is a common procedure in grading.

MysteryX
19th September 2021, 00:09
I tried a few options

Source
https://i.postimg.cc/47NgLy7G/Source.jpg (https://postimg.cc/47NgLy7G)

KNLMeansCL(d=2, a=2, h=4)
FineSharp(mode=2, sstr=2.0, cstr=1.3, xstr=0.0, lstr=1.49, pstr=1.472)
https://i.postimg.cc/YvYMjJK5/KNLMeans-Sharp.jpg (https://postimg.cc/YvYMjJK5)

mClean(clip, thSAD=400, chroma=True, sharp=21, rn=14, deband=0, depth=0, strength=20)
https://i.postimg.cc/PLHhD2dP/mClean.jpg (https://postimg.cc/PLHhD2dP)

mClean(clip, thSAD=400, chroma=True, sharp=21, rn=14, deband=0, depth=0, strength=10)
https://i.postimg.cc/WD7Vwg4S/m-Clean-Str10.jpg (https://postimg.cc/WD7Vwg4S)

mClean gives far superior results as KNLMeansCL and also applies much stronger denoising. Cutting it down with Strength=10 is what gives the best results, making the face "acceptable" while keeping the noisy areas good enough. It feels like compromising though, as the "great" areas aren't great anymore.

btw mClean works WAY better in VapourSynth. I'm using StaxRip to run it through VapourSynth.

Dogway
19th September 2021, 00:32
This is my attempt:
http://i.imgur.com/U4aXZSRm.jpg (https://i.imgur.com/U4aXZSR.jpg)

Upload a 5K snapshot that is not the first frame, so I can compare results better.

Tomorrow I will try to make a face protection approach.

MysteryX
19th September 2021, 01:17
It's not just about the face, there are lots of situations where this kind of problems happen. Waterfalls among other things fail epic.

ChaosKing
19th September 2021, 09:24
I remember achieving much better results with a reference clip (rclip parameter) with KNLMeansCL. Basically make a stronger filtered clip with KNLMeansCL and feed it as rclip to KNLMeansCL. A larger d value can help too.

MysteryX
20th September 2021, 06:24
I remember achieving much better results with a reference clip (rclip parameter) with KNLMeansCL. Basically make a stronger filtered clip with KNLMeansCL and feed it as rclip to KNLMeansCL. A larger d value can help too.
You're saying to call KNLMeansCL twice, first time stronger to pass to 2nd one?

Actually, applying dynamic strength based on Luma wouldn't be difficult; replace the Merge line applying strength with a MaskedMerge, pass it straight Luma channel, or slightly alter it to my wish. Worth a try. It's one line of code that has nothing to do with the complexity of the script.

ChaosKing
20th September 2021, 07:51
You're saying to call KNLMeansCL twice, first time stronger to pass to 2nd one?

Basically yes. But you're not limited to knlmeans, you can use whatever filter you like.
https://github.com/Khanattila/KNLMeansCL/wiki/Filter-description#advanced

clip rclip [default: not set]
Extra reference clip option to do weighting calculation.

MysteryX
21st September 2021, 00:58
Using KNLMeans with stronger rclip made the beard even blurrier.

I played around with mClean and came out with good results.

Here's the modified VapourSynth version; since mClean runs *way* better on VapourSynth; and yes 5K did freeze Avisynth.

Set strength to a value between 0 and -255 to activate dynamic noise reduction strength based on Luma. I'm calling Maximum() 3x to expand the exclusion areas. Anyway, edges don't need to be denoised as much. Strength=-50 is giving me good results, which means that out of 255, the 50 blackest values have full-reduction and the 50 whitest values are merged at a minimum value of 50/255.

Also note that strength alters the sharpening. With dynamic noise reduction strength, this is not desirable, especially if I'm excluding more of the edges from processing. For that reason, I moved strength processing further up. Even normal strength, I would recommend moving it up there as well to apply only towards noise reduction and not towards sharpening which can be adjusted separately.


def mClean(clip, thSAD=400, chroma=True, sharp=10, rn=14, deband=0, depth=0, strength=20, outbits=None, icalc=True, rgmode=18):
"""
From: https://forum.doom9.org/showthread.php?t=174804 by burfadel
mClean spatio/temporal denoiser

+++ Description +++
Typical spatial filters work by removing large variations in the image on a small scale, reducing noise but also making the image less
sharp or temporally stable. mClean removes noise whilst retaining as much detail as possible, as well as provide optional image enhancement.

mClean works primarily in the temporal domain, although there is some spatial limiting.
Chroma is processed a little differently to luma for optimal results.
Chroma processing can be disabled with chroma = False.

+++ Artifacts +++
Spatial picture artifacts may remain as removing them is a fine balance between removing the unwanted artifact whilst not removing detail.
Additional dering/dehalo/deblock filters may be required, but should ONLY be uses if required due the detail loss/artifact removal balance.

+++ Sharpening +++
Applies a modified unsharp mask to edges and major detected detail. Range of normal sharpening is 0-20. There are 4 additional settings,
21-24 that provide 'overboost' sharpening. Overboost sharpening is only suitable typically for high definition, high quality sources.
Actual sharpening calculation is scaled based on resolution.

+++ ReNoise +++
ReNoise adds back some of the removed luma noise. Re-adding original noise would be counterproductive, therefore ReNoise modifies this noise
both spatially and temporally. The result of this modification is the noise becomes much nicer and it's impact on compressibility is greatly
reduced. It is not applied on areas where the sharpening occurs as that would be counterproductive. Settings range from 0 to 20.
The strength of renoise is affected by the the amount of original noise removed and how this noise varies between frames.
It's main purpose is to reduce the 'flatness' that occurs with any form of effective denoising.

+++ Deband +++
This will perceptibly improve the quality of the image by reducing banding effect and adding a small amount of temporally stabilised grain
to both luma and chroma. The settings are not adjustable as the default settings are suitable for most cases without having a large effect
on compressibility. 0 = disabled, 1 = deband only, 2 = deband and veed

+++ Depth +++
This applies a modified warp sharpening on the image that may be useful for certain things, and can improve the perception of image depth.
Settings range up from 0 to 5. This function will distort the image, for animation a setting of 1 or 2 can be beneficial to improve lines.

+++ Strength +++
The strength of the denoising effect can be adjusted using this parameter. It ranges from 20 percent denoising effect with strength 0, up to the
100 percent of the denoising with strength 20. This function works by blending a scaled percentage of the original image with the processed image.
A value between 0 and -255 will apply dynamic noise reduction strength based on Luma, where black zones get full denoising and white areas
preserve the source. Specifying a value of -50 means that out of 255, the 50 blackest values have full-reduction and the 50 whitest values
are merged at a minimum value of 50/255.

+++ Outbits +++
Specifies the bits per component (bpc) for the output for processing by additional filters. It will also be the bpc that mClean will process.
If you output at a higher bpc keep in mind that there may be limitations to what subsequent filters and the encoder may support.
"""
# New parameter icalc, set to True to enable pure integer processing for faster speed. (Ignored if input is of float sample type)

defH = max(clip.height, clip.width // 4 * 3) # Resolution calculation for auto blksize settings
sharp = min(max(sharp, 0), 24) # Sharp multiplier
rn = min(max(rn, 0), 20) # Luma ReNoise strength
deband = min(max(deband, 0), 5) # Apply deband/veed
depth = min(max(depth, 0), 5) # Depth enhancement
strength = min(max(strength, -255), 20) # Strength of denoising
bd = clip.format.bits_per_sample
isFLOAT = clip.format.sample_type == vs.FLOAT
icalc = False if isFLOAT else icalc
S = core.mv.Super if icalc else core.mvsf.Super
A = core.mv.Analyse if icalc else core.mvsf.Analyse
R = core.mv.Recalculate if icalc else core.mvsf.Recalculate

if not isinstance(clip, vs.VideoNode) or clip.format.color_family != vs.YUV:
raise TypeError("mClean: This is not a YUV clip!")

if outbits is None: # Output bits, default input depth
outbits = bd

if deband or depth:
outbits = min(outbits, 16)

RE = core.rgsf.Repair if outbits == 32 else core.rgvs.Repair
RG = core.rgsf.RemoveGrain if outbits == 32 else core.rgvs.RemoveGrain
sc = 8 if defH > 2880 else 4 if defH > 1440 else 2 if defH > 720 else 1
i = 0.00392 if outbits == 32 else 1 << (outbits - 8)
peak = 1.0 if outbits == 32 else (1 << outbits) - 1
bs = 16 if defH / sc > 360 else 8
ov = 6 if bs > 12 else 2
pel = 1 if defH > 720 else 2
truemotion = False if defH > 720 else True
lampa = 777 * (bs ** 2) // 64
depth2 = -depth*3
depth = depth*2

if sharp > 20:
sharp += 30
elif defH <= 2500:
sharp = 15 + defH * sharp * 0.0007
else:
sharp = 50

# Denoise preparation
c = core.vcmod.Median(clip, plane=[0, 1, 1]) if chroma else clip

# Temporal luma noise filter
if not (isFLOAT or icalc):
c = c.fmtc.bitdepth(flt=1)
cy = core.std.ShufflePlanes(c, [0], vs.GRAY)

super1 = S(c if chroma else cy, hpad=bs, vpad=bs, pel=pel, rfilter=4, sharp=1)
super2 = S(c if chroma else cy, hpad=bs, vpad=bs, pel=pel, rfilter=1, levels=1)
analyse_args = dict(blksize=bs, overlap=ov, search=5, truemotion=truemotion)
recalculate_args = dict(blksize=bs, overlap=ov, search=5, truemotion=truemotion, thsad=180, _lambda=lampa)

# Analysis
bvec4 = R(super1, A(super1, isb=True, delta=4, **analyse_args), **recalculate_args) if not icalc else None
bvec3 = R(super1, A(super1, isb=True, delta=3, **analyse_args), **recalculate_args)
bvec2 = R(super1, A(super1, isb=True, delta=2, badsad=1100, lsad=1120, **analyse_args), **recalculate_args)
bvec1 = R(super1, A(super1, isb=True, delta=1, badsad=1500, lsad=980, badrange=27, **analyse_args), **recalculate_args)
fvec1 = R(super1, A(super1, isb=False, delta=1, badsad=1500, lsad=980, badrange=27, **analyse_args), **recalculate_args)
fvec2 = R(super1, A(super1, isb=False, delta=2, badsad=1100, lsad=1120, **analyse_args), **recalculate_args)
fvec3 = R(super1, A(super1, isb=False, delta=3, **analyse_args), **recalculate_args)
fvec4 = R(super1, A(super1, isb=False, delta=4, **analyse_args), **recalculate_args) if not icalc else None

# Applying cleaning
if not icalc:
clean = core.mvsf.Degrain4(c if chroma else cy, super2, bvec1, fvec1, bvec2, fvec2, bvec3, fvec3, bvec4, fvec4, thsad=thSAD)
else:
clean = core.mv.Degrain3(c if chroma else cy, super2, bvec1, fvec1, bvec2, fvec2, bvec3, fvec3, thsad=thSAD)

if c.format.bits_per_sample != outbits:
c = c.fmtc.bitdepth(bits=outbits, dmode=1)
cy = cy.fmtc.bitdepth(bits=outbits, dmode=1)
clean = clean.fmtc.bitdepth(bits=outbits, dmode=1)

uv = core.std.MergeDiff(clean, core.tmedian.TemporalMedian(core.std.MakeDiff(c, clean, [1, 2]), 1, [1, 2]), [1, 2]) if chroma else c
clean = core.std.ShufflePlanes(clean, [0], vs.GRAY) if clean.format.num_planes != 1 else clean

# Post clean, pre-process deband
filt = core.std.ShufflePlanes([clean, uv], [0, 1, 2], vs.YUV)

if deband:
filt = filt.f3kdb.Deband(range=16, preset="high" if chroma else "luma", grainy=defH/15, grainc=defH/16 if chroma else 0, output_depth=outbits)
clean = core.std.ShufflePlanes(filt, [0], vs.GRAY)
filt = core.vcmod.Veed(filt) if deband == 2 else filt

# Spatial luma denoising
clean2 = RG(clean, rgmode)

# Apply dynamic noise reduction strength based on Luma.
if strength <= 0:
cleanm = cy.std.Maximum().std.Maximum().std.Maximum().std.Levels(-strength, 255, 0.8, 0, 255+strength)
clean2 = core.std.MaskedMerge(clean2, cy, cleanm)
filt = core.std.MaskedMerge(filt, c, cleanm)

# Unsharp filter for spatial detail enhancement
if sharp:
if sharp <= 50:
clsharp = core.std.MakeDiff(clean, muf.Blur(clean2, amountH=0.08+0.03*sharp))
else:
clsharp = core.std.MakeDiff(clean, clean2.tcanny.TCanny(sigma=(sharp-46)/4, mode=-1))
clsharp = core.std.MergeDiff(clean2, RE(clsharp.tmedian.TemporalMedian(), clsharp, 12))

# If selected, combining ReNoise
noise_diff = core.std.MakeDiff(clean2, cy)
if rn:
expr = "x {a} < 0 x {b} > {p} 0 x {c} - {p} {a} {d} - / * - ? ?".format(a=32*i, b=45*i, c=35*i, d=65*i, p=peak)
clean1 = core.std.Merge(clean2, core.std.MergeDiff(clean2, Tweak(noise_diff.tmedian.TemporalMedian(), cont=1.008+0.00016*rn)), 0.3+rn*0.035)
clean2 = core.std.MaskedMerge(clean2, clean1, core.std.Expr([core.std.Expr([clean, clean.std.Invert()], 'x y min')], [expr]))

# Combining spatial detail enhancement with spatial noise reduction using prepared mask
noise_diff = noise_diff.std.Binarize().std.Invert()
clean2 = core.std.MaskedMerge(clean2, clsharp if sharp else clean, core.std.Expr([noise_diff, clean.std.Sobel()], 'x y max'))

# Combining result of luma and chroma cleaning
output = core.std.ShufflePlanes([clean2, filt], [0, 1, 2], vs.YUV)
if strength > 0 and strength < 20:
output = core.std.Merge(c, output, 0.2+0.04*strength)
return core.std.MergeDiff(output, core.std.MakeDiff(output.warp.AWarpSharp2(128, 3, 1, depth2, 1), output.warp.AWarpSharp2(128, 2, 1, depth, 1))) if depth else output


Code changes:
- changing Strength range to -255 - 20
- Add a few lines at "if strength <= 0:"
- only apply the final strength if > 0

TODO:
- adjust mask level based on TV range if needed. How to get the range parameter from script?
- make sure it's working with all formats and bitdepth.
- port the few lines to Avisynth

Since a PNG screenshot is larger than the actual video... I uploaded a few sample clips here. (https://mega.nz/file/fMxS3C6J#E05aEYDLhK4X-zk9BaIdW9xpkBTUnxAVbEgTdig1Ng0) It compares: original, strength=20, strength=10, strength=-45, strength=-50, strength=-55

BTW. Setting deband=2 (deband+veed) in VapourSynth causes the image to turn green.

real.finder
21st September 2021, 01:14
Switched from ffvideo to DGSource, but same story. I tried to optimize mClean removing some slow filters like Median(), but then saw veed, autoadjust and said, well just stop here you work enough already lol.

Veed http://www.avisynth.nl/users/vcmohan/manyPlus/Veed.html

autoadjust, no, no alternative replacement/update since it close source, I think vs port use something else for autoadjust so it's not 100% same output as avs one (also why it crash in avs with 5k) at least because of autoadjust if not more than it.

edit: in anycase, many vs ports like this, they kinda rip-off not act and output as original function, whatever they do better or worst, it should use another name like mClean2 and said it based on mClean in avs, same if avs+ port with HBD and cleanup happened

edit2: also here https://forum.doom9.org/showthread.php?p=1952918#post1952918

MysteryX
21st September 2021, 16:35
There is a problem with my mod. Dynamic strength is also applying to debanding (which increases file size); as a result, the output file size is very unpredictable.

I could move the strength application just above deband, but spacial luma denoising is applied AFTER deband.


# Spatial luma denoising
clean2 = RG(clean, rgmode)

Why is this applied after debanding? Should strength reduce this, or always apply it full-strength? The question is whether this line is responsible for some of the troubles or not.

IMO the current implementation of strength that applies to deband and sharp isn't very useful, it was patched quickly at the end without thinking about it. When I set strength, I'm expecting to alter the noise reduction for my needs. I'm not expecting to double-cross the sharp and deband settings.

MysteryX
21st September 2021, 18:00
The denoise after deband is just softening the deband and thus should be applied normally.

Other than detecting PC or TV range when altering the Luma mask, there's another detail to look at. I'm currently calling Maximum() 3x to expand exclusion areas (to preserve small details like beard or branches). Perhaps how many times we call it should depend on the video resolution?

This latest video kept better details of the background branches; but then the video is 6.2mb instead of 4.9mb too.

Changed Lume mask gamma from .8 to .85

Here are sample clips: (https://mega.nz/file/WdhwAKQJ#r6vHuDp5nzgxrxaf0bb8VBjkfr1ZHuveEQkRiA_wdu8)
- Original
- Strength=20
- Strength=10
- Strength=-50 (previous with partial debanding)
- Strength=-50 with gamma=.8
- Strength=-50 with gamma=.85

Zooming and playing the clip, the last one feels much better than the default strength=20.

Here's the current code (part of G41Fun.py)


def mClean(clip, thSAD=400, chroma=True, sharp=10, rn=14, deband=0, depth=0, strength=20, outbits=None, icalc=True, rgmode=18):
"""
From: https://forum.doom9.org/showthread.php?t=174804 by burfadel
mClean spatio/temporal denoiser

+++ Description +++
Typical spatial filters work by removing large variations in the image on a small scale, reducing noise but also making the image less
sharp or temporally stable. mClean removes noise whilst retaining as much detail as possible, as well as provide optional image enhancement.

mClean works primarily in the temporal domain, although there is some spatial limiting.
Chroma is processed a little differently to luma for optimal results.
Chroma processing can be disabled with chroma = False.

+++ Artifacts +++
Spatial picture artifacts may remain as removing them is a fine balance between removing the unwanted artifact whilst not removing detail.
Additional dering/dehalo/deblock filters may be required, but should ONLY be uses if required due the detail loss/artifact removal balance.

+++ Sharpening +++
Applies a modified unsharp mask to edges and major detected detail. Range of normal sharpening is 0-20. There are 4 additional settings,
21-24 that provide 'overboost' sharpening. Overboost sharpening is only suitable typically for high definition, high quality sources.
Actual sharpening calculation is scaled based on resolution.

+++ ReNoise +++
ReNoise adds back some of the removed luma noise. Re-adding original noise would be counterproductive, therefore ReNoise modifies this noise
both spatially and temporally. The result of this modification is the noise becomes much nicer and it's impact on compressibility is greatly
reduced. It is not applied on areas where the sharpening occurs as that would be counterproductive. Settings range from 0 to 20.
The strength of renoise is affected by the the amount of original noise removed and how this noise varies between frames.
It's main purpose is to reduce the 'flatness' that occurs with any form of effective denoising.

+++ Deband +++
This will perceptibly improve the quality of the image by reducing banding effect and adding a small amount of temporally stabilised grain
to both luma and chroma. The settings are not adjustable as the default settings are suitable for most cases without having a large effect
on compressibility. 0 = disabled, 1 = deband only, 2 = deband and veed

+++ Depth +++
This applies a modified warp sharpening on the image that may be useful for certain things, and can improve the perception of image depth.
Settings range up from 0 to 5. This function will distort the image, for animation a setting of 1 or 2 can be beneficial to improve lines.

+++ Strength +++
The strength of the denoising effect can be adjusted using this parameter. It ranges from 20 percent denoising effect with strength 0, up to the
100 percent of the denoising with strength 20. This function works by blending a scaled percentage of the original image with the processed image.
A value between 0 and -255 will apply dynamic noise reduction strength based on Luma, where black zones get full denoising and white areas
preserve the source. Specifying a value of -50 means that out of 255, the 50 blackest values have full-reduction and the 50 whitest values
are merged at a minimum value of 50/255.


+++ Outbits +++
Specifies the bits per component (bpc) for the output for processing by additional filters. It will also be the bpc that mClean will process.
If you output at a higher bpc keep in mind that there may be limitations to what subsequent filters and the encoder may support.
"""
# New parameter icalc, set to True to enable pure integer processing for faster speed. (Ignored if input is of float sample type)

defH = max(clip.height, clip.width // 4 * 3) # Resolution calculation for auto blksize settings
sharp = min(max(sharp, 0), 24) # Sharp multiplier
rn = min(max(rn, 0), 20) # Luma ReNoise strength
deband = min(max(deband, 0), 5) # Apply deband/veed
depth = min(max(depth, 0), 5) # Depth enhancement
strength = min(max(strength, -255), 20) # Strength of denoising
bd = clip.format.bits_per_sample
isFLOAT = clip.format.sample_type == vs.FLOAT
icalc = False if isFLOAT else icalc
S = core.mv.Super if icalc else core.mvsf.Super
A = core.mv.Analyse if icalc else core.mvsf.Analyse
R = core.mv.Recalculate if icalc else core.mvsf.Recalculate

if not isinstance(clip, vs.VideoNode) or clip.format.color_family != vs.YUV:
raise TypeError("mClean: This is not a YUV clip!")

if outbits is None: # Output bits, default input depth
outbits = bd

if deband or depth:
outbits = min(outbits, 16)

RE = core.rgsf.Repair if outbits == 32 else core.rgvs.Repair
RG = core.rgsf.RemoveGrain if outbits == 32 else core.rgvs.RemoveGrain
sc = 8 if defH > 2880 else 4 if defH > 1440 else 2 if defH > 720 else 1
i = 0.00392 if outbits == 32 else 1 << (outbits - 8)
peak = 1.0 if outbits == 32 else (1 << outbits) - 1
bs = 16 if defH / sc > 360 else 8
ov = 6 if bs > 12 else 2
pel = 1 if defH > 720 else 2
truemotion = False if defH > 720 else True
lampa = 777 * (bs ** 2) // 64
depth2 = -depth*3
depth = depth*2

if sharp > 20:
sharp += 30
elif defH <= 2500:
sharp = 15 + defH * sharp * 0.0007
else:
sharp = 50

# Denoise preparation
c = core.vcmod.Median(clip, plane=[0, 1, 1]) if chroma else clip

# Temporal luma noise filter
if not (isFLOAT or icalc):
c = c.fmtc.bitdepth(flt=1)
cy = core.std.ShufflePlanes(c, [0], vs.GRAY)

super1 = S(c if chroma else cy, hpad=bs, vpad=bs, pel=pel, rfilter=4, sharp=1)
super2 = S(c if chroma else cy, hpad=bs, vpad=bs, pel=pel, rfilter=1, levels=1)
analyse_args = dict(blksize=bs, overlap=ov, search=5, truemotion=truemotion)
recalculate_args = dict(blksize=bs, overlap=ov, search=5, truemotion=truemotion, thsad=180, _lambda=lampa)

# Analysis
bvec4 = R(super1, A(super1, isb=True, delta=4, **analyse_args), **recalculate_args) if not icalc else None
bvec3 = R(super1, A(super1, isb=True, delta=3, **analyse_args), **recalculate_args)
bvec2 = R(super1, A(super1, isb=True, delta=2, badsad=1100, lsad=1120, **analyse_args), **recalculate_args)
bvec1 = R(super1, A(super1, isb=True, delta=1, badsad=1500, lsad=980, badrange=27, **analyse_args), **recalculate_args)
fvec1 = R(super1, A(super1, isb=False, delta=1, badsad=1500, lsad=980, badrange=27, **analyse_args), **recalculate_args)
fvec2 = R(super1, A(super1, isb=False, delta=2, badsad=1100, lsad=1120, **analyse_args), **recalculate_args)
fvec3 = R(super1, A(super1, isb=False, delta=3, **analyse_args), **recalculate_args)
fvec4 = R(super1, A(super1, isb=False, delta=4, **analyse_args), **recalculate_args) if not icalc else None

# Applying cleaning
if not icalc:
clean = core.mvsf.Degrain4(c if chroma else cy, super2, bvec1, fvec1, bvec2, fvec2, bvec3, fvec3, bvec4, fvec4, thsad=thSAD)
else:
clean = core.mv.Degrain3(c if chroma else cy, super2, bvec1, fvec1, bvec2, fvec2, bvec3, fvec3, thsad=thSAD)

if c.format.bits_per_sample != outbits:
c = c.fmtc.bitdepth(bits=outbits, dmode=1)
cy = cy.fmtc.bitdepth(bits=outbits, dmode=1)
clean = clean.fmtc.bitdepth(bits=outbits, dmode=1)

uv = core.std.MergeDiff(clean, core.tmedian.TemporalMedian(core.std.MakeDiff(c, clean, [1, 2]), 1, [1, 2]), [1, 2]) if chroma else c
clean = core.std.ShufflePlanes(clean, [0], vs.GRAY) if clean.format.num_planes != 1 else clean

# Apply dynamic noise reduction strength based on Luma.
if strength <= 0:
cleanm = cy.std.Maximum().std.Maximum().std.Maximum().std.Levels(-strength, 255, 0.85, 0, 255+strength)
clean = core.std.MaskedMerge(clean, cy, cleanm)
uv = core.std.MaskedMerge(uv, c, cleanm)

# Post clean, pre-process deband
filt = core.std.ShufflePlanes([clean, uv], [0, 1, 2], vs.YUV)

if deband:
filt = filt.f3kdb.Deband(range=16, preset="high" if chroma else "luma", grainy=defH/15, grainc=defH/16 if chroma else 0, output_depth=outbits)
clean = core.std.ShufflePlanes(filt, [0], vs.GRAY)
filt = core.vcmod.Veed(filt) if deband == 2 else filt

# Spatial luma denoising
clean2 = RG(clean, rgmode)

# Unsharp filter for spatial detail enhancement
if sharp:
if sharp <= 50:
clsharp = core.std.MakeDiff(clean, muf.Blur(clean2, amountH=0.08+0.03*sharp))
else:
clsharp = core.std.MakeDiff(clean, clean2.tcanny.TCanny(sigma=(sharp-46)/4, mode=-1))
clsharp = core.std.MergeDiff(clean2, RE(clsharp.tmedian.TemporalMedian(), clsharp, 12))

# If selected, combining ReNoise
noise_diff = core.std.MakeDiff(clean2, cy)
if rn:
expr = "x {a} < 0 x {b} > {p} 0 x {c} - {p} {a} {d} - / * - ? ?".format(a=32*i, b=45*i, c=35*i, d=65*i, p=peak)
clean1 = core.std.Merge(clean2, core.std.MergeDiff(clean2, Tweak(noise_diff.tmedian.TemporalMedian(), cont=1.008+0.00016*rn)), 0.3+rn*0.035)
clean2 = core.std.MaskedMerge(clean2, clean1, core.std.Expr([core.std.Expr([clean, clean.std.Invert()], 'x y min')], [expr]))

# Combining spatial detail enhancement with spatial noise reduction using prepared mask
noise_diff = noise_diff.std.Binarize().std.Invert()
clean2 = core.std.MaskedMerge(clean2, clsharp if sharp else clean, core.std.Expr([noise_diff, clean.std.Sobel()], 'x y max'))

# Combining result of luma and chroma cleaning
output = core.std.ShufflePlanes([clean2, filt], [0, 1, 2], vs.YUV)
if strength > 0 and strength < 20:
output = core.std.Merge(c, output, 0.2+0.04*strength)
return core.std.MergeDiff(output, core.std.MakeDiff(output.warp.AWarpSharp2(128, 3, 1, depth2, 1), output.warp.AWarpSharp2(128, 2, 1, depth, 1))) if depth else output

MysteryX
22nd September 2021, 17:28
I'm really no expert in debanding and sharpening but question for those who know about it.

It's applying deband and generating noise patterns, then denoising it with NG, and then sharpening it at the end. Does that make any sense at all?

One issue with that is how each parameter affects other parameters... sharp parameter alters deband parameter. Deband noise is used for the renoise algorithm.

I could try moving deband at the end. I'm expecting this to know what it's supposed to be doing before sending for encoding? But what about AutoLevels and Veed, where are those supposed to be placed?

Edit: I tried moving deband at the end, and it makes a HUGE difference. Sharpening debanding is causing very strong noise patterns, and wasting a lot of bandwidth to store that noise, whereas putting the same debanding at the end has a very subtle effect. But at that point -- could as well exclude debanding altogether and apply it manually after.

kedautinh12
22nd September 2021, 18:00
I'm really no expert in debanding and sharpening but question for those who know about it.

It's applying deband and generating noise patterns, then denoising it with NG, and then sharpening it at the end. Does that make any sense at all?

One issue with that is how each parameter affects other parameters... sharp parameter alters deband parameter. Deband noise is used for the renoise algorithm.

I could try moving deband at the end. I'm expecting this to know what it's supposed to be doing before sending for encoding? But what about AutoLevels and Veed, where are those supposed to be placed?

Edit: I tried moving deband at the end, and it makes a HUGE difference. Sharpening debanding is causing very strong noise patterns, and wasting a lot of bandwidth to store that noise, whereas putting the same debanding at the end has a very subtle effect. But at that point -- could as well exclude debanding altogether and apply it manually after.

If you have new idea, can you update new mod for mClean avs+?? Thanks

MysteryX
22nd September 2021, 18:27
I will, but I would like to know what the original idea was to analyze and sharpen the deband

real.finder
22nd September 2021, 18:29
If you have new idea, can you update new mod for mClean avs+?? Thanks

even before his mod, vs mClean is not same as avs one, so if one backport vs port he/she should call it vsmClean same as asd-g (https://github.com/Asd-g?tab=repositories) do. since yes, even vs plugins are not same as original avs one! in case of everything! including default value like the case of vs dfttest that become neo_dfttest after backport

MysteryX
22nd September 2021, 19:32
Looking at Deband, AutoLevels and Veed -- should those even be included in the script? It adds lots of dependencies. The benefit is that it adds default values for AutoLevels and Deband.

What's the ideal location to apply each though? Deband, at the end of the script, correct? Whereas mClean is among the first filters. Still, it applies parameter values that we wouldn't manually specify which can be useful.

Veed? IMO it should be done before denoising, in cases where it's needed. No need to be included in this script. Plus in VapourSynth it's causing the image to turn green.

AutoLevels? I think... either before or after denoising, but you don't want "strength" affecting it. It sets good default generic values but that aren't related to the clip or parameters. You can just create a convenience function with those defaults. Applying it first could interfere with denoising algorithm, whereas applying it after sharpening could make sharpening work in a non-optimal way. Thus... could be useful to keep. in the middle.

If 2 of the 3 are kept, could keep Veed... apply Veed at the start or middle, AutoLevels in the middle, and Deband in the end.

MysteryX
22nd September 2021, 21:44
ok now the VapourSynth script makes more sense. Remains to test on various videos, and port to Avisynth.

v1.1 by Etienne Charland (2021-09-22):
- Added dynamic noise reduction strength based on Luma where dark areas get full reduction and
white areas preserve more of the source. Set Strength between 0 and -200, recommended -50. A value of -50 means that out of 255,
the 50 blackest values have full-reduction and the 50 whitest values are merged at a minimal strength of 50/255.
- Strength no longer apply to deband and sharpen, only to noise reduction.
- Deband was denoised and then sharpened. It has been moved to the end after sharpening.
- Veed is run between noise reduction and sharpening and is not affected by strength.

It now applies Maximum to widen the exclusion mask based on the resolution, and it reads ColorRange from clip parameters.


def mClean(clip, thSAD=400, chroma=True, sharp=10, rn=14, deband=0, depth=0, strength=20, outbits=None, icalc=True, rgmode=18):
"""
From: https://forum.doom9.org/showthread.php?t=174804 by burfadel
mClean spatio/temporal denoiser

v1.1 by Etienne Charland (2021-09-22):
- Added dynamic noise reduction strength based on Luma where dark areas get full reduction and
white areas preserve more of the source. Set Strength between 0 and -200, recommended -50. A value of -50 means that out of 255,
the 50 blackest values have full-reduction and the 50 whitest values are merged at a minimal strength of 50/255.
- Strength no longer apply to deband and sharpen, only to noise reduction.
- Deband was denoised and then sharpened. It has been moved to the end after sharpening.
- Veed is run between noise reduction and sharpening and is not affected by strength.

+++ Description +++
Typical spatial filters work by removing large variations in the image on a small scale, reducing noise but also making the image less
sharp or temporally stable. mClean removes noise whilst retaining as much detail as possible, as well as provide optional image enhancement.

mClean works primarily in the temporal domain, although there is some spatial limiting.
Chroma is processed a little differently to luma for optimal results.
Chroma processing can be disabled with chroma = False.

+++ Artifacts +++
Spatial picture artifacts may remain as removing them is a fine balance between removing the unwanted artifact whilst not removing detail.
Additional dering/dehalo/deblock filters may be required, but should ONLY be uses if required due the detail loss/artifact removal balance.

+++ Sharpening +++
Applies a modified unsharp mask to edges and major detected detail. Range of normal sharpening is 0-20. There are 4 additional settings,
21-24 that provide 'overboost' sharpening. Overboost sharpening is only suitable typically for high definition, high quality sources.
Actual sharpening calculation is scaled based on resolution.

+++ ReNoise +++
ReNoise adds back some of the removed luma noise. Re-adding original noise would be counterproductive, therefore ReNoise modifies this noise
both spatially and temporally. The result of this modification is the noise becomes much nicer and it's impact on compressibility is greatly
reduced. It is not applied on areas where the sharpening occurs as that would be counterproductive. Settings range from 0 to 20.
The strength of renoise is affected by the the amount of original noise removed and how this noise varies between frames.
It's main purpose is to reduce the 'flatness' that occurs with any form of effective denoising.

+++ Deband +++
This will perceptibly improve the quality of the image by reducing banding effect and adding a small amount of temporally stabilised grain
to both luma and chroma. The settings are not adjustable as the default settings are suitable for most cases without having a large effect
on compressibility. 0 = disabled, 1 = deband only, 2 = deband and veed

+++ Depth +++
This applies a modified warp sharpening on the image that may be useful for certain things, and can improve the perception of image depth.
Settings range up from 0 to 5. This function will distort the image, for animation a setting of 1 or 2 can be beneficial to improve lines.

+++ Strength +++
The strength of the denoising effect can be adjusted using this parameter. It ranges from 20 percent denoising effect with strength 0, up to the
100 percent of the denoising with strength 20. This function works by blending a scaled percentage of the original image with the processed image.
A value between 0 and -200 will apply dynamic noise reduction strength based on Luma, where black zones get full denoising and white areas
preserve the source. Specifying a value of -50 means that out of 255, the 50 blackest values have full-reduction and the 50 whitest values
are merged at a minimal strength of 50/255.

+++ Outbits +++
Specifies the bits per component (bpc) for the output for processing by additional filters. It will also be the bpc that mClean will process.
If you output at a higher bpc keep in mind that there may be limitations to what subsequent filters and the encoder may support.
"""
# New parameter icalc, set to True to enable pure integer processing for faster speed. (Ignored if input is of float sample type)

defH = max(clip.height, clip.width // 4 * 3) # Resolution calculation for auto blksize settings
sharp = min(max(sharp, 0), 24) # Sharp multiplier
rn = min(max(rn, 0), 20) # Luma ReNoise strength
deband = min(max(deband, 0), 5) # Apply deband/veed
depth = min(max(depth, 0), 5) # Depth enhancement
strength = min(max(strength, -200), 20) # Strength of denoising
bd = clip.format.bits_per_sample
isFLOAT = clip.format.sample_type == vs.FLOAT
icalc = False if isFLOAT else icalc
S = core.mv.Super if icalc else core.mvsf.Super
A = core.mv.Analyse if icalc else core.mvsf.Analyse
R = core.mv.Recalculate if icalc else core.mvsf.Recalculate

if not isinstance(clip, vs.VideoNode) or clip.format.color_family != vs.YUV:
raise TypeError("mClean: This is not a YUV clip!")

props = clip.get_frame(0).props
fullRange = '_ColorRange' in props and props['_ColorRange'] == 0

if outbits is None: # Output bits, default input depth
outbits = bd

if deband or depth:
outbits = min(outbits, 16)

RE = core.rgsf.Repair if outbits == 32 else core.rgvs.Repair
RG = core.rgsf.RemoveGrain if outbits == 32 else core.rgvs.RemoveGrain
sc = 8 if defH > 2880 else 4 if defH > 1440 else 2 if defH > 720 else 1
i = 0.00392 if outbits == 32 else 1 << (outbits - 8)
peak = 1.0 if outbits == 32 else (1 << outbits) - 1
bs = 16 if defH / sc > 360 else 8
ov = 6 if bs > 12 else 2
pel = 1 if defH > 720 else 2
truemotion = False if defH > 720 else True
lampa = 777 * (bs ** 2) // 64
depth2 = -depth*3
depth = depth*2

if sharp > 20:
sharp += 30
elif defH <= 2500:
sharp = 15 + defH * sharp * 0.0007
else:
sharp = 50

# Denoise preparation
c = core.vcmod.Median(clip, plane=[0, 1, 1]) if chroma else clip

# Temporal luma noise filter
if not (isFLOAT or icalc):
c = c.fmtc.bitdepth(flt=1)
cy = core.std.ShufflePlanes(c, [0], vs.GRAY)

super1 = S(c if chroma else cy, hpad=bs, vpad=bs, pel=pel, rfilter=4, sharp=1)
super2 = S(c if chroma else cy, hpad=bs, vpad=bs, pel=pel, rfilter=1, levels=1)
analyse_args = dict(blksize=bs, overlap=ov, search=5, truemotion=truemotion)
recalculate_args = dict(blksize=bs, overlap=ov, search=5, truemotion=truemotion, thsad=180, _lambda=lampa)

# Analysis
bvec4 = R(super1, A(super1, isb=True, delta=4, **analyse_args), **recalculate_args) if not icalc else None
bvec3 = R(super1, A(super1, isb=True, delta=3, **analyse_args), **recalculate_args)
bvec2 = R(super1, A(super1, isb=True, delta=2, badsad=1100, lsad=1120, **analyse_args), **recalculate_args)
bvec1 = R(super1, A(super1, isb=True, delta=1, badsad=1500, lsad=980, badrange=27, **analyse_args), **recalculate_args)
fvec1 = R(super1, A(super1, isb=False, delta=1, badsad=1500, lsad=980, badrange=27, **analyse_args), **recalculate_args)
fvec2 = R(super1, A(super1, isb=False, delta=2, badsad=1100, lsad=1120, **analyse_args), **recalculate_args)
fvec3 = R(super1, A(super1, isb=False, delta=3, **analyse_args), **recalculate_args)
fvec4 = R(super1, A(super1, isb=False, delta=4, **analyse_args), **recalculate_args) if not icalc else None

# Applying cleaning
if not icalc:
clean = core.mvsf.Degrain4(c if chroma else cy, super2, bvec1, fvec1, bvec2, fvec2, bvec3, fvec3, bvec4, fvec4, thsad=thSAD)
else:
clean = core.mv.Degrain3(c if chroma else cy, super2, bvec1, fvec1, bvec2, fvec2, bvec3, fvec3, thsad=thSAD)

if c.format.bits_per_sample != outbits:
c = c.fmtc.bitdepth(bits=outbits, dmode=1)
cy = cy.fmtc.bitdepth(bits=outbits, dmode=1)
clean = clean.fmtc.bitdepth(bits=outbits, dmode=1)

uv = core.std.MergeDiff(clean, core.tmedian.TemporalMedian(core.std.MakeDiff(c, clean, [1, 2]), 1, [1, 2]), [1, 2]) if chroma else c
clean = core.std.ShufflePlanes(clean, [0], vs.GRAY) if clean.format.num_planes != 1 else clean

# Post clean, pre-process deband
filt = core.std.ShufflePlanes([clean, uv], [0, 1, 2], vs.YUV)

# Spatial luma denoising
clean2 = RG(clean, rgmode)

# Apply dynamic noise reduction strength based on Luma.
if strength <= 0:
cleanm = cy.std.Maximum() # Slightly widen the exclusion mask to preserve details and edges
if defH > 500:
cleanm = cleanm.std.Maximum()
if defH > 1200:
cleanm = cleanm.std.Maximum()
cleanm = cleanm.std.Levels((0 if fullRange else 16) - strength, 255 if fullRange else 235, 0.85, 0, 255+strength)
clean = core.std.MaskedMerge(clean, cy, cleanm)
clean2 = core.std.MaskedMerge(clean2, cy, cleanm)
filt = core.std.MaskedMerge(filt, c, cleanm)
elif strength < 20:
clean = core.std.Merge(c, clean, 0.2+0.04*strength)
clean2 = core.std.Merge(c, clean2, 0.2+0.04*strength)
filt = core.std.Merge(c, filt, 0.2+0.04*strength)

# Apply Veed (auto-levels would also go here)
if deband == 2:
filt = core.vcmod.Veed(filt)

# Unsharp filter for spatial detail enhancement
if sharp:
if sharp <= 50:
clsharp = core.std.MakeDiff(clean, muf.Blur(clean2, amountH=0.08+0.03*sharp))
else:
clsharp = core.std.MakeDiff(clean, clean2.tcanny.TCanny(sigma=(sharp-46)/4, mode=-1))
clsharp = core.std.MergeDiff(clean2, RE(clsharp.tmedian.TemporalMedian(), clsharp, 12))

# If selected, combining ReNoise
noise_diff = core.std.MakeDiff(clean2, cy)
if rn:
expr = "x {a} < 0 x {b} > {p} 0 x {c} - {p} {a} {d} - / * - ? ?".format(a=32*i, b=45*i, c=35*i, d=65*i, p=peak)
clean1 = core.std.Merge(clean2, core.std.MergeDiff(clean2, Tweak(noise_diff.tmedian.TemporalMedian(), cont=1.008+0.00016*rn)), 0.3+rn*0.035)
clean2 = core.std.MaskedMerge(clean2, clean1, core.std.Expr([core.std.Expr([clean, clean.std.Invert()], 'x y min')], [expr]))

# Combining spatial detail enhancement with spatial noise reduction using prepared mask
noise_diff = noise_diff.std.Binarize().std.Invert()
clean2 = core.std.MaskedMerge(clean2, clsharp if sharp else clean, core.std.Expr([noise_diff, clean.std.Sobel()], 'x y max'))

# Combining result of luma and chroma cleaning
output = core.std.ShufflePlanes([clean2, filt], [0, 1, 2], vs.YUV)
output = core.std.MergeDiff(output, core.std.MakeDiff(output.warp.AWarpSharp2(128, 3, 1, depth2, 1), output.warp.AWarpSharp2(128, 2, 1, depth, 1))) if depth else output

if deband:
output = output.f3kdb.Deband(range=16, preset="high" if chroma else "luma", grainy=defH/15, grainc=defH/16 if chroma else 0, output_depth=outbits)
return output


G41Fun (https://github.com/groucho86/G41Fun) has however been abandonned since 2 years, with a pull request from a year ago that had no response. It's unlikely that the changes will be merged. What are the options here?

MysteryX
23rd September 2021, 01:27
Now something annoying. I was doing tests on a heavily noised video of a lightning storm. mClean is doing a "fine" job but not denoising enough. I compared to KNLMeansCL and it gave smoother results.... in most frames, but it did a terrible job during lightning bursts! (Which is most of the video)

So... whenever there are quick flashes of light, mClean is actually doing a better job; but not enough overall. Question is, is there a way to achieve stronger denoising with mClean? (other than reducing renoise)

Overboost sharpening for dark scenes using KNLMeansCL instead of MvTools?

kedautinh12
23rd September 2021, 02:05
Can you check BM3D??

ChaosKing
23rd September 2021, 08:01
G41Fun (https://github.com/groucho86/G41Fun) has however been abandonned since 2 years, with a pull request from a year ago that had no response. It's unlikely that the changes will be merged. What are the options here?

This is not the original author of G41Fun.
Wolfberry is https://forum.doom9.org/showthread.php?t=175989 but he deleted his github repo and does not seem to be active anymore.

Your options are:
- release it on doom9 only
- release it on github (this way it can be added easily to vsrepo)
- Just wait and hope someone picks it up and adds it to his repo / collection

Dogway
23rd September 2021, 09:53
Now something annoying. I was doing tests on a heavily noised video of a lightning storm.

Can you share the clip? It would make for a great sample clip to benchmark denoisers. Use DCT=5 for lightnings.

MysteryX
23rd September 2021, 13:49
Can you share the clip? It would make for a great sample clip to benchmark denoisers. Use DCT=5 for lightnings.
Here's the clip: Lightning Storm Veracruz 5K (439MB) (https://drive.google.com/file/d/1Ci3mhUi2lZLcAZ6r5kMnF3JxDj0hz7EV/view?usp=sharing)

You can see what you can do with it.

real.finder
23rd September 2021, 16:52
Can you share the clip? It would make for a great sample clip to benchmark denoisers. Use DCT=5 for lightnings.

so you will backport VS mClean (I think this (https://github.com/groucho86/G41Fun/blob/6074ced4d481a461a53bf4b40403c73bdf1fe4a7/G41Fun.py#L2426)) to avs+ as vsmClean?

kedautinh12
23rd September 2021, 17:23
so you will backport VS mClean (I think this (https://github.com/groucho86/G41Fun/blob/6074ced4d481a461a53bf4b40403c73bdf1fe4a7/G41Fun.py#L2426)) to avs+ as vsmClean?

I think last ver from MysteryX
https://forum.doom9.org/showthread.php?p=1952921#post1952921

real.finder
23rd September 2021, 17:33
I think last ver from MysteryX
https://forum.doom9.org/showthread.php?p=1952921#post1952921

this is VSmCleanMod

kedautinh12
23rd September 2021, 17:38
this is VSmCleanMod

Yeah, mod ver with better result, why not??

ChaosKing
23rd September 2021, 18:49
I was so free and uploaded G41Fun here https://github.com/Vapoursynth-Plugins-Gitify/G41Fun
It is the last version 0.4.1 before it was deleted: https://github.com/vapoursynth/vsrepo/blob/f35e779f23bb964a4d97cd5528b96ba138ac5723/local/g41fun.json


@MysteryX the groucho version is not the latest G41Fun version but mClean was not modified since then, so nothing to worry about :)

I added your mClean changes but changed vcmod to vcm since vcmod is deprecated.

2. note: The mvsf plugin is referencing the old mvtools-sf version in G41Fun (the one with mvmulti script). There were some major rewrites: https://forum.doom9.org/showthread.php?p=1911978#post1911978

MysteryX
23rd September 2021, 19:28
If it's doing much different work than before, I was thinking as releating as xClean or something; but if the same repo is updated and I keep the API and results compatible, then can just keep it as it is with G41Fun. That will make it easier to keep things like StaxRip up-to-date. Note that I'm not completely done yet.

Also pSharpen in that script isn't working. Gives black&white output.

MysteryX
23rd September 2021, 22:42
btw is there a way to calculate Average Luma after discarding 10% brighest pixels? So that spotlights don't alter whether a scene is dark or not.

btw KNLMeansCL seems to be doing a much better job in very dark noisy scenes, and renoise works fine with it. Strangely, KNLMeansCL fails on the very first frame (due to lack of temporal data?)

I'm implementing hyperboost denoising in dark scenes and it seems to be working pretty well now. Tested on the lightning clip: works perfectly. Only issue is that on quick 1-frame bursts of light, sometimes there's color changes with mClean.

Also although KNLMeansCL can be blurrier, it has its uses with renoise and sharpening. Such as in dark scenes.

ChaosKing
23rd September 2021, 22:44
psharpen works for me (R55), but I get a white noise like pattern. It's ok with std.SetMaxCPU("none"). So it's either a Vapoursynth Expr bug or an invalid expr. Opend a bug report on github.

MysteryX
24th September 2021, 15:12
ChaosKing, I think the best is to keep the existing mClean in G41Fun.

I've made too many changes to keep it under the same name, I'll release xClean. Among incompatible changes:
- Deband is now done after sharpening
- By default, dynamic noise reduction and boost will be enabled
- thSAD parameter is removed, renamed to p1

It will be possible to add various denoisers, with method=0 (MvTools2) and method=1 (KNLMeansCL), can add method=2 (BM3D), each using renoise & sharpen.

ChaosKing
24th September 2021, 16:35
OK I will revert the commit later.

kedautinh12
24th September 2021, 17:04
ChaosKing, I think the best is to keep the existing mClean in G41Fun.

I've made too many changes to keep it under the same name, I'll release xClean. Among incompatible changes:
- Deband is now done after sharpening
- By default, dynamic noise reduction and boost will be enabled
- thSAD parameter is removed, renamed to p1

It will be possible to add various denoisers, with method=0 (MvTools2) and method=1 (KNLMeansCL), can add method=2 (BM3D), each using renoise & sharpen.

And can xClean for avs+?? Thanks

MysteryX
24th September 2021, 18:58
Posted beta 1 here. (https://forum.doom9.org/showthread.php?p=1953058)

It will be converted to Avisynth only when the VapourSynth script is completed.

MysteryX
25th September 2021, 23:46
Can you check BM3D??
I tried BM3D. I need to set sigma to a really high value (20 instead of 3) to see some difference, and it's subtle. It's not removing all the noisy grain in the videos I'm testing with. Since it's a conservative filter, renoise/sharp isn't so useful with BM3D since it's already very selective in what it removes. I can add it but I don't know whether it will be useful vs standard BM3D.

What kind of videos does BM3D excel with?

ChaosKing
26th September 2021, 10:14
bm3d Readme from here https://github.com/HomeOfVapourSynthEvolution/VapourSynth-BM3D says:

Employ custom denoising filter as basic estimate, refined with V-BM3D final estimate.
May compensate the shortages of both denoising filters: SMDegrain is effective at spatial-temporal smoothing but can lead to blending and detail loss, V-BM3D preserves details well but is not very effective for large noise pattern (such as heavy grain).


My quick test shows that xClean+bm3d does not give the same good results as bm3d + (xClean as ref clip). At least not with my noisy 90s anime source :D

Idk if the "large noise pattern" part also applies to bm3d_cuda.

Have you tried bm3d with a radius?

kedautinh12
26th September 2021, 11:13
I tried BM3D. I need to set sigma to a really high value (20 instead of 3) to see some difference, and it's subtle. It's not removing all the noisy grain in the videos I'm testing with. Since it's a conservative filter, renoise/sharp isn't so useful with BM3D since it's already very selective in what it removes. I can add it but I don't know whether it will be useful vs standard BM3D.

What kind of videos does BM3D excel with?

You forgot bm3d.VAggregate??? bm3d.VAggregate should be called after temporal filtering, as in VapourSynth-BM3D
https://github.com/WolframRhodium/VapourSynth-BM3DCUDA#notes

MysteryX
26th September 2021, 15:58
My quick test shows that xClean+bm3d does not give the same good results as bm3d + (xClean as ref clip).
xClean as ref into BM3D gives very impressive results!!

Have you tried bm3d with a radius?
Can't get it to work.

You forgot bm3d.VAggregate??? bm3d.VAggregate should be called after temporal filtering, as in VapourSynth-BM3D
https://github.com/WolframRhodium/VapourSynth-BM3DCUDA#notes


str = 10
clean = clip.resize.Bicubic(format=vs.RGBS, matrix_in_s="709")
clean = clean.bm3dcuda.BM3D(sigma=[str,str,str], radius=1).bm3d.VAggregate()
clip = clean.resize.Bicubic(format=clip.format, matrix_s="709")



bm3d.VAggregate: Invalid input clip, must be of Gray, YUV or YCoCg color family

Reel.Deel
26th September 2021, 16:07
Can't get it to work.


str = 10
clean = clip.resize.Bicubic(format=vs.RGBS, matrix_in_s="709")
clean = clean.bm3dcuda.BM3D(sigma=[str,str,str], radius=1).bm3d.VAggregate()
clip = clean.resize.Bicubic(format=clip.format, matrix_s="709")


bm3d.VAggregate: Invalid input clip, must be of Gray, YUV or YCoCg color family


Your input clip is RGB.

MysteryX
26th September 2021, 16:13
Your input clip is RGB.

from doc (https://github.com/HomeOfVapourSynthEvolution/VapourSynth-BM3D#important-note)
"The denoising quality is best when filtering in opponent color space (abbr. OPP, a kind of YUV color space with simple and intuitive matrix coefficients), which significantly outperforms the quality when filtering in RGB, YCbCr, YCgCo, etc. Thus RGB input is recommended, this filter will convert it to OPP internally and convert back to RGB for output."

MysteryX
26th September 2021, 18:37
Whenever I use BM3D (non-CUDA version, or CODA + bm3d.VAggregate), the whites get turned into grey.

Video is full-range, and I explicitly set

clip = clip.std.SetFrameProp("_ColorRange", intval=1)
str = 10
clean = clip.resize.Bicubic(format=vs.YUV444PS)
clean = clean.bm3dcuda.BM3D(sigma=[str,str,str], radius=1).bm3d.VAggregate()
clip = clean.resize.Bicubic(format=clip.format)

poisondeathray
26th September 2021, 18:46
Whenever I use BM3D (non-CUDA version, or CODA + bm3d.VAggregate), the whites get turned into grey.

Video is full-range, and I explicitly set

clip = clip.std.SetFrameProp("_ColorRange", intval=1)
str = 10
clean = clip.resize.Bicubic(format=vs.YUV444PS)
clean = clean.bm3dcuda.BM3D(sigma=[str,str,str], radius=1).bm3d.VAggregate()
clip = clean.resize.Bicubic(format=clip.format)


clip = clip.std.SetFrameProp("_ColorRange", intval=1)

1 is limited range
0 is full range

Reel.Deel
26th September 2021, 18:49
Can't get it to work.

str = 10
clean = clip.resize.Bicubic(format=vs.RGBS, matrix_in_s="709")
clean = clean.bm3dcuda.BM3D(sigma=[str,str,str], radius=1).bm3d.VAggregate()
clip = clean.resize.Bicubic(format=clip.format, matrix_s="709")


Ok, after updating to VS R55, then reverting back R52 because R55 just did not work for some reason and about 50 errors in the script I think I figured it out.


import vapoursynth as vs
from vapoursynth import core

str = 10
src = core.ffms2.Source(source='somevideo.mp4')
src = src.resize.Bicubic(format=vs.RGBS, matrix_in_s="709")
src = core.bm3d.RGB2OPP(src, sample=1)
flt = src.bm3dcuda.BM3D(sigma=[str,str,str], radius=1).bm3d.VAggregate(radius=1, sample=1)
flt = core.std.ShufflePlanes([flt,src,src], [0,1,2], vs.YUV)
flt = core.bm3d.OPP2RGB(flt, sample=1) # back to RGBS
flt.set_output()

RGB2OPP() is needed because "For V-BM3D, the filtered output is always OPP for RGB input, and you should manually call bm3d.OPP2RGB afterwards." ... But in your script bm3dcuda.BM3D is used so bm3d.VAggregate does not get the 'special' YUV clip and it throws an error.

MysteryX
26th September 2021, 18:54
clip = clip.std.SetFrameProp("_ColorRange", intval=1)

1 is limited range
0 is full range

Not according to the bottom of this page... who is right?
http://www.vapoursynth.com/doc/functions/video/resize.html

MysteryX
26th September 2021, 18:56
BM3D with xClean as ref gives hugely improved results!

KNLMeansCL with xClean as ref... very minimal improvement.

poisondeathray
26th September 2021, 19:04
Not according to the bottom of this page... who is right?
http://www.vapoursynth.com/doc/functions/video/resize.html

http://www.vapoursynth.com/doc/apireference.html#reserved-frame-properties


int _ColorRange

Full or limited range (PC/TV range). Primarily used with YUV formats.

0=full range, 1=limited range.

MysteryX
26th September 2021, 19:23
http://www.vapoursynth.com/doc/apireference.html#reserved-frame-properties


int _ColorRange

Full or limited range (PC/TV range). Primarily used with YUV formats.

0=full range, 1=limited range.



Who can fix the other page?

poisondeathray
26th September 2021, 19:32
The other page is Resize, based zimg, based on h265 document. The presence of full range flag is 1, absence is 0

Internal frame props are not the same thing. Avisynth frame props were based on vapoursynth frame props and they have 0 as full too
http://avisynth.nl/index.php/Internal_functions#Functions_for_frame_properties

But yes, it's inconsistent

MysteryX
26th September 2021, 21:53
OK I've done testing with

(image is 5K, the web page doesn't allow displaying in full resolution unless you drag the image into a new tab)

Details to note:
- Noise in the upper left
- Beard details
- Seat square texture
- Moving trees

Note that dynamic denoiser strength is enabled so white areas have less denoising.

Original [9.01MB]
https://i.postimg.cc/jC5G6bbV/Original.png (https://postimg.cc/jC5G6bbV)

xClean [20.5MB] -- banding enabled
https://i.postimg.cc/k697Mh6J/xClean.png (https://postimg.cc/k697Mh6J)

BM3D(sigma=12) [10.6MB]
https://i.postimg.cc/5XhxTtfF/BM3D-12.png (https://postimg.cc/5XhxTtfF)

BM3D(6) ref=xClean [10.6MB]
https://i.postimg.cc/HJbpgCLv/x-Clean-BM3-D-6.png (https://postimg.cc/HJbpgCLv)

BM3D(7) ref=xClean [10.6MB]
https://i.postimg.cc/6yQ5GSHT/x-Clean-BM3-D-7.png (https://postimg.cc/6yQ5GSHT)

BM3D(8) ref=xClean [10.6MB]
https://i.postimg.cc/w1yqwNYp/x-Clean-BM3-D-8.png (https://postimg.cc/w1yqwNYp)

BM3D(10) ref=xClean [10.5MB]
https://i.postimg.cc/zVPDwjM7/x-Clean-BM3-D-10.png (https://postimg.cc/zVPDwjM7)

BM3D(12) ref=xClean [10.5MB]
https://i.postimg.cc/Cd7x5YsW/x-Clean-BM3-D-12.png (https://postimg.cc/Cd7x5YsW)

I'm noting that xClean has deband in its output, that then gets removed by BM3D. Let's set deband=0.

xClean [12.3MB]
https://i.postimg.cc/KKDdfcjb/x-Clean-nodeband.png (https://postimg.cc/KKDdfcjb)

BM3D(8) ref=xClean [10.5MB]
https://i.postimg.cc/p9HwK52k/x-Clean-BM3-D-8-nodeband.png (https://postimg.cc/p9HwK52k)

I've done tests with radius, and I'm really not convinced of using temporal BM3D over a previous temporal analysis. It didn't seem productive.

Now in this latest test without debanding, PNG file size reduces fro 12.3MB to 10.5MB, closer to the 9.01MB original file. Although there is less "data", more details definitely come out. Some trees re-appear, beard gains a little details, and the seat square textures become better defined. I think that's a winner, xClean with BM3D with sigma around 8. There is a slight increase of grain noise but it's not significant.


As a bonus

KNLMeansCL(d=2, a=2, h=4) [12.0MB]
https://i.postimg.cc/cKP8DWbD/KNL-4.png (https://postimg.cc/cKP8DWbD)

xClean(method=1, p1=4) # KNLMeans(h=4) [11.9MB]
https://i.postimg.cc/3ykvCNn4/x-Clean-KNL-4.png (https://postimg.cc/3ykvCNn4)

Strangely, the xClean version doesn't seem any clearer with renoise/sharpen applied, unless I'm doing something wrong?

Running KNLMeansCL over xClean clip is bad, but...

KNLMeansCL(2) with rclip=xClean [9.82MB]
https://i.postimg.cc/SJsdt6Qj/KNL-Ref-2.png (https://postimg.cc/SJsdt6Qj)

Nearly as good as BM3D with ref! Just slightly blurrier.

kedautinh12
27th September 2021, 01:48
I think bm3d.VAggregate must same radius with bm3dcuda.BM3D


clean = clean.bm3dcuda.BM3D(sigma=[str,str,str], radius=1).bm3d.VAggregate(radius=1)

MysteryX
27th September 2021, 02:21
radius=1 worked fine in YUV444PS colorspace. I didn't see benefits here over radius=0. Considering we already did temporal denoising.

MysteryX
27th September 2021, 06:15
Did extensive testing with using KNLMeansCL and BM3D with xClean as ref. You'll be pleased with the results!

Dithering from 16-bit with dmode=3, no debanding. Set str=20 to disable dynamic denoiser strength.

Original [9.01MB]
https://i.postimg.cc/jC5G6bbV/Original.png (https://postimg.cc/jC5G6bbV)

xClean [10.5MB]
https://i.postimg.cc/CR7W2z1m/m-Clean-vy.png (https://postimg.cc/CR7W2z1m)

BM3D(sigma=9) with ref=xClean [10.5MB]
https://i.postimg.cc/cKH8dmj8/Finalm2-9.png (https://postimg.cc/cKH8dmj8)

KNLMeansCL(d=3, a=2, h=1.4) with ref=xClean, processing luma and chroma separately, chroma with h=.7 (half) [10.2MB]
https://i.postimg.cc/B8VPbF97/Finalm1-14-d3a2.png (https://postimg.cc/B8VPbF97)

Same KNLMeansCL, enabling dynamic noise strength with str=-50
https://i.postimg.cc/bZqtB89g/Finalm1-14-d3a2-dynamic.png (https://postimg.cc/bZqtB89g)

Conclusion: KNLMeansCL with the right fine-tuning gives better results than BM3D, and provides considerable benefits over plain xClean.

Syntax to get the best results

clean = xclean.xClean(clip, sharp=21, strength=-50, outbits=16, finalm=1, f1=1.4) # final method: KNLMeansCL

real.finder
27th September 2021, 09:34
I find some bugs in original avs script, one in mt_lut

anyway, I did try backport vs one

function VSmClean(clip c, int "thSAD", bool "chroma", int "sharp", int "rn", int "deband", int "depth", float "strength", int "outbits", bool "icalc", int "rgmode")
{

defH = Max (C.Height, C.Width/4*3) # Resolution calculation for auto blksize settings
thSAD = Default (thSAD, 400) # Denoising threshold
chroma = Default (chroma, true) # Process chroma
sharp = Default (sharp, 10) # Sharp multiplier
rn = Default (rn, 14) # Luma ReNoise strength from 0 (disabled) to 20
deband = Default (deband, 0) # Apply deband/veed and/or auto balance
depth = Default (depth, 0) # Depth enhancement
strength = Default (strength, 20) # Strength of denoising.
rgmode = Default (rgmode, 18) # Strength of denoising.
bd = BitsPerComponent(c)
sisvfloat = isvideofloat(c)
icalc = sisvfloat ? false : Default(icalc, true)
outbits = Default (outbits, bd) # Output bits, default input depth
deband = depth !=0 || deband != 0 ? min(outbits, 16) : deband

Assert(isYUV(c)==true, """mClean: This is not a YUV clip!""")
Assert(isYUY2(c)==false, """mClean: YUY2 not Supported""")

sharp = min(max(sharp, 0), 24) # Sharp multiplier
rn = min(max(rn, 0), 20) # Luma ReNoise strength
deband = min(max(deband, 0), 5) # Apply deband/veed
depth = min(max(depth, 0), 5) # Depth enhancement
strength = min(max(strength, 0), 20) # Strength of denoising

sc = defH>2800 ? 8 : defH>1400 ? 4 : defH>720 ? 2 : 1
blksize = sc==8 ? 8 : ((defH/sc)/360)>1.5 ? 16 : ((defH/sc)/360)>0.8 ? 12 : 8
overlap = blksize>=12 ? 6 : 2
pel = defH>720 ? 1 : 2
truemotion = defH>20 ? false : True
lambda = 775*(blksize*blksize)/64
sharp = sharp>20 ? sharp+30 : DefH<=2600 ? 16+round(defH*(34/2600)*sharp/20) : 50
depth = depth*2
depth2 = -depth*3

sharp = sharp > 20 ? sharp + 30 : defH <= 2500 ? 15 + defH * sharp * 0.0007 : 50


# Denoise preparation
c = chroma ? Median(c, yy=false, uu=true, vv=true) : c
c = !(sisvfloat || icalc) ? c.fmtc_bitdepth(flt=true) : c
cy = ExtractY(c)

# Temporal luma noise filter
super1 = MSuper (chroma ? c : cy, hpad=blksize, vpad=blksize, rfilter=4, sharp=1)
super2 = MSuper (chroma ? c : cy, hpad=blksize, vpad=blksize, rfilter=1, levels=1)

# --> Analysis
bvec4 = !icalc ? MRecalculate(super1, MAnalyse(super1, isb=true, delta=4, blksize=blksize, overlap=overlap, search=5, truemotion=truemotion),
\ blksize=blksize, overlap=overlap, search=5, truemotion=truemotion, lambda=lambda, thSAD=180) : nop()
bvec3 = MRecalculate(super1, MAnalyse(super1, isb=true, delta=3, blksize=blksize, overlap=overlap, search=5, truemotion=truemotion),
\ blksize=blksize, overlap=overlap, search=5, truemotion=truemotion, lambda=lambda, thSAD=180)
bvec2 = MRecalculate(super1, MAnalyse(super1, isb=true, delta=2, badsad=1100, lsad=1120, blksize=blksize, overlap=overlap, search=5, truemotion=truemotion),
\ blksize=blksize, overlap=overlap, search=5, truemotion=truemotion, lambda=lambda, thSAD=180)
bvec1 = MRecalculate(super1, MAnalyse(super1, isb=true, delta=1, badsad=1500, lsad=980, badrange=27, blksize=blksize, overlap=overlap, search=5, truemotion=truemotion),
\ blksize=blksize, overlap=overlap, search=5, truemotion=truemotion, lambda=lambda, thSAD=180)
fvec1 = MRecalculate(super1, MAnalyse(super1, isb=false, delta=1, badsad=1500, lsad=980, badrange=27, blksize=blksize, overlap=overlap, search=5, truemotion=truemotion),
\ blksize=blksize, overlap=overlap, search=5, truemotion=truemotion, lambda=lambda, thSAD=180)
fvec2 = MRecalculate(super1, MAnalyse(super1, isb=false, delta=2, badsad=1100, lsad=1120, blksize=blksize, overlap=overlap, search=5, truemotion=truemotion),
\ blksize=blksize, overlap=overlap, search=5, truemotion=truemotion, lambda=lambda, thSAD=180)
fvec3 = MRecalculate(super1, MAnalyse(super1, isb=false, delta=3, blksize=blksize, overlap=overlap, search=5, truemotion=truemotion),
\ blksize=blksize, overlap=overlap, search=5, truemotion=truemotion, lambda=lambda, thSAD=180)
fvec4 = !icalc ? MRecalculate(super1, MAnalyse(super1, isb=false, delta=4, blksize=blksize, overlap=overlap, search=5, truemotion=truemotion),
\ blksize=blksize, overlap=overlap, search=5, truemotion=truemotion, lambda=lambda, thSAD=180) : nop()

# --> Applying cleaning
clean = !icalc ? MDegrain4(chroma ? c : cy, super2, bvec1, fvec1, bvec2, fvec2, bvec3, fvec3, bvec4, fvec4, thSAD=thSAD) : MDegrain3(chroma ? c : cy, super2, bvec1, fvec1, bvec2, fvec2, bvec3, fvec3, thSAD=thSAD)
c = bd != outbits ? c.fmtc_bitdepth(outbits,dmode=1) : c
cy = bd != outbits ? cy.fmtc_bitdepth(outbits,dmode=1) : cy
clean = bd != outbits ? clean.fmtc_bitdepth(outbits,dmode=1) : clean
uv = chroma ? mt_adddiff(clean, neo_tmedian(mt_makediff(c, clean, y=1, u=3, v=3), 1, y=1 , u=3, v=3), y=1 , u=3, v=3) : c
clean = ExtractY(clean)

# Post clean, pre-process deband
filt = CombinePlanes(clean, uv, planes="YUV", sample_clip=c)
filt = deband==0 ? filt : neo_f3kdb(range=16, preset=chroma ? "high" : "luma", grainy=defH/15, grainc=chroma ? defH/16 : 0, output_depth=outbits)
clean = deband==0 ? clean : ExtractY(filt)
filt = deband == 2 ? veed(filt) : filt

# Spatial luma denoising
clean2 = removegrain(clean, rgmode)

# Unsharp filter for spatial detail enhancement
clsharp = sharp>0 ? sharp <=50 ? mt_makediff(clean, Blur(clean2, 0.08+0.03*sharp,0)) :
\ mt_makediff(clean, clean2.vsTCanny(sigmaY=(sharp-46)/4, mode=-1)) : nop()
clsharp = sharp>0 ? mt_adddiff(clean2, repair(neo_tmedian(clsharp).ExtractY(), clsharp, 12)) : nop()

# If selected, combining ReNoise
noise_diff = mt_makediff (clean2, cy)
clean2 = rn>0<=20 ? mt_merge(clean2, mergeluma (clean2, mt_adddiff(clean2, tweak(clense(noise_diff, reduceflicker=true), cont=1.008+(0.0032*(rn/20)))),
\ 0.3+(rn*0.035)), mt_lut (overlay(clean, invert(clean), mode="darken"), "x 32 scaleb < 0 x 45 scaleb > range_max 0 x 35 scaleb - range_max 32 scaleb 65 scaleb - / * - ? ?")) : clean2

# Combining spatial detail enhancement with spatial noise reduction using prepared mask
noise_diff = mt_invert(mt_binarize(noise_diff))
clean2 = sharp>0 ? mt_merge (clean2, clsharp, overlay(noise_diff, mt_edge(clean, "prewitt"), mode="lighten")) :
\ mt_merge (clean2, clean, overlay(noise_diff, mt_edge(clean, "prewitt"), mode="lighten"))

# Combining result of luma and chroma cleaning
output = CombinePlanes(clean2, filt, planes="YUV", sample_clip=c)
output = strength<20 ? Merge(c, output, 0.2+(0.04*strength)) : output
output = depth>0 ? mt_adddiff(output, spline36resize(mt_makediff(awarpsharp2(output, depth=depth2, blur=3),
\ awarpsharp2(output, depth=depth, blur=2)), output.width, output.height)) : output

return output
}

didn't test it much, so test it, also there are small bug https://github.com/HomeOfAviSynthPlusEvolution/neo_TMedian/issues/2 but I did a workaround for it

real.finder
27th September 2021, 18:20
update and bugfix and much better clone of vs port

# backport of vs mClean that originally a rip-off of buggy avs mClean
# aside from usual differences that come with vs rip-off of avs this one is indeed act differently from the original avs mClean
# since original one has bug in mt_lut line that were hidden in old versions of masktools2 but in recent updates of masktools2 there is an error message for such case
# v1.11
function VSmClean(clip c, int "thSAD", bool "chroma", int "sharp", int "rn", int "deband", int "depth", float "strength", int "outbits", bool "icalc", int "rgmode")
{

defH = Max (C.Height, C.Width/4*3) # Resolution calculation for auto blksize settings
thSAD = Default (thSAD, 400) # Denoising threshold
chroma = Default (chroma, true) # Process chroma
sharp = Default (sharp, 10) # Sharp multiplier
rn = Default (rn, 14) # Luma ReNoise strength from 0 (disabled) to 20
deband = Default (deband, 0) # Apply deband/veed and/or auto balance
depth = Default (depth, 0) # Depth enhancement
strength = Default (strength, 20) # Strength of denoising.
rgmode = Default (rgmode, 18) # Strength of denoising.
bd = BitsPerComponent(c)
sisvfloat = isvideofloat(c)
icalc = sisvfloat ? false : Default(icalc, true)
outbits = Default (outbits, bd) # Output bits, default input depth
deband = depth !=0 || deband != 0 ? min(outbits, 16) : deband

Assert(isYUV(c)==true, """mClean: This is not a YUV clip!""")
Assert(isYUY2(c)==false, """mClean: YUY2 not Supported""")

sharp = min(max(sharp, 0), 24) # Sharp multiplier
rn = min(max(rn, 0), 20) # Luma ReNoise strength
deband = min(max(deband, 0), 5) # Apply deband/veed
depth = min(max(depth, 0), 5) # Depth enhancement
strength = min(max(strength, 0), 20) # Strength of denoising

sc = defH>2800 ? 8 : defH>1400 ? 4 : defH>720 ? 2 : 1
blksize = sc==8 ? 8 : ((defH/sc)/360)>1.5 ? 16 : ((defH/sc)/360)>0.8 ? 12 : 8
overlap = blksize>=12 ? 6 : 2
pel = defH>720 ? 1 : 2
truemotion = defH>20 ? false : True
lambda = 777*(blksize*blksize)/64
sharp = sharp>20 ? sharp+30 : DefH<=2600 ? 16+round(defH*(34/2600)*sharp/20) : 50
depth = depth*2
depth2 = -depth*3

sharp = sharp > 20 ? sharp + 30 : defH <= 2500 ? 15 + defH * sharp * 0.0007 : 50


# Denoise preparation
c = chroma ? Median(c, yy=false, uu=true, vv=true) : c
c = !(sisvfloat || icalc) ? c.fmtc_bitdepth(flt=true) : c
cy = ExtractY(c)

# Temporal luma noise filter
super1 = MSuper (chroma ? c : cy, hpad=blksize, vpad=blksize, rfilter=4, sharp=1)
super2 = MSuper (chroma ? c : cy, hpad=blksize, vpad=blksize, rfilter=1, levels=1)

# --> Analysis
bvec4 = !icalc ? MRecalculate(super1, MAnalyse(super1, isb=true, delta=4, blksize=blksize, overlap=overlap, search=5, truemotion=truemotion),
\ blksize=blksize, overlap=overlap, search=5, truemotion=truemotion, lambda=lambda, thSAD=180) : nop()
bvec3 = MRecalculate(super1, MAnalyse(super1, isb=true, delta=3, blksize=blksize, overlap=overlap, search=5, truemotion=truemotion),
\ blksize=blksize, overlap=overlap, search=5, truemotion=truemotion, lambda=lambda, thSAD=180)
bvec2 = MRecalculate(super1, MAnalyse(super1, isb=true, delta=2, badsad=1100, lsad=1120, blksize=blksize, overlap=overlap, search=5, truemotion=truemotion),
\ blksize=blksize, overlap=overlap, search=5, truemotion=truemotion, lambda=lambda, thSAD=180)
bvec1 = MRecalculate(super1, MAnalyse(super1, isb=true, delta=1, badsad=1500, lsad=980, badrange=27, blksize=blksize, overlap=overlap, search=5, truemotion=truemotion),
\ blksize=blksize, overlap=overlap, search=5, truemotion=truemotion, lambda=lambda, thSAD=180)
fvec1 = MRecalculate(super1, MAnalyse(super1, isb=false, delta=1, badsad=1500, lsad=980, badrange=27, blksize=blksize, overlap=overlap, search=5, truemotion=truemotion),
\ blksize=blksize, overlap=overlap, search=5, truemotion=truemotion, lambda=lambda, thSAD=180)
fvec2 = MRecalculate(super1, MAnalyse(super1, isb=false, delta=2, badsad=1100, lsad=1120, blksize=blksize, overlap=overlap, search=5, truemotion=truemotion),
\ blksize=blksize, overlap=overlap, search=5, truemotion=truemotion, lambda=lambda, thSAD=180)
fvec3 = MRecalculate(super1, MAnalyse(super1, isb=false, delta=3, blksize=blksize, overlap=overlap, search=5, truemotion=truemotion),
\ blksize=blksize, overlap=overlap, search=5, truemotion=truemotion, lambda=lambda, thSAD=180)
fvec4 = !icalc ? MRecalculate(super1, MAnalyse(super1, isb=false, delta=4, blksize=blksize, overlap=overlap, search=5, truemotion=truemotion),
\ blksize=blksize, overlap=overlap, search=5, truemotion=truemotion, lambda=lambda, thSAD=180) : nop()

# --> Applying cleaning
clean = !icalc ? MDegrain4(chroma ? c : cy, super2, bvec1, fvec1, bvec2, fvec2, bvec3, fvec3, bvec4, fvec4, thSAD=thSAD) : MDegrain3(chroma ? c : cy, super2, bvec1, fvec1, bvec2, fvec2, bvec3, fvec3, thSAD=thSAD)
c = bd != outbits ? c.fmtc_bitdepth(outbits,dmode=1) : c
cy = bd != outbits ? cy.fmtc_bitdepth(outbits,dmode=1) : cy
clean = bd != outbits ? clean.fmtc_bitdepth(outbits,dmode=1) : clean
uv = chroma ? mt_adddiff(clean, neo_tmedian(mt_makediff(c, clean, y=1, u=3, v=3), 1, y=1 , u=3, v=3), y=1 , u=3, v=3) : c
clean = ExtractY(clean)

# Post clean, pre-process deband
filt = CombinePlanes(clean, uv, planes="YUV", sample_clip=c)
filt = deband==0 ? filt : filt.neo_f3kdb(range=16, preset=chroma ? "high" : "luma", grainy=defH/15, grainc=chroma ? defH/16 : 0, output_depth=outbits)
clean = deband==0 ? clean : ExtractY(filt)
filt = deband == 2 ? veed(filt) : filt

# Spatial luma denoising
clean2 = removegrain(clean, rgmode)

# Unsharp filter for spatial detail enhancement
clsharp = sharp>0 ? sharp <=50 ? mt_makediff(clean, Blur(clean2, 0.08+0.03*sharp,0)) :
\ mt_makediff(clean, clean2.vsTCanny(sigmaY=(sharp-46)/4, mode=-1)) : nop()
clsharp = sharp>0 ? mt_adddiff(clean2, repair(neo_tmedian(clsharp), clsharp, 12)) : nop()

# If selected, combining ReNoise
noise_diff = mt_makediff (clean2, cy)
clean1 = rn>0<=20 ? mergeluma(clean2, mt_adddiff(clean2, tweak(neo_tmedian(noise_diff), cont=1.008+0.00016*rn)), 0.3+rn*0.035) : nop()
clean2 = rn>0<=20 ? mt_merge(clean2, clean1, mt_lut(overlay(clean, invert(clean), mode="darken"), "x 32 scaleb < 0 x 45 scaleb > range_max 0 x 35 scaleb - range_max 32 scaleb 65 scaleb - / * - ? ?", use_expr=2)) : clean2

# Combining spatial detail enhancement with spatial noise reduction using prepared mask
noise_diff = mt_invert(mt_binarize(noise_diff))
clean2 = sharp>0 ? mt_merge (clean2, clsharp, overlay(noise_diff, mt_edge(clean, "prewitt"), mode="lighten")) :
\ mt_merge (clean2, clean, overlay(noise_diff, mt_edge(clean, "prewitt"), mode="lighten"))

# Combining result of luma and chroma cleaning
output = CombinePlanes(clean2, filt, planes="YUV", sample_clip=c)
output = strength<20 ? Merge(c, output, 0.2+0.04*strength) : output
output = depth>0 ? mt_adddiff(output, mt_makediff(awarpsharp2(output, depth=depth2, blur=3), awarpsharp2(output, depth=depth, blur=2))) : output

return output
}

MysteryX
27th September 2021, 19:57
update and bugfix and much better clone of vs port
What's the difference between Avisynth and VapourSynth versions?

real.finder
28th September 2021, 00:20
What's the difference between Avisynth and VapourSynth versions?

It is better to say what is the similarity

aside from mt_lut line bug in avs mClean, there are differences in default settings, how clip passed from used filter to another, and even some constant numbers, not mention using different filters like bluring and missing things in vs side like autoadjust as I already said https://forum.doom9.org/showthread.php?p=1952785#post1952785

so VapourSynth mClean like mClean2 or mCleanLite compared to Avisynth version

you can compare differences between VSmClean I made and the old one in OP

MysteryX
28th September 2021, 03:23
Interesting difference between Avisynth and VapourSynth.

Original
https://i.postimg.cc/jC5G6bbV/Original.png (https://i.postimg.cc/C091bdjb/Original.png)

Avisynth
https://i.postimg.cc/B8vdf4Ps/m-Clean-avs.png (https://i.postimg.cc/BJkvr8kC/m-Clean-avs.png)

VapourSynth
https://i.postimg.cc/CR7W2z1m/m-Clean-vy.png (https://i.postimg.cc/nnCzSKRz/m-Clean-vy.png)

btw the xClean posted previously seemed to be wrong, I fixed the image link.

Avisynth version is a lot more aggressive. VapourSynth preserves a lot more the details. I like the VapourSynth version better.

Yes, both achieve a very different work.

real.finder
28th September 2021, 04:45
did you try VSmClean I made in avs or mClean in vs? in anyway they should be same unless I missed something

avs mClean v3.2 (01 March 2018) should not used unless the mt_lut line fixed by replace

\ 0.3+(rn*0.035)), mt_lut (overlay(clean, invert(clean), mode="darken"), "x 32 scaleb < 0 x 45 scaleb > range_max 0 x 35 scaleb - range_max 32
\ scaleb 65 scaleb - / * - ? ?")) : clean2

with

\ 0.3+(rn*0.035)), mt_lut (overlay(clean, invert(clean), mode="darken"), "x 32 scaleb < 0 x 45 scaleb > range_max 0 x 35 scaleb - range_max 32 scaleb 65 scaleb - / * - ? ?")) : clean2

MysteryX
28th September 2021, 06:26
What's the difference between those 2 lines? looks identical to me

real.finder
28th September 2021, 20:48
What's the difference between those 2 lines? looks identical to me

obviously one line replace the two, I can fix it with keep them two but I wont bother, in last avs+ expr one can make fix this situation easily https://github.com/AviSynth/AviSynthPlus/commit/a11711d43b1420fbf9b432dfcd44d95b4ebc68dd maybe mt_lut* should be updated like this too

MysteryX
28th September 2021, 23:35
The other page is Resize, based zimg, based on h265 document. The presence of full range flag is 1, absence is 0

Internal frame props are not the same thing. Avisynth frame props were based on vapoursynth frame props and they have 0 as full too
http://avisynth.nl/index.php/Internal_functions#Functions_for_frame_properties

But yes, it's inconsistent
Wow, calling resize with range=1 sets _ColorRange=0, and calling with range=0 sets _ColorRange=1. Good to know!

MysteryX
29th September 2021, 04:34
xClean now fully support MVTools (0), KNLMeansCL (1) and BM3D (2) methods with renoising and sharpening, and can then apply KNLMeans (1) or BM3D (2) with the first method as ref using finalm.

I find that all 3 methods benefit from renoising and sharpening from xClean. All are running with sharp=20, str=20.

Testing out all the combinations. Using settings:
Method 0 (MVTools2, VapourSynth version): thSAD=400
Method 1 (KNLMeansCL): d=3, a=2, h=1.4, processing Y and UV separately
Method 2 (BM3D): sigma=9, radius=1, processing in YUV444PS

Frame comparison: Driving (https://slow.pics/c/G7BpFepI)

Comparing method 0, 1, 2. BM3D fails to remove the noise grain. Overall, I'd say the winner is KNLMeansCL.
Using finalm=1 (KNLMeans), between method 0 and 2, it's a very close call... I'd say the winner is Method0-Final1, the seat looks a little bit better and it removes more grain
Using finalm=2 (BM3D), between method 0 and 1, KNLMeans keeps sharp details, I like it. I'd say the winner is Method1-Final2
Between the 3 winners, Method1-Final2 gives sharper details, but Method0-Final1 removes more grain.
Winner: Method0-Final1

I will do the same comparison on other types of content.

Frame comparison: Pirate's Cave (https://slow.pics/c/ynJvsNuD)

Comparing method 0, 1, 2. MVTools blurs it all out. KNLMeansCL and BM3D are very very similar, hard to notice any difference at all... but BM3D has a tiny bit more grains and/or details. Though call.
Using finalm=1 (KNLMeansCL), between method 0 and 2. 2 avoids the blurry mess. Winner is Method2-Final1!
Using finalm=2 (BM3D), between method 0 and 1. Same, winner is Method1-Final2.
Comparing the 3 winners... it's kind of weird. All 3 are super super close, but at the end of the day, Method2-Final1 softens a bit more bad grains while retaining the details. You need to look very closely though.
Actually scrap that. Method0-Final2 softens the grain more, that is the winner.

Interesting observation here. Adding more 'h' to KNLMeansCL doesn't make it better, just more blurry. Adding more 'sigma' to BM3D doesn't make it better, it just gives more defined edges. h=1.4 and sigma=9 seem to be magic numbers for those 5K footage at least.

Frame comparison: Mexican Fog (https://slow.pics/c/TpRtML8t)

Noise + Window + Fog

Comparing method 0, 1 and 2. MVTools does a very fine job. KNLMeansCL looks abit artificial and also keeps a lot of grain. BM3D... WOW! Winner.
Using finalm=1, between method 0 and 2. I like Method2-Final1 better.
Using finalm=2, between method 0 and 1. Noise is the same, and Method1-Final2 keeps a bit more details.
Comparing the 3 winners, Method1-Final2 definitely gives a more defined image that looks more natural.
Clear winner: Method1-Final2

It is curious that each of those clips has a different winner combination. BM3D isn't so useful in most decent-quality clips, but I'm surprised at how it performs with the fog!

Doing an encoding run with Method0-Final2 with radius=1 on 5K video. Runs at 0.49fps here. Memory usage fluctuates between 7GB and 11GB in Task Manager.

Frame comparison: Cliff & Water (https://slow.pics/c/yz3HKsQJ)

Comparing method 0, 1 and 2. MVTools blurs out the water and the cliff. KNLMeans makes it look like plastic. BM3D wins.
Using finalm=1, between method 0 and 2. I like the cliff with Method2-Final1 better, and I like the water with Method0-Final1 better. Though call...
Using finalm=2, between method 0 and 1. It's close, but I don't like the slight cliff blur of 0. Method1-Final2 wins.
Winner: Method1-Final2

Frame comparison: WebCam (https://slow.pics/c/Jk3UxIPF)

Comparing method 0, 1 and 2. MVTools blur out too much and BM3D leaves too much grain. KNLMeans wins.
Using finalm=1, between method 0 and 2. Method0-Final1 wins.
Using finalm=2, between method 0 and 1. Method1-Final2 wins.
Winner: Method0-Final1

Conclusion: Method0-Final1 works best for general purposes. Method1-Final2 works best for delicate textures such as water, fog, cliffs, etc. Method0-Final2 works best for dark scenes.

Thanks to Burdafel for his renoising & sharpening idea. Renoising & sharpening has been changed by someone while porting to VapourSynth; and I'm changing the rest. Not much left of the original mClean isn't it?

Edit: when using BM3D with ref, the output looks fine frame-by-frame, but the noise grain remains too chaotic while in motion. I'll keep testing.

MysteryX
30th September 2021, 20:21
After more testing. With MVTools now allowing taking other filters as ref for analysis.

BM3D takes very little from ref clip and gives poor temporal stability. For that reason, even though it may look fine on single frames, it's no a good choice to run it with ref clip.

MVTools gains a bit of motion vector precision by using ref clip but it doesn't contribute much to output. Passing a complex analysis to it as ref is also a waste.

KNLMeansCL takes a lot from ref clip and generally gives better output than its ref clip alone. It's generally the best final method. I haven't yet seen cases where passing a ref clip gives worse output than ref alone.

MvTools feeding into KNLMeans is generally what's best. In some cases where BM3D actually does a good job, like fog and delicate textures, feeding BM3D into KNLMeans works. These seem to be the 2 combinations.

ChaosKing
30th September 2021, 21:04
This is more or less what I also experienced while playing around these filters. I like how "forgiving" smdegrain is even with a bad prefilter + it can help hiding artifacts generated by prefilters like SpotLess or a heavy deblocking filter.
Then feeding this into knlmeans or bm3d can lead to even better quality.

Have you tested bm3d with a larger radius? Could be too slow for 5K material :D

MysteryX
30th September 2021, 23:26
This is more or less what I also experienced while playing around these filters. I like how "forgiving" smdegrain is even with a bad prefilter + it can help hiding artifacts generated by prefilters like SpotLess or a heavy deblocking filter.
Then feeding this into knlmeans or bm3d can lead to even better quality.

Have you tested bm3d with a larger radius? Could be too slow for 5K material :D
Very very slight benefit with radius=1; not worth it IMO, especially if used as prefilter.

It kills my computer with 16GB ram with radius=2. Sometimes it works (zzzzz), sometimes I get BSOD.

Comparing the fog video with BM3D fed into KNLMeansCL, the difference with plain KNLMeansCL is actually very very subtle -- hard to notice frame-by-frame. But when watching in motion, the KNLMeansCL video feels like plastic, whereas the video with BM3D+KNLMeans+renoise+sharp looks natural with the same amount of noise.

Made further tests with BM3D as prefilter for KNLMeansCL. RemoveGrain after BM3D helps. Slightly sharpening BM3D helps. Renoising the prefilter helps make it look more natural. Those made more difference than I expecdted.

MysteryX
1st October 2021, 02:53
About radius 0 vs 1, the difference is very subtle. I'm not seeing any difference on the noise itself. The difference I see is that tree edges are square with radius=0, and softer with radius=1. Once you notice that detail, it does make it feel more natural. With KNL d= 3 vs 2, the difference is also subtle, but there's a very slight improvement. You need to look with a microscope.

As comparison, here are the file sizes of processed videos
d=2, radius=0: 171 240 KB
d=2, radius=1: 171 219 KB
d=3, radius=0: 169 741 KB
d=3, radius=1: 169 732 KB

d=2 runs at 1.6 fps, d=3 runs at 1.1 fps, d=3/radius=1 runs at 0.65 fps

d=3 helps with compressibility. radius=1 helps with softer edges. No impact on noise.

and the weird thing is -- all this is kind of non-adjustable strength. Increasing KNL h only makes it blurrier and doesn't improve on noise. Increasing BM3D sigma only makes the edges sharper, it doesn't improve on noise either.

I also tested KNL chroma encoding. When processing luma and chroma separately, h=1.4 works best for luma, and half that (.7) for chroma. A bit more or a bit less on chroma gives worse results.

Now for the crazy overkill test: MVTools as prefilter for BM3D as prefilter for KNLMeans..... 0.7 fps with d=2, radius=0. File size: 127 450 KB holy smoke it blurred things out big times

MysteryX
2nd October 2021, 06:35
OK now I got a problem.

BM3D always look better with MVTools as prefilter otherwise it doesn't filter enough.

KNLMeans always look better with BM3D (or MVTools) as prefilter otherwise it looks plastic.

Both BM3D and KNLMeans benefit from running KNLMeans after it.

So the most consistent output is to run MVTools, then BM3D, then KNLMeans, then x265. Way overkill.

Otherwise, most content work very well with MVTools+KNLMeans, including dark scenes. There is a slight gain of details by adding BM3D in-between.

Videos with delicate textures (water, fog, etc.) work very well with BM3D+KNLMeans; but then dark scenes require MVTools prefilter. I got a video that would require BM3D and also MVTools for dark scenes. So either I detect dark scenes to switch prefilter, or I run all 3 and don't worry about it.

If run as a prefilter to a prefilter... is there a way to optimize MVTools for performance?

Then also comes the question: do I get better quality by running 2 denoisers in quality mode, or 3 denoisers in performance mode? Cutting corners with KNL d and BM3D radius.

Then, what's the quality difference between NVENC highest preset and x265 fast preset? Because that's another big CPU hog. Is NVENC acceptable?

zorr
2nd October 2021, 14:28
If run as a prefilter to a prefilter... is there a way to optimize MVTools for performance?

It's possible to use Zopti for searching best quality / performance settings. The way to do that is to first create a reference video using whatever parameters give the absolutely best quality disregarding performance completely. You need to find these settings manually as Zopti cannot evaluate the quality without a reference video.

Then you let Zopti find faster settings which output very similar results as your best quality video. The results would contain speed / quality pairs with increasing similarity to reference and decreasing speed. From those you can then select the best compromises. You may need to add more adjustable parameters for MVTools in order to get most benefit from the search (but that also makes the search slower).

Then also comes the question: do I get better quality by running 2 denoisers in quality mode, or 3 denoisers in performance mode? Cutting corners with KNL d and BM3D radius.

Zopti can test this too.

MysteryX
3rd October 2021, 08:42
Chaining MVTools -> BM3D -> KNLMeans is working surprisingly well!

Posted benchmark and screenshots here. (https://forum.doom9.org/showthread.php?p=1953732#post1953732)

The surprising part is that the denoising strength is non-adjustable; yet it performs well on any type of content that I've tested. Cliff, water and dark cave, looks clean and natural. WebCam with heavy noise, same settings take out a LOT of noise while preserving a surprising amount of details.

MysteryX
5th October 2021, 03:54
My research and work with the denoisers is going very well. Released xClean beta 2 for VapourSynth. (https://github.com/mysteryx93/xClean/blob/main/xClean.py)

According to my tests, it's doing really well in removing heavy grain from webcam. It's also doing well at removing some noise from HD videos while preserving clear image quality.

Where it struggles, though, is with large noise patterns. Denoisers work with surrounding pixels, and when looking at 5K video on a 1080p display, surrounding pixels don't affect visible pixels, and large noise patterns are thus very difficult to remove with all methods that I tested.

vcm.Median was designed to remove impulsive noise in larger radius like that, but it doesn't improve much the output and is terribly slow.

Any idea on how I could improve on large-radius noise patterns, particularly for 4K and 5K content?

Dogway
5th October 2021, 08:28
You can use a large radius undot style median. Coincidence because I made it last night called 'unblob3', still not released in ExTools.

Expr(last, "x[-2,-2] x[-2,-1] dup1 dup1 min W^ max X^
x[-2,0] x[-2,1] dup1 dup1 min U^ max V^
x[-2,2] x[-1,-2] dup1 dup1 min S^ max T^
x[-1,-1] x[-1,0] dup1 dup1 min Q^ max R^
x[-1,1] x[-1,2] dup1 dup1 min O^ max P^
x[0,-2] x[0,-1] dup1 dup1 min M^ max N^
x[0,1] x[0,2] dup1 dup1 min K^ max L^
x[1,-2] x[1,-1] dup1 dup1 min I^ max J^
x[1,0] x[1,1] dup1 dup1 min G^ max H^
x[1,2] x[2,-2] dup1 dup1 min E^ max F^
x[2,-1] x[2,0] dup1 dup1 min C^ max D^
x[2,1] x[2,2] dup1 dup1 min A^ max B^
V X dup1 dup1 min V^ max X^
U W dup1 dup1 min U^ max W^
R T dup1 dup1 min R^ max T^
Q S dup1 dup1 min Q^ max S^
N P dup1 dup1 min N^ max P^
M O dup1 dup1 min M^ max O^
J L dup1 dup1 min J^ max L^
I K dup1 dup1 min I^ max K^
F H dup1 dup1 min F^ max H^
E G dup1 dup1 min E^ max G^
B D dup1 dup1 min B^ max D^
A C dup1 dup1 min A^ max C^
T X dup1 dup1 min T^ max X^
S W dup1 dup1 min S^ max W^
P V dup1 dup1 min P^ max V^
O U dup1 dup1 min O^ max U^
N R dup1 dup1 min N^ max R^
M Q dup1 dup1 min M^ max Q^
H L dup1 dup1 min H^ max L^
G K dup1 dup1 min G^ max K^
D J dup1 dup1 min D^ max J^
C I dup1 dup1 min C^ max I^
B F dup1 dup1 min B^ max F^
A E dup1 dup1 min A^ max E^
V X dup1 dup1 min V^ max X^
U W dup1 dup1 min U^ max W^
R T dup1 dup1 min R^ max T^
Q S dup1 dup1 min Q^ max S^
N P dup1 dup1 min N^ max P^
M O dup1 dup1 min M^ max O^
J L dup1 dup1 min J^ max L^
I K dup1 dup1 min I^ max K^
F H dup1 dup1 min F^ max H^
E G dup1 dup1 min E^ max G^
B D dup1 dup1 min B^ max D^
A C dup1 dup1 min A^ max C^
L X min L^
K W dup1 dup1 min K^ max W^
T V dup1 dup1 min T^ max V^
S U dup1 dup1 min S^ max U^
P R dup1 dup1 min P^ max R^
O Q dup1 dup1 min O^ max Q^
B N dup1 dup1 min B^ max N^
A M max M^
H J dup1 dup1 min H^ max J^
G I dup1 dup1 min G^ max I^
D F dup1 dup1 min D^ max F^
C E dup1 dup1 min C^ max E^
L W dup1 dup1 min L^ max W^
J V dup1 dup1 min J^ max V^
I U dup1 dup1 min I^ max U^
H T max T^
G S min G^
F R max R^
E Q min E^
D P dup1 dup1 min D^ max P^
C O dup1 dup1 min C^ max O^
B M dup1 dup1 min B^ max M^
V W min V^
J U max U^
R T dup1 dup1 min R^ max T^
K P max P^
D O min D^
I N min I^
E G dup1 dup1 min E^ max G^
B C max C^
R U max U^
L P max P^
I M min I^
D G min D^
U V max P T max max
E I min C D min min

x swap2 clip","")

MysteryX
5th October 2021, 15:57
You can use a large radius undot style median. Coincidence because I made it last night called 'unblob3', still not released in ExTools.
It's not doing anything on the type of large-scale noise I'm dealing with. So far MVTools is the only one dealing with that.

Dogway
5th October 2021, 23:43
You can try with "median7" or "unblob3D" when I release it. It depends on whether you want to remove grain or impulse noise.

real.finder
6th October 2021, 00:51
did you try VSmClean I made in avs or mClean in vs? in anyway they should be same unless I missed something

avs mClean v3.2 (01 March 2018) should not used unless the mt_lut line fixed by replace

\ 0.3+(rn*0.035)), mt_lut (overlay(clean, invert(clean), mode="darken"), "x 32 scaleb < 0 x 45 scaleb > range_max 0 x 35 scaleb - range_max 32
\ scaleb 65 scaleb - / * - ? ?")) : clean2

with

\ 0.3+(rn*0.035)), mt_lut (overlay(clean, invert(clean), mode="darken"), "x 32 scaleb < 0 x 45 scaleb > range_max 0 x 35 scaleb - range_max 32 scaleb 65 scaleb - / * - ? ?")) : clean2

with last update of masktools https://github.com/pinterf/masktools/issues/21#issuecomment-934372767
this

\ 0.3+(rn*0.035)), mt_lut (overlay(clean, invert(clean), mode="darken"), "x 32 scaleb < 0 x 45 scaleb > range_max 0 x 35 scaleb - range_max 32
scaleb 65 scaleb - / * - ? ?")) : clean2

should do it

real.finder
7th October 2021, 08:15
update and bugfix and much better clone of vs port

# backport of vs mClean that originally a rip-off of buggy avs mClean
# aside from usual differences that come with vs rip-off of avs this one is indeed act differently from the original avs mClean
# since original one has bug in mt_lut line that were hidden in old versions of masktools2 but in recent updates of masktools2 there is an error message for such case
# v1.11
function VSmClean(clip c, int "thSAD", bool "chroma", int "sharp", int "rn", int "deband", int "depth", float "strength", int "outbits", bool "icalc", int "rgmode")
{

defH = Max (C.Height, C.Width/4*3) # Resolution calculation for auto blksize settings
thSAD = Default (thSAD, 400) # Denoising threshold
chroma = Default (chroma, true) # Process chroma
sharp = Default (sharp, 10) # Sharp multiplier
rn = Default (rn, 14) # Luma ReNoise strength from 0 (disabled) to 20
deband = Default (deband, 0) # Apply deband/veed and/or auto balance
depth = Default (depth, 0) # Depth enhancement
strength = Default (strength, 20) # Strength of denoising.
rgmode = Default (rgmode, 18) # Strength of denoising.
bd = BitsPerComponent(c)
sisvfloat = isvideofloat(c)
icalc = sisvfloat ? false : Default(icalc, true)
outbits = Default (outbits, bd) # Output bits, default input depth
deband = depth !=0 || deband != 0 ? min(outbits, 16) : deband

Assert(isYUV(c)==true, """mClean: This is not a YUV clip!""")
Assert(isYUY2(c)==false, """mClean: YUY2 not Supported""")

sharp = min(max(sharp, 0), 24) # Sharp multiplier
rn = min(max(rn, 0), 20) # Luma ReNoise strength
deband = min(max(deband, 0), 5) # Apply deband/veed
depth = min(max(depth, 0), 5) # Depth enhancement
strength = min(max(strength, 0), 20) # Strength of denoising

sc = defH>2800 ? 8 : defH>1400 ? 4 : defH>720 ? 2 : 1
blksize = sc==8 ? 8 : ((defH/sc)/360)>1.5 ? 16 : ((defH/sc)/360)>0.8 ? 12 : 8
overlap = blksize>=12 ? 6 : 2
pel = defH>720 ? 1 : 2
truemotion = defH>20 ? false : True
lambda = 777*(blksize*blksize)/64
sharp = sharp>20 ? sharp+30 : DefH<=2600 ? 16+round(defH*(34/2600)*sharp/20) : 50
depth = depth*2
depth2 = -depth*3

sharp = sharp > 20 ? sharp + 30 : defH <= 2500 ? 15 + defH * sharp * 0.0007 : 50


# Denoise preparation
c = chroma ? Median(c, yy=false, uu=true, vv=true) : c
c = !(sisvfloat || icalc) ? c.fmtc_bitdepth(flt=true) : c
cy = ExtractY(c)

# Temporal luma noise filter
super1 = MSuper (chroma ? c : cy, hpad=blksize, vpad=blksize, rfilter=4, sharp=1)
super2 = MSuper (chroma ? c : cy, hpad=blksize, vpad=blksize, rfilter=1, levels=1)

# --> Analysis
bvec4 = !icalc ? MRecalculate(super1, MAnalyse(super1, isb=true, delta=4, blksize=blksize, overlap=overlap, search=5, truemotion=truemotion),
\ blksize=blksize, overlap=overlap, search=5, truemotion=truemotion, lambda=lambda, thSAD=180) : nop()
bvec3 = MRecalculate(super1, MAnalyse(super1, isb=true, delta=3, blksize=blksize, overlap=overlap, search=5, truemotion=truemotion),
\ blksize=blksize, overlap=overlap, search=5, truemotion=truemotion, lambda=lambda, thSAD=180)
bvec2 = MRecalculate(super1, MAnalyse(super1, isb=true, delta=2, badsad=1100, lsad=1120, blksize=blksize, overlap=overlap, search=5, truemotion=truemotion),
\ blksize=blksize, overlap=overlap, search=5, truemotion=truemotion, lambda=lambda, thSAD=180)
bvec1 = MRecalculate(super1, MAnalyse(super1, isb=true, delta=1, badsad=1500, lsad=980, badrange=27, blksize=blksize, overlap=overlap, search=5, truemotion=truemotion),
\ blksize=blksize, overlap=overlap, search=5, truemotion=truemotion, lambda=lambda, thSAD=180)
fvec1 = MRecalculate(super1, MAnalyse(super1, isb=false, delta=1, badsad=1500, lsad=980, badrange=27, blksize=blksize, overlap=overlap, search=5, truemotion=truemotion),
\ blksize=blksize, overlap=overlap, search=5, truemotion=truemotion, lambda=lambda, thSAD=180)
fvec2 = MRecalculate(super1, MAnalyse(super1, isb=false, delta=2, badsad=1100, lsad=1120, blksize=blksize, overlap=overlap, search=5, truemotion=truemotion),
\ blksize=blksize, overlap=overlap, search=5, truemotion=truemotion, lambda=lambda, thSAD=180)
fvec3 = MRecalculate(super1, MAnalyse(super1, isb=false, delta=3, blksize=blksize, overlap=overlap, search=5, truemotion=truemotion),
\ blksize=blksize, overlap=overlap, search=5, truemotion=truemotion, lambda=lambda, thSAD=180)
fvec4 = !icalc ? MRecalculate(super1, MAnalyse(super1, isb=false, delta=4, blksize=blksize, overlap=overlap, search=5, truemotion=truemotion),
\ blksize=blksize, overlap=overlap, search=5, truemotion=truemotion, lambda=lambda, thSAD=180) : nop()

# --> Applying cleaning
clean = !icalc ? MDegrain4(chroma ? c : cy, super2, bvec1, fvec1, bvec2, fvec2, bvec3, fvec3, bvec4, fvec4, thSAD=thSAD) : MDegrain3(chroma ? c : cy, super2, bvec1, fvec1, bvec2, fvec2, bvec3, fvec3, thSAD=thSAD)
c = bd != outbits ? c.fmtc_bitdepth(outbits,dmode=1) : c
cy = bd != outbits ? cy.fmtc_bitdepth(outbits,dmode=1) : cy
clean = bd != outbits ? clean.fmtc_bitdepth(outbits,dmode=1) : clean
uv = chroma ? mt_adddiff(clean, neo_tmedian(mt_makediff(c, clean, y=1, u=3, v=3), 1, y=1 , u=3, v=3), y=1 , u=3, v=3) : c
clean = ExtractY(clean)

# Post clean, pre-process deband
filt = CombinePlanes(clean, uv, planes="YUV", sample_clip=c)
filt = deband==0 ? filt : filt.neo_f3kdb(range=16, preset=chroma ? "high" : "luma", grainy=defH/15, grainc=chroma ? defH/16 : 0, output_depth=outbits)
clean = deband==0 ? clean : ExtractY(filt)
filt = deband == 2 ? veed(filt) : filt

# Spatial luma denoising
clean2 = removegrain(clean, rgmode)

# Unsharp filter for spatial detail enhancement
clsharp = sharp>0 ? sharp <=50 ? mt_makediff(clean, Blur(clean2, 0.08+0.03*sharp,0)) :
\ mt_makediff(clean, clean2.vsTCanny(sigmaY=(sharp-46)/4, mode=-1)) : nop()
clsharp = sharp>0 ? mt_adddiff(clean2, repair(neo_tmedian(clsharp), clsharp, 12)) : nop()

# If selected, combining ReNoise
noise_diff = mt_makediff (clean2, cy)
clean1 = rn>0<=20 ? mergeluma(clean2, mt_adddiff(clean2, tweak(neo_tmedian(noise_diff), cont=1.008+0.00016*rn)), 0.3+rn*0.035) : nop()
clean2 = rn>0<=20 ? mt_merge(clean2, clean1, mt_lut(overlay(clean, invert(clean), mode="darken"), "x 32 scaleb < 0 x 45 scaleb > range_max 0 x 35 scaleb - range_max 32 scaleb 65 scaleb - / * - ? ?", use_expr=2)) : clean2

# Combining spatial detail enhancement with spatial noise reduction using prepared mask
noise_diff = mt_invert(mt_binarize(noise_diff))
clean2 = sharp>0 ? mt_merge (clean2, clsharp, overlay(noise_diff, mt_edge(clean, "prewitt"), mode="lighten")) :
\ mt_merge (clean2, clean, overlay(noise_diff, mt_edge(clean, "prewitt"), mode="lighten"))

# Combining result of luma and chroma cleaning
output = CombinePlanes(clean2, filt, planes="YUV", sample_clip=c)
output = strength<20 ? Merge(c, output, 0.2+0.04*strength) : output
output = depth>0 ? mt_adddiff(output, mt_makediff(awarpsharp2(output, depth=depth2, blur=3), awarpsharp2(output, depth=depth, blur=2))) : output

return output
}

I did compare VSmClean with vs mClean and seems they are not same in output, but at least it's not from neo_tmedian from quick comparison, maybe it's mvtools

MysteryX
7th October 2021, 13:55
MVTools behaves different. In particular, artefact mask output is completely different between Avisynth and VaporuSynth versions.

Gives much cleaner output in VapourSynth. Somehow.

real.finder
7th October 2021, 15:38
I did more tests, mvtools at least for MDegrainX is not a problem (both look same)

I find that vsmclean was kinda hybrid between vs mclean and avs mclean so I did an update https://github.com/realfinder/AVS-Stuff/blob/Community/Others/VSmClean.avsi they are more close now. maybe it need more work, maybe not since I think "VapourSynth Editor" kinda not output same as avspmod

real.finder
8th October 2021, 03:54
MVTools behaves different.

VapourSynth guys did it again! they change things without say so! Avisynth mvtools MSuper defaults for hpad and vpad are 8 but in vs they are 16!

so MSuper(hpad=16, vpad=16) will make avs mvtools act similar to vs one

ChaosKing
8th October 2021, 07:19
The defaults are listed here https://github.com/dubhater/vapoursynth-mvtools#usage

real.finder
8th October 2021, 07:32
The defaults are listed here https://github.com/dubhater/vapoursynth-mvtools#usage

I note it already, but why not also mention them it here https://github.com/dubhater/vapoursynth-mvtools#differences ?

also still not act same https://forum.doom9.org/showpost.php?p=1954243&postcount=685