Code:
Function MatchFrames(clip c,clip f,bool "Show",float "Thresh",float "Thresh2",string "FileName",string "LogFile", \
int "Start",int "Stop",bool "XP", Bool "Chroma",Float "ChromaWeight",string "Title",bool "Debug") {
ver = "v1.10 - 25 June 2014" # Version Number
myName ="MatchFrames: "
c = c.Killaudio() f = f.Killaudio() # Audio return not sensible.
Show = Default(Show,true) # Defaults true = show StackHorizontal find & found frames
Thresh = Float(Default(Thresh, 0.0)) # Threshold for declaring match, Default==0.0 to search for best possible match.
Thresh2 = Float(Default(Thresh2,0.0)) # Threshold2 Default==0.0, find clip f, not time ordered.
FileName = Default(FileName,"MatchFramesCmd.txt")# "" to switch OFF Frames output command file.
LogFile = Default(LogFile,"MatchFrames.Log") # "" to switch OFF Logfile.
Start = Default(Start,0) # Frame to start search from.
Stop = Default(Stop,0) # Frame to stop search (0 == last frame + 1).
XP = Default(XP,true) # Extra Paranoid, dont fully trust Thresh.
Chroma = Default(Chroma,True) # Default Luma + Chroma.
ChromaWeight= Float(Default(ChromaWeight,1.0/3.0)) # Default, 33% of Total Difference (Combined U and V).
Title = Default(Title,"") # Shown in log and command file, Default "".
Debug = Default(Debug,True) # Default no debug logging to DebugView
Start = (Start<0)?0:Start # Silent check
Stop=(Stop<=0||Stop>c.FrameCount)?c.FrameCount:Stop # Silent check
cfrms = Stop-Start # Number of frames to scan in c
ChromaWeight=(Chroma)?ChromaWeight:0.0 # If Not Needed, ignore default weight.
Assert(c.Width==f.Width,myName+"- Clip Width mismatch")
Assert(c.Height==f.Height,myName+"- Clip Height mismatch")
Assert(RT_VarIsSame(c,f),myName+"- ColorSpace Mismatch") # Same colorspace ?
Assert(cfrms>0,myName+"- Invalid Start>=Stop")
Assert(Thresh>=0.0,myName+"- Invalid Thresh")
Assert(f.Framecount<=cfrms,myName+"- f Frames > c[START,STOP] Frames")
Assert(ChromaWeight>=0.0&&ChromaWeight<=1.0,myName+"- ChromaWeight Range 0.0->1.0")
DBug = (cfrms==f.FrameCount && Thresh < Thresh2) # Debugging Mode ?
Thresh = (DBug) ? 255.0 : Thresh # If DBug, Ensure everything breaks Thresh.
Thresh2 = (DBug) ? 255.1 : Thresh2 # If DBug, Maintain Ordered.
TDHi =0.0 # DEBUG: TotalDifference Hi (Extra precision)
TDLo =0.0 # DEBUG: TotalDifference Lo
MinDiff = 255.0 + 1.0 # DEBUG: Output, max + 1
MaxDiff = 0.0 - 1.0 # DEBUG: Output, min - 1
Ordered = Thresh < Thresh2 # If true then: Find frames are in time order (match(f[n]) searches from match(f[n-1])+1
Result = 0 # Dummy, Will become a Clip, Dont use 'c.BlankClip(length=0)' (Maybe StackHorizontal)
Scanned = 0 # Init Total Frames scanned
EOL = Chr(10) # End Of Line. (Do NOT use Carriage Return, Line Feed, Will auto add the CR)
TLINE = "##########################################################################" + EOL
TGEN = "# MatchFrames " + Ver
TCMD = "Command File for FrameSel() Plugin." + EOL + "#" + EOL
TLOG = " (c) StainlessS : LogFile." + EOL + "#" + EOL
TNAM = (Title=="") ? "" : "# Title='" + Title + "'" + EOL + "#" + EOL
TSET = "# Width=" + String(c.Width) + " : Height=" + String(c.Height) + " : Start=" + String(Start) + " : Stop=" + String(Stop)
\ + ((ORDERED && !DBug) ? " : ORDERED" : "") + EOL+"#"+EOL
TSET2 = "# Thresh="+String(Thresh,"%.3f")+" : Thresh2="+String(Thresh2,"%.3f")+" : XP="+String(XP)
TSET3 = ((Chroma)?" : ChromaWeight="+String(ChromaWeight,"%.3f"):"") + EOL + "#" + EOL
TSET4 = ((DBug) ? "# DEBUG MODE" + EOL:"") + "#" + EOL
LogT = RT_String("%s#\n%s%s%s%s%s%s%s%s",TLINE,TGEN,TLOG,TNAM,TSET,TSET2,TSET3,TSET4,TLINE)
(Debug) ? RT_DebugF("%s",LogT,name=myName) : NOP # DebugView
(LogFile !="") ? RT_WriteFile(LogFile, "%s",LogT) : NOP
(FileName!="") ? RT_WriteFile(FileName,"%s#\n%s : %s%s%s%s%s%s%s",TLINE,TGEN,TCMD,TNAM,TSET,TSET2,TSET3,TSET4,TLINE) : NOP
FileName= (DBug) ? "" : FileName # No Command file frame numbers if DBug Mode, HEADER ONLY (ie Wipe command file).
TimeStart = RT_LocalTimeString(file=false) # Format "YYYY-MM-DD HH:MM:SS.mmm"
Start_Secs = RT_TimerHP()
GScript("""
for(ix = 0, f.Framecount-1) { # Iterate through find frames clip.
s = Start # Init Start search position
BestSoFar = s # Search start position
BestSoFarDiff = 255.0 + 1.0 # Maximum Possible + 1
sS =" S=" + String(s,"%-4.0f") # Start offset this time, as string (at least 4 digits)
mS=" (BM: )" # Init to 'scanned all the way' message (Flag Best Match).
eterm=(Ordered)?Stop-f.Framecount+ix:Stop-1 # Init last frame to compare for this iteration.
e=eterm # Init to 'scanned all the way'
Assert(s<=e,myName+"- Internal Error, s > e") # Oops.
xS="" # Prep XP skip Count string
for(n=s, eterm) { # Search c clip(s to eterm) or until satisfactory match found. (eterm evaluated on entry only)
# Difference to current find frame.
CurrDiff=RT_FrameDifference(c,f,n=n,n2=ix,ChromaWeight=ChromaWeight)
if(CurrDiff < BestSoFarDiff) { # Better match than found so far ?
BestSoFar = n # Remember frame number
BestSoFarDiff = CurrDiff # Remember difference
if(BestSoFarDiff <= thresh) { # Good enough to satisfy user requirement ?
if(DBug) { # DEBUG
MinDiff = Min(CurrDiff,MinDiff)
MaxDiff = Max(CurrDiff,MaxDiff)
TDLo = TDLo + CurrDiff
TDHi = TDHi + floor(TDLo / 256.0) # Hi part, Extra Precision
TDLo = frac(TDLo / 256.0) * 256.0 # Lo part, Extra Precision
}
e=BestSoFar # Where we've scanned to so far
mS=" (T1: )" # Found satisfactory frame, (Flag Thresh)
if(XP && BestSoFar < eterm && BestSoFarDiff > 0.0) { # DBug will NOT enter here!, eterm condition will fail.
tmp = BestSoFar # Remember
for(n=BestSoFar+1,eterm) {
CurrDiff=RT_FrameDifference(c,f,n=n,n2=ix,ChromaWeight=ChromaWeight)
e=n # We scanned more frames past original BestSoFar
if(CurrDiff < BestSoFarDiff) { # Better match ?
BestSoFar = n # Remember frame number
BestSoFarDiff= CurrDiff # Remember difference
} else {
n = eterm+1 # Force Exit with n == eterm + 2 (normally exit @ eterm+1)
}
}
if(BestSoFar != tmp) {
mS=" (T1:XP)" # Found satisfactory frame, (Flag Thresh, and Xtra Paranoid found better frame)
xS=" XPCNT=" + String(BestSoFar-tmp)# How many frames Xtra Paranoid skipped to find better match than Thresh.
}
}
Start=(Ordered)?BestSoFar+1:Start # If required, Next time search from BestSoFar + 1
n = eterm + 1 # Force Exit with current_frame == eterm + 2 (normally exit @ eterm + 1)
}
}
}
if(Ordered && n == eterm+1 && BestSoFarDiff < Thresh2) {
mS = " (BM:T2)" # For Log, Time ordered, (Flag, Best Match and Thresh2)
Start = BestSoFar + 1 # Next time start from BEST MATCH found + 1.
}
Scanned = Scanned + e - s + 1
Text = RT_String("%4d]=[%6d] Diff=%7.3f%s%s E =%-4.0f%s",ix,BestSoFar,BestSoFarDiff,ms,sS,e,xS)
(Debug) ? RT_DebugF("%s",Text,name=myName) : NOP # DebugView
(LogFile!="") ? RT_WriteFile(LogFile,"%s",Text,Append=true) : NOP # Log file
(FileName!="") ? RT_WriteFile(FileName,"%d",BestSoFar,Append=true) : NOP # Command file
FindFrame =f.Trim(ix,-1).SubTitle(Text)
wrClip =c.Trim(BestSoFar,-1)
GotFrame=(Show) ? StackHorizontal(FindFrame,wrClip) : wrClip
Result = (IsClip(Result)) ? Result ++ GotFrame : GotFrame # Append to 'result clip so far'.
}
""") # End Of GScript
TimeEnd = RT_LocalTimeString(file=false)
Tim = RT_TimerHP - Start_Secs
Text = RT_String("\n%s\nStart Time = %s\nEnd Time = %s\nTotal Time = %.3f Secs (%.3f Mins)\n",TLINE,TimeStart,TimeEnd,Tim,Tim/60.0)
Text2 = RT_String("Total Frames Searched =%6d (%7.3f FPS : %10.6f SPF)",Scanned,Scanned/Tim,Tim/Scanned)
Text3 = RT_String("Find Frames Located =%6d (%7.3f FPS : %10.6f SPF)",f.FrameCount,f.FrameCount/Tim,Tim/f.FrameCount)
(Debug) ? RT_DebugF("%s\n%s\n%s",Text,Text2,Text3,name=myName) : NOP
(LogFile!="") ? RT_WriteFile(LogFile,"%s\n%s\n%s",Text,Text2,Text3,Append=True) : NOP
Text4 = RT_String("DEBUG: AveDiff=%.3f : MinDiff=%.3f : MaxDiff=%.3f : [L=%.3f : (U+V)=%.3f]\n",
\ (TDHi / cfrms * 256.0) + (TDLo / cfrms),MinDiff,MaxDiff,1.0-ChromaWeight,ChromaWeight)
(DBug&&Debug) ? RT_DebugF("%s",Text4,name=myName) : NOP
(DBug&&LogFile!="") ? RT_WriteFile(LogFile,"%s",Text4,Append=True) : NOP
Return Result.AssumeFPS(1.0)
}