geometer
5th April 2026, 15:20
This is a fairly short function that targets ringing or echo-artifacts from sharp edges,
it can be applied, when you remaster or recompress old DVDs and try some sharpening, eg. with convolutional sharpeners.
The quality level therefor is DVD-related, but regarding Avisynth processing, it was tested with planar formats like YUV444 and 12-bit resolution.
It should be applied on the Luma signal, but you can experiment as you like.
What it does, it analyzes the signal and predicts one artifact to the left and one to the right of a vertical edge.
The placement of the artifact is set by the offset parameters. This means you look at the pixel distance, and set it to 1, 2, or 3, depending on which one is the most disturbing of several echoes.
In some cases, also in upsampled mode, values of 0 or even 4 or 5 may fit the issue.
Tune in on a strongly visible artifact that is repetitive.
(Actually it has dynamic function and works on all parts of the frame that are prone to ringing or to thin echo lines.)
Find its offset, then nudge the weight of the corrective signal.
Different scenes and different artifacts will ideally require different settings, so you want to find some averaged balance.
The result is, properly tweaked, that the video as a whole looks cleaner and more brilliant.
The algorithm avoids blurring.
The predicted artifact thus is being reengineered, and then subtracted from the original.
In other words, you have to set the weight parameters, and monitor the effect on a particular echo or Gibbs ringing structure.
The task is to zero in weight, not too light, not too dark, to make the artifacts less visible.
When your process does some filtering, you may run into sub-pixel issues. If you can't get the offset right, you can try to resample to a higher resolution and apply the function there, with a higher offset.
You can use it freely, but please when you store it somewhere, add the line "Hans' ringing remover".
Example:
(read data with MPEG2Source (), convert to 12-bit planar, whatever Expr() can handle)
last = AntiRingLR(last, "luma", weightL=0.2,offsetL=1,weightR=0.17,offsetR=1,knee=0.7,pr2=0.5,pr3=0.5)
The "output" parameter when specified, is either a format string, or the literal "out=in" (try this in case of error messages), within the limits of Expr()
It was tested with a recent "avisynth plus 64bit" version, please make sure that you run a compatible version.
Thanks, please enjoy, have fun, have success!
function AntiRingLR(clip cl, string "planes", string "output", float "weightL", int "offsetL", float "weightR", int "offsetR", float "knee", float "pr2", float "pr3")
{ # --- Hans' Ringing Remover ---
weightL = Default(weightL, 0.25) # 0.15 .. 0.7, left intensity; 0.0 = inactive
offsetL = Default(offsetL,1) # 0..5, distance from an edge
weightR = Default(weightR, 0.25) # right side
offsetR = Default(offsetR,1)
knee = Default(knee,0.7) # 0.2 .. 1.5 lower = softer, more linear. higher = more on-off like, useful for printed text
pr2 = Default(pr2,0.5) # 0.0 .. 1.5 protection for signal components with radius=2
pr3 = Default(pr3,0.5) # 0.0 .. 1.5 protection for signal components with radius=3
# the higher you set the pr parameters, the less often the artifact remover will get triggered. it depends on the content of the frame.
#
# you invoke the function for one particular distance of an artifact to an edge, left, or right, or both.
# you can invoke it again with a different offset, if there is more than one strong artifact on the same side, but the instances may interact in unexpected ways.
expr = "x[2,0] x[1,0] - dup * .75 * x[1,0] x - dup * + x x[-1,0] - dup * + x[-1,0] x[-2,0] - dup * .75 * + sqrt _F1@ " +\
"x[3,0] x[1,0] - dup * .66 * x[2,0] x[0,0] - dup * + x[1,0] x[-1,0] - dup * + x[0,0] x[-2,0] - dup * .66 * + sqrt " + String(pr2*0.36) + " * - " +\
"x[3,0] x[0,0] - dup * .5 * x[2,0] x[-1,0] - dup * + x[1,0] x[-2,0] - dup * + x[0,0] x[-3,0] - dup * .66 * + sqrt " + String(pr3*0.3) + " * - " +\
String(10.0/knee) + " / " + String(0.5*knee) + " - 0.0 max dup 1.0 - swap 1.0 + / 1.0 + 128.0 * 255.0 min " +\
"x["+ String(offsetL) +",0] x["+ String(2+offsetL) +",0] - 256.0 / * "+ String(weightL) +" * -64.0 max 127.0 min " +\
" _F1 " +\
"x[-3,0] x[-1,0] - dup * .66 * x[-2,0] x[0,0] - dup * + x[-1,0] x[1,0] - dup * + x[0,0] x[2,0] - dup * .66 * + sqrt " + String(pr2*0.36) + " * - " +\
"x[-3,0] x[-0,0] - dup * .5 * x[-2,0] x[1,0] - dup * + x[-1,0] x[2,0] - dup * + x[0,0] x[3,0] - dup * .66 * + sqrt " + String(pr3*0.3) + " * - " +\
String(10.0/knee) + " / " + String(0.5*knee) + " - 0.0 max dup 1.0 - swap 1.0 + / 1.0 + 128.0 * 255.0 min " +\
"x["+ String(-offsetR) +",0] x["+ String(-2-offsetR) +",0] - 256.0 / * "+ String(weightR) +" * -64.0 max 127.0 min + x + "
planes = Default(planes, "luma")
Y = (planes == "luma" || planes == "all") ? 3 : 1
U = (planes == "chroma" || planes == "all") ? 3 : 1
V = (planes == "chroma" || planes == "all") ? 3 : 1
output = Default(output, "YUV444P12")
output = (output=="out=in") ? "" : output
if (output=="") {
return cl.Expr( (Y==3 ? expr : "x"), (U==3 ? expr : "x"), (V==3 ? expr : "x"), scale_inputs = "allf")
} else {
return cl.Expr( (Y==3 ? expr : "x"), (U==3 ? expr : "x"), (V==3 ? expr : "x"), scale_inputs = "allf", format=output)
}
}
it can be applied, when you remaster or recompress old DVDs and try some sharpening, eg. with convolutional sharpeners.
The quality level therefor is DVD-related, but regarding Avisynth processing, it was tested with planar formats like YUV444 and 12-bit resolution.
It should be applied on the Luma signal, but you can experiment as you like.
What it does, it analyzes the signal and predicts one artifact to the left and one to the right of a vertical edge.
The placement of the artifact is set by the offset parameters. This means you look at the pixel distance, and set it to 1, 2, or 3, depending on which one is the most disturbing of several echoes.
In some cases, also in upsampled mode, values of 0 or even 4 or 5 may fit the issue.
Tune in on a strongly visible artifact that is repetitive.
(Actually it has dynamic function and works on all parts of the frame that are prone to ringing or to thin echo lines.)
Find its offset, then nudge the weight of the corrective signal.
Different scenes and different artifacts will ideally require different settings, so you want to find some averaged balance.
The result is, properly tweaked, that the video as a whole looks cleaner and more brilliant.
The algorithm avoids blurring.
The predicted artifact thus is being reengineered, and then subtracted from the original.
In other words, you have to set the weight parameters, and monitor the effect on a particular echo or Gibbs ringing structure.
The task is to zero in weight, not too light, not too dark, to make the artifacts less visible.
When your process does some filtering, you may run into sub-pixel issues. If you can't get the offset right, you can try to resample to a higher resolution and apply the function there, with a higher offset.
You can use it freely, but please when you store it somewhere, add the line "Hans' ringing remover".
Example:
(read data with MPEG2Source (), convert to 12-bit planar, whatever Expr() can handle)
last = AntiRingLR(last, "luma", weightL=0.2,offsetL=1,weightR=0.17,offsetR=1,knee=0.7,pr2=0.5,pr3=0.5)
The "output" parameter when specified, is either a format string, or the literal "out=in" (try this in case of error messages), within the limits of Expr()
It was tested with a recent "avisynth plus 64bit" version, please make sure that you run a compatible version.
Thanks, please enjoy, have fun, have success!
function AntiRingLR(clip cl, string "planes", string "output", float "weightL", int "offsetL", float "weightR", int "offsetR", float "knee", float "pr2", float "pr3")
{ # --- Hans' Ringing Remover ---
weightL = Default(weightL, 0.25) # 0.15 .. 0.7, left intensity; 0.0 = inactive
offsetL = Default(offsetL,1) # 0..5, distance from an edge
weightR = Default(weightR, 0.25) # right side
offsetR = Default(offsetR,1)
knee = Default(knee,0.7) # 0.2 .. 1.5 lower = softer, more linear. higher = more on-off like, useful for printed text
pr2 = Default(pr2,0.5) # 0.0 .. 1.5 protection for signal components with radius=2
pr3 = Default(pr3,0.5) # 0.0 .. 1.5 protection for signal components with radius=3
# the higher you set the pr parameters, the less often the artifact remover will get triggered. it depends on the content of the frame.
#
# you invoke the function for one particular distance of an artifact to an edge, left, or right, or both.
# you can invoke it again with a different offset, if there is more than one strong artifact on the same side, but the instances may interact in unexpected ways.
expr = "x[2,0] x[1,0] - dup * .75 * x[1,0] x - dup * + x x[-1,0] - dup * + x[-1,0] x[-2,0] - dup * .75 * + sqrt _F1@ " +\
"x[3,0] x[1,0] - dup * .66 * x[2,0] x[0,0] - dup * + x[1,0] x[-1,0] - dup * + x[0,0] x[-2,0] - dup * .66 * + sqrt " + String(pr2*0.36) + " * - " +\
"x[3,0] x[0,0] - dup * .5 * x[2,0] x[-1,0] - dup * + x[1,0] x[-2,0] - dup * + x[0,0] x[-3,0] - dup * .66 * + sqrt " + String(pr3*0.3) + " * - " +\
String(10.0/knee) + " / " + String(0.5*knee) + " - 0.0 max dup 1.0 - swap 1.0 + / 1.0 + 128.0 * 255.0 min " +\
"x["+ String(offsetL) +",0] x["+ String(2+offsetL) +",0] - 256.0 / * "+ String(weightL) +" * -64.0 max 127.0 min " +\
" _F1 " +\
"x[-3,0] x[-1,0] - dup * .66 * x[-2,0] x[0,0] - dup * + x[-1,0] x[1,0] - dup * + x[0,0] x[2,0] - dup * .66 * + sqrt " + String(pr2*0.36) + " * - " +\
"x[-3,0] x[-0,0] - dup * .5 * x[-2,0] x[1,0] - dup * + x[-1,0] x[2,0] - dup * + x[0,0] x[3,0] - dup * .66 * + sqrt " + String(pr3*0.3) + " * - " +\
String(10.0/knee) + " / " + String(0.5*knee) + " - 0.0 max dup 1.0 - swap 1.0 + / 1.0 + 128.0 * 255.0 min " +\
"x["+ String(-offsetR) +",0] x["+ String(-2-offsetR) +",0] - 256.0 / * "+ String(weightR) +" * -64.0 max 127.0 min + x + "
planes = Default(planes, "luma")
Y = (planes == "luma" || planes == "all") ? 3 : 1
U = (planes == "chroma" || planes == "all") ? 3 : 1
V = (planes == "chroma" || planes == "all") ? 3 : 1
output = Default(output, "YUV444P12")
output = (output=="out=in") ? "" : output
if (output=="") {
return cl.Expr( (Y==3 ? expr : "x"), (U==3 ? expr : "x"), (V==3 ? expr : "x"), scale_inputs = "allf")
} else {
return cl.Expr( (Y==3 ? expr : "x"), (U==3 ? expr : "x"), (V==3 ? expr : "x"), scale_inputs = "allf", format=output)
}
}