Log in

View Full Version : Need help identifying and fixing VHS artifact


zambelli
2nd December 2012, 21:35
I'm cleaning up some old home video tapes which, for reasons that we shall attribute to being young and foolish, were created through this unfortunate workflow:

1. Shot on MiniDV (PAL)
2. Recorded to VHS (PAL) via composite
3. Recorded back to MiniDV (PAL) via composite

So now I have to deal with the joyous marriage of analog and digital artifacts. Hooray. :)

Anyway, I'm noticing a strange aliasing effect that occurs typically around the borders of red and green surfaces, as seen in the following screenshots:

Field example:
http://alexzambelli.com/images/Ski_Frame_1_field.png

If this were a purely digital workflow that went through multiple video apps and a myriad of interlaced/progressive filters, I would immediately suspect it's some form of chroma sampling bug. However, the workflow was a straightforward digital-->analog-->digital conversion without any digital editing, so the only place where a chroma bug could've crept in would've been in the D/A or the A/D circuits of the camcorder. Certainly possible, but seems less likely.

The AVI I'm working with is a DV PAL AVI, so its YUV is already in 4:2:0. I'm using Cedocida DV v0.2.3 codec to decode the video (with output option set to default YV12 MPEG-2 interlaced). The screenshots were taken in VirtualDub from a script that looks like this:
AVISource("Ski 1999 Part 1.avi")
AssumeBFF()
SeparateFields()
ConvertToRGB24() # for VDub's sake

I see the same artifact in SeparateFields output, Bob output, or the output of any advanced deinterlacer.

Any ideas what could have caused this and how to fix it?

Guest
2nd December 2012, 23:03
It's your standard dot-crawl (cross luma), IMHO. Somewhere in your chain was a comb filter and not a very good one, it seems. Anywhere you carry signal on a composite connection you introduce this possibility.

zambelli
2nd December 2012, 23:24
It's your standard dot-crawl (cross luma), IMHO. Somewhere in your chain was a comb filter and not a very good one, it seems. Anywhere you carry signal on a composite connection you introduce this possibility.
Ah, good call, thanks! I've been spending too much time in digital processing lately, I forgot how to recognize all the nasty analog bugs, even the simple ones. :)

Any recommendations for good dot crawl removal plugin?

Mounir
2nd December 2012, 23:25
2nd generation copy hmm that should give you plenty of work
I can see the dot crawls /herring bones and possibly a color registration problem ; colors tend to shift to the right and down when you make copies but we need the actual footage to be certain

Guest
3rd December 2012, 03:22
I agree with Mounir, please post a video source sample.

And look for a better capture solution that does a better job of luma/chroma separation of the composite signal. As in all things, you get what you pay for.

zambelli
3rd December 2012, 04:34
I agree with Mounir, please post a video source sample.

No problem, here you go: http://alexzambelli.com/video/Ski_sample.avi. Thank you!

And look for a better capture solution that does a better job of luma/chroma separation of the composite signal. As in all things, you get what you pay for.
Unfortunately, that's not possible. These 2nd gen DV tapes are all I've got, the DV-->VHS-->DV capture was done over a decade ago. Hence I have to salvage as much as I can.

Lynx Pardinus
3rd December 2012, 08:30
Iīve found that FFTQuiver works very well with PAL dot crawl. I normally use:

F1Quiver(last, "gn", 336, 10, test=false)

Basically it applies a notch filter centered in the chroma carrier. The only drwback is it blurs a bit and increases the ringing.

In your case, being a second generation, I am not sure if itīll work.

2Bdecided
3rd December 2012, 16:03
As Mounir suggested, it looks much better if you re-align the chroma with the luma...

avisource("Ski_sample.avi")
#blur(1.0,0.0)
F1Quiver(last, "gn", 336, 10, test=false)
Vshift=4
Hshift=0
MergeChroma(last.crop(Hshift,Vshift,0,0).addborders(0,0,Hshift,Vshift))
bob()

Cheers,
David.

Mounir
3rd December 2012, 17:29
I agree with the -4 for the vertical shift but it seems there is a horizontal shift aswell to the left (oddly enough) so i propose
A=Last
B=A.Greyscale()
Overlay(B,A,X=4,Y=-4,Mode="Chroma")

But the sample provided is not really helping imo you should upload one with a person so it's easier to spot the colors shift of a red shirt for example

zambelli
6th December 2012, 10:06
Thanks Mounir and 2BDecided, that's very helpful. Looks like there is indeed a chroma shift present. I compared a few values and it looks like the chroma needs to be horizontally shifted by 2 and vertically by -4.

Question: In your code example you applied F1Quiver before the chroma shift. Shouldn't the chroma shift be applied before the notch filter?

Also, since this is an interlaced video imported in YV12, is it OK to apply the chroma shift in interlaced YV12? Or should I perhaps upsample to YUY2 before shifting chroma?

zambelli
6th December 2012, 10:18
Here is a second video clip, this one should make testing a lot easier:
http://alexzambelli.com/video/Ski_sample_2.avi

Mounir
6th December 2012, 14:47
Based on this 2nd sample the Vshift is -5, not sure about the hshift though
Try this script:
http://www.mediafire.com/?1wdfwgr2shm67l8

Mounir
6th December 2012, 17:17
i've forgotten the F1Quiver(last, "gn", 336, 10, test=false) part put it before the limitedsharpen part, it seems to work great

2Bdecided
6th December 2012, 18:09
Thanks Mounir and 2BDecided, that's very helpful. Looks like there is indeed a chroma shift present. I compared a few values and it looks like the chroma needs to be horizontally shifted by 2 and vertically by -4.

Question: In your code example you applied F1Quiver before the chroma shift. Shouldn't the chroma shift be applied before the notch filter?If it's just a simple notch filter, it won't make any difference - shifting and linear filtering are both linear operations, so the order doesn't matter. However, if you use something more complex, you should absolutely apply the dot crawl filter before messing with the video because the dot crawl wouldn't match the pattern the filter was expecting if you had moved it first.

Also, since this is an interlaced video imported in YV12, is it OK to apply the chroma shift in interlaced YV12? Or should I perhaps upsample to YUY2 before shifting chroma?Assuming the output will be YV12, I don't think you'll gain anything.

IIRC for interlaced video frames the shift must be mod2 or mod4 (depending whether you count luma lines, or YV12 chroma lines) otherwise you'll move the chroma to the other field. You can always bob the video and shift by whatever value you like (though cropping yv12 has restrictions - there are other ways to shift if you must: either going via YUY2 or using resizers to crop etc).

Cheers,
David.

2Bdecided
6th December 2012, 18:36
Way back when I used to use something like this...

avisource("Ski_sample_2.avi")
#blur(1.0,0.0)
F1Quiver(last, "gn", 336, 10, test=false)
Vshift=4
Hshift=2
MergeChroma(last.crop(0,Vshift,-Hshift,0).addborders(Hshift,0,0,Vshift))
#qtgmc()
Bob(0.0,1.0)

#return last
a=last

super = MSuper(pel=2, sharp=1)
backward_vec2 = MAnalyse(super, isb = true, delta = 2, overlap=4)
backward_vec1 = MAnalyse(super, isb = true, delta = 1, overlap=4)
forward_vec1 = MAnalyse(super, isb = false, delta = 1, overlap=4)
forward_vec2 = MAnalyse(super, isb = false, delta = 2, overlap=4)
MDegrain2(super, backward_vec1,forward_vec1,backward_vec2,forward_vec2,thSAD=400)

addborders(16,16,16,16)
# warp horrible fuzzy chroma to match nice sharp luma
c=aWarpSharp(depth=20.0, thresh=0.75, blurlevel=2, cm=1)

# put a little of the original noise back in to avoid "plastic" look
overlay(a.sharpen(0.5,0.0).addborders(16,16,16,16),opacity=0.5) # 0.5 keeps 50% noise, 0.25 keeps 25% noise etc

# completely replace chroma with nice version
mergechroma(c)

crop(16,16,-16,-16)

#re-interlace
assumetff().separatefields().selectevery(4,0,3).weave()


Haven't compared it with Mounir's script though.

Cheers,
David.

zambelli
7th December 2012, 09:35
Thanks for the replies, guys. I was working on this last night before I saw your replies so I ended up going in a slightly different direction. Would you mind taking a quick look and seeing if this looks like a reasonable script?

Also, 2Bdecided, if you've got two minutes of time, could you process Ski_sample_2.avi with your script, save it to Lagarith or Huffy and upload it somewhere so I can compare it to my output?

Here's what I came up with last night:

function Levels_Smooth(clip clp, int min_before, float gamma, int max_before, int min_after, int max_after)
{
pc2tv = clp.Levels(min_before, gamma, max_before, min_after, max_after, coring=false)
pc2tvd=yv12lutxy(clp,pc2tv,"x y - 4 * 128 +","x y - 4 * 128 +","x y - 4 * 128 +",U=3,V=3)
yv12lutxy(clp,pc2tvd.removegrain(mode=19),"x y 128 - 4 / -","x y 128 - 4 / -","x y 128 - 4 / -",U=3,V=3)
MergeChroma(clp)
}

# import using Cedocida in YV12 mode (PAL)
AVISource("Ski.avi")

# upsample to 4:2:2 interlaced using Gavino's improved method (http://forum.doom9.org/showthread.php?t=147629)
YV12ToYUY2i()

# shift chroma +2 horizontally and -4 vertically
chroma = last
Overlay(chroma.Greyscale(), chroma, X=2, Y=-4, Mode="Chroma")

# crop out horizontal overscan
Crop(8,0,704,576)

# run notch filter to remove dot crawl
F1Quiver(last, "gn", 336, 10)

# stabilize analog chroma with CNR2
# I run it on separated fields because I don't think CNR works with interlaced video
AssumeBFF()
SeparateFields()
CNR2("xxx")
Weave()

# downsample back to 4:2:0 interlaced using Gavino's method - YV12 needed for Levels_Smooth and QTGMC
YUY2ToYV12i()

# the luma in the source video looks blown out in the top range so I manually normalize it back to Y=235
# I confirmed in A/B comparisons that this restores visible detail in the top luma range
Levels_Smooth(0,1.0,255,0,235)

AssumeBFF()

# remove remaining chroma noise
FFT3DFilter(sigma=8, plane=3, bt=5, interlaced=true, ncpu=4)

# deinterlace
QTGMC(preset="slow")

# denoise/sharpen using Didee's SeeSaw script
original = last
denoised = FFT3DFilter(sigma=2.5, plane=0, bt=5, ncpu=4)
SeeSaw(original, denoised, NRlimit=3, Sstr=1.5, Spower=5, Szp=16)
MergeChroma(denoised)

# add artificial grain to cover up any remaining analog luma noise
AddGrain(3, 0, 0)


My end result looks like this:
http://alexzambelli.com/video/Ski_sample_2_processed.avi
(reduced to 25 fps with SelectEven and compressed with Lagarith)

SeeMoreDigital
7th December 2012, 11:01
Out of interest...

Could any further improvement be made by turning each interlaced field into a progressive frame?

Mounir
7th December 2012, 11:25
You do it all wrong imo zambelli, if you want progressive then deinterlace first (actually, chroma shift, F1Quiver (separate the fields) first then deinterlace) the 4.2.2 step seems not necessary and then a couple of other things like a crazy high sigma of 8(!) when your footage actually don't need much chroma denoise.
Try my script really
Edit:
Actually there is one good thing the "chroma stabilisation" (but it creates ghosting) and it seems i can't replicate that in my script so far
Edit2:
Ok i've found out what was wrong you must use separatefields+weave for Overlay(B,A,X=2,Y=-2,Mode="Chroma")

Mounir
7th December 2012, 15:05
Video result (lagarith)
http://www.mediafire.com/?0t2spyerhmpcb22

My final script:
http://www.mediafire.com/?0t2spyerhmpcb22

zambelli
7th December 2012, 19:24
You do it all wrong imo zambelli
Well, it can't be ALL wrong because I'm definitely getting some good results out of my script. :) We're arguing fine-tuning at this point.

if you want progressive then deinterlace first (actually, chroma shift, F1Quiver (separate the fields) first then deinterlace)

That's not really all that different from what I'm doing. The reason I'm trying to clean up chroma (CNR2, FFT3DFilter(plane=3)) *before* deinterlacing is because most of that chroma noise is analog noise and therefore I don't want it to get in the way of the deinterlacer. QTGMC does its own interpolation and degraining so my thinking was that I should attack chroma noise while it was still "raw" and unchanged by QTGMC.

the 4.2.2 step seems not necessary and then a couple of other things like a crazy high sigma of 8(!) when your footage actually don't need much chroma denoise.
The FFT3Dfilter chroma denoising is pretty subtle. You're right - I probably don't need quite a high sigma as 8, but I didn't notice it having an adverse effect either so I left it high.


Ok i've found out what was wrong you must use separatefields+weave for Overlay(B,A,X=2,Y=-2,Mode="Chroma")
I think the vertical chroma shift in my video is more like -4, but I see your point.

Now, you seem to suggest that F1Quiver should be run on separated fields instead of interlaced video. Why is that? 2Bdecided, in his example, used it on interlaced video. Also, it was my understanding that in analog video the comb filter is applied on a frame basis, not a field basis, so shouldn't a notch filter be applied the same?

My final script:
http://www.mediafire.com/?0t2spyerhmpcb22
I think there's a typo in the URL - it's the same as the AVI download URL, so I couldn't download your script.

Mounir
7th December 2012, 19:49
Sorry url for the final script: http://www.mediafire.com/download.php?ajnkaw446p07d9q

2Bdecided
10th December 2012, 19:33
Now, you seem to suggest that F1Quiver should be run on separated fields instead of interlaced video. Why is that? 2Bdecided, in his example, used it on interlaced video.I assumed it was only filtering in the horizontal direction (which is what it should be doing), so fields/frames/interlaced/whatever won't make any difference - I didn't check though.

Cheers,
David.