#----------------------------------------------------------------#
# #
# EdgeClean v1.0, by Vitreous, 2010 #
# #
# Avisynth script to clean up halos, ringing and noise at edges #
# Written to clean up after TempGaussMC, but doesn't require it #
# #
# Notes: #
# - May blur fine detail near edges; measures edge density #
# and will reduce cleaning in complex areas for this reason #
# - Will not remove thick rings/halos #
# #
#----------------------------------------------------------------#
# Requires:
# MaskTools V2
# RemoveGrain
# TUnsharp
# Strength (0-255) : Overall strength of cleaning effect
# Radius (1-...) : Radius outside edge within which to reduce noise / halos / ghosts
# Resharpen (0-512) : Used for strength parameter to TUnsharp - how much to resharpen cleaned areas
# DensityThr (0-511) : Amount of cleaning reduction in areas of high edge density
# DensityDS (1-...) : Downsampling factor for edge density calculation
# EdgeLoThr (0-255) : Low edge detection threshold for detecting larger set of edges used in hysteresis
# EdgeHiThr (0-255) : High edge detection threshold for detecting smaller set of edges used in hysteresis
# Chroma (bool) : Whether to process chroma as well as luma
# ViewMask (0,1,2) : Debugging: 1 = Show cleaning mask, 2 = Show edge density mask (set Strength=255 for mode 2)
function EdgeClean( clip Input, int "Strength", int "Radius", int "Resharpen", int "DensityThr", int "DensityDS", \
int "EdgeLoThr", int "EdgeHiThr", bool "Chroma", int "ViewMask" )
{
Strength = default( Strength, 255 )
Radius = default( Radius, 3 )
Resharpen = default( Resharpen, 200 )
DensityThr = default( DensityThr, 448 )
DensityDS = default( DensityDS, 22 )
EdgeLoThr = default( EdgeLoThr, 120 )
EdgeHiThr = default( EdgeHiThr, 240 )
Chroma = default( Chroma, true )
ViewMask = default( ViewMask, 0 )
w = Input.width()
h = Input.height()
wd = m4( w / DensityDS )
hd = m4( h / DensityDS )
Black = BlankClip(Input)
OuputChroma = (ViewMask == 0) ? (Chroma?3:2) : -128
# Create edge masks (one binary, one with gradient)
Prewitt = Input.mt_edge( "prewitt", 0,255, 0,255 )
Edge = mt_hysteresis( Prewitt.mt_binarize(EdgeHiThr), Prewitt.mt_binarize(EdgeLoThr))
EdgeGrad = mt_merge( Black, Prewitt, Edge, U=1,V=1 ).Levels( EdgeLoThr,1,255 ,0,255, false )
# Create cleaning mask around edges - subtract edge from expanded edge with some blurring
EdgeInner = Edge.mt_inflate().RemoveGrain( 20, -1 )
EdgeOuter = EdgeGrad.mt_expand( mode=mt_square(Radius) ).RemoveGrain( 20, -1 ).RemoveGrain( 20, -1 )
EdgeClean = mt_merge( EdgeOuter, Black, EdgeInner, U=1,V=1 )
# Weaken mask in areas of high edge density
DLo = (DensityThr >= 256) ? DensityThr - 256 : 0
DHi = (DensityThr < 256) ? DensityThr : 255
EdgeDensity = EdgeGrad.BicubicResize( wd,hd ).LanczosResize( w,h ).mt_invert().Levels( DLo,1,DHi, 0,Strength, false )
Mask = (ViewMask == 2) ? EdgeDensity.mt_invert( U=0,V=0 ) : mt_merge( Black, EdgeClean, EdgeDensity, U=0,V=0 )
# Clean / resharpen input within mask
Clean = Input.RemoveGrain( 19, Chroma?19:0 ).TUnsharp( Resharpen, type=2 )
Output = mt_merge( Input, (ViewMask != 0) ? Mask : Clean, Mask, luma=true )
return Output
}
function m4( float x ) { return (x < 16 ? 16 : x - (x % 4)) }