scharfis_brain
8th May 2004, 14:53
This function is able to automatically restore 24 fps out of a 30fps blended video,
that originally was interlaced telecined FILM.
the function:
function undoblendedtc(clip input)
{
# prepare clips
a=loop(input).trim(0,5)+input
b=a.trim(1,0)
c=a.trim(2,0)
global d=a.trim(3,0)
e=a.trim(4,0)
half=127
half1=128
# recreate the deblended frame out of 4 frames doing some subtractions
c2=subtract(d,e.levels(0,1,255,half,255)).levels(0,1,half,0,255)
c1=subtract(c,b.levels(0,1,255,half,255)).levels(0,1,half,0,255)
global cr=layer(c2,c1,"fast").subtitle("c").levels(1,1,255,0,249)
# create testclips to identify the blends
dc0=subtract(layer(e,c2,"fast"),d)
dc1=dc0.levels(0,1,half1,255,0)
dc2=dc0.levels(half1+1,3,255,0,255)
global dc=layer(dc1,dc2,"fast").converttoyv12()
cc0=subtract(layer(b,c1,"fast"),c)
cc1=cc0.levels(0,1,half1,255,0)
cc2=cc0.levels(half1+1,3,255,0,255)
global cc=layer(cc1,cc2,"fast").converttoyv12()
#conditionalfilter(cc,cr,d,"averageluma()","<","31.3")
#conditionalfilter(dc.trim(1,0),cr.trim(1,0),d,"averageluma()","<","31.3")
# replace the blended frames with the deblended ones
c10 = ScriptClip(c, "t")
c20 = FrameEvaluate(c10, "t=blendcc ? cr : t")
c30 = FrameEvaluate(c20, "t=blenddc ? cr.trim(1,0) : d")
c40 = FrameEvaluate(c30, "blenddc = (ALdc2 < ALdc1) && (ALdc2 < ALdc3) && (ALdc2 < ALdc0) && (ALdc2 < ALdc4) ")
c50 = FrameEvaluate(c40, "")
c51 = FrameEvaluate(c50, "blendcc = (ALcc2 < ALcc1) && (ALcc2 < ALcc3) && (ALcc2 < ALcc0) && (ALcc2 < ALcc4) ")
c52 = FrameEvaluate(c51, "")
c53 = FrameEvaluate(c52, "ALdc4=averageluma(dc.duplicateframe(0))")
c54 = FrameEvaluate(c53, "ALdc3=averageluma(dc)")
c55 = FrameEvaluate(c54, "ALdc1=averageluma(dc.trim(2,0))")
c56 = FrameEvaluate(c55, "ALdc0=averageluma(dc.trim(3,0))")
c57 = FrameEvaluate(c56, "ALdc2=averageluma(dc.trim(1,0))")
c58 = FrameEvaluate(c57, "")
c59 = FrameEvaluate(c58, "ALcc4=averageluma(cc.duplicateframe(0).duplicateframe(0))")
c60 = FrameEvaluate(c59, "ALcc3=averageluma(cc.duplicateframe(0))")
c70 = FrameEvaluate(c60, "ALcc1=averageluma(cc.trim(1,0))")
c80 = FrameEvaluate(c70, "ALcc0=averageluma(cc.trim(2,0))")
c90 = FrameEvaluate(c80, "ALcc2=averageluma(cc)")
c90.trim(2,0)
# diags
#d=d.converttoyv12()
#cr=cr.converttoyv12()
#f=stackvertical(d,cr,cc)
#g=stackvertical(d,cr.trim(1,0),dc.trim(1,0))
#stackhorizontal(g,f)
}
how the function works:
the input video almost looks like this:
FilmFrame: A B BC CD D E F FG GH H
Framenumber: a b c d e f g h i j
let's recreate FILMframe C:
at first recreate the part of C in frame d
C1 = 2*(d-e/2)
then recreate the part of C in frame c
C2 = 2*(c-b/2)
finally mix C1 & C2 to reduce noise and compression artifacts:
C = (C1+C2)/2
then we will try to do some blend-detection.
my first tries always were based using and edgemask to detect the blends, but this was
unreliable in low motion and som special cases.
now I recreate a blended frame out of the previously deblended frame:
this means d must equal to a blend of C2 (the part of C in frame c) and e [ ((C2+e)/2) ]
also c must equal to a blend of C1 (the part of C in frame d) and b [ ((C1+b)/2) ]
this means the following condition must fit:
(d == (C2+e)/2) ? C : d
(c == (C1+b)/2) ? C : c
but because were dealing not with numbers, we have to do more comparision:
I am building a difference map between d and ((C2+e)/2) and then do this comparision:
the luma of the current differencemap has to be smaller than the luma of its two previous frames
AND its two following frames.
If this condition is fit, the actual frame is a blend and gets replaced with C
the same is done using a difference map between c and ((C1+b)/2)
the output of the function will then look like this:
FilmFrame: A B C C D E F G G H
Framenumber: a b c d e f g h i j
finally you habe to apply decimate(5) after undoblendedtc() to get a smooth output.
Some limitations are present with the current method:
- on hybrid video, ie. music clips the methode wouldn't work fine
- on telecined film with LOTS of offset changes that function will leave blended frames on that offset changes
- flashes etc. aren't properly recognized making the output going crazy.
that originally was interlaced telecined FILM.
the function:
function undoblendedtc(clip input)
{
# prepare clips
a=loop(input).trim(0,5)+input
b=a.trim(1,0)
c=a.trim(2,0)
global d=a.trim(3,0)
e=a.trim(4,0)
half=127
half1=128
# recreate the deblended frame out of 4 frames doing some subtractions
c2=subtract(d,e.levels(0,1,255,half,255)).levels(0,1,half,0,255)
c1=subtract(c,b.levels(0,1,255,half,255)).levels(0,1,half,0,255)
global cr=layer(c2,c1,"fast").subtitle("c").levels(1,1,255,0,249)
# create testclips to identify the blends
dc0=subtract(layer(e,c2,"fast"),d)
dc1=dc0.levels(0,1,half1,255,0)
dc2=dc0.levels(half1+1,3,255,0,255)
global dc=layer(dc1,dc2,"fast").converttoyv12()
cc0=subtract(layer(b,c1,"fast"),c)
cc1=cc0.levels(0,1,half1,255,0)
cc2=cc0.levels(half1+1,3,255,0,255)
global cc=layer(cc1,cc2,"fast").converttoyv12()
#conditionalfilter(cc,cr,d,"averageluma()","<","31.3")
#conditionalfilter(dc.trim(1,0),cr.trim(1,0),d,"averageluma()","<","31.3")
# replace the blended frames with the deblended ones
c10 = ScriptClip(c, "t")
c20 = FrameEvaluate(c10, "t=blendcc ? cr : t")
c30 = FrameEvaluate(c20, "t=blenddc ? cr.trim(1,0) : d")
c40 = FrameEvaluate(c30, "blenddc = (ALdc2 < ALdc1) && (ALdc2 < ALdc3) && (ALdc2 < ALdc0) && (ALdc2 < ALdc4) ")
c50 = FrameEvaluate(c40, "")
c51 = FrameEvaluate(c50, "blendcc = (ALcc2 < ALcc1) && (ALcc2 < ALcc3) && (ALcc2 < ALcc0) && (ALcc2 < ALcc4) ")
c52 = FrameEvaluate(c51, "")
c53 = FrameEvaluate(c52, "ALdc4=averageluma(dc.duplicateframe(0))")
c54 = FrameEvaluate(c53, "ALdc3=averageluma(dc)")
c55 = FrameEvaluate(c54, "ALdc1=averageluma(dc.trim(2,0))")
c56 = FrameEvaluate(c55, "ALdc0=averageluma(dc.trim(3,0))")
c57 = FrameEvaluate(c56, "ALdc2=averageluma(dc.trim(1,0))")
c58 = FrameEvaluate(c57, "")
c59 = FrameEvaluate(c58, "ALcc4=averageluma(cc.duplicateframe(0).duplicateframe(0))")
c60 = FrameEvaluate(c59, "ALcc3=averageluma(cc.duplicateframe(0))")
c70 = FrameEvaluate(c60, "ALcc1=averageluma(cc.trim(1,0))")
c80 = FrameEvaluate(c70, "ALcc0=averageluma(cc.trim(2,0))")
c90 = FrameEvaluate(c80, "ALcc2=averageluma(cc)")
c90.trim(2,0)
# diags
#d=d.converttoyv12()
#cr=cr.converttoyv12()
#f=stackvertical(d,cr,cc)
#g=stackvertical(d,cr.trim(1,0),dc.trim(1,0))
#stackhorizontal(g,f)
}
how the function works:
the input video almost looks like this:
FilmFrame: A B BC CD D E F FG GH H
Framenumber: a b c d e f g h i j
let's recreate FILMframe C:
at first recreate the part of C in frame d
C1 = 2*(d-e/2)
then recreate the part of C in frame c
C2 = 2*(c-b/2)
finally mix C1 & C2 to reduce noise and compression artifacts:
C = (C1+C2)/2
then we will try to do some blend-detection.
my first tries always were based using and edgemask to detect the blends, but this was
unreliable in low motion and som special cases.
now I recreate a blended frame out of the previously deblended frame:
this means d must equal to a blend of C2 (the part of C in frame c) and e [ ((C2+e)/2) ]
also c must equal to a blend of C1 (the part of C in frame d) and b [ ((C1+b)/2) ]
this means the following condition must fit:
(d == (C2+e)/2) ? C : d
(c == (C1+b)/2) ? C : c
but because were dealing not with numbers, we have to do more comparision:
I am building a difference map between d and ((C2+e)/2) and then do this comparision:
the luma of the current differencemap has to be smaller than the luma of its two previous frames
AND its two following frames.
If this condition is fit, the actual frame is a blend and gets replaced with C
the same is done using a difference map between c and ((C1+b)/2)
the output of the function will then look like this:
FilmFrame: A B C C D E F G G H
Framenumber: a b c d e f g h i j
finally you habe to apply decimate(5) after undoblendedtc() to get a smooth output.
Some limitations are present with the current method:
- on hybrid video, ie. music clips the methode wouldn't work fine
- on telecined film with LOTS of offset changes that function will leave blended frames on that offset changes
- flashes etc. aren't properly recognized making the output going crazy.