mwhitlock
27th June 2006, 13:19
I've recently been working on converting the Futurama DVDs into MPEG-4 AVC+AAC (x264 + Nero Digital AAC). The MPEGs on the DVDs are encoded with 3:2 pulldown flags, so I simply used "Forced Film" mode in DGIndex, giving me 24000/1001-fps clips with mostly progressive frames. In a perfect world, they'd all be progressive frames, but due to the lipsyncing process used by the animators, there are many frames that are still combed. Too bad the DVD authors encoded those frames as progressive, thus screwing up the chroma subsampling, but that's another story for another day.
Careful study using AviSynth's SeparateFields and StackHorizontal functions led me to realize that most of the combed frames were in fact weaves of one field from the previous frame and the opposite field from the next frame. I knew that any kind of motion-based smart deinterlacing would be stupid because I wasn't dealing with an actual interlaced source, but I also couldn't run IVTC because I was already at 24fps. The solution of course was to field match to restore the progressive frames.
However, in many cases, particularly due to the bad chroma subsampling, pairing the even field from one frame with the odd field from another frame produced rainbowy artifacts. Tritical's TFM field matcher likes pairing fields from different frames, so I couldn't use it. I needed a field matcher that would prefer taking both fields from the same source frame and only resort to mixing fields from two source frames if absolutely necessary. To solve my problem, I scripted my own field matcher using purely built-in AviSynth functions.
http://img53.imageshack.us/img53/8051/compare6xc.png (http://imageshack.us)
My first task was finding a way to tell if any two fields were meant to go together in the same progressive frame. The fields of almost any progressive frame are extremely similar to each other, and when bobbed to correct for the vertical displacement, they're almost identical. Therefore, to tell if two fields belonged to the same progressive frame, I only needed to determine "how identical" they were. The easiest way I found was to difference the two bobbed fields using the Overlay function and examine the value of YPlaneMax() on the result. The magic number for my content was around 235-240: maximum lumas higher than that meant the two fields were definitely not from the same progressive frame.
Now I will turn to the task of picking out the fields to compare from the source clip. My current frame is cc, which is my source clip, and my next frame is nn = cc.DeleteFrame(0). I bob cc and nn so I have offset-corrected fields that should overlap perfectly if they belong together. I then do the four differences: the current even field with the current odd field, the next even field with the next odd field, the current even field with the next odd field, and the next even field with the current odd field.
You might wonder why I test both diagonal directions. If the field order of the clip is constant, I should never get matches in both diagonals in the same clip. But guess what, the field order of my source material isn't constant! Incredible what the lipsyncing process does to video.
At this point, it's simply a matter of choosing the highest preference pairing that satisfies my match condition. The AviSynth script for my field matcher follows:
function FieldMatch(clip c) {
global cc = c
global nn = cc.DeleteFrame(0)
global bc = cc.Bob()
global bn = nn.Bob()
return ScriptClip( \
cc, \
"bc.SelectEven().Overlay(bc.SelectOdd(), mode="+Chr(34)+"Difference"+Chr(34)+").YPlaneMax() < 240 ? cc : " + \
"bn.SelectEven().Overlay(bn.SelectOdd(), mode="+Chr(34)+"Difference"+Chr(34)+").YPlaneMax() < 240 ? nn : " + \
"bc.SelectEven().Overlay(bn.SelectOdd(), mode="+Chr(34)+"Difference"+Chr(34)+").YPlaneMax() < 240 ? " + \
"Interleave(cc.SeparateFields().SelectEven(), nn.SeparateFields().SelectOdd()).Weave() : " + \
"bn.SelectEven().Overlay(bc.SelectOdd(), mode="+Chr(34)+"Difference"+Chr(34)+").YPlaneMax() < 240 ? " + \
"Interleave(nn.SeparateFields().SelectEven(), cc.SeparateFields().SelectOdd()).Weave() : " + \
"cc" \
)
}
To do a real nice comparison, I set up the following script:
cc = MPEG2source("F:\1ACV01.d2v", cpu=6).BicubicResize(640, 480)
nn = cc.DeleteFrame(0)
bc = cc.Bob()
bn = nn.Bob()
dceco = bc.SelectEven().Overlay(bc.SelectOdd(), mode="Difference")
dneno = bn.SelectEven().Overlay(bn.SelectOdd(), mode="Difference")
dceno = bc.SelectEven().Overlay(bn.SelectOdd(), mode="Difference")
dneco = bn.SelectEven().Overlay(bc.SelectOdd(), mode="Difference")
c = cc.SeparateFields()
n = nn.SeparateFields()
out = ScriptClip( \
cc, \
"dceco.YPlaneMax() < 240 ? cc.Subtitle("+Chr(34)+"Curr/Curr"+Chr(34)+") : " + \
"dneno.YPlaneMax() < 240 ? nn.Subtitle("+Chr(34)+"Next/Next"+Chr(34)+") : " + \
"dceno.YPlaneMax() < 240 ? Interleave(c.SelectEven(), " + \
"n.SelectOdd()).Weave().Subtitle("+Chr(34)+"Curr/Next"+Chr(34)+") : " + \
"dneco.YPlaneMax() < 240 ? Interleave(n.SelectEven(), " + \
"c.SelectOdd()).Weave().Subtitle("+Chr(34)+"Next/Curr"+Chr(34)+") : " + \
"cc.Subtitle("+Chr(34)+"Curr/Curr - Match Failure"+Chr(34)+")" \
)
StackVertical( \
StackHorizontal( \
cc.Subtitle("Original"), \
StackVertical( \
StackHorizontal( \
ScriptClip(c.SelectEven().HorizontalReduceBy2().Subtitle("Curr Even"), \
"Subtitle(String(dceco.YPlaneMax()), align=2).\
Subtitle(String(dceno.YPlaneMax()), align=3)"), \
ScriptClip(n.SelectEven().HorizontalReduceBy2().Subtitle("Next Even"), \
"Subtitle(String(dneco.YPlaneMax()), align=1).\
Subtitle(String(dneno.YPlaneMax()), align=2)") \
), \
StackHorizontal( \
c.SelectOdd().HorizontalReduceBy2().Subtitle("Curr Odd"), \
n.SelectOdd().HorizontalReduceBy2().Subtitle("Next Odd") \
) \
) \
), \
StackHorizontal( \
cc.TFM(PP=0, display=true, micout=2).Subtitle("TFM(PP=0)", align=1), \
out.Subtitle("Mine", align=3) \
) \
)
Download a sample output from this comparison script (http://www.filewire.com/download.php?id=84ef0ef71c699e333905722) (66.2 MB)
This video file is ripe with good points for comparison of the two field matchers. Be sure to look at frame 692.
Careful study using AviSynth's SeparateFields and StackHorizontal functions led me to realize that most of the combed frames were in fact weaves of one field from the previous frame and the opposite field from the next frame. I knew that any kind of motion-based smart deinterlacing would be stupid because I wasn't dealing with an actual interlaced source, but I also couldn't run IVTC because I was already at 24fps. The solution of course was to field match to restore the progressive frames.
However, in many cases, particularly due to the bad chroma subsampling, pairing the even field from one frame with the odd field from another frame produced rainbowy artifacts. Tritical's TFM field matcher likes pairing fields from different frames, so I couldn't use it. I needed a field matcher that would prefer taking both fields from the same source frame and only resort to mixing fields from two source frames if absolutely necessary. To solve my problem, I scripted my own field matcher using purely built-in AviSynth functions.
http://img53.imageshack.us/img53/8051/compare6xc.png (http://imageshack.us)
My first task was finding a way to tell if any two fields were meant to go together in the same progressive frame. The fields of almost any progressive frame are extremely similar to each other, and when bobbed to correct for the vertical displacement, they're almost identical. Therefore, to tell if two fields belonged to the same progressive frame, I only needed to determine "how identical" they were. The easiest way I found was to difference the two bobbed fields using the Overlay function and examine the value of YPlaneMax() on the result. The magic number for my content was around 235-240: maximum lumas higher than that meant the two fields were definitely not from the same progressive frame.
Now I will turn to the task of picking out the fields to compare from the source clip. My current frame is cc, which is my source clip, and my next frame is nn = cc.DeleteFrame(0). I bob cc and nn so I have offset-corrected fields that should overlap perfectly if they belong together. I then do the four differences: the current even field with the current odd field, the next even field with the next odd field, the current even field with the next odd field, and the next even field with the current odd field.
You might wonder why I test both diagonal directions. If the field order of the clip is constant, I should never get matches in both diagonals in the same clip. But guess what, the field order of my source material isn't constant! Incredible what the lipsyncing process does to video.
At this point, it's simply a matter of choosing the highest preference pairing that satisfies my match condition. The AviSynth script for my field matcher follows:
function FieldMatch(clip c) {
global cc = c
global nn = cc.DeleteFrame(0)
global bc = cc.Bob()
global bn = nn.Bob()
return ScriptClip( \
cc, \
"bc.SelectEven().Overlay(bc.SelectOdd(), mode="+Chr(34)+"Difference"+Chr(34)+").YPlaneMax() < 240 ? cc : " + \
"bn.SelectEven().Overlay(bn.SelectOdd(), mode="+Chr(34)+"Difference"+Chr(34)+").YPlaneMax() < 240 ? nn : " + \
"bc.SelectEven().Overlay(bn.SelectOdd(), mode="+Chr(34)+"Difference"+Chr(34)+").YPlaneMax() < 240 ? " + \
"Interleave(cc.SeparateFields().SelectEven(), nn.SeparateFields().SelectOdd()).Weave() : " + \
"bn.SelectEven().Overlay(bc.SelectOdd(), mode="+Chr(34)+"Difference"+Chr(34)+").YPlaneMax() < 240 ? " + \
"Interleave(nn.SeparateFields().SelectEven(), cc.SeparateFields().SelectOdd()).Weave() : " + \
"cc" \
)
}
To do a real nice comparison, I set up the following script:
cc = MPEG2source("F:\1ACV01.d2v", cpu=6).BicubicResize(640, 480)
nn = cc.DeleteFrame(0)
bc = cc.Bob()
bn = nn.Bob()
dceco = bc.SelectEven().Overlay(bc.SelectOdd(), mode="Difference")
dneno = bn.SelectEven().Overlay(bn.SelectOdd(), mode="Difference")
dceno = bc.SelectEven().Overlay(bn.SelectOdd(), mode="Difference")
dneco = bn.SelectEven().Overlay(bc.SelectOdd(), mode="Difference")
c = cc.SeparateFields()
n = nn.SeparateFields()
out = ScriptClip( \
cc, \
"dceco.YPlaneMax() < 240 ? cc.Subtitle("+Chr(34)+"Curr/Curr"+Chr(34)+") : " + \
"dneno.YPlaneMax() < 240 ? nn.Subtitle("+Chr(34)+"Next/Next"+Chr(34)+") : " + \
"dceno.YPlaneMax() < 240 ? Interleave(c.SelectEven(), " + \
"n.SelectOdd()).Weave().Subtitle("+Chr(34)+"Curr/Next"+Chr(34)+") : " + \
"dneco.YPlaneMax() < 240 ? Interleave(n.SelectEven(), " + \
"c.SelectOdd()).Weave().Subtitle("+Chr(34)+"Next/Curr"+Chr(34)+") : " + \
"cc.Subtitle("+Chr(34)+"Curr/Curr - Match Failure"+Chr(34)+")" \
)
StackVertical( \
StackHorizontal( \
cc.Subtitle("Original"), \
StackVertical( \
StackHorizontal( \
ScriptClip(c.SelectEven().HorizontalReduceBy2().Subtitle("Curr Even"), \
"Subtitle(String(dceco.YPlaneMax()), align=2).\
Subtitle(String(dceno.YPlaneMax()), align=3)"), \
ScriptClip(n.SelectEven().HorizontalReduceBy2().Subtitle("Next Even"), \
"Subtitle(String(dneco.YPlaneMax()), align=1).\
Subtitle(String(dneno.YPlaneMax()), align=2)") \
), \
StackHorizontal( \
c.SelectOdd().HorizontalReduceBy2().Subtitle("Curr Odd"), \
n.SelectOdd().HorizontalReduceBy2().Subtitle("Next Odd") \
) \
) \
), \
StackHorizontal( \
cc.TFM(PP=0, display=true, micout=2).Subtitle("TFM(PP=0)", align=1), \
out.Subtitle("Mine", align=3) \
) \
)
Download a sample output from this comparison script (http://www.filewire.com/download.php?id=84ef0ef71c699e333905722) (66.2 MB)
This video file is ripe with good points for comparison of the two field matchers. Be sure to look at frame 692.