HeartlessS Usurer
Join Date: Dec 2009
Location: Over the rainbow
Posts: 10,980
|
SpatialAlign v1.0 : Spatial Alignment of clips
Code:
Function SpatialAlign(clip Fix,clip Src,Int "MatchLen",Int "Rad",Float "LdTh",String "Prefix") {
/*
SpatialAlign.avs v1.0
SpatialAlign v1.0: By StainlessS. http://forum.doom9.org/showthread.php?p=1722142#post1722142
Fix spatial alignment between two clips containing similar scenes (dont have to be temporally aligned).
Can take Long time for long clips where they contain no common sequences.
Spatial mis-alignment should not be too great or may not find temporal (and therefore spatial) alignment.
Requires:- GScript, RT_stats, RoboCrop'ed before calling. DebugView for viewing debug info.
Frame Accurate Progressive sources of same Size, FrameRate, Colorspace.
Fix, clip to align with Src.
Src, clip that Fix clip will be spatially aligned to.
MatchLen, Default 50 frames. Number of frames that have to match when finding temporal alignment.
Rad, Default 4 (1 -> 6). Maxiumum number of pixels (H and V) that fix clip may be shifted by.
LdTh, Default 3.0 * Rad (ie 12.0 when Rad=Default 4), LumaDifference has to be less than or equal to this to detect temporal alignment.
Prefix, Default "SA_Crop_". Default sets Global Variables with "SA_Crop_" Prefix.
Sets variables "L", "T", "R" and "B", prefixed by the Prefix string, eg 'SA_Crop_L'.
Can be used to crop off gunk remaining around clip edges after fixing a shifted clip.
Prefix, allows you to change the prefix to the names of the variables.
eg if Prefix == "My_Crop_", then set Global variables 'My_Crop_L' as Global Int.
*/
myName = "SpatialAlign: "
MatchLen = Default(MatchLen,50)
Rad = Default(Rad,4)
AutoLDTh = !Defined(LdTh)
LdTh = Float(Default(LdTh,3.0*Rad))
Prefix = Default(Prefix,"SA_Crop_")
Assert(Src.Width==Fix.Width,RT_String("%sClip Width mismatch",myName))
Assert(Src.Height==Src.Height,RT_String("%sClip Height mismatch",myName))
Assert(RT_VarIsSame(Src,Fix,sig=false),RT_String("%sClip ColorSpace mismatch",myName))
Assert(MatchLen>0,RT_String("%sInvalid MatchLen, Must be greater than 0(%d)",myName,MatchLen))
Assert(Rad>=1 && Rad<=6,RT_String("%sInvalid Rad, 1 -> 6(%d)",myName,Rad))
SrcMax = Src.FrameCount - MatchLen
FixMax = Fix.FrameCount - MatchLen
Assert(SrcMax>=0,RT_String("%sInvalid MatchLen, Src too short",myName))
Assert(FixMax>=0,RT_String("%sInvalid MatchLen, Fix too short",myName))
GScript("""
FixMatchFrame = -1 SrcMatchFrame = -1 # Match frames not yet found
# Test already temporally aligned
mnlen = Min(FixMax,SrcMAx) + 1
nTests = Min(mnlen,10)
tstMul = (nTests<=1) ? 0.0 : (mnlen-1) / (nTests-1.0)
WorstOfAllDf = -1.0
For(Test = 1, nTests) {
tStart = int((Test - 1) * tstMul + 0.5)
if(RT_YPlaneMinMaxDifference(Fix,n=tStart,x=rad,y=rad,w=-rad,h=-rad,Threshold=1.0) >= 32) { # Reliable frame ?
worstDf= -1.0
for(j = 0, Matchlen-1) {
df = RT_LumaDifference(Fix,Src,n=tStart+j,n2=tStart+j,x=rad,y=rad,w=-rad,h=-rad)
worstDf = (df>worstDf) ? df : worstDf
if(df > LdTh) {
j = Matchlen + 1 # Break, MatchLen, not found
}
}
if(j==MatchLen) {
RT_DebugF("Already temporally aligned @ frame %d (Worst LumaDif of %d frames =%.3f)",tStart,Matchlen,worstDf,name=myName)
FixMatchFrame = tStart SrcMatchFrame = tStart
Test = nTests # Break Test
}
WorstOfAllDf = (worstDf>WorstOfAllDf) ? worstDf : WorstOfAllDf
}
}
if(FixMatchFrame<0) {
RT_DebugF("Not already temporally aligned (Worst LumaDif of all tested frames=%.3f)",WorstOfAllDf,name=myName)
RT_DebugF("Searching for temporal alignment",name=myName)
Tim = RT_LocalTimeString(File=True)
QDB = RT_String("~QDB_%s.txt",Tim) NDB = RT_String("~NDB_%s.txt",Tim)
RT_QwikScanCreate(Src,QDB,prevdb="",nextdb=NDB,debug=True)
LumaTolStart = 5 LumaTolStep = 3 LumaTolEnd=17 ScanPerc = 0.5 / 2.0
for(LT = LumaTolStart,17,LumaTolStep) {
ScanPerc = ScanPerc * 2.0
LumaTol = Float(min(LT,16))
Testlen = FixMax + 1
nTests = Min(Testlen,Max(Round(ScanPerc*TestLen/100.0),50))
tstMul = (nTests<=1) ? 0.0 : (TestLen-1) / (nTests-1.0)
If(LT != LumaTolStart) {
RT_DebugF("Scan failed @ LumaTol==%d, LdTh=%.2f retrying",LT-LumaTolStep,LdTh,name=myName)
LdTh = (AutoLdTh) ? LdTh + 1.0 : LdTh
}
RT_DebugF("Scanning %d frames (%.1f%%) of Fix clip @ LumaTol==%d LdTh=%.2f",
\ nTests,nTests*100.0/Fix.Framecount,LT,LdTh,name=myName)
for(Test = 1,nTests) {
Fi = int((Test - 1) * tstMul + 0.5)
if(RT_YPlaneMinMaxDifference(Fix,n=Fi,x=rad,y=rad,w=-rad,h=-rad,Threshold=1.0) >= 32) { # Reliable frame ?
# Best match LumaDifference scan, Only exit early on EXACT match
Result=RT_QwikScan(Src,0,Fix,Fi,QDB,NDB,lumatol=LumaTol,Flags=2,ld=0.0,maxdistance=SrcMax,XP=0)
if(Result < 0 && QWKS_BM_COUNT > 0 && QWKS_BM_LD <= LdTh) {
Result = QWKS_BM_LD_FRM # Best match to Fix[Fi] in entire Src
}
if(Result>=0) {
mxdif = -1.0
for(j=1,MatchLen-1) {
df=RT_LumaDifference(Fix,Src,n=Fi+j,n2=Result+j)
mxdif = (df>mxdif) ? df : mxdif
if(df > LdTh) {
j = Matchlen + 1 # Break MatchLen, not found
}
}
if(j == MatchLen) { # completed normally ?
SrcMatchFrame = Result
FixMatchFrame = Fi
RT_DebugF("Fix[%d] Match Frame found at Src[%d] MaxLumaDif=%.3f (over %d frames)",
\ FixMatchFrame,SrcMatchFrame,mxdif,MatchLen,name=myName)
Test = nTests # Break Tests
}
}
}
}
if(FixMatchFrame != -1) {
LT = LumaTolEnd # Break LT
}
}
RT_FileDelete(QDB) RT_FileDelete(NDB)
}
Crop_L=0 Crop_T=0 Crop_R=0 Crop_B=0
if(FixMatchFrame>=0) {
X = rad*2
Y = rad*2
W = - X # As in crop(), clip width relative, same as c.width - X - (RADIUS * 2)
H = - Y # clip height relative, same as c.height - Y - (RADIUS * 2)
HDir = 0
VDir = 0
corr = -1.0
for(i= - rad, rad) {
for (j= - rad, rad) {
cor = RT_LumaCorrelation(Fix, Src,n=FixMatchFrame, x=X,y=Y,w=W,h=H,n2=SrcMatchFrame, x2=X+i, y2=Y+j)
if(cor>corr) {
corr=cor
HDir = i
VDir = j
}
}
}
Org_corr = RT_LumaCorrelation(Fix, Src,n=FixMatchFrame, x=X,y=Y,w=W,h=H,n2=SrcMatchFrame, x2=X, y2=Y)
RT_DebugF("Shifting clip HDir=%d VDir=%d (orig correlation=%f shifted=%f",HDir,VDir,Org_corr,corr,name=myName)
LCrop = (HDir<0) ? -HDir : 0 RCrop = (HDir>0) ? HDir : 0
TCrop = (VDir<0) ? -VDir : 0 BCrop = (VDir>0) ? VDir : 0
Wid = Fix.Width Hit = Fix.Height
XM = Fix.RT_ColorSpaceXMod() YM = Fix.RT_ColorSpaceYMod(Laced=False) # NOT for Interlaced
Fix = Fix.PointResize(Wid*XM,Hit*YM)
Fix = Fix.Crop(LCrop*XM,TCrop*YM,-RCrop*XM,-BCrop*YM).Addborders(RCrop*XM,BCrop*YM,LCrop*XM,TCrop*YM)
Fix = Fix.PointResize(Wid,Hit)
SA_x = (Abs(HDir) + XM - 1) / XM * XM
SA_y = (Abs(VDir) + YM - 1) / YM * YM
Crop_L = (HDir<0) ? 0 : SA_x Crop_T = (VDir<0) ? 0 : SA_y
Crop_R = (HDir<0) ? SA_x : 0 Crop_B = (VDir<0) ? SA_y : 0
} Else {
RT_DebugF("Cannot temporally align frames",name=myName)
}
SS = RT_String("Global %sL=%d Global %sT=%d Global %sR=%d Global %sB=%d",
\ Prefix,Crop_L,Prefix,Crop_T,Prefix,Crop_R,Prefix,Crop_B)
Eval(SS) RT_DebugF("%s",SS,name=myName)
""") # End Of GScript
return Fix
}
Utility for demo only
Code:
Function ShiftClip(clip c,Int "HDir",Int "VDir",Int "BordCol") {
c
HDir = Default(HDir,0) VDir = Default(VDir,0) BordCol = Default(BordCol,0)
LCrop = (HDir<0) ? -HDir : 0 RCrop = (HDir>0) ? HDir : 0
TCrop = (VDir<0) ? -VDir : 0 BCrop = (VDir>0) ? VDir : 0
XM = RT_ColorSpaceXMod() YM = RT_ColorSpaceYMod(Laced=False) # NOT for Interlaced
PointResize(c.Width*XM,c.Height*YM)
Crop(LCrop*XM,TCrop*YM,-RCrop*XM,-BCrop*YM)
Addborders(RCrop*XM,BCrop*YM,LCrop*XM,TCrop*YM,BordCol)
PointResize(c.Width,c.Height)
Return Last
}
# Return Clip Difference of input clips (amp==true = Amplified, show==true = show background)
Function ClipDelta(clip clip1,clip clip2,bool "amp",bool "show") {
amp=Default(amp,false)
show=Default(show,false)
c2=clip1.levels(128-32,1.0,128+32,128-32,128+32).greyscale()
c1=clip1.subtract(clip2)
c1=(amp)?c1.levels(127,1.0,129,0,255):c1
return (show)?c1.Merge(c2):c1
}
Function Stack4Delta(clip c1,clip c2,bool "gray") {
Gray =Default(gray,false)
c1=(gray) ? c1.GrayScale:c1
c2=(gray) ? c2.GrayScale:c2
L=StackVertical(c1,c2)
D1=ClipDelta(c1,c2)
D2=ClipDelta(c1,c2,AMP=True)
R=StackVertical(D1,D2)
StackHorizontal(L,R)
Return Last
}
Demo client script
Code:
DOFIX = True # Fix the shift ?
FIXGUNK = True # crop off gunk after shift fix ?
ColorBars.ConvertToYV12.Trim(0,-500).ShowFrameNumber
shifted = ShiftClip(2,4) # Mis-aligned test clip. Multiple of X/Y Mod so as not to mess up chroma
SRC=Last
# Fix shifted clip
Result = (DOFIX) ? SpatialAlign(Shifted,Rad=4,SRC,Prefix="Crop_") : Shifted
# Crop off rubbish from edges
Result = (DOFIX&&FIXGUNK) ? Result.Crop(Crop_L,Crop_T,-Crop_R,-Crop_B) : Result
SRC = (DOFIX&&FIXGUNK) ? SRC.Crop(Crop_L,Crop_T,-Crop_R,-Crop_B) : SRC
Return Stack4Delta(SRC,Result)
Last edited by StainlessS; 30th May 2015 at 11:04.
Reason: Update
|