Log in

View Full Version : Removing Horizontal Black stripes and short lines from analog source videos


fonzzie31
5th December 2012, 13:26
Hello,

I'm working on digitizing and mastering some NTSC analog video materials (laserdiscs and 3/4inch tapes).

The video are mastering through a SNELL&WILCOX CVR450 TBC which permits also to do a conversion into PAL format.

So, after the digitize, I got a 720x576 PAL Interlaced 10Bits 4:2:2 Uncompressed (v210) video, with 48Khz 16Bits Stereo PCM audio track.

On the video, it remains some black stripes and short lines like that, I want to remove.

http://revival02.free.fr/pix/blines3.jpg

For that, I separate the fields of the video, with Revision FX Fieldskit on Adobe After Effects CS5, in order to get two different video files. One with all the top fields, one with all the bottom fields.

Then I use the following AVISYNTH script, provided by YUP (see thread http://forum.doom9.org/showthread.php?t=121197), in order to remove black stripes. I Run the script with avs2avi.

---------------------------------------

SetMemoryMax(384)
SetMTMode(3,2)
source=AVISource("videotest.avi")
#source file
SetMTMode(2,2)
source=source.ConvertToYV12(matrix="Rec601")
SetMTMode(4,2)
bobnn=source
SetMTMode(2,2)
# bobbing for better motion estimation, for 3 pixels thickness line became 5 pixels thickness, for 2 3, for 1 1
bobnnmed=bobnn.mt_luts(bobnn,mode="median",pixels=mt_rectangle(0,2),U=3,V=3)
# vertical median filter rectangle 5 for 3 pixels source (not bobbing) thikness line, 3 for 2, 1 for 1
bobnnmedf=bobnnmed.dfttest(tbsize=1,ftype=1,sbsize=8,sosize=6,sigma=1000,U=false,V=false)
#dft filter remove segregation after median vertical filter
bobnnf=bobnn#.dfttest(tbsize=1,ftype=1,sbsize=16,sosize=12,sigma=1000,U=false,V=false)
# motion estimation on filtered and compensating on source
super=MSuper(bobnn)
superf=MSuper(bobnnmedf,chroma=false)
superff=MSuper(bobnnf)
bw1 = MAnalyse(superf, blksize=16, isb = true, delta = 1, overlap=8, dct=5,chroma=false)
bw2 = MAnalyse(superf, blksize=16, isb = true, delta = 2, overlap=8, dct=5,chroma=false)
fw1 = MAnalyse(superf, blksize=16, isb = false,delta = 1, overlap=8, dct=5,chroma=false)
fw2 = MAnalyse(superf, blksize=16, isb = false,delta = 2, overlap=8, dct=5,chroma=false)
bc1 = MCompensate(bobnnf, super, bw1, thSAD=16000, thSCD1=16000)
bc2 = MCompensate(bobnnf, super, bw2, thSAD=16000, thSCD1=16000)
fc1 = MCompensate(bobnnf, super, fw1, thSAD=16000, thSCD1=16000)
fc2 = MCompensate(bobnnf, super, fw2, thSAD=16000, thSCD1=16000)
# calculation absolute difference between source and compensated
bwabs1=mt_lutxy(bobnnf,bc1,"x y - abs")#,U=-128,V=-128)
bwabs2=mt_lutxy(bobnnf,bc2,"x y - abs")#,U=-128,V=-128)
fwabs1=mt_lutxy(bobnnf,fc1,"x y - abs")#,U=-128,V=-128)
fwabs2=mt_lutxy(bobnnf,fc2,"x y - abs")#,U=-128,V=-128)
# calculation spike detection index for center, forwad and backward
SDIc=SDIAdapt(bwabs1,fwabs1)
SDIb=SDIAdapt(bwabs1,bwabs2)
SDIf=SDIAdapt(fwabs1,fwabs2)
# calculating SAD for filtered for estimation better choice for motion compensation
mb1sad=bobnnmedf.MMask(bw1,kind=1,ml=100,Ysc=255, thSCD1=16000)
mb2sad=bobnnmedf.MMask(bw2,kind=1,ml=100,Ysc=255, thSCD1=16000)
mf1sad=bobnnmedf.MMask(fw1,kind=1,ml=100,Ysc=255, thSCD1=16000)
mf2sad=bobnnmedf.MMask(fw2,kind=1,ml=100,Ysc=255, thSCD1=16000)
centersad=mt_logic(mf1sad,mb1sad,"max")#,U=2,V=2)
# maximum SAD from forward and bacward measure for quality center compensation
bwsad=mt_logic(mb1sad,mb2sad,"max")#,U=2,V=2)
#for bacward compensation
fwsad=mt_logic(mf1sad,mf2sad,"max")#,U=2,V=2)
#for forward compensation
centermask=mt_invert(centersad)
bwmask=mt_invert(bwsad)
fwmask=mt_invert(fwsad)
# inverting mask for using mt_merge
mcfcenter=clense(bc1, bobnn,fc1,increment=0, grey=false)
mcfbw=clense(bc1, bobnn,bc2,increment=0, grey=false)
mcffw=clense(fc1, bobnn,fc2,increment=0, grey=false)
# motion compensated median filtering for center, backward and forward compensation
#
# sort SAD for finding better motion compensated median filtering, this approach can use without SDI for pixels and

1 line thickness stripes
# center filtered
mt_logic(mt_logic(mt_lutxy(centersad,bwsad,"x y <= "), mt_lutxy(centersad,fwsad,"x y <="),"and"),mt_lut(centersad,"x

255 <="),"and")
maskcentersad=mt_lutxy(last,centersad,"x 255 y - 0 ?",U=3,V=3)
# backward filtered
mt_logic(mt_lutxy(bwsad,centersad,"x y < "), mt_lutxy(bwsad,fwsad,"x y <="),"and")
maskbwsad=mt_lutxy(last,bwsad,"x 255 y - 0 ?",U=3,V=3)
# forward filtered
mt_logic(mt_lutxy(fwsad,centersad,"x y < "), mt_lutxy(fwsad,bwsad,"x y < "),"and")
maskfwsad=mt_lutxy(last,fwsad,"x 255 y - 0 ?",U=3,V=3)
#
mcfmaskedsad=mt_merge(bobnnmed,mcfbw,maskbwsad,luma=true)
mcfmaskedsad=mt_merge(mcfmaskedsad,mcffw,maskfwsad,luma=true)
mcfcentersad=mt_merge(mcfmaskedsad,mcfcenter,maskcentersad,luma=true)
#best value based on 3 motion compensated median time filterd value and spatial filtered
# where motion compensation bad, work only for one frame with stripe
mcfbwsad=mt_merge(bobnnmed,mcfbw,maskbwsad,luma=true)
mcffwsad=mt_merge(bobnnmed,mcffw,maskfwsad,luma=true)
# backward and forward compensated full for SDI approach
#end sort SAD
#

#
# sort SDI for choose center, forward or forward compensation work with 2 sequential frames, for 3 need 2 pass

filtering, for 4 3
# center SDI
maskcentersdi=mt_logic(mt_logic(mt_lutxy(SDIc,SDIb,"x y >= "), mt_lutxy(SDIc,SDIf,"x y

>="),"and"),SDIc,"and").mt_lut("x 255 0 ?")
# backward SDI
maskbwsdi=mt_logic(mt_lutxy(SDIb,SDIc,"x y > "), mt_lutxy(SDIb,SDIf,"x y >="),"and").mt_lut("x 255 0 ?")
# forward SDI
maskfwsdi=mt_logic(mt_lutxy(SDIf,SDIc,"x y > "), mt_lutxy(SDIf,SDIb,"x y >"),"and").mt_lut("x 255 0 ?")
#
maskedsdi=mt_merge(bobnn,mcfcentersad,maskcentersdi,luma=true)
maskedsdi=mt_merge(maskedsdi,mcffwsad,maskfwsdi,luma=true)
maskedsdi=mt_merge(maskedsdi,mcfbwsad,maskbwsdi,luma=true)
#end sort SDI
#


# this place for repair now I am thinking

#fieldmaskedsdi=maskedsdi.AssumeTFF().SeparateFields().SelectEvery(4,0,3)
#fieldmaskedsdi=SDIc.AssumeTFF().SeparateFields().SelectEvery(4,0,3)
#StackVertical(Crop(bobnn,12,64,-22,-54),Crop(maskedsdi,12,64,-22,-54))
#StackVertical(Separatefields(source),mt_makediff(fieldmaskedsdi,Separatefields(source)))
#Weave(fieldmaskedsdi)
maskedsdi



# some kind comparing and weave for interlaced source, may be at double rate and for second pass not need bobbing

# Spike Detection Function
#

function SDIAdapt(clip b1,clip b2)
{
threshsp=10
# threshold for pixels value absolute difference
THP=string(threshsp)
threshavg=2
# threshold how much times actual SDI greater avearage and decimated SDI for remove false alarm for bad

motion compensation
THAVG=string(threshavg)
SDI=mt_lutxy(b1,b2,"x "+THP+" > y "+THP+" > | 1 x y - x y + / abs - 255 * 0 ? ")
# SDI calculation
SDIavg=SDI.mt_luts(SDI,mode="avg",pixels=mt_square(5))
# SDI averaging
SDIAvgblk=SDIavg.PointResize(90,72)
# decimation
SDIavgblkX=SDIAvgblk.mt_luts(SDIAvgblk,mode="median",pixels="1 -1 0 0 -1 1 1 1 -1 -1")
# median point at X position
SDIavgblkCross=SDIAvgblk.mt_luts(SDIAvgblk,mode="median",pixels="1 0 0 0 -1 0 0 1 0 -1")
#median point at Cross position
SDIAvgblk=clense(SDIAvgblk,SDIavgblkCross,SDIavgblkX,increment=0, grey=true).mt_lut("x")
# multi median filtering for remove false alarm SDI
SDIavg=SDIAvgblk.PointResize(720,576)
# backward resize to original frame size
SDIad=mt_lutxy(SDI,SDIavg,"x "+THAVG+" y * > x 128 > & 255 0 ?")
# comparing with actual SDI and if greater it is real spike, value 128 can increase to 192
# be carefull I can not find this at original paper, big value can loose spike, small false alarm
SDImask=SDIad.mt_luts(SDIad,mode="median",pixels=mt_rectangle(0,1))
# rectangle size could one less than for bobnnmed
SDImask=SDIad#.mt_luts(SDIad,mode="median",pixels=mt_rectangle(4,0))
# remove line shorter depend Your source
# SDImaskleft=SDImask.mt_luts(SDIad,mode="max",pixels="0 0 -1 0 -2 0 -3 0 -4 0 -5 0 -6 0 -7 0 -8 0 -9 0 -10 0

-11 0")
# try join single short spike to long 11 distance between spikes from left side
# SDImaskright=SDImask.mt_luts(SDIad,mode="max",pixels="0 0 1 0 2 0 3 0 4 0 5 0 6 0 7 0 8 0 9 0 10 0 11 0")
# from right
# SDIMask=mt_logic(SDImaskleft,SDImaskright,"min")
# if pixels exist from left and right simultanessly this spike for detections series short spikes


SDImask=SDImask.mt_luts(SDImask,mode="min",pixels=mt_rectangle(5,0))
# remove spikes shorter than 11 pixels tune for source
SDImask=SDImask.HorlLinExp()
# if line have only one pixel line will be filled


SDIad=mt_logic(SDIad,SDImask,"min")
# combination source and adapting SDI for better robustness


# SDIad=SDIad.mt_luts(SDIad,mode="median",pixels=mt_rectangle(0,1))
# refinement rectangle size could one less than for bobnnmed


return(SDIad)

}

# fill full horizontal line if exist one pixel per line
# basaed on Didee idea se masktools thread
function HorlLinExp(clip mask1) {

mask2 = mask1.mt_lut(Y=-255, u=-128, v=-128)
black = mask1.mt_lut(Y=0, u=-128, v=-128)

v1=interleave(mask1,black).assumefieldbased.assumetff.weave
v2=interleave(mask2,black).assumefieldbased.assumetff.weave

mt_hysteresis(v1,v2, u=-128, v=-128)
separatefields.selecteven.assumeframebased
}

---------------------------------------

But I've some problems with the script.

This script does not remove all the black stripes, even after 5-6 passes.
For a weird reason, it produces also artefacts on some frames, especially in transitions between two scenes or on fast movements. Those frames might not contain any stripes or short lines to remove.

If i separate the video fields on After Effects or on Avisyth, I got artefacts on result.

Please find below some frames examples

Sample 1 :
----------
- The source frame :

http://revival02.free.fr/pix/bananarama2s.jpg

- The result :

http://revival02.free.fr/pix/bananarama2r.jpg


Sample 2 :
----------
- The source frame :

http://revival02.free.fr/pix/bananarama3s.jpg

- The result :

http://revival02.free.fr/pix/bananarama3r.jpg


Sample 3 :
----------
- The source frame :

http://revival02.free.fr/pix/bananarama4s.jpg

- The result :

http://revival02.free.fr/pix/bananarama4r.jpg


So, I would like to know how to improve my script...
Is there more efficient Avisynth method to remove black stripes ?
I'm not an Avisynth specialist, and I hope you would help me :)

Awaiting your suggestions and your questions...

scharfis_brain
5th December 2012, 19:44
your NTSC to PAL conversion effectively harms any restoration process!
capture your Tape as it is without standards conversion involved.

StainlessS
6th December 2012, 01:50
@Scharfis_Brain, Would you mind if I made available a (pretty bad) translation of your Exotic Interlacing page (whatever it's called),
I did a goggle translate (~2 years back, not seen you here very often since) and fixed what I thought were a few unclear bits, but
my German is not too clever (un-tutored) and so is not very good.
If OK, I shall put up on Mediafire and if you so wish, make some corrections and re-post on your own page, I tried my best to
correct problems in the original translation (google) that screwed up alignment in the pictograms.

If not OK, then I shall keep it closed.

EDIT: Sorry fonzzie31, a little off topic.

EDIT: If I can find it.

EDIT: Exotic Interlacing by Scharfis_Brain, English translation @ MediaFire in sig below this post, in the DATA folder.

yup
6th December 2012, 05:55
Hi fonzzie31!

Welcome back!
I little improve script at summer time. See my thread. Script oriented for interlace source. But Your problem also relate to conversion NTSC to PAL or vice versa. Try pure interlaced source.
yup.