Log in

View Full Version : Mixed 60p/30i and 24p content? No problem!


yukichigai
20th July 2013, 02:23
One of the most maddening bits of conversion that I see frequent threads on is content that is a mix of 24p and 30i. The simplest method is to just prioritize one over the other and accept that some of it going to have some pretty bad ghosting, blended frames, and/or jerky motion. But what about content where it's somewhat evenly split, content where the 30i and 24p parts are equally important? What do you do then?

This isn't a new problem, and there aren't a shortage of solutions to it. A lot of them may be a bit hard to grasp though, or may only be useable for certain portions of the content. Another thing I've noticed is that some very nice, workable solutions haven't been updated for the newer tools which are available, or the syntax used by new editions of the plugins they rely on. To that end, I've cobbled together and tweaked two very excellent routines: MVBob and MVFPS (http://forum.doom9.org/showthread.php?p=615027#post615027) by scharfis_brain (http://forum.doom9.org/member.php?u=30929), and ivtc_txt60mc (http://forum.doom9.org/showthread.php?p=1466105#post1466105) by cretindesalpes (http://forum.doom9.org/member.php?u=163021). I've put them together in two avs scripts you can drop in your plugin directory for import, as well as automatically loading the various plugins those functions need in said scripts. I've also put together an example script that you can use to help figure out the breakpoints and so on that the functions will need to work properly.

Now before I go further, a few disclaimers:

These routines are incredibly slow. Currently I have an 8-core 3.6Ghz machine and it takes me about 14 hours to render an hour and a half of 480p video. Could this be sped up? I have no idea, which leads me into my second disclaimer....

I am not an expert. Really, I'm just sharing something that worked for me and worked remarkably well. I didn't see a very good thread outlining anything like what I'd done, so I thought I'd make one. If this thread is redundant or someone has made a way better method, "oops", I guess.

So with that out of the way, let's get to how this works. The basic idea is that you use MVBob + MVFPS to turn any "pure" 30i content into 24p video, ivtc_txt60mc for any mixed 30i/24p content, and whatever IVTC routine works for you to handle any pure 24p content. Using MVBob + MVFPS is pretty straightforward, as are most IVTC routines for pure 24p video. It's only ivtc_txt60mc that requires some careful usage. The test script I will post below helps to figure that out.

ivtc_txt60mc is dependent on you telling it which frame in the segment is the last "whole" frame before two "mixed"/interlaced frames in the sequence. It means you have to know what the precise pulldown pattern is in the video. If it changes at some point, perhaps due to a scene change, then you'll need to use trim to make another segment once the pattern stabilizes (i.e. goes back to standard 3:2 pulldown) and call ivtc_txt60mc on the new segment.

(Sidenote: one of the nice features of ivtc_txt60mc is that it can handle brief little "blips" in pulldown pattern. The first thing I ran these scripts on was an episode of MST3K (Mystery Science Theater 3000), a prime example of a situation where the 30i content is just as important as the 24p stuff. The movie they were riffing had a few awful, awful crossfade scene changes where the pulldown pattern started dropping mixed frames or even adding extras, where the frame order would be something like Aa Bb Bc Cd De EE or Aa Bc Cd Dd Ee. ivtc_txt60mc handled that like a champ, to the point where I had to squint really hard to see where it had motion-blended frames to account for the issue.)

The point of the text script is to simulate the breaks where you'd call the different functions. MVBob + MVFPS is simulated by a basic Bob + SelectEvery, and ivtc_txt60mc is simulated by a blood-simple IVTC using DoubleWeave and Pulldown. The script is simple enough that you can scroll through it in realtime, making it easier to detect situations where you need to make another break because the pulldown pattern has changed. See a bunch of interlace artifacts/jaggies, means the pulldown pattern changed somewhere and you need to use Trim to make another segment. Repeat until the thing is mostly jaggie free. Once you do that, it's a simple matter of cut-and-paste to replace the placeholder functions with the real deal. Then you can set your computer to render your video for the next 14+ hours and go do something else, like sleep.

Like I said, I'm not sure how useful this is, or if I'm just reinventing the wheel. I found it useful at least, and hopefully you will too.

yukichigai
20th July 2013, 02:26
Note that this is not just mvbob, it also contains an updated version of mvfps

#mvbob.avsi

Function ReYV12(Clip Input)
{
Y = Input.ConverttoYV12() #.Greyscale()
Input = Input.SeparateFields().AssumeFrameBased().SeparateFields().SelectEvery(4, 0, 2).Weave()
U = Input.UtoY().ConvertToYV12()
V = Input.VtoY().ConvertToYV12()
YtoUV(U, V, Y)
}

Function PointBob(clip Input)
{
Order = Getparity(Input)# ? False : True
Input = Input.SeparateFields.PointResize(Input.Width, Input.Height)
Even = Input.SelectEven()
Odd = Input.SelectOdd()
Even = Order ? Even.Crop(0, 1, 0, 0).AddBorders(0, 0, 0, 1) : Even
Odd = Order ? Odd : Odd .Crop(0, 1, 0, 0).AddBorders(0, 0, 0, 1)
Interleave(Even, Odd).AssumeFrameBased()
Order ? AssumeTFF() : AssumeBFF()

}

Function DumbBob(clip Input, int "Height")
{
Input.Bob(0, 0.5, height = Height)
GetParity(Input) ? AssumeTFF() : AssumeBFF()
}


Function TomsBob(clip Input, int "se")
{
se = Default(se, 0)

Interleave(\
Input.TomsMoComp(-1, se, 0), \
Input.DoubleWeave().SelectOdd().TomsMoComp(-1, se, 0))

AssumeFrameBased()
GetParity(Input) ? AssumeTFF() : AssumeBFF()
}

Function KrnlBob(clip Input, int "Threshold")
{
Threshold = Default(Threshold, 0)
Input.LeakKernelBob(Order = (GetParity(Input) ? 1 : 0), Threshold = Threshold)
AssumeFrameBased()
GetParity(Input) ? AssumeTFF() : AssumeBFF()
}

Function EEDIbob(clip Input, int "maxd")
{
#GetParity(Input) ? Input.SeparateFields().EEDI2(Field = 3, maxd = maxd, pp = 0, estr = 0, dstr = 0, mthresh = 0, vthresh = 0, lthresh = 0) : Input.SeparateFields().EEDI2(Field = 2, maxd = maxd, pp = 0, estr = 0, dstr = 0, mthresh = 0, vthresh = 0, lthresh = 0)
GetParity(Input) ? Input.SeparateFields().EEDI2(Field = 3, maxd = maxd) : Input.SeparateFields().EEDI2(Field = 2, maxd = maxd)

AssumeFrameBased()
GetParity(Input) ? AssumeTFF() : AssumeBFF()
}

Function Sangbob(clip Input, int "aa")
{
Function SangnomBob(clip Input, int "aa")
{
Getparity(Input) ? Interleave(Input.Sangnom(Order = 1, aa = aa), Input.Sangnom(Order = 0, aa = aa)) : Interleave(Input.Sangnom(Order = 0, aa = aa), Input.Sangnom(Order = 1, aa = aa))

AssumeFrameBased()
GetParity(Input) ? AssumeTFF() : AssumeBFF()
}
Y = Input.ConvertToYV12().SangnomBob(aa)
U = Input.UtoY().ConvertToYV12().SangnomBob(aa)
V = Input.VtoY().ConvertToYV12().SangnomBob(aa)
Y = IsYV12(Input) ? Y : Y.ConvertToYUY2()
U = IsYV12(Input) ? U : U.ConvertToYUY2()
V = IsYV12(Input) ? V : V.ConvertToYUY2()
YUV = YtoUV(U, V, Y)

Return YUV
}

Function ReInterlace(clip Input, bool "YV12")
{
YV12 = Default(YV12, false)
Input = YV12 ? Input.ConvertToYV12 : Input.ConvertToYUY2()
Input.SeparateFields().SelectEvery(4, 0, 3).Weave()
}


Function SceneChange(Clip Input, int Threshold)
{
MaskE = Input.Selecteven().Motionmask(thY1 = Threshold, thY2 = Threshold, thC1 = Round(Threshold * 0.5), thC2 = Round(Threshold * 0.5), thSD = 255, U = 3, V = 3)
MaskO = Input.SelectOdd() .Motionmask(thY1 = Threshold, thY2 = Threshold, thC1 = Round(Threshold * 0.5), thC2 = Round(Threshold * 0.5), thSD = 255, U = 3, V = 3)
Mask = Interleave(MaskE, MaskO)
Mask = Logic(Mask, Mask.Deleteframe(0), "AND", U = 3, V = 3)

Return stackvertical(Input, Mask)
}

Function SecureBob(clip Input, int "Threshold", int "Length", int "Type")
{
# StaticMask uses a long temporal window to create a motionmask that is resist to
Function StaticMask(Clip Fields, int Threshold, int Length)
{
Fields_0 = Fields.MotionMask(thY1 = Threshold, thY2 = Threshold, thC1 = Round(Threshold * 0.5), thC2 = Round(Threshold * 0.5), thSD = 255, U = 3, V = 3)

Fields_1 = Fields_0.Trim(1, 0)
Fields_2 = Fields_0.Trim(2, 0)
Fields_3 = Fields_0.Trim(3, 0)
Fields_4 = Fields_0.Trim(4, 0)
Fields_5 = Fields_0.Trim(5, 0)
Fields_6 = Fields_0.Trim(6, 0)
Fields_7 = Fields_0.Trim(7, 0)

Fields_01 = Logic(Fields_0, Fields_1, "OR", U = 3, V = 3)
Fields_23 = Logic(Fields_2, Fields_3, "OR", U = 3, V = 3)
Fields_45 = Logic(Fields_4, Fields_5, "OR", U = 3, V = 3)
Fields_67 = Logic(Fields_6, Fields_7, "OR", U = 3, V = 3)

Fields_0123 = Logic(Fields_01, Fields_23, "OR", U = 3, V = 3)
Fields_4567 = Logic(Fields_45, Fields_67, "OR", U = 3, V = 3)

Fields_01234567 = Logic(Fields_0123, Fields_4567, "OR", U = 3, V = 3)

(Length == 0) ? Fields_01234567.DuplicateFrame(0).DuplicateFrame(0).DuplicateFrame(0) :\
(Length == 1) ? Fields_0123. DuplicateFrame(0) :\
(Length == 2) ? Fields_01 :\
Fields_0
Return Last
}


Threshold = Default(Threshold, 3)
Length = Default(Length, 1)
Type = Default(Type, 3)

Input.ConvertToYV12(Interlaced = True).SeparateFields()
EvenFields = Last.SelectEven().StaticMask(Threshold, Length) #.subtitle("X")
OddFields = Last.SelectOdd() .StaticMask(Threshold, Length) #.subtitle("X")

CombMask = Interleave(EvenFields, OddFields).dumbbob()
CombMask = Merge(CombMask.Trim(1, 0), CombMask, 0.5).Binarize(U = 3, V = 3, upper=false)

Bobbed = (Type == 0) ? Input.DumbBob() :\
(Type == 1) ? Input.TomsBob() :\
(Type == 2) ? Input.KrnlBob() :\
(Type == 3) ? Input.EEDIBob() :\
Input.SangBob()

Weaved = Input.DoubleWeave()

Output = IsYV12(Input) ? MaskedMerge(Weaved, Bobbed, CombMask, U = 3, V = 3) :\
Overlay(Weaved, Bobbed, Mask = CombMask.ConvertToYUY2(), GreyMask = False)

Output = Output.AssumeFrameBased()
Output = GetParity(Input) ? Output.AssumeTFF() : Output.AssumeBFF()

Ouput = (Threshold < 0) ? Bobbed : Output

Return Output #StackVertical(Output, Combmask)
}



# Function to aid motion compensated standards conversions of camera materials (true interlaced)
# It offers a little automatism to help easy NTSC <-> PAL conversions.
# if the input is 50 fps, then it'll convert to 59.94 fps without any parameters given
# elseif the input is 60 fps, then it'll convert to 50 fps without any parameters given
# else if parameters given it uses num and den for the desired framerate

Function ConvertMCfps(clip Input, int "Num", int "Den", int "BlkSize", int "lambda", float "ml")
{
BlkSize = Default(BlkSize, 8)
BlkSize = (BlkSize == 8) ? 8 : (BlkSize == 16) ? 16 : 4
Rate= (Defined(Num) && Defined(Den)) ? -1 : FrameRate(Input)

TM=true
#lm=10000

#Input = IsYV12(Input) ? Input : Input.ConvertToYV12() # Needed with older versions of mvtools
Cropped = (BlkSize == 8) ? Input.Crop(4, 4, -4, -4) : (Blksize == 4) ? Input.Crop(2, 2, -6, -6) : Input.Crop(8, 8, -8, -8)

Backward_Vec = Input. MVAnalyse(isb = True, BlkSize=BlkSize, TrueMotion=TM, Pel=2, Idx=4, sharp=1, lambda = lambda)
Forward_Vec = Input. MVAnalyse(isb = False, BlkSize=BlkSize, TrueMotion=TM, Pel=2, Idx=4, sharp=1, lambda = lambda)
Backward_Vec2 = Cropped.MVAnalyse(isb = True, BlkSize=BlkSize, TrueMotion=TM, Pel=2, Idx=3, sharp=1, lambda = lambda)
Forward_Vec2 = Cropped.MVAnalyse(isb = False, BlkSize=BlkSize, TrueMotion=TM, Pel=2, Idx=3, sharp=1, lambda = lambda)

Output = (Rate == 50) ? \
Input.MVFlowfps2(Backward_Vec, Forward_Vec, Backward_Vec2, Forward_Vec2, Num = 60000, Den = 1001, Idx = 4, Idx2 = 3, ml=ml) : \
((Rate > 59.90) && (Rate < 60.01)) ? \
Input.MVFlowFps2(Backward_Vec, Forward_Vec, Backward_Vec2, Forward_Vec2, Num = 50, Den = 1, Idx = 4, Idx2 = 3, ml=ml) : \
Input.MVFlowFps2(Backward_Vec, Forward_Vec, Backward_Vec2, Forward_Vec2, Num = Num, Den = Den, Idx = 4, Idx2 = 3, ml=ml)

Return Output
}



function MvBob(clip Input, int "BlkSize", int "Pel", int "CorrectTh", int "Threshold", int "Type", int "Length")
{

#disable all scene detection of mvcompensate(), because mvbob() is self-correcting due to the corrector()
sc = 255

# threshold for correcting failed compensated motion
CorrectTh = Default(CorrectTh, 8)

# deinterlacing threshold for the motion vector analysis do not go much higher than 10
BobTh = Default(Threshold, 6)
BobType = Default(Type, 3)

#defaults for the MoComp
BlkSize = Default(BlkSize, 8)
scd = round(2000 * ((blksize * blksize) / 64))
Pel = default(Pel, 2)
Lambda = 6000

#determine clip Fieldorder
Order = (Input.GetParity == True) ? 1 : 0
YV12 = IsYV12(Input)

# create a motion adaptive bobbed clip with ELA interpolation from the Input
Bobbed = Input.SecureBob(Threshold = BobTh, Type=BobType, Length=Length)
AnalyseBob = Bobbed #.Reduceflicker() #YV12 ? Bobbed.RemoveGrain(11) : Bobbed.ConverTtoYV12().removegrain(11).ConvertToYUY2()

# create clip for motion compensation
Fields = Bobbed

# create motion vectors
mvf = AnalyseBob.MvAnalyse(BlkSize = BlkSize, Pel = Pel, isB = False, Chroma = True, idx = 1, TrueMotion = True, Sharp = 2, Lambda = Lambda, Overlap = BlkSize/2)
mvb = AnalyseBob.MvAnalyse(BlkSize = BlkSize, Pel = Pel, isB = True, Chroma = True, idx = 1, TrueMotion = True, Sharp = 2, Lambda = Lambda, Overlap = BlkSize/2)
#DePanMV = Fields.depanestimate()
#DePanMVBWD = Fields.MvDePan(mvb, thSCD1 = scd, thSCD2 = 255, error=255, pixaspect=0.911)


# create the motion compensated clip that are passed through to the output
MoCompFWD = Fields.MvCompensate(mvf, idx = 1, thSCD1 = scd)
MoCompBWD = Fields.MvCompensate(mvb, idx = 1, thSCD1 = scd)
# Experimental: Flow
# MoFlowFWD = Fields.MvFlow(mvf, idx = 1, thSCD1 = scd)
# MoFlowBWD = Fields.MvFlow(mvb, idx = 1, thSCD1 = scd)
# Experimental: GMC
# DePanFWD = Fields.DePan(data=DepanMV, offset=1.0)
# DePanBWD = Fields.DePan(data=DepanMV, offset=-1.0)

# create clips for mismatch detection
# AnalyseBobFWD = AnalyseBob.MvCompensate(mvf, idx = 1, thSCD1 = scd).converttoyv12().medianblur()
# AnalyseBobBWD = AnalyseBob.MvCompensate(mvb, idx = 1, thSCD1 = scd).converttoyv12().medianblur()

AnalyseBobCurr = AnalyseBob.Converttoyv12().BicubicResize(Input.Width * 2, Input.Height).MedianBlur().BicubicResize(Input.Width, Input.Height)
AnalyseCompFWD = MoCompFWD .converttoyv12().BicubicResize(Input.Width * 2, Input.Height).MedianBlur().BicubicResize(Input.Width, Input.Height)
AnalyseCompBWD = MoCompBWD .converttoyv12().BicubicResize(Input.Width * 2, Input.Height).MedianBlur().BicubicResize(Input.Width, Input.Height)
# Experimental: Flow
# AnalyseFlowFWD = MoFlowFWD .converttoyv12().BicubicResize(Input.Width * 2, Input.Height).MedianBlur().BicubicResize(Input.Width, Input.Height)
# AnalyseFlowBWD = MoFlowBWD .converttoyv12().BicubicResize(Input.Width * 2, Input.Height).MedianBlur().BicubicResize(Input.Width, Input.Height)
# Experimental: GMC
# AnalyseDePanFWD = DePanFWD .Converttoyv12().BicubicResize(Input.Width * 2, Input.Height).MedianBlur().BicubicResize(Input.Width, Input.Height)
# AnalyseDePanBWD = DePanBWD .Converttoyv12().BicubicResize(Input.Width * 2, Input.Height).MedianBlur().BicubicResize(Input.Width, Input.Height)

AnalyseBobCurr = YV12 ? AnalyseBobCurr : AnalyseBobCurr.ConvertToYUY2()
AnalyseCompFWD = YV12 ? AnalyseCompFWD : AnalyseCompFWD.ConvertToYUY2()
AnalyseCompBWD = YV12 ? AnalyseCompBWD : AnalyseCompBWD.ConvertToYUY2()
# Experimental: Flow
# AnalyseFlowFWD = YV12 ? AnalyseFlowFWD : AnalyseFlowFWD.ConvertToYUY2()
# AnalyseFlowBWD = YV12 ? AnalyseFlowBWD : AnalyseFlowBWD.ConvertToYUY2()
# Experimental: GMC
# AnalyseDePanFWD = YV12 ? AnalyseDePanFWD : AnalyseDePanFWD.ConvertToYUY2()
# AnalyseDePanBWD = YV12 ? AnalyseDePanBWD : AnalyseDePanBWD.ConvertToYUY2()

# detect mismatches of mvtools and replace them with motion adaptive bobbed areas
#MoCompCorrected = Corrector(AnalyseBobCurr, Fields, AnalyseCompFWD, AnalyseCompBWD, AnalyseFlowFWD, AnalyseFlowBWD, MoCompFWD, MoCompBWD, MoFlowFWD, MoFlowBWD, Mode = 0, Th = CorrectTh)
MoCompCorrected = Corrector(AnalyseBobCurr, Fields, AnalyseCompFWD, AnalyseCompBWD, MoCompFWD, MoCompBWD, Mode = 0, Th = CorrectTh)

# Experimental: GMC
#MoCompCorrected = Corrector(AnalyseBobCurr, Fields, AnalyseBobFWD, AnalyseBobBWD, AnalyseDePanFWD, AnalyseDePanBWD, MoCompFWD, MoCompBWD, DePanFWD, DePanBWD, Mode = 1, Th = CorrectTh)
#MoCompCorrected = Corrector(AnalyseBobCurr, Fields, AnalyseCompFWD, AnalyseCompBWD, MoCompFWD, MoCompBWD, Mode = 0, Th = CorrectTh)

#MoCompCorrected = Merge(Merge(MoCompFWD, MoCompBWD), Merge(MoFlowFWD, MoFlowBWD))
# Select the Interpolated Fields
Compensated_Even = MoCompCorrected.SelectEven().SeparateFields().SelectOdd()
Compensated_Odd = MoCompCorrected.SelectOdd() .SeparateFields().SelectEven()

# Select the Original Fields
Original_Even = Input.SeparateFields().SelectEven()
Original_Odd = Input.SeparateFields().SelectOdd()

# Merge them
Output_Even = Interleave(Original_Even, Compensated_Even).Weave()
Output_Odd = Interleave(Original_Odd , Compensated_Odd ).Weave()
Output = Interleave(Output_Even, Output_Odd)
# Output = Stackhorizontal(Bobbed, Output)
# Assign the correct Fieldorder to the Output in Order to allow correct processing for following filters
Output = (order == 1) ? Output.AssumeTFF() : Output.AssumeBFF()
#Output=interleave(Depanfwd, fields, depanbwd)
Return Output #stackvertical(AnalyseBobCurr, AnalyseCompFWD)
}

function mvfps(clip i, int num, int den, int "oversample", int "blurradius")
{
oversample=default(oversample,1)
blurradius=default(blurradius,1)
j=i.temporalsoften(2,4,5,2)
fwd=mvanalyse(j,isb=false,lambda=4000)
bwd=mvanalyse(j,isb=true, lambda=4000)
i.mvflowfps(bwd,fwd,num*oversample,den)
(oversample>1) ? last.temporalsoften(blurradius,255,255,mode=2).selectevery(oversample,0) : last
}


# needed plugins
loadplugin("masktools.dll")
loadplugin("mvtools.dll")
loadplugin("tomsmocomp.dll")
loadplugin("leakkerneldeint.dll")
loadplugin("eedi2.dll")
loadplugin("medianblur.dll")
loadplugin("corrector.dll")
loadplugin("sangnom.dll")

yukichigai
20th July 2013, 02:27
ivtc_txt60mc.avsi

# Version 1.1
Import("QTGMC-3.32.avsi")
LoadPlugin("mvtools2.dll")
LoadPlugin("mt_masktools-25.dll")
LoadPlugin("nnedi3.dll")
LoadPlugin("SSETools.dll")
LoadPlugin("RemoveGrainSSE2.dll")
LoadPlugin("RepairSSE2.dll")
LoadPlugin("VerticalCleanerSSE2.dll")

Function ivtc_txt60mc (clip src, int frame_ref, bool "srcbob", bool "draft")
{
srcbob = Default (srcbob, false)
draft = Default (draft, false)

field_ref = (srcbob) ? frame_ref : frame_ref * 2
field_ref = field_ref % 5
invpos = (5 - field_ref) % 5
pel = (draft) ? 1 : 2

src
(srcbob) ? last
\ : (draft ) ? Bob ()
\ : QTGMC (SourceMatch=3, Lossless=2, tr0=1, tr1=1, tr2=1)

clean = SelectEvery (5, 1 - invpos)
jitter = SelectEvery (5, 3 - invpos, 4 - invpos)
jsup = jitter.MSuper (pel=pel)
vect_f = jsup.MAnalyse (isb=false, delta=1, overlap=4)
vect_b = jsup.MAnalyse (isb=true, delta=1, overlap=4)
comp = jitter.MFlowInter (jsup, vect_b, vect_f, time=50, thSCD1=400)
fixed = comp.SelectEvery (2, 0)
Interleave (clean, fixed)
Trim (invpos / 2, 0)
}

yukichigai
20th July 2013, 02:28
# 30i -> 24p mixed source script
# TEST SCRIPT
#
# This script gives you a fast way to test if you've got your frame order right
# before running the more time consuming filters. Line "A" is meant to be used
# on the "pure 30i" content, while Line "B" is for the (mostly) 24p content.
#
# If you see jaggies/interlacing for an extended duration when viewing this it
# means the telecine frame order on a "B" line is wrong, and either the Trim
# range needs to be adjusted or the segment needs to be broken up into multiple
# calls to Trim


# PLUGINS
LoadPlugin("X:\..\DGMPGDec\DGDecode.dll")
#LoadPlugin("X:\..\AviSynthPlugins\UnDot.dll")

#Import("X:\..\AviSynthPlugins\mvbob.avsi")
#Import("X:\..\AviSynthPlugins\ivtc_txt60mc.avsi")

# SOURCE
mpeg2source("DVDMOVIE.d2v")

KillAudio()

#
# EXAMPLE LINES
#

# LINE "A"
# Use this for pure 30i content (replace <start> and <end> with trim values)
#
# a = a+Trim(<start>,<end>).Bob().SelectEvery(5,1,4)

# LINE "B"
# Use this for 24p Telecine content. The first frame indicated by Trim should
# be a whole frame which is immediately followed by two mixed/interlaced frames
#
# a = a+Trim(<start>,<end>).DoubleWeave().Pulldown(0,3)

# IMPORTANT
# Remove "a+" from the first line you put down. For example:
#
# a = Trim(<start>,<end>).DoubleWeave().Pulldown(0,3)


a = Trim(0,10384).Bob().SelectEvery(5,1,4)
a = a+Trim(10385,31926).DoubleWeave().Pulldown(0,3)
a = a+Trim(31927,39494).DoubleWeave().Pulldown(0,3)
a = a+Trim(39495,43345).Bob().SelectEvery(5,1,4)
a = a+Trim(43346,74512).DoubleWeave().Pulldown(0,3)
a = a+Trim(74513,78666).Bob().SelectEvery(5,1,4)
a = a+Trim(78667,93743).DoubleWeave().Pulldown(0,3)
a = a+Trim(93744,108344).DoubleWeave().Pulldown(0,3)
a = a+Trim(108345,124454).DoubleWeave().Pulldown(0,3)
a = a+Trim(124455,128107).Bob().SelectEvery(5,1,4)
a = a+Trim(128108,143554).DoubleWeave().Pulldown(0,3)
a = a+Trim(143555,158364).DoubleWeave().Pulldown(0,3)
a = a+Trim(158365,164632).Bob().SelectEvery(5,1,4)
a = a+Trim(164633,164842).DoubleWeave().Pulldown(0,3)

# This line just outputs the video
a


# WHEN YOU'RE DONE AND THE VIDEO IS FREE OF JAGGIES
#
# FIRST, uncomment all the Import and LoadPlugin lines in the script
#
# THEN...
# Replace all instances of Line "A" (ends with SelectEvery) with:
#
# Trim(<start>,<end>).mvbob().mvfps(24000,1001)
#
# AND...
# Replace all instances of Line "B" (ends with Pulldown) with:
#
# Trim(<start>,<end>).ivtc_txt60mc(0)
#
# AND...
# Add in a call to UnDot()
#
# AND FINALLY...
# Rename the script

# UnDot()