Log in

View Full Version : Flaw in the Logic Behind Motion Compensated Denoising?


johnmeyer
14th March 2017, 23:09
I have a question about a possible downside to motion-compensated denoising.

I'm transferring and restoring a large number of VHS and 8mm videos to be shown at a client's wedding reception (after editing, of course). Most tapes are OK, but one tape has major issues with horizontal black lines (i.e., I don't think the problem was created by my transfer equipment).

http://i177.photobucket.com/albums/w208/johnmeyer/Horizontal%20tears_zpszw0gwcgn.jpg

Here is a four-second 16 MB test clip that has seven short scenes of 15-30 frames each. It is in DV format.

Four-Second Test Clip (https://www.mediafire.com/?sdfk6brkuyds4qs)

I've copied below the script that I'm using. It uses MDegrain2 for basic denoising, followed by Despot to get rid of the black lines/tears/comets. I spent quite of bit of time once again wrangling with Despot and actually was able to get some reasonable results, although far from perfect.

If you use this script, I have it set so that as you walk through the video it shows you each original frame followed immediately by the frame that results from the script.

Here's the issue.

On some frames, even when most of the lines are removed, I sometimes get added lines. Upon inspection, these new noise lines are from the previous frame. It would appear that since motion compensation is done before any artifact removal that the artifacts used in the motion compensation are considered to be OK, and therefore when the compensated frame is used, it contains these artifacts which are allowed to creep back into the video, although somewhat degraded, one frame later.

If you use the script below, as written, it gives you 2x the original frames because it interleaves the original and the final output. Go to frame 252 in the test clip. Then, go to frame 253, the restored version of 252, and look at the pillar immediately to the right of the back of the piano, just below the microphone. You'll see several horizontal lines that were not in frame 252. But, if you go to frame 250, which is the input frame that immediately precedes 252, you'll see where those lines came from.

Am I missing something? Is there a way around this issue?

The script is getting rid of about 70% of the lines, and almost all the other noise artifacts, so I am reasonably happy.

However, I'd always like to do better.

Here's the script. Yes, I know I should put the variables at the start of the function, and I should re-use the code for the even and odd fields, but I was in a hurry, and this was easy.

Loadplugin("C:\Program Files\AviSynth 2.5\plugins\MVTools\mvtools2.dll")
LoadPlugin("c:\Program Files\AviSynth 2.5\plugins\CNR\Cnr2.dll")
LoadPlugin("C:\Program Files\AviSynth 2.5\plugins\Film Restoration\Script_and_Plugins\RemoveGrainSSE2.dll")

source=AVISource("E:\fs2.avi").killaudio().AssumeBFF().ConvertToYV12()

chroma=source.Cnr2("oxx",8,16,191,100,255,32,255,false)

output=MDegrain2i2(chroma,16,4,600,0)
output=Remove_TearsI(output)

#Remove comment and then add back comments to block below in order to return fixed output
#return output

# Remove comments from this code block to alternate between input and output versions of each frame
#/*
return Interleave(
\ source
\ , output
\ )
#*/

#End Main Script Body

#------------------------
# Functions
#------------------------


#------------------------------------
# This function degrains interlaced footage

function MDegrain2i2(clip source, int "blocksize", int "over", int "denoising_strength", int "dct")
{
Vshift=0 # 2 lines per bobbed-field per tape generation (PAL); original=2; copy=4 etc
Hshift=0 # determine experimentally
overlap=default(over,0) # overlap value (0 to 4 for blksize=8)
dct=default(dct,0) # use dct=1 for clip with light flicker

fields=source.SeparateFields() # separate by fields

#This line gets rid of vertical chroma halo - use for VHS 2nd generation
#fields=MergeChroma(fields,crop(fields,Hshift,Vshift,0,0).addborders(0,0,Hshift,Vshift))

#This line will shift chroma down and to the right instead of up and to the left
#fields=MergeChroma(fields,Crop(AddBorders(fields,Hshift,Vshift,0,0),0,0,-Hshift,-Vshift))

prefiltered = RemoveGrain(fields,2)
superfilt = MSuper(prefiltered, hpad=32, vpad=32,pel=2)
super= MSuper(fields, hpad=32, vpad=32,pel=2)

halfblksize= (blocksize>4) ? blocksize/2 : 4
halfoverlap= (over>2) ? over/2 : 2

bvec1 = MAnalyse(superfilt, isb = true, delta = 2, blksize=blocksize, overlap=over,dct=dct)
bvec1 = MRecalculate(super, bvec1, blksize=halfblksize, overlap=halfoverlap,thSAD=100)

fvec1 = MAnalyse(super, isb = false, delta = 2, blksize=blocksize, overlap=over,dct=dct)
fvec1 = MRecalculate(super, fvec1, blksize=halfblksize, overlap=halfoverlap,thSAD=100)

bvec2 = MAnalyse(super, isb = true, delta = 4, blksize=blocksize, overlap=over,dct=dct)
bvec2 = MRecalculate(super, bvec2, blksize=halfblksize, overlap=halfoverlap,thSAD=100)

fvec2 = MAnalyse(super, isb = false, delta = 4, blksize=blocksize, overlap=over,dct=dct)
fvec2 = MRecalculate(super, fvec2, blksize=halfblksize, overlap=halfoverlap,thSAD=100)

denoised = fields.MDegrain2(super, bvec1,fvec1,bvec2,fvec2,thSAD=denoising_strength)

Weave(denoised)
} # End Function MDegrain2i2

#------------------------------------
#This function removes horizontal "tears" on interlaced video
function Remove_TearsI(clip source) {

ml = 100 # mask scale
scene_change = 200 # scene change

fields=source.SeparateFields()
even_fields=selecteven(fields)
odd_fields =selectodd(fields)

#=================
#Do Even Field Remove Tears
super_even = MSuper(even_fields,hpad=16, vpad=16, pel=2, sharp=1)

vbe = MAnalyse(super_even,isb = true, blksize=8,overlap=4,search=3,dct=0) # backward vectors
vfe = MAnalyse(super_even,isb = false, blksize=8,overlap=4,search=3,dct=0) # forward vectors

cbe = MFlow(even_fields,super_even,vbe,thSCD1=scene_change) # next compensated backward
cfe = MFlow(even_fields,super_even,vfe,thSCD1=scene_change) # previous compensated forward

sadfe = MMask(super_even,vfe, ml=100,kind=1,gamma=1, thSCD1 = scene_change)
msadfe= sadfe.Binarize() # binary inverted forward SAD mask

sadbe = MMask(super_even,vbe, ml=ml, gamma=1, kind=1, thSCD1 = scene_change) # backward SAD mask
msadbe= sadbe.Binarize() # binary inverted backward SAD mask

msade = Logic(msadfe,msadbe,"OR") # combined inverted SAD mask
msade = msade.Expand() # expanded inverted SAD mask
msadie = Interleave(msade, msade, msade) # interleaved 3-frame inverted SAD mask

Interleave(cfe,even_fields,cbe) # interleave forward compensated, source, and backward compensated

even_result_fields=DeSpot(show=0,tsmooth=0,extmask=msadie,p1percent=2,dilate=20,maxpts=0,p2=12,\
mthres=16,p1=24,pwidth=100,pheight=12,mwidth=7,mheight=5,merode=33,\
interlaced=false,seg=1,sign=0,ranked=true).SelectEvery(3,1)

#=================
#Do Odd Field Remove Tears
super_odd = MSuper(odd_fields,hpad=16, vpad=16, pel=2, sharp=1)

vbo = MAnalyse(super_odd,isb = true, blksize=8,overlap=4,search=3,dct=0) # backward vectors
vfo = MAnalyse(super_odd,isb = false, blksize=8,overlap=4,search=3,dct=0) # forward vectors

cbo = MFlow(odd_fields,super_odd,vbo,thSCD1=scene_change) # next compensated backward
cfo = MFlow(odd_fields,super_odd,vfo,thSCD1=scene_change) # previous compensated forward

sadfo = MMask(super_odd,vfo, ml=100,kind=1,gamma=1, thSCD1 = scene_change)
msadfo= sadfo.Binarize() # binary inverted forward SAD mask

sadbo = MMask(super_odd,vbo, ml=ml, gamma=1, kind=1, thSCD1 = scene_change) # backward SAD mask
msadbo= sadbo.Binarize() # binary inverted backward SAD mask

msado = Logic(msadfo,msadbo,"OR") # combined inverted SAD mask
msado = msado.Expand() # expanded inverted SAD mask
msadio = Interleave(msado, msado, msado) # interleaved 3-frame inverted SAD mask

Interleave(cfo,odd_fields,cbo) # interleave forward compensated, source, and backward compensated

odd_result_fields=DeSpot(show=0,tsmooth=0,extmask=msadio,p1percent=2,dilate=20,maxpts=0,p2=12,\
mthres=16,p1=24,pwidth=100,pheight=12,mwidth=7,mheight=5,merode=33,\
interlaced=false,seg=1,sign=0,ranked=true).SelectEvery(3,1)

#=================
#Put even and odd fields back together into interlaced video

interleave(even_result_fields,odd_result_fields)
weave()

} # End Function RemoveTearsI

feisty2
15th March 2017, 00:57
I guess you should probably use median filter for prefiltering, so removegrain(4) instead of removegrain(2)

johnmeyer
15th March 2017, 01:32
I guess you should probably use median filter for prefiltering, so removegrain(4) instead of removegrain(2)Hmmm ... that's only in the MDegreain2 code. However, I went ahead and tried it and much to my surprise, there was a slight change in how the tears were removed. The result was better on some frames, but worse on others. The fact that it changed at all is what is surprising because I didn't think that MDegrain2 was contributing to the tear removal.

Now I'm totally confused because, to figure out what is going on, I removed the call to my RemoveTearsI function, letting MDegrain2 work on its own. As I expected, there was no tear removal whatsoever.

So how the heck is a call in one function affecting what happens in another function? I know that AVISynth is quirky, but I thought it followed normal programming conventions where what goes on in Vegas stays in Vegas (i.e., what happens internally to one function does not spill over to other functions unless you declare global variable which, if you do, will get Gavino to call you on the carpet.

[edit]Even more confusing is that if I compare the results denoised with just the MDegrain2i2 function, one with Removegrain(2) and the other with Removegrain(4) there are a few differences, but they are so subtle as to make no difference at all.

johnmeyer
15th March 2017, 02:15
This is the same script, but written to make it easier to change things (I should have spent the ten minutes to do this before I started this thread): [edit] Corrected errors in Mrecalculate lines 7/12/2021

Loadplugin("C:\Program Files\AviSynth 2.5\plugins\MVTools\mvtools2.dll")
LoadPlugin("c:\Program Files\AviSynth 2.5\plugins\CNR\Cnr2.dll")
LoadPlugin("C:\Program Files\AviSynth 2.5\plugins\Film Restoration\Script_and_Plugins\RemoveGrainSSE2.dll")

source=AVISource("E:\fs.avi").killaudio().AssumeBFF().ConvertToYV12()

chroma=source.Cnr2("oxx",8,16,191,100,255,32,255,false)

output=MDegrain2i2(chroma,16,4,600,0)
output=Remove_TearsI(output)

#Remove comment and then add back comments to block below in order to return fixed output
#return output

# Remove comments from this code block to alternate between input and output versions of each frame
#/*
return Interleave(
\ source
\ , output
\ )
#*/

#End Main Script Body

#------------------------
# Functions
#------------------------


#------------------------------------
# This function degrains interlaced footage

function MDegrain2i2(clip source, int "blocksize", int "over", int "denoising_strength", int "dct")
{
Vshift=0 # 2 lines per bobbed-field per tape generation (PAL); original=2; copy=4 etc
Hshift=0 # determine experimentally
overlap=default(over,0) # overlap value (0 to 4 for blksize=8)
dct=default(dct,0) # use dct=1 for clip with light flicker

fields=source.SeparateFields() # separate by fields

#This line gets rid of vertical chroma halo - use for VHS 2nd generation
#fields=MergeChroma(fields,crop(fields,Hshift,Vshift,0,0).addborders(0,0,Hshift,Vshift))

#This line will shift chroma down and to the right instead of up and to the left
#fields=MergeChroma(fields,Crop(AddBorders(fields,Hshift,Vshift,0,0),0,0,-Hshift,-Vshift))

prefiltered = RemoveGrain(fields,4)
superfilt = MSuper(prefiltered, hpad=32, vpad=32,pel=2)
super= MSuper(fields, hpad=32, vpad=32,pel=2)

halfblksize= (blocksize>4) ? blocksize/2 : 4
halfoverlap= (over>2) ? over/2 : 2

bvec1 = MAnalyse(superfilt, isb = true, delta = 2, blksize=blocksize, overlap=over,dct=dct)
bvec1 = MRecalculate(super, bvec1, blksize=halfblksize, overlap=halfoverlap,thSAD=100)

fvec1 = MAnalyse(superfilt, isb = false, delta = 2, blksize=blocksize, overlap=over,dct=dct)
fvec1 = MRecalculate(super, fvec1, blksize=halfblksize, overlap=halfoverlap,thSAD=100)

bvec2 = MAnalyse(superfilt, isb = true, delta = 4, blksize=blocksize, overlap=over,dct=dct)
bvec2 = MRecalculate(super, bvec2, blksize=halfblksize, overlap=halfoverlap,thSAD=100)

fvec2 = MAnalyse(superfilt, isb = false, delta = 4, blksize=blocksize, overlap=over,dct=dct)
fvec2 = MRecalculate(super, fvec2, blksize=halfblksize, overlap=halfoverlap,thSAD=100)

denoised = fields.MDegrain2(super, bvec1,fvec1,bvec2,fvec2,thSAD=denoising_strength)

Weave(denoised)
} # End Function MDegrain2i2

#------------------------------------

#This is the interlaced front end to Remove_Tears

function Remove_TearsI(clip source)
{

ml = 100 # mask scale
scene_change = 200 # scene change

fields=source.SeparateFields()
even_fields=selecteven(fields)
odd_fields =selectodd(fields)

even_result_fields = RemoveTears(even_fields,ml,scene_change)
odd_result_fields = RemoveTears(odd_fields, ml,scene_change)

#=================
#Put even and odd fields back together into interlaced video

interleave(even_result_fields,odd_result_fields)
weave()

} # End Function RemoveTearsI


#This is the function that actually removes tears

function RemoveTears(clip source, int ml, int scene_change)
{
show=0
tsmooth=0
p1percent=2
dilate=20
maxpts=0
p2=12
mthres=16
p1=24
pwidth=100
pheight=12
mwidth=7
mheight=5
merode=33
interlaced=false
seg=1
sign=0
ranked=true

super = MSuper(source,hpad=16, vpad=16, pel=2, sharp=1)

vb = MAnalyse(super,isb = true, blksize=8,overlap=4,search=3,dct=0) # backward vectors
vf = MAnalyse(super,isb = false, blksize=8,overlap=4,search=3,dct=0) # forward vectors

cb = MFlow(source,super,vb,thSCD1=scene_change) # next compensated backward
cf = MFlow(source,super,vf,thSCD1=scene_change) # previous compensated forward

sadf = MMask(super,vf, ml=ml,kind=1,gamma=1, thSCD1 = scene_change)
msadf= sadf.Binarize() # binary inverted forward SAD mask

sadb = MMask(super,vb, ml=ml, gamma=1, kind=1, thSCD1 = scene_change) # backward SAD mask
msadb= sadb.Binarize() # binary inverted backward SAD mask

msad = Logic(msadf,msadb,"OR") # combined inverted SAD mask
msad = msad.Expand() # expanded inverted SAD mask
msadi = Interleave(msad, msad, msad) # interleaved 3-frame inverted SAD mask

Interleave(cf,source,cb) # interleave forward compensated, source, and backward compensated

DeSpot(show=show,extmask=msadi,tsmooth=tsmooth,p1percent=p1percent,dilate=dilate,maxpts=maxpts,p2=p2,\
mthres=mthres,p1=p1,pwidth=pwidth,pheight=pheight,mwidth=mwidth,mheight=mheight,merode=merode,\
interlaced=interlaced,seg=seg,sign=sign,ranked=ranked).SelectEvery(3,1)

}

real.finder
15th March 2017, 03:33
quick try


#This is the function that actually removes tears

function RemoveTears(clip source, int "ml", int "scene_change")
{
show=0
tsmooth=0
p1percent=2
dilate=20
maxpts=0
p2=12
mthres=16
p1=24
pwidth=100
pheight=12
mwidth=7
mheight=5
merode=33
interlaced=false
seg=1
sign=0
ranked=true

super = MSuper(source,hpad=16, vpad=16, pel=2, sharp=1)

vb = MAnalyse(super,isb = true, blksize=8,overlap=4,search=3,dct=0) # backward vectors
vf = MAnalyse(super,isb = false, blksize=8,overlap=4,search=3,dct=0) # forward vectors

cb = MFlow(source,super,vb,thSCD1=scene_change) # next compensated backward
cf = MFlow(source,super,vf,thSCD1=scene_change) # previous compensated forward

sadf = MMask(super,vf, ml=ml,kind=1,gamma=1, thSCD1 = scene_change)
msadf= sadf.Binarize() # binary inverted forward SAD mask

sadb = MMask(super,vb, ml=ml, gamma=1, kind=1, thSCD1 = scene_change) # backward SAD mask
msadb= sadb.Binarize() # binary inverted backward SAD mask

msad = Logic(msadf,msadb,"OR") # combined inverted SAD mask
msad = msad.Expand() # expanded inverted SAD mask
msadi = Interleave(msad, msad, msad) # interleaved 3-frame inverted SAD mask

Interleave(cf,source,cb) # interleave forward compensated, source, and backward compensated

DeSpot(show=show,extmask=msadi,tsmooth=tsmooth,p1percent=p1percent,dilate=dilate,maxpts=maxpts,p2=p2,\
mthres=mthres,p1=p1,pwidth=pwidth,pheight=pheight,mwidth=mwidth,mheight=mheight,merode=merode,\
interlaced=interlaced,seg=seg,sign=sign,ranked=ranked).SelectEvery(3,1)

}
sanimebob #or any faster like yadifmod2
RemoveDirtMC_SE(twopass=false,dct=4,mcthSAD=2800,resrepmode=16,repmode=0,clmode=0,rstr=1.5,PreNR=FFT3DFilter(sigma=4,plane=4,bw=16,bh=16,ow=8,oh=8,bt=3,wintype=1).ColorYUV(autogain=true).TemporalDeGrain_SE(5,9))
RemoveTears()

johnmeyer
15th March 2017, 04:00
Can't immediately find RemoveGrainT (one of the RemoveDirtMC_SE dependencies). I'm out of time today. I'll try again tomorrow.

StainlessS
15th March 2017, 09:58
For OP streaks,

Decomet (JMac698), or DePulse, DePulseC, DeVCR. (DePulseC, for chroma streaks only, DePulse/DePulseC YUY2 only).

DeComet,
https://forum.videohelp.com/threads/357149-Can-someone-create-a-comet-removal-Avisynth-plugin-please/page2
https://forum.doom9.org/showthread.php?t=163049

DePulse/DePulseC, DeVCR:- https://forum.doom9.org/showthread.php?t=151426

EDIT: DeVCR (link from above link is 404):- http://avisynth.nl/index.php/DeVCR

I Added some English doc in DePulse link above, original is Japanese.

DePulse 2005/04/10, 2005/04/11 update

Author Unknown

Function:
Dropout remover, removes white/black noise horizontal streaking in
analogue captures. Spatio-Temporal.

Usage instructions.

Assumes TFF (top field first) field order interlacing.

YUY2 only.

Syntax is: DePulse(clip, h=180[0-255], l=60[0-255], d=30[0-255], debug=false[true/false])


Pixel brightness values greater than h (high) are targeted as white noise.
If a pixel (t) is target'ed as white noise then surrounding pixels
(above, below, before and after [temporal], Not left and right) are tested
and if ALL of them are less than (t - d) the target pixel t is
depulsed.

Pixel brightness values less than l (low) are targeted as black noise.
If a pixel (t) is target'ed as black noise then surrounding pixels
(above, below, before and after [temporal], Not left and right) are tested and
if ALL of them are greater than (t + d) the target pixel t is
depulsed.

In debug mode, pixels judged to be black or white noise are coloured pink or green.


DePulseC, is EXACTLY same as above but fixes chroma YUY2 offsets 1 and 3 instead of Luma 0 and 2, not offset via chroma 128.

EDIT: There is also D. Graft DeClick, not sure if for same problem:- https://forum.doom9.org/showthread.php?t=52310

EDIT: Depulse/DepulseC/DeClick in one 7z archive:- http://www.mediafire.com/file/e56wv9l3bkki9eu/DeStuff.7z
DeVCR is a script.

EDIT: Depulse update to v1.00, here:- https://forum.doom9.org/showthread.php?p=1814102#post1814102

EDIT: (From memory) DePulse is quite effective, but requires pulses as 1 pixel height only.

EDIT: DeVCR() By Ricardo Garcia

function DetectVCRLines(clip c,int threshold)
{
spacial_data = GeneralConvolution(ConvertToRGB(c),0,"0 -1 0 0 2 0 0 -1 0")
bar_data = ConvertToRGB(BilinearResize(spacial_data,16,c.height))
st_data = Overlay(bar_data,Trim(bar_data,1,0),mode = "subtract")
st_data2 = Greyscale(Levels(st_data,threshold,10.0,threshold+1,0,255,coring = false))
st_data3 = Greyscale(Levels(st_data2,127,10.0,128,0,255,coring = false))
st_data4 = Overlay(st_data3,st_data3,y = -1, mode = "add")
return PointResize(st_data4,c.width,c.height)
}

function deVCR(clip c,int threshold)
{
mybars = DetectVCRLines(c,threshold)
return Overlay(c,Trim(c,1,0), mask = mybars,greymask = true)
}

Usage

myclip = AVISource("vcr.avi")
fixedclip = deVCR(myclip,30)
StackHorizontal(myclip,fixedclip,Overlay(myclip,fixedclip,mode = "subtract"))

Sharc
15th March 2017, 11:15
Is the uploaded 4-Second Test Clip unprocessed (i.e. before applying the script)? Or is one field fixed and the other field left untouched and interleaved?
There are significant quality differences between the 2 temporal different fields of the interlaced video.

StainlessS
15th March 2017, 11:19
Is the uploaded 4-Second Test Clip unprocessed

OP is John Meyer, it will be unprocessed.

Sharc
15th March 2017, 11:27
@johnmeyer:
If the uploaded clip is unprocessed: How important is the temporal resolution? Would dropping the bad fields and restoring the good fields be an option (base rate deinterlacing)

For the testclip something like:
Trim(0,26).bob().selectodd()+Trim(27,72).bob().selecteven()+Trim(73,84).bob().selectodd()+Trim(85,0).bob().selecteven()

VS_Fan
15th March 2017, 13:05
Let me start by the not so obvious: The corruption in your source is concentrated in one of the fields, not the whole frame. For some reason, the corrupted field is alternating: (look at the included script):

It starts in the bottom (first) field; then
at some point (an edit done by you?) it is at the top (second) field; but then
at some other point (another edit done by you?) it is back in the bottom (first) field; and so on
Then: It seems you intend to output an interlaced, but I strongly recommend to deinterlace, discarding the corrupted field. That way you keep the good fields, upscaled vertically by means of Bobbing or better with nnedi3. This way you get a progressive output, that could be cleaned using your preferred filters.
I personally use mDeGrain1 (motion compensated, radius 1), supplying it with a mildly pre-filtered input (dfttest). As the current version of mvtools doesn’t support 411 subsampling, I convert to 422 to preserve chroma, but at the end I convert to YV12 (420)
So your problem is not mvtools, but btw I recommend:

For mDegrain, I disable “truemotion”, the resulting vectors will be more “independent”, less “coherent”, which helps preserving details in moving objects.
With temporal radius = 1 (mDeGrain1) It is safer (than radius>1) using higher values for thSAD. It will preserve more details and “kill grain” than it would be with mDeGrain2.
P.D.: You could also enhance/ tweak the brightness, contrast, saturation and hue
This is a script with my suggestions for the sample you shared:
FFmpegSource2("Dropout Test Clip.avi")
#Bob(0,1)
nnedi3(field=2)
Odd = SelectOdd()
Even = SelectEven()
ret = trim(Odd,0,26)
ret = ret ++ trim(Even,27,72)
ret = ret ++ trim(Odd,73,84)
ret = ret ++ trim(Even,85,127)

ret = ConvertToYUV422(ret)

#ret = tweak()

bsize = 8
olap = bsize / 2
pref = dfttest(ret, sigma=2.0, tbsize=1)
super = mSuper(pref, rfilter=4)
backvectors = mAnalyse(super, isb=True, search=5, blksize=bsize, overlap=olap, truemotion=False, global=True)
forwvectors = mAnalyse(super, isb=False, search=5, blksize=bsize, overlap=olap, truemotion=False, global=True)
ret = mDeGrain1(ret, super, backvectors, forwvectors, thSAD=640)

ret = ConvertToYUV420(ret)

ret

Mounir
15th March 2017, 14:17
Try this little script it takes care of the black streaks extremely well on this video i think:
avisource()
assumebff()
SeparateFields()
noisy = last
nonoise = RemoveDirtMC()
diff = 5 # default = 10
m = mt_lutxy(noisy,nonoise,"y x - "+string(diff)+" - 255 *")
mt_merge(noisy,nonoise,m)
weave()

and for the chroma you need of course the magic of removespots + ccd, so:


AVISource("Dropout Test Clip.avi")
assumebff()
converttoyv12(interlaced=true)
SeparateFields()
noisy = last
nonoise = RemoveDirtMC()
diff = 5 #
m = mt_lutxy(noisy,nonoise,"y x - "+string(diff)+" - 255 *")
mt_merge(noisy,nonoise,m)
weave()
separatefields()
input=last
U = UtoY().RemoveSpotsMC()
V = VtoY().RemoveSpotsMC()
YtoUV(U, V, last)
weave()
ConverttoRGB32(matrix="rec601",interlaced=true)
separatefields()
#### CCD COLOR DENOISING :
LoadVirtualDubPlugin("Camcorder_Color_Denoise_sse2.vdf", "CCD", 0)
CCD(6,1) # default: 30
weave()
converttoyv12(matrix="Rec601",interlaced=true)

AVIL
15th March 2017, 17:11
@johnmeyer

IMHO 600 is a value too high for the parameter "denoising strengh":

output=MDegrain2i2(chroma,16,4,600,0)


Try with values about 200-250.

In general blocks with abrupt changes inter-frame don't be MC denoised.

johnmeyer
15th March 2017, 19:51
Sorry for the late response. My 8mm deck quit in the middle of this job, and I've been sidetracked getting it out for service.

The video posted in this thread is unprocessed video (thank you for your faith, StainlessS). I simply did a lossless cuts-only edit to create the test clip, something that is trivial to do with DV (Sony Vegas does it directly, something they call "smart rendering").

The video was captured from standard VHS cassettes playing in my somewhat unusual VHS deck, a JVC SR-VS30U which is a DV/VHS/S-VHS combo deck. It has both a VHS and also a DV transport built in, along with a Firewire/1394 output (as well as all the usual analog in/out connections). It has a TBC built in that actually seems to do more good than harm.

I usually run all video through a seperatefields() script just to look for field abnormalities. However, this is a huge project (about 50 tapes), and so I've got my head down trying to get through it before the wedding happens in three weeks. I just did a quick look, using seperatefields(), and I am absolutely flabbergasted that all of the junk is on just one of the fields. Thanks to everyone who spotted this!!

Even though I can't do a lot of testing until this afternoon, I quickly put all 14 hours of VHS from this second batch of captures on the timeline and frameserved it into a seperatefields() script. The good news: ALL of the other tapes, in SP, EP, and LP speeds (there was a mix of all three) had fields that exhibited normal noise characteristics, and the fields on these other tapes are NOT different from each other, unlike this one bad tape where one field is damaged and the other OK. Thus, the problem is "baked into" this one tape, and is not an equipment problem. Given that I am dealing with a failing 8mm deck, this was good news for me.

When I get back to this later today, I'll do some tests based on all the comments I've received so far, and see if I can get better results with one or more of the scripts and hints posted here. As for using only the one good field, that is certainly an option, especially given the general lack of detail on this damaged tape. It is, by far, the simplest option, but I'll give each of the other scripts a try before I do something.

I am assuming, now that I realize that I have a bad field issue, that some of the weirdness I encountered was related to that problem. I guess what I need to do is run my script (or use one of the other filters suggested by StainlessS and others) but only on the one bad field, and then combine this result with the good field.

Many, many thanks to everyone who replied. You've all been very helpful.

johnmeyer
16th March 2017, 07:48
It's almost midnight and I've been capturing the 8mm tapes with a Digital8 camcorder I bought so I can get through this job while my 8mm deck is being repaired. I'm posting this because the restoration job that is the subject of this thread just got easier. Yup, the bad VHS tape is a dub, and at least some of the original footage (how much, I don't know yet) is on the 8mm tapes I'm now transferring. The original 8mm video is just fine.

I have no idea how the dubbed tape ended up with a bad field (clogged head on the recording machine?), but it really doesn't matter.

I'll probably still have to do some restoration, because I don't think the client sent me every tape in the house, so all the help I've gotten here will not go to waste.

I don't do these projects often, but if you've never transferred 50+ tapes and tried to get through it all, especially when they are someone elses' tapes, it is quite a trip.