4.2 Postprocessing the video using AviSynth

There are several advantages using AviSynth instead of Virtualdub:

We will use VirtualdubMod instead of VirtualDub because
  1. VirtualdubMod can process YV12 (necessary if you use AviSynth v2.5)
  2. VirtualdubMod has a script editor (necessary when doing color corrections)
More information about AviSynth and its filters can be found here.

Determine cuts in VirtualdubMod:
Often you want to cut parts of your capture (for example to cut out commercials). An easy way to do this is to make an avs file (also called script), loading your captured avi:

AviSource("d:\capture.avi")

or if you captured in segments:

SegmentedAVISource("d:\capture.avi")

where the filenames are given by d:\capture.00.avi, d:\capture.01.avi and so on through d:\capture.99.avi. Open the avs file in VirtualdubMod and delete sections of your clip (select a range, and press del). Open the Script editor (under the Tools tab), and  under the edit-tab select "Import Frameset as Trims". Your script should look like this (if the lines are switched, switch them back).

AviSource("c:\lopez-Jenny_from_the_bronx.avi")
Trim(0,3) ++ Trim(8,31) ++ Trim(43,101)

You can also use a utility called vcf2avs. It converts your processing file (vcf) to an avs file including the trims. For more information see AviSynth Q&A or vcf2avs by Dark$oul71 (discussion and download) and vcf2avs by bb (discussion and download). Btw those are not the same programs. They just have been developed at the same time and therefore somehow carry the same name!
If you do it manually, remember that VirtualdubMod starts counting at frame one, while AviSynth starts counting at frame zero. For example: Suppose you want to keep the frames 1-2000, your script becomes:

AviSource("d:\capture.avi")
Trim(0, 1999)

or if you want to cut something in the middle (for example you want to remove the frames 100-200)::

clip=AviSource("d:\capture.avi")
Trim(clip, 0, 99)+Trim(clip, 201, 1999)

or if you captured in segments:

SegmentedAVISource("d:\capture.avi")
Trim(0, 1999)

Removing Rainbow effects and dot crawls:
Your clip can have rainbows or dot crawls. Rainbows are caused by imperfect separation of the luma and chroma components of a composite video signal. It is most noticeable on computer generated images like subtitles, weather maps, and stationary logos. Whenever you have an edge (= high frequencies) in luma, you have rainbows, even faint. Whenever you have a great change in chroma (typically logos), you have dot crawls. For details, have a look at removing rainbow effects and dot crawls.

Deinterlacing:
When capturing from television you have to decide whether you want to deinterlace or not. You should look at the following considerations:

In case you have chosen not to deinterlace, see the optional part: "processing interlaced video". Here it is assumed that the video needs to be deinterlaced:

You should do this first before any smoothing, cropping or resizing. First you have to determine what kind of interlacing effect your source has. Note this is different for every broadcaster, and sometimes broadcasting. This implies you have to do this procedure every time you captured something. There are several cases:

PAL:
1) phase shifts and or fields in a reversed order (= swapped fields) [for more information, see readme of the Decomb plugin]
2) trully interlaced
3) bad NTSC to PAL conversions

It might be that the deinterlacer you want to use doesn't work in the color format of your capture. Decomb for example requires YUY2 or YV12. If you have this problem you should convert the color format on the fields instead of the clip itself, for example:

AviSource("d:\capture.avi")  # assuming source is RGB
Trim(begin, end)
SeparateFields()
ConvertToYUY2()
Weave()

To decide which case you are dealing adjust your avs file, such that the fields can be seen separately:

AviSource("d:\capture.avi")  # assuming source is YUY2
Trim(begin, end)
SeparateFields()

and open it in VirtualdubMod. Right click on the left (source) clip and select "2x size" to enlarge the clip, and move the slider frame by frame (in a small range of frames, say 20). If you hardly see any horizontal lines in both fields, it means we are dealing with case 1. If you still see horizontal lines we are dealing with case 2. If you see blended frames (also called ghosting) it means you are dealing with case 3.

1) In the first case the following script takes care of the interlacing effects (using the Decomb plugin):

AviSource("d:\capture.avi")
Trim(begin, end)
Telecide()

2) In the second case you can try the following script (using the Decomb plugin):

AviSource("d:\capture.avi")
Trim(begin, end)
FieldDeinterlace()

or if some frames are trully interlaced:

AviSource("d:\capture.avi")
Trim(begin, end)
FieldDeinterlace(full=false)

But you also might want to try similar deinterlacers like GreedyHMA or TomsMoComp. If your aren't satisfied with the quality (and are using AviSynth v2.5x) you might want to consider a slower deinterlacer "Area based deinterlacer". Note this deinterlacer requires RGB32.

3) In this case you have bad luck. Best is to complain to your broadcasting station :) If the blended fields are mainly present in one of the two fields you can throw away one field and "reduce" the other field by using the following script:

AviSource("d:\capture.avi")
Trim(begin, end)
SeparateFields()
SelectEven()  # or SelectOdd() if you want the odd frames
HorizontalReduceBy2()

You will loose information, but it's better then the presence of blended fields. Note that your clip is scaled (vertically and horizontally) by a factor of two. Finally, if blended fields are presents in both fields, you are out of luck. There is no satisfying way of treating this kind of material. You have basically three choises:

a) Don't deinterlace, see "processing interlaced video".

b) Treat your clip as totally interlaced and use the following script:

AviSource("d:\capture.avi")
Trim(begin, end)
FieldDeinterlace()

c) Use a Bob:

Bob()
SelectEven()

or for better quality, a smart Bob (using the plugin SmoothDeinterlace for example):

 # SmoothBob uses Smoothdeinterlace to bob the video.
 # Parameter Topfirst = 1 (default) will use the top field as the first field
 #                                 0 will use the bottom field as first field

function SmoothBob(clip clip, int "Topfirst")
{
  t = default(Topfirst, 1)
  top = (t==1) ? true : false
  clip.SmoothDeinterlace(tff=true, doublerate=true)
}

 # SmoothDeinterlace requires YUY2:
SeparateFields()
ConvertToYUY2()
Weave()
SmoothBob()
SelectEven()

NTSC:
1) telecined material (29.97 fps)
2) phase shifts and or swapped fields (23.976 fps)
3) trully interlaced (29.97 fps)

It might be that the deinterlacer you want to use doesn't work in the color format of your capture. Decomb for example requires YUY2 or YV12. If you have this problem you should convert the color format on the fields instead of the clip itself, for example:

AviSource("d:\capture.avi")  # assuming source is RGB
Trim(begin, end)
SeparateFields()
ConvertToYUY2()
Weave()

1) First check whether the clip is telecined. Open the avs file in VirtualdubMod:

AviSource("d:\capture.avi")  # assuming source is YUY2
Trim(begin, end)

If in every five frames you see three progressive and two interlaced frames you have a telecined clip. In that case people perform an InVerse TeleCine (IVTC):

AviSource("d:\capture.avi")
Trim(begin, end)
Telecide()
Decimate(cycle=5)

For the other two cases, adjust your avs file to see the fields separately:

AviSource("d:\capture.avi")
Trim(begin, end)
SeparateFields()

and open it in VirtualdubMod. Right click on the left (source) clip and select "2x size" to enlarge the clip. Move the slider slowely (in a small range of frames, say 20). If you see hardly any horizontal lines in both fields, it means we are dealing with case 2. If you see horizontal lines we are dealing with case 3.

2) In the first case the following script takes care of the interlace effects (using the Decomb plugin):

AviSource("d:\capture.avi")
Trim(begin, end)
Telecide()

3) In the third case you can try the following script (using the Decomb plugin):

AviSource("d:\capture.avi")
Trim(begin, end)
FieldDeinterlace()

or if some frames are trully interlaced:

AviSource("d:\capture.avi")
Trim(begin, end)
FieldDeinterlace(full=false)

But you also might want to try similar deinterlacers like GreedyHMA or TomsMoComp. If your aren't satisfied with the quality (and are using AviSynth v2.5x) you might want to consider a slower deinterlacer "Area based deinterlacer". Note this deinterlacer requires RGB32.

Above deinterlacing scripts are assuming that your original clip is YUY2 (or YV12), because Decomb requires YUY2 (or YV12). If your clip is RGB you should adjust your script, for example like this:

AviSource("d:\capture.avi")
Trim(begin, end)
SeparateFields()
ConvertToYUY2()
Weave()
Telecide()

References:
IVTC tutorial: More about IVTC and deinterlace stuff.
Decomb, GreedyHMA and other deinterlacers: Here you can find plugins for AviSynth v2.0x and v2.5x.

Correcting chroma shifts:
Sometimes the capture of vhs-recordings can have chromashifts (also called color bleeding). This means that one or some colors are shifted in a direction. It is possible to correct for this using the ChromaShift plugin. For details, have a look at Correcting chroma shifts!

Removing a Logo:
Don't expect miracles! If the logo is very small, it is possible to get reasonable results. Personally I wouldn't remove any logo, because you will always see removal artefacts. For sure, I don't recommend it for people that are new to the capturing scene. For details see the Logo removal excursion!

Removing garbage at the bottom (and top) of the clip:
Usually you will see (about) ten lines of garbage at the bottom of your captured clip (something you will also a few garbage at the top of your captured clip). (Look here for an explanation.) Since this is ugly we should remove it and replace it with a black bar. So your script becomes for example (assuming PAL):

LoadPlugin("c:\LoadPluginEx.dll")
LoadPlugin("c:\AviSynth2\plugins\dustv5.dll")
AviSource("d:\capture.avi")
Trim(begin, end)
Telecide()
LetterBox(0, 16)  # You can also block out the left and right portions. Crop and AddBorders can also be used (a bit slower).

Note that the cropping lines of a YUY2 (and also YV12) clip must be even.

Smoothing:
There are basically two reasons why you should smooth a clip:

a) Analog captures (as opposed to digital captures which are often clean) contain noise. The source of this noise is mainly due to the cables carrying the signal to your house.
b) To make your final clip more compressible. This is especially important if your target is SVCD (or VCD), because DivX/XviD compresses better than SVCD/VCD.

The main difficulty is to find good a trade off between noise/compressibility, the level of detail you want to keep in your clip and the encoding time. Best is to smooth before any resizing, because you will remove more noise this way. I recommend to use a spatio-temporal smoother, or a spatial and a temporal smoother.

This way you will remove more noise than when using only a spatial smoother. But remember if you use a temporal smoother with strong setting, you will see blending between frames. So, use low settings when using temporal smoothers.

It is advisible to do the smoothing before resizing. If the image is unresized, then it is larger. It has higher resolution, more data for the filter to work with, which implies that noise can be easier to find. Then resize to a smaller size, less resolution, and the resizer usually hides some noise too. So you end up with the best case senario. The consequence and disadvantage is that it takes more time, then when you smooth after resizing.

If you look into the forums and look at posted script, you will see sometimes that people are using tons of smoothers in their script. This is not necessary and it only costs much encoding time. Remember that even if you use one smoother your encoding time will decrease dramatically.

What smoothers should you use? It basically is a matter of taste, the color format that is requires for the this smoother and the encoding time when using this smoother. Some examples of spatio-temporal smoothers:

If you want to use AviSynth v1.0x/v2.0x plugins in AviSynth v2.5x, here is a way described how to do that. Note these plugins require YUY2. Note that AviSynth v2.5x has an auto plugin loading feature, so you don't need load those plugins explicitly.

 # LoadPlugin("c:\LoadPluginEx.dll")
 # LoadPlugin("c:\AviSynth2\plugins\dustv5.dll")
 # LoadPlugin("c:\AviSynth2\plugins\peachsmoother.dll")
 # LoadPlugin("c:\AviSynth2\plugins\nomosmooth.dll")

 # AviSynth v1.0x/v2.0x plugins:
 # PixieDust(5)  # very slow, but compression (and noise removal) is good, while preserving much detail.
 # PeachSmoother()  # people reported that this plugin contains a bug (results in blended frames, visible in all settings)
 # NoMoSmooth()
 # Convolution3d(preset="vhsBQ")
 # FluxSmooth(7, 7)
 # STMedianFilter(8, 15, 4, 7)

 # AviSynth v2.5x plugins:
 # Convolution3d(preset="vhsBQ")
 # FluxSmooth(7, 7)
 # STMedianFilter(8, 15, 4, 7)
 # deen("c3d", 0, 10, 12, 3)

I used the standard settings here, you can tweak them yourself if you are not satisfied. Personally I like PixieDust (when capturing short clips like videoclips), so the script becomes for example:

 # using AviSynth v2.5x (clip is YUY2):
LoadPlugin("c:\!LoadPluginEx.dll")  # be carefull not to put this plugin in your plugin dir.
LoadPlugin("c:\!AviSynth2\plugins\dustv5.dll")
AviSource("d:\capture.avi")
Trim(begin, end)
Telecide()
LetterBox(0, 16)
PixieDust(5)

 # using AviSynth v2.0x (clip is YUY2):
LoadPlugin("c:\AviSynth2\plugins\dustv5.dll")
AviSource("d:\capture.avi")
Trim(begin, end)
Telecide()
LetterBox(0, 16)
PixieDust(5)

When capturing long clips, you could use Convolution3d for example.

If you want to use a spatial and a separate temporal smoother you can use for example "Chroma Noise Reducer (CNR2)" and TemporalCleaner. The script becomes:

 # using AviSynth v2.5x (clip is YUY2):
LoadPlugin("c:\LoadPluginEx.dll")  # be carefull not to put this plugin in your plugin dir.
AviSource("d:\capture.avi")
Trim(begin, end)
Telecide()
LetterBox(0, 16)
ConvertToYV12()  # TemporalCleaner requires YV12
Cnr2()
TemporalCleaner()

References:
Smoothers: Here you can find plugins for AviSynth v2.0x and v2.5x.

Resizing:
After deinterlacing, cropping garbage and smoothing, the clip must be resized. Be careful when using for example FitCD to calculate your new resolutions. The problem is that some cards (ATI cards, or cards with a BT 8x8 Chipset) perform some internal scaling, instead of adds overscan (like Asus/NVidia drivers). First you have to figure out which of the two situations you are dealing with (see preface). Of course, the new resolution depends on the encoding (XviD or SVCD) and on the bitrate of the encoding. To find out the correct resize settings, have a look at the preface: PAL or NTSC:

resizing for PAL:
704x576 with a PAR of 128/117 and 2 pixels overscan
720x576 with a PAR of 128/117 and 18 pixels overscan
720x576 without overscan -> not ITU compliant -> use a generic PAR 48/45
768x576 with a PAR of 1/1 without overscan

This implies the following two situations:
[1]: capturing at 704x576, 768x576 or 720x576 without overscan.
[2]: capturing at 720x576 with 18 pixels overscan (not ITU compliant).

 # PAL [1], XviD:
BicubicResize(640, 480)  # or scalings of this size

 # PAL [2], XviD:
Crop(8, 0, 704, 576)
BicubicResize(640, 480)  # or scalings of this size

 # PAL [1], SVCD
BicubicResize(480, 576)

 # PAL [2], SVCD
Crop(8, 0, 704, 570)
BicubicResize(448, 544)
AddBorders(16, 16, 16, 16)  # adds overscan to your SVCD

resizing for NTSC:
704x480 with a PAR of 72/79 and 2 pixels overscan
704x480 without overscan -> not ITU compliant -> use a generic PAR of 81/88
720x480 with a PAR of 72/79 and 9 pixels overscan
720x480 without overscan -> not ITU compliant -> use a generic PAR of 9/10
640x480 with a PAR of 1/1

This implies the following three situations:
[1]: capturing at 704x480, 640x480 or 720x480 (all three ITU compliant).
[2]: capturing at 720x480 with overscan (not ITU compliant).
[3]: capturing at 704x480 (not ITU compliant).

 # NTSC [1], XviD:
BicubicResize(640, 480)  # or scalings of this size

 # NTSC [2], XviD:
Crop(8, 0, 712, 480)
AddBorders(0, 6, 0, 0)
BicubicResize(640, 480)  # or scalings of this size

 # NTSC [3], XviD:
AddBorders(0, 6, 0, 0)
BicubicResize(640, 480)  # or scalings of this size

 # NTSC [1], SVCD
BicubicResize(480, 480)

 # NTSC [2], SVCD
Crop(8, 0, 712, 480)
BicubicResize(448, 448)
AddBorders(16, 16, 16, 16)  # adds overscan to your SVCD

 # NTSC [3], SVCD
AddBorders(0, 6, 0, 0)
BicubicResize(448, 448)
AddBorders(16, 16, 16, 16)  # adds overscan to your SVCD
 

Your script becomes for example (assuming PAL [1] and target XviD):

LoadPlugin("c:\LoadPluginEx.dll")
LoadPlugin("c:\AviSynth2\plugins\dustv5.dll")
AviSource("d:\capture.avi")
Trim(begin, end)
Telecide()
LetterBox(0, 16)
PixieDust(5)
BicubicResize(640, 480)

References:
Square and non-square pixels: About square and non-square pixels.
Resolutions, Aspect Ratios And The ITU-R BT.601 Standard: More general introduction about Resolutions, Aspect Ratios And The ITU-R BT.601 Standard.
FitCD: A utility to calculate how you should resize/crop various sources.
Der Karl's Apect Ratio for Dummies: The name says it all, but it is only for PAL.
A Quick Guide to Digital Video Resolution and Aspect Ratio Conversions: The name says it all.

Color adjustment (with AviSynth v2.5):
If you at your capture, you might see that the colors will be a bit distorted. Shiny colors will be more shiny, dark colors will be more dark. Normally YUV values are not mapped from 0 to 255 (PC range), but a limited range (TV range). This limited range is 16-236 for the luma component and 16-240 for the chroma component of the YUV signal. The problem is that most capture devices scale it to the range 0-255, and this must be scaled back to 16-236. Scroll through the clip and see if your colors are indeed distored. If so, it can be corrected by using the internal ColorYUV filter (for AviSynth v2.08 there is a plugin ColorYUV available):

ColorYUV(levels="PC->TV")

Scroll again through the clip, to see if the distortion is corrected. If not, remove the line and correct it manually. You have to find the settings only once. For the next capture you can use the same values, provided you don't change them in the capture settings. Btw, I don't know how to do this in AviSynth v2.08, since Histogram is only,available in AviSynth v2.5.

We will use the histogram the adjust the brightness and contrast. Since the histogram requires YV12, we add the following lines to the script:

ColorYUV(off_y=0, gain_y=0)
ConvertToYV12()
Histogram()

Open this script in VirtualdubMod. Right-click on the clip and select normal to enlarge the clip. You will see the clip with next to it the histogram. On the horizontal axis the luminance is plotted. If you look closely you will see that the range 0-15 is brown (meaning invalid), the range 16-236 black (valid) and 237-255 brown (invalid). The histogram itself can be white (if it lies in the valid range 16-236) or yellow (if it lies in the invalid range). Note that the brown bars are exaggerated, otherwise you won't see them hardly on the images (because they are compressed).

Look up a part of a frame that is black (black borders for example, this frame for example). If the histogram is yellow on that part, it means that we have to increase the luminance (i.e. brightness) till the histogram becomes white. If it is white we have to decrease the luminance just before it becomes yellow. Open the script editor (under tools), adjust the off_y and press F5 for previewing. The script becomes for example:

ColorYUV(off_y=-20, gain_y=0)
ConvertToYV12()
Histogram()

Look up a part of a frame which is very bright (the frame above for examle). Increase (or decrease if necessary) the gain_y to stretch the luminance till you can't increase it any further. The script becomes for example:

ColorYUV(off_y=-20, gain_y=64)
ConvertToYV12()
Histogram()

You will see that also the left boundary has moved (because it was about 16 instead of 0, and changing gain_y means multiplying). Go back to your original frame, and adjust it again. The script becomes for example:

ColorYUV(off_y=-28, gain_y=64)
ConvertToYV12()
Histogram()

Continue till you are satisfied. If you are done, it is time to adjust the saturation (i.e. colorness/chrominance). The options "cont_u" and "cont_v" depend the saturation in the following way:

cont_u = cont_v = - (1 - saturation) * 256

Example: saturation = 0.8 implies cont_u = cont_v = - 0.2 * 256 = - 51.2. Look up a frame which contains something very red or blue (for example clothes), or just look at the skin of people, and increase/decrease the saturation. The script becomes for example:

saturation = 0.8
cu = - (1-saturation)*256
ColorYUV(off_y=-28, gain_y=64, cont_u=cu, cont_v = cu)

To be sure that the luminance and chrominance lie in a valid range, set opt="coring" as an option in ColorYUV. Remove the histogram since we don't need it anymore:

saturation = 0.8
cu = - (1-saturation)*256
ColorYUV(off_y=-28, gain_y=64, cont_u=cu, cont_v = cu, opt="coring")

Fade in and out:
It is always nice to fade in and fade out your clip, but of course not necessary. If you do it, you can use the FadeIO2 filter. Your script becomes for example:

LoadPlugin("c:\LoadPluginEx.dll")
LoadPlugin("c:\AviSynth2\plugins\dustv5.dll")
AviSource("d:\capture.avi")
Trim(begin, end)
Telecide()
LetterBox(0, 16)
PixieDust(5)
BicubicResize(640, 480)
ColorYUV(levels="PC->TV")
FadeIO2(13)

4.2.1 Processing interlaced video
If you want to process interlaced video (thus leaving it interlaced), you should remember the following issue. When using only a spatial smoother, it is sufficient to use:

SeparateFields()
Filter(...)
Weave()

But when using a spatio-temporal smoother (or just a temporal smoother), this produces incorrect results. The reason is that the temporal smoothing occurs over two fields from the same frame (this happens for half the number of frames). To avoid this, you should put them in different clips:

SeparateFields()
even = SelectEven(last).Filter(...)
odd = SelectOdd(last).Filter(...)
Interleave(even, odd)
Weave()

The script above becomes for a spatial smoother:

LoadPlugin("c:\LoadPluginEx.dll")
LoadPlugin("c:\AviSynth2\plugins\dustv5.dll")
AviSource("d:\capture.avi")
Trim(begin, end)
SeparateFields()
LetterBox(0, 16)
SpaceDust(5)  # spatial smoother
BicubicResize(640, 240)  # note that the height is divided by two, since we are resizing the fields instead of the frames
ColorYUV(levels="PC->TV")
FadeIO2(13)
Weave()

and spatio-temporal smoother:

LoadPlugin("c:\LoadPluginEx.dll")
LoadPlugin("c:\AviSynth2\plugins\dustv5.dll")
AviSource("d:\capture.avi")
Trim(begin, end)
SeparateFields()
LetterBox(0, 16)
even = SelectEven(last).Convolution3d(0, 10, 17, 10, 13, 2.8, 0) # spatio-temporal smoother
odd = SelectOdd(last).Convolution3d(0, 10, 17, 10, 13, 2.8, 0) # spatio-temporal smoother
Interleave(even, odd)
BicubicResize(640, 240)  # note that the height is divided by two, since we are resizing the fields instead of the frames
weave()
ColorYUV(levels="PC->TV")
FadeIO2(13)
Weave()

4.2.2 Using multiple captures to reduce noise
The noise in your capture is mainly due to the cable that carries the television signal to your home. This implies the noise will be random, compared to a different broadcasting (of the same clip). Using AviSynth it is possible to average multiple broadcastings (of the same clip) using the filter Layer. The idea is to deinterlace first before averaging the clips, and use a lighter denoiser afterwards. First you have to determine whether there are frames present in the first source that are missing in the secound source (and the other way around). Look up which frames they are (by opening both captures at the same time, looking up the first common frame and skipping through the clips by say 1000 frames). Your script becomes for example:

clip1 = AviSource("f:\atomic_kitten-the_tide_is_high.avi").Trim(248,5389).Telecide()
clip2 = AviSource("f:\atomic_kitten-the_tide_is_high2.avi").Trim(129,5271).DeleteFrame(501).Telecide()
Layer(clip1, clip2, "fast")
LetterBox(0, 16)
ConvertToYV12()  # if necessary, Convolution3d requires YV12
Convolution3d(preset="movieLQ")
BicubicResize(640, 480)
saturation = 0.8
cu = - (1-saturation)*256
ColorYUV(off_y=-28, gain_y=64, cont_u=cu, cont_v = cu, opt="coring")
FadeIO2(13)

References:
Averaging two analog captures for noise reduction: A big Doom9-thread about averaging of analog captures to reduce noise.


Next step: video and audio compression: <NEXT>

Back to the Index: <HOME>


Last edited on: 09/22/2003 | First release: n/a | Author: Wilbert | Content by Doom9.org