Log in

View Full Version : DirtBox_MI v0.03 - 4th Sept 2018


StainlessS
2nd September 2018, 18:31
Prompted by this thread (Thread owner GreyMouse):- https://forum.doom9.org/showthread.php?t=175640

Fixes damage like this (two consecutive damaged frames, left fixed with metrics, right bad source, middle Zebra dc detection clip)

Frame Before Bad Frames pair [showing LHS detection metric of 4.598, and hilited 'L' flag denoting left Source frame for interpolation]
https://s20.postimg.cc/4ox3enegp/Dirt_Box_MI_v0.03_165.jpg (https://postimg.cc/image/4ox3enegp/)
First of bad frame pair
https://s20.postimg.cc/u7pfrosvt/Dirt_Box_MI_v0.03_166.jpg (https://postimg.cc/image/u7pfrosvt/)
Second of bad frame pair
https://s20.postimg.cc/51ohkuzbd/Dirt_Box_MI_v0.03_167.jpg (https://postimg.cc/image/51ohkuzbd/)
Frame after bad frame pair [showing RHS detection metric of 2.303, and hilited 'R' flag denoting right Source frame for interpolation]
https://s20.postimg.cc/5r79x87kp/Dirt_Box_MI_v0.03_168.jpg (https://postimg.cc/image/5r79x87kp/)
(Above top frame, LHS detection [left of damage detection is shown in the rhs metric], the damage is to the right, bottom frame RHS detection, the damage is to the left)

Or Flash Frame Photography (static or near static cam), where Th arg might want to be somewhat bigger.
Adjust Th setting by viewing RHS RAT metrics on frame before bad frames, so that it is below the lowest bad frame metrcs ratio.

Post #1 of 4

DirtBox_MI_v0.03.avs

Function DirtBox_MI(clip c,clip "dc",Float "Th",Float "ThMin",Int "MinLen",Int "MaxLen",
\ Int "SPad", Int "SSharp", Int "SRFilter", [* MSuper *]
\ Int "ABlkSize", Int "AOverlap", Int "ASearch",Int "ADct", [* MAnalyse *]
\ Int "RBlkSize", Int "ROverlap", Int "RthSAD", [* MRecalculate *]
\ Float "Iml", Bool "IBlend", Int "IthSCD1", Int "IthSCD2", [* MFlowInter *]
\ Int "OthSCD1",Int "OthSCD2", [* Detect Override *]
\ bool "Show",Int "Chop",String "SurgeonFile",String "OverrideFile"
\ ) {
/*
DirtBox_MI(), v0.03

[ Take your time saying the name out aloud when in public place ].

Req:- GScript OR Avs+, Grunt, MvTools2, Masktools2, RemoveGrain, CallCmd, RT_Stats v1.43.
CallCmd() optional, will auto delete DBase on clip closure if present.
YUV Only (well whatever MvTools2 + MaskTools2 supports).

Function DirtBox_MI(clip c,clip "dc"=c,Float "Th"=1.3,Float "ThMin"=0.5,Int "MinLen=1",Int "MaxLen"=2,
\ Int "SPad"=16, Int "SSharp"=1, Int "SRFilter"=4, [* MSuper *]
\ Int "ABlkSize"=16, Int "AOverlap"=4, Int "ASearch"=3,Int "ADct"=0, [* MAnalyse *]
\ Int "RBlkSize"=8, Int "ROverlap"=2, Int "RthSAD"=100, [* MRecalculate *]
\ Float "Iml"=200.0, Bool "IBlend"=True, Int "IthSCD1"=400, Int "IthSCD2"=130, [* MFlowInter *]
\ Int "OthSCD1"=400,Int "OthSCD2"=130, [* Detect Override *]
\ bool "Show",Int "Chop",String "SurgeonFile",String "OverrideFile"
\ )

Args:-
c, Source clip.
dc, Detection Clip, Default c. Must have same number of frames as Src c, no other requirement.
Intent to use eg Zebra Bands to better tune for detection, Used only for primary detect of
leftmost and rightmost frames where damage occurs.

Th, Bad Ratio Threshold, default 1.3. If eg Cur->Nxt / Max(Prv->Cur,0.001) > Th then Nxt
frame is possble 1st of damaged frames (but Cur->Nxt must also be >= ThMin as described below).
Suggest much higher for flash frame detection (probably dont need Zebra dc clip)..
Default Th of 1.3 is VERY sensitive, take care with this setting, it could well chew up your
prized video clip.
ThMin, Difference of Cur->Nxt must be at least this to detect Left 'breakup'. default 0.5.
(Avoid detection where current frame is a duplicate of prev frame and Cur->Nxt diff is very small.)
[Same in reverse for detecting RHS breakup]. Do Not set to 0.0, ie OFF.

MinLen, Minimum length of damaged sequence to fix, default 1.
MaxLen, Maximum length of damaged sequence to fix, default 2 (1 <= MinLen <= MaxLen <= 9).
MaxLen provides the maximum supported by the FrameSurgeon script, ie 9, and so will not be
increased further.
Suggest only use MaxLen of 9 where Th is really quite high, bare minimum of maybe 5.0 or
perhaps even 10.0, even then is probably a bit dodgy.

MSuper args:
SPad, Default 16, See MvTools MSuper(hpad=8,vpad=8) [We use same for both].
SSharp, Default 1, See MvTools MSuper(sharp=2)
SRFilter, Default 4, See MvTools MSuper(rfilter=2)

MAnalyse args:
ABlkSize, Default 16, See MvTools MAnalyse(BlkSize=8,BlkSizeV=8) [We use same for both].
AOverLap, Default 4, See MvTools MAnalyse(OverLap=0)
ASearch, Default 3, See MvTools MAnalyse(Search=4)
ADct, Default 0, See MvTools MAnalyse(Dct=0). Using other than default value can be VERY slow.

MRecalculate args:
RBlkSize, Default 8, See MvTools MRecalculate(BlkSize)
NOTE, RBlkSize = 0 Will Switch OFF MRecalculate, probably a bit faster at possible expense of quality.
ROverlap, Default 2, See MvTools MRecalculate(Overlap)
RthSAD, Default 100, See MvTools MRecalculate(thSAD=200)

MFlowInter args:
Iml, Default 200.0, See MvTools MFlowInter(ml=100.0)
IBlend, Default True, See MvTools MFlowInter(Blend=True). Blend or copy frame at scene change.
IthSCD1, Default 400, See MvTools MFlowInter(thSCD1=400) : (0 < IthSCD1 <= (8*8)*255)
IthSCD2, Default 130, See MvTools MFlowInter(thSCD2=130) : (0 < IthSCD2 <= 255)
Threshold which sets how many blocks have to change for the frame to be considered as a
scene change. It is ranged from 0 to 255, 0 meaning 0 %, 255 meaning 100 %.
Default is 130 (which means 51.0%).
Used by MvTools2 MFlowInter during interpolation.

Detection Override args:
OthSCD1, Default 400. as MVTools MSCDetection arg. (0 < OthSCD1 <= (8*8)*255). See MvTools2.
OthSCD2, Default 130. as MVTools MSCDetection arg. (0 < OthSCD2 <= 255)
These two thresholds govern whether frames either side of bad sequence are determined to be
from the same scene, if not from Same scene then detection is Overidden and 'E' flag is set in metrics,
on what would have been the 1st/leftmost interpolation Source frame.
NOTE, If OthSCD1 > 8*8*255 or OthSCD2 > 255 then switches OFF scne change testing, will NOT override
any detections that seem to have good frames either end from different scenes. (will likely
result in blended scene transitions).

Metrics Args:
Show, Show Metrics, default True.
Chop, Show Mode if Show=True,
0 Normal
1 Left half of frame is fixed, Right original.
2 Top half of frame is fixed, Bottom original.

File Output args:
SurgeonFile, Default "FrameSurgeonCmd.txt". If Non "", then writes a FrameSurgeon() command file to
recreate result, so can manually edit the result file and re-render in FrameSurgeon.
[part of the Sawbones/FrameSurgeon combo which also requires ClipClop()].
If using Multi-instances, each instance should have its own SurgeonFile file.

OverrideFile, Default "Overridden.txt". Detected Sequences that were Overridden because the interpolation
source frames seemed to be from Different scenes
[In FrameSurgeon() format,
"I2 666" would be two frame bad seq starting at frame 666, where interp src frames would have
been frames 665 and 668].

Dc clip implemented to assist in detection, where can use eg Zebra() to create a 'tuned to error' diagnostic clip.
Most damage will likely occur horizontally (in eg VHS tracking gunk), and so we might use Zebra Default Row=true
to create a vertical set of Zebra bars (same height as clip c), and can use the Zebra args, Threshold, Lo and Hi,
to tune some of the bars to better reveal where errors occur. If any of the bars seem of no use, then best
get rid of those bars so as not to 'water down' the detection metrics.
For original 'GreyMouse' (a user) clip, YPlaneMin bar is always black and so was cropped off for dc detection
clip, however, YPlaneMin bar might actually be useful in such case if border cropped version of clip were given
to Zebra() for the DC detection clip. So, where a clip has horizontal damage and black borders left and right, then
crop off those borders for creation of Zebra bars, but keep them in original clip. (if vertical damage and top/bot
black borders, then do the same cropping top and bottom borders instead, and of course set Zebra(Row=false)).

May be able to detect several kinds of frame corruption, including flash frames, up to 9 frames catered for.
Where it can detect flash frames, will only likely be possible where frames are from the SAME SCENE, not two
different scenes separated by white gunk.
Dc clip probably not needed for flash photography flashes.
To deal with Interlaced clip, can eg SeparateFields / SelectEven/Odd and handle each separately, then weave them
back together again afterwards. Is multi-instance capable and so (despite use of global vars) will work just fine.
Probably better to not jump about, jumping into middle of bad sequence will start detect from jump point,
but OK to go backwards where already visited.

Shows Flags where hi-lited,
E = End Of Scene
Shown only on 1st detected Interpolation Src frame (frame before bad sequence starts).
Override where detected bad seq interpolation source frames are NOT from same scene (by OthSCD1 and OthSCD2)
0 = Any Digit 0 to 9, 0 when E flag not hi-lited, else digit representing length of bad sequence
that was Overridden as for E flag above.
L = Left Intep Src frame
R = Right Interpolate Src frame,
* = Interpolated frame.
*/
c myName="DirtBox_MI: "
IsAvsPlus=(FindStr(UCase(versionString),"AVISYNTH+")!=0) HasGScript=RT_FunctionExist("GScript")
HasGRunt =RT_FunctionExist("GScriptClip") HasMvTools=RT_FunctionExist("MSuper")
HasMaskTools=RT_FunctionExist("MT_Lutxy")
HasRemoveGrain=RT_FunctionExist("RemoveGrain") HasCallCmd = RT_FunctionExist("CallCmd")
Assert(IsAvsPlus || HasGScript,RT_String("%sNeed either GScript or AVS+",myName))
Assert(HasGRunt,RT_String("%sNeed GRunt:-https://forum.doom9.org/showthread.php?t=139337 ",myName))
Assert(HasMVTools,RT_String("%sNeed MvTools2:-http://forum.doom9.org/showthread.php?t=131033",myName))
Assert(HasMaskTools,RT_String("%sNeed MaskTools",myName))
Assert(HasRemoveGrain,RT_String("%sNeed RemoveGrain",myName))
dc = Default(dc,c)
Th = Default(Th,1.3) ThMin = Default(ThMin,0.5)
MinLen = Default(MinLen,1) MaxLen = Default(MaxLen,2)
SPad = Default(SPad,16) SSharp = Default(SSharp,1) SRFilter= Default(SRFilter,4)
ABlkSize= Default(ABlkSize,16) AOverlap= Default(AOverlap,4) ASearch = Default(ASearch,3) ADct = Default(ADct,0)
RBlkSize= Default(RBlkSize,8) ROverlap= Default(ROverlap,2) RthSAD = Default(RthSAD,100)
Iml = Default(Iml,200.0) IBlend = Default(IBlend,True) IthSCD1 = Default(IthSCD1,400) IthSCD2= Default(IthSCD2,130)
OthSCD1 = Default(OthSCD1,400) OthSCD2 = Default(OthSCD2,130)
Show = Default(Show, True) Chop = Default(Chop, 0)
SurgeonFile = Default(SurgeonFile, "FrameSurgeonCmd.txt")
OverrideFile = Default(OverrideFile,"OverrideFile.txt")
Assert(dc.Framecount==c.FrameCount,RT_String("%sdc.FrameCount does not match",myName))
Assert(1 <= MinLen <= MaxLen <= 9,RT_String("%s1 <= MinLen(%d) <= MaxLen(%d) <= 9",myName,MinLen,MaxLen))
Chop = Show ? Min(Max(Chop,0),2) : 0



EDIT: SurgeonFile looks like this where frame 166 is 2 frame interpolated seq (the two frames shown in above images)

###
# FrameSurgeon() Command file [ Generated by DirtBox() ]
###

I1 71
I1 88
I1 94
I1 116
I2 166



Also available in zip via MediaFire or SendSpace in my sig below this post. (~8KB)

StainlessS
2nd September 2018, 18:31
post #2 of 4 (append to part 1)


Func_S="""
Function EndOfSceneClip@@@(clip c,Int "thSCD1",Int "thSCD2") { # All Luma Samples set 255 at EOS
thSCD1=Default(thSCD1,400) thSCD2=Default(thSCD2,130)
sup=c.MSuper(pel=1,sharp=0,rfilter=2,hpad=16, vpad=16)
bv=sup.MAnalyse(isb=true, delta=1,blksize=16)
Return c.MSCDetection(bv,thSCD1=thSCD1,thSCD2=thSCD2)
}
Function Fn@@@(clip c,clip dc,String DB,Float Th,Float ThMin,Int MinLen,Int MaxLen,Bool Show) {
c i=current_frame # Using i, (avoid confusion with n of Next)
Stat=RT_DBaseGetField(DB,i,0)
if(Stat == 0) { # In here if unvisited
Stat=1 EOSOS_L=0 # Default to Src if not found
# Here testing i as possible Left Interpolation Src frame,
# Dc_iNum = dc(i),dc(i+1) is numerator in LHS Ratio, dc(i-1),dc(i) is divisor.
Dc_iNum = RT_LumaDifference(dc,dc,n=i,n2=i+1) # dc(i), dc(i+1)
if (Dc_iNum >= ThMin) { # Check not likely silly numbers ratio by ThMin
Dc_iDiv = RT_LumaDifference(dc,dc,n=i-1,n2=i) # dc(i-1), dc(i)
LftRat = Dc_iNum / Max(Dc_iDiv,0.001)
if (LftRat >= Th) { # Got Left suspect breakup @ i
# We have possible bad frames LEFT EDGE Interp Src @ i, as tested via DC detection clip
# Get C_iNum [c[i],c(i+1)] here avoid fetching many times below (it dont change, real clip NOT dc)
C_iNum = RT_LumaDifference(c,c,n=i,n2=i+1) # c(i),c(i+1)
BestLen=0 BestMax=256.0
ETerm=Min(i+MaxLen,FrameCount-3) # Last possible INTERPOLATED BAD frame position
for(j=i+1,ETerm) { # Scan up to MaxLen frames(stop if not unknown nor Src)
if(RT_DBaseGetField(DB,j,0)<=1) { # Unknown or Src frame, else break
# Here testing j as possible rightmost bad frame,
# dc(j),dc(j+1) is numerator in RHS Ratio, dc(j+1),dc(j+2) is divisor.
Dc_jNum = RT_LumaDifference(dc,dc,n=j,n2=j+1) # dc(j),dc(j+1)
if(Dc_jNum >= ThMin) { # Check not likely silly numbers ratio by ThMin
Dc_jDiv = RT_LumaDifference(dc,dc,n=j+1,n2=j+2) # dc(j+1),dc(j+2)
RgtRat = Dc_jNum / Max(Dc_jDiv,0.001)
if(RgtRat >= Th) { # Got Right suspect breakup, last bad frame @ j
# Here, we are DONE with dc clip, we now switch to real c and MC clips.
# We have possible bad frames as tested via DC detection clip via lft and rgt Dif Ratios.
# i = Left Interp Src frame. j = possible Right Interp Src frame - 1, ie last bad frame.
BadLen = j - i # current suspect bad length frames
# Select MC clip for interpolation of 1st (left) for BadLen frames.
# Eg, M31@@@ is frame 1 (1st frame) for 3 frame interpolation
# (Builtin Arrays would be real handy)
ML_c = BadLen==1?M11@@@:BadLen==2?M21@@@:BadLen==3?M31@@@
\ : BadLen==4?M41@@@:BadLen==5?M51@@@:BadLen==6?M61@@@
\ : BadLen==7?M71@@@:BadLen==8?M81@@@:M91@@@
# Compare c clip i with MC clip i+1, ie potential Interp src with 1st MC interp frame.
# M_iNum = c(i),ML_c(i+1) is numerator equiv for LHS Ratio,
# NOTE, c(i-1),c(i) would be CONSTANT LHS ratio divisor, so only need compare Numerators
M_iNum=RT_Lumadifference(c,ML_c,n=i,n2=i+1) # c(i),ML_c(i+1)
if(M_iNum < BestMax) { # Better than BestSoFar ?
# Now test whether 1st Interp also better matches 1st InterpSrc
if(M_iNum < C_iNum) {
# Now do same for RHS
# Get Last (Rgt) MC clip for interpolation of BadLen frames.
MR_c = BadLen==1?M11@@@:BadLen==2?M22@@@:BadLen==3?M33@@@
\ : BadLen==4?M44@@@:BadLen==5?M55@@@:BadLen==6?M66@@@
\ : BadLen==7?M77@@@:BadLen==8?M88@@@:M99@@@
M_jNum = RT_Lumadifference(MR_c,c,n=j,n2=j+1) # MR_c(j),c(j+1)
if(M_jNum < BestMax) { # Better than BestSoFar ?
# Now test whether last Interp better matches Last InterpSrc
C_jNum = RT_LumaDifference(c,c,n=j,n2=j+1) # c(j), c(j+1)
if(M_jNum < C_jNum) {
# NOTE, Max of the two,
# Left MC has to be better than both Left bad & BestMax,
# but not necesarily better than Best right match.
BestMax=Max(M_iNum,M_jNum)
BestLen=BadLen # Bingo !, At least So Far.
}
}
}
}
} # End, if(RgtRat >= Th)
} # if(Dc_jNum >= ThMin)
} Else { j = Eterm } # Break
} # End, for(j=i+1,ETerm)
if(BestLen>=MinLen) {
Proc = OthSCD2@@@ > 255 || OthSCD1@@@ > 8*8*255 # EOS detection switched OFF ?
If(!Proc) { # NO, check for scene change between Interp Src frames
Tmp_c = c.Trim(i-1,-2) + c.Trim(i+BestLen+1,-2)
Proc = EndOfSceneClip@@@(Tmp_c,OthSCD1@@@,OthSCD2@@@).RT_AverageLuma(n=1,w=1,h=1).Int == 0
}
if(Proc) {
Stat=2 # Left Interp Src
RT_DBaseSet(DB,i,Stat,0,BestLen,0)
# Calculate DBase Stat clip Index for first interpolated frame for BestLen interpolated seq.
# Below, Terminal() function is sort of like Factorial, except for addition.
# Terminal(n) = (n * ((n + 1) / 2) : Get Sum of +ve integer numbers up to n
# However we want Terminal(n-1) + lowest DB Stat Index of lowest MC clip, ie M11@@@, which is 4
# So, is ((n-1)*n/2) + 4
BadStart = (BestLen-1)*BestLen/2 + 4
# Set DBase data for frames i+1 to i + BestLen [ie bad frames]
for(k=0,BestLen-1) {
RT_DBaseSet(DB,i+1+k,BadStart+k,0,BestLen,k+1)
}
RT_DBaseSet(DB,i+BestLen+1,3,0,BestLen,0) # Right Interp Src
if(SurgeonFile@@@!="") { RT_WriteFile(SurgeonFile@@@,"I%d %d",BestLen,i+1,Append=True) }
} Else {
EOSOS_L=BestLen # Scene Change Override, (flag length of aborting detection)
if(OverrideFile@@@!="") { RT_WriteFile(OverrideFile@@@,"I%d %d",BestLen,i+1,Append=True) }
}
} # End if BestLen >= MinLen
} # End, if (LftRat >= Th)
}
if(Stat == 1) { # Defaulted Src, with Override if Scene Change detected between Interp Src frames.
RT_DBaseSet(DB,i,Stat,EOSOS_L,0,0)
}
# HERE, will exit with Stat=1 if bad frames not found, or 2 if Starting Interpolation Src frame,
# Otherwise Stat already established and gotten at top of function from DBase.
} # End, if(Stat == 0)
# Stat 1, 2 and 3 use Source clip c, ie Last, not interpolated.
# Assert(1 <= Stat <= 48,RT_String("Bad Stat=%d",Stat)) # EDIT: Comment out
if(Stat >= 4) {
if(Stat <=6) { Stat==4?M11@@@:Stat==5?M21@@@:M22@@@ }
Else if(Stat <=9) { Stat==7?M31@@@:Stat==8?M32@@@:M33@@@ }
Else if(Stat <=13) { Stat==10?M41@@@:Stat==11?M42@@@:Stat==12?M43@@@:M44@@@ }
Else if(Stat <=18) { Stat==14?M51@@@:Stat==15?M52@@@:Stat==16?M53@@@:Stat==17?M54@@@:M55@@@ }
Else if(Stat <=24) { Stat==19?M61@@@:Stat==20?M62@@@:Stat==21?M63@@@:Stat==22?M64@@@:Stat==23?M65@@@:M66@@@ }
Else if(Stat <=31) { Stat==25?M71@@@:Stat==26?M72@@@:Stat==27?M73@@@:Stat==28
\? M74@@@:Stat==29?M75@@@:Stat==30?M76@@@:M77@@@ }
Else if(Stat <=39) { Stat==32?M81@@@:Stat==33?M82@@@:Stat==34?M83@@@:Stat==35?M84@@@
\ :Stat==36?M85@@@:Stat==37?M86@@@:Stat==38?M87@@@:M88@@@ }
Else { Stat==40?M91@@@:Stat==41?M92@@@:Stat==42?M93@@@:Stat==43?M94@@@:Stat==44?M95@@@
\ :Stat==45?M96@@@:Stat==46?M97@@@:Stat==47?M98@@@:M99@@@ }
}
if(Show) {
if(Stat >= 4) { # Otherwise pointless, frame was not fixed, so cannot show half bad frame.
if(Chop@@@==1) { W=Last.Width/8*4 StackHorizontal(Last.Crop(0,0,W,0),c.Crop(Last.Width-W,0,0,0)) }
Else if (Chop@@@==2) { H=Last.Height/8*4 StackVertical(Last.Crop(0,0,0,H),c.Crop(0,Last.Height-H,0,0)) }
}
EOS = RT_DBaseGetField(DB,i,1)
EOS_C = EOS != 0 ? 45 : 76
Dc_iP = RT_LumaDifference(dc,dc,n=i-1,n2=i) # dc(i-1,i)
Dc_iN = RT_LumaDifference(dc,dc,n=i,n2=i+1) # dc(i,i+1)
# Dc_iNN = RT_LumaDifference(dc,dc,n=i+1,n2=i+2) # dc(i+1,i+2) # EDIT: Unused
Dc_iNRat = Dc_iN / Max(Dc_iP,0.001)
Dc_iPRat = Dc_iP / Max(Dc_iN,0.001)
if(Stat == 1) { RT_Subtitle(Fmt1@@@,i,EOS_C,EOS,Dc_iP,Dc_iN,Dc_iPRat,Dc_iNRat) }
Else { # Here, Interp Src frame, or Interpolated bad frame
BadLen = RT_DBaseGetField(DB,i,2)
if(Stat==2) { # i is Left InterpSrc
RT_Subtitle(Fmt2@@@,i,EOS_C,EOS,Dc_iP,Dc_iN,Dc_iPRat,Dc_iNRat)
} Else if(Stat==3) { # i is Right InterpSrc
RT_Subtitle(Fmt3@@@,i,EOS_C,EOS,Dc_iP,Dc_iN,Dc_iPRat,Dc_iNRat)
} Else { # i is an interpolated frame
BadIx = RT_DBaseGetField(DB,i,3)
RT_Subtitle(Fmt4@@@,i,EOS_C,EOS,BadIx,BadLen,Dc_iP,Dc_iN,Dc_iPRat,Dc_iNRat)
}
}
}
Return Last
}
Global Fmt1@@@="%d] \a%cE%d\aLLR*\a-\n n-1 <-> n <-> n+1\nDIF: %8.3f | %8.3f\nRAT: %8.3f | %8.3f"
Global Fmt2@@@="%d] \a%cE%d\a-L\aLR*\a-\n n-1 <-> n <-> n+1\nDIF: %8.3f | %8.3f\nRAT: %8.3f | %8.3f"
Global Fmt3@@@="%d] \a%cE%d\aL\a-R\aL*\a-\n n-1 <-> n <-> n+1\nDIF: %8.3f | %8.3f\nRAT: %8.3f | %8.3f"
Global Fmt4@@@="%d] \a%cE%d\aLLR\a-*\a*\a- [%d/%d]\n n-1 <-> n <-> n+1\nDIF: %8.3f | %8.3f\nRAT: %8.3f | %8.3f"
if(SurgeonFile!="") {
SurgeonFile=RT_GetFullPathName(SurgeonFile)
RT_FileDelete(SurgeonFile)
RT_WriteFile(SurgeonFile,"###\n# FrameSurgeon() Command file [ Generated by DirtBox_MI() ]\n###\n\n")
}
if(OverrideFile!="") {
OverrideFile=RT_GetFullPathName(OverrideFile)
RT_FileDelete(OverrideFile)
RT_WriteFile(OverrideFile,"###\n# OverrideFile file [ Generated by DirtBox_MI() ]\n###\n\n")
}

StainlessS
2nd September 2018, 18:32
Post #3 of 4 (append to parts 1 & 2)


Global SurgeonFile@@@=SurgeonFile Global OverrideFile@@@=OverrideFile
Global Chop@@@=Chop Global OthSCD1@@@=OthSCD1 Global OthSCD2@@@=OthSCD2
DB=RT_GetFullPathName("~@@@_"+RT_LocalTimeString+".DB") RT_DBaseAlloc(DB,FrameCount,"iiii")
/* DBase Fields:-
0) Stat,
0 =UnKnown
1 =Src
2 =Left Interp Src
3 =Right Interp Src
4 =M11 # :- sI=Mni, sI is DB Clip Stat Index for ith frame of n length bad seq, and Mni@@@ Interp clip for same.
5 =M21 6=M22
7 =M31 8=M32 9=M33
10=M41 11=M42 12=M43 13=M44
14=M51 15=M52 16=M53 17=M54 18=M55
19=M61 20=M62 21=M63 22=M64 23=M65 24=M66
25=M71 26=M72 27=M73 28=M74 29=M75 30=M76 31=M77
32=M81 33=M82 34=M83 35=M84 36=M85 37=M86 38=M87 39=M88
40=M91 41=M92 42=M93 43=M94 44=M95 45=M96 46=M97 47=M98 48=M99
1) EOS
0 Normal
1->9 EOS, (End Of Scene), Detected bad sequence was overriden as potential interpolation source frames
seem to be from different scenes. (shown only on overridden Leftmost interp source frame).
Number (greater than 0) denotes length of Overridden bad seq detection.
2) InterpLen 0 Src, or 1 to 9
3) InterpIx, 1 to InterpLen if if Not Src.
*/
RT_DBaseSet(DB,0,1,0,0,0) RT_DBaseSet(DB,FrameCount-1, 1,0,0,0) # 1st and last frames are normal frames
# Manufacture 44 Mnn@@@ Interpolation clips # EDIT: Added
Prefilt = c.RemoveGrain(22)
Super = c.MSuper(hpad=SPad,vpad=SPad,levels=1,sharp=SSharp,rfilter=SRFilter) # One level is enough for MRecalculate
Superfilt= Prefilt.MSuper(hpad=SPad,vpad=SPad,sharp=SSharp,rfilter=SRFilter) # All levels for MAnalyse
For(i=1,MaxLen) {
Eval(RT_String("bv=Superfilt.MAnalyse(isb=true ,blksize=ABlkSize,overlap=AOverLap,search=ASearch,delta=%d,dct=ADct)",i+1))
Eval(RT_String("fv=Superfilt.MAnalyse(isb=false,blksize=ABlkSize,overlap=AOverLap,search=ASearch,delta=%d,dct=ADct)",i+1))
if(RBlkSize>0) {
Eval(RT_String("bv=Super.MRecalculate(bv,blksize=RBlkSize,overlap=ROverLap,thSAD=RthSAD)"))
Eval(RT_String("fv=Super.MRecalculate(fv,blksize=RBlkSize,overlap=ROverLap,thSAD=RthSAD)"))
}
for(j=1,i) {
Eval(RT_String("Global M%d%d@@@=c.MFlowInter(Super,bv,fv,time=100.0*%d/%d," +
\ "ml=Iml,blend=IBlend,thSCD1=IthSCD1,thSCD2=IthSCD2).SelectEvery(1,%d)", i,j, j,i+1,-j))
}
}
ARGS = "dc,DB,Th,ThMin,MinLen,MaxLen,Show"
ScriptLine="Fn@@@(last, "+ARGS+")"
c.GScriptClip(ScriptLine, local=true, args=ARGS)
Return Last
"""
GIFunc="DirtBox_MI" # Function Name, Supply unique name for your multi-instance function.
GIName=GIFunc+"_InstanceNumber" # Name of the Instance number Global
RT_IncrGlobal(GIName) # Increment Instance Global (init to 1 if not already exists)
GID = GIFunc + "_" + String(Eval(GIName))
InstS = RT_StrReplace(Func_S,"@@@","_"+GID)
# RT_WriteFile("DEBUG_"+GID+".TXT","%s",InstS)
HasGScript ? GScript(InstS) : Eval(InstS) # Use GSCript if installed (loaded plugs override builtin)
# if CallCmd available, Auto delete DBase file on clip closure.
HasCallCmd?CallCmd(close=RT_String("""CMD /C chcp 1252 && del "%s" """,DB), hide=true, Synchronous=7):NOP
Return Last
}

StainlessS
2nd September 2018, 18:33
Post #4 of 4

Client script

Import("DirtBox_MI_v0.03.avs")
FN="GreyMouse.avi"
ORG=AVISource("D:\"+FN).ConvertToYV12

############ Config ################
TH = 1.3 # 1.3, If either DC LRat or RRat greater or equal this then frame is bad.
THMIN = 0.5 # 0.5,
MINLEN = 1 # 1, Min bad seq detect length in frames (min 1)
MAXLEN = 2 # 2, Max detect frame length, (max 9)
IthSCD1 = 400 # 400, Used by MFlowInter
IthSCD2 = 130 # 130, Ditto, See MvTools2 MFlowInter.
OthSCD1 = 400 # 400, Detect and Override Scene change between detected Interpolation Src Frames.
OthSCD2 = 130 # 130, Ditto, See MvTools2 MSCDetection.
SHOW = True # True, Show Metrics
CHOP = 0 # 0, 0)=fix whole frame, 1) Fix only Left half of frame (Only when Show=true)
STACK = True # True, Stack view.
USE_DC = True # True, Use Zebra Bars as DC detect clip
###
DC_PIXELS = 16 # 16, Zebra Bar width, multiple of 4 (Used only if STACK and USE_DC, else 4)
DC_SKIPBARS = 1 # 1, Skip YPlaneMin Bars (2 also skips YPLaneMax bar)
DC_THRESHOLD = 0 # 0, As Zebra Default
DC_LO = 128 # 128, As Zebra Default
DC_HI = 255 # 255, As Zebra Default
#########################################
ORG
# Dont use YPlaneMin Bar (on Greymouse clip is always near black, watering down metrics).
# Zebra:- https://forum.doom9.org/showthread.php?t=167663
DC=ORG.Zebra(pix=STACK&&USE_DC?DC_PIXELS:4,Threshold=DC_THRESHOLD,lo=DC_Lo,hi=DC_HI).Crop(DC_SKIPBARS*DC_PIXELS,0,0,0)
SEP=DC.BlankClip(Width=4,Color=$FF00FF)
Clean = Last.DirtBox_MI(dc=USE_DC?DC:ORG,Th=TH,ThMin=THMIN,MinLen=MINLEN,MaxLen=MAXLEN,
\ IthSCD1=IthSCD1,IthSCD2=IthSCD2,OthSCD1=OthSCD1,OthSCD2=OthSCD2,Show=SHOW,Chop=CHOP)
Return (STACK && USE_DC)
\ ? StackHorizontal(Clean,SEP,DC,SEP,ORG.RT_Subtitle("BadSrc"))
\ : STACK ? StackHorizontal(Clean,SEP,ORG.RT_Subtitle("BadSrc"))
\ : Clean

StainlessS
2nd September 2018, 18:44
Just did a series of tests on flash photography clips (fair quality, in photo studio, almost constant flashes but static and near static cam).
Nailed them all (about 1 hours worth of flashes), with exeception of one which had flash as start frame of a separate scene.
Even that one could be overridden by setting OthSCD2 to 255, where would have been a blended scene transition result.

Th arg should be significantly higher for flash detection, and DC clip not necessary.

Improved speed a bit since 20FPS of earlier script, now about 27.5 FPS on GreyMouse's test clip. (2.4GHz Core Duo).

johnmeyer
2nd September 2018, 21:21
Did the photographer's flash have "pre-flashes?" Here is why I ask.

I have long had a simple script for removing photographer's flashes. It is only a few lines long, and works like this:

1. Look for orphan frame that has high difference metrics with both the preceding and succeeding frames.
2. Replace that frame with a motion-estimated version.

I had been using this for years, but then came across some video where the photographer used a flash which had 2-3 lower-power flashes which preceded the actual flash. The reason for this feature is to cause everyone's pupils to contract, thus minimizing red-eye. The problem, however, is that these flashes happen so fast that sometimes you can't find an adjacent frame to use for the ME.

It sounds like you attempted to solve this sort of problem. I remember that test clip you reference in #1 above, and it did indeed have more than one bad frame in a row, just like my video with the pre-flashes. Are you saying that this script can handle that situation? If so, I'll definitely be trying it out.

StainlessS
2nd September 2018, 21:31
Dont know John, why not girve it a whirl and tell us all (possible if 5 or less frames flashing all told and is in same scene).
The clips I tested had no more than 2 flash frames and non of the red-eye prep thing.

1. Look for orphan frame that has high difference metrics with both the preceding and succeeding frames.
2. Replace that frame with a motion-estimated version.

Pretty much the same but over up to 5 bad frames, and also the ThMin thing prevents detection where dupe/neardupe followed by
pretty much anything would fire a [EDIT: LeftRatio] detection [EDIT: or RightRatio where reverse situation at RHS of bad seq].

StainlessS
2nd September 2018, 21:54
Further to above John,

Where status not already known, and LeftRatio thing together with ThMin requirement satified,
Scans ahead to find extent of unknown frames (up to 5 possible bad),
when length to test established,
Scans looking for RightRatio (and ThMin thing), and where any right bad edge found then,
compares LeftPotentialInterPolateSrcFrame with 1st MotionComprensated frame [for that length at that position) and if
better match than to the suspect bad Left bad frame, does same test for the other end of bad seq.
If both MC interpolated frames match better with BOTH PotentialInterPolateSrcFrames than the outer bad frames,
then also checks if both have 'better numbers' then the best interpolate length so far, and if so then
sets that length as the BestSoFar and rechecks with next longer length, etc.
Once Bad range found, then so long as OThSCD1 and OThSCD2, not above max setting, then also detects if there is
scene change detected between both potential Inpolation source frames, if there is. it abandons interpolation
for that bad sequence (Flagged by E flag). If either OThSCD1 or OThSCD2 above max (eg OThSCD2>255) then
does interpolation without even checking on scene change status.

Best quick explaination I can do,

EDIT: OK, think I just explained myself into a slightly quicker (and fairly obvious) method, not sure, have to check.

johnmeyer
2nd September 2018, 23:06
Thanks for that explanation!

StainlessS
3rd September 2018, 02:40
Nope, no faster, but changed a bit.
First few posts updated.

Put a number of extra comments in source, hope you can figure it out (see post 2).
Zip updated on MediaFire and Sendspace.

EDIT: Skipped the unvisted frames testing scan ahead, is combined into testing loop.

EDIT: Below (with the MI @@@ thing stripped), makes a clip with every pixel 255 if EOS, else 0.
Used for the scene change detection thing.

Function EndOfSceneClip(clip c,Int "thSCD1",Int "thSCD2") { # All Luma Samples set 255 at EOS
thSCD1=Default(thSCD1,400) thSCD2=Default(thSCD2,130)
sup=c.MSuper(pel=1,sharp=0,rfilter=2,hpad=16, vpad=16)
bv=sup.MAnalyse(isb=true, delta=1,blksize=16) # isb=false for SOS, StartOfSceneClip, & change bv's to fv
Return c.MSCDetection(bv,thSCD1=thSCD1,thSCD2=thSCD2)
}


We Create a temp clip for detection, including left preceding and right postceding frames (outside of Interp src frames)
and detect if tmp clip frame 1 (left Interp source) is EOS, by Sampling a single pixel from frame 1.

Tmp_c = c.Trim(i-1,-2) + c.Trim(i+BestLen+1,-2) # 4 frames total
Proc = EndOfSceneClip@@@(Tmp_c,OthSCD1,OthSCD2).RT_AverageLuma(n=1,w=1,h=1).Int == 0

FranceBB
3rd September 2018, 06:12
Thank you very much indeed!
I just tested with an old content from that had frequent "jumps" at every scene change (the lower part gets darker and then comes back fine with the new frame due to the way it was edited linearly with VTRs back then).
After playing a bit with parameters, it behaved really fine.
Very well done, Stainless, a filter like this was needed.
You do make some nice useful filters whenever you have a mouse in your hand instead of a beer xD (I'm just joking about your signature).

StainlessS
3rd September 2018, 14:58
Glad you like, FranecBB.
EDIT: What kind of frame lengths did you need replacing, just curious, am currently playing with 7 frame replacement version, End EDIT:

In the pub'ed source, I did not play with the Zebra tuning settings at all, just defaults.
But maybe Threshold ~ 100.0/3 might do something useful (will affect YPlaneMin, YPlaneMax,
YPlaneMinMax), perhaps we need individual settings for all 3 and 2 for YPlaneMinMax (one
upper, one lower). [YPlaneMedian is essentially YPlaneMin(Threshold=50.0)].

Further to SceneChange thing posted above, see this

Function EndOfSceneClip(clip c,Int "thSCD1",Int "thSCD2") { # All Luma Samples set 255 at EOS, else 0
thSCD1=Default(thSCD1,400) thSCD2=Default(thSCD2,130)
sup=c.MSuper(pel=1,sharp=0,rfilter=2,hpad=16, vpad=16)
bv=sup.MAnalyse(isb=true, delta=1,blksize=16)
Return c.MSCDetection(bv,thSCD1=thSCD1,thSCD2=thSCD2)
}

Function StartOfSceneClip(clip c,Int "thSCD1",Int "thSCD2") { # All Luma Samples set 255 at SOS, else 0
thSCD1=Default(thSCD1,400) thSCD2=Default(thSCD2,130)
sup=c.MSuper(pel=1,sharp=0,rfilter=2,hpad=16, vpad=16)
fv=sup.MAnalyse(isb=false,delta=1,blksize=16)
Return c.MSCDetection(fv,thSCD1=thSCD1,thSCD2=thSCD2)
}

Function SceneCutClip(clip c,Int "thSCD1",Int "thSCD2") { # All Luma pixel = 0 =Norm, 1=EOS, 2=SOS, 3=EOS & SOS
thSCD1=Default(thSCD1,400) thSCD2=Default(thSCD2,130)
Sup = c.MSuper(pel=1,sharp=0,rfilter=2,hpad=16, vpad=16)
BvEos= Sup.MAnalyse(isb=True, delta=1,blksize=16)
FvSos= Sup.MAnalyse(isb=False, delta=1,blksize=16)
Eos = c.MSCDetection(BvEos,thSCD1=thSCD1,thSCD2=thSCD2)
Sos = c.MSCDetection(FvSos,thSCD1=thSCD1,thSCD2=thSCD2)
Return MT_Lutxy(Eos,Sos,yexpr="y 0 == x 0 == 0 1 ? x 0 == 2 3 ? ?",u=-128,v=-128)
}

# Stack Overhead Subtitle Text, with optional FrameNumber shown.
Function TSub(clip c,string Tit,Bool "ShowFrameNo",Int "Col"){
c.BlankClip(height=20,Color=Default(Col,0))
(Default(ShowFrameNo,False))?ScriptClip("""Subtitle(String(current_frame,"%.f] """+Tit+""""))"""):Subtitle(Tit)
Return StackVertical(c).AudioDubEx(c)
}

Function ShowAveLuma(clip c,String "Tit",Bool "ShowFrameNo") {
Tit=Default(Tit,"")
S="""Y=AverageLuma Subtitle(String(Y,"AveLuma=%.3f"),Align=5,size=48)"""
Return c.ScriptClip(S).TSub(Tit,ShowFrameNo,$0000FF)
}

K=blankClip(Length=1,Width=480,height=180,Pixel_Type="YV12")
W=K.Invert

c=K+K+W+W+W+K+W+K+W+K+K+W

ORG = c.Subtitle("c=K+K+W+W+W+K+W+K+W+K+K+W",Align=5).TSub("ORG",True,$0000FF)
EOS = c.EndOfSceneClip.ShowAveLuma("EOS")
SOS = c.StartOfSceneClip.ShowAveLuma("SOS")
CUT = c.SceneCutClip.ShowAveLuma("CUT")

Return StackVertical(ORG,EOS,SOS,CUT)


https://s20.postimg.cc/ctdh7ya0d/Scene_Cut.jpg (https://postimages.org/)

EDIT: The first two functions also set chroma to 255, or 0.

.
.
.

@ Pinterf, above is showing SOS as well as EOS on last frame, looks like minor prob [mvtools2_26_2_7_27_0.dll].

.
.
.

FranceBB
4th September 2018, 00:09
What kind of frame lengths did you need replacing, just curious, am currently playing with 7 frame replacement version

In my content, the issue occurs at every scene change and lasts for as little as 3 frames.
Basically, every time there's a scene change, the image gets slightly shifted up, the lower part becomes darker and then the new scene comes.
It's like when you move from a picture to another in a very old projector.

https://i.imgur.com/BfaY4w3.png

Luckily, with DirtBox I got rid of the darker, inactive part (the filter replaces it with the one of the former frame that doesn't have the issue).
I then used ReduceFlicker to get rid of the flickering left, DeSpot and DeScratch to get rid of spots and scratches, GamMac for color correction and finally a bit of denoise with dfttest.
Overall, I'm happy with the result I got. ^_^

StainlessS
4th September 2018, 01:32
Thanx FranCeBB, I've seen that stuff before in a film where I made about 2000 single frame edits a number of years ago (2009/2010 I think),
part of the reason I joined the D9.

Dirtbox_MI_v0.03, new version, first few posts updated as are zips in MediaFire and SendSpace.

Upped to MaxLen=9 frames, and added a few args.


Function DirtBox_MI(clip c,clip "dc"=c,Float "Th"=1.3,Float "ThMin"=0.5,Int "MinLen=1",Int "MaxLen"=2,
\ Int "SPad"=16, Int "SSharp"=1, Int "SRFilter"=4, [* MSuper *]
\ Int "ABlkSize"=16, Int "AOverlap"=4, Int "ASearch"=3,Int "ADct"=0, [* MAnalyse *]
\ Int "RBlkSize"=8, Int "ROverlap"=2, Int "RthSAD"=100, [* MRecalculate *]
\ Float "Iml"=200.0, Bool "IBlend"=True, Int "IthSCD1"=400, Int "IthSCD2"=130, [* MFlowInter *]
\ Int "OthSCD1"=400,Int "OthSCD2"=130, [* Detect Override *]
\ bool "Show",Int "Chop",String "SurgeonFile",String "OverrideFile"
\ ) {


Even with MaxLen=9, and keeping same args on GreyMouse clip, no difference to previous detections, was half expecting some instability
due to additional length, but exactly same results.

Getting about 26FPS (maxlen=2) with GreyMouse 720x576 clip, maybe because its so jittery and firing scans so often.
On clip with quite a few photo flashes, 640x400 and reasonable quality, was getting 122FPS, so maybe GreyMouse
clip is in part responsible for slowish speed.

EDIT: Got it, GreyMouse clip is RGB, with convert to YV12 [+ bigger file, worse compression], maybe part reason.

StainlessS
4th September 2018, 01:45
Forget it I musta broke something before posting.
Up v0.04 in a while.

EDIT: Pretty much a false alarm, see below.

StainlessS
4th September 2018, 02:23
Panic stations over, I had just set USE_DC to false, ie did not use the DC detection clip and so did not perform properly.
(Had changed setting for testing flash photography stuff, changed back the Th setting but forgot about Zebra DC clip).
[EDIT: DC Detection clip shown below between the two frames.]

Client script updated in posts, and zips ammended and re-upped to MediaFire and SendSpace.

No change to version, still at v0.03.

Sorry chaps.

Now looks like this.
https://s20.postimg.cc/lio1vy2il/Dirt_Box_v0.03.jpg (https://postimg.cc/image/wv0ndqb7d/)

EDIT: On the frame before above, the lower RHS RAT Ratio shows 1.910 which is Greater than Th setting of 1.3
and the lower LHS RAT setting on following frame shows 2.157, which also is greater than 1.3, which are the two
primary conditions to fire a detection.

EDIT:
FranceBB,
For your prev post, can eg switch off Scene detection override via OthSCD2 arg (>255) and also switch off MFlowInter
IthSCD2 (>255, or at least higher than default 130), also change IBlend setting if required, ... if you ever have same problem but
refuses to replace the bad scene change frames.

EDIT: Just noticed something, GreyMouse clip has black borders, reason Zebra YPlaneMin bar is always black,
YPlaneMin bar might actually be useful in such case if border cropped version of clip were given to Zebra
for the DC detection clip.
EDIT: Although that would mean the demo client script would fail if all borders cropped, due to vertical size
of zebra clip not being same as src when StackHorizontal. Could resize Zebra prior to stack, OR,
only crop left and right borders leaving same height for Zebra, that would work fine I think, and is the better idea.

StainlessS
5th September 2018, 14:16
Alternative DirtBox_Client.avs, for GreyMouse clip with left/right black borders. Defaults cropping source for Zebra, DC=Src.crop(8,0,-12,0).Zebra().
Switched off option to crop the Zebra result, as no longer deemed necessary.
Same results as with cropped zebra dc clip.

No change to orignal posts/zips (although added a couple of edits to posts).

######################
# DirtBox_Client.avs
# Script for GreyMouse clip with Horizontal frame damage and Black Borders Left and Right
######################
#Import("DirtBox_MI.avs")
FN="GreyMouse.avi"
ORG=AVISource("D:\"+FN).ConvertToYV12

############ Config ################
TH = 1.3 # 1.3, If either DC LRat or RRat greater or equal this then frame is bad.
THMIN = 0.5 # 0.5,
MINLEN = 1 # 1, Min bad seq detect length in frames (min 1)
MAXLEN = 2 # 2, Max detect frame length, (max 7)
IthSCD1 = 400 # 400, Used by MFlowInter
IthSCD2 = 130 # 130, Ditto, See MvTools2 MFlowInter.
OthSCD1 = 400 # 400, Detect and Override Scene change between detected Interpolation Src Frames.
OthSCD2 = 130 # 130, Ditto, See MvTools2 MSCDetection.
SHOW = True # True, Show Metrics
CHOP = 0 # 0, 0)=fix whole frame, 1) Fix only Left half of frame (Only when Show=true)
STACK = True # True, Stack view.
USE_DC = True # True, Use Zebra Bars as DC detect clip
###
DC_PIXELS = 16 # 16, Zebra Bar width, multiple of 4 (Used only if STACK and USE_DC, else 4)
DC_SKIPBARS = 0 # 0, 1=Skip YPlaneMin Bar (2 also skips YPLaneMax bar)
DC_THRESHOLD = 0 # 0, As Zebra Default
DC_LO = 128 # 128, As Zebra Default
DC_HI = 255 # 255, As Zebra Default
DC_CROP_SRC_L= 8 # 8, if Clip has Left Black border, needs crop left for DC.
DC_CROP_SRC_R= 12 # 12, if Clip has Right Black border, needs crop Right for DC.
#########################################
ORG
# Dont use YPlaneMin Bar (on Greymouse clip is always near black, watering down metrics).
# Zebra:- https://forum.doom9.org/showthread.php?t=167663
DC=ORG.Crop(DC_CROP_SRC_L,0,-DC_CROP_SRC_R,0).Zebra(pix=STACK&&USE_DC?DC_PIXELS:4,Threshold=DC_THRESHOLD,
\ lo=DC_Lo,hi=DC_HI).Crop(DC_SKIPBARS*DC_PIXELS,0,0,0)
#return DC
SEP=DC.BlankClip(Width=4,Color=$FF00FF)
Clean = Last.DirtBox_MI(dc=USE_DC?DC:ORG,Th=TH,ThMin=THMIN,MinLen=MINLEN,MaxLen=MAXLEN,
\ IthSCD1=IthSCD1,IthSCD2=IthSCD2,OthSCD1=OthSCD1,OthSCD2=OthSCD2,Show=SHOW,Chop=CHOP)
Return (STACK && USE_DC)
\ ? StackHorizontal(Clean,SEP,DC,SEP,ORG.RT_Subtitle("BadSrc"))
\ : STACK ? StackHorizontal(Clean,SEP,ORG.RT_Subtitle("BadSrc"))
\ : Clean


@JohnMeyer,
Actually, scipt does differ from your method a little, we have made it so BOTH Left and Right riatios have to be above Th, rather than
either of them (ie AND instead of OR), think might be a little unstable if using OR method, especially at longer length MaxLen.

@GreyMouse,

35506,35526 # len = 21 (there might be longer breakups, gave up counting when I saw this one)

Aint no way we can scan ahead an unlimited number of frames using separate clips for individual repair frames, just too many
clips to create [Out Of Memory and Windows Resources Exhaustion Issues], ClipClop's DoctorFrames script creates about 210 clips to support up to 20 frame interpolation.
Creating on-the-fly interpolation clips would just take was too long as would have to create entire interp clips at every test length
at every position for every frame in test run, just not practical.