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
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