View Full Version : Stablizing a film image ...
frobber
19th February 2026, 05:13
I'm trying to use Stab() to reduce vertical jitter on a TV broadcast of an old black-and-white movie (the usual problem with worn-down sprockets on the print, I guess).
It seems to help, although I see what looks like a 1-pixel black line on the top of the frame that flashes on and off.
Is there some way to eliminate this artifact? Or, is there another plug-in I should use for this purpose?
Also, I'm de-interlacing and converting the video from 29.970 to 23.976 fps, using:
tfm().TDecimate()
Not sure if(?) I can just add Stab() after that line, or if I need(?) to change my script somehow.
TIA !
RetsimLegin
19th February 2026, 09:00
Crop the top line so it's always black.
johnmeyer
19th February 2026, 16:05
You should always recover the original 24 fps cadence before applying stabilization.
"VideoFred" over at doom9.org started several epically long threads about film restoration. This includes film stabilization. I re-wrote some of his scripts and posted my own version there. Here is the original thread:
The power of Avisynth: restoring old 8mm films (https://forum.doom9.org/showthread.php?t=144271)
He specifically addressed just stabilization here:
An advanced stabilisation script (https://forum.doom9.org/showthread.php?t=175669)
Here is the stabilization code from my version of his script. You can try some of the parameters in your script.
#STABILISING PARAMETERS
#----------------------------------------------------------------------------------------------------------------------------
maxstabH=10 #maximum values for the stabiliser (in pixels) 20 is a good start value
maxstabV=10
est_left=40 est_top=40 est_right=40 est_bottom=40 #crop and contast values for special Estimate clip
est_cont=0.6 #Too large a value defeats stabilization
#STABILIZING
#....................................................................................................................................................................
stab_reference= cropped_source.crop(est_left,est_top,-est_right,-est_bottom).tweak(cont=est_cont).MT_binarize(threshold=80).greyscale().invert()
mdata=DePanEstimate(stab_reference,trust=1.0,dxmax=maxstabH,dymax=maxstabV)
#stab=DePanStabilize(cropped_source,data=mdata,cutoff=0.5,dxmax=maxstabH,dymax=maxstabV,method=1,mirror=15).deflicker() #use this instead of next line to add the Deflicker function
stab=DePanStabilize(cropped_source,data=mdata,cutoff=0.5,dxmax=maxstabH,dymax=maxstabV,method=1,mirror=15)
frobber
20th February 2026, 10:44
@johnmeyer, thank you for kindly sharing this.
I looked at VideoFred's script 256.1.01_Stab_only.avs, but I'm not sure how to use it. I have the various DLLs installed, but the script itself is fairly complicated.
I tried pasting your script into what I've already got to correct the frame rate (after that step, as you indicated), but when I run it through MeGUI I get an error because cropped_source is undefined.
Any additional hints to make this work would be much appreciated. :)
johnmeyer
20th February 2026, 17:49
Sorry about posting only that fragment. My main intent was to show you some Depan parameters that work and produce good results.
If you click on the first link in my previous post, it takes you directly to my full script. Search for the undefined variable and you'll see what it is and how it gets defined.
Here is a video that shows "before/after" examples of what the script can do. The first half dozen examples mostly show the stabilization and, as you will see, it works quite well.
Before/After Examples (https://www.youtube.com/watch?v=tBAHzO7rJS0)
frobber
20th February 2026, 23:49
Thanks, but now I'm lost. The first link in your previous post leads to a thread by videoFred. I don't see johnmeyer anywhere in that thread. Are you "videoFred"? I'm confused.
videoFred links to a ZIP file from 2012. I DL'd it and opened "01_E_Stabilisation_Only.avs". It is similar to the script fragment you pasted, but not the same. No mention of cropped_source anywhere.
Perhaps I misunderstood something and just got completely lost.
Also, these scripts don't really explain the various parameters. Are the crop values going to actually crop the image, or are these only for the stabilization code to do image processing? If they do crop the image, can I just use zero values, or will the algorithm cease to function?
I need to know if any cropping/zooming will happen to the result, because I'm afraid that is a deal-breaker for my purposes.
Leo 69
21st February 2026, 04:41
Hey, could you provide the source clip? I'm developing my own software that will include automatic frame stabilization and would love to test it on specific sources like yours.
frobber
21st February 2026, 06:35
Hey, could you provide the source clip? I'm developing my own software that will include automatic frame stabilization and would love to test it on specific sources like yours.
Sure. Here 'ya go: https://www.sendspace.com/file/x1fraj
johnmeyer
21st February 2026, 18:04
Just a few days ago I gave someone else the link directly to my own script that is posted in the middle of that massive thread. I thought I had done that above, but as you found, it is just a link to the beginning of that thread.
Here is the link I should have sent to you:
John Meyer's version of the film restoration script (https://forum.doom9.org/showthread.php?p=1861734)
frobber
2nd May 2026, 09:31
Thanks. I looked at the script, but I can't really tell what it will do, so I didn't try it.
As mentioned, I have used Stab() but either the edge pixels are black or you can use the "mirror" param to fill them in.
To me, this mirroring seems like sort of a hack, as there will be weird artifacts at the edge of the frame.
Is there perhaps another way to do this with a function that can copy pixels from the previous stabilized frame(s)?
Intuitively, I would think that should produce better results, though there will be still some artifacts if the camera is moving.
Any ideas?
real.finder
2nd May 2026, 09:49
you can try my stab3()
frobber
2nd May 2026, 10:22
Thanks @real.finder. What will stab3() do?
The comment says "same as Stab2 but with some changes" — but what is that?
Does it do what I am asking about, above?
real.finder
2nd May 2026, 16:27
the main point is to fill the borders in better way, try with stab3(mirror=15,fillborder=2,use_inpaintdelogo=true, tstab=true) or stab3(use_Feedback=true)
johnmeyer
3rd May 2026, 01:11
Thanks. I looked at the script, but I can't really tell what it will do, so I didn't try it.
As mentioned, I have used Stab() but either the edge pixels are black or you can use the "mirror" param to fill them in.
That script is long. Here are the two key sections that have to do with stabilization:
#STABILISING PARAMETERS
#----------------------------------------------------------------------------------------------------------------------------
maxstabH=10 #maximum values for the stabiliser (in pixels) 20 is a good start value
maxstabV=10
est_left=40 est_top=40 est_right=40 est_bottom=40 #crop and contast values for special Estimate clip
est_cont=1.1 #Too large a value defeats stabilization
#STABILIZING
#....................................................................................................................................................................
stab_reference= cropped_source.crop(est_left,est_top,-est_right,-est_bottom).tweak(cont=est_cont).MT_binarize(threshold=80).greyscale().invert()
mdata=DePanEstimate(stab_reference,trust=1.0,dxmax=maxstabH,dymax=maxstabV)
stab=DePanStabilize(cropped_source,data=mdata,cutoff=0.5,dxmax=maxstabH,dymax=maxstabV,method=1,mirror=15)Note mirror=15.
I have a feeling you are not going to be successful with AVISynth and stab. You might want to download and try the commercial Mercalli stabilizer. Yes, it costs money, but it is also the best stabilization available to non-commercial people.
real.finder
3rd May 2026, 02:18
BTW, you can keep using old Stab() or johnmeyer stab with black border (which mean dont use any mirror settings); then use this https://forum.doom9.org/showpost.php?p=2030585&postcount=909 after stabilize
frobber
3rd May 2026, 03:20
Thank you, @real.finder and @johnmeyer, for very kindly elaborating.
I have investigated a little bit, and here's what Claude AI says:
The core issue is that AviSynth is a pull-based, single-pass framework — when it processes frame N, it can request neighbouring frames (N-1, N+1, etc.), but it cannot store the result of a previously processed frame to use as input for the next one. This makes true temporal fill — where you use the stabilized output of frame N-1 to fill the border pixels of stabilized frame N — impossible within a single AviSynth script. The processed output of one frame simply isn't available as input to the next.
Idk to what extent we should trust Claude on this, but it seems like a known design limitation of AviSynth.
The suggestion is to try VapourSynth.
real.finder
3rd May 2026, 03:44
Thank you, @real.finder and @johnmeyer, for very kindly elaborating.
I have investigated a little bit, and here's what Claude AI says:
The core issue is that AviSynth is a pull-based, single-pass framework — when it processes frame N, it can request neighbouring frames (N-1, N+1, etc.), but it cannot store the result of a previously processed frame to use as input for the next one. This makes true temporal fill — where you use the stabilized output of frame N-1 to fill the border pixels of stabilized frame N — impossible within a single AviSynth script. The processed output of one frame simply isn't available as input to the next.
Idk to what extent we should trust Claude on this, but it seems like a known design limitation of AviSynth.
The suggestion is to try VapourSynth.
that false information, and VapourSynth is not better than AVS+ (it better in some ways than old AviSynth but that "AI" hallucination is not one of them)
and both of my methods above has some form of temporal fill anyway
frobber
3rd May 2026, 06:18
Thanks, @real.finder, for speaking to that. Honestly, I would prefer AVS instead of VapourSynth, as I've already got it installed and experience using it with MeGUI.
Let me try your latest suggestion and report back.
frobber
27th May 2026, 07:46
No luck. Can't get the script to work at all.
Script error: There is no function named 'bore_SinglePlane'.
I installed libbore.dll from here: https://github.com/OpusGang/bore
How to proceed?
real.finder
27th May 2026, 13:34
No luck. Can't get the script to work at all.
Script error: There is no function named 'bore_SinglePlane'.
I installed libbore.dll from here: https://github.com/OpusGang/bore
How to proceed?
try load bore manually and post what you get here, because it seems you have a problem with it.
frobber
27th May 2026, 14:14
Thanks. Here's my script for MeGUI-64:
Import("C:\Program Files (x86)\AviSynth+\plugins64\QTGMC.avsi")
Import("C:\Program Files (x86)\AviSynth+\plugins64\Zs_RF_Shared.avsi")
Import("C:\Program Files (x86)\AviSynth+\plugins64\Stab.avsi")
Import("C:\Program Files (x86)\AviSynth+\plugins64\AutoBorderFiller.avsi")
LoadPlugin("C:\Program Files (x86)\AviSynth+\plugins64\LSMASHSource.dll")
LoadPlugin("C:\Program Files (x86)\AviSynth+\plugins64\TIVTC.dll")
LoadPlugin("C:\Program Files (x86)\AviSynth+\plugins64\masktools2.dll")
LoadPlugin("C:\Program Files (x86)\AviSynth+\plugins64\mvtools2.dll")
LoadPlugin("C:\Program Files (x86)\AviSynth+\plugins64\nnedi3.dll")
LoadPlugin("C:\Program Files (x86)\AviSynth+\plugins64\DePan.dll")
LoadPlugin("C:\Program Files (x86)\AviSynth+\plugins64\DePanEstimate.dll")
LoadPlugin("C:\Program Files (x86)\AviSynth+\plugins64\RgTools.dll")
LoadPlugin("C:\Program Files (x86)\AviSynth+\plugins64\libbore.dll")
MPEG2Source("\\OMV\OMV-Storage\MyMovie.d2v")
tfm().TDecimate()
Stab()
AutoBorderFiller()
bmasks=ExtractA().mt_inflate().TemporalSoften(3, 255, 255, 0, 2)
RemoveAlphaPlane().InpaintDelogo(mask=bmasks, Loc="0,0,0,0",Inflate=0)
mt_merge(last,TemporalSoften(3, 255, 255, 10, 2),bmasks,luma=true)
Greyscale()
Spline36resize(1920,1080)
Crop(240, 0, -240, 0)
What am I doing wrong?
real.finder
27th May 2026, 14:24
I mean post the error message after load bore dll manually
frobber
27th May 2026, 14:44
The only error that I see appears in the video preview window:
Script error: There is no function named 'bore_SinglePlane'.
C:\Program Files (x86)\AviSynth+\plugins64\AutoBorderFiller.avsi, line 9 ... [and here it's cropped by the window]
real.finder
27th May 2026, 15:13
The only error that I see appears in the video preview window:
Script error: There is no function named 'bore_SinglePlane'.
C:\Program Files (x86)\AviSynth+\plugins64\AutoBorderFiller.avsi, line 9 ... [and here it's cropped by the window]
try with simple script (to test)
ColorBars(width=640, height=480, pixel_type="yv12")
convertbits(32)
bore_SinglePlane()
if you still get "There is no function named 'bore_SinglePlane'" then you should ask bore devs or MeGUI
frobber
27th May 2026, 17:20
Yep. Same error. Thanks.
I'll report back what I find.
frobber
28th May 2026, 04:05
Following up: I think the error from MeGUI is due to some stupid permissions issue.
I resolved that but hit another problem:
[Error] MediaInfo
-[Error] [5/28/2026 11:59:02 AM] Error parsing media file C:\Users\Me\Downloads\MyScript-1.avs
--[NoImage] FillBorders: frame must be not interlaced or use interlaced=true.
--[NoImage] (C:/Program Files (x86)/AviSynth+/plugins64/InpaintDelogo.avsi, line 1766)
--[NoImage] (C:\\Users\\Me\\Downloads\\MyScript-1.avs, line 28)
I don't understand the error, because my script looks like this:
tfm().TDecimate()
Stab()
AutoBorderFiller()
bmasks=ExtractA().mt_inflate().TemporalSoften(3, 255, 255, 0, 2)
RemoveAlphaPlane().InpaintDelogo(mask=bmasks, Loc="0,0,0,0",Inflate=0)
mt_merge(last,TemporalSoften(3, 255, 255, 10, 2),bmasks,luma=true)
Greyscale()
# descratch(maxwidth=5)
Spline36resize(1920,1080)
Crop(240, 0, -240, 0)
Doesn't tfm() restore a progressive frame w/o interlacing? I'm confused about the complaint here.
Selur
28th May 2026, 18:35
You are assuming the TFM does change the frame info,...
adding "AssumeFrameBased()" should make sure the frame info is as you expected,...
see: http://avisynth.nl/index.php/Parity#AssumeFieldBased_.2F_AssumeFrameBased
real.finder
28th May 2026, 18:50
You are assuming the TFM does change the frame info,...
adding "AssumeFrameBased()" should make sure the frame info is as you expected,...
see: http://avisynth.nl/index.php/Parity#AssumeFieldBased_.2F_AssumeFrameBased
that will not work in @frobber case I think, since it's in frame properties not in clip, so it should be "propSet("_FieldBased", 0).propset("_Combed", 0)"
frobber
28th May 2026, 23:33
Thank you! This works:
tfm().TDecimate().propSet("_FieldBased", 0).propset("_Combed", 0)
Stab()
AutoBorderFiller()
bmasks=ExtractA().mt_inflate().TemporalSoften(3, 255, 255, 0, 2)
RemoveAlphaPlane().InpaintDelogo(mask=bmasks, Loc="0,0,0,0",Inflate=0)
mt_merge(last,TemporalSoften(3, 255, 255, 10, 2),bmasks,luma=true)
I'm trying Stab() both with and without this additional filtering, and will tile the samples together using ffmpeg to compare the difference.
Thank you, again, for helping me with this :)
vBulletin® v3.8.11, Copyright ©2000-2026, vBulletin Solutions Inc.