View Full Version : New Script: Software TBC 0.6 & Sample (was Fast Line Shifter 0.53)
jmac698
9th October 2011, 03:56
Hi,
This is a software "line" TBC. There is a new version, but it doesn't have more quality, just fast enough to be practical (in those cases where it can be used.)
Yes, it has worked for some people's real clips, which had severe "wobbly lines", but don't expect much. I still need to do some basic research here.
Update: New plugin version 0.61
http://www.sendspace.com/file/s78pp4
Installation note:
Please extract the included plugins and place into your plugins directory.
jitter02.zip -> jitter.dll -> plugins\jitter.dll
findpos01.zip -> findpos.dll -> plugins\findpos.dll
You may also need the VC2008 runtime.
If you get an error when trying to load the plugin, try these versions. They are called "static builds", a programmer's term, which means they are fully self contained. The only disadvantage is a bigger download. They are no different in functionality.
jdejitter03_static
http://www.sendspace.com/file/qk1ifv
findpos02_static
http://www.sendspace.com/file/e3jblt
The following applies to the old 0.53, and the theory in general can be used with the new version
----------------------------------
This script is in development and is only for the following cases:
-#1 Video was captured with HSYNC area (via driver tweaks)
-#2 Video contains easily distinguishable black borders on both sides (again, probably needing tweaks).
-If you have a hardware TBC there's no point, but case #2 in a digital file with the original analog source unavailable might be useful
-It's not necessarily any better than existing plugins for case #2
-If you have a supported card, it's possible to have the equivalent effect of a hardware TBC (they work very similar), at least for the "wobbly" function.
How to get the capture
The capture section has moved to "How to Capture with HSYNC" in the capture forum. That topic is unrelated to script usage.
http://forum.doom9.org/showthread.php?t=162832
Results (this only worked so well because I have HSYNC)
http://screenshotcomparison.com/comparison/88810
Results for case#2 from a user supplied video:
http://screenshotcomparison.com/comparison/91853
Theory of Operation
Before I get into usage, it would help to know what we're trying to accomplish. An analog video signal starts and ends each line with a pulse called an HSYNC. On a VCR, the timing is not stable. The capture card starts with the first HSYNC and reads in the video for a set time-period, then waits for the next HSYNC. The result is that the video can appear shifted by a different amount on each line. A hardware TBC probably works by reading the full line and re-adjusting it's timing to the standard amount, thus when captured, you get the entire video line.
With Case #1 we are capturing the HSYNC (the right half of it) and a large window of the video line - thus including all of it, regardless of it's timing. Now it's simply an image that needs lining-up. The script looks for the HSYNC line at the left, then a black area at the right, and resizes the line to a standard size (exactly equivalent to how a hardware TBC re-times). The HSYNC is very easy to find and is a one-pixel or so line. The right edge relies on a short black area which is 'blacker' than anything the video could be, so it's also reliable.
With Case #2 we're relying on black borders being in the video and the video being relatively bright. We simply line-up the black borders to the same spot. Dark scenes in the video could confuse the border detection so it's not as reliable, and that is the case with any other plugin (which is why I say mine is not necessarily any better in this case).
Usage
Quite simple, there is an autothresh which looks for black pixels in the left border. A small amount is added to this to form the real thresh. Adjust the added amount to tweak, until the picture lines up. I used AvsPmod to look at the raw video, and moving my cursor over the black area, I saw it was a noisy 15-25 (in a user test clip). I decided to just set 32. Values slightly lower would leave a few lines wrong, seen as little black stripes at the left edge of the video. For that particular video, only the bright scenes worked correctly. Having it work for dark scenes is being experimented with.
Advanced Usage
The script first makes a mask, in this case every pixel at thresh or above is marked to luma=255 in the mask or 0 elsewhere.
Next I pass my mask to findpos_h which searches for the first 255 value on each line, within the searchwindow. It also simultaneously searches from right to left by searchwindow pixels. It saves the results in a special clip, which records the number of pixels before the mask was found. For example if the HSYNC line occurs at x=4, the luma of the shift clip contains luma=4 for that line, at the left hand side. The right hand side contains the offset from the right of where the video ended, for example if it ended at x=710 and the video is 720 pixels wide, the luma of shift is 10 on the right hand side.
alignbyluma now reads the original video and the shift video and resizes to a standard size. Hopefully this is enough information to do anything else you want here.
What about the aspect ratio?
I still need to calibrate to return the correct aspect ratio. Currently the fixed video is probably wider than it should be.
Limitations
Check the history. Currently searchwindow is not working correctly, but it's enough to work. Also I need to add subpixel detection/shifting to get a really stable result. Currently the result looks like a little noise because of subpixel jitter.
#Fast line shifter Ver 0.53 by jmac698
#Lines up either or both edges of a video. Can also be used as displacement for 3d scripts.
#Requires Masktools v2a45+ (mt_lutspa mode), GRunT, GScript
#MinMax http://forum.doom9.org/showthread.php?p=1532124#post1532124
#Limitations: still no subpixel shifting
#0.53: Avoid possible "ScriptClip: Function did not return a video clip of the same colorspace as the source clip!"
#0.52: Less blurry resize
#0.51: Autothresh (uses 2 pixels of left border as a starting point, then adds a small amount to avoid noise)
#0.5: Slow, but using a completely new approach, and can resize whole lines
#0.4: Fast, can detect and line up on left or right edges
#note: sample was frame 167, http://screenshotcomparison.com/comparison/88810
#Modified to work with wide-window sample capture, which includes hsync
src=AVISource("D:\project001a\tbc2\vhs hysnc sample.avi").converttoyuy2
#crop(8,0,0,0)#Uncomment and adjust to remove extra black left border
thresh=int(findthresh(src))+3#This may not always work, try to manually set to 32 for example. Pick the lowest value which lines up picture.
ScriptClip(src, """
#Mark video edges
converttoyv12
m=mt_binarize(thresh)
#Line up video
findpos_h(m, searchwidth=22)
alignbyluma(src,last)
""")
addborders(56,0,0,0)
function findpos_h(clip m, int "searchwidth", int "x1", int "x2"){
#Searches m from left to right in the range x1 to x1+searchwidth-1 and right to left in the range width-1-x2 to x2-searchwidth-1
#for the first luma=255 pixel, then colors the output line with the offset from x
#for example m is 0 0 255 255 255 0 0 0, width=8, x1=0, x2=0, searchwidth=4 becomes 4 4 2 3 3 4 4 4, then 2 2 2 2 3 3 3 3
#Can only search for 255 pixels (as the luma output is only 8 bit)
#c and m should have the same clip properties (same size)
#searchwidth should be <=width/2
searchwidth=default(searchwidth,32)
x1=default(x1,0)
x2=default(x2,0)
rampexpr="x "+string(m.width/2)+" < x "+string(m.width-1)+" x - ?"#x w/2 < x w-1 x - ?
ramp=mt_lutspa(m, mode="absolute",expr=rampexpr)
notfound=searchwidth#Value to return if no mask on this line, should be >=searchwidth or you'll find the wrong minimum later
maskmarker=255#The luma value in the mask which indicates a detected pixel
#(if m=maskmarker return ramp else notfound), 255 means x>=255, x<searchwidth or notfound
mt_lutxy(m,ramp,yexpr="x "+string(maskmarker)+" = y "+string(notfound)+" ?")
#now make solid lines based on min luma found in each line
l=crop(0,0,-width/2,0)
r=crop(width/2,0,0,0)
l=l.minmax(0,0)
r=r.minmax(0,0)
StackHorizontal(l,r)
}
function alignbyluma(clip src, clip shift, int "mode"){
#Shift/scale each line of clip src by the x offset defined by the luma of shift
#for example if shift were all luma=8, the entire src clip would move 8 pixels to the (dir)
#This works on a pixel basis, so solid horizontal lines in shift can shift src by variable amounts per line
#It uses a simple replacement strategy, where each pixel in shift is tested and replaced by the same pixel in a shifted copy
#Currently handles only 0-15 shifts
#Magnify everything to get full color resolution
mode=default(mode, 2)
shiftuv=shift
shift=shift.pointresize(shift.width*2,shift.height*2)
shift=ytouv(shiftuv,shiftuv,shift)
src=src.pointresize(src.width*2,src.height*2)#We double here to preserve chroma rez
GScript("
for (y=0, src.height/2-1, 1) {
l=int(getpixel(shift,0,y).YPlaneMin)
r=int(getpixel(shift,shift.width/2-2,y).YPlaneMin)
getline(src, y*2)
align(l*2, r*2, 4, 4)
out=y==0?last:stackvertical(out,last)
}#for y
")#GScript
out
converttoyuy2
bilinearresize(src.width/2,src.height/2)
}
function align(clip v, int xl, int xr, int lb, int rb, int "mode") {
v#shift an image, x>0 shifts left, xl is amount to shift left, xr is amount to shift right
#mode 0 is shift left only, 1 shift right, 2 scale to shift left and right
mode=default(mode, 2)
offx=mode==0?xl:-xr
mode<2?pointresize(last.width, last.height, offx, 0, last.width, last.height):crop(xl,0,-xr,0).addborders(lb, 0, rb, 0).Spline36Resize(last.width,last.height)
}
function getpixel(clip v, int x, int y) {
#get color of a single pixel and return as a fat 2x2 yv12 pixel
v
#pointresize(last.width*2,last.height*2)
crop(x>0?x*2:0,y>0?y*2:0,-(last.width-x*2-2),-(last.height-y*2-2))
}
function getline(clip v, int y) {
v#return a line of height 2 from y to y+1
crop(0,y,0,-(last.height-y-2))
}
function findthresh(clip v){
#Find a resonable starting point for thresh by searching border
current_frame=0
v.converttoyv12
crop(0,16,-last.width+2,-16)
AverageLuma
}
sven_x
9th October 2011, 16:20
The subject has been discussed several times. Please take a look at this (http://forum.doom9.org/showthread.php?t=152706) thread.
There were some other attempts to correct line sync jitter (linked in the thread above). I have tested all of these functions - they are very vulnerable to noise which is an element of VHS recordings. Sometimes the resulting line jitter after correction is bigger and more random than that of the input video.
I do not know if someone has tested using a cross correlation function to find the beginning of a line which might reduce the influence of noise.
jmac698
9th October 2011, 20:23
sven,
I'm doing something that hasn't been done before. I have access to the raw signal including hsync so I have a very strong image to align to. See http://screenshotcomparison.com/comparison/86066
I have aligned the image, even though it was bright, and this line which seems to be hsync, but neither work correctly. My theory now is that it needs both sides. I've written a correlation function, but I don't need it as my test cases were quite clear but they didn't work. The problem must be somewhere else, not in our technique.
johnmeyer
9th October 2011, 21:00
As has been pointed out in previous posts on this subject, a good time base corrector, used properly, and inserted at the appropriate point in the analog signal path, will probably completely eliminate this problem.
jmac698
9th October 2011, 21:27
john,
That doesn't seem to make sense - I have access to the full raw signal, I should be able to make a real hardware TBC with only software. If you could d/l a program that does a TBC directly on capture, wouldn't that save you money compared to buying a TBC?
It only takes a low level access driver and any capture card can turn into a TBC.
I just need to develop the technique.
johnmeyer
10th October 2011, 01:51
Software is wonderful, but some things can only be done in hardware, especially when dealing with an analog signal. You really cannot create a digital TBC because the sync signal is only available in the analog domain and is not available once the analog video has been digitized. You cannot do the same thing simply by looking at the resulting badly captured video and then trying to figure out what to do with the bad video.
If you want really good video from your old VHS analog video, then you MUST use a TBC. I admire your desire to do many things in software, but this is one situation where you are not going to get very good results.
jmac698
10th October 2011, 02:51
john,
I appreciate your sentiment, however I seem to have not managed to communicate that I *do* have access to the sync signal, through special driver tweaks, you can see a picture of it (including the color burst) above.
So I actually agree with you, you need the sync, and I do have it.
ronnylov
10th October 2011, 07:45
The time between two line sync pulses should in theory be a constant but on a recorded tape it may fluctuate a little bit during playback because of tape tension. So instead of just syncing the start maybe you need to stretch back the length of each line to the nominal value. Check the time distance between each pair of hsync pulses and readjust the signal back to nornal. Then you can align the lines.
Just an idea you can try if you have captured the complete video signal. I don't know how a real hardware TBC work.
AVIL
10th October 2011, 14:50
@jmac698.
I think your approach is good. But IMHO the result is worst than original (as seen in your pictures). Could be that image pixels must be moved in opposite direction to sync ones. Perhaps the line must be streched/expanded instead moving the pixels. But I like this method.
BTW. Where can be obtained the modified driver that does sync signal capture? I find it very useful
sven_x
10th October 2011, 15:38
I have applied a very basic approach to your screenshots:
1. Analysing the provided VHS grabbings
2. Trying to sync the lines manually in photoshop and looking if this is possible and how the result looks.
Analysis
The picture below shows begin, middle part and end of a group if lines taken from your screenshot. One can see, that offset (time delay) is nearly constant in each line, that is the pixel shift is the same at beginning and end of the lines.
It does not correlate to the sync signal position.
http://www.engon.de/temp/avisynth/jmac698_line_sync1.png
Correction
I have imported the synced screenshot into photoshop. Line heigth was increased to 300% to get a better view.
Using a fixed selection window (720px x 3px) I copied each line into a new have layer and than manually shifted that line to a position that looked good.
The result is given below:
http://www.engon.de/temp/avisynth/jmac698_line_sync2.png
Conclusions
When a correction of the image could be done by eyes view it also must be possible to put that in a computer algorithm.
Lines from VHS grabbings cannot be synced by using what was regarded as sync signal
Lines from VHS grabbings can be synced using cross correlation between subsequent lines.
There is also a small jitter in line length, that needs an extra treatment.
The line offset jitter does not correlate very well to the beginning of the lines (which is the beginning of total black pixels) (but not as bad as to the sync signal)
Many thanks to jmac698 for the interesting approach and for the screen shots which helped me a lot.
johnmeyer
10th October 2011, 16:34
I think Sven's amazing post shows clearly that there is no correlation between the sync pulses you have captured, and the artifacts you are trying to remove. The easy conclusion is that they are caused by something other than time base errors. However, another conclusion is that you have not really "captured" time base information (or at least have not captured it correctly) with your modified driver.
I do not say this in order to be difficult, but at the risk of sounding like I only have one thing to offer, I would once again like to suggest trying to borrow a real hardware TBC and use it. I say this because the visual disturbance you are trying to correct is something I have seen many times, and a TBC circuit has, for me, always been able to remove the line displacements.
jmac698
10th October 2011, 16:42
:goodpost:sven,
That was incredible! I know this has been tackled many times, but I believe it's the first time there's been an analysis including the hidden area of hsync. It's very inspiring to me. However I can do a better job in making examples. I should record a test signal which is easier to line up, then we can measure directly the stretch/offset. My only concern is that the result won't be as bad as this tape. I picked this tape initially because it is a well-worn kids movie and showed massive jitter.
Does anyone know how I can make a tape with *bad* jitter? Perhaps if I copy many generations?
I had already felt that something was inherently wrong with lining up just by the left edge. People thought it failed because of their line-up algoritm (due to noise, etc.), but I had thought of the possibility of tape stretch and/or slight variations in head speed causing the line-stretching effect. In this case there's bad news, because I can only capture hsync once per line, if I capture the next hsync then I've missed the hsync for the next line and essentially never get any more lines.
Why that line is not good for lining-up is confusing to me however.
:goodpost:
jmac698
10th October 2011, 16:50
john,
You may be correct that the line is not an hsync, I will verify this. I'm not even trying to TBC that image; what's more interesting to me is to develop a method of software TBC. So I'm not trying to be practical here, just doing research and development. The research itself is interesting, but I still believe there's a good chance of a practical result in the end, and I'm using some techniques that haven't been tried before. That it failed initially doesn't matter, research is about answering a question. I haven't given up until I have fully understood the problem and concluded that it's impossible (at least for someone with my abilities).
Ghitulescu
10th October 2011, 17:15
Shifting is not the only algorithm in a HW TBC. It stretches and squeezes the analog line too. You need to do also a scaling in your algorithm.
IanB
10th October 2011, 22:35
...
Does anyone know how I can make a tape with *bad* jitter?
...
Using a tape you definitely do not love, unspool an amount of tape and carefully stretch it a little, then respool it. Viola jitter video.
When the tape is stretched it is not uniform. Better quality tapes use stronger base material which is more resistant to deformation. Commercial video releases notoriously use tape from the lower quality end of the range, penny pinching misers.
jmac698
11th October 2011, 23:32
I'm still looking to answer my original question: Can you suggest a way to speed up the dejitter script I first posted?
General discussion about the practicality of a software TBC has been moved to
http://forum.doom9.org/showthread.php?t=152706&page=2
Thanks.
(Where's the amazing Gavino? I bet you can optimize the script! And I finally had to use GScript :)
Gavino
12th October 2011, 00:21
I'm still looking to answer my original question: Can you suggest a way to speed up the dejitter script I first posted?
A very minor speedup to the existing script would be to initialise 'out' to a fixed blank line instead of calling getline(), since that first line is a dummy that gets cropped at the end.
Nice to see you using GScript :), but I wonder if it could be done more quickly using some Masktools wizardry to process the rows in parallel. I don't have time just now to figure out how.
jmac698
12th October 2011, 00:36
That's a good idea, you can do a pixel shift with a convolution, let me try.
Update:
You can shift the luma 10 pixels to the right like this:
mt_convolution(horizontal="1 0 0 0 0 0 0 0 0 0 0",chroma="process")
This also shifts the luma:
mt_luts( last,last, mode = "0 0 0 1 0 0 0 0 0", pixels = mt_square(3), expr = "y" ,chroma="process")
jmac698
12th October 2011, 04:14
Fast Line Shifter 0.2
[deleted]
StainlessS
12th October 2011, 07:38
Hi,
The script I have is extremely slow but it works - is there any way to speed it up?
How about moving GScript(" ... ") # GScript outside of ScriptClip,
I think that there is some kind of initialiser involved [Executed on every frame???] and it might
(or might not) make a bit of an improvement in speed, or maybe even try the GImport option. I think perhaps Gavino would have mentioned this if it would be useful, & so perhaps may not help.
jmac698
12th October 2011, 07:45
Good point.
My new version is faster; I'm still working on it.
Gavino
12th October 2011, 09:57
How about moving GScript(" ... ") # GScript outside of ScriptClip,
I think that there is some kind of initialiser involved [Executed on every frame???]...
A call to GScript has a relatively small overhead, equivalent to calling Eval, in which the code string is parsed and evaluated. It's all done at compile-time, so there is a per-frame penalty only when inside ScriptClip.
Doing it the other way round, calling ScriptClip inside a GScript string, has a problem in that GScript constructs cannot be used directly inside the ScriptClip part (without calling GScript again), as ScriptClip creates a new standard parser instance for each frame, bypassing the GScript parser.
What you can do is extract the GScript code into a function defined inside GScript (yes, entire functions can be GScript'ed), and then call the function from within ScriptClip. This reduces the per-frame parsing overhead to a minimum. Using this scheme, the original code could be rewritten as:
GScript("
function f(clip src) {
out=getline(src,0)
for (y=0, src.height-1, 1) {
line=getline(src, y)
shiftx=0
for (x=0, 25, 1) {
if (getpixel(src, x, y)>200) {
shiftx=x
}#if
}#for x
out=stackvertical(out,line.shift(shiftx-20,0))
}#for y
out.crop(0,1,0,0)
} # end f
")#GScript
ScriptClip("""
f()
""")#ScriptClip
I'm not sure how much difference this would make - probably most of the overhead is coming from the pixel manipulation anyway.
jmac698
12th October 2011, 11:05
Sorry, it was close, but pixel manipulation in any kind of loop is too slow, so I don't have a first use for gscript after all :(. If you could add a pixel reading/writing feature, that would make it extremely useful!
Update: See first page for a new version of a line shifter script.
Perepandel
12th October 2011, 14:07
The subject has been discussed several times. Please take a look at this (http://forum.doom9.org/showthread.php?t=152706) thread.
Wow Sven, you're referencing a thred in which jmac698, the user you're replying to, has already participated xD
As has been pointed out in previous posts on this subject, a good time base corrector, used properly, and inserted at the appropriate point in the analog signal path, will probably completely eliminate this problem.
John, you seem to miss the point: he's trying to make a time base corrector.
Software is wonderful, but some things can only be done in hardware, especially when dealing with an analog signal. You really cannot create a digital TBC because the sync signal is only available in the analog domain and is not available once the analog video has been digitized. You cannot do the same thing simply by looking at the resulting badly captured video and then trying to figure out what to do with the bad video.
If you want really good video from your old VHS analog video, then you MUST use a TBC. I admire your desire to do many things in software, but this is one situation where you are not going to get very good results.
Maybe we are not talking properly then. The truth is that it would be a hardware+software TBC. A bt878-based capture card is used to capture the (raw?) video signal, and then a software algorithm is used to correctly horizontally allign the video lines.
A friend of mine lend me a SVHS video with an integrated TBC. I already captured my tapes with it and the horizontal jitter went of; it really made miracles with some badly jittered tapes. But I still have the intereset in this project, sharing the motivations with jmac698.
The label on the SVHS says, about the TBC, "Digital 3-D circuit" and "Digital 3R picture system". If the advertising is true, then it means that the signal is digitized somewhere, then an algorithm applied to the digital signal to retore the stabilization, etc. That is the same principle that what we are trying to archieve with the so-called "software TBC". So John, I wouldn't be that radical in my opinion.
The time between two line sync pulses should in theory be a constant but on a recorded tape it may fluctuate a little bit during playback because of tape tension. So instead of just syncing the start maybe you need to stretch back the length of each line to the nominal value. Check the time distance between each pair of hsync pulses and readjust the signal back to nornal. Then you can align the lines.
I'm glad ronnylov had the same idea I shared with jmac698 in one of my previous private messages with (before seeing this thread and posting in the forum). That's were I would focus my efforts right now.
One of the problems would be the amount of pixels we could get per raw line or digitizing resolution. If we are limited to 800 and some, and we have to adquire the active video from it and translate the result to 720 pixels, then maybe we could get a lot of aliasing/lack of resolution, etc. But we should try it to see if it is acceptable or not. Also jmac698 said somewhere that he's able to get about twice per line that values, so in that case we'd probably have by far enough information to get good results.
Just an idea you can try if you have captured the complete video signal. I don't know how a real hardware TBC work.
That's also another thing I wanted to say: we need more information on how a stand-alone TBC works, in order to make ours.
jmac698
16th October 2011, 06:02
Hi,
I made a new sample, it's quite interesting
http://screenshotcomparison.com/comparison/87833
This seems to be a partial success; overall it's much better, but it seems to need lining up on both sides. That will be tough to script.
I tried to get as much of the video as I could this time, it's brighter, there's no filtering, as much of the left border, as much resolution as possible, and even all of the right hand side of the picture.
The bright green line turns out to not be relevant; I can move it with a register called AGC delay so it's not part of the video.
I've also made a package for you to analyze;
http://www.sendspace.com/file/jdh9j9
I don't think the cause of jitter is what we think at all! It seems to be related to picture content, and it can vary dramatically between lines. It's also common over both fields. It seems to happen more in bright areas but it's not consistent. Anyhow have any observations?
sven_x
16th October 2011, 11:43
Great job!
The corrected image points out that there is also a jitter in line length (wich might be a delay in head rotation or tape stretch or a delay in tape speed or even rippled tape). This jitter is very fast( i.e. a 1/30 of the time of a frame, about 900 Hz). A VHS player uses several loops to control frame synchronisation. So the source of this effect could also be any ringing or disturbance in the electrical signal flow.
I do not have the impression that jitter is related to picture content. It has an amplitude envelope that is rather a sinus curve. For me that speeks for electrical or mechanical "ringing".
using nnedi3(field=-2,nsize=2) one would notice, that
a) the jitter is different in the even and odd field
b) in both fields it is more present in the upper part of the frame and also in the same regions (line numbers). That is strange.
The AGC of the video signal reacts on the negative sync impulse. The AGC produces an overshoot because it tries to compensate the negative impulse (AGC tries to amplify video amplitude to 16...235). The AGC delay should be set so fast, that sync jitter does not affect luma of the first video pixels in a line. On the other hand a very fast AGC delay might lead to less stable synchronisation. Have you noticed any influences of the value to the quality of synchronisation, jitter frequency or luma modulations?
Another aspect is, the problem that appears in your grabbing can be described in other words with "the video grabber card does not synchronize to a proper line synchronisation signal". Have you ever tried to grab the same tapes with another card?
Mounir
16th October 2011, 23:33
I'm not sure if you have chosen the right video sample because animes are supposed to be 24fps right ? Perhaps a video with live content (concert) which is filmed at 29.97 fps, truly interlaced would be suited for the test ? The challenge would be to find one that has defects (lines shifts)
jmac698
17th October 2011, 01:07
@mounir
I don't follow your reasoning, I like this content because it's fairly steady and you can tell if the picture itself is not lined up. I'm really looking for straight edges in the video. With animation, there's a few frames that aren't interlaced so I can view the frame as a whole (though we could also analyze through separatefields; I don't care about picture quality/interlacing at all just the jitter). The video is still 29.97 btw; just that entire pictures repeat themselves.
@sven
The AGC register seems to set the point in time where the amplitude is measured. I've positioned it in the hsync area. The picture brightness changes if I move it into various zones. The line it causes to be drawn on the video seems to be as you say; the ringing as it reacts to the amplitude. The video was far brighter than it had to be too, but it doesn't matter for now.
Notice the first scene change - the jitter area instantly changes. We also have to consider if macrovision could be influencing our analysis, I should try another example. Anyhow I'll try lining up both sides and some other experiments. I am still hopeful this can be done based on the partial improvement.
Btw I'm seen "bowed" video which bent to the right on the lines which had high average brightness, I suspect that's an electrical effect, possibly due to aged components in the circuit which is not operating properly.
*.mp4 guy
17th October 2011, 04:51
I have not read the entirety of the thread however, I do not think this has been mentioned. It looks like instead of shifting only, you must shift, and stretch so that both ends of the signal space line up. If you think about how the jitter errors are created (stretching of tape / time signal), it makes sense that the entire "time" (horizontal) dimension would need to be resampled to enforce consistency.
So what must be done is:
1 shift signal so one edge is aligned
2 calculate stretch/squash (resample factor) needed to maintain correct signal length
3 resample signal accordingly
4 hope that most time-scale variance is between lines rather then within lines
jmac698
17th October 2011, 10:10
@*.mp4
This seems to be a partial success; overall it's much better, but it seems to need lining up on both sides.
@sven
Oops, also I can capture with two cards at once - but have no doubt, even modern cards show jitter. In fact, some chips have a feature such as Ultralock, which are supposed to be a line TBC (horizontal shift only), however they don't work. It will be interesting to test the performance of various capture cards in this way, but also to finally discover why it's not working as well as a typical TBC.
sven_x
17th October 2011, 18:58
@jmac698
I just had a second look at your latest screen shots (http://screenshotcomparison.com/comparison/87833). Now it looks to me that not only the sync impulse could be taken to line up the lines, but also the transition from very black to grey would do. If the very black borders on the left and right are deeper black than the rest of the image, an algorithm should be possible that finds the very first and very last pixel of the line video content. Doing so we'll get length and pixel offset for each line so that we can
- shrink the line to standard length
- shift the beginning of the line to a standard position
Then all of the driver manipulation would not be necessary and the method might work with USB grabbers as well.
If the borders are not in a deeper black this method will fail in some cases.
jmac698
19th October 2011, 22:03
Ok, I have an alpha version of the stretching function. It works:
http://screenshotcomparison.com/comparison/88691
So we've solved software TBC - it was so simple, why didn't anyone do this before? All you need to do is line up both sides. I doubt we even need hsync at all. It's still going to help to see the porch area.
ronnylov
19th October 2011, 22:36
It seems to introduce aliasing in areas that did not have aliasing in the original sample, like on the strings across the chest of the hunter.
Maybe it needs some more tweaking?
Mounir
20th October 2011, 00:48
that is surprizingly good result to me! (with some aliasing, true that) ; if you could give the procedure to tweak the drivers...
jmac698
20th October 2011, 02:23
I don't think you need to tweak the drivers at all, it's just a simple script to line up the edges. The aliasing and top line are just bugs. I'll be tweaking for speed and quality.
Ghitulescu
20th October 2011, 09:12
:goodpost:
I can hardly wait for the script.
jmac698
20th October 2011, 10:20
Thanks, I suppose I should stop playing video games and try to finish it :)
Maybe I'll get a coffee first...
There's speed and quality issues still.
sven_x
20th October 2011, 10:41
@jmac698
I am very impressed! Fantastic result.
Perhaps the algorithm to find borders could become a bit more robust. There is still some ripple. (Using a small cross correlation function for border areas only?)
I am just starting to tweak my VHS grabbings with avisynth. Didée has posted some scripts that use NNDEDI(-2) as deinterlacer. It extracts both fields of a frame, wich gives the odd or even lines of two successing frames and interpolates the missing lines. Then the two resulting frames are merged back into one. With digital sources this also acts as good antialiasing algorithm.
oo=last
nnedi3(field=-2,nsize=0,nns=3) #DVD content: nns=3
merge(selecteven(),selectodd())
D1=mt_makediff(oo,last)
D2=mt_makediff(last,last.removegrain(11,-1))
last.mt_adddiff(D2.repair(D1,13,-1).mt_lutxy(D2,"x 128 - y 128 - * 0 < 128 x 128 - abs y 128 - abs < x y ? ?"),U=2,V=2)
o=last
But in VHS sources (at least in my own sources) we have the problem, that every second frame has a slightly different position as every first, after deinterlacing. Just browse through the frames after a NNEDI3(-2). You will see it.
Now when both frames are merged back into one the offset between the two frames leads to a blurring effect. The resulting composed frame is less sharp. On the other hand it has more details as one of both single frames, because NNEDI3 cannot invent details that are smaller than three lines.
His method also reduces the noise, because tape noise from both frames is not correlated much. (But film grain of course is not reduced, when both frames originate from a 25 fps movie).
Nevertheless -- his method would work much better, if we succeed in lining up both interpolated frames at exactly the same position.
@Mounir
The aliazing might result from the fact, that the grabbed frame is still interlaced, not processed.
jmac698
20th October 2011, 10:59
Thanks,
The antialiasing is a bug in my script which is easily fixed. I've seen Didee's method on a german forum, it was very impressive! But for cartoons I have another method already for this, I call relative jitter. It correlates the same background image over several frames, so even if there is jitter, it's the *same* jitter for several frames, so the frames can be temporally processed. The result is less noise, but still has a static jitter pattern. It doesn't help your problem.
There is a slight jitter still in my sample because I need a subpixel algorithm. That will take some more work.
jmac698
20th October 2011, 13:25
New version, faster, bugs fixed.
http://screenshotcomparison.com/comparison/88810
sven_x
20th October 2011, 14:55
Just wanted to apply your script to my own source, but I get an error: Invalid arguments to function "YPlaneMin" (GScript line 3 and 8).
jmac698
20th October 2011, 15:19
It was tested on 2.6a3
I dunno what to say, it works for me.. maybe I should repost.. I actually did edit it some online
Gavino
20th October 2011, 15:41
Just wanted to apply your script to my own source, but I get an error: Invalid arguments to function "YPlaneMin" (GScript line 3 and 8).
You need to use the GRunT (http://forum.doom9.org/showthread.php?t=139337) version of ScriptClip, which allows run-time functions like YPlaneMin to be called inside a user function.
jmac698
20th October 2011, 16:06
In other words you need GRunT installed...
Mounir
20th October 2011, 16:10
Error: "compare plane: this filter can only be used within run-time filters" (Gscript line3 ,line8)
tested with avisynth 2.6.0.2 and good filters versions i believe
jmac698
20th October 2011, 16:28
I used the version I posted and it works, do you have GRunT installed as well? I think I'll just remove the dependency on Gscript/Grunt...
New comparison
http://screenshotcomparison.com/comparison/88873
This shows lining up with video only. To me it looks like it's worse. So that means my hsync technique does help and the side should look ragged when TBC'd.
sven_x
20th October 2011, 17:16
@Gavino
Thanks! Works with Avisynth 2.5.8 and GRunT installed.
To me it looks like it's worse.
The last 10 lines or so are shifted to the left, but they should'nt. Looking at the source frame you see a black vertical line there in the lower left corner that the algorithm is falsely taking for the left border (indeed it is content of the frame).
If the black inside the line cannot be separated from the black of the border perhaps some plausibility checks must be involved (see this (http://forum.doom9.org/showpost.php?p=1530806&postcount=26) post).
jmac698
20th October 2011, 17:57
Aha. There's other differences, but I can't tell which is better. I should run a test signal through. I left detection as a mask just for the purpose of problems like this. The resulting shift mask can just be blurred to reduce sudden changes! The same ideas of limiting it can be turned into a vertical convolution...
How does it look on your sources?
johnmeyer
20th October 2011, 18:43
I just tried the script, unaltered, on some 720x480 NTSC interlaced footage. The left side is the original footage and the right side is the footage processed by the script. As you can see, it looks like the script is actually producing the very problems it is supposed to eliminate! I didn't alter anything in the script -- just downloaded it and ran it.
http://i177.photobucket.com/albums/w208/johnmeyer/Before-After.jpg
Do I need to have the additional information that your capture drivers provide? If so, how do I get that?
sven_x
20th October 2011, 19:42
@johnmeyer
Did you feed the script with the left input? So there are the borders missing that the script uses to find the transition to the beginning of the "real" line beginning and end. Without borders it cannot estimate anything.
@jmac698
My sources are grabbed with an 15 Euro USB Video Grabber and a fairly good, very old Toshiba VHS recorder. With this setup sync disturbances are rather small. In most cases your script does not find another solution to line up the lines. I can see no differences between source and processed (switching with AvsP through the scripts).
One video is the copy of a copy from a camcorder. This is the only one with some ripple at the border that needs adjustment. The result is better.
In one case the result looks pure: lots of lines out of row that were okay in the source. I have no idea what is causing this. The borders look clear and smooth in the source.
PAL recordings have 576 lines. A part of the algorithm seem to work with 480 lines only.
I have the impression that removing the blur effect (that occurs when merging two frames) requires sub-pixel accuracy (at least 1/2 pixel).
johnmeyer
21st October 2011, 01:14
Did you feed the script with the left input? So there are the borders missing that the script uses to find the transition to the beginning of the "real" line beginning and end. Without borders it cannot estimate anythingOK, here's another one from an ancient LP VHS recording. It definitely has the left & right borders that give the script something to "grab" onto:
http://i177.photobucket.com/albums/w208/johnmeyer/Before-After2.jpg
I've read through the script, and tried changing both the "thresh" and "searchwidth" parameters, but that didn't fix the problem.
The left border in the original (before) is about ten pixels wide, and the black border on the right is about four pixels.
Here's a link to 2-3 seconds of the footage from which the left snap was taken:
Test File (https://www.yousendit.com/download/T2dmS3duT2JEa1VYRHNUQw)
jmac698
21st October 2011, 02:09
Top post updated. It's up to you to make a mask, try returning mt_binarize(thresh) to see what's going on. AvsPmod is useful to cursor over the black bits and read the Y (luma) value. It was made for a special capture with hsync and/or viewing blacker than black at the borders (with NTSC-J capture).
Mounir
21st October 2011, 15:27
You still haven't explained how to tweak the drivers exactly and capture the HSync, i hope you'll talk about it
jmac698
21st October 2011, 16:33
It's pretty simple, open virtualdub, select bt8x8 tweaker, move the left border slider to the left.
sven_x
21st October 2011, 17:18
@johnmeyer
Opening your source screenshot in Photoshop, selecting the inner image area and displaying a histogramm shows, that the image possibly has undergone a levels adjustment. The levels of the inner area lay in the range 0 ... 255 (well, deepest luma is a bit higher), so that searching for a minium might not land in the border area. After a levels adjustment the borders are not in a deeper black.
http://www.engon.de/temp/avisynth/jmlevels.jpg
Nevertheless in one case the script produced also a bad result with my own recordings. That was recorded from TV with a very cheap PAL VHS recorder.
William.Lemos.BR
21st October 2011, 18:29
I´m trying to use your script but I´m getting this error message:
"Script error: there is no function named "mt_binarize" ([ScriptClip], line 5)"
I have Masktools v.2a48 installed (also GRunT and GScript). I´m using Avisinth 2.6.
I´ve looked for a solution, but didn´t find it. Don´t know what to do.
I know probably this is a noobie mistake, but I appreciate if somebody could help me...
jmac698
21st October 2011, 19:13
That doesn't seem a problem with the script itself, but you can try
colorbars(pixel_type="YV12")
mt_binarize(162)
If that doesn't work on it's own, something's wrong with masktools installation. Try the support thread at http://forum.doom9.org/showthread.php?t=98985&page=23
William.Lemos.BR
21st October 2011, 19:29
It didn´t work. I´ll try the support thread.
Thanks for your reply!
jmac698
21st October 2011, 19:32
@sven, john
I should note that the search stops at the first pixel from the left or from the right, so it stops at the border even if there are black pixels in the picture.
jmac698
21st October 2011, 20:00
Since people have asked about it, there's no a preliminary capture guide in the capture forum: How to capture with HSYNC
William.Lemos.BR
22nd October 2011, 01:14
Just for the record: the problem I´ve reported before (with mt_binarize) had nothing to do with your script or masktools;
The problem was in my Avisinth (solved when I uninstall and reinstall it).
Sorry for this...
sven_x
22nd October 2011, 09:52
I discoverd, that the thresh value in ScriptClip has extreme influence in videos that have black details in the pixels on the left and right side. They are falsely regarded as borders by the algorithm. (see picture)
http://www.engon.de/temp/avisynth/tbc02.jpg
Influence of the Thresh value
Thresh=72 much too large, considers dark image details to be part of the borders, produces wrong results (see above)
Thresh=16 This should be the best value with standard levels video, but I found that the results are extremely random, that is, they react strongly on the noise that is superimposed to the ramp. Often gives very bad results (random line offset fluctuations).
Thresh=12 Much more stable, gives a nice vertical alignment of the lines. But in some videos borders are not recognized anymore.
Disadvantage: The algorithm thinks, that the real border is somewhere in the deeper black border areas, so that it shrinks the whole line to match the standard line width - details are lost.
Conclusions and proposals
The tricky part of the algorithm is how to find the border. Using a thresh reacts to strong on noise in the transition area between border and video content of the line. Some kind of avaraging should be applied.
Plausibility checks might be necessary to prevent wrong offset values that lay wide out of normal range. In a debug mode this checks could trigger detailed error information.
Perhaps thresh value could be adapted automaticly somehow to the luma levels (in other words: using autolevels for the frame that is analyzed, but perform stretching to the original frame.)
The blur effect that occurs when shrinking the line could be avoided, when all lines are stretched to, say 120% or even 200% instead of shrinking.
sven_x
22nd October 2011, 13:34
This is a smart part of the left border from the above frame. Scaled to 800% and luma range is stretched to 0...128 (and 0...32). This gives an impression, how tricky the algorithm has to be to find the real border. It is very difficult.
http://www.engon.de/temp/avisynth/tbc03.jpg
jmac698
22nd October 2011, 14:30
@sven
Great post again! I implemented one of your suggestions already, it works for me.
I didn't design this for the "case #2", of black borders in existing video. But it depends on your video. My videos have huge jitter, yours seem quite smooth. I would need a sample of your video to work with.
jmac698
22nd October 2011, 15:11
Updated to a better resizer. You can also enlarge your clip before using the TBC.
sven_x
22nd October 2011, 15:20
What to do, if one has no idea of programming languages, mathematica and those things? Voilla: Building a simple cross correlation function in Photoshop!
(Edit: This is not so easy as I thought before. Meanwhile version 0.2 of this post.)
Below you see the result. Input was from a frame which lines had a bumb on the upper left side.
Layer 1: Border area from frame (10 px black + border area + 10 px of last value stretched to 10 px)
Layer 2: an ideal border (15 px black, 15 px 50% grey), modus = multiply
Than select all, copy all reduced to one layer
The following has to be applied one time only and can be recorded with the "actions" panel in photoshop:
- Insert layer, copy it 9 times an set transparency for each layer to 1/(1+n) (the first must have 100%, then 50%, 33%, 25%...)
- Shift first 5 copies 1...5 pixels to the left, shift copy 6...10 1...5 pixels to the right.
Doing so you get the sum of all layers in the row in the middle. This simply acts as a trick to sum up the 10 pixels of each line of the product of layer 1 and layer 2.
- Then shift layer 2 (the ideal edge) 1 px to the left and run the same action. Do so for -2...-5 and +1...+5 px.
In a last step I copied the middle row of each layer set into one image, giving cross correlation from tau - 5px to + 5px as 10 rows of pixels. For a better look I have inverted the result and enhanced contrast by using the levels menu.
http://www.engon.de/temp/avisynth/tbc05.png
It would be better to test such an algorithm using a mathematic programming language.
Problems:
Borders are dark areas, which have luma levels close to 0.So a multiplication with another value (the ideal edge) gives allmost no information. We would obtain more information about the borders when inverting the image before multiplying.
Ghitulescu
22nd October 2011, 15:39
Imagine you do this by hand 135000 times, for a regular VHS movie. Not to think about interlacing issues :)
jmac698
22nd October 2011, 16:44
That's easy...
#Border Detection 0.3 by jmac698
#A function to find the left edge of a video with a black border
#Requirements: corr2d http://avisynth.org/vcmohan/Corr2D/Corr2D.html
# GRunT
AVISource("D:\project001a\tbc2\vhs hysnc sample.avi")
border=16
crop(0,0,-last.width+border,0)
edge=makeedge(last,border/2)
corrbyline(last, edge)
function SplitLines(clip c, int n) {#duplicates then crops each copy in a different spot
Assert(c.height%n == 0, "Clip height not a multiple of 'n'")
Assert(!(c.IsYV12() && n%2==1), "'n' must be even for YV12 clips")
nStrips = c.height/n
c = c.ChangeFPS(nStrips*Framerate(c)).AssumeFPS(c) # Repeat each frame 'nStrips' times
BlankClip(c, height=n) # template for ScriptClip result
GScriptClip("c.Crop(0, (current_frame%nStrips)*n, 0, n)", args="c, nStrips, n")
}
function MergeLines(clip c, int n) {MergeLines2(c,n,n)}
function MergeLines2(clip c, int n,int i) {
i<2?c.SelectEvery(n):stackvertical(MergeLines2(c,n,i-1),c.SelectEvery(n, i))
}
function makeedge(clip v, int x){
#Based on the properties of v, make an edge of black/white, with white at x
x=x/2*2
v
blk=blankclip(last, width=x, color_yuv=$108080)
wht=blankclip(last, width=last.width-x, color_yuv=$EB8080)
stackhorizontal(blk, wht)
}
function corrbyline(clip v1, clip v2){
#Correlate two videos, line by line, and return the correlation surface. The position of peak luma indicates maximum correlation position.
scale=2
interleave(v1, v2)
pointresize(last.width, last.height*scale)
h=last.height
splitlines(scale)
corr2d
selectevery(2,1)
pointresize(last,width, last.height*2)
crop(0,0,0,-last.height/2)#get only the top line
mergelines(h/2)
pointresize(last.width, last.height/scale)
}
sven_x
23rd October 2011, 20:17
I run a few test with Corr2D, because its documentation sais fairly nothing about its implementation.
The origin of the output coordinate system lies at 128,128 (which is 1/4 of width and height of input frame). The function is symmetrical both in x and y direction.
Corr2D calculates cross correlation between each two consecutive frames. So interleave(edge2,edge1) provides a test input which gives cross correlation between two different edges.
Results
http://www.engon.de/temp/avisynth/tbc06.png
The first two rows show the auto correlation functions for a black 255 px border and a black 170 px border.
The next rows show cross correlation for different edge combinations. One can see that the length of the correlation function correlates to the pixel distance of the input edges.
With a very small distance of 3 px one could not tell much about this distance, because output does not go to zero with a zero offset (see the ACF plots in the first two rows). A cross correlation between two identical edges is the same as an autocorrelation of that edge.
Edit: The bad resolution for small distances is an effect of scaling. If we blow up input line and reference line to, say, 400% length, then CCF should be able to measure distances of a few pixels too.
Part 2: Getting closer to the real thing...
The next picture shows the cross correlation between an ideal edge model (256 px black + 256 px white) and a second edge (170 px black).
The second edge has been disturbed by
a) binmialblur(20)
b) binmialblur(80)
c) binmialblur(80) and make contrast weaker (black + dark grey with luma 64 only)
c) all of the above + strong noise superimposed
This was done for better simulation of a real transition from the black border to the beginn of video content of a line.
http://www.engon.de/temp/avisynth/tbc07.png
The output result of CCF looks very stable. It does not react on noise, blur, and bad contrast.
Continued with a test of real video input in posting 90 (http://forum.doom9.org/showpost.php?p=1535150&postcount=90).
ChiDragon
24th October 2011, 01:14
The technical details are over my head. Is this solution going to be limited to content without black or near-black as part of the active video image touching the dead black borders?
vcmohan
24th October 2011, 04:13
My plugin DeJitter (http://avisynth.org/vcmohan/DeJitter/DeJitter.htm) may help under some circumstances.
jmac698
24th October 2011, 04:56
@sven,
Excellent analysis again, that's exactly what I was going to do to verify the calculations, except you should know that the location of the brightest pixel corresponds to the change in edge between frames, so you are measuring the relative distance between the two edges. If one edge is fixed this becomes the jitter. The coordinates are based where 0,0 is at width/4, height/4.
@Chi
This is just an experiment proposed by sven to find a better way of detecting the edge of the video. The black/white has nothing to do with it, the correlation function is a way to detect a shape when there is noise. We are making the ideal shape (a black/white border) and comparing it against video, and the location of the best match is the edge of the video (even though it's not black/white). The diagrams above are testing with fully artificial borders to ensure we are getting the proper coordinates back.
@vc
Unfortunately I started this because I couldn't get your dejitter to work in my case, also it turns out we need not just shifting, but stretching to fix the video. If you could update your plugin it would probably be better than my script at this point.
sven_x
24th October 2011, 18:08
Look in my post above. I have it updated with part II of the test, which is getting closer to the real thing (a blurred line with noise and low contrast).
jmac698
25th October 2011, 01:27
@sven,
Great analysis again.. I have analyzed correlation myself before, it is not affected by:
-order of the pixels
-mean of the two sets (brightness)
-noise
-multiplication (contrast) of one of the sets
-probably not affected by blur, because it's a combination of multiplications
In short, it's a good way to match shapes except for the order part. Anyhow, did you test my script on real video?
sven_x
26th October 2011, 14:59
vcmohan, the author of Corr2D has sent me some more comments that I am allowed to post here.
Thanks for using the Corr2D plugin. It was designed to find shift between two frames. [...]
We want to process each line separately. Infact we only need a Corr1D plugin (which would be much faster), but we have none :-)
So were testing in a first step whether it is possible to use cross correlation to find the offset where the video signals starts in a line compared with the edge position of an ideal line model.
If it works the next step will be that someone writes a plugin.
I tested Dejitter and some other avisynth scripts that were posted over the years. The results are very random and it is not clear what produces artefacts.
Do you have an example script where you use the output of Corr2D as input for another function?
The Corr2D plugin output is apart from fft display is textual at end. Its output can not be used automatically in other plugins. It helps in arriving at parameters for FExpanse plugin. Or UNFurl Plugin.
1D FFT correlation is used in UNFurl plugin, but it is averaged over a number of adjacent scan lines. So it will not help you. In FFTQuiver plugin F1Quiver function does a 1D FFT and and in test mode displays.
For your specific need an extension to any of these plugins need to be coded. While making the DeJitter plugin at first I tried 1D Correlation, but gave up as there was no way of separating image charecteristics from the scan line distortion.
This is exactly the reason even FFT fails. Unless the image has a measurable difference on the left edge, it is not possible to identify its start.
When there are other structures in the very first pixels of the line they will contribute to the cross correlation output.
In any case I think a dejitter algorithm has to apply some plausibility checks.
Another idea is to clamp the video content of the line to a luma of, say 30%, so that the jump between black border pixels and video content gets more weight.
jmac698
26th October 2011, 15:16
I already posted a working script for this, to get correlation on each line. See above.
William.Lemos.BR
26th October 2011, 20:10
First of all I´d like to congratulate you for your good work. I´m following for some time your efforts to make a software TBC and how you didn´t give up even with so many people saying it would be impossible. Your tenacity complies with Einstein´s quote: "Something is only impossible until someone doubts and prove otherwise". I admire that!
I´ve tested your script and can say: it works wonderfully. It is necessary some adjustments in the threshold depending on the video, but it really works!
I intend to use your script to line up the fields of multiples copies of the same video in order to calculate it´s median (I´ve contacted you in the related thread, do you remember?). At that time (as I said to you) I came to the conclusion that the shifts between each video´s fields were equivalent, so it wouldn´t be necessary to line them up. But I tried to apply the same principle to the audio. After much struggle I´ve managed to do it using MatLab, since I still don´t have the needed programming skills (I can share the Matlab program I made with you, if you want). As expected there was a noise reduction, but with a side effect: a residual and constant noise (light “crackling”). So I figured out the reason: the misalignment between the audio of each video. The audio of a videotape don´t have a sync pulse so even if you line up the beginning of each waveform will not fix the shift present some samples ahead. That was the cause of that constant crackling. That made me think: if this happens with audio (that has a much smaller bandwidith) happens much more with video. So I took time to compare again the different videos line by line and noticed that actually THERE WAS very little shifts between them. My (obvious) opinion is that it is imperative to ensure the strict alignment of the pixels between each video in order to do a correct median process, otherwise the resulting video will be full of noise.
The problem is that at the present stage of development your script is extremely slooooooow and I need to implement it right now. I wanted to help someway but I still need time to learn and understand all tech stuff, etc.
So when I was practically giving up remembered of an old VirtualDub´s plugin that could do something similar and decided to give it a try. Maybe you know it...
http://midimaker.narod.ru/filters/vhsrest.html
It is in russian (nothing that google translator can´t solve). Have you tested this one? It is impressive, much better than Dejitter! It is surprisingly fast and has a reliable edge detection, since you made the needed adjustments on it´s parameters (your automatic threshold detection algorithm beats it).
I´ve noticed it is important to set:
“Max offset” - big enough to get video´s edge (increasing it too much may provoque wrong edge detection)
“Interline SR” – this limits the shift diference between each line; also don´t increase it too much (in my case used 3, but depends on the video)
“Adicional offset” – OFF (it seems to be a way to avoid wrong detection caused by noise but in my case only provoked jitter on the top lines)
It also may be used with a kind of “subsampling” precision using this trick: stretching the horizontal resolution 2x before process (it seems you need to select the “2x oversample for processing” option for that). I´ve only tested it yesterday but the results were promising. (OBS: This option caused a crash in my Virtualdub (1.9.11, 32bits). I had to use other program to run it, then it worked).
This plugin don´t resize the lines (it only aligns them), so our aproach is more robust. But I think that perhaps you can get some new idea or even use it, as I will.
I´ll let you know if I discover something that can help somehow. Sorry for my bad english...
Best regards!
jmac698
26th October 2011, 23:06
Thanks, that was very interesting.
Audio
I've completely ignored audio. In the case of multiple pass VHS, I was thinking to take one version. I'm surprised that combining multiple audio passes leads to crackling; I've mixed two tracks of audio before and it created a phasing effect or hollow sound. Ideally, the audio should be stretched at the same time as the video, line-by-line. however, it takes 3 samples at 48KHz for each video line, so it's a very small effect.
Multipass video
I had thought of a completely different technique for this; a "relative" TBC; each copy is lined up, line-by-line, but the lines themselves are not lined up, so each copy is "wrong" in the same way, enough to perform the median. I had noticed that my lines looked approximately the same, but hadn't looked at the lines in detail, good to know. I have noticed the mismatch effect on a large scale, when one of my copies was missing a frame. It's an odd effect. However the resolution of VHS is low enough that a few pixels of mismatch I feel doesn't make a huge difference. There's a few ways to line up the lines; corr2d plugin, mvtools especially with horizontal search only, even line-by-line versions of dejitter or vhs-restore. Did you notice any stretch as well as shift in your lines? I suggest to do a relative line-up, median, followed by a software TBC.
Improving Software TBC
I know my current version is slow, it would be even slower without the new plugin made for it. I'm sure I can improve it but it will take time and work. I see this problem as broken down into detection phase and repair phase; the detection is fast but the repair is slow now. I can improve detection for the "case #2" of black borders in existing videos. I can use vhs-restore.vdf in the detection; to apply it normally and also to a mirror-image video; finally to detect what actions it took, then to re-repair it myself! Quite a long away around it. Ideally I have to eventually write a full plugin for this. The detection improvements are obvious, someone has suggested them before (such as limiting per-line change). However this depends on the source as well, others seem to have typically low change per line, but in my current test video there is large change per line.
Coming Soon - Perfect TBC
I am very close to the ultimate test which I've always wanted to do: perfect TBC. I do this by making a test signal which I can line-up perfectly. I put this signal on each edge and then can gather statistics of the true jitter; also test the performance of any "blind" dejitter algorithms. This test signal is amazing, I can deduce the linearity of the digitizer; find the video levels calibration; do dejitter and shifting; measure frequency response; measure noise. Ultimately it can perfectly calibrate any aspect. I can't wait to see what such a video would look like!
jmac698
26th October 2011, 23:38
For another topic; thanks for your compliments! On the contrary, why do people think it wouldn't work? I'm only guessing here; but perhaps they're reasoning is, it's unheard of and no one has done it before, or else people have tried but had no real success; and theoretically, they know a hardware TBC has special access to the signal, so it can't work. Why has no one noticed before that you need stretch? Has "mental baggage" really stopped many people from trying? All I've done is line up two edges of an image; it's really simple. I don't know if I don't give up; but rather I retained my interest. I love learning and like to explore something until I understand it for myself, so if someone tells me it's impossible I still want to know why, and maybe then I notice it's not impossible :)
I generally like to deal with abstract ideas. In one personality theory, I have the "dreamer" personality dimension as predominate. This is common in 1/3 of the population. Most people have the "practical do-er" personality, and they are likely to give up much sooner and for example, either say it can't be done or just buy the hardware TBC. They tend to disreggard basic research as impractical. I know that the world needs each type of personality; many inventions really need a specific personalty. After it's invented; we lose interest, that's where the do-er might take over (perhaps a busiiness partner) to get it out there.
jmac698
27th October 2011, 00:08
I should point out that sven was the first to post about noticing stretch; though it was obvious in my tests.
jmac698
27th October 2011, 00:20
Also to point out that Border Detection 0.3 needs some more work; it seems it should be scaled wider to show more of the correlation surface (it's for testing only).
sven_x
27th October 2011, 11:15
I found that the input for Corr2D CCF should have a least an area of 100 px height and 200 px width (better 400 px). Then we could cut the line with the right half of the CCF from the resulting output area. With smaller resolutions the output pixels of ccf are scaled down which results in avaraging with their black neighbor pixels.
It is better to use the right side because the left side is terminated at (width/4) px.
When using the output of CCF it better has to be
blurred (perhaps - please look at the last example in my Part 2 image with strong noise superimposed on input - this gives some noise on the ccf output)
converted to black/white using a thresh to get a sharp length of CCF
subtracting the number of pixels that the auto correlation of the border model produces, because in the case when the real border is exactly at the same position as the border model (i.e. offset = 0) the output reduces to the autocorrelation function of the border (and not to zero)
(To acchieve a similar effect perhaps the left side of the CCF could be automaticly subtracted from the right side using a kind of mirroring at x=width/4.)
set the border model to a minal number of pixels (say 2 px) to get offset values in one direction only
When the input is scaled up enough we do not need convert exactly the number of pixels of the output function to get an offset value, because 11 and 14 px give the same result (3) when 5 pixels in CCF correspond to 1 px input (before it was inflated).
ronnylov
27th October 2011, 13:34
How to get the capture
The capture section has moved to "How to Capture with HSYNC" in the capture forum. That topic is unrelated to script usage.
http://forum.doom9.org/showthread.php?p=153072
[/code]
This link is wrong? They talk about SVCD creation in a 9 year old thread.
Should be: http://forum.doom9.org/showthread.php?t=162832
jmac698
27th October 2011, 13:58
thanks, fixed
jmac698
27th October 2011, 19:21
First test of my "perfect TBC" test signal, I have a number which is proportional to the shift in high accuracy. This was a test of an actual VHS. It's just the detection value, no dejitter yet. I also have to scale the number into actual pixels first. At least it looks like it's going to work. Oh, and that 'head switching noise' that everyone crops? No need to, I can recover quite a bit of it.
jmac698
28th October 2011, 23:24
Results of my jitter analysis. Good and bad. My test shows jitter is +-.25 in left border and +-.5 in right border. This is with a modern card which probably has an Ultralock type feature. I hardly need TBC for this tape.
The samples of jitter I've provided before were from an older capture card, and a worn VHS which possibly had macrovision, that could explain why it's so much worse.
I need to prepare more tests with a VHS in LP mode, with a stretched tape, or with a copy of a copy to get some real jitter.
Perepandel
30th October 2011, 13:28
Hi! I finally tried to take a look at this. Have a hardware TBCd capture that, even that, has a couple of seconds with jitter that it seem it wasn't able to recover (or maybe it came from a previous generetion or whatever). I've haven't been able to make virtualdub vhsrest2.vdf plugin work (it makes it crash, nor through avisynth's LoadVirtualDubPlugin), and with DeJitter.dll I get far more jitter than in the original file.
The problem is I am not able to run this script. I've been upgraded to Avisynth 2.6 alpha, downloaded and included the required plugins with the following lines:
LoadPlugin("mt_masktools-26.dll")
LoadPlugin("GRunT.dll")
LoadPlugin("minmax.dll")
and loaded the video file, but I always get an "I don't know what 'thresh' means ([ScriptClip], line 4)" error.
I'm suspecting something is not getting loaded. Any hints??
Gavino
30th October 2011, 14:31
I always get an "I don't know what 'thresh' means ([ScriptClip], line 4)" error.
Perhaps you are running an older version of the script.
Check your script is the same as in the first post.
Before the call to ScriptClip, there should be this line:
thresh=int(findthresh(src))+3
jmac698
30th October 2011, 15:19
It's true, an "auto" thresh feature was added in the last .52 version.
sven_x
30th October 2011, 17:58
This is part 3 of posting 69 (http://forum.doom9.org/showpost.php?p=1533645&postcount=69) and 82 (http://forum.doom9.org/showpost.php?p=1534433&postcount=82).
Basing on Border Detection 0.3 by jmac698 (same thread, here (http://forum.doom9.org/showthread.php?p=1533363#post1533363)) I have modified the script that calculates cross correlation between the border of a real video and an edge model, whose parameters can be adjusted (edge blur, white level and number of black pixels of the edge).
You are welcome to do your own tests.
#AvsP script
#loadplugin("J:\plugins\Gscript.dll") # For y=0 ... loop
loadplugin("J:\plugins\GRunT.dll") # For use of Runtime functions inside user functions
loadplugin("J:\plugins\Corr2D.dll")
loadplugin("J:\plugins\VariableBlur.dll")
loadplugin("J:\plugins\NNEDI3.dll")
#Border Detection Sven_X Corr2D version basing on 0.3 by jmac698
# see http://forum.doom9.org/showthread.php?p=1533363#post1533363
# A function to show cross correlation between the left border of a video and an edge model that can be tweaked
# Requirements: corr2d http://avisynth.org/vcmohan/Corr2D/Corr2D.html
# infact we do neet a Corr1D only, which would be much faster, but we have none...
# GRunT (NNEDI3, Variablelbur in some cases for the edge model)
SetMemoryMax(800) #set this to 1/3 of the available memory
[<separator="Clamping">]
clamp2=[<"Input clamping", 16, 255, 43>]
global clamp=[<"cff out clamping", 16, 255, 27>]
mag2 = [<"Magnification", 1, 8, 1>]
[<separator="Edge model">]
bwidth=[<"Black px", 2, 16, 2>]
bblur=[<"Edge blur (0)", 0, 5, 1>]
bgrey=[<"Edge white (255)", 16, 255, 43>]
Directshowsource("J:\test\test.avi")
converttoyuy2
#nnedi3(-2) #deinterlace, double frame rate, to view a non interlaced version
#return last #view input video
border=32#32
crop(0,0,-last.width+border,0)
#crop(0,0,-0,last.height*1/4) #1/2 for test purposes only, faster,
levels(0,1,clamp2,0,clamp2) #clamp bright areas
edge=makeedge(last,bwidth).converttoYV12.averageblur(bblur).converttoYUY2.levels(0,1,255,0,bgrey)
#make an edge model with minium edge at x=2 px
#return edge
corrbyline3(last, edge) # input, reference
pointresize(last.width*mag2/2,last.height*mag2/2) #Magnify output
function SplitLines2(clip c, int n) {#duplicates then crops each copy in a different spot
Assert(c.height%n == 0, "Clip height not a multiple of 'n'")
Assert(!(c.IsYV12() && n%2==1), "'n' must be even for YV12 clips")
nStrips = c.height/n #= 576 lines PAL
c = c.ChangeFPS(nStrips*Framerate(c)).AssumeFPS(c) # Repeat each frame 'nStrips' times
BlankClip(c, height=n) # template for ScriptClip result
GScriptClip("c.Crop(0, (current_frame%nStrips-1)*n, 0, n)", args="c, nStrips, n") #(left, top,width,heigth)
}
function MergeLines(clip c, int n) {MergeLines2(c,n,n)}
function MergeLines2(clip c, int n,int i) {
i<2 ? c.SelectEvery(n) : stackvertical(MergeLines2(c,n,i-1),c.SelectEvery(n, i))
}
function makeedge(clip v, int x){
#Based on the properties of v, make an edge of x black + white pixels
x=x/2*2
v
blk=blankclip(last, width=x, color_yuv=$108080)
wht=blankclip(last, width=last.width-x, color_yuv=$EB8080)
stackhorizontal(blk, wht)
}
function corrbyline3(clip v1, clip v2){
#Correlate two videos, line by line, and return the horizontal cross correlation.
# when input 1 and 2 are edges (a square waveform, then the length of the resulting ccf correponds to offset between both edges
scale=2
v1
pointresize(v1.width, v1.height*scale) #scale line height, for yv12 a line has to have at least 2 px height
h=last.height #PAL 2x576 lines = 1152
splitlines2(scale) #make n frames with one line (height=scale) from a frame
v2l=v2.crop(0,0,0,scale)
interleave(last, v2l)
pointresize(last, 400, 100) #to enlarge output of CCF, at least 200x100
corr=corr2d (last)
Crop(corr,100, 24, -0, -72).pointresize(corr.width,corr.height*4) #400x400
Crop(0, 126, -200, 2) #to get the interesting part of CCF (x=0...) 2 px height
selectevery(2,1) #drop cff output of every odd line
mergelines(h/2) #compose a frame from h/2 frames that contain a single line
v2=pointresize(v1,v1.width*2,v1.height*2)
stackhorizontal(v2,last,last.levels(clamp-2,1,clamp,0,255)) #clamps grey levels of ccf output above clamp to white
}
function corrbyline2(clip v1, clip v2){
#Correlate one video line by line, and return the cross correlation between two succeeding lines
scale=2
v1
pointresize(last.width, last.height*scale) #scale line height, for yv12 a line has to have at least 2 px height
h=last.height #PAL 2x576 lines = 1152
splitlines2(scale) #make n frames with one line (height=scale) from a frame
pointresize(last, 400, 100) #to enlarge output of CCF, at least 200x100
corr=corr2d (last)
Crop(corr,50, 24, -0, -72).pointresize(corr.width,corr.height*4) #400x400
Crop(0, 126, -300, 2) #to get the interesting part of CCF (x=0...) 2 px height
mergelines(h/2) #compose a frame from h/2 frames that contain a single line
v2=pointresize(v1,v1.width*2,v1.height*2)
stackhorizontal(v2,last,last.levels(clamp-2,1,clamp,0,255)) #clamps grey levels in Cff output above clamp to white
}
Here are a few first results.
http://www.engon.de/temp/avisynth/tbc08.png
http://www.engon.de/temp/avisynth/tbc09.png
http://www.engon.de/temp/avisynth/tbc10.png
It can be seen that the width of the cross correlation reacts on the luma of the input video. Bright image parts produce a longer CCF output. The effect is smaller when input contrast is lowered (which is done in this version by clamping all luma values above a certain threshold).
By looking at these pictures I am not so convinced anymore that cross correlation can provide a good border detection.
jmac698
30th October 2011, 22:59
sven,
Great work and analysis. I have a simple suggestion, just change the 'white' color of the edge model into the averageluma of the video, this would eliminate the 'border bias' you are getting.
If I analyzed the actual correlation calculations, I'm sure I could understand why it's doing this anyhow.
Does your script work on two model edges correctly?
What about using a pure threshold, but then using salt&pepper removal to clear the noise?
It seems your borders are quite good, there just has to be some way to detect them. Did you try the edge masks built into masktools?
Perepandel
1st November 2011, 11:58
Perhaps you are running an older version of the script.
Check your script is the same as in the first post.
Before the call to ScriptClip, there should be this line:
thresh=int(findthresh(src))+3
Yeah, I'm using that version, dated 27th october... I also tried to put the "int(findthresh(src))+3" instead of the "thresh" variable inside the ScriptClip part to try to isolate the problem but no luck... I guess I'm using some old/different version of some of the plugins...
By the way, I just wanted to test how the alignment was working with one of my already-captured-without-sync captures...
jmac698
1st November 2011, 13:46
Oh, make sure you have gscript installed.
http://forum.doom9.org/showthread.php?t=147846
Can also set thresh manually, thresh=20 (for example) inside the scriptclip.
Gscript is sort of dangerous to have around for development, it silently affects variable scope I believe making me not realize I need it in a script. I mean something will just work for me when a plain avisynth would give an error.
Gavino
1st November 2011, 14:44
Oh, make sure you have gscript installed.
http://forum.doom9.org/showthread.php?t=147846
You mean GRunT (http://forum.doom9.org/showthread.php?t=139337), not GScript (although GScript is also needed later in your script).
Gscript is sort of dangerous to have around for development, it silently affects variable scope I believe making me not realize I need it in a script. I mean something will just work for me when a plain avisynth would give an error.
Again I assume you mean GRunT, as GScript does not affect variable scope.
The fact that "something will just work" is what it is intended to achieve, so is for the most part an advantage(!), but I understand what you mean in that it is harder to realise you need it for a given script. This is because once installed it extends the standard run-time environment and will still be used if you call 'ScriptClip' instead of 'GScriptClip' - it has to be this way as all run-time filters must work the same way for it to function properly.
Perepandel, are you saying your script is like this:
src=AVISource("...")
thresh=int(findthresh(src))+3
ScriptClip(src, """
#Mark video edges
converttoyv12
m=mt_binarize(thresh)
#Line up video
findpos_h(m, searchwidth=22)
alignbyluma(src,last)
""")
...
and you get "I don't know what thresh means" inside ScriptClip?
That should work whether or not you are using GRunT. The only exception would be if you have put this code inside a function (or if you have also configured GRunT to run each run-time script in a separate scope, by calling GRTConfig(local=true)), in which case you would have to add args="src, thresh" to the ScriptClip call (or simply make src and thresh global).
Perepandel
1st November 2011, 20:49
Oh, make sure you have gscript installed.
http://forum.doom9.org/showthread.php?t=147846
It was GScript! After I installed it, the script worked! Thanks a lot! I need to make further tests now ;)
jmac698
1st November 2011, 22:03
I've updated the instructions and requirements.
Also have my perfect TBC working today :)
Gavino
1st November 2011, 22:04
It was GScript! After I installed it, the script worked!
Certainly, GScript is required, but without it you should get the error 'there is no function named "GScript"', not a message about 'thresh'. Or is this actually what you did get?
(I need to understand the details just in case there is something strange happening with GRunT.)
jmac698
2nd November 2011, 00:15
Example of my ideal TBC performance
http://screenshotcomparison.com/comparison/91487
You'll see some aliasing, that's due to a simple resizer to fix the jitter. On VHS you'd never notice.
What this is, a test signal is added to the video, this allows my software to line-up the video and remove the jitter. This is only useful for new VHS recordings. It's use really is just to measure the actual jitter added, not to perfect obsolete VHS :)
I'm still working on measuring jitter from 2nd generation tapes, etc.
mammo1789
2nd November 2011, 23:33
Excellent result jmac can we download the whole script and what plugins are required to work
Thanks
jmac698
3rd November 2011, 00:04
What, my "perfect TBC"? Oh, that's useless to you, you have to make a new recording with an extra test signal on it. It's too late to perfect the obsolete VHS anymore :)
jmac698
3rd November 2011, 20:52
Some small updates. New sample:
http://screenshotcomparison.com/comparison/91853
I'm still working on measuring jitter from 2nd generation tapes, etc.
I'm being mis-understood here, I know most of you can't follow these technical discussions. I should explain that I'm jumping around with a lot of different ideas, most of which have nothing to do with my script or fixing your tapes - but consider them basic research and exploring the issues.
My script *does* work on 2nd generation tapes, it only lines up black borders, the tape has nothing to do with it.
What I was referring to was basic research into technical measurements on 2nd generation tapes. I want to make my own 2nd generation copy and do some measurements on it, that's all.
cherbette
12th November 2011, 05:51
I wanted to thank you once again for all of your efforts with this software TBC. So far the results have been pretty stellar so I can't wait to see the final product. Keep up the good work, my friend.
jmac698
12th November 2011, 06:30
Welcome to the board. Bit of a different community I'm told, but hope you like it.
And your welcome *blush*
I'm working on some plugin stuff now.
cherbette
12th November 2011, 20:34
Awesome! I'm excited for the plugin. If it works on the darker parts of the video as well then you will have officially done what many I have read said couldn't be done...a software TBC.
jmac698
13th November 2011, 03:19
@sven:
Regarding your line length theory, I did some calculations, but haven't finished analyzing it yet. If you correlate two steps, first of all the mean is length/n, where length is the length of the step. I don't have the rest derived yet.
Sliding the steps against each other forms a pattern, if size=8 over 16 pixels, we have:
x=y=(1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0)
r=1
if size=7 or 9 we have
x=(1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0), y=(1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0) or (1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0)
r=.88
Continuing we have
size
8 1
7/9 .88
6/10 .77
5/11 .67
4/12 .58
3/13 .48
2/14 .38
1/15 .26
0/16 (1/0)
So we have the maximum when edges are aligned and it gets less accurate the further the edges are from each other, also you can't tell sign, that is if the edge is to the left or the right. A simple inspection could tell you probably, like the average on either side.
I still don't see why the lines get bigger, it should be symmetrical. Could be the plugin is stopping at the left edge, but in the video the right edge continues.
jmac698
13th November 2011, 03:37
Update: sum(x-xavg)^2=n(1-avg)^2-(n/2-size)/(n/2)
xavg=size/n
This is relevent cause r=sum(x-xavg)(y-yavg)/(sqr(sum(x-xavg)^2)*sqr(sum(y-yavg)^2))
So I have the bottom half of the expression.
jmac698
13th November 2011, 03:46
I almost have the answer, but a quick guess is it's something like a/n/(sqr(b/n)*sqr(c/n)) where b+c=n or something like that, anyhow this means I can do a single correlation and compute the exact distance by working backwards, then just gotta find it's sign.
So what we've just discovered is a simple formula to find the edge which is robust to noise, brightness and contrast. It gets very sensitive to noise the farther apart the edges are. I could do two correlations to get the maximum accuracy.
cherbette
16th January 2012, 15:35
Hey Jmac just wanted to see if you had made any progress with this great idea? :)
jmac698
16th January 2012, 23:40
Hey Cher,
thanks for asking, it reminds me and also encourages me. In fact I haven't done any video work since xmas, once the festivities took me away from my hobby it wasn't easy getting back. So you never know, I would like to finish these ideas...
zerowalker
10th June 2012, 19:12
I can get the Dscaler driver to work With Dscaler, but don´t know how to set it up in Virtualdub;S
Conextant driver, w7 x64.
Anyone knows:)?
NoX1911
23rd June 2012, 00:02
Looking at your screenshot (http://screenshotcomparison.com/comparison/88810) it shows a visible area (52us) of roughly 670x576 pixel with a loss of horizontal information by 704->670 (~34 pixels per line). So technically the scan rate/pixel clock hasn't change noticeably if i see that correctly.
Do you have any informations about that changed parameters (clock rate, hsync, prescale...)? I'm currently looking into patching philips drivers and it would be much easier if i would have some values i can work with.
jmac698
23rd June 2012, 20:06
Modern cards sample SD ntsc & pal at a multiple of 13.5MHz. Modern cards use oversampling of 27MHz or more. As a result, there's actually over 1000 samples per line.
There is a standard for VCRs for jitter to be +-3uS (I believe). In my sample, I used a tweaked card with standard sampling rate, but the registers adjusted to begin active video line earlier, that is less offset from front porch. If you tell me the datasheet for your device, I can suggest which register and value to put there, although if you can make all the timing registers variable then it's easier to experiment.
Hopefully I'll get back to this this year. I'm collecting new equipment to examine it in more detail. I believe I bought a TBC the other day, haven't tested it yet.
lordsmurf
4th July 2012, 08:25
I'm wondering if this could be modified to solve whole-frame vertical image jitter. (Not horizontal signal jitter, as this virtual TBC is attempting to resolve.)
What I'd like to see is an if/then/else type dejitter script for videos with jumps in the frame that are only ONE frame long (compared against several before/after framse). Not even frequent bouncing or vibration necessarily, but jumping frames maybe once per second (at most). If whole-frame movement exists, then act. If not, ignore.
Vibration and serious jitter can come later. It's actually less common to be that ruined.
Trying to apply deshake filters is crap because it wants to "correct" vertical pans and movements of large objects. It could be tracked against the head switching noise at the bottom of the frame. In fact, I have a matted widescreen VHS movie, where it jumps, so there's two huge black borders on top/bottom to track against.
I've tried to find a good vertical de-jitter method for probably 10 years now. All create more problems than they solve.
I'd still like to see this virtual TBC fix my fubar Robotech test captures. ;)
.
jmac698
8th July 2012, 07:11
Yep, vertical jitter shouldn't be hard to write in script. I'm trying to put my TBC into a plugin, but again, writing plugins is very hard. So I have a new idea for the script to make it a lot faster.
jmac698
11th July 2012, 12:51
New Avisynth script
TBC 0.6 by jmac698
Jul 11, 2012
A script to resize each line of the source by a different amount, by searching for black borders. YUV only. Avisynth 2.58+
Usage: see tbc.avs demo
Requirements: dejitter 0.2, findpos 0.1 (included)
0.6: first plugin version
http://www.sendspace.com/file/01u1wc
The quality is no different, but it's a lot faster. Example included.
Mounir
11th July 2012, 13:22
it return an error : unable to Load dejitter.dll , error=0x36b1, any idea ?
jmac698
11th July 2012, 13:27
Please extract the included plugins and place into your plugins directory.
jitter02.zip -> jitter.dll -> plugins\jitter.dll
findpos01.zip -> findpos.dll -> plugins\findpos.dll
Fun with jitter analysis
Using the download package, try this script instead:
srcdir="C:\Documents and Settings\me\My Documents\Downloads\filters\tbc\"
src=ImageSource(srcdir+"jittered.jpg").converttoyuy2
thresh=85
#Mark video edges
findpos_h(src, x1=60, x2=0, thresh=thresh)#search for the first bright pixel, starting 48 from left and 0 from right
#test note: (65,0)=92
converttoyv12.histogram(mode="levels")#show distribution of jitter
#histogram(mode="classic")#recreate the jitter with a solid white line
return last
function findpos_h(clip src, int "searchwidth", int "x1", int "x2", int "thresh"){
searchwidth=default(searchwidth,32)
x1=default(x1,0)
x2=default(x2,0)
l=findpos(src, x1=x1, x2=x1+searchwidth, thresh=thresh)#.crop(0,0,-src.width+2,0)
r=findpos(src.fliphorizontal, x1=x2, x2=x2+searchwidth, thresh=thresh).crop(0,0,-src.width+2,0)
stackhorizontal(l,r).pointresize(2,src.height)
l
greyscale
}
It shows the distribution of jitter amounts; it looks like a normal distribution! That's good actually, it can help me learn how to limit change per line etc.
In the classic mode, you can see more clearly the jitter in this video, but with an artificial white line.
StainlessS
11th July 2012, 15:33
Jmac, hope you remembered to point out that users need VC++ runtimes for 2008 (think thats your version).
jmac698
11th July 2012, 21:29
Oh no! The classic "works for the developer only" has bit me! Is there any way to remove this requirement?
StainlessS
11th July 2012, 22:20
Is there any way to remove this requirement?
Yes of course there is, use a different compiler. :devil:
Just be glad you did not choose 2010 Express.
(A lot, but not all will already have 2008 runtimes).
Robert Martens
12th July 2012, 00:20
Yes of course there is, use a different compiler. :devil:
Or just link the dependency statically, unless there's some danger in that? I know of problems throwing exceptions from DLLs, but as far as I can tell, env->ThrowError is just passing a string back to avisynth, which itself throws the exception; any design issues with regard to DLLs and exceptions are a matter of core development, and as plugin authors we don't have to worry about that, as long as we stick to ThrowError and don't use the throw keyword ourselves.
As for CRT version mismatches, the official, single threaded builds of Avisynth are dynamically linked with the VC6 toolset, while SEt's MT builds are statically linked with VC10 (I'm getting this from examining the different DLLs themselves, and from certain forum posts (http://forum.doom9.org/showthread.php?p=1567883), someone please correct me if I've misinterpreted something -- EDIT: Looks like I was wrong (http://forum.doom9.org/showthread.php?p=1582947#post1582947), though I don't think it changes my point), so I would think plugins that use anything else would be an absolute nightmare for everyone, yet people seem to be successfully using plugins that were linked with the 2003, 2005, or 2008 runtimes, some using static linking, some dynamic. Is there some hidden issue with this that's going to swim up and bite us all on the ass one day? Or, for that matter, something not so hidden, that I'm just overlooking?
Avery Lee touched on these dependencies in a blog post some time ago: http://www.virtualdub.org/blog/pivot/entry.php?id=296 It goes into rather intimidating detail about this stuff, though I don't know exactly how much of it changes when building plugin DLLs as opposed to executable applications.
If you do decide to stick with the CRT DLLs, jmac, you'd do well to offer up the Release builds of your plugins to users. You should be doing that anyway, but when it comes to the Runtime it's especially important since, as you can see in the second paragraph of this MSDN article (http://msdn.microsoft.com/en-us/library/8kche8ah(v=vs.90)), linked from that blog post, the Debug CRT DLLs are not redistributable.
jmac698
12th July 2012, 01:00
I've updated everything to use release builds.
Does even a void main() type program require CRT? I thought the error on dejitter might have come from me using math.h. I can work around that and not include it. Then it's possible I wouldn't need any runtime.
Robert Martens
12th July 2012, 01:16
I was embarrassed to not know the answer right off the top of my head, but a quick test in both VS2010 and 2008 shows me that even just
int main()
{
return 0;
}
ends up depending on the appropriate version of the CRT, if the project is set that way (which it is, by default). If you don't redistribute the DLLs, point people to the appropriate redistributable, or use static linking for your plugins, I don't think you can avoid the dependency, even if you ignore math.h.
Mounir
12th July 2012, 04:39
So what do i need to make this script work? I'm loading both dlls and still get this error
jmac698
12th July 2012, 09:22
I tested the latest package on a separate machine, it works fine. You have the latest package and vc runtime installed?
Did you change any parameters? Are you using avisynth 2.6?
I've only tested on avisynth 2.58. Don't use any of the new colorspaces.
Mounir
12th July 2012, 11:25
I have avisynth 2.6.0.2 and have installed Vc++ 2008(x86). I'm on win764 though not sure if that's what i need, you tell me
I load the findpos.dll as follow:
LoadPlugin("C:\Program Files (x86)\AviSynth 2.6\plugins\tbc\findpos.dll")
the error remain the same: error 0x36b1 unable to load dll
jmac698
12th July 2012, 11:37
Thanks for the report, I'll see if I can test with those conditions.
StainlessS
12th July 2012, 18:10
error 0x36b1 unable to load dll
Can you verify that the above error is an Avisynth Error in little red window?
Cant say I've ever seen an Avisynth error like that, with hex error code.
Are you using a debug version that was mistakenly uploaded, if so try new download.
StainlessS
12th July 2012, 18:33
Jmac, hate to have to tell you this, but on trying a test install (I dont really have need of the
filter), I found I was going to overwrite an already installed DeJitter.dll, on inspection, it's a
V.C Mohan filter from 2006.
http://avisynth.org/vcmohan/DeJitter/DeJitter.htm
Oh, the humanity. :eek:
EDITED: You MUST do a name change, check in future.
jmac698
13th July 2012, 04:36
I did, I made another one called jcorr to avoid collision. Anyhow, did it work for you?
And I've seen errors like that before, I think it was when fftw was missing. I don't see why that should be the case though.
jmac698
13th July 2012, 05:31
No need to worry about conflicts, http://avisynth.org/mediawiki/Plugins
just rename to jdejitter.dll, and call with jdejitter_dejitter().
Static build for Mounir, please report:
jdejitter03_static
http://www.sendspace.com/file/qk1ifv
findpos02_static
http://www.sendspace.com/file/e3jblt
btw, I think I'll be using some of your filters soon :)
Note: those links won't be updated, see the head of the thread instead.
Mounir
13th July 2012, 08:05
It seems you have fixed the problem Jmac, thank you.
Can someone send a video with lot of jitter i don't have any at the moment
jmac698
13th July 2012, 08:29
Great! The download is slightly bigger, but otherwise I don't know if there's any real disadvantage to compiling the plugin this way.
As for a video sample, it's easy to make your own, as I've made a jitter simulator (which will be going into a plugin as well soon).
http://forum.doom9.org/showthread.php?p=1470667#post1470667
You need also gscript and grunt, but you can skip noisegenerator if you comment out the respective line. You can use any of your videos as source.
StainlessS
13th July 2012, 16:24
Jmac, shall give em a try now re-named/updated.
Might I suggest that you consider setting up a single place where plugs can be obtained,
either 1 per plug or for the whole lot, or you are going to get into a helluva pickle,
eg 'Jmac's plugin Emporium'.
Decide where you're gonna put it and point links to there so you need not update in multiple
places.
You can also get an account (free) on eg MediaFire called pretty much whatever you want
and store them all there (better than sendspace, I think). Take a peek a StainlessS on Mediafire
in sig, the user can browse.
zerowalker
13th July 2012, 17:28
Has someone been able to get the Hsync with a conextant2388x card?
jmac698
13th July 2012, 18:12
Please see How to Capture with HSYNC
http://forum.doom9.org/showthread.php?t=162832
in the capture forum.
Robert Martens
16th July 2012, 00:16
Quick note for anyone who read my earlier posts in this thread, it turns out I got some details wrong: http://forum.doom9.org/showthread.php?p=1582947#post1582947
I don't think my ultimate point changes, but I've updated the post (http://forum.doom9.org/showthread.php?p=1582294#post1582294) accordingly, and just wanted to make everyone aware.
jmac698
18th July 2012, 01:39
Ok, a big limitation with the technique so far is that it requires clear black borders on each side. So the obvious approach is to not always rely on them. I've done a test using *internal* lines of the video, which happen to be there in some scenes. Now we can theoretically use the following information:
-any parts of a black border, even one side, and only those parts that are clear
-any internal lines, possibly at any high angle
-extend the fixed area with a motion tracking technique
This can extend the number of scenes which can be fixed, but doesn't totally solve the problem. I've placed a sample here:
http://www.digitalfaq.com/forum/21764-post18.html
A big application for software TBC is 2nd generation tapes; there's no other way to fix these.
Also, a simple technique to play with in your tests is to use an edge detector. Just adding one line might improve your results:
src=avisource
mt_edge("0 0 0 -1 0 1 0 0 0")#1 dimensional Roberts edge detector, requires Masktools v2
findpos_h(src, x1=0, x2=0, thresh=254)
rescale(src,last)
You can also get the edge detector to show your left/right black borders, so you can tell if they're good enough. I'm also experimenting to see if just one edge has enough information to resize; perhaps it's a proportion of the resizing, however so far if I fix a video on one edge, I can see that side of the screen looking good and the other side is messed up. I've also noticed that VCR comets can appear in the borders and mess up the detection, so you need some decomet operation first.
dokworm
18th October 2012, 00:57
These are amazing results, it might make it worth recapturing some of my old tapes now!
I also want to do a median pass where I have multiple copies of a particular movie. What is the best thing to use for that?
Oh, and will this help with laserdisc captures at all, or does the TBC built into the LD player take care of it all?
Asmodian
18th October 2012, 19:28
There are some good median functions for Avisynth.
I have used them to do 3, 5, and one 9 capture median's. Three captures helped a lot, five helps denoise some more (in my tests), and nine looked exactly like five. :p
It was a pain syncing all the captures because the TBC in my VCR would drop/dupe frames in different places during the captures. I had to trim out or dupe 3-4 frames in every capture to keep them in sync at all times. I used interleave and overlaid subtitles to indicate each capture during this process; please let me know if you or anyone else finds a better way!
Both of these taken from posts by jmac698, thanks for all the help!
Function Median1(clip input_1, clip input_2, clip input_3, string "chroma")
{# median of 3 clips from Helpers.avs by G-force
chroma = Default(chroma,"process") #default is "process". Alternates: "copy first" or "copy second"
Interleave(input_1,input_2,input_3)
chroma == "process" ? Clense(reduceflicker=false) : Clense(reduceflicker=false,grey=true)
SelectEvery(3,1)
chroma == "copy first" ? last.MergeChroma(input_1) : chroma == "copy second" ? last.MergeChroma(input_2) : last
Return(last)
}
Function Median2(clip "input_1", clip "input_2", clip "input_3", clip "input_4", clip "input_5", string "chroma")
{# median of 5 clips from Helpers.avs by G-force
chroma = default(chroma,"process") #default is "process". Alternates: "copy first" or "copy second"
#MEDIAN(i1,i3,i5)
Interleave(input_1,input_3,input_5)
chroma == "process" ? Clense() : Clense(grey=true)
m1 = selectevery(3,1)
#MAX(MIN(i1,i3,i5),i2)
m2 = input_1.MT_Logic(input_3,"min",chroma=chroma).MT_Logic(input_5,"min",chroma=chroma).MT_Logic(input_2,"max",chroma=chroma)
#MIN(MAX(i1,i3,i5),i4)
m3 = input_1.MT_Logic(input_3,"max",chroma=chroma).MT_Logic(input_5,"max",chroma=chroma).MT_Logic(input_4,"min",chroma=chroma)
Interleave(m1,m2,m3)
chroma == "process" ? Clense() : Clense(grey=true)
selectevery(3,1)
chroma == "copy first" ? last.MergeChroma(input_1) : chroma == "copy second" ? last.MergeChroma(input_2) : last
Return(last)
}
dokworm
19th October 2012, 00:10
Thanks for those, I will give it a go.
I'm thinking that a median pass first will get rid of dropouts and glitches, and then an average of the now dropout free captures would clean up the noise.
ChibiBoi
14th November 2012, 10:11
So I basically tried copying and pasting the script as is, and it didn't really work for me :-/ I don't have the HSYNC area for my videos, but I do have black bars on the sides.
Before using script:
http://i50.tinypic.com/2hyw8dk.png
After using script:
http://i50.tinypic.com/2uz58k1.png
Somehow, I don't think I'm doing this correctly :-/
vcmohan
15th November 2012, 14:04
you may try my plugin DeJitter.The following script gave better results. The very dark top and the very bottom strip can not be handled.
src=imagereader("D:\2hyw8dk.png",0,1,25,false)
DeJitter(src,jmax=50,th=40, wsyn=0, extend=false)
ChibiBoi
15th November 2012, 21:33
you may try my plugin DeJitter.The following script gave better results. The very dark top and the very bottom strip can not be handled.
No doubt the script does work better, for the most part. Here are some successful screenshots:
Before:
http://i3.photobucket.com/albums/y68/ChibiBoi/dejitterbefore2.png
http://i3.photobucket.com/albums/y68/ChibiBoi/dejitterbefore3.png
After:
http://i3.photobucket.com/albums/y68/ChibiBoi/dejitterafter2.png
http://i3.photobucket.com/albums/y68/ChibiBoi/dejitterafter3.png
However, there are some scenes that get really glitchy. Sometimes, there's not too much of a jitter in the scene, and then the filter shifts the video to produce a huge glitch. Sometimes, there's a lot of jitter in the scene before, but the filter doesn't fix the jitter, it adds more glitches to the video. What settings do I need to fix this? Will capturing the video with the HSYNC lines help?
Also, is there a way to make the blank area black instead of gray?
Before:
http://i3.photobucket.com/albums/y68/ChibiBoi/dejitterbefore.png
http://i3.photobucket.com/albums/y68/ChibiBoi/dejitterbefore4.png
http://i3.photobucket.com/albums/y68/ChibiBoi/dejitterbefore5.png
After:
http://i3.photobucket.com/albums/y68/ChibiBoi/dejitterafter.png
http://i3.photobucket.com/albums/y68/ChibiBoi/dejitterafter4.png
http://i3.photobucket.com/albums/y68/ChibiBoi/dejitterafter5.png
EDIT: I realize now that the glitches are caused whenever there is black footage on the left side of the video (ie. black hair, or shadow) because the filter thinks it's the black overscan bar, it tries to overcompensate by shifting the video out of the frame. I'm not quite sure how to fix this...
vcmohan
16th November 2012, 13:24
I have slightly modified DeJitter plugin and is available for down loading. uselast is a new option. Also it uses all colors of RGB. Try by playing with the parameters. Black at the left gives some problem always.
DeJitter(src5,jmax=30,th=20, wsyn=0, extend=false, uselast = true)
vcmohan
17th November 2012, 08:14
Sorry I inadvertently introduced a bug. Now it is corrected. As the black part is at the top you may flip vertically prior to dejitter and flipvertical again to correct. Also try convertto YV12. As part of black is the hair of the girl you can not avoid glitches.
sr = sr.flipvertical().converttoYV12()
dj= Dejitter(sr,jmax=30,th=40, wsyn=0, extend=false, uselast = true)
dj = dj.flipvertical()
ChibiBoi
28th November 2012, 23:18
Sorry I inadvertently introduced a bug. Now it is corrected. As the black part is at the top you may flip vertically prior to dejitter and flipvertical again to correct. Also try convertto YV12. As part of black is the hair of the girl you can not avoid glitches.
Too bad about the hair, there's a lot of scenes that involve black at the left edge so the glitches happen quite frequently :-/ your filter works great otherwise though!
and as for the OP's filter, i can't seem to get the filter to work, and i can't seem to capture with HSYNC because of my cap card :-/
jmac698
3rd December 2012, 04:01
My filter should work as well but no better, though I have ideas to improve it. Not set up right now, I'll have to get back to you.
ChibiBoi
4th December 2012, 06:07
My filter should work as well but no better, though I have ideas to improve it. Not set up right now, I'll have to get back to you.
Alright, thanks for your hard work though! :D
I actually went out and bought a SVHS player with built-in TBC so hopefully it'll fix everything, because the jitters were really pissing me off LOL But I can't wait to see what you come up with :cool:
Mounir
14th August 2013, 09:17
So the project has been cancelled or what ? it's been a while since we've heard of Jmac, i'm still looking forward to his drivers tweak on linux
StainlessS
14th August 2013, 15:42
I've had it confirmed that Jmac698 is in fact as dead as a doornail, sad but true.
EDIT: Jmac was on-line when I posted this, he disappeared without responding, must be in mourning.
(Nah, he was probably looking up his below response)
Jmac698, Aug 1969 - Aug 2013, sorely missed. R.I.P. :)
jmac698
14th August 2013, 16:14
Rumours of my death are greatly exaggerated :P
anathema
4th October 2013, 23:09
I'm trying to use the Fast Line Shifter script to fix up a chunk of video which has some fairly major line-displacements. The problem I've hit is that the method used to detect where the blanking ends and the active picture begins is getting tripped up by the rather variable black levels in the picture. If I set 'thresh' to 34 then the displaced lines are pulled back into position, but unfortunately a dark area of the picture ends up being distorted. If I set 'thresh' low enough to prevent the distortion, the displaced lines don't move far enough.
This is the captured image (luma-channel only - for some reason the chroma didn't suffer from the timebase problem):
http://www.nightshade.org.uk/raw.jpg
With 'thresh=34' the displaced lines are detected in their entireity but you can see a large chunk of black in the lower-left of the frame, which corresponds to a dark area of the original picture:
http://www.nightshade.org.uk/thresh34.jpg
Setting 'thresh=24' gets rid of the error, but at the expense of missing bits of the displacement problem:
http://www.nightshade.org.uk/thresh24.jpg
Looking at the luma values in the blanking area it's pretty clear that, while the black areas are noisy, there is a marked jump where the active picture starts. I was thinking that a better algorithm might be to look for the rate-of-change in the luma, rather than for an absolute difference between two adjacent pixels. Before I set about writing one, does anyone know: a) does such a detector already exist; or b) can one be written using existing filters?
Ludvig Friberg
6th October 2013, 13:23
Very interesting thread, keep up the good work!
jmac: I tried sending a PM but your quota was full.
StainlessS
7th October 2013, 02:05
Jmac's quota is always full.
Not because popular, cos does not clear old ones. Long standing problem. (EDIT: been full for a year or so).
Ludvig Friberg
8th October 2013, 18:08
Ahhh. I see. I want to get hold of him to ask about a different project. I am looking for a way to construct and filter video from raw sampled waveforms. Doing chroma separation, TBC and everything else in software.
lansing
3rd January 2014, 00:19
hi I wanted to use this filter for my vhs record, I copied the script in the first post, and it's giving me an error about no function name minmax(), where does this function come from?
edit: nvm, I just found it on the top of the first post.
edit2: Now I'm getting "MinMax: this filter can only be used within run-time filters, [GScript], line x..." message. My script is a just copy from the sample script in the first post.
lansing
3rd January 2014, 03:13
I also tried vcmohan dejitter, it runs more easily. But there's a lot of false positive detections.
I played around with it for an hour, I think it's missing something like a jitter minimum parameter for better control. And the threshold parameter doesn't seem to do what it suppose to do. For example, in one jitter scene anything above threshold 30 will detect it, on another scene I need to raise it to 80. But on the 3rd scene, the 80 threshold will not detect the jitter, I have to lower it back to 30 for it to work, which doesn't make sense to me.
rs008f
24th July 2017, 18:37
The download links no longer works. Where can I download the files?
Is this script a good candidate to fix this horizontally moving back and forth video.
https://mega.nz/#!T19inJjK!nRCI9349rOQHJ1USeewaUD3y7oJ9klz5s87fDxZDuTE
lordsmurf
24th July 2017, 20:17
The download links no longer works. Where can I download the files?
jmac had the bad habit of using freebie "file sharing" sites, which are infamous for purging/losing files. I kept telling him not to do that. He and I discussed his software TBC work quite a bit at digitalFAQ.com. And at that site, we have a habit of attaching files to forum posts, to prevent exactly this sort of loss. So a lot of his files can still be found there, though sadly not all.
His tbc06 zip is here: http://www.digitalfaq.com/forum/video-restore/2742-power-motion-tracking-2.html#post21874
As are the Robotech samples we were working with.
But, as you can see, the TBC never really worked. It was marginal improvements at best. So don't get your hopes up.
I think he was doing all this in MatLab. It had potential, but he lost interest too quickly. A software TBC is sorely needed, to correct all the botched nth-generation VHS copies that exist, and lousy DVD recordings from amateur equipment.
johnmeyer
24th July 2017, 23:09
I agree. It was a cool idea that seemed like it could have worked.
I wonder if some of StainlessS' RT_Stats code could be used? I've been able to use it for all sorts of line-to-line comparisons. The shifting part of the code (shifting an entire line left/right) should be straightforward, as long as you have a clear left edge (which is what he was using). The bigger issue is that time base errors are often not consistent across the entire frame, left to right. However, even a first-order correction would be welcome.
lordsmurf
24th July 2017, 23:25
What's missing is temporal intelligence.
When you take a video with horizontal jitter, and hit it with a harsh temporal NR, you'll notice that the wiggling largely dissipates. What you're left with, of course, is nasty temporal artifacts (aka "mouse trails", ghosting). But motion-stable artifacts!
The solution for a software TBC could be to run temporal NR, use the results as a baseline for where the lines should be, and then shift accordingly.
More chaotic timing noise would need some degree of both the line shifting, and temporal NR. Perhaps even something semi-intelligent, maybe based on Donald Graft's dynamic NR, to reduce ghosting artifacts.
I think this filter was going down the wrong path.
(1) You never have the sync data. Proof-of-concept was somewhat stupid, as it required a VHS tape be captured with special extra data. That's just not real-world whatsoever. I think that was a sidetrack tangent to where it could have gone.
(2) Timing errors are almost never static, but continuously move. It's analog. Analog is controlled/restrained chaos. The quicker you realize this, the better you are long-term.
I wish somebody would take up where jmac698 stopped. He was on to something.
StainlessS
25th July 2017, 01:07
You need to be logged into your DigitalFAQ.com account to download above linked files.
jmac698
28th July 2017, 12:07
@johnmeyer
"The bigger issue is that time base errors are often not consistent across the entire frame, left to right."
Correct. That's why it started as fast line shifter then turned into software tbc. Both the left and right edges have jitter, which means there's some horizontal resizing per line. The filter does work dynamically per line.
@lordsmurf
"You never have the sync data" Semi-true; for a professional, it is assumed they are going to buy the equipment they need to get the sync data. Not only that, there were a huge number of cards based on that chipset which could be made to work, most famously the WinTV series.
Second, I've successfully applied it even without sync data; if the capture window is large to enough to leave black borders on both sides (video supplied by cherbette).
You misunderstand that I never came into this thinking it would be a polished, end-user solution; it was experimental, meaning I was trying to understand the nature of the problem to start. And I did learn something; I learned specifically that left-aligning the sync is not the solution; I have to fit between two edges or two syncs. I just put it out there expecting people to play with the functions, but no one ever did :(
This is how engineering is done, start by a) capturing data on the problem b) creating a model for the problem c) solving the model d) apply the solution to the real-world e) refine and iterate.
I also showed to a lot of people that it can be done in software and helped dispel a lot of the misinformation that you need some kind of hardware box. The internet was adamant that it was impossible without sync, that's not true though.
Anyhow, there is another way. There's a paper which works by using mathematical properties (an L1 norm*) which have a good chance of saying if lines are aligned. Someone else tried to implement it too, but we were both stuck over one sentence which wasn't explained. I was in contact with the author but she was very sick at the time. I can still finish this someday.
Another experiment someone did is detecting alignment with a Fourier Transform, but that didn't work out.
*an L1 norm is based on Least Absolute Deviations
"What's missing is temporal intelligence" what you're getting is a frame blending with random horizontal offset. What you call stable jitter motion is due to this, mathematically speaking:
the jitter is guassian random, and as you average frames, the jitter is reduced by the square root of the number of frames, making it look more stable. However, the ghosting is increasing in the same proportion. Features which don't move will literally be guassian blurred. Features that move will also have motion blur. I don't think using this as a reference would really work.
Using my process of engineering I could attempt to prove this; I would simulate jitter, apply temporal denoising, then try to fix it. That's going through the steps of modelling and simulation.
I still can't explain why there is jitter to begin with; the capture card clearly adjusts sample rate based on the sync timing, it is in fact a built-in TBC.
lordsmurf
28th July 2017, 12:26
I have at least one sample clip that could be right-aligned, as the black image border is visible. If you can align that, the video should be steady.
I'm still not sure you understand what I mean by "temporal intelligence". You'd not average anything, aside from analysis. If you sample the same line across (X) [3-7] frames, and at least (Y) [25-75] % is still the same (noting "the same" occurs AFTER the pixel shift), then decide if the line shall be moved in the direction of the majority position. Temporal analysis, not temporal application.
This would process slow as hell, but in the modern computing era (Skylake+), it can probably be done. I'd imagine its probably not worse than Mercalli on an old CPU.
I can think it up, but programmers (even mathematicians) more gift than I must create it. That part is beyond me (although I could probably learn, given many years).
jmac698
28th July 2017, 12:41
As to the person who couldn't get it to work 3 years ago; you were using it wrong. I believe I can script around the problems with the varying thresh levels.
I agree with your conclusions that the border detection wasn't good enough. I was expecting that other scripters around here would easily be able to address that problem. If anyone can post a sample I could work on that problem and it will still work.
The basics of this are sound and proven; all you need is some black border, and a better border detection. No need for sync.
jmac698
28th July 2017, 12:45
Post your sample.
Yes I understand you meant it as a detect clip. Pans would obviously make it fail, because is it a pan or jitter? What about diagonal lines? I think it could work in some scenes but not others. It's worth an experiment though; I believe to always keep an open mind and each failure only helps you learn more :)
jmac698
28th July 2017, 12:54
ohh... I have to post this before I forget. I had an idea for static scenes (stationary scenery with just characters moving, end credits, titles etc.). All you have to do is align the frame relative to the last one; more than that, to the average offset of all the static frames, this then has to average close to the true position.
**** +4
**** +1
**** +2
2nd line, being the left-most aligned, is taken as a reference line. From that, we get +1 and +3 offsets, average that to +2 offset and move that line (in all frames) to that position. It is now much closer to the true offset, and it's guaranteed to converge to the true offset by the properties of statistics. I'd say a 1 second scene is easily enough to get nearly perfect sync (30 frames can account for 5 pixels of offset). Each line will independently line up with those above/below like magic :)
*1 proof:
https://math.stackexchange.com/questions/715629/proving-a-sample-mean-converges-in-probability-to-the-true-mean
*2 assuming jitter is i.i.d.
lordsmurf
28th July 2017, 12:58
Pans would obviously make it fail
Not necessarily.
It would make it again hit the CPU and/or RAM, but this one is (probably?) easy. Analyze by frame temporally, then by the line. If the entire frame shows signs of panning, then said motion should be accounted for. Yet another %/slider/option should be present, to provide threshold for "what is a pan". Because, as you say, all clips are different.
To not waste CPU cycles, put the entire frame range into RAM.
I have 16gb RAM, i7-6700k CPU. I routinely get annoyed by Avisynth filters not taking advantage of the resources, usually using less than 25% (even with MT options).
jmac698
28th July 2017, 13:10
@lordsmurf
Great idea! Though I thought of something like that after I posted.
lordsmurf
28th July 2017, 14:53
I'm going to fully recapture the footage I want you to test a correction with. I pulled the tape tonight, and will get back to you sometime soon. I'll see what my capturing options are.
jmac698
28th July 2017, 15:06
My suggestion is, try to get black on both sides, and turn up the brightness (with procamp) so we can distinguish the blacker than black from the dark. In cases I've seen, there was a clear difference.
Ohh.. now I had another idea. By the same process, if you capture a scene many times (too many to be practical, tbh), it should naturally average out as well.
How would you feel about capturing a few seconds 9 times? haha :)
jmac698
28th July 2017, 15:11
I think what I need now is an edge filter followed by RANSAC (this is an algorithm to get rid of outliers; what should remain is true corresponding edges) and can use that for alignment.
The other way is correlation/fourier, that tries to find the best "sum" that matches position and scale.
lordsmurf
28th July 2017, 20:45
RANSAC (this is an algorithm to get rid of outliers;
Ah, that gets into statistics, which I do have some skill at. :)
You need to be careful about the stats method across all frames. You don't want 100% averaging, as outliers can drag the norm in its direction. What you want is a truncated mean of some sort, and I find a winsorized mean to often be more accurate.
The other way is correlation/fourier, that tries to find the best "sum" that matches position and scale.
I'm not sure a fourier transform is the way to go. For one thing, even with a still image, it just kills a CPU. Does the sum exclude outliers?
Ohh.. now I had another idea. By the same process, if you capture a scene many times (too many to be practical, tbh), it should naturally average out as well.
I think that would be more academic than anything else.
Generally damaged footage will already be digital, with access to the tape unavailable. Aside from really bad nth gen errors embedded/compounded on the tape, a quality S-VHS VCR with line TBC (or Es10 on passthrough) would easily correct the timing problem, thus negating the filter's need.
I mean, yeah, sure, I can do it. But I don't think it will help much. In my experience, multi capturing doesn't result in a big difference, if at all. Again, embedded errors, meaning the positioning would remain mostly or entirely unchanged.
jmac698
28th July 2017, 22:36
If your sample has embedded errors, don't bother then.
I've been looking at robust statistics lately, and ran across windsoring, but it has a bias. That would still leave the line with an outlier of jitter sticking out (just not as much).
Robust methods are rated by their breakdown point, which means what % of the data has to be corrupted in order to give you a bad estimate. A method called least median of squares has 50% breakdown, which is really good.
Imagine a graph where the x positions are non-linear, based on the cummulative sum of a guassian. It will be .5 halfway, increase quickly then taper off..
* * **** * *
The Y positions, you plot your data, but sorted and cummulative. If the data comes from a guassian, this should now form a straight diagonal line. It's similar to the concept of log/log graph paper. This straight line has an intercept (where the line touches the x-axis, ideally 0), which is the mean, and a slope (normally 1) which is the standard deviation. At this point you would call it the linear regression method. If the mean of the data is higher, the line should shift to the right. If the sample is 'fatter' (more variation), the area under the curve is always higher, so the line slope should increase more, and slant up.
Modify this concept again, but choose only the points that cause the least deviation from a straight line. Yes that means trying every set of points and see how good of a line they make. However this is now called the least median of squares. But you can see how it's getting rid of outliers, it chooses only the most 'clean' data. It works like a majority vote; outliers are completely eliminated from consideration. Using the chosen points to form a slope, is like taking an average of the best data. You can see it's adaptive; it uses the most clean data available and then reduces to a simple average (the sample mean is the population mean, it's consistent, efficient, unbiased, but not robust).
You might think that a guassian is infinite, however our data isn't, and it's not an actual graph but a virtual calculation, so you can include all the data arbitrarily.
This method of choosing only certain points is what puts 'median' in the name. It's also what gives it it's robustness, as you know a median isn't affected by outliers. In the worst case think of 3 points in a triangle; the longest side would be the best fit line.
There is an even more powerful method called maximum likelihood, but if you solve it for the case of Gaussian/normal, you get the method above.
This method is the best you can do; it's consistent, which means it always gets closer to the population mean (the true mean) as you use more data, it's efficient, which means it reaches the true conclusion the quickest as you use more data, it's unbiased.
jmac698
28th July 2017, 23:29
Ok I finally realized your point, this whole process is moot because if you have the tape then you can use a TBC to begin with. So baked in jitter is the only problem that needs solving here? Does TBC also fail sometimes?
I mean, it was still useful to me and some other people who couldn't get a TBC for whatever reason.
lordsmurf
29th July 2017, 04:54
I've been looking at robust statistics lately, and ran across windsoring, but it has a bias.
That's the point. You need bias. If the frame exceeds x pixels, then it should be ignored. Otherwise erratic skew can throw off the mean used for the line readjustment.
This should also be a variable switch, letting you adjust it higher or lower. Note the minimum must be 3 pixels, while something like 20 disables it.
this whole process is moot because if you have the tape then you can use a TBC to begin with.
Exactly.
So baked in jitter is the only problem that needs solving here?
Yes.
Does TBC also fail sometimes?
TBC is useless on embedded errors, and fails 99% of the time. It's rare to have an embedded error respond to TBC corrections.
I mean, it was still useful to me and some other people who couldn't get a TBC for whatever reason.
You'd be wasting time reinventing the wheel solely for the cheapness of others. You can buy a good ES10 for under $150, or JVC for under $300. If you're cheap, then buy it, use it, resell it.
Let's focus on currently-uncorrectable problems with video, creating something that does not exist to address it. And software TBC (lack thereof) is still a major issue.
I think we're on to something here. :)
I know MUCH more than I did 6-7 years ago when we met and this project was started, and you're probably the same. Let's finish it.
FranceBB
14th August 2020, 15:01
Hi there,
I was actually interested in a software TBC through Avisynth but the link is dead. Did someone archive the plugin?
johnmeyer
14th August 2020, 18:02
I have one version of the script, copied below. However, I think this was created before he had plugins. Are you looking for the jitter.dll and findpos.dll?
My guess is that those two plugins use somewhat the same algorithms that he used in this original script.
#Fast line shifter Ver 0.5 by jmac698
#Lines up either or both edges of a video. Can also be used as displacement for 3d scripts.
#Requires Masktools v2a45+ (mt_lutspa mode), GScript & GRunT http://forum.doom9.org/showthread.php?t=147846
#MinMax http://forum.doom9.org/showthread.php?p=1532124#post1532124
#Limitations: still no subpixel shifting
#0.5: Slow, but using a completely new approach, and can resize whole lines
#0.4: Fast, can detect and line up on left or right edges
#note: sample was frame 167, http://screenshotcomparison.com/comparison/88810
#Modified to work with wide-window sample capture, which includes hsync
loadPlugin("C:\Program Files\AviSynth 2.5\plugins\GScript.dll")
loadPlugin("C:\Program Files\AviSynth 2.5\plugins\minmax.dll")
loadplugin("C:\Program Files\AviSynth 2.5\plugins\mt_masktools-25.dll")
loadplugin("C:\Program Files\AviSynth 2.5\plugins\GRunT.dll")
src=AVISource("e:\frameserver.avi")
#crop(58,0,0,0)#Uncomment to line up by picture edge
out=ScriptClip(src, """
#Mark video edges
#thresh=72
thresh=55
converttoyv12
m=mt_binarize(thresh)
#Line up video
#findpos_h(m, searchwidth=22)
findpos_h(m, searchwidth=8)
alignbyluma(src,last)
""")
stackhorizontal(src,out)
function findpos_h(clip m, int "searchwidth", int "x1", int "x2"){
#Searches m from left to right in the range x1 to x1+searchwidth-1 and right to left in the range width-1-x2 to x2-searchwidth-1
#for the first luma=255 pixel, then colors the output line with the offset from x
#for example m is 0 0 255 255 255 0 0 0, width=8, x1=0, x2=0, searchwidth=4 becomes 4 4 2 3 3 4 4 4, then 2 2 2 2 3 3 3 3
#Can only search for 255 pixels (as the luma output is only 8 bit)
#c and m should have the same clip properties (same size)
#searchwidth should be <=width/2
searchwidth=default(searchwidth,32)
x1=default(x1,0)
x2=default(x2,0)
rampexpr="x "+string(m.width/2)+" < x "+string(m.width-1)+" x - ?"#x w/2 < x w-1 x - ?
ramp=mt_lutspa(m, mode="absolute",expr=rampexpr)
notfound=searchwidth#Value to return if no mask on this line, should be >=searchwidth or you'll find the wrong minimum later
maskmarker=255#The luma value in the mask which indicates a detected pixel
#(if m=maskmarker return ramp else notfound), 255 means x>=255, x<searchwidth or notfound
mt_lutxy(m,ramp,yexpr="x "+string(maskmarker)+" = y "+string(notfound)+" ?")
#now make solid lines based on min luma found in each line
l=crop(0,0,-width/2,0)
r=crop(width/2,0,0,0)
l=l.minmax(0,0)
r=r.minmax(0,0)
StackHorizontal(l,r)
}
function alignbyluma(clip src, clip shift, int "mode"){
#Shift/scale each line of clip src by the x offset defined by the luma of shift
#for example if shift were all luma=8, the entire src clip would move 8 pixels to the (dir)
#This works on a pixel basis, so solid horizontal lines in shift can shift src by variable amounts per line
#It uses a simple replacement strategy, where each pixel in shift is tested and replaced by the same pixel in a shifted copy
#Currently handles only 0-15 shifts
#Magnify everything to get full color resolution
mode=default(mode, 2)
shiftuv=shift
shift=shift.pointresize(shift.width*2,shift.height*2)
shift=ytouv(shiftuv,shiftuv,shift)
src=src.pointresize(src.width*2,src.height*2)#We double here to preserve chroma rez
GScript("
for (y=0, src.height/2-1, 1) {
l=int(getpixel(shift,0,y).YPlaneMin)
r=int(getpixel(shift,shift.width/2-2,y).YPlaneMin)
getline(src, y*2)
crop(l*2,0,-r*2,0).addborders(4,0,20,0).BilinearResize(last.width,last.height)
out=y==0?last:stackvertical(out,last)
}#for y
")#GScript
out
converttoyuy2
bilinearresize(src.width/2,src.height/2)
}
function align(clip v, int xl, int xr, int "mode") {
v#shift an image, x>0 shifts left, xl is amount to shift left, xr is amount to shift right
#mode 0 is shift left only, 1 shift right, 2 scale to shift left and right
mode=default(mode, 2)
offx=mode==0?xl:-xr
mode<2?pointresize(last.width, last.height, offx, 0, last.width, last.height):crop(xl,0,-xr,0).BilinearResize(last.width,last.height)
}
function getpixel(clip v, int x, int y) {
#get color of a single pixel and return as a fat 2x2 yv12 pixel
v
#pointresize(last.width*2,last.height*2)
crop(x>0?x*2:0,y>0?y*2:0,-(last.width-x*2-2),-(last.height-y*2-2))
}
function getline(clip v, int y) {
v#return a line of height 2 from y to y+1
crop(0,y,0,-(last.height-y-2))
}
StainlessS
14th August 2020, 18:41
Not Jmac TBC, but I have this from 2008:- https://code.google.com/archive/p/avisynthrestoration/downloads
Copyright 2008 halifaxgeorge licenced under GPL v3
tbcv01.avs [link has additional stuff]
#~ This program is free software: you can redistribute it and/or modify
#~ it under the terms of the GNU General Public License as published by
#~ the Free Software Foundation, either version 3 of the License, or
#~ (at your option) any later version.
#~ This program is distributed in the hope that it will be useful,
#~ but WITHOUT ANY WARRANTY; without even the implied warranty of
#~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
#~ GNU General Public License for more details.
#~ You should have received a copy of the GNU General Public License
#~ along with this program. If not, see <http://www.gnu.org/licenses/>.
#TBC 0.1
#Released: Sep 24, 2008
#License: Copyright 2008 halifaxgeorge licenced under GPL v3
#Author: halifaxgeorge
#Description: Avisynth Script which stabilizes timing jitter in video from analog VCRs, similiar in function to a TimeBaseCorrector.
#Limitations: This script is very slow and uses a lot of memory due to limitations in Avisynth.
#Simple things that are possible to program are not practical to run, I consider this a limitation. There is nothing compute intensive here.
#Functions:
#greybars(clip "vid", int "count", float "starty", int "endy", int "n", int "w", int "h") - create testpattern in length frames. strty,endy - starting
#and ending luminence values. n number of vertical greybars. w width of resulting clip. h height of resulting clip. count, used internally for loop counter.
#vid, used internally for stacking greybars together.
#greybars() creates a 300 frame video.
#hexcolor(float "c1", float "c2", float "c3"), the values c1-c3 are y,u,v. The result is a avisynth colorvalue, which can be used for example in blankclip.
#hexcolor(y) - specify only the Y value.
#jitter(clip in, int jitter) - simulates timing jitter. in is video to be de-stabilized. jitter is pixels/2 to horizontally shift video sections
#limitations: this is a very rough simulation. The video is sliced into 5 horizontal sections, of only 16 pixels, each slice is randomly shifted
#in the horizontal direction. A true jitter would shift every line randomly.
#Now what is this shift operation? It's a scaling in x with revealing a black background behind, equivalent to the timescaling
#of a mechanical video head turning too fast (thus shorter video line).
#tbc(clip) - the supplied clip is stabilized as a whole, thus ideally, one would submit a one-pixel high line.
#however, avisynth doesn't handle 1 pixel lines in YV12, so practically we will send a point-resized 2 pixel high line for each
#true line of video.
#The stabilization algorithm: the luminence values are searched from far right to left by 16 pixels (ie 704-719) until
#a luminence above thresh is found. Thresh is hardcoded into the function at the moment. The result is the x value
#where the thresh was found. Based on this x value, the line is resized to full size. Full size is normally 720 pixels,
#however another hardcoded value, the offset, resizes to less than 720 pixels.
#The purpose of offset is, that searching for thresh consumes some part of the right edge of the video, thus
# the last few pixels (less than thresh) would be cutoff. Therefore, now one can consider that *detection* of the video edge
#is separated from the re-scaling.
#Results: This script does work, and was able to stabilize a real analog video. However it was clear that many improvements
#could be made. One is that thresh is too simple to detect the edge of the video. The presence of a spike of noise would
#cause instant pixel jumps of that line when it shouldn't. Also varying subpixel posiitioning and luma variances by line
#can cause imprecise de-jittering. What is needed is a stable, subpixel edge detection.
#Also the left side of the video can be stabilized as well; this is easy if you flip the video and pass it through twice,
#though the roundoff of the calculations would build up. A true double-sided one pass filter would be better.
#In summary, it's amazing that this actually works and demonstrates advanced per-pixel and per-line manipulation
#solely in a script, where AviSynth is mostly per-frame based. It is useful to prototype stabilization algorithms
#with rapid prototyping before writing a filter (which I currently don't know how to do anyhow :( ).
function greybars(clip "vid", int "count", float "starty", int "endy", int "n", int "w", int "h") {
starty=defined(starty)?starty:16
endy=defined(endy)?endy:235
n=defined(n)?n:6
w=defined(w)?w:720
h=defined(h)?h:480
vid=defined(vid)?vid:blankclip(30*10,w/n,h,"YV12",color_yuv=hexcolor(starty))
count=defined(count)?count:0
step=(endy-starty)/(n-1.0)
count<n-1?stackhorizontal(blankclip(30*10,w/n,h,"YV12",color_yuv=hexcolor(starty+count*step)),greybars(vid=vid,count=count+1,starty=starty,endy=endy,n=n,w=w,h=h)):blankclip(30*10,w/n,h,"YV12",color_yuv=hexcolor(starty+count*step))
}
function hexcolor(float "c1", float "c2", float "c3") {
#This function returns a hex color value from 3 decimal values.
#It defaults to black as defined in the YUV colorspace, which is "$108080".
#This is an example: hexcolor(235) returns "$EB8080".
c1=defined(c1)?c1:16
c2=defined(c2)?c2:128
c3=defined(c3)?c3:128
c1=round(c1)
c2=round(c2)
c3=round(c3)
c1*65536+c2*256+c3
}
#To use on a real video:
#v=MPEG2Source("C:\MyVideos\vcr\rca.d2v", cpu=0)
#remove jitter line
#remove stackvertical line, which is just for visual comparison of before and after
#adjust hardcoded offset and thresh in tbc function
#adjust hardcoded '80' in slice to 480.
#hope it doesn't crash - it's slow and takes a lot of memory. Adjust slice value slowly upward.
#this works! The first 80 lines are TBC'd and highlighted
v=greybars()
#v=MPEG2Source("C:\MyVideos\vcr\rca.d2v", cpu=0)
v
jitter(4+1)
slice(last,0)
pointresize(last.width,last.height/4)
stackvertical(last.coloryuv(off_y=24),v.crop(0,0,0,480-80))
# pointresize(last.width,last.height*2)
# crop(0,4*2,0,2).pointresize(720,4)
# tbc
function tbc(clip last) {
ScriptClip("""
v3=last
function getpixel(clip v2, int x1, int y1) {
v2
pointresize(last.width*2,last.height*2)#double to avoid problems reading single pixel of YV12
crop(x1*2,y1*2,2,2)
}
y=0
v3
#find where >=thresh
p0=getpixel(last,719,0).averageluma
p1=getpixel(last,718,0).averageluma
p2=getpixel(last,717,0).averageluma
p3=getpixel(last,716,0).averageluma
p4=getpixel(last,715,0).averageluma
p5=getpixel(last,714,0).averageluma
p6=getpixel(last,713,0).averageluma
p7=getpixel(last,712,0).averageluma
p8=getpixel(last,711,0).averageluma
p9=getpixel(last,710,0).averageluma
p10=getpixel(last,709,0).averageluma
p11=getpixel(last,708,0).averageluma
p12=getpixel(last,707,0).averageluma
p13=getpixel(last,706,0).averageluma
p14=getpixel(last,705,0).averageluma
p15=getpixel(last,704,0).averageluma
thresh=85
offset=4
x=p0>=thresh?719:p1>=thresh?718:p2>=thresh?717:p3>=thresh?716:p4>=thresh?715:p5>=thresh?714:p6>=thresh?713:p7>=thresh?712: \
p8>=thresh?711:p9>=thresh?710:p10>=thresh?709:p11>=thresh?708:p12>=thresh?707:p13>=thresh?706:p14>=thresh?705:p15>=thresh?704:720
v3.bilinearresize(last.width,last.height,0,last.height,x+offset,last.height)#+4 moves detection point in
#subtitle(string(x),x=100)
""")
}
function jitter(clip in, int jitter) {
n=5#this works and make 5 slices of horizontally resized areas
j1=rand(jitter)#determined once per clip
j2=rand(jitter)
j3=rand(jitter)
j4=rand(jitter)
j5=rand(jitter)
w=720
h=480
h1=h/n
h1=16
slice=blankclip(width=w,height=h1,pixel_type="YV12")
c1=in.bilinearresize(720-j1*2,h1,0,0*h1,w,h1)
c1=overlay(slice,c1,opacity=1).subtitle(string(j1))
#c1=c1.bilinearresize(720,h1,0,h1,720-j1*2,h1)#test resizing back to original size
c2=in.bilinearresize(720-j2*2,h1,0,1*h1,w,h1)
c2=overlay(slice,c2,opacity=1).subtitle(string(j2))
#c2=c2.bilinearresize(720,h1,0,h1,720-j2*2,h1)
c3=in.bilinearresize(720-j3*2,h1,0,2*h1,w,h1)
c3=overlay(slice,c3,opacity=1).subtitle(string(j3))
c4=in.bilinearresize(720-j4*2,h1,0,3*h1,w,h1)
c4=overlay(slice,c4,opacity=1).subtitle(string(j4))
c5=in.bilinearresize(720-j5*2,h1,0,4*h1,w,h1)
c5=overlay(slice,c5,opacity=1).subtitle(string(j5))
stackvertical(c1,c2,c3,c4,c5)
}
function slice(clip v5, int y) {
#warning! this function is very slow. It eats a lot of memory also. Only good for experimentation.
v5
pointresize(last.width,last.height*2)
crop(0,y*2,0,2).pointresize(720,4)
tbc
y<80-1?stackvertical(last,slice(v5,y+1)):last
}
EDIT: Later version of JMac698 Tbc.avs than posted by JohnMeyer, v0.53 (from 1st post) rather than JM v0.5 [ Last edited by jmac698; 14th July 2012 at 06:16. Reason: Updated ]
#Fast line shifter Ver 0.53 by jmac698
#Lines up either or both edges of a video. Can also be used as displacement for 3d scripts.
#Requires Masktools v2a45+ (mt_lutspa mode), GRunT, GScript
#MinMax http://forum.doom9.org/showthread.php?p=1532124#post1532124
#Limitations: still no subpixel shifting
#0.53: Avoid possible "ScriptClip: Function did not return a video clip of the same colorspace as the source clip!"
#0.52: Less blurry resize
#0.51: Autothresh (uses 2 pixels of left border as a starting point, then adds a small amount to avoid noise)
#0.5: Slow, but using a completely new approach, and can resize whole lines
#0.4: Fast, can detect and line up on left or right edges
#note: sample was frame 167, http://screenshotcomparison.com/comparison/88810
#Modified to work with wide-window sample capture, which includes hsync
src=AVISource("D:\project001a\tbc2\vhs hysnc sample.avi").converttoyuy2
#crop(8,0,0,0)#Uncomment and adjust to remove extra black left border
thresh=int(findthresh(src))+3#This may not always work, try to manually set to 32 for example. Pick the lowest value which lines up picture.
ScriptClip(src, """
#Mark video edges
converttoyv12
m=mt_binarize(thresh)
#Line up video
findpos_h(m, searchwidth=22)
alignbyluma(src,last)
""")
addborders(56,0,0,0)
function findpos_h(clip m, int "searchwidth", int "x1", int "x2"){
#Searches m from left to right in the range x1 to x1+searchwidth-1 and right to left in the range width-1-x2 to x2-searchwidth-1
#for the first luma=255 pixel, then colors the output line with the offset from x
#for example m is 0 0 255 255 255 0 0 0, width=8, x1=0, x2=0, searchwidth=4 becomes 4 4 2 3 3 4 4 4, then 2 2 2 2 3 3 3 3
#Can only search for 255 pixels (as the luma output is only 8 bit)
#c and m should have the same clip properties (same size)
#searchwidth should be <=width/2
searchwidth=default(searchwidth,32)
x1=default(x1,0)
x2=default(x2,0)
rampexpr="x "+string(m.width/2)+" < x "+string(m.width-1)+" x - ?"#x w/2 < x w-1 x - ?
ramp=mt_lutspa(m, mode="absolute",expr=rampexpr)
notfound=searchwidth#Value to return if no mask on this line, should be >=searchwidth or you'll find the wrong minimum later
maskmarker=255#The luma value in the mask which indicates a detected pixel
#(if m=maskmarker return ramp else notfound), 255 means x>=255, x<searchwidth or notfound
mt_lutxy(m,ramp,yexpr="x "+string(maskmarker)+" = y "+string(notfound)+" ?")
#now make solid lines based on min luma found in each line
l=crop(0,0,-width/2,0)
r=crop(width/2,0,0,0)
l=l.minmax(0,0)
r=r.minmax(0,0)
StackHorizontal(l,r)
}
function alignbyluma(clip src, clip shift, int "mode"){
#Shift/scale each line of clip src by the x offset defined by the luma of shift
#for example if shift were all luma=8, the entire src clip would move 8 pixels to the (dir)
#This works on a pixel basis, so solid horizontal lines in shift can shift src by variable amounts per line
#It uses a simple replacement strategy, where each pixel in shift is tested and replaced by the same pixel in a shifted copy
#Currently handles only 0-15 shifts
#Magnify everything to get full color resolution
mode=default(mode, 2)
shiftuv=shift
shift=shift.pointresize(shift.width*2,shift.height*2)
shift=ytouv(shiftuv,shiftuv,shift)
src=src.pointresize(src.width*2,src.height*2)#We double here to preserve chroma rez
GScript("
for (y=0, src.height/2-1, 1) {
l=int(getpixel(shift,0,y).YPlaneMin)
r=int(getpixel(shift,shift.width/2-2,y).YPlaneMin)
getline(src, y*2)
align(l*2, r*2, 4, 4)
out=y==0?last:stackvertical(out,last)
}#for y
")#GScript
out
converttoyuy2
bilinearresize(src.width/2,src.height/2)
}
function align(clip v, int xl, int xr, int lb, int rb, int "mode") {
v#shift an image, x>0 shifts left, xl is amount to shift left, xr is amount to shift right
#mode 0 is shift left only, 1 shift right, 2 scale to shift left and right
mode=default(mode, 2)
offx=mode==0?xl:-xr
mode<2?pointresize(last.width, last.height, offx, 0, last.width, last.height):crop(xl,0,-xr,0).addborders(lb, 0, rb, 0).Spline36Resize(last.width,last.height)
}
function getpixel(clip v, int x, int y) {
#get color of a single pixel and return as a fat 2x2 yv12 pixel
v
#pointresize(last.width*2,last.height*2)
crop(x>0?x*2:0,y>0?y*2:0,-(last.width-x*2-2),-(last.height-y*2-2))
}
function getline(clip v, int y) {
v#return a line of height 2 from y to y+1
crop(0,y,0,-(last.height-y-2))
}
function findthresh(clip v){
#Find a resonable starting point for thresh by searching border
current_frame=0
v.converttoyv12
crop(0,16,-last.width+2,-16)
AverageLuma
}
EDIT: Maybe Reel.Deel has any missing plugins.
@jmac698
I believe I have most of those plugins. Do you mind if I upload them?
EDIT:
Ok here they are:
addcode02.zip (https://dl.dropbox.com/s/yaj13afjr5ttpd8/addcode02.zip)
corr03.zip (https://dl.dropbox.com/s/rjnwwz94izl4pyd/corr03.zip)
decomet04.zip (https://dl.dropbox.com/s/67iybsgmah7hnnj/decomet04.zip)
dejitter03.zip (https://dl.dropbox.com/s/1c09rcpbjwmzfd6/dejitter03.zip)
findpos02.zip (https://dl.dropbox.com/s/hpk9n9wkr2i10g6/findpos02.zip)
mandelbrot01a.zip (https://dl.dropbox.com/s/sfyiiir2ptqv6eu/mandelbrot01a.zip)
slicer02.zip (https://dl.dropbox.com/s/n2j8tdhz092m2la/slicer02.zip)
taverage 01.zip (https://dl.dropbox.com/s/9lzco9qggolza64/taverage 01.zip)
taverage 01 src.zip (https://dl.dropbox.com/s/m45ryrmub2zvgxk/taverage 01 src.zip)
tbc061.zip (https://dl.dropbox.com/s/3epcmnd5pyk7xrt/tbc061.zip)
Above updated TBC061.zip has dejitter03 and findpos02 dll's.
FranceBB
14th August 2020, 23:21
Thanks to you both! I'm gonna try it out and see whether it does what it's supposed to do with my source which definitely would have benefited from an hardware time base correction (but the tape is on another site 500 km away and it's not exactly the time to... uh... travel... you know...). ;)
Reel.Deel
15th August 2020, 20:16
EDIT: Maybe Reel.Deel has any missing plugins.
Those are all of jmac's plugins. He also lost the source code to all of them :(.
StainlessS
15th August 2020, 20:29
Yep, Sendspace only keeps them for 30 days after latest download. [ what a silly billy :) ]
RD, did you update this on Wiki: http://avisynth.nl/index.php/Filter_SDK/Env_SaveString
Thats a pretty awful example of env->SaveString() use, eg
Filter SDK/Env SaveString
env->SaveString is given to allow users to pass strings to AVSValue, and ensure that they are being deallocated on unload.
An Example:
fnpluginnew = new char[string_len];
strcpy(fnpluginnew, fnplugin.AsString());
strcat(fnpluginnew, " ");
strcat(fnpluginnew, name);
env->SetGlobalVar("$PluginFunctions$", AVSValue(env->SaveString(fnpluginnew, string_len)));
// Since fnpluginnew has now been saved it can safely be deleted.
delete[] fnpluginnew;
What newbie is gonna know what "$PluginFunctions$" is, and if thats not bad enough, looks like it will squash your current plugin functions list to me.
I think previous example was taken from one of my posts in dev forum, way better .
I'll find it if you want.
EDIT: Here:- https://forum.doom9.org/showthread.php?p=1633936#post1633936
Here how to return strings:-
char * strlit="AnyOldName";
int len=strlen(strlit);
char * s=new char[len+1];
if(s==NULL)
env->ThrowError("Cannot Allocate string mem");
strcpy(s,strlit); // dup
char *e=s+len; // point at nul
// make safe copy of string (freed on Avisynth closure)
AVSValue ret = env->SaveString(s,e-s); // e-s is text len only (excl nul) {SaveString uses memcpy)
// AVSValue ret = env->SaveString(s); // alternative, Avisynth uses strlen to ascertain length
// AVSValue ret = env->SaveString(s,-1); // alternative, Avisynth uses strlen to ascertain length
delete [] s; // delete our temp s buffer
return ret; // return Saved Str as AVSValue
// return strlit; // Alternative to MOST of above code char* converted to AVSValue.
// return "AnyOldName"; // Alternative to ALL of above code char* converted to AVSValue.
// String literals are read only and at constant address and so need not be saved.
above untested
EDIT: Dont use the one in red above (Its a bit naughty).
EDIT:
Avisynth and your plugin both have their own heap memory [new/malloc] and when you return a string allocated on your plugin heap,
you MUST hand over a string copy to avisynth, you must make a copy on avisynth heap (using avisynth's env->SaveString()) and give it the copy,
this gives avisynth full control over the string memory and also the responsibility to delete the memory when avisynth closes down.
After you make a string copy for avisynth, you can then delete/free your temp string memory buffer.
It is not necessary to use SaveString() on a string literal, "String literals are read only and at constant address and so need not be saved".
Set a Global var named MyGlobalString="Some GlobalVar Text" (actually does not need to use SaveString as "Some GlobalVar Text" is a String Literal).
env->SetGlobalVar("MyGlobalString", AVSValue(env->SaveString("Some GlobalVar Text"))); // Set Global [SaveString will use strlen() to establish length of string]
...
env->SetVar("MyLocalString", AVSValue(env->SaveString("Some LocalVar Text"))); // Set Local [SaveString will use strlen() to establish length of string]
[B]Also NOTE above, if MyGlobalString and MyLocalString variable name strings above were NOT string literals and located in some dynamic memory buffer, you would
similarly have to use env->SaveString() to make copies of them, and give the copies as the names to SetGlobalVar() and SetVar() functions.
Set Global variable MyPi=3.1415926 [again, below MyPiName is a pointer to "MyPi" string literal (text wrapped in double quotes) and does not need env->SaveString, is just for illustration purposes]
char *MyPiName = "MyPi";
float pi=3.1415926f;
env->SetGlobalVar(env->SaveString(MyPiName), AVSValue(pi));
...
env->SetGlobalVar(env->SaveString(MyPiName), pi); // Implicit type conversion of float pi to an AVSValue
jmac698
10th June 2024, 01:11
Thanks for finding my old plugins. I still don't know where a good place to post stuff is though. What site lasts 10 years these days?
I actually did work on an aspect of this recently. What I called the perfect TBC method was using a correlation to find horizontal line jitter. This used several samples so was able to be more robust to noise, besides changes in brightness and contrast. I was able to use ChatGPT to solve all the math behind it. I've learned much more since then. There's a bunch of approaches to my problem and they can be measured against something called the Rao bound. Math like this is used for many things such as photogrammatry and aligning audio from different sources, measuring echo, and other types of alignment problems.
The baked in type of jitter could probably be trained with AI now.
Good thing I turned into a data scientist :)
Meanwhile we have vhsdecode which is improving all the time.
https://www.videohelp.com/software/VHS-Decode
You need to solder two wires to your direct head feed. You don't need an expensive Doomsday decoder, you can use a cheap TV USB stick as used in SDR or Software Defined Radio.
https://www.rtl-sdr.com/buy-rtl-sdr-dvb-t-dongles/
Since VHS is an FM signal, the capture doesn't device doesn't have to be precise, it only needs to measure a frequency.
jmac698
10th June 2024, 01:11
Also my mailbox is not full, I don't know what's wrong. mail away
poisondeathray
10th June 2024, 02:36
I still don't know where a good place to post stuff is though. What site lasts 10 years these days?
Authors, contributors of many code projects (not just avisynth related) use Github . It's owned by Microsoft now, but unlikely to "disappear"
Reel.Deel
10th June 2024, 22:01
Thanks for finding my old plugins. I still don't know where a good place to post stuff is though. What site lasts 10 years these days?
Too bad you were hesitant on releasing the source code back then :p. But having something instead of nothing is better.
If you don't want to host on GitHub as poisondeathray suggested, VideoHelp offers 512mb of storage. They have proven to be reliable and you can easily archive it also. I've done exactly that with a handful of plugins that are on the wiki.
vBulletin® v3.8.11, Copyright ©2000-2026, vBulletin Solutions Inc.