This forum uses cookies
This forum makes use of cookies to store your login information if you are registered, and your last visit if you are not. Cookies are small text documents stored on your computer; the cookies set by this forum can only be used on this website and pose no security risk. Cookies on this forum also track the specific topics you have read and when you last read them. Please confirm whether you accept or reject these cookies being set.

A cookie will be stored in your browser regardless of choice to prevent you being asked this question again. You will be able to change your cookie settings at any time using the link in the footer.

Restoring badly digitized VHS
#1
I have a digitized VHS (no access to the original tape) file where it looks like the field heads wobble slightly (vertically) independent of each other.  So even after deinterlacing with QTGMC there is some combing or echo (I'm not sure of the right way to describe this) that can last several seconds before aligning again and looking correct.  Is there any hope to fix/restore this?

   
Reply
#2
If you can share a small sample of the original, I can have a look at it tomorrow evening.
My first try would be to look at the separated fields, an think about whether filtering the separated fields before deinterlacing might help. Also, maybe using fields matching instead of deinterlacing is the better approach,..

Maybe the separated fields are already messed Up,...


Cu Selur
----
Dev versions are in the 'experimental'-folder of my GoogleDrive, which is linked on the download page.
Reply
#3
Thanks!  I tried VITVC as well since this is a DVD digitization of a telecined movie, and it gives overall slightly more appealing results, but it still has a lot of the same artifacts you seen in the screenshot.

Here is a 30-second clip of the VHS warning.

And here is a 2-minute clip of the movie opening credits which shows the same effects.
Reply
#4
Looking at the 30sec sample:
Try Filtering->Vapoursynth->Frames->Stabilize->ReduceFlicker, it will at least stabilize the video.
Text will still be messed up, since the fields are already broken.
[Image: grafik.png]
Looking at the 2min sample, I would also Crop and add Filtering->Vapoursynth->Frames->Stabilize->Stab and probably Filtering->Vapoursynth->Artifacts->SpotLess.

Cu Selur
----
Dev versions are in the 'experimental'-folder of my GoogleDrive, which is linked on the download page.
Reply
#5
Thanks, I'll give this a try and report back.  Also, just to make it interesting, I believe the movie (not the VHS warning, of course) was a PAL telecine (not sped up) on VHS, then converted to NTSC DVD.
Reply
#6
Haven't really looked at frame pattern Smile ( no horizontal movement, so hard to spot )
But reading https://forum.doom9.org/showthread.php?t=176104 might be interesting.
Norm conversions are always a pain, you can try using sRestore to get to 25 fps or TIVTC in mode 7 and aim for 25fps,..

Cu Selur
----
Dev versions are in the 'experimental'-folder of my GoogleDrive, which is linked on the download page.
Reply
#7
Still sticking with my guess that the text lines look so horrible because the two VHS heads where vertically wobbling slightly out of sync from each other, I tried separating the top/bottom fields into a single stream with one field per frame, stabilize that, then reweave back to proper interlaced.

Interestingly, when I used ffmpeg to separate fields, then hybrid stab(mirror=15, dxmax=0, dymax=2, range=1) on that new half-height field-per-frame file, then finally ffmpeg again to weave back to interlaced, I was happy with the result and it did look improved to my eye.

I thought I should be able to accomplish the same thing with a custom VS filter, so tried this script (below) inserted just before the End event, but the results did not turn out the same at all.  Any thoughts why this may be?

core.std.LoadPlugin(path="C:/Program Files/Hybrid/64bit/vsfilters/MiscFilter/MiscFilters/MiscFilters.dll")
core.std.LoadPlugin(path="C:/Program Files/Hybrid/64bit/vsfilters/DenoiseFilter/ZSmooth/zsmooth.dll")
core.std.LoadPlugin(path="C:/Program Files/Hybrid/64bit/vsfilters/Support/libmvtools.dll")
core.std.LoadPlugin(path="C:/Program Files/Hybrid/64bit/vsfilters/Support/DePan.dll")
import stabilize
clip = core.std.SeparateFields(clip, tff=True)
clip = core.std.AssumeFPS(clip=clip, fpsnum=60000, fpsden=1001)
clip = stabilize.Stab(clp=clip, mirror=15, dxmax=0, dymax=2, range=1)
clip = core.std.DoubleWeave(clip, tff=True)
clip = core.std.SelectEvery(clip, 2, 0)
clip = core.std.AssumeFPS(clip=clip, fpsnum=30000, fpsden=1001)

The difference is quite obvious when you apply to the clip of the VHS warning that I linked previously.

These are the ffmpeg commands I used to separate fields and weave:


ffmpeg -i <in> -vf separatefields -c:v ffv1 -r "60000/1001" -c:a copy -map_metadata -1 <out>
ffmpeg -i <in> -vf weave -c:v ffv1 -r "30000/1001" -c:a copy -map_metadata -1 <out>
Reply
#8
Quote:Any thoughts why this may be?
No, no clue what the characteristics of the ffv1 file the first call outputs are.
=> not sure that this will actually do the same. Smile
----
Dev versions are in the 'experimental'-folder of my GoogleDrive, which is linked on the download page.
Reply
#9
For stabilization, try Stab+SpotLess+ReduceFlicker+TemporalFix. (https://www.mediafire.com/file/fygwu6cl4...e.mkv/file)
# Imports
import vapoursynth as vs
# getting Vapoursynth core
import ctypes
import sys
import os
core = vs.core
# Limit frame cache to 48473MB
core.max_cache_size = 48473
# Import scripts folder
scriptPath = 'F:/Hybrid/64bit/vsscripts'
sys.path.insert(0, os.path.abspath(scriptPath))
# Loading Support Files
Dllref = ctypes.windll.LoadLibrary("F:/Hybrid/64bit/vsfilters/Support/libfftw3f-3.dll")
# loading plugins
core.std.LoadPlugin(path="F:/Hybrid/64bit/vsfilters/Support/libmotionmask.dll")
core.std.LoadPlugin(path="F:/Hybrid/64bit/vsfilters/ColorFilter/Retinex/Retinex.dll")
core.std.LoadPlugin(path="F:/Hybrid/64bit/vsfilters/Support/vszip.dll")
core.std.LoadPlugin(path="F:/Hybrid/64bit/vsfilters/DenoiseFilter/CTMF/CTMF.dll")
core.std.LoadPlugin(path="F:/Hybrid/64bit/vsfilters/Support/TCanny.dll")
core.std.LoadPlugin(path="F:/Hybrid/64bit/vsfilters/Support/libfillborders.dll")
core.std.LoadPlugin(path="F:/Hybrid/64bit/vsfilters/Support/libtemporalmedian.dll")
core.std.LoadPlugin(path="F:/Hybrid/64bit/vsfilters/FrameFilter/ReduceFlicker/ReduceFlicker.dll")
core.std.LoadPlugin(path="F:/Hybrid/64bit/vsfilters/MiscFilter/MiscFilters/MiscFilters.dll")
core.std.LoadPlugin(path="F:/Hybrid/64bit/vsfilters/Support/DePan.dll")
core.std.LoadPlugin(path="F:/Hybrid/64bit/vsfilters/DenoiseFilter/ZSmooth/zsmooth.dll")
core.std.LoadPlugin(path="F:/Hybrid/64bit/vsfilters/GrainFilter/AddGrain/AddGrain.dll")
core.std.LoadPlugin(path="F:/Hybrid/64bit/vsfilters/DenoiseFilter/NEO_FFT3DFilter/neo-fft3d.dll")
core.std.LoadPlugin(path="F:/Hybrid/64bit/vsfilters/DenoiseFilter/DFTTest/DFTTest.dll")
core.std.LoadPlugin(path="F:/Hybrid/64bit/vsfilters/Support/EEDI3m_opencl.dll")# vsQTGMC
core.std.LoadPlugin(path="F:/Hybrid/64bit/vsfilters/ResizeFilter/nnedi3/NNEDI3CL.dll")
core.std.LoadPlugin(path="F:/Hybrid/64bit/vsfilters/Support/libmvtools.dll")
core.std.LoadPlugin(path="F:/Hybrid/64bit/vsfilters/Support/fmtconv.dll")
core.std.LoadPlugin(path="F:/Hybrid/64bit/vsfilters/DeinterlaceFilter/Bwdif/Bwdif.dll")
core.std.LoadPlugin(path="F:/Hybrid/64bit/vsfilters/Support/akarin.dll")
core.std.LoadPlugin(path="F:/Hybrid/64bit/vsfilters/SourceFilter/DGDecNV/DGDecodeNV_AVX2.dll")
# Import scripts
import vs_temporalfix
import SpotLess
import stabilize
import qtgmc
import validate
# Source: 'C:\Users\Selur\Desktop\A1_t00-00.00.10.247-00.00.39.835.mkv'
# Current color space: YUV420P8, bit depth: 8, resolution: 704x480, frame rate: 29.97fps, scanorder: top field first, yuv luminance scale: limited, matrix: 470bg, transfer: bt.601, primaries: bt.601 ntsc, format: mpeg-2
# Loading C:\Users\Selur\Desktop\A1_t00-00.00.10.247-00.00.39.835.mkv using DGSource
clip = core.dgdecodenv.DGSource("J:/tmp/mkv_1e10dacb566c29e0d6d6d62f9640a558_853323747.dgi",fieldop=0)# 29.97 fps, scanorder: top field first
frame = clip.get_frame(0)
# setting color matrix to 470bg.
clip = core.std.SetFrameProps(clip, _Matrix=vs.MATRIX_BT470_BG)
# setting color transfer (vs.TRANSFER_BT601), if it is not set.
if validate.transferIsInvalid(clip):
  clip = core.std.SetFrameProps(clip=clip, _Transfer=vs.TRANSFER_BT601)
# setting color primaries info (to vs.PRIMARIES_BT470_BG), if it is not set.
if validate.primariesIsInvalid(clip):
  clip = core.std.SetFrameProps(clip=clip, _Primaries=vs.PRIMARIES_BT470_BG)
# setting color range to TV (limited) range.
clip = core.std.SetFrameProps(clip=clip, _ColorRange=vs.RANGE_LIMITED)
# making sure frame rate is set to 29.97fps
clip = core.std.AssumeFPS(clip=clip, fpsnum=30000, fpsden=1001)
# making sure the detected scan type is set (detected: top field first)
clip = core.std.SetFrameProps(clip=clip, _FieldBased=vs.FIELD_TOP) # tff
# Deinterlacing using QTGMC
clip = qtgmc.QTGMC(Input=clip, Preset="Fast", InputType=0, TFF=True, TR2=1, SourceMatch=0, Lossless=0, opencl=True) # new fps: 59.94
# Making sure content is preceived as frame based
clip = core.std.SetFrameProps(clip=clip, _FieldBased=vs.FIELD_PROGRESSIVE) # progressive
# stabilizing using Stab
clip = stabilize.Stab(clp=clip,mirror=15,dxmax=0,dymax=2)
# removing flickering using ReduceFlicker
clip = core.rdfl.ReduceFlicker(clip=clip, strength=3, aggressive=1)
# Spot removal using SpotLess
clip = SpotLess.SpotLess(clip=clip, radT=3, rec=True, pel=1, smoother="zsmooth")
# changing range from limited to full range for vsTemporalfix
clip = core.resize.Bicubic(clip, format=vs.YUV420P8, range_in_s="limited", range_s="full")
# setting color range to PC (full) range.
clip = core.std.SetFrameProps(clip=clip, _ColorRange=vs.RANGE_FULL)
# stabilizing using Temporalfix
clip = vs_temporalfix.vs_temporalfix(clip=clip,denoise=True)
# changing range from full to limited range for vsTemporalfix
clip = core.resize.Bicubic(clip, format=vs.YUV420P8,range_in_s="full", range_s="limited")
# setting color range to TV (limited) range.
clip = core.std.SetFrameProps(clip=clip, _ColorRange=vs.RANGE_LIMITED)
# adjusting output color from: YUV420P8 to YUV420P10 for NVEncModel
clip = core.resize.Bicubic(clip=clip, format=vs.YUV420P10)
# set output frame rate to 59.94fps (progressive)
clip = core.std.AssumeFPS(clip=clip, fpsnum=60000, fpsden=1001)
# output
clip.set_output()
but the text is still ugly.

Cu Selur
----
Dev versions are in the 'experimental'-folder of my GoogleDrive, which is linked on the download page.
Reply
#10
(21.09.2025, 18:58)Selur Wrote:
Quote:Any thoughts why this may be?
No, no clue what the characteristics of the ffv1 file the first call outputs are.

Here's the process on a smaller clip, I prefer the output here after the fields are stabilized and woven back to interlaced.  I'm surprised I can't duplicate this using a pure VS script (mostly suspicious of my lack of 
understanding here, not VS).

https://ln5.sync.com/dl/1cce6c470#5ck9r5...j-kgnb327u
Reply


Forum Jump:


Users browsing this thread: 1 Guest(s)