Log in

View Full Version : Stab_Light - deshaker


Pages : [1] 2

Arx1meD
11th March 2021, 19:37
This script will be useful for stabilizing video from shaking.
The script is based on Stab3 (https://github.com/realfinder/AVS-Stuff/raw/master/avs%202.5%20and%20up/StabilizationToolsPack.avsi) (found on real.finder's Avisynth Stuff (https://forum.doom9.org/showthread.php?t=174121)). It is simplified and can fix large borders. It is also faster.


# Stab_Light() v1.2 - deshaker
#
# v1.0 : - first release
# v1.1 : - adapted for use in all versions of AviSynth (by StainlessS)
# - added motion search method
# v1.2 : - added Pre_Clip function for better search of motion vectors

/*
Needed plugins:
MVTools2 https://github.com/pinterf/mvtools/releases
DePan DePan and DePanEstimate are bundled as a package with MVTools2
DePanEstimate
RgTools https://github.com/pinterf/RgTools/releases
MaskTools2 https://github.com/pinterf/masktools/releases/
FillBorders https://github.com/Asd-g/AviSynth-FillBorders/releases
AVSInpaint https://github.com/pinterf/AvsInpaint/releases
GRunT https://github.com/pinterf/GRunT/releases
GScript for AviSynth v2.60 only https://forum.doom9.org/showthread.php?t=147846

External dependencies:
fftw3.dll (or renamed to libfftw3f-3.dll) from http://www.fftw.org/ (http://www.fftw.org/install/windows.html)
Copy the file fftw3.dll to the folder C:\Windows\System32 for Win32. For Win64 copy 32-bit version in C:\Windows\SysWOW64
and copy 64-bit version in C:\Windows\System32.
Or use LoadDLL (http://avisynth.nl/index.php/LoadDLL)


Stab_Light(clip, ts, range, dxmax, dymax, zoom, rotation, PAR, mirror,
MaxBorders, FixBorders, FixFalPos, MethodMS, debug)

ts Default: 7 - Frames to temporal average for better motion estimation (max. 7)
range Default: 1 - Frames before/after to estimate motion
dxmax Default: autodetect - Maximum deviation in pixels
dymax Default: autodetect - x, and y should be the same
zoom Default: 1.0 - Maximum zoom factor (1 disabled). When zoom > 0 borders can be large
rotation Default: false - Rotation estimation for MethodMS=2 only. If rotation=true black borders can be curved.
To fix them set mirror=true
PAR Default: 1.0 - Pixel Aspect Ratio of your source
mirror Default: false - Depan it self fill empty borders with mirrored from frame edge pixels
MaxBorders Default: autodetect - Maximum of black borders in pixels
FixBorders Default: true - Fix black borders
FixFalPos Default: true - Fixes borders of MaxBorders more pixels wide
MethodMS Default: 1 - Method of motion search: 1 - DePan is used to search for motion
in many cases better and faster
2 - MVTools is used to search for motion
slower, bad border fix, recommended to set mirror=true
debug Default: false - Show black borders if mirror=false and FixBorders=true


This script can be Import()'ed under AviSynth+, OR, under AviSynth v2.60 Standard [ONLY IF GScript is installed].
Can also be GImport()'ed when GScript Installed.
Can also be auto Loaded by AviSynth if in Plugins directory. [Needs GScript installed if not AviSynth+].
Can also be dumped in AvsInit()'s GIMPORT directory for auto loading by AvsInit().

GScript needs to be installed BEFORE attempting to AviSynth v2.60 std Import()/GImport() "Stab_Light.avsi".
Either by Plugins AutoLoad, or Manual LoadPlugin() loading.


Example:
for AviSynth v2.60 : LoadPlugin("C:\AVS\plugins\GScript_26_32.dll")
Import("C:\AVS\scripts\Stab_Light.avsi")

for AviSynth+ : Import("C:\AVS\scripts\Stab_Light.avsi")

Stab_Light(ts=7, zoom=1.0, mirror=false, rotation=false, FixFalPos=true, FixBorders=true, MethodMS=1, debug=false)


Thread in Doom9's forum: https://forum.doom9.org/showthread.php?t=182532
*/

Function Fb2FnNameEx(string Fn) {
Try{
Eval(Fn+"()")B = true
} Catch (e) {
Assert(e.FindStr("syntax") == 0, "Fb2FnNameEx: Error in Function Name '"+Fn+"'")
B = (e.FindStr("no function named") == 0)
}
return B
}

Fb2IsPlus = (FindStr(VersionString,"AviSynth+") != 0)
Fb2HasGScript = Fb2FnNameEx("GScipt")

Fb2_GS_S = """
Function FindBorders(clip c, int edge, float thr, int MaxB, int pad, int w, int h) {
# edge: 0=left, 1=right, 2=top, 3=bottom
result=0
for(i = 1, MaxB) {
crpd = c.Crop(edge==1?w-i:0, edge==3?h-i:0, edge==0?i:0, edge==2?i:0)
if(crpd.AverageLuma <= thr) { result = i + pad } else { i = MaxB }
}
return result
}
"""

/*
If this avsi script is not in plugins, then HasGScript [Fb2HasGScript] can be FALSE, even if using GImport() from GScript dll.
Not sure why this occurs, so even if Fb2HasGScript==FALSE and is NOT AviSynth+, then we use GScript's GEval instead of Eval to
install above FindBorders() function.
We use Eval() to install the function, ONLY if GScript not installed and is AviSynth+.
*/

Try {
Fb2HasGScript || !Fb2IsPlus ? GEval(Fb2_GS_S) : Eval(Fb2_GS_S)
} Catch (msg) {
Assert(false, ScriptFile+": Need GScript or AviSynth+ or GScript mast be loaded before the script"+Chr(10)+
\ "IsPlus="+String(Fb2IsPlus)+"; HasGScript="+String(Fb2HasGScript)+Chr(10)+
\ "SysErr="+Msg)
}

Function Stab_Light(clip clp, int "ts", int "range", int "dxmax", int "dymax", float "zoom", bool "rotation", float "PAR",
\ bool "mirror", int "MaxBorders", bool "FixBorders", bool "FixFalPos", int "MethodMS", bool "debug") {
ts = Default(ts, 7)
range = Default(range, 1)
dxmax = Default(dxmax, Round(clp.Width()/180.0))
dymax = Default(dymax, dxmax)
zoom = Default(zoom, 1.0)
rotation = Default(rotation, false)
PAR = Default(PAR, 1.0)
mirror = Default(mirror, false)
MaxBorders = Default(MaxBorders, Max(Round(clp.Width()/60.0), 20))
FixBorders = Default(FixBorders, true)
FixFalPos = Default(FixFalPos, true)
MethodMS = Default(MethodMS, 1)
debug = Default(debug, false)

Assert(MethodMS == 1 || MethodMS == 2, "Stab_Light: Motion search method should be 1 or 2")

temp = TemporalSoften(clp, ts, 255, 255, 25, 2) # SC thr to 25 otherwise pans will stutter
rep = Repair(temp, TemporalSoften(clp, 1, 255, 255, 25, 2))
inter = Interleave(rep, clp) # temporal stable (better subpixel detection)
pre_clp = inter.Pre_Clip(blur=1.0, contrast=0, brightness=0).RemoveGrain(mode=17)

vectors = MethodMS == 2 ? pre_clp.MSuper(hpad=8, vpad=8, pel=1, sharp=2, chroma=false)
\ .MAnalyse(isb=false, blksize=Height(clp)<=720?8:16, overlap=Height(clp)<=720?2:4,
\ chroma=false, truemotion=false, global=true, search=5, dct=7)
\ : Undefined

mdata = MethodMS == 1 ? DePanEstimate(pre_clp, range=range, pixaspect=PAR, trust=0, dxmax=dxmax, dymax=dymax, zoommax=zoom)
\ : pre_clp.MDepan(vectors, zoom=zoom>1, rot=rotation, thSCD1=400)

dp = DePan(inter, data=mdata, offset=-1, mirror=mirror?15:0, pixaspect=PAR, matchfields=false, subpixel=2)
stab = SelectEvery(dp, 2, 0)

w = Width(clp)
h = Height(clp)
thr = 10.0
pre = stab.ConvertToY8()#.ColorYUV(off_y=-15)

stab = !mirror && FixFalPos ? \
gScriptClip(stab, "
AverageLuma(Crop(pre, 0, 0, -w+MaxBorders+1, 0)) < thr ? clp : \
AverageLuma(Crop(pre, w-MaxBorders-1, 0, 0, 0)) < thr ? clp : \
AverageLuma(Crop(pre, 0, 0, 0, -h+MaxBorders+1)) < thr ? clp : \
AverageLuma(Crop(pre, 0, h-MaxBorders-1, 0, 0)) < thr ? clp : Last
return Last
", args="clp, pre, MaxBorders, w, h, thr",Local=true) : stab

return !mirror && FixBorders ? FillBorders2(stab, thr=thr, pad=1, MaxBorders=MaxBorders, debug=debug) : stab
}

Function FillBorders2(clip c, float "thr", int "pad", int "MaxBorders", bool "debug") {
thr = Default(thr, 5.0) # Threshold for black borders
pad = Default(pad, 0) # Extra padding for borders
MaxB = Default(MaxBorders, Max(Round(c.Width()/60.0), 20)) # Maximum of black borders in pixels
debug = Default(debug, false) # Show black borders

w = Width(c)
h = Height(c)
pre = c.ConvertToY8()#.ColorYUV(off_y=-15)
blank = c.mt_lut("0", chroma="-128")

gScriptClip(c, """
x1 = FindBorders(pre, 0, thr, MaxB, pad, w, h) # left
x2 = FindBorders(pre, 1, thr, MaxB, pad, w, h) # right
y1 = FindBorders(pre, 2, thr, MaxB, pad, w, h) # top
y2 = FindBorders(pre, 3, thr, MaxB, pad, w, h) # bottom

PreBlur = 0 # If > 0 then there may be an error
PostBlur = 0 # Max(x1, x2, y1, y2, 4) # If > 0 then there may be an error
b_fix = (x1 + x2 + y1 + y2) > 0
max_fix = Max(x1, x2, y1, y2)

msk = b_fix && max_fix >= 8 ? blank.ConvertToY8().LetterBox(y1, y2, x1, x2, color=$ffffff).ConvertToYV12() : blank

fixed = !b_fix ? Last
\ : max_fix < 8 ? FillBorders(left=x1, top=y1, right=x2, bottom=y2, mode=0, y=3, u=3, v=3)
\ : InpaintLogo(mask=msk, Radius=Max(x1,x2,y1,y2,5)*1.5, PreBlur=PreBlur, PostBlur=PostBlur)

debug ? mt_merge(fixed,
\ blank.ConvertToRGB().LetterBox(y1, y2, x1, x2, color=$ff3333).ConvertToYV12(),
\ blank.ConvertToY8().LetterBox(y1, y2, x1, x2, color=$ffffff).ConvertToYV12(),
\ luma=true)
\ .Subtitle("Black borders:\n "+string(y1)+"\n "+string(x1)+" "+string(x2)+"\n "+ string(y2),
\ lsp=0, text_color=$ff3333, font="Courier New", x=MaxB+5, y=MaxB+5)
\ : fixed

return Last
""", args="pre, blank, thr, pad, MaxB, debug, w, h")
}

function Pre_Clip(clip clp, float "blur", float "contrast", float "brightness"){
blur = Default(blur, 1.0)
contrast = Default(contrast, 1.2)
brightness = Default(brightness, 10.0)

v = clp.mt_lut(expr="x 20 1 x 127.5 / - 21 ^ pi x * 255 / cos 21 ^ - * 1.05 * -", Y=3, U=2, V=2, scale_inputs="allf", use_expr=2)

mask_1 = clp.Tweak(sat=0).Blur(blur).mt_edge(mode="min/max", thY1=0, thY2=255, Y=3, U=2, V=2)

pre_1 = v.Tweak(cont=contrast, bright=brightness)
pre_2 = mt_merge(v, pre_1, mask_1, Y=3, U=2, V=2)
pre_3 = pre_2.Tweak(sat=0).mt_invert().Blur(1.58).Blur(1.58).Blur(1.58)
pre_4 = mt_lutxy(pre_2, pre_3, expr="x 127.5 > y 255 x - 127.5 / * x 255 x - - + y x 127.5 / * ? ", Y=3, U=2, V=2, scale_inputs="allf", use_expr=2)

# s = clp.vsTCanny().mt_binarize(threshold=0, chroma="-128")
# Overlay(pre_4, s, mask=s, opacity=0.5)

return pre_4
}


Old version for AviSynth+ only

# Needed plugins: DePan, DePanEstimate, GRunT, RgTools, MaskTools2, FillBorders, AVSInpaint
function Stab_Light(clip clp, int "ts", int "range", int "dxmax", int "dymax", float "zoom", float "PAR", \
bool "mirror", int "MaxBorders", bool "FixBorders", bool "FixFalPos", bool "debug") {
ts = Default(ts, 7) #frames to temporal average for better motion estimation (max. 7)
range = Default(range, 1) #frames before/after to estimate motion
dxmax = Default(dxmax, Round(clp.Width()/180.0)) #maximum deviation in pixels
dymax = Default(dymax, dxmax) #x, and y should be the same
zoom = Default(zoom, 1) #maximum zoom factor (1 disabled)
PAR = Default(PAR, 1.0) #PAR of your source
mirror = Default(mirror, false) #Edge filling
MaxBorders = Default(MaxBorders, Max(Round(clp.Width()/60.0), 20))
FixBorders = Default(FixBorders, true)
FixFalPos = Default(FixFalPos, true) #Fixes borders of MaxBorders more pixels wide
debug = Default(debug, false)

temp = TemporalSoften(clp, ts, 255, 255, 25, 2) # SC thr to 25 otherwise pans will stutter
rep = Repair(temp, TemporalSoften(clp, 1, 255, 255, 25, 2))
inter = Interleave(rep, clp) # temporal stable (better subpixel detection)
pre_clp = inter.mt_lut(expr="x 20 1 x 127.5 / - 21 ^ pi x * 255 / cos 21 ^ - * 1.05 * -", Y=3, U=2, V=2).RemoveGrain(mode=17) #ColorYUV(levels="PC->TV")

mdata = DePanEstimate(pre_clp, range=range, pixaspect=PAR, trust=0, dxmax=dxmax, dymax=dymax, zoommax=zoom)
dp = DePan(inter, data=mdata, offset=-1, mirror=mirror?15:0, pixaspect=PAR, matchfields=false, subpixel=2)
stab = SelectEvery(dp, 2, 0)

w = Width(clp)
h = Height(clp)
thr = 10

stab = mirror == false && FixFalPos == true ? gScriptClip(stab, """
pre = ConvertToY8()#.ColorYUV(off_y=-15)
AverageLuma(Crop(pre, 0, 0, -w+MaxBorders+1, 0)) < thr ? clp : \
AverageLuma(Crop(pre, w-MaxBorders-1, 0, 0, 0)) < thr ? clp : \
AverageLuma(Crop(pre, 0, 0, 0, -h+MaxBorders+1)) < thr ? clp : \
AverageLuma(Crop(pre, 0, h-MaxBorders-1, 0, 0)) < thr ? clp : stab
""", args="clp, stab, MaxBorders, w, h, thr") : stab

mirror == false && FixBorders ==true ? FillBorders2(stab, thr=thr, pad=1, MaxBorders=MaxBorders, debug=debug) : stab
}

function FillBorders2(clip c, int "thr", int "pad", int "MaxBorders", bool "debug") {
thr = Default(thr, 5)
pad = Default(pad, 0)
MaxB = Default(MaxBorders, Max(Round(c.Width()/60.0), 20))
debug = Default(debug, false)

w = width(c)
h = height(c)

gScriptClip(c, """
pre = ConvertToY8()#.ColorYUV(off_y=-15)

x1 = 0
x2 = 0
y1 = 0
y2 = 0
for(i = 1, MaxB, 1) {
if (AverageLuma(Crop(pre, 0, 0, -w+i, 0)) < thr) {x1 = i + pad} else {break}
}
for(i = 1, MaxB, 1) {
if (AverageLuma(Crop(pre, w-i, 0, 0, 0)) < thr) {x2 = i + pad} else {break}
}
for(i = 1, MaxB, 1) {
if (AverageLuma(Crop(pre, 0, 0, 0, -h+i)) < thr) {y1 = i + pad} else {break}
}
for(i = 1, MaxB, 1) {
if (AverageLuma(Crop(pre, 0, h-i, 0, 0)) < thr) {y2 = i + pad} else {break}
}

PreBlur = 0 #If > 0 then there may be an error
PostBlur = 0 #Max(x1, x2, y1, y2, 4) #If > 0 then there may be an error
b_fix = (x1 + x2 + y1 + y2) > 0
max_fix = Max(x1, x2, y1, y2)

msk = b_fix && max_fix >= 8 ? pre.mt_lut("0", chroma="-128").LetterBox(y1, y2, x1, x2, color_yuv=$ffffff).ConvertToYV12() : \
pre.mt_lut("0", chroma="-128")

fixed = b_fix ? max_fix < 8 ? FillBorders(left=x1, top=y1, right=x2, bottom=y2, mode=0, y=3, u=3, v=3) : \
InpaintLogo(mask=msk, Radius=Max(x1,x2,y1,y2,5)*1.5, PreBlur=PreBlur, PostBlur=PostBlur) : \
c

debug == true ? mt_merge(fixed, \
c.mt_lut("0", chroma="-128").ConvertToRGB().LetterBox(y1, y2, x1, x2, color=$ff3333).ConvertToYV12(), \
pre.mt_lut("0", chroma="-128").LetterBox(y1, y2, x1, x2, color_yuv=$ffffff).ConvertToYV12(), \
luma=true)\
.Subtitle("Black borders:\n "+string(y1)+"\n "+string(x1)+" "+string(x2)+"\n "+ string(y2), \
lsp=0, text_color=$ff3333, font="Courier New", x=MaxB+5, y=MaxB+5) : \
fixed
""", args="c, thr, pad, MaxB, debug, w, h")
}


Example:
Stab_Light(zoom = 1.00, mirror = false, FixFalPos = true, FixBorders = true, debug = false)
or
Stab_Light(ts=7, zoom=1.0, mirror=false, rotation=false, FixFalPos=true, FixBorders=true, MethodMS=1, debug=false)

Original and stabilized clip (https://mega.nz/file/MLBS1DwS#ud4OL1LS0W6arzWup9DDB8aU5_cq9gjRtWdtc8qLkVo)

manono
12th March 2021, 20:25
Hi,
Your filter has arrived at the right time as I've just begun work on an especially jumpy video. It seems to work fine but there's a line imprinted at the top of the entire video:

Script error: Syntax error
([Script Clip], line 8 column 21)

Would you happen to know anything about that?

Thank you.

StainlessS
12th March 2021, 20:57
Avs+ ? [Uses for/next loop - OR, if not Avs+, use GScript's GImport() on OP script]
and all requirements fulfilled ? # DePan, DePanEstimate, GRunT, RgTools, MaskTools2, FillBorders, AVSInpaint

Script error: Syntax error
([Script Clip}, line 8 column 21)

There are 2 instances of gScriptclip in that script, 1st instance only 7 lines, so must be 2nd instance, ie

gScriptClip(c, """
pre = ConvertToY8()#.ColorYUV(off_y=-15)

x1 = 0
x2 = 0
y1 = 0
y2 = 0
for(i = 1, MaxB, 1) { # <<<<< Line 8, coloumn 21 is @ the curly brace thing '{' : looks like need Avs+ or GScript for for/next loop
if (AverageLuma(Crop(pre, 0, 0, -w+i, 0)) < thr) {x1 = i + pad} else {break}
}
for(i = 1, MaxB, 1) {
if (AverageLuma(Crop(pre, w-i, 0, 0, 0)) < thr) {x2 = i + pad} else {break}
}
for(i = 1, MaxB, 1) {
if (AverageLuma(Crop(pre, 0, 0, 0, -h+i)) < thr) {y1 = i + pad} else {break}
}
for(i = 1, MaxB, 1) {
if (AverageLuma(Crop(pre, 0, h-i, 0, 0)) < thr) {y2 = i + pad} else {break}
}

PreBlur = 0 #If > 0 then there may be an error
PostBlur = 0 #Max(x1, x2, y1, y2, 4) #If > 0 then there may be an error
b_fix = (x1 + x2 + y1 + y2) > 0
max_fix = Max(x1, x2, y1, y2)

msk = b_fix && max_fix >= 8 ? pre.mt_lut("0", chroma="-128").LetterBox(y1, y2, x1, x2, color_yuv=$ffffff).ConvertToYV12() : \
pre.mt_lut("0", chroma="-128")

fixed = b_fix ? max_fix < 8 ? FillBorders(left=x1, top=y1, right=x2, bottom=y2, mode=0, y=3, u=3, v=3) : \
InpaintLogo(mask=msk, Radius=Max(x1,x2,y1,y2,5)*1.5, PreBlur=PreBlur, PostBlur=PostBlur) : \
c

debug == true ? mt_merge(fixed, \
c.mt_lut("0", chroma="-128").ConvertToRGB().LetterBox(y1, y2, x1, x2, color=$ff3333).ConvertToYV12(), \
pre.mt_lut("0", chroma="-128").LetterBox(y1, y2, x1, x2, color_yuv=$ffffff).ConvertToYV12(), \
luma=true)\
.Subtitle("Black borders:\n "+string(y1)+"\n "+string(x1)+" "+string(x2)+"\n "+
\ string(y2), lsp = 10, text_color=$ff3333, font="Courier New", x=MaxB+5, y=MaxB+5) : \
fixed
""", args="c, thr, pad, MaxB, debug, w, h")


Script works ok for me [avs+], but using defaults, what are the args you are using ?

AviSource("d:\Parade.avi")

Stab_Light()


EDIT: below seems to work ok too [as OP example].

AviSource(".\stab_example.mkv.AVI") # OP's sample, After ffmpeg conversion to UT_Video AVI
W=width/2
crop(0,0,W,0) # only unstable half
ORG=Last
Stab_Light(zoom = 1.00, mirror = false, FixFalPos = true, FixBorders = true, debug = false)
D=Last.Subtract(ORG)
T=StackHorizontal(ORG,Last)
B=StackHorizontal(D,D)
StackVertical(T,B)

just to show its doing something [and no error message]
https://i.postimg.cc/CdbX7LQz/man-00.jpg (https://postimg.cc/CdbX7LQz)

manono
13th March 2021, 03:26
Thanks for taking the time to have a look, StainlessS.
Avs+ ?
Regular AviSynth 2.60 32bit.
...use GScript's GImport() on OP script
Tried it but no change.
...and all requirements fulfilled ?
I have them all loaded explicitly in the script, but I can't vouch for them all being the latest versions.
...what are the args you are using ?
The same as his example. Mine jumps similarly to his before-and-after, and I first wanted to get it going before I experimented. This is the complete script at the moment:

LWLibavVideoSource("EK Do Teen.mp4")
Stab_Light(zoom = 1.00, mirror = false, FixFalPos = true, FixBorders = true, debug = false)
Return Last

Even with no arguments (Stab_Light()), same thing. Maybe Arx1meD will have an idea. Thanks again.

StainlessS
13th March 2021, 05:04
OK, I'm gettin' same error message as you now with 2.6 std.
I'll play a bit more but may not do it tonite if takes very long, its after 04:00 am here.

StainlessS
13th March 2021, 06:05
Well I've had a bit more play, and not sure whats going on (maybe I need sleep badly or something, things just dont add up).

this thing

for(i = 1, MaxB, 1) {
if (AverageLuma(Crop(pre, 0, 0, -w+i, 0)) < thr) {x1 = i + pad} else {break}
}

I dont think there is a break keyword in GScript, but it dont cause a problem under AVS+.
I think I once asked for Gavino to implement break and Do {} Until (), he was non too keen, he likes the easy life.

Does anybody know if Pinterf impemented break, and if it was documented somewhere ?
[Break seems to be the prob, but it somehow flags the error earlier in script].

I'll try fix it [to work under GScript].

EDIT:
Fixed as below, but still gives pretty much same error.

for(i = 1, MaxB, 1) {
if (AverageLuma(Crop(pre, 0, 0, -w+i, 0)) < thr) {x1 = i + pad} else { i = MaxB }
}


EDIT: I'll come back to this tomorrow if Arx1meD aint fixed it.

Arx1meD
13th March 2021, 08:32
manono I am using AVS + and I have no errors.
Earlier I used the borders search method written in FillBorders_stabi. In FillBorders_stabi, the maximum borders is 4 px. I increased the search for the borders to 16 px.

Old version of my FillBorders2. See the modified version in post #27 (https://forum.doom9.org/showthread.php?p=1938066#post1938066)


function FillBorders2(clip c, int "thr", int "pad", int "MaxBorders", bool "debug") {
thr = Default(thr, 5)
pad = Default(pad, 0)
MaxB = Default(MaxBorders, 16)
debug = Default(debug, false)

w = width(c)
h = height(c)

gScriptClip(c, """
pre = ConvertToY8()#.ColorYUV(off_y=-15)

x1 = AverageLuma(crop(pre, 0, 0, -w+1, 0)) < thr ? \
(AverageLuma(crop(pre, 0, 0, -w+2, 0)) < thr ? \
(AverageLuma(crop(pre, 0, 0, -w+3, 0)) < thr ? \
(AverageLuma(crop(pre, 0, 0, -w+4, 0)) < thr ? \
(AverageLuma(crop(pre, 0, 0, -w+5, 0)) < thr ? \
(AverageLuma(crop(pre, 0, 0, -w+6, 0)) < thr ? \
(AverageLuma(crop(pre, 0, 0, -w+7, 0)) < thr ? \
(AverageLuma(crop(pre, 0, 0, -w+8, 0)) < thr ? \
(AverageLuma(crop(pre, 0, 0, -w+9, 0)) < thr ? \
(AverageLuma(crop(pre, 0, 0, -w+10, 0)) < thr ? \
(AverageLuma(crop(pre, 0, 0, -w+11, 0)) < thr ? \
(AverageLuma(crop(pre, 0, 0, -w+12, 0)) < thr ? \
(AverageLuma(crop(pre, 0, 0, -w+13, 0)) < thr ? \
(AverageLuma(crop(pre, 0, 0, -w+14, 0)) < thr ? \
(AverageLuma(crop(pre, 0, 0, -w+15, 0)) < thr ? \
(AverageLuma(crop(pre, 0, 0, -w+16, 0)) < thr ? 16 : 15) : 14) : 13) : 12) : 11) : 10) : 9) : 8) : 7) : 6) : 5) : 4) : 3) : 2) : 1) : 0

x2 = AverageLuma(Crop(pre, w-1, 0, 0, 0)) < thr ? \
(AverageLuma(Crop(pre, w-2, 0, 0, 0)) < thr ? \
(AverageLuma(Crop(pre, w-3, 0, 0, 0)) < thr ? \
(AverageLuma(Crop(pre, w-4, 0, 0, 0)) < thr ? \
(AverageLuma(Crop(pre, w-5, 0, 0, 0)) < thr ? \
(AverageLuma(Crop(pre, w-6, 0, 0, 0)) < thr ? \
(AverageLuma(Crop(pre, w-7, 0, 0, 0)) < thr ? \
(AverageLuma(Crop(pre, w-8, 0, 0, 0)) < thr ? \
(AverageLuma(Crop(pre, w-9, 0, 0, 0)) < thr ? \
(AverageLuma(Crop(pre, w-10, 0, 0, 0)) < thr ? \
(AverageLuma(Crop(pre, w-11, 0, 0, 0)) < thr ? \
(AverageLuma(Crop(pre, w-12, 0, 0, 0)) < thr ? \
(AverageLuma(Crop(pre, w-13, 0, 0, 0)) < thr ? \
(AverageLuma(Crop(pre, w-14, 0, 0, 0)) < thr ? \
(AverageLuma(Crop(pre, w-15, 0, 0, 0)) < thr ? \
(AverageLuma(Crop(pre, w-16, 0, 0, 0)) < thr ? 16 : 15) : 14) : 13) : 12) : 11) : 10) : 9) : 8) : 7) : 6) : 5) : 4) : 3) : 2) : 1) : 0

y1 = AverageLuma(Crop(pre, 0, 0, 0, -h+1)) < thr ? \
(AverageLuma(Crop(pre, 0, 0, 0, -h+2)) < thr ? \
(AverageLuma(Crop(pre, 0, 0, 0, -h+3)) < thr ? \
(AverageLuma(Crop(pre, 0, 0, 0, -h+4)) < thr ? \
(AverageLuma(Crop(pre, 0, 0, 0, -h+5)) < thr ? \
(AverageLuma(Crop(pre, 0, 0, 0, -h+6)) < thr ? \
(AverageLuma(Crop(pre, 0, 0, 0, -h+7)) < thr ? \
(AverageLuma(Crop(pre, 0, 0, 0, -h+8)) < thr ? \
(AverageLuma(Crop(pre, 0, 0, 0, -h+9)) < thr ? \
(AverageLuma(Crop(pre, 0, 0, 0, -h+10)) < thr ? \
(AverageLuma(Crop(pre, 0, 0, 0, -h+11)) < thr ? \
(AverageLuma(Crop(pre, 0, 0, 0, -h+12)) < thr ? \
(AverageLuma(Crop(pre, 0, 0, 0, -h+13)) < thr ? \
(AverageLuma(Crop(pre, 0, 0, 0, -h+14)) < thr ? \
(AverageLuma(Crop(pre, 0, 0, 0, -h+15)) < thr ? \
(AverageLuma(Crop(pre, 0, 0, 0, -h+16)) < thr ? 16 : 15) : 14) : 13) : 12) : 11) : 10) : 9) : 8) : 7) : 6) : 5) : 4) : 3) : 2) : 1) : 0

y2 = AverageLuma(Crop(pre, 0, h-1, 0, 0)) < thr ? \
(AverageLuma(Crop(pre, 0, h-2, 0, 0)) < thr ? \
(AverageLuma(Crop(pre, 0, h-3, 0, 0)) < thr ? \
(AverageLuma(Crop(pre, 0, h-4, 0, 0)) < thr ? \
(AverageLuma(Crop(pre, 0, h-5, 0, 0)) < thr ? \
(AverageLuma(Crop(pre, 0, h-6, 0, 0)) < thr ? \
(AverageLuma(Crop(pre, 0, h-7, 0, 0)) < thr ? \
(AverageLuma(Crop(pre, 0, h-8, 0, 0)) < thr ? \
(AverageLuma(Crop(pre, 0, h-9, 0, 0)) < thr ? \
(AverageLuma(Crop(pre, 0, h-10, 0, 0)) < thr ? \
(AverageLuma(Crop(pre, 0, h-11, 0, 0)) < thr ? \
(AverageLuma(Crop(pre, 0, h-12, 0, 0)) < thr ? \
(AverageLuma(Crop(pre, 0, h-13, 0, 0)) < thr ? \
(AverageLuma(Crop(pre, 0, h-14, 0, 0)) < thr ? \
(AverageLuma(Crop(pre, 0, h-15, 0, 0)) < thr ? \
(AverageLuma(Crop(pre, 0, h-16, 0, 0)) < thr ? 16 : 15) : 14) : 13) : 12) : 11) : 10) : 9) : 8) : 7) : 6) : 5) : 4) : 3) : 2) : 1) : 0

x1 = x1 > 0 ? x1+pad : x1
x2 = x2 > 0 ? x2+pad : x2
y1 = y1 > 0 ? y1+pad : y1
y2 = y2 > 0 ? y2+pad : y2

PreBlur = 0 #If > 0 then there may be an error
PostBlur = 0 #Max(x1, x2, y1, y2, 4) #If > 0 then there may be an error
b_fix = (x1 + x2 + y1 + y2) > 0
max_fix = Max(x1, x2, y1, y2)

msk = b_fix && max_fix >= 8 ? pre.mt_lut("0", chroma="-128").LetterBox(y1, y2, x1, x2, color=$ffffff).ConvertToYV12() : \
pre.mt_lut("0", chroma="-128")

fixed = b_fix ? max_fix < 8 ? FillBorders(left=x1, top=y1, right=x2, bottom=y2, mode=0, y=3, u=3, v=3) : \
InpaintLogo(mask=msk, Radius=Max(x1,x2,y1,y2,5)*1.5, PreBlur=PreBlur, PostBlur=PostBlur) : \
c

debug == true ? mt_merge(fixed, \
c.mt_lut("0", chroma="-128").ConvertToRGB().LetterBox(y1, y2, x1, x2, color=$ff3333).ConvertToYV12(), \
pre.mt_lut("0", chroma="-128").LetterBox(y1, y2, x1, x2, color=$ffffff).ConvertToYV12(), \
luma=true)\
.Subtitle("Black borders:\n "+string(y1)+"\n "+string(x1)+" "+string(x2)+"\n "+string(y2), lsp = 10, text_color=$ff3333, font="Courier New", x=MaxB+5, y=MaxB+5) : \
fixed
""", args="c, thr, pad, MaxB, debug, w, h")
}


Use it with MaxBorders = 16
Stab_Light(zoom = 1.00, mirror = false, MaxBorders=16, FixFalPos = true, FixBorders = true, debug = false)

manono
13th March 2021, 08:46
Hi,

And thank you for taking a look. I'm not entirely sure I understand you correctly, but if change the filter settings to:

Stab_Light(zoom = 1.00, mirror = false, MaxBorders=16, FixFalPos = true, FixBorders = true, debug = false)

I still get the same error message. Should this filter be used only with AviSynth+?

Arx1meD
13th March 2021, 09:05
Thank you for taking the time to test this script.
Have you replaced the FillBorders2 function?
Can you write what kind of error? As before: [Script Clip], line 8 column 21 ?

Try using Stab3 until I find the error.
Perhaps you are right and my FillBorders2 works correctly only in AVS+.

Arx1meD
13th March 2021, 15:20
manono, I checked my old version of the FillBorders2 function in AviSynth 2.6 and there was no error.
Whatever I do I have failed to fix a bug in gScriptClip ([Script Clip], line 8 column 21). I guess I'm using "for(i = 1, MaxB, 1)..." incorrectly.

StainlessS
13th March 2021, 15:23
I dont think Manono quite understood that you intended for him to replace posted Fillborders2 with your older one in post #7.
I'll try with both old and new and see if I can find solution more like your inital posted Fillborders2.

EDIT: OK, you just beat me with your post.

Arx1meD
13th March 2021, 15:59
After many attempts to understand the cause of the error, I am inclined to believe that AviSynth 2.6 does not support "{" "}" inside a function.

StainlessS
13th March 2021, 16:23
It should with GScript installed. [ so long as used with eg for(){} : If/Else, or While(){} ].

EDIT: If below GG() function is held in eg GG.avsi, and GImport'ed instead of inline with GSCript(""" ... """) would work OK.

GScript("""
Function GG() {
t=0
for(i=0,10,1) {
t=t+i
if(i==5) {
i=10 # Break
}
}
return t
}
""")

blankclip
x = GG()

Subtitle(String(x))
return last

Arx1meD
13th March 2021, 16:35
Need to use this GScript (Function GG) for AviSynth 2.6 only or for any version?

StainlessS
13th March 2021, 17:18
Sorry, that was just an example showing that can use '{' '}' using GScript with V2.60 Std.
Example can use as given in prev post, ie surrounded by GScript(""" ... """),
or without the GScript(""" ... """) thing if GImport'ed.
But of course you need GScript dll in plugins.

Avs+ has GScript functionality builtin, does not need the GScript(""" ... """) thing, nor GImport(), nor the plugin dll.

If we get a solution, then avs 2.60 std [with gscript dll loaded] needs to GImport the avsi, and Avs+ needs to Import, or have it in plugins dir.

Arx1meD
13th March 2021, 17:47
And there is one more question left. Is it correct to use break?

StainlessS
13th March 2021, 19:44
Break is not implemented in GScript [despite my asking for it some years ago].

Arx1meD
13th March 2021, 20:16
It is strange that AVS+ did not cause an error. Probably break is implemented in AVS+.
StainlessS, do you think it is necessary to replace break with i = MaxB ?

manono
13th March 2021, 20:18
Have you replaced the FillBorders2 function?
As I mentioned last time, I wasn't sure I understood your earlier post. Your list of required filters in your first post mentions only FillBorders. There's nothing about FillBorders2. Okay, I copied it from your post and added an Import line to the script.

Since the last time I ran an encode using other filters and now have a Lagarith AVI with which to work. The current script:

AVISource("Movie.avi")
Stab_Light(zoom = 1.00, mirror = false, MaxBorders=16, FixFalPos = true, FixBorders = true, debug = false)
Return Last

I opened it in VDub and got a different error message this time:

"_lut: unsupported colorspace, masktools only support planar YUV colorspaces (YV12, YV16, YV
([ScriptClip], line 83)"

The message appears to be cut off after the 'YV'. The AVI is already in YV12.

Arx1meD
13th March 2021, 20:40
manono, which version of MaskTools2 (http://avisynth.nl/index.php/MaskTools2#Filters) are you using?
The problem in mt_lut. In http://avisynth.nl says that mt_lut supports the Y8 color format.

msk = b_fix && max_fix >= 8 ? pre.mt_lut("0", chroma="-128").LetterBox(y1, y2, x1, x2, color=$ffffff).ConvertToYV12() : \
pre.mt_lut("0", chroma="-128") # line 83
mt_lut uses clip:
pre = ConvertToY8()#.ColorYUV(off_y=-15)

Try updating to the latest version of MaskTools2.

manono
13th March 2021, 21:06
The masktools2 is 2.2.10.0 dated 6/12/2017. It's served me well since then but you think I should get a newer version? Okay, replaced it with MaskTools2 2.2.26, pinterf from 9/4/2020.

The same message appears:

"_lut: unsupported colorspace, masktools only support planar YUV colorspaces (YV12, YV16, YV"
([ScriptClip], line 83)"

Arx1meD
13th March 2021, 21:19
This is really weird because I am using the latest version too. I can't understand what the problem is.
Perhaps you need to use the Stab3 (https://raw.githubusercontent.com/realfinder/AVS-Stuff/master/avs%202.5%20and%20up/StabilizationToolsPack.avsi) function. It works the same and gives the same result, but works slower.

pinterf
13th March 2021, 21:30
There must be then another masktool somewhere among your plugins.

manono
13th March 2021, 21:31
Okay, I'll give it a try. Thanks very much for helping. Good luck with your filter and I hope it finds wide use.

StainlessS
14th March 2021, 00:41
Script removed, see https://forum.doom9.org/showthread.php?p=1938110#post1938110

manono
14th March 2021, 02:01
EDIT: Damn, it was staring me in the face all the time.

Meaning you might come up with a solution one of these days? I'll keep my fingers crossed. Thanks for your and Arx1meD's efforts.

Arx1meD
14th March 2021, 05:57
manono, I was thinking about the error in mt_lut (([ScriptClip], line 83)) and changed my old version of the FillBorders2 function (post #7 (https://forum.doom9.org/showthread.php?p=1937981#post1937981)) for you.

Try using this version:function FillBorders2(clip c, int "thr", int "pad", int "MaxBorders", bool "debug") {
thr = Default(thr, 5)
pad = Default(pad, 0)
MaxB = Default(MaxBorders, 16)
debug = Default(debug, false)

w = width(c)
h = height(c)

gScriptClip(c, """
pre = ConvertToY8()#.ColorYUV(off_y=-15)

x1 = AverageLuma(crop(pre, 0, 0, -w+1, 0)) < thr ? \
(AverageLuma(crop(pre, 0, 0, -w+2, 0)) < thr ? \
(AverageLuma(crop(pre, 0, 0, -w+3, 0)) < thr ? \
(AverageLuma(crop(pre, 0, 0, -w+4, 0)) < thr ? \
(AverageLuma(crop(pre, 0, 0, -w+5, 0)) < thr ? \
(AverageLuma(crop(pre, 0, 0, -w+6, 0)) < thr ? \
(AverageLuma(crop(pre, 0, 0, -w+7, 0)) < thr ? \
(AverageLuma(crop(pre, 0, 0, -w+8, 0)) < thr ? \
(AverageLuma(crop(pre, 0, 0, -w+9, 0)) < thr ? \
(AverageLuma(crop(pre, 0, 0, -w+10, 0)) < thr ? \
(AverageLuma(crop(pre, 0, 0, -w+11, 0)) < thr ? \
(AverageLuma(crop(pre, 0, 0, -w+12, 0)) < thr ? \
(AverageLuma(crop(pre, 0, 0, -w+13, 0)) < thr ? \
(AverageLuma(crop(pre, 0, 0, -w+14, 0)) < thr ? \
(AverageLuma(crop(pre, 0, 0, -w+15, 0)) < thr ? \
(AverageLuma(crop(pre, 0, 0, -w+16, 0)) < thr ? 16 : 15) : 14) : 13) : 12) : 11) : 10) : 9) : 8) : 7) : 6) : 5) : 4) : 3) : 2) : 1) : 0

x2 = AverageLuma(Crop(pre, w-1, 0, 0, 0)) < thr ? \
(AverageLuma(Crop(pre, w-2, 0, 0, 0)) < thr ? \
(AverageLuma(Crop(pre, w-3, 0, 0, 0)) < thr ? \
(AverageLuma(Crop(pre, w-4, 0, 0, 0)) < thr ? \
(AverageLuma(Crop(pre, w-5, 0, 0, 0)) < thr ? \
(AverageLuma(Crop(pre, w-6, 0, 0, 0)) < thr ? \
(AverageLuma(Crop(pre, w-7, 0, 0, 0)) < thr ? \
(AverageLuma(Crop(pre, w-8, 0, 0, 0)) < thr ? \
(AverageLuma(Crop(pre, w-9, 0, 0, 0)) < thr ? \
(AverageLuma(Crop(pre, w-10, 0, 0, 0)) < thr ? \
(AverageLuma(Crop(pre, w-11, 0, 0, 0)) < thr ? \
(AverageLuma(Crop(pre, w-12, 0, 0, 0)) < thr ? \
(AverageLuma(Crop(pre, w-13, 0, 0, 0)) < thr ? \
(AverageLuma(Crop(pre, w-14, 0, 0, 0)) < thr ? \
(AverageLuma(Crop(pre, w-15, 0, 0, 0)) < thr ? \
(AverageLuma(Crop(pre, w-16, 0, 0, 0)) < thr ? 16 : 15) : 14) : 13) : 12) : 11) : 10) : 9) : 8) : 7) : 6) : 5) : 4) : 3) : 2) : 1) : 0

y1 = AverageLuma(Crop(pre, 0, 0, 0, -h+1)) < thr ? \
(AverageLuma(Crop(pre, 0, 0, 0, -h+2)) < thr ? \
(AverageLuma(Crop(pre, 0, 0, 0, -h+3)) < thr ? \
(AverageLuma(Crop(pre, 0, 0, 0, -h+4)) < thr ? \
(AverageLuma(Crop(pre, 0, 0, 0, -h+5)) < thr ? \
(AverageLuma(Crop(pre, 0, 0, 0, -h+6)) < thr ? \
(AverageLuma(Crop(pre, 0, 0, 0, -h+7)) < thr ? \
(AverageLuma(Crop(pre, 0, 0, 0, -h+8)) < thr ? \
(AverageLuma(Crop(pre, 0, 0, 0, -h+9)) < thr ? \
(AverageLuma(Crop(pre, 0, 0, 0, -h+10)) < thr ? \
(AverageLuma(Crop(pre, 0, 0, 0, -h+11)) < thr ? \
(AverageLuma(Crop(pre, 0, 0, 0, -h+12)) < thr ? \
(AverageLuma(Crop(pre, 0, 0, 0, -h+13)) < thr ? \
(AverageLuma(Crop(pre, 0, 0, 0, -h+14)) < thr ? \
(AverageLuma(Crop(pre, 0, 0, 0, -h+15)) < thr ? \
(AverageLuma(Crop(pre, 0, 0, 0, -h+16)) < thr ? 16 : 15) : 14) : 13) : 12) : 11) : 10) : 9) : 8) : 7) : 6) : 5) : 4) : 3) : 2) : 1) : 0

y2 = AverageLuma(Crop(pre, 0, h-1, 0, 0)) < thr ? \
(AverageLuma(Crop(pre, 0, h-2, 0, 0)) < thr ? \
(AverageLuma(Crop(pre, 0, h-3, 0, 0)) < thr ? \
(AverageLuma(Crop(pre, 0, h-4, 0, 0)) < thr ? \
(AverageLuma(Crop(pre, 0, h-5, 0, 0)) < thr ? \
(AverageLuma(Crop(pre, 0, h-6, 0, 0)) < thr ? \
(AverageLuma(Crop(pre, 0, h-7, 0, 0)) < thr ? \
(AverageLuma(Crop(pre, 0, h-8, 0, 0)) < thr ? \
(AverageLuma(Crop(pre, 0, h-9, 0, 0)) < thr ? \
(AverageLuma(Crop(pre, 0, h-10, 0, 0)) < thr ? \
(AverageLuma(Crop(pre, 0, h-11, 0, 0)) < thr ? \
(AverageLuma(Crop(pre, 0, h-12, 0, 0)) < thr ? \
(AverageLuma(Crop(pre, 0, h-13, 0, 0)) < thr ? \
(AverageLuma(Crop(pre, 0, h-14, 0, 0)) < thr ? \
(AverageLuma(Crop(pre, 0, h-15, 0, 0)) < thr ? \
(AverageLuma(Crop(pre, 0, h-16, 0, 0)) < thr ? 16 : 15) : 14) : 13) : 12) : 11) : 10) : 9) : 8) : 7) : 6) : 5) : 4) : 3) : 2) : 1) : 0

x1 = x1 > 0 ? x1+pad : x1
x2 = x2 > 0 ? x2+pad : x2
y1 = y1 > 0 ? y1+pad : y1
y2 = y2 > 0 ? y2+pad : y2

PreBlur = 0 #If > 0 then there may be an error
PostBlur = 0 #Max(x1, x2, y1, y2, 4) #If > 0 then there may be an error
b_fix = (x1 + x2 + y1 + y2) > 0
max_fix = Max(x1, x2, y1, y2)

msk = b_fix && max_fix >= 8 ? c.mt_lut("0", chroma="-128").ConvertToY8().LetterBox(y1, y2, x1, x2, color=$ffffff).ConvertToYV12() : \
c.mt_lut("0", chroma="-128")

fixed = b_fix ? max_fix < 8 ? FillBorders(left=x1, top=y1, right=x2, bottom=y2, mode=0, y=3, u=3, v=3) : \
InpaintLogo(mask=msk, Radius=Max(x1,x2,y1,y2,5)*1.5, PreBlur=PreBlur, PostBlur=PostBlur) : \
c

debug == true ? mt_merge(fixed, \
c.mt_lut("0", chroma="-128").ConvertToRGB().LetterBox(y1, y2, x1, x2, color=$ff3333).ConvertToYV12(), \
c.mt_lut("0", chroma="-128").ConvertToY8().LetterBox(y1, y2, x1, x2, color=$ffffff).ConvertToYV12(), \
luma=true)\
.Subtitle("Black borders:\n "+string(y1)+"\n "+string(x1)+" "+string(x2)+"\n "+string(y2), lsp = 10, text_color=$ff3333, font="Courier New", x=MaxB+5, y=MaxB+5) : \
fixed
""", args="c, thr, pad, MaxB, debug, w, h")
}

Changes are highlighted in red

manono
14th March 2021, 07:28
...and changed my old version of the FillBorders2 function (post #7 (https://forum.doom9.org/showthread.php?p=1937981#post1937981)) for you.

I hope not just for me. Surely I'm not the only one still using the old AviSynth 2.6.0?

In any event, it now works! No more message defacing the top of the movie. And thank you so much. You might want to edit your first post and mention that 2.6.0 users should change the FillBorders2.avs they have to this edited version. And mention it also in the list of required filters.

Thanks again.

Arx1meD
14th March 2021, 08:50
The Stab_Light function works correctly and does not need to be changed. The problem in FillBorders2 only.
StainlessS, maybe we can find a good example in the AWB_131201.avsi (https://forum.doom9.org/showthread.php?t=168062) script?

StainlessS
14th March 2021, 21:38
Maybe check this out, Arx1meD needs to check that it works as for original.

stab_light_ssS.avsi [EDIT: Remove the _ssS when checked out ok ]

/*
# If Avs+, then use Import, else use GImport with Gscript installed.

IsPlus = (FindStr(VersionString,"AviSynth+")!=0)
AVSI_FN = ".\Stab_Light_ssS.avsi"
(IsPlus) ? Import(AVSI_FN) : GImport(AVSI_FN)

*/

Function Stab_Light(clip clp, int "ts", int "range", int "dxmax", int "dymax", float "zoom", float "PAR",
\ bool "mirror", int "MaxBorders", bool "FixBorders", bool "FixFalPos", bool "debug") {
ts = Default(ts, 7) # frames to temporal average for better motion estimation (max. 7)
range = Default(range, 1) # frames before/after to estimate motion
dxmax = Default(dxmax, Round(clp.Width()/180.0)) #maximum deviation in pixels
dymax = Default(dymax, dxmax) # x, and y should be the same
zoom = Default(zoom, 1) # maximum zoom factor (1 disabled)
PAR = Default(PAR, 1.0) # PAR of your source
mirror = Default(mirror, false) # Edge filling
MaxBorders = Default(MaxBorders, Max(Round(clp.Width()/60.0), 20))
FixBorders = Default(FixBorders, true)
FixFalPos = Default(FixFalPos, true) # Fixes borders of MaxBorders more pixels wide
debug = Default(debug, false)

temp = TemporalSoften(clp, ts, 255, 255, 25, 2) # SC thr to 25 otherwise pans will stutter
rep = Repair(temp, TemporalSoften(clp, 1, 255, 255, 25, 2))
inter = Interleave(rep, clp) # temporal stable (better subpixel detection)
pre_clp = inter.mt_lut(expr="x 20 1 x 127.5 / - 21 ^ pi x * 255 / cos 21 ^ - * 1.05 * -", Y=3, U=2, V=2).RemoveGrain(mode=17) #ColorYUV(levels="PC->TV")

mdata = DePanEstimate(pre_clp, range=range, pixaspect=PAR, trust=0, dxmax=dxmax, dymax=dymax, zoommax=zoom)
dp = DePan(inter, data=mdata, offset=-1, mirror=mirror?15:0, pixaspect=PAR, matchfields=false, subpixel=2)
stab = SelectEvery(dp, 2, 0)

w = Width(clp)
h = Height(clp)
thr = 10

pre = stab.ConvertToY8()#.ColorYUV(off_y=-15)
stab = (!mirror && FixFalPos)
\ ? gScriptClip(stab, "
AverageLuma(Crop(pre, 0, 0, -w+MaxBorders+1, 0)) < thr ? clp : \
AverageLuma(Crop(pre, w-MaxBorders-1, 0, 0, 0)) < thr ? clp : \
AverageLuma(Crop(pre, 0, 0, 0, -h+MaxBorders+1)) < thr ? clp : \
AverageLuma(Crop(pre, 0, h-MaxBorders-1, 0, 0)) < thr ? clp : Last
Return Last
", args="clp, pre, MaxBorders, w, h, thr",Local=true)
\ : stab
Return (!mirror && FixBorders)
\ ? FillBorders2(stab, thr=thr, pad=1, MaxBorders=MaxBorders, debug=debug)
\ : stab
}

Function FillBorders2(clip c, int "thr", int "pad", int "MaxBorders", bool "debug") {
Function FillBorders2_dBugS(int y1,int x1,int x2,int y2) {
Return "Black borders:\n "+string(y1)+"\n "+string(x1)+" "+string(x2)+"\n "+ string(y2)
}
thr = Default(thr, 5)
pad = Default(pad, 0)
MaxB = Default(MaxBorders, Max(Round(c.Width()/60.0), 20))
debug = Default(debug, false)
w = width(c)
h = height(c)
cblank = c.mt_lut( "0", chroma="-128")
pre = c.ConvertToY8()#.ColorYUV(off_y=-15) # Moved out of gScriptclip
preblank = pre.mt_lut("0", chroma="-128")
font="Courier New"
gScriptClip(c, "
x1 = Pre.FillBorders2_TestBord(0, thr, MaxB, Pad)
x2 = Pre.FillBorders2_TestBord(1, thr, MaxB, Pad)
y1 = Pre.FillBorders2_TestBord(2, thr, MaxB, Pad)
y2 = Pre.FillBorders2_TestBord(3, thr, MaxB, Pad)
PreBlur = 0 # If > 0 then there may be an error
PostBlur = 0 # Max(x1, x2, y1, y2, 4) #If > 0 then there may be an error
b_fix = (x1 + x2 + y1 + y2) > 0
max_fix = Max(x1, x2, y1, y2)
msk = (b_fix && max_fix >= 8)
\ ? preblank.LetterBox(y1, y2, x1, x2, color_yuv=$ffffff).ConvertToYV12()
\ : preblank
fixed = (!b_fix) ? Last
\ : (max_fix < 8) ? FillBorders(left=x1, top=y1, right=x2, bottom=y2, mode=0, y=3, u=3, v=3)
\ : InpaintLogo(mask=msk, Radius=Max(x1,x2,y1,y2,5)*1.5, PreBlur=PreBlur, PostBlur=PostBlur)
(debug)
\ ? mt_merge(fixed, cblank.ConvertToRGB().LetterBox(y1, y2, x1, x2, color=$ff3333)
\ .ConvertToYV12(),preblank.LetterBox(y1, y2, x1, x2, color_yuv=$ffffff).ConvertToYV12(), luma=true)
\ .Subtitle(FillBorders2_dBugS(y1,x1,x2,y2), lsp = 10, text_color=$ff3333, font=Font, x=MaxB+5, y=MaxB+5)
\ : fixed
Return Last
", args="pre, cblank, preblank, thr, pad, MaxB, debug, w, h,Font",Local=true)
}

Function FillBorders2_TestBord(clip c,int edge, Float thr, int MaxB, Int Pad) {
# edge: 0=lft, 1=rgt, 2=top, 3=bot
result=0 w=c.width h=c.height
for(i = 1, MaxB) {
crpd=c.Crop((edge==1)?w-i:0,(edge==3)?h-i:0,(edge==0)?i:0,(edge==2)?i:0)
if(crpd.AverageLuma < thr) { result=i+pad }
else { i=MaxB }
}
return result
}


Test.avs

IsPlus = (FindStr(VersionString,"AviSynth+")!=0)
AVSI_FN = ".\Stab_Light_ssS.avsi"
(IsPlus) ? Import(AVSI_FN) : GImport(AVSI_FN)

ORG=AviSource(".\Src.Avi") # OP's sample [Left Half] , After ffmpeg conversion to UT_Video AVI
STB = ORG.Stab_Light(zoom = 1.00, mirror = false, FixFalPos = true, FixBorders = true, debug = false)
D=STB.Subtract(ORG)
T=StackHorizontal(ORG,STB)
B=StackHorizontal(D,D)
StackVertical(T,B)


EDIT: Only the FillBorders2_TestBord() func actually requires GImport or wrapped in GScript(""" ... """) when not Avs+.

EDIT: If using AvsInit(), can just dump the avsi into GIMPORT folder [whether Avs+ or not].

EDIT: FillBorders2_TestBord(thr) implemented as Float.

manono
15th March 2021, 02:38
1. I have to use GImport when opening your Stab_Light.avs, StainlessS
2. I also have to include an Import line for FillBorders2 that Arx1meD modified. I use Import and LoadPlugin lines for all filters. I don't have a functioning AviSynth Plugins folder. I think that means the FillBorders2 in your all-in-one function isn't working for me. If I don't, and depend on the one in your script, I get:

AviSynth open failure:
mt_lut: unsupported colorspace. masktools only support planar
YUV colorspaces (YV12, YV16, YV24).
H:\AviSynth\Plugins\Stab_Light.avs, line 64
H:\AviSynth\Plugins\Stab_Light.avs, line 49
G:\AviSynth\Ek Do Teen\2.Stabilize.avs, line 63

One of them refers to my script, probably the Stab_Light.avs line:

Stab_Light(zoom = 1.00, mirror = false, MaxBorders=16, FixFalPos = true, FixBorders = true, debug = false)

I never used the "_ssS". Maybe that's the reason but I don't understand what it's doing in there to begin with. And thanks for putting so much time into this. It's much appreciated.

Arx1meD
15th March 2021, 06:32
StainlessS, I checked your version of the script. Everything works just like the original.
However, there was an error in AviSynth 2.6.
I have inserted the FillBorders2_TestBord function wrapped in GScript("""...""") in FillBorders2 and there was no error.

We can say that this version is for AviSynth 2.6 only and needs a GScript (https://forum.doom9.org/showthread.php?t=147846) plugin. (Test version. I do not recommend using it.)

Function Stab_Light(clip clp, int "ts", int "range", int "dxmax", int "dymax", float "zoom", float "PAR",
\ bool "mirror", int "MaxBorders", bool "FixBorders", bool "FixFalPos", bool "debug") {
ts = Default(ts, 7) # frames to temporal average for better motion estimation (max. 7)
range = Default(range, 1) # frames before/after to estimate motion
dxmax = Default(dxmax, Round(clp.Width()/180.0)) #maximum deviation in pixels
dymax = Default(dymax, dxmax) # x, and y should be the same
zoom = Default(zoom, 1) # maximum zoom factor (1 disabled)
PAR = Default(PAR, 1.0) # PAR of your source
mirror = Default(mirror, false) # Edge filling
MaxBorders = Default(MaxBorders, Max(Round(clp.Width()/60.0), 20))
FixBorders = Default(FixBorders, true)
FixFalPos = Default(FixFalPos, true) # Fixes borders of MaxBorders more pixels wide
debug = Default(debug, false)

temp = TemporalSoften(clp, ts, 255, 255, 25, 2) # SC thr to 25 otherwise pans will stutter
rep = Repair(temp, TemporalSoften(clp, 1, 255, 255, 25, 2))
inter = Interleave(rep, clp) # temporal stable (better subpixel detection)
pre_clp = inter.mt_lut(expr="x 20 1 x 127.5 / - 21 ^ pi x * 255 / cos 21 ^ - * 1.05 * -", Y=3, U=2, V=2).RemoveGrain(mode=17) #ColorYUV(levels="PC->TV")

mdata = DePanEstimate(pre_clp, range=range, pixaspect=PAR, trust=0, dxmax=dxmax, dymax=dymax, zoommax=zoom)
dp = DePan(inter, data=mdata, offset=-1, mirror=mirror?15:0, pixaspect=PAR, matchfields=false, subpixel=2)
stab = SelectEvery(dp, 2, 0)

w = Width(clp)
h = Height(clp)
thr = 10

pre = stab.ConvertToY8()#.ColorYUV(off_y=-15)
stab = (!mirror && FixFalPos)
\ ? gScriptClip(stab, "
AverageLuma(Crop(pre, 0, 0, -w+MaxBorders+1, 0)) < thr ? clp : \
AverageLuma(Crop(pre, w-MaxBorders-1, 0, 0, 0)) < thr ? clp : \
AverageLuma(Crop(pre, 0, 0, 0, -h+MaxBorders+1)) < thr ? clp : \
AverageLuma(Crop(pre, 0, h-MaxBorders-1, 0, 0)) < thr ? clp : Last
Return Last
", args="clp, pre, MaxBorders, w, h, thr",Local=true)
\ : stab
Return (!mirror && FixBorders)
\ ? FillBorders2(stab, thr=thr, pad=1, MaxBorders=MaxBorders, debug=debug)
\ : stab
}

Function FillBorders2(clip c, int "thr", int "pad", int "MaxBorders", bool "debug") {
Function FillBorders2_dBugS(int y1,int x1,int x2,int y2) {
Return "Black borders:\n "+string(y1)+"\n "+string(x1)+" "+string(x2)+"\n "+ string(y2)
}
thr = Default(thr, 5)
pad = Default(pad, 0)
MaxB = Default(MaxBorders, Max(Round(c.Width()/60.0), 20))
debug = Default(debug, false)
w = width(c)
h = height(c)
cblank = c.mt_lut("0", chroma="-128")
pre = c.ConvertToY8()#.ColorYUV(off_y=-15) # Moved out of gScriptclip
preblank = cblank.ConvertToY8()
font="Courier New"
gScriptClip(c, """
GScript("
Function FillBorders2_TestBord(clip c,int edge, Float thr, int MaxB, Int Pad) {
# edge: 0=lft, 1=rgt, 2=top, 3=bot
result=0 w=c.width h=c.height
for(i = 1, MaxB) {
crpd=c.Crop((edge==1)?w-i:0,(edge==3)?h-i:0,(edge==0)?i:0,(edge==2)?i:0)
if(crpd.AverageLuma < thr) { result=i+pad }
else { i=MaxB }
}
return result
}
")
x1 = Pre.FillBorders2_TestBord(0, thr, MaxB, Pad)
x2 = Pre.FillBorders2_TestBord(1, thr, MaxB, Pad)
y1 = Pre.FillBorders2_TestBord(2, thr, MaxB, Pad)
y2 = Pre.FillBorders2_TestBord(3, thr, MaxB, Pad)
PreBlur = 0 # If > 0 then there may be an error
PostBlur = 0 # Max(x1, x2, y1, y2, 4) #If > 0 then there may be an error
b_fix = (x1 + x2 + y1 + y2) > 0
max_fix = Max(x1, x2, y1, y2)
msk = (b_fix && max_fix >= 8)
\ ? preblank.LetterBox(y1, y2, x1, x2, color=$ffffff).ConvertToYV12()
\ : cblank
fixed = (!b_fix) ? Last
\ : (max_fix < 8) ? FillBorders(left=x1, top=y1, right=x2, bottom=y2, mode=0, y=3, u=3, v=3)
\ : InpaintLogo(mask=msk, Radius=Max(x1,x2,y1,y2,5)*1.5, PreBlur=PreBlur, PostBlur=PostBlur)
(debug)
\ ? mt_merge(fixed, cblank.ConvertToRGB().LetterBox(y1, y2, x1, x2, color=$ff3333)
\ .ConvertToYV12(),preblank.LetterBox(y1, y2, x1, x2, color=$ffffff).ConvertToYV12(), luma=true)
\ .Subtitle(FillBorders2_dBugS(y1,x1,x2,y2), lsp = 10, text_color=$ff3333, font=Font, x=MaxB+5, y=MaxB+5)
\ : fixed
Return Last
""", args="pre, cblank, preblank, thr, pad, MaxB, debug, w, h,Font",Local=true)
}

Changes are highlighted in blue. What I'm not sure about is highlighted in red. In the AviSynth help: Crop(clip, +left, +top, -right, -bottom)

Оr in the original script just wrapped in GScript ("...") for(i = 1, MaxB, 1) {... . (Test version)

function FillBorders2(clip c, float "thr", int "pad", int "MaxBorders", bool "debug") {
thr = Default(thr, 5)
pad = Default(pad, 0)
MaxB = Default(MaxBorders, Max(Round(c.Width()/60.0), 20))
debug = Default(debug, false)

w = width(c)
h = height(c)

gScriptClip(c, """
pre = ConvertToY8()#.ColorYUV(off_y=-15)

x1 = 0
x2 = 0
y1 = 0
y2 = 0
GScript("
for(i = 1, MaxB, 1) {
if (AverageLuma(Crop(pre, 0, 0, -w+i, 0)) < thr) {x1 = i + pad} else {i=MaxB}
}
for(i = 1, MaxB, 1) {
if (AverageLuma(Crop(pre, w-i, 0, 0, 0)) < thr) {x2 = i + pad} else {i=MaxB}
}
for(i = 1, MaxB, 1) {
if (AverageLuma(Crop(pre, 0, 0, 0, -h+i)) < thr) {y1 = i + pad} else {i=MaxB}
}
for(i = 1, MaxB, 1) {
if (AverageLuma(Crop(pre, 0, h-i, 0, 0)) < thr) {y2 = i + pad} else {i=MaxB}
}
")

PreBlur = 0 #If > 0 then there may be an error
PostBlur = 0 #Max(x1, x2, y1, y2, 4) #If > 0 then there may be an error
b_fix = (x1 + x2 + y1 + y2) > 0
max_fix = Max(x1, x2, y1, y2)

msk = b_fix && max_fix >= 8 ? c.mt_lut("0", chroma="-128").ConvertToY8().LetterBox(y1, y2, x1, x2, color=$ffffff).ConvertToYV12() : \
c.mt_lut("0", chroma="-128")

fixed = b_fix ? max_fix < 8 ? FillBorders(left=x1, top=y1, right=x2, bottom=y2, mode=0, y=3, u=3, v=3) : \
InpaintLogo(mask=msk, Radius=Max(x1,x2,y1,y2,5)*1.5, PreBlur=PreBlur, PostBlur=PostBlur) : \
c

debug == true ? mt_merge(fixed, \
c.mt_lut("0", chroma="-128").ConvertToRGB().LetterBox(y1, y2, x1, x2, color=$ff3333).ConvertToYV12(), \
c.mt_lut("0", chroma="-128").ConvertToY8().LetterBox(y1, y2, x1, x2, color=$ffffff).ConvertToYV12(), \
luma=true)\
.Subtitle("Black borders:\n "+string(y1)+"\n "+string(x1)+" "+string(x2)+"\n "+string(y2), lsp = 10, text_color=$ff3333, font="Courier New", x=MaxB+5, y=MaxB+5) : \
fixed
""", args="c, thr, pad, MaxB, debug, w, h")
}

Arx1meD
15th March 2021, 15:13
So. There is a good solution:VersionNumber() > 2.60 ? Eval("
for(i = 1, MaxB) { if (AverageLuma(Crop(pre, 0, 0, -w+i, 0)) < thr) {x1 = i + pad} else {i = MaxB} }
for(i = 1, MaxB) { if (AverageLuma(Crop(pre, w-i, 0, 0, 0)) < thr) {x2 = i + pad} else {i = MaxB} }
for(i = 1, MaxB) { if (AverageLuma(Crop(pre, 0, 0, 0, -h+i)) < thr) {y1 = i + pad} else {i = MaxB} }
for(i = 1, MaxB) { if (AverageLuma(Crop(pre, 0, h-i, 0, 0)) < thr) {y2 = i + pad} else {i = MaxB} }
") : GScript("
for(i = 1, MaxB) { if (AverageLuma(Crop(pre, 0, 0, -w+i, 0)) < thr) {x1 = i + pad} else {i = MaxB} }
for(i = 1, MaxB) { if (AverageLuma(Crop(pre, w-i, 0, 0, 0)) < thr) {x2 = i + pad} else {i = MaxB} }
for(i = 1, MaxB) { if (AverageLuma(Crop(pre, 0, 0, 0, -h+i)) < thr) {y1 = i + pad} else {i = MaxB} }
for(i = 1, MaxB) { if (AverageLuma(Crop(pre, 0, h-i, 0, 0)) < thr) {y2 = i + pad} else {i = MaxB} }
")

StainlessS
15th March 2021, 16:01
Not really too good if inside gScriptclip,
Reason, will scan and install FillBorders2_TestBord() function as GScript function at every iteration of gScriptclip, ie slow + memory consuming.
Above written for using GScript(""" FillBorders2_TestBord()""") inside scriptCip, also applies to GScript(""" for( ... etc """) and Eval().
FillBorders2_TestBord() is better outside of function at main level, or maybe after FillBorders2_dBugS() in FillBorders2().

However, there was an error in AviSynth 2.6.
Would you like to clarify, was no promblem here for v2.60 std.

I'm still working on this a bit.

And dont worry bout this, its OK.

Function FillBorders2_TestBord(clip c,int edge, Float thr, int MaxB, Int Pad) {
# edge: 0=lft, 1=rgt, 2=top, 3=bot
result=0 w=c.width h=c.height
for(i = 1, MaxB) {
crpd=c.Crop((edge==1)?w-i:0,(edge==3)?h-i:0,(edge==0)?i:0,(edge==2)?i:0)
if(crpd.AverageLuma < thr) { result=i+pad }
else { i=MaxB }
}
return result
}


Where clip = 640x480, and crop width = 8. [ w=640, h=480, crop width below is i ]

# edge: 0=lft, 1=rgt, 2=top, 3=bot
#crpd=c.Crop( (edge==1)?w-i:0 , (edge==3)?h-i:0 , (edge==0)?i:0 , (edge==2)?i:0)
#crpd=c.Crop( ( RGT)?640-8:0 , ( BOT)?480-8:0 , ( LFT)?8:0 , ( TOP)?8:0)
LFT = crop( 0, 0, 8, 0) # Width = 8
RGT = crop( 640-8, 0, 0, 0) # Width = 0, means from left coord(640-8) all the way to right edge
TOP = crop( 0, 0, 0, 8) # Height = 8
BOT = crop( 0, 480-8, 0, 0) # Height = 0, means from top coord(480-8) all the way to bot edge

crop(0,0,0,0) is Full Frame.


EDIT:
For for() loop, dont need specify increment if its 1 (default = 1),
ie

for(i = 1, MaxB, 1)
for(i = 1, MaxB)

Arx1meD
15th March 2021, 18:40
Reason, will scan and install FillBorders2_TestBord() function as GScript function at every iteration of gScriptclip, ie slow + memory consuming.
You're right. Bad ideas. I tested in AVSMeter and the memory used was constantly growing.

Would you like to clarify, was no promblem here for v2.60 std.
When I tested your Stab_Light_ssS.avsi in AviSynth 2.6 I got an error. Then I inserted the FillBorders2_TestBord function in FillBorders2. Checked it and got no error.

StainlessS
15th March 2021, 19:13
Yeah, but Which Error Message ?

EDIT: You can fix for usage under v2.60 Std ONLY with


GScript("""
Function FillBorders2_TestBord(clip c,int edge, Float thr, int MaxB, Int Pad) {
# edge: 0=lft, 1=rgt, 2=top, 3=bot
result=0 w=c.width h=c.height
for(i = 1, MaxB) {
crpd=c.Crop((edge==1)?w-i:0,(edge==3)?h-i:0,(edge==0)?i:0,(edge==2)?i:0)
if(crpd.AverageLuma < thr) { result=i+pad }
else { i=MaxB }
}
return result
}
""")

At Main level, and just use Import on the file. [GScript dll must already be installed / LoadPlugin ]

Arx1meD
15th March 2021, 19:39
Yeah, but Which Error Message

I understood. Without GScript ("""...""") there is an error:Script error: syntax error
(D:\stab\stab_light_ssS.avsi, line 93, column 22)
this line: for(i = 1, MaxB) {

With GScript ("""...""") there is an error:Script error: syntax error
(D:\stab\stab_light_ssS.avsi, line 100)
this line:""")

EDIT: Sorry with GScript working. This is my mistake. I haven't loaded the plugin

StainlessS
15th March 2021, 21:42
Arx1meD,

Right, here we go, FINAL version as far as I'm concerned.
I've used thr=10.0 instead of 10, and thr = Default(thr,5.0) instead of thr = Default(thr,5), ie float values.
I also made other changes you suggested [excluding the GScript stuff].

Here new script, read the comments.

Stab_Light.avsi

# Stab_light.avsi
/*
This script can be IMPORT()'ed under Avs+, OR, under Avs v2.60 Standard [ONLY IF GScript is installed].
Can also be GIMPORT()'ed when GScript Installed.
Can Also be auto Loaded by Avisynth if in Plugins directory. [needs Gscript installed if not AVS+].
Can Also be dumped in AvsInit()'s GIMPORT directory for auto loading by AvsInit().
*/

Function Fb2FnNameEx(String Fn){Try{Eval(Fn+"()")B=True}catch(e)
\ {Assert(e.FindStr("syntax")==0,"Fb2FnNameEx: Error in Function Name '"+Fn+"'")B=(e.FindStr("no function named")==0)}Return B}
Fb2IsPlus = (FindStr(VersionString,"AviSynth+")!=0)
Fb2HasGScript = Fb2FnNameEx("GScipt")

Fb2_GS_S = """
Function FillBorders2_TestBord(clip c,int edge, Float thr, int MaxB, Int Pad) {
# edge: 0=lft, 1=rgt, 2=top, 3=bot
result=0 w=c.width h=c.height
for(i = 1, MaxB) {
crpd=c.Crop((edge==1)?w-i:0,(edge==3)?h-i:0,(edge==0)?i:0,(edge==2)?i:0)
if(crpd.AverageLuma < thr) { result=i+pad }
else { i=MaxB }
}
return result
}
"""

/*
If this avsi script is not in plugins, then HasGScript [Fb2HasGScript] can be FALSE, even if using GImport() from Gscript dll.
Not sure why this occurs, so even if Fb2HasGScript==FALSE and is NOT Avs+, then we use Gscript's GEval instead of Eval to
install above FillBorders2_TestBord().
We use Eval() to install the function, ONLY if GScript not installed and is AVS+.
*/


Try {
(Fb2HasGScript||!Fb2IsPlus) ? GEval(Fb2_GS_S) : Eval(Fb2_GS_S)
} catch (msg) {
Assert(False,ScriptFile+": Need GScript or Avs+"+Chr(10)+
\ "IsPlus="+String(Fb2IsPlus)+" : HasGScript="+String(Fb2HasGScript)+Chr(10)+
\ "SysErr="+Msg)
}

Function Stab_Light(clip clp, int "ts", int "range", int "dxmax", int "dymax", float "zoom", float "PAR",
\ bool "mirror", int "MaxBorders", bool "FixBorders", bool "FixFalPos", bool "debug") {
ts = Default(ts, 7) # frames to temporal average for better motion estimation (max. 7)
range = Default(range, 1) # frames before/after to estimate motion
dxmax = Default(dxmax, Round(clp.Width()/180.0)) #maximum deviation in pixels
dymax = Default(dymax, dxmax) # x, and y should be the same
zoom = Default(zoom, 1) # maximum zoom factor (1 disabled)
PAR = Default(PAR, 1.0) # PAR of your source
mirror = Default(mirror, false) # Edge filling
MaxBorders = Default(MaxBorders, Max(Round(clp.Width()/60.0), 20))
FixBorders = Default(FixBorders, true)
FixFalPos = Default(FixFalPos, true) # Fixes borders of MaxBorders more pixels wide
debug = Default(debug, false)

temp = TemporalSoften(clp, ts, 255, 255, 25, 2) # SC thr to 25 otherwise pans will stutter
rep = Repair(temp, TemporalSoften(clp, 1, 255, 255, 25, 2))
inter = Interleave(rep, clp) # temporal stable (better subpixel detection)
pre_clp = inter.mt_lut(expr="x 20 1 x 127.5 / - 21 ^ pi x * 255 / cos 21 ^ - * 1.05 * -", Y=3, U=2, V=2)
\ .RemoveGrain(mode=17) #ColorYUV(levels="PC->TV")

mdata = DePanEstimate(pre_clp, range=range, pixaspect=PAR, trust=0, dxmax=dxmax, dymax=dymax, zoommax=zoom)
dp = DePan(inter, data=mdata, offset=-1, mirror=mirror?15:0, pixaspect=PAR, matchfields=false, subpixel=2)
stab = SelectEvery(dp, 2, 0)

w = Width(clp)
h = Height(clp)
thr = 10.0

pre = stab.ConvertToY8()#.ColorYUV(off_y=-15)
stab = (!mirror && FixFalPos)
\ ? gScriptClip(stab, "
AverageLuma(Crop(pre, 0, 0, -w+MaxBorders+1, 0)) < thr ? clp : \
AverageLuma(Crop(pre, w-MaxBorders-1, 0, 0, 0)) < thr ? clp : \
AverageLuma(Crop(pre, 0, 0, 0, -h+MaxBorders+1)) < thr ? clp : \
AverageLuma(Crop(pre, 0, h-MaxBorders-1, 0, 0)) < thr ? clp : Last
Return Last
", args="clp, pre, MaxBorders, w, h, thr",Local=true)
\ : stab
Return (!mirror && FixBorders)
\ ? FillBorders2(stab, thr=thr, pad=1, MaxBorders=MaxBorders, debug=debug)
\ : stab
}

Function FillBorders2(clip c, float "thr", int "pad", int "MaxBorders", bool "debug") {
Function FillBorders2_dBugS(int y1,int x1,int x2,int y2) {
Return "Black borders:\n "+string(y1)+"\n "+string(x1)+" "+string(x2)+"\n "+ string(y2)
}
thr = Default(thr, 5.0)
pad = Default(pad, 0)
MaxB = Default(MaxBorders, Max(Round(c.Width()/60.0), 20))
debug = Default(debug, false)
w = width(c)
h = height(c)
cblank = c.mt_lut( "0", chroma="-128")
pre = c.ConvertToY8()#.ColorYUV(off_y=-15) # Moved out of gScriptclip
preblank = cblank.ConvertToY8()
font="Courier New"
gScriptClip(c, "
x1 = Pre.FillBorders2_TestBord(0, thr, MaxB, Pad)
x2 = Pre.FillBorders2_TestBord(1, thr, MaxB, Pad)
y1 = Pre.FillBorders2_TestBord(2, thr, MaxB, Pad)
y2 = Pre.FillBorders2_TestBord(3, thr, MaxB, Pad)
PreBlur = 0 # If > 0 then there may be an error
PostBlur = 0 # Max(x1, x2, y1, y2, 4) #If > 0 then there may be an error
b_fix = (x1 + x2 + y1 + y2) > 0
max_fix = Max(x1, x2, y1, y2)
msk = (b_fix && max_fix >= 8)
\ ? preblank.LetterBox(y1, y2, x1, x2, color=$ffffff).ConvertToYV12()
\ : cblank
fixed = (!b_fix) ? Last
\ : (max_fix < 8) ? FillBorders(left=x1, top=y1, right=x2, bottom=y2, mode=0, y=3, u=3, v=3)
\ : InpaintLogo(mask=msk, Radius=Max(x1,x2,y1,y2,5)*1.5, PreBlur=PreBlur, PostBlur=PostBlur)
(debug)
\ ? mt_merge(fixed, cblank.ConvertToRGB().LetterBox(y1, y2, x1, x2, color=$ff3333)
\ .ConvertToYV12(),preblank.LetterBox(y1, y2, x1, x2, color=$ffffff).ConvertToYV12(), luma=true)
\ .Subtitle(FillBorders2_dBugS(y1,x1,x2,y2), lsp = 10, text_color=$ff3333, font=Font, x=MaxB+5, y=MaxB+5)
\ : fixed
Return Last
", args="pre, cblank, preblank, thr, pad, MaxB, debug, w, h,Font",Local=true)
}


Manono.avs # Hopefully no probs now


# GScript needs to be installed BEFORE attempting to Avs v2.60 std Import/GImport "Stab_Light.avsi".
# Either by Plugins AutoLoad, or Manual LoadPlugin() loading.


# LoadPlugin(GSCRIPT_PATH_AND_PLUG_NAME)

AVSI_FN = ".\Stab_Light.avsi"

Import(AVSI_FN) # Import, If not in plugins, Avs+ or avs Std [with gscript].

ORG=AviSource(".\Src.Avi") # OP's sample, After ffmpeg conversion to UT_Video AVI
STB = ORG.Stab_Light(zoom = 1.00, mirror = false, FixFalPos = true, FixBorders = true, debug = false)
D=STB.Subtract(ORG)
T=StackHorizontal(ORG,STB)
B=StackHorizontal(D,D)
StackVertical(T,B)


Real.Finder, IMPORTANT, see comments in script and Eval/GEval install of gscript style script function.

real.finder
15th March 2021, 22:38
I have no problem with using it like this https://github.com/realfinder/AVS-Stuff/blob/877bdaa97237ae026b229a2f0f139372ae6b4e0d/avs%202.5%20and%20up/insertsign.avsi#L64 IIRC

StainlessS
15th March 2021, 22:54
OK, that works.
But I usually use/force Gscript if it is installed, [ external plugins Override builtin ].
( If Gscript is installed, then presumably the user intended for it to be used, even if Avs+ )

Plugin Autoload and Name Precedence :- http://avisynth.nl/index.php/Plugins

The order in which function names take precedence is as follows:

User-defined function (always have the highest priority)
Plugin (external) function (higher priority than built-in)
Built-in (internal) function

Inside those groups the function loaded last takes precedence; there is no error in a namespace conflict.
It follows that there is no harm in loading the same plugin more than once.[dubious – discuss]

manono
16th March 2021, 07:22
Manono.avs # Hopefully no probs now

No problems at all. Don't need a separate FillBorders2 and don't even need GImport to open it. Just a simple Import line seems to be enough.

Cool, can stop shaky movies more easily and better than the old Stab I've been using for years. Thanks to you both.

Arx1meD
16th March 2021, 11:51
Thank you StainlessS. I need to do some tests before updating post #1.

StainlessS
16th March 2021, 13:58
Arx1meD,
I dont really understand what your fiilter is doing [I was just getting it working, not trying to understand it, years since I used Stab/Depan stuff].
As far as the edge scanning by FillBorders2_TestBord() is concerned, On each edge you test increasing CropWidth until it breaks thr,
would it perhaps be better to crop and test individual lines instead [EDIT: pixel rows or columns] ?
Where eg left edge and i=8, crop, crop(i-1,0,1,0) [crop(7,0,1,0)] AverageLuma, and compare that with Thr.

If you think it might be worth a try, I'll make mod, if dont work then just remain as is now.

[I'm not going to try it unless you think it may make a +ve difference].

Off to the shops now, and then after that, gonna feed the ducks. [ No, that is not a euphemism ]

EDIT: If works, would likely be more precise, thr may need be lowered.

EDIT: Also,Instead of AverageLuma, yPlaneMax could be used to catch individual pixel >= thr
[although thr may need go back to int again, but maybe it dont matter].

Arx1meD
16th March 2021, 16:49
I dont really understand what your fiilter is doing
This is a filter for stabilizing video from shaking. The result of his work can be seen in the example in post #1.

would it perhaps be better to crop and test individual lines instead [EDIT: pixel rows or columns] ?
If I understood you correctly, you suggest doing the same thing as in the FillBorders_stabi function (used in Stab3 (https://raw.githubusercontent.com/realfinder/AVS-Stuff/master/avs%202.5%20and%20up/StabilizationToolsPack.avsi)).
Fragment from FillBorders_stabi:# 4px or greater
L4=FalPos ? crop(0,0,-w+4,0) : nop()
R4=FalPos ? crop(w-4,0,0 ,0) : nop()
T4=FalPos ? crop(0,0,0,-h+4) : nop()
B4=FalPos ? crop(0,h-4,0 ,0) : nop()
# 3px
L3=crop(0,0,-w+3,0)
R3=crop(w-3,0,0 ,0)
T3=crop(0,0,0,-h+3)
B3=crop(0,h-3,0 ,0)
# 2px
L2=crop(L3,0,0,-1, 0)
R2=crop(R3,1,0,0 , 0)
T2=crop(T3,0,0,0 ,-1)
B2=crop(B3,0,1,0 , 0)
# 1px
L1=crop(L3,0,0,-2,0 )
R1=crop(R3,2,0,0 ,0 )
T1=crop(T3,0,0,0 ,-2)
B1=crop(B3,0,2,0 ,0 )

c
Fill = gScriptClip("""
yclip
pad= blur ? pad+2 : pad

L1A = AverageLuma(L1)
R1A = AverageLuma(R1)
T1A = AverageLuma(T1)
B1A = AverageLuma(B1)

x1o = L1A < thr ? (AverageLuma(L2) < thr ? (AverageLuma(L3) < thr ? 3 : 2) : 1) : 0
x1 = L1A < thr ? x1o+pad : x1o
x2o = R1A < thr ? (AverageLuma(R2) < thr ? (AverageLuma(R3) < thr ? 3 : 2) : 1) : 0
x2 = R1A < thr ? x2o+pad : x2o
y1o = T1A < thr ? (AverageLuma(T2) < thr ? (AverageLuma(T3) < thr ? 3 : 2) : 1) : 0
y1 = T1A < thr ? y1o+pad : y1o
y2o = B1A < thr ? (AverageLuma(B2) < thr ? (AverageLuma(B3) < thr ? 3 : 2) : 1) : 0
y2 = B1A < thr ? y2o+pad : y2o
I think this is a bad way to define black borders. Each frame of the clip has a different border. And we can only define a border of 3 px. The border can be much larger after the clip has stabilized. Stab3 replaces these frames from the original clip.

Fragment from Stab3:b3fix && defined(FalPosclip) ? FalPosclip : FixFalPos ? eval("""
thick = b3fix ? 3.0 : 2.0 # removing >2px wide borders
cropx = dxmax*2
ratiox = "YPlaneMax("+string(ceil(99-thick/cropx*100))+")"

crop(0,0,0,cropx).conditionalfilter(last,clp,ratiox,">","0")
crop(0,height-cropx,0,0).conditionalfilter(last,clp,ratiox,">","0")
crop(0,0,cropx,0).conditionalfilter(last,clp,ratiox,">","0")
crop(width-cropx,0,0,0).conditionalfilter(last,clp,ratiox,">","0")""") : last
My filter can find and fix large black borders.

StainlessS
16th March 2021, 18:33
This is a filter for stabilizing video from shaking. The result of his work can be seen in the example in post #1.
Thanks, I know roughly what its for, but not examined how it does it.

what I meant was something like this demo test for pixel row/column coord scan. [need DebugView]


Blankclip

Function FillBorders2_Mod_TestBord_Mod(clip c,int edge, Float thr, int MaxB, Int Pad) {
# edge: 0=lft, 1=rgt, 2=top, 3=bot
# Horizontal edges:- [ie top(2) and bot(3) always have x coord of 0, and are full width (can use 0)], AND height always 1 pixel
# Vertical edges:- [ie lft(0) and rgt(1) always have y coord of 0, and are full height(can use 0)], AND width always 1 pixel
# Lft(0) and Rgt(1), x coord varies
# Top(2) and Bot(3), y coord varies
edge=min(max(edge,0),3)
result=0 w=c.width h=c.height
EdS=MidStr("LFTRGTTOPBOT",(edge*3)+1,3)
for(i = 1, MaxB) {
# crpd=c.Crop((edge==1)?w-i:0,(edge==3)?h-i:0,(edge==0)?i:0,(edge==2)?i:0)
cx = (edge>=2) ? 0 : (edge==0) ? i-1 : w-i
cw = (edge>=2) ? 0 : 1
cy = (edge<=1) ? 0 : (edge==2) ? i-1 : h-i
ch = (edge<=1) ? 0 : 1
RT_DebugF("%s: i=%d] cx=%3d cy=%3d cw=%3d ch=%3d",EdS,i,cx,cy,cw,ch,name="TEST: ")
# crpd=c.Crop(cx,cy,cw,ch)
# if(crpd.AverageLuma < thr) { result=i+pad } # Orig
# if(crpd.YPlaneMax < thr) { result=i+pad } # ALTERNATIVE
# else { i=MaxB }
}
return result
}

For(edge=0,3) {
FillBorders2_Mod_TestBord_Mod(Last,edge,10.0,8,8)
RT_DebugF("",name="")
}

Messageclip("DONE")


Debugview output

00000019 1.95851886 [5060] TEST: LFT: i=1] cx= 0 cy= 0 cw= 1 ch= 0
00000020 1.95860696 [5060] TEST: LFT: i=2] cx= 1 cy= 0 cw= 1 ch= 0
00000021 1.95868516 [5060] TEST: LFT: i=3] cx= 2 cy= 0 cw= 1 ch= 0
00000022 1.95875907 [5060] TEST: LFT: i=4] cx= 3 cy= 0 cw= 1 ch= 0
00000023 1.95883310 [5060] TEST: LFT: i=5] cx= 4 cy= 0 cw= 1 ch= 0
00000024 1.95903337 [5060] TEST: LFT: i=6] cx= 5 cy= 0 cw= 1 ch= 0
00000025 1.95910084 [5060] TEST: LFT: i=7] cx= 6 cy= 0 cw= 1 ch= 0
00000026 1.95917070 [5060] TEST: LFT: i=8] cx= 7 cy= 0 cw= 1 ch= 0
00000027 1.95928669 [5060]
00000028 1.95945764 [5060] TEST: RGT: i=1] cx=639 cy= 0 cw= 1 ch= 0
00000029 1.95952940 [5060] TEST: RGT: i=2] cx=638 cy= 0 cw= 1 ch= 0
00000030 1.95960116 [5060] TEST: RGT: i=3] cx=637 cy= 0 cw= 1 ch= 0
00000031 1.95966387 [5060] TEST: RGT: i=4] cx=636 cy= 0 cw= 1 ch= 0
00000032 1.95973265 [5060] TEST: RGT: i=5] cx=635 cy= 0 cw= 1 ch= 0
00000033 1.95979965 [5060] TEST: RGT: i=6] cx=634 cy= 0 cw= 1 ch= 0
00000034 1.95986748 [5060] TEST: RGT: i=7] cx=633 cy= 0 cw= 1 ch= 0
00000035 1.95993960 [5060] TEST: RGT: i=8] cx=632 cy= 0 cw= 1 ch= 0
00000036 1.96003377 [5060]
00000037 1.96018815 [5060] TEST: TOP: i=1] cx= 0 cy= 0 cw= 0 ch= 1
00000038 1.96026278 [5060] TEST: TOP: i=2] cx= 0 cy= 1 cw= 0 ch= 1
00000039 1.96033478 [5060] TEST: TOP: i=3] cx= 0 cy= 2 cw= 0 ch= 1
00000040 1.96040797 [5060] TEST: TOP: i=4] cx= 0 cy= 3 cw= 0 ch= 1
00000041 1.96048045 [5060] TEST: TOP: i=5] cx= 0 cy= 4 cw= 0 ch= 1
00000042 1.96055186 [5060] TEST: TOP: i=6] cx= 0 cy= 5 cw= 0 ch= 1
00000043 1.96062434 [5060] TEST: TOP: i=7] cx= 0 cy= 6 cw= 0 ch= 1
00000044 1.96069789 [5060] TEST: TOP: i=8] cx= 0 cy= 7 cw= 0 ch= 1
00000045 1.96079099 [5060]
00000046 1.96094501 [5060] TEST: BOT: i=1] cx= 0 cy=479 cw= 0 ch= 1
00000047 1.96101999 [5060] TEST: BOT: i=2] cx= 0 cy=478 cw= 0 ch= 1
00000048 1.96109211 [5060] TEST: BOT: i=3] cx= 0 cy=477 cw= 0 ch= 1
00000049 1.96116567 [5060] TEST: BOT: i=4] cx= 0 cy=476 cw= 0 ch= 1
00000050 1.96123731 [5060] TEST: BOT: i=5] cx= 0 cy=475 cw= 0 ch= 1
00000051 1.96131086 [5060] TEST: BOT: i=6] cx= 0 cy=474 cw= 0 ch= 1
00000052 1.96138334 [5060] TEST: BOT: i=7] cx= 0 cy=473 cw= 0 ch= 1
00000053 1.96145618 [5060] TEST: BOT: i=8] cx= 0 cy=472 cw= 0 ch= 1


EDIT: do you thinks above would be beneficial, testing Averageluma [or YPaneMax] by scanlines [row/column].

EDIT: Yep, I know theres too many Mod's in FillBorders2_Mod_TestBord_Mod.
EDIT: For YPlaneMax it would not matter if single scanline scan as opposed to block scan, but might be marginally faster.
However, If AverageLuma, then might be better for scanline scan [row/column].

EDIT: I can now easily provide two alternative versions of your script for test compare if you wanna test [already done, pretty much].
take maybe 5 mins.

Arx1meD
16th March 2021, 18:52
How do I use this method to create a mask (black background, white borders)?blank.ConvertToY8().LetterBox(y1, y2, x1, x2, color=$ffffff)

StainlessS
16th March 2021, 19:07
How do I use this method to create a mask (black background, white borders)?

Dont think would work for v2.60, Think Pinterf implemented color_YUV for AVS+ Letterbox, not valid for v2.60 std.
I assume that black = PC_Levels $00 and white = PC_Levels $FF required.

I'll see what I can do with Masktools for above.

HERE: Mod for testing against the stab_light.avsi that you already have.

Stab_light_Mod.avsi

# Stab_Light_Mod.avsi
/*
This script can be IMPORT()'ed under Avs+, OR, under Avs v2.60 Standard [ONLY IF GScript is installed].
Can also be GIMPORT()'ed when GScript Installed.
Can Also be auto Loaded by Avisynth if in Plugins directory. [needs Gscript installed if not AVS+].
Can Also be dumped in AvsInit()'s GIMPORT directory for auto loading by AvsInit().
*/

Function Fb2FnNameEx_Mod(String Fn){Try{Eval(Fn+"()")B=True}catch(e){Assert(e.FindStr("syntax")==0,"Fb2FnNameEx_Mod: Error in Function Name '"+Fn+"'")B=(e.FindStr("no function named")==0)}Return B}
Fb2IsPlus_Mod = (FindStr(VersionString,"AviSynth+")!=0)
Fb2HasGScript_Mod = Fb2FnNameEx_Mod("GScipt")

Fb2_GS_S_Mod = """
Function FillBorders2_TestBord_Mod(clip c,int edge, Float thr, int MaxB, Int Pad) {
# edge: 0=lft, 1=rgt, 2=top, 3=bot
# Horizontal edges:- [ie top(2) and bot(3) always have x coord of 0, and are full width (can use 0)], AND height always 1 pixel
# Vertical edges:- [ie lft(0) and rgt(1) always have y coord of 0, and are full height(can use 0)], AND width always 1 pixel
# Lft(0) and Rgt(1), x coord varies
# Top(2) and Bot(3), y coord varies
edge=min(max(edge,0),3)
result=0 w=c.width h=c.height
for(i = 1, MaxB) {
cx = (edge>=2) ? 0 : (edge==0) ? i-1 : w-i
cw = (edge>=2) ? 0 : 1
cy = (edge<=1) ? 0 : (edge==2) ? i-1 : h-i
ch = (edge<=1) ? 0 : 1
crpd=c.Crop(cx,cy,cw,ch)
if(crpd.AverageLuma < thr) { result=i+pad } # Orig
# if(crpd.YPlaneMax < thr) { result=i+pad } # ALTERNATIVE
else { i=MaxB }
}
return result
}
"""




/*
If this avsi script is not in plugins, then HasGScript [Fb2HasGScript_Mod] can be FALSE, even if using GImport() from Gscript dll.
Not sure why this occurs, so even if Fb2HasGScript_Mod==FALSE and is NOT Avs+, then we use Gscript's GEval instead of Eval to
install above FillBorders2_TestBord_Mod().
We use Eval() to install the function, ONLY if GScript not installed and is AVS+.
*/

Try {
(Fb2HasGScript_Mod||!Fb2IsPlus_Mod) ? GEval(Fb2_GS_S_Mod) : Eval(Fb2_GS_S_Mod)
} catch (msg) {
Assert(False,ScriptFile+": Need GScript or Avs+"+Chr(10)+
\ "IsPlus="+String(Fb2IsPlus_Mod)+" : HasGScript="+String(Fb2HasGScript_Mod)+Chr(10)+
\ "SysErr="+Msg)
}

Function Stab_Light_Mod(clip clp, int "ts", int "range", int "dxmax", int "dymax", float "zoom", float "PAR",
\ bool "mirror", int "MaxBorders", bool "FixBorders", bool "FixFalPos", bool "debug") {
ts = Default(ts, 7) # frames to temporal average for better motion estimation (max. 7)
range = Default(range, 1) # frames before/after to estimate motion
dxmax = Default(dxmax, Round(clp.Width()/180.0)) #maximum deviation in pixels
dymax = Default(dymax, dxmax) # x, and y should be the same
zoom = Default(zoom, 1) # maximum zoom factor (1 disabled)
PAR = Default(PAR, 1.0) # PAR of your source
mirror = Default(mirror, false) # Edge filling
MaxBorders = Default(MaxBorders, Max(Round(clp.Width()/60.0), 20))
FixBorders = Default(FixBorders, true)
FixFalPos = Default(FixFalPos, true) # Fixes borders of MaxBorders more pixels wide
debug = Default(debug, false)

temp = TemporalSoften(clp, ts, 255, 255, 25, 2) # SC thr to 25 otherwise pans will stutter
rep = Repair(temp, TemporalSoften(clp, 1, 255, 255, 25, 2))
inter = Interleave(rep, clp) # temporal stable (better subpixel detection)
pre_clp = inter.mt_lut(expr="x 20 1 x 127.5 / - 21 ^ pi x * 255 / cos 21 ^ - * 1.05 * -", Y=3, U=2, V=2).RemoveGrain(mode=17) #ColorYUV(levels="PC->TV")

mdata = DePanEstimate(pre_clp, range=range, pixaspect=PAR, trust=0, dxmax=dxmax, dymax=dymax, zoommax=zoom)
dp = DePan(inter, data=mdata, offset=-1, mirror=mirror?15:0, pixaspect=PAR, matchfields=false, subpixel=2)
stab = SelectEvery(dp, 2, 0)

w = Width(clp)
h = Height(clp)
thr = 10.0

pre = stab.ConvertToY8()#.ColorYUV(off_y=-15)
stab = (!mirror && FixFalPos)
\ ? gScriptClip(stab, "
AverageLuma(Crop(pre, 0, 0, -w+MaxBorders+1, 0)) < thr ? clp : \
AverageLuma(Crop(pre, w-MaxBorders-1, 0, 0, 0)) < thr ? clp : \
AverageLuma(Crop(pre, 0, 0, 0, -h+MaxBorders+1)) < thr ? clp : \
AverageLuma(Crop(pre, 0, h-MaxBorders-1, 0, 0)) < thr ? clp : Last
Return Last
", args="clp, pre, MaxBorders, w, h, thr",Local=true)
\ : stab
Return (!mirror && FixBorders)
\ ? FillBorders2_Mod(stab, thr=thr, pad=1, MaxBorders=MaxBorders, debug=debug)
\ : stab
}

Function FillBorders2_Mod(clip c, float "thr", int "pad", int "MaxBorders", bool "debug") {
Function FillBorders2_Mod_dBugS(int y1,int x1,int x2,int y2) {
Return "Black borders:\n "+string(y1)+"\n "+string(x1)+" "+string(x2)+"\n "+ string(y2)
}
thr = Default(thr, 5.0)
pad = Default(pad, 0)
MaxB = Default(MaxBorders, Max(Round(c.Width()/60.0), 20))
debug = Default(debug, false)
w = width(c)
h = height(c)
cblank = c.mt_lut( "0", chroma="-128")
pre = c.ConvertToY8()#.ColorYUV(off_y=-15) # Moved out of gScriptclip
preblank = cblank.ConvertToY8()
font="Courier New"
gScriptClip(c, "
x1 = Pre.FillBorders2_TestBord_Mod(0, thr, MaxB, Pad)
x2 = Pre.FillBorders2_TestBord_Mod(1, thr, MaxB, Pad)
y1 = Pre.FillBorders2_TestBord_Mod(2, thr, MaxB, Pad)
y2 = Pre.FillBorders2_TestBord_Mod(3, thr, MaxB, Pad)
PreBlur = 0 # If > 0 then there may be an error
PostBlur = 0 # Max(x1, x2, y1, y2, 4) #If > 0 then there may be an error
b_fix = (x1 + x2 + y1 + y2) > 0
max_fix = Max(x1, x2, y1, y2)
msk = (b_fix && max_fix >= 8)
\ ? preblank.LetterBox(y1, y2, x1, x2, color=$ffffff).ConvertToYV12()
\ : cblank
fixed = (!b_fix) ? Last
\ : (max_fix < 8) ? FillBorders(left=x1, top=y1, right=x2, bottom=y2, mode=0, y=3, u=3, v=3)
\ : InpaintLogo(mask=msk, Radius=Max(x1,x2,y1,y2,5)*1.5, PreBlur=PreBlur, PostBlur=PostBlur)
(debug)
\ ? mt_merge(fixed, cblank.ConvertToRGB().LetterBox(y1, y2, x1, x2, color=$ff3333)
\ .ConvertToYV12(),preblank.LetterBox(y1, y2, x1, x2, color=$ffffff).ConvertToYV12(), luma=true)
\ .Subtitle(FillBorders2_Mod_dBugS(y1,x1,x2,y2), lsp = 10, text_color=$ff3333, font=Font, x=MaxB+5, y=MaxB+5)
\ : fixed
Return Last
", args="pre, cblank, preblank, thr, pad, MaxB, debug, w, h,Font",Local=true)
}


Blue line is alternative, need comment/uncomment to activate.

Client

#AVSI_FN = ".\Stab_Light.avsi"
AVSI_FN = ".\Stab_Light.avsi"
AVSI_FN_MOD = ".\Stab_Light_Mod.avsi"
Import(AVSI_FN)
Import(AVSI_FN_MOD)

ORG=AviSource(".\Src.Avi") # OP's sample, After ffmpeg conversion to UT_Video AVI
A = ORG.Stab_Light(zoom = 1.00, mirror = false, FixFalPos = true, FixBorders = true , debug = false)
B = ORG.Stab_Light_Mod(zoom = 1.00, mirror = true, FixFalPos = false, FixBorders = true, debug = false)
D=B.ClipDelta(A,true)
T=StackHorizontal(ORG,A)
B=StackHorizontal(B,D)
StackVertical(T,B)

Return Last

# Return Clip Difference of input clips (amp==true = Amplified, show==true = show background)
Function ClipDelta(clip clip1,clip clip2,bool "amp",bool "show") {
amp=Default(amp,false)
show=Default(show,false)
c2=clip1.levels(128-32,1.0,128+32,128-32,128+32).greyscale()
c1=clip1.subtract(clip2)
c1=(amp)?c1.levels(127,1.0,129,0,255):c1
return (show)?c1.Merge(c2):c1
}


The results are definitely different, time for you to test.
(May need to try varying Thr [probably lower for mod]).

EDIT:
Letterbox(clip clip, int top, int bottom [, int x1 , int x2 , int color ] [, int color_yuv AVS+])

int top, bottom = (required)

Number of top and bottom rows to blank out.

For YV12 sources, top and bottom must be mod2 (divisible by 2).

int x1, x2 = 0

Number of left (x1) and right (x2) columns to blank out.

For YUY2, YV16 and YV12 sources, left and right must be mod2 (divisible by 2).
For YV411 sources, left and right must be mod4 (divisible by 4).

int color = (black)

Fill color; specified as an RGB value. See this page for more information on specifying colors.

For YUV clips, colors are converted from full-range (0d-255d) to YV-range (16d-235d).

int color_yuv =
AVS+

Specifies the color of the border using YUV values. It requires setting pixel_type to one of the YUV formats; otherwise it doesn't do anything.
Similar to color_yuv in BlankClip

Arx1meD
16th March 2021, 19:53
Did some tests of the modified version. Here's the result:

Stab_Light - 62.76 fps
Stab_Light_Mod - 61.72 fps
Equal memory usage.
Almost the same search for black borders.

I think there is no point in changing the FillBorders2_TestBord function. If in one cycle for-loop find all 4 values x1 x2 y1 y2 then it would be cool. We still run FillBorders2_TestBord 4 times.

Arx1meD
16th March 2021, 20:00
I refused to create a mask using Masktools. Because there can be absolutely black parts of objects in the frame.

StainlessS
16th March 2021, 20:22
Stab_Light - 62.76 fps
Stab_Light_Mod - 61.72 fps
That could be down to assigning to temp variables cx,cy,cw,ch instead of using direct results in the crop.
[was mainly like that for showing Debug output]

Then it would be cool. We still run FillBorders2_TestBord 4 times.
I'll take a look at that.

Arh, so you dont want PC_Levels black and white ? [I thought that you did want PC Levels]
In that case your Letterbox thing would do OK.

However, I've now been looking for something and found this. Posting it anyways.


Function BoxMask(clip c, Int "X", Int "Y", Int "W", Int "H",Int "BW",Int "BH",Bool "Y8") {
/*
Return a single frame mask for a box # http://forum.doom9.org/showthread.php?p=1817736#post1817736
c, clip, template for frame size.
X,Y,W,H Box dimensions all default 0, as for crop eg 0,0,0,0 = full frame.
BW, Default=0=Solid box. Frame width for uprights.
BH, Default=BW, Frame height for horizontals.
Y8, Default False=YV12.
Req MaskTools v2.0
*/
c myName="BoxMask: " x=Default(x,0) y=Default(y,0) W=Default(W,0) H=Default(H,0) BW=Default(BW,0) BH=Default(BH,BW) Y8=Default(Y8,False)
W=(W<=0) ? Width-X+W : W H=(H<=0) ? Height-Y+H : H
Assert(0 <= X < Width, myName+String(X,"0 <= X(%.0f) < ")+String(Width, "Width( %.0f)"))
Assert(0 <= Y < Height,myName+String(Y,"0 <= Y(%.0f) < ")+String(Height,"Height(%.0f)"))
Assert(0 < W <= Width, myName+String(W,"0 < W(%.0f) <= ")+String(Width, "Width( %.0f)"))
Assert(0 < H <= Height,myName+String(H,"0 < H(%.0f) <= ")+String(Height,"Height(%.0f)"))
Assert(0 <= BW <= W/2,myName+String(BW,"0 <= BW(%.0f) <= ")+String(W/2,"W/2(%.0f)"))
Assert(BW==0 || (0 < BH <= H/2),myName+String(BH,"0 <= BH(%.0f) <= ")+String(H/2,"H/2(%.0f)"))
Rpn=(BW==0)
\ ? String(X,"x %.0f")+String(X+W," >= x %.0f")+String(Y," < & y %.0f")+String(Y+H," >= & y %.0f < & 255 0 ?")
\ : String(X,"x %.0f")+String(X+W," >= x %.0f")+String(Y," < & y %.0f")+String(Y+H," >= & y %.0f < &") +
\ String(X+BW," x %.0f")+String(X+W-BW," < x %.0f")+String(Y+BH," >= | y %.0f")+String(Y+H-BH," < | y %.0f >= | & 255 0 ?")
return Last.Blankclip(Length=1,pixel_type=(Y8)?"Y8":"YV12").mt_lutspa(relative=false,Yexpr=Rpn, chroma = "-128" )
}



ColorBars.Killaudio
BlankClip(Width=640,height=480,Pixel_type="YV12").BoxMask(x=32,y=32,w=-32,h=-32,BW=0)
return last


https://i.postimg.cc/qvq3bsVT/Box-Mask-00.jpg (https://postimages.org/)

Its a bit more complicated than needed [does hollow box if required], and could just change the "255 0 ?" bits at the end of lines to whatever values required.

Hollow Box

ColorBars.Killaudio
BlankClip(Width=640,height=480,Pixel_type="YV12").BoxMask(x=32,y=32,w=-32,h=-32,BW=64)
return last

https://i.postimg.cc/9Q9WQ0y9/Box-Mask-01.jpg (https://postimages.org/)