geometer
5th April 2026, 15:54
I wanted to create a fast alternative for generalconvolution().
So here it is, suitable for convolutional kernels that are in a "normal" range, like for sharpening.
It computes the divisor (to maintain brightness), and it removes "zero" coefficients for efficiency.
Save the package as "Convtools by Hans".
usage:
kernel1a = "0 0 0 0 0 0 0 -1 0 0 0 -1 11 -1 0 0 0 -1 0 0 0 0 0 0 0"
kernel3 = "0 3 -33 3 0 3 -36 363 -36 3 -33 363 1331 363 -33 3 -36 363 -36 3 0 3 -33 3 0"
ConvertToPlanarRGB(matrix="PC.709",interlaced=false,chromaresample="lanczos4")
last = Conv5x5(last, kernel3, "all", "out=in")
ConvertToYUV444(matrix="PC.709",interlaced=false,chromaresample="lanczos4")
last=SharpLumaX(last, kernel1a)
Please, enjoy!
#---------------------------------
function StringToNumberA(string cIn, int "pL")
{
pL = Default(pL, 0)
Width = 8
L = pL
c = LeftStr(cIn, 1)
cIn = MidStr(cIn, 2)
If (FindStr("1234567890-.", c) > 0) {
If (cIn > " ") {
c = c + String(StringToNumberA(cIn, L + 1))
}
return c
}Else If (c == " ") {
If ((LeftStr(cIn, 1) == " ") && (L==0)) {
return String(StringToNumberA(MidStr(cIn, 2), L))
}Else If ( (FindStr("1234567890-.", LeftStr(cIn, 1)) > 0) && (L==0)) {
return String(StringToNumberA(cIn, 0))
}Else{
return FillStr( (((Width - L) >= 0) ? (Width - L) : 0) , c) + String(StringToNumberA(cIn, 0))
}
}Else{
return String(StringToNumberA(cIn, L))
}
}
Function Arraysum(string pArraystring, int pLength) {
i = 0
n = 0
For (i = 1, StrLen(pArraystring) + 1, pLength) {
n = n + Value(MidStr(pArraystring, i, pLength))
}
n = (n<1.0 ? 1.0 : n) # never divide by zero
return n
}
Function RemoveZero(string pIn) {
nFind = FindStr(pIn, " 0 ")
while (nFind>0) {
nStart = FindStr( Midstr(pIn, nFind-8, 8), "x") # " 0 * + "
pIn = LeftStr(pIn, nFind-10+nStart) + MidStr(pIn, nFind+7)
nFind = FindStr(pIn, " 0 ")
}
return pIn
}
function Conv5x5(clip cl, string kernel, string "planes", string "output")
{
planes = Default(planes, "all")
Y = (planes == "luma" || planes == "all") ? 3 : 1
U = (planes == "chroma" || planes == "U" || planes == "all") ? 3 : 1
V = (planes == "chroma" || planes == "V" || planes == "all") ? 3 : 1
output = Default(output, "YUV444P12") ### 420?
output = (output=="out=in") ? "" : output
aM=string(StringToNumberA(kernel))
nTotal=Arraysum(aM,8)
# Build the full expression, add "0 " at start to make every coefficient the same for easy zero removal
expr = "0 " + \
"x[-2,-2] " + TrimRight(MidStr(aM,1+(8 * ( 1-1)),8)) + " * + " +\
"x[-1,-2] " + TrimRight(MidStr(aM,1+(8 * ( 2-1)),8)) + " * + " +\
"x[0,-2] " + TrimRight(MidStr(aM,1+(8 * ( 3-1)),8)) + " * + " +\
"x[1,-2] " + TrimRight(MidStr(aM,1+(8 * ( 4-1)),8)) + " * + " +\
"x[2,-2] " + TrimRight(MidStr(aM,1+(8 * ( 5-1)),8)) + " * + " +\
"x[-2,-1] " + TrimRight(MidStr(aM,1+(8 * ( 6-1)),8)) + " * + " +\
"x[-1,-1] " + TrimRight(MidStr(aM,1+(8 * ( 7-1)),8)) + " * + " +\
"x[0,-1] " + TrimRight(MidStr(aM,1+(8 * ( 8-1)),8)) + " * + " +\
"x[1,-1] " + TrimRight(MidStr(aM,1+(8 * ( 9-1)),8)) + " * + " +\
"x[2,-1] " + TrimRight(MidStr(aM,1+(8 * (10-1)),8)) + " * + " +\
"x[-2,0] " + TrimRight(MidStr(aM,1+(8 * (11-1)),8)) + " * + " +\
"x[-1,0] " + TrimRight(MidStr(aM,1+(8 * (12-1)),8)) + " * + " +\
"x[0,0] " + TrimRight(MidStr(aM,1+(8 * (13-1)),8)) + " * + " +\
"x[1,0] " + TrimRight(MidStr(aM,1+(8 * (14-1)),8)) + " * + " +\
"x[2,0] " + TrimRight(MidStr(aM,1+(8 * (15-1)),8)) + " * + " +\
"x[-2,1] " + TrimRight(MidStr(aM,1+(8 * (16-1)),8)) + " * + " +\
"x[-1,1] " + TrimRight(MidStr(aM,1+(8 * (17-1)),8)) + " * + " +\
"x[0,1] " + TrimRight(MidStr(aM,1+(8 * (18-1)),8)) + " * + " +\
"x[1,1] " + TrimRight(MidStr(aM,1+(8 * (19-1)),8)) + " * + " +\
"x[2,1] " + TrimRight(MidStr(aM,1+(8 * (20-1)),8)) + " * + " +\
"x[-2,2] " + TrimRight(MidStr(aM,1+(8 * (21-1)),8)) + " * + " +\
"x[-1,2] " + TrimRight(MidStr(aM,1+(8 * (22-1)),8)) + " * + " +\
"x[0,2] " + TrimRight(MidStr(aM,1+(8 * (23-1)),8)) + " * + " +\
"x[1,2] " + TrimRight(MidStr(aM,1+(8 * (24-1)),8)) + " * + " +\
"x[2,2] " + TrimRight(MidStr(aM,1+(8 * (25-1)),8)) + " * + " + String(nTotal) + " /"
# the following contains a clamping function, it depends on number format, you can remove it or make your own, for your task
expr = RemoveZero(expr) + ((planes == "chroma") ? " dup x[0,0] 2048 < 0 max 2047 min 2048 max 4095 min ?" : " 0 max 4095 min")
if (output=="") {
return cl.Expr( (Y==3 ? expr : "x"), (U==3 ? expr : "x"), (V==3 ? expr : "x"))
} else {
return cl.Expr( (Y==3 ? expr : "x"), (U==3 ? expr : "x"), (V==3 ? expr : "x"), format=output)
}
}
#--------------------------- helper funcs ---------------------------------------
function SharpLumaX(clip In, string kernel) {
In = Conv5x5(In, kernel, "luma", "out=in")
return In
}
function SharpChromaX(clip In, string kernel) {
In = Conv5x5(In, kernel, "chroma", "out=in")
return In
}
So here it is, suitable for convolutional kernels that are in a "normal" range, like for sharpening.
It computes the divisor (to maintain brightness), and it removes "zero" coefficients for efficiency.
Save the package as "Convtools by Hans".
usage:
kernel1a = "0 0 0 0 0 0 0 -1 0 0 0 -1 11 -1 0 0 0 -1 0 0 0 0 0 0 0"
kernel3 = "0 3 -33 3 0 3 -36 363 -36 3 -33 363 1331 363 -33 3 -36 363 -36 3 0 3 -33 3 0"
ConvertToPlanarRGB(matrix="PC.709",interlaced=false,chromaresample="lanczos4")
last = Conv5x5(last, kernel3, "all", "out=in")
ConvertToYUV444(matrix="PC.709",interlaced=false,chromaresample="lanczos4")
last=SharpLumaX(last, kernel1a)
Please, enjoy!
#---------------------------------
function StringToNumberA(string cIn, int "pL")
{
pL = Default(pL, 0)
Width = 8
L = pL
c = LeftStr(cIn, 1)
cIn = MidStr(cIn, 2)
If (FindStr("1234567890-.", c) > 0) {
If (cIn > " ") {
c = c + String(StringToNumberA(cIn, L + 1))
}
return c
}Else If (c == " ") {
If ((LeftStr(cIn, 1) == " ") && (L==0)) {
return String(StringToNumberA(MidStr(cIn, 2), L))
}Else If ( (FindStr("1234567890-.", LeftStr(cIn, 1)) > 0) && (L==0)) {
return String(StringToNumberA(cIn, 0))
}Else{
return FillStr( (((Width - L) >= 0) ? (Width - L) : 0) , c) + String(StringToNumberA(cIn, 0))
}
}Else{
return String(StringToNumberA(cIn, L))
}
}
Function Arraysum(string pArraystring, int pLength) {
i = 0
n = 0
For (i = 1, StrLen(pArraystring) + 1, pLength) {
n = n + Value(MidStr(pArraystring, i, pLength))
}
n = (n<1.0 ? 1.0 : n) # never divide by zero
return n
}
Function RemoveZero(string pIn) {
nFind = FindStr(pIn, " 0 ")
while (nFind>0) {
nStart = FindStr( Midstr(pIn, nFind-8, 8), "x") # " 0 * + "
pIn = LeftStr(pIn, nFind-10+nStart) + MidStr(pIn, nFind+7)
nFind = FindStr(pIn, " 0 ")
}
return pIn
}
function Conv5x5(clip cl, string kernel, string "planes", string "output")
{
planes = Default(planes, "all")
Y = (planes == "luma" || planes == "all") ? 3 : 1
U = (planes == "chroma" || planes == "U" || planes == "all") ? 3 : 1
V = (planes == "chroma" || planes == "V" || planes == "all") ? 3 : 1
output = Default(output, "YUV444P12") ### 420?
output = (output=="out=in") ? "" : output
aM=string(StringToNumberA(kernel))
nTotal=Arraysum(aM,8)
# Build the full expression, add "0 " at start to make every coefficient the same for easy zero removal
expr = "0 " + \
"x[-2,-2] " + TrimRight(MidStr(aM,1+(8 * ( 1-1)),8)) + " * + " +\
"x[-1,-2] " + TrimRight(MidStr(aM,1+(8 * ( 2-1)),8)) + " * + " +\
"x[0,-2] " + TrimRight(MidStr(aM,1+(8 * ( 3-1)),8)) + " * + " +\
"x[1,-2] " + TrimRight(MidStr(aM,1+(8 * ( 4-1)),8)) + " * + " +\
"x[2,-2] " + TrimRight(MidStr(aM,1+(8 * ( 5-1)),8)) + " * + " +\
"x[-2,-1] " + TrimRight(MidStr(aM,1+(8 * ( 6-1)),8)) + " * + " +\
"x[-1,-1] " + TrimRight(MidStr(aM,1+(8 * ( 7-1)),8)) + " * + " +\
"x[0,-1] " + TrimRight(MidStr(aM,1+(8 * ( 8-1)),8)) + " * + " +\
"x[1,-1] " + TrimRight(MidStr(aM,1+(8 * ( 9-1)),8)) + " * + " +\
"x[2,-1] " + TrimRight(MidStr(aM,1+(8 * (10-1)),8)) + " * + " +\
"x[-2,0] " + TrimRight(MidStr(aM,1+(8 * (11-1)),8)) + " * + " +\
"x[-1,0] " + TrimRight(MidStr(aM,1+(8 * (12-1)),8)) + " * + " +\
"x[0,0] " + TrimRight(MidStr(aM,1+(8 * (13-1)),8)) + " * + " +\
"x[1,0] " + TrimRight(MidStr(aM,1+(8 * (14-1)),8)) + " * + " +\
"x[2,0] " + TrimRight(MidStr(aM,1+(8 * (15-1)),8)) + " * + " +\
"x[-2,1] " + TrimRight(MidStr(aM,1+(8 * (16-1)),8)) + " * + " +\
"x[-1,1] " + TrimRight(MidStr(aM,1+(8 * (17-1)),8)) + " * + " +\
"x[0,1] " + TrimRight(MidStr(aM,1+(8 * (18-1)),8)) + " * + " +\
"x[1,1] " + TrimRight(MidStr(aM,1+(8 * (19-1)),8)) + " * + " +\
"x[2,1] " + TrimRight(MidStr(aM,1+(8 * (20-1)),8)) + " * + " +\
"x[-2,2] " + TrimRight(MidStr(aM,1+(8 * (21-1)),8)) + " * + " +\
"x[-1,2] " + TrimRight(MidStr(aM,1+(8 * (22-1)),8)) + " * + " +\
"x[0,2] " + TrimRight(MidStr(aM,1+(8 * (23-1)),8)) + " * + " +\
"x[1,2] " + TrimRight(MidStr(aM,1+(8 * (24-1)),8)) + " * + " +\
"x[2,2] " + TrimRight(MidStr(aM,1+(8 * (25-1)),8)) + " * + " + String(nTotal) + " /"
# the following contains a clamping function, it depends on number format, you can remove it or make your own, for your task
expr = RemoveZero(expr) + ((planes == "chroma") ? " dup x[0,0] 2048 < 0 max 2047 min 2048 max 4095 min ?" : " 0 max 4095 min")
if (output=="") {
return cl.Expr( (Y==3 ? expr : "x"), (U==3 ? expr : "x"), (V==3 ? expr : "x"))
} else {
return cl.Expr( (Y==3 ? expr : "x"), (U==3 ? expr : "x"), (V==3 ? expr : "x"), format=output)
}
}
#--------------------------- helper funcs ---------------------------------------
function SharpLumaX(clip In, string kernel) {
In = Conv5x5(In, kernel, "luma", "out=in")
return In
}
function SharpChromaX(clip In, string kernel) {
In = Conv5x5(In, kernel, "chroma", "out=in")
return In
}