PDA

View Full Version : Script for video comparison


Alain2
4th November 2005, 14:18
Hi,

2 Days ago i was trying to see the differences between 2 short clips of the same scene that i encoded with slightly different parameters. So far I had a tidious method consisting of watching the movies separately and/or making screenshots of the same scene and compare them with visioflash. However this is not very efficient and also apparently (at least for x264 played with mpc) it is not possible to pause on a B-frame (so if the I/P frame is not on the exact same position on both clips i can't compare at this position). Getting bored of this, I decided to code a small script to compare my clips.

What it does : cut the frames in segments, distribute the 2 clips in these segments, and swap every n frames the clip shown in the segments.

I was actually very pleased with the results, which enabled me to easily compare 2 "similar" clips and actually see the differences (or the ~lack of them if my encoding parameters resulting in nearly no difference ^^ )

So if someone is interested, here is the script with a short syntaxe description in the header:

#
# Syntaxe :
#
# Function ClipComp(clip a, clip b, string "mode", bool "showSubs", \
# string "aSub", string "bSub", int "swapFreq")
#
# a,b : clips to be compared, must be same height and width, with at least div4 values
#
# mode : Comparison mode, syntaxe is "Nb_Of_Lignes x Nb_Of_Columns x separate"
# available modes : 1x1
# 2x1x0 , 2x1x1(Default) , 1x2x0 , 1x2x1 , 2x2x0 , 2x2x1
# 4x1x0 , 4x1x1 , 1x4x0 , 1x4x1 , 4x4x0 , 4x4x1
# 8x1x0 , 8x1x1 , 1x8x0 , 1x8x1
#
# showSubs [ True (Default) / False ] : Display clip subtitle on each segment
#
# aSub : User string to use as subtitle of clip a segments, Default = "1"
# bSub : User string to use as subtitle of clip b segments, Default = " 2"
#
# swapFreq : This determines the number of frames to be displayed
# before swapping occurs, Default = 1
#


Function ClipComp(clip a, clip b, string "mode", bool "showSubs", \
string "aSub", string "bSub", int "swapFreq")
{
### -= VARIABLES =- ###
mode = default( mode, "2x1x1" )
showSubs = Default( showSubs, true )
aSub = showSubs ? Default( aSub, "1" ) : ""
bSub = showSubs ? Default( bSub, " 2" ) : ""
global swapFreq = Default( swapFreq, 1 )

global swapParam = 0
global frameParam = 1
global frameParamNext = 1

### -= SCRIPT =- ###
ConditionalFilter(a,ClipComp2( a, b, mode, aSub, bSub ), \
ClipComp2( b, a, mode, bSub, aSub ), \
"swapParam", "=", "1")

FrameEvaluate("""(frameParam == 1) ? Eval("swapParam = (swapParam == 1) ? 0 : 1") : NOP()""")
FrameEvaluate("""global frameParamNext = ((frameParam / swapFreq) < 1) ? (frameParam + 1) : 1""")
FrameEvaluate("frameParam = true ? frameParamNext : 0")
}


Function ClipComp2(clip a, clip b, string "mode", string "aSub", string "bSub" )
{
hClip = a.height()
wClip = a.width()

(mode == "1x1") ? a.subtitle(aSub) \
\
: (mode == "2x1x0") ? stackvertical(a.crop(0,0*hClip/2,-0,-1*hClip/2).subtitle(aSub), \
b.crop(0,1*hClip/2,-0,-0*hClip/2).subtitle(bSub)) \
\
: (mode == "2x1x1") ? stackvertical(a.crop(0,0*hClip/2,-0,-1*hClip/2).subtitle(aSub).addborders(0,0,0,1), \
b.crop(0,1*hClip/2,-0,-0*hClip/2).subtitle(bSub).addborders(0,0,0,1)) \
\
: (mode == "1x2x0") ? stackhorizontal(a.crop(0*wClip/2,0,-1*wClip/2,-0).subtitle(aSub), \
b.crop(1*wClip/2,0,-0*wClip/2,-0).subtitle(bSub)) \
\
: (mode == "1x2x1") ? stackhorizontal(a.crop(0*wClip/2,0,-1*wClip/2,-0).subtitle(aSub).addborders(0,0,1,0), \
b.crop(1*wClip/2,0,-0*wClip/2,-0).subtitle(bSub).addborders(0,0,1,0)) \
\
: (mode == "2x2x0") ? stackvertical(stackhorizontal(a.crop(0*wClip/2,0*hClip/2,-1*wClip/2,-1*hClip/2).subtitle(aSub), \
b.crop(1*wClip/2,0*hClip/2,-0*wClip/2,-1*hClip/2).subtitle(bSub)), \
stackhorizontal(b.crop(0*wClip/2,1*hClip/2,-1*wClip/2,-0*hClip/2).subtitle(bSub), \
a.crop(1*wClip/2,1*hClip/2,-0*wClip/2,-0*hClip/2).subtitle(aSub))) \
\
: (mode == "2x2x1") ? stackvertical(stackhorizontal(a.crop(0*wClip/2,0*hClip/2,-1*wClip/2,-1*hClip/2).subtitle(aSub).addborders(0,0,2,2), \
b.crop(1*wClip/2,0*hClip/2,-0*wClip/2,-1*hClip/2).subtitle(bSub).addborders(0,0,2,2)), \
stackhorizontal(b.crop(0*wClip/2,1*hClip/2,-1*wClip/2,-0*hClip/2).subtitle(bSub).addborders(0,0,2,2), \
a.crop(1*wClip/2,1*hClip/2,-0*wClip/2,-0*hClip/2).subtitle(aSub).addborders(0,0,2,2))) \
\
: (mode == "4x1x0") ? stackvertical(a.crop(0,0*hClip/4,-0,-3*hClip/4).subtitle(aSub), \
b.crop(0,1*hClip/4,-0,-2*hClip/4).subtitle(bSub), \
a.crop(0,2*hClip/4,-0,-1*hClip/4).subtitle(aSub), \
b.crop(0,3*hClip/4,-0,-0*hClip/4).subtitle(bSub)) \
\
: (mode == "4x1x1") ? stackvertical(a.crop(0,0*hClip/4,-0,-3*hClip/4).subtitle(aSub).addborders(0,0,0,1), \
b.crop(0,1*hClip/4,-0,-2*hClip/4).subtitle(bSub).addborders(0,0,0,1), \
a.crop(0,2*hClip/4,-0,-1*hClip/4).subtitle(aSub).addborders(0,0,0,1), \
b.crop(0,3*hClip/4,-0,-0*hClip/4).subtitle(bSub).addborders(0,0,0,1)) \
\
: (mode == "1x4x0") ? stackhorizontal(a.crop(0*wClip/4,0,-3*wClip/4,-0).subtitle(aSub), \
b.crop(1*wClip/4,0,-2*wClip/4,-0).subtitle(bSub), \
a.crop(2*wClip/4,0,-1*wClip/4,-0).subtitle(aSub), \
b.crop(3*wClip/4,0,-0*wClip/4,-0).subtitle(bSub)) \
\
: (mode == "1x4x1") ? stackhorizontal(a.crop(0*wClip/4,0,-3*wClip/4,-0).subtitle(aSub).addborders(0,0,1,0), \
b.crop(1*wClip/4,0,-2*wClip/4,-0).subtitle(bSub).addborders(0,0,1,0), \
a.crop(2*wClip/4,0,-1*wClip/4,-0).subtitle(aSub).addborders(0,0,1,0), \
b.crop(3*wClip/4,0,-0*wClip/4,-0).subtitle(bSub).addborders(0,0,1,0)) \
\
: (mode == "8x1x0") ? stackvertical(a.crop(0,0*hClip/8,-0,-7*hClip/8).subtitle(aSub), \
b.crop(0,1*hClip/8,-0,-6*hClip/8).subtitle(bSub), \
a.crop(0,2*hClip/8,-0,-5*hClip/8).subtitle(aSub), \
b.crop(0,3*hClip/8,-0,-4*hClip/8).subtitle(bSub), \
a.crop(0,4*hClip/8,-0,-3*hClip/8).subtitle(aSub), \
b.crop(0,5*hClip/8,-0,-2*hClip/8).subtitle(bSub), \
a.crop(0,6*hClip/8,-0,-1*hClip/8).subtitle(aSub), \
b.crop(0,7*hClip/8,-0,-0*hClip/8).subtitle(bSub)) \
\
: (mode == "8x1x1") ? stackvertical(a.crop(0,0*hClip/8,-0,-7*hClip/8).subtitle(aSub).addborders(0,0,0,1), \
b.crop(0,1*hClip/8,-0,-6*hClip/8).subtitle(bSub).addborders(0,0,0,1), \
a.crop(0,2*hClip/8,-0,-5*hClip/8).subtitle(aSub).addborders(0,0,0,1), \
b.crop(0,3*hClip/8,-0,-4*hClip/8).subtitle(bSub).addborders(0,0,0,1), \
a.crop(0,4*hClip/8,-0,-3*hClip/8).subtitle(aSub).addborders(0,0,0,1), \
b.crop(0,5*hClip/8,-0,-2*hClip/8).subtitle(bSub).addborders(0,0,0,1), \
a.crop(0,6*hClip/8,-0,-1*hClip/8).subtitle(aSub).addborders(0,0,0,1), \
b.crop(0,7*hClip/8,-0,-0*hClip/8).subtitle(bSub).addborders(0,0,0,1)) \
\
: (mode == "1x8x0") ? stackhorizontal(a.crop(0*wClip/8,0,-7*wClip/8,-0).subtitle(aSub), \
b.crop(1*wClip/8,0,-6*wClip/8,-0).subtitle(bSub), \
a.crop(2*wClip/8,0,-5*wClip/8,-0).subtitle(aSub), \
b.crop(3*wClip/8,0,-4*wClip/8,-0).subtitle(bSub), \
a.crop(4*wClip/8,0,-3*wClip/8,-0).subtitle(aSub), \
b.crop(5*wClip/8,0,-2*wClip/8,-0).subtitle(bSub), \
a.crop(6*wClip/8,0,-1*wClip/8,-0).subtitle(aSub), \
b.crop(7*wClip/8,0,-0*wClip/8,-0).subtitle(bSub)) \
\
: (mode == "1x8x1") ? stackhorizontal(a.crop(0*wClip/8,0,-7*wClip/8,-0).subtitle(aSub).addborders(0,0,1,0), \
b.crop(1*wClip/8,0,-6*wClip/8,-0).subtitle(bSub).addborders(0,0,1,0), \
a.crop(2*wClip/8,0,-5*wClip/8,-0).subtitle(aSub).addborders(0,0,1,0), \
b.crop(3*wClip/8,0,-4*wClip/8,-0).subtitle(bSub).addborders(0,0,1,0), \
a.crop(4*wClip/8,0,-3*wClip/8,-0).subtitle(aSub).addborders(0,0,1,0), \
b.crop(5*wClip/8,0,-2*wClip/8,-0).subtitle(bSub).addborders(0,0,1,0), \
a.crop(6*wClip/8,0,-1*wClip/8,-0).subtitle(aSub).addborders(0,0,1,0), \
b.crop(7*wClip/8,0,-0*wClip/8,-0).subtitle(bSub).addborders(0,0,1,0)) \
\
: (mode == "4x4x0") ? stackvertical(stackhorizontal(a.crop(0*wClip/4,0*hClip/4,-3*wClip/4,-3*hClip/4).subtitle(aSub), \
b.crop(1*wClip/4,0*hClip/4,-2*wClip/4,-3*hClip/4).subtitle(bSub), \
a.crop(2*wClip/4,0*hClip/4,-1*wClip/4,-3*hClip/4).subtitle(aSub), \
b.crop(3*wClip/4,0*hClip/4,-0*wClip/4,-3*hClip/4).subtitle(bSub)), \
stackhorizontal(b.crop(0*wClip/4,1*hClip/4,-3*wClip/4,-2*hClip/4).subtitle(bSub), \
a.crop(1*wClip/4,1*hClip/4,-2*wClip/4,-2*hClip/4).subtitle(aSub), \
b.crop(2*wClip/4,1*hClip/4,-1*wClip/4,-2*hClip/4).subtitle(bSub), \
a.crop(3*wClip/4,1*hClip/4,-0*wClip/4,-2*hClip/4).subtitle(aSub)), \
stackhorizontal(a.crop(0*wClip/4,2*hClip/4,-3*wClip/4,-1*hClip/4).subtitle(aSub), \
b.crop(1*wClip/4,2*hClip/4,-2*wClip/4,-1*hClip/4).subtitle(bSub), \
a.crop(2*wClip/4,2*hClip/4,-1*wClip/4,-1*hClip/4).subtitle(aSub), \
b.crop(3*wClip/4,2*hClip/4,-0*wClip/4,-1*hClip/4).subtitle(bSub)), \
stackhorizontal(b.crop(0*wClip/4,3*hClip/4,-3*wClip/4,-0*hClip/4).subtitle(bSub), \
a.crop(1*wClip/4,3*hClip/4,-2*wClip/4,-0*hClip/4).subtitle(aSub), \
b.crop(2*wClip/4,3*hClip/4,-1*wClip/4,-0*hClip/4).subtitle(bSub), \
a.crop(3*wClip/4,3*hClip/4,-0*wClip/4,-0*hClip/4).subtitle(aSub))) \
\
: (mode == "4x4x1") ? stackvertical(stackhorizontal(a.crop(0*wClip/4,0*hClip/4,-3*wClip/4,-3*hClip/4).addborders(0,0,1,1).subtitle(aSub), \
b.crop(1*wClip/4,0*hClip/4,-2*wClip/4,-3*hClip/4).addborders(0,0,1,1).subtitle(bSub), \
a.crop(2*wClip/4,0*hClip/4,-1*wClip/4,-3*hClip/4).addborders(0,0,1,1).subtitle(aSub), \
b.crop(3*wClip/4,0*hClip/4,-0*wClip/4,-3*hClip/4).addborders(0,0,1,1).subtitle(bSub)), \
stackhorizontal(b.crop(0*wClip/4,1*hClip/4,-3*wClip/4,-2*hClip/4).addborders(0,0,1,1).subtitle(bSub), \
a.crop(1*wClip/4,1*hClip/4,-2*wClip/4,-2*hClip/4).addborders(0,0,1,1).subtitle(aSub), \
b.crop(2*wClip/4,1*hClip/4,-1*wClip/4,-2*hClip/4).addborders(0,0,1,1).subtitle(bSub), \
a.crop(3*wClip/4,1*hClip/4,-0*wClip/4,-2*hClip/4).addborders(0,0,1,1).subtitle(aSub)), \
stackhorizontal(a.crop(0*wClip/4,2*hClip/4,-3*wClip/4,-1*hClip/4).addborders(0,0,1,1).subtitle(aSub), \
b.crop(1*wClip/4,2*hClip/4,-2*wClip/4,-1*hClip/4).addborders(0,0,1,1).subtitle(bSub), \
a.crop(2*wClip/4,2*hClip/4,-1*wClip/4,-1*hClip/4).addborders(0,0,1,1).subtitle(aSub), \
b.crop(3*wClip/4,2*hClip/4,-0*wClip/4,-1*hClip/4).addborders(0,0,1,1).subtitle(bSub)), \
stackhorizontal(b.crop(0*wClip/4,3*hClip/4,-3*wClip/4,-0*hClip/4).addborders(0,0,1,0).subtitle(bSub), \
a.crop(1*wClip/4,3*hClip/4,-2*wClip/4,-0*hClip/4).addborders(0,0,1,0).subtitle(aSub), \
b.crop(2*wClip/4,3*hClip/4,-1*wClip/4,-0*hClip/4).addborders(0,0,1,0).subtitle(bSub), \
a.crop(3*wClip/4,3*hClip/4,-0*wClip/4,-0*hClip/4).addborders(0,0,1,0).subtitle(aSub))) \
\
: Eval("""return a.subtitle("This mode doesn't exit")""")
}
btw personnaly I saved this script in a file named ClipComp.avsi in the avisynth plugins folder, so it auto import itself in any script ;)

A few notes:

- This is not a robust script (it started as a personal script but I think it can be useful for others), meaning it doesn't check if the 2 clips you compare are of the same dimension, etc... So the default error message in these cases may not be explicit

- Clips a and b must have the same dimensions (well no point of comparing otherwise anyway), and have dimensions at least div4, even div 8 if using modes 8x1x? or 1x8x?

- Speed of the script is relatively fast (real time on my amd xp1800+; tested with clips encoded in x264 and imported via directshowsource) for all modes except 1x1 (at least for me on my little cpu when swapFreq is set > 2 ). I didn't test this script thoroughly (as said above, I started writing it 2 days ago on my spare evening time ^^ ). It may take one or a few seconds at startup before getting real time playing.

- swapFreq parameter is set at 10 (swap every 10 frames), but will accept any integer. Thus setting it at 1 can be useful if you want to see at a particular position the difference between the 2 scripts (in that case pause the film and move frame by frame to see the difference). On the other hand, setting it at 10000000 for instance will cancel the swap functionnality (unless your clip is longer than 10000000 frames ^^ ). Other faster method to cancel swapping: just call directly ClipComp2 ;)

- 2x1x1 and 4x1x1 modes are particularly efficient IMHO. Also cutting the frame in smaller sections makes the differences between the 2 clips less obvious, well at least it's my impression after testing it on my samples, but I didn't use much the script yet ^^

Example of a script using this ClipComp.avsi:
a=directshowsource("video1.mp4")
b=directshowsource("video2.mp4")

ClipComp(a,b)

If someone here thinks my script is not well optimised / nicely written, I am eager to learn ;)

Alain2

neuron2
4th November 2005, 20:21
(Mod bump after forum move.)

Richard Berg
16th November 2005, 05:13
Nice function -- thanks for sharing! I use something similar, rb-boxcompare.avsi


# BoxCompare
#
# Compares up to 4 clips at once, with optional descriptive text overlaid.
#
#
#
# PARAMETERS:
# "ul", "ur", "ll", "lr" : Upper-left clip, upper-right clip, etc.
# If any of these is omitted, it will be filled in
# by blank space. At least ul is required, though.
#
# Source clips must have the same height & colorspace,
# as well as the same width if more than 2 are compared.
# If not, they will be forced into compliance! :)
# [shouldn't do anything bad unless you're mixing
# progressive & interlaced clips, in which case conversions
# might be suboptimal]
#
# "ulTxt", "urTxt", "llTxt", "lrTxt" : Optional text descriptions for each
# clip.
#
# USAGE:
# # Compare three clips, leaving the lower-left quadrant blank
# # Output clip is RGB32 (for accurate viewing in VDub), 2x the height/width of clip1.
# BoxCompare(clip1, clip2, lr = clip3)
#
# # Compare two clips (one row side by side, equivalent to StackHorizontal) with
# text overlaid on one of them.
# BoxCompare(clip1, clip2, urTxt = "clip 2")
#
#
Function BoxCompare(clip ul, clip "ur", clip "ll", clip "lr",
\ string "ulTxt", string "urTxt", string "llTxt", string "lrTxt")
{
ul = ul.ConvertToRGB32()
doRight = Defined(ur)
doLower = Defined(ll) || Defined(lr)

ur = Defined(ur)
\ ? ur.LanczosResize(ul.width, ul.height).ConvertToRGB32()
\ : BlankClip(ul)
ll = Defined(ll)
\ ? ll.LanczosResize(ul.width, ul.height).ConvertToRGB32()
\ : BlankClip(ul)
lr = Defined(lr)
\ ? lr.LanczosResize(ul.width, ul.height).ConvertToRGB32()
\ : BlankClip(ul)

ul = Defined(ulTxt)
\ ? ul.Subtitle(ulTxt)
\ : ul
ur = Defined(urTxt)
\ ? ur.Subtitle(urTxt)
\ : ur
ll = Defined(llTxt)
\ ? ll.Subtitle(llTxt)
\ : ll
lr = Defined(lrTxt)
\ ? lr.Subtitle(lrTxt)
\ : lr

out = (doLower)
\ ? StackVertical( StackHorizontal(ul, ur), StackHorizontal(ll, lr) )
\ : (doRight) ? StackHorizontal(ul, ur)
\ : ul
return out
}


# FileCompare
#
# Wrapper that simplifies a common usage of BoxCompare: comparing unprocessed AVI files.
#
# PARAMETERS:
# "a", "b", "c", "d" : Strings that represent both (1) the filename of AVIs to be
# read, and (2) the text that will be overlaid. The first parameter
# is mandatory. If you define parameter #x, you must also define #(x-1)
# or it will be ignored.
#
# USAGE:
# # Quickly compare 2 files
# FileCompare("foo.avi", "bar.mp4")
#
# NOTE:
# If path names make the subtitles awkward, consider using SetWorkingDir.
#
#
function FileCompare(string "a", string "b", string "c", string "d")
{
return Defined(b)
\ ? Defined(c)
\ ? Defined(d)
\ ? BoxCompare(ChooseSource(a), ChooseSource(b), ChooseSource(c), ChooseSource(d), a, b, c, d)
\ : BoxCompare(ChooseSource(a), ChooseSource(b), ChooseSource(c), ulTxt=a, urTxt=b, llTxt=c)
\ : BoxCompare(ChooseSource(a), ChooseSource(b), ulTxt=a, urTxt=b)
\ : BoxCompare(ChooseSource(a), ulTxt=a)
}

function ChooseSource(string "filename")
{
ext = RightStr(filename, 3)
return ext == "avi" || ext == "avs" || ext == "wav"
\ ? AviSource(filename)
\ : DirectShowSource(filename)
}


Feel free to contribute to Avisynth.org/ShareFunctions. It's poorly organized right now (anyone want to refactor?), but there are some cool scripts up there.