View Single Post
Old 15th May 2015, 00:32   #1  |  Link
StainlessS
HeartlessS Usurer
 
StainlessS's Avatar
 
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)
__________________
I sometimes post sober.
StainlessS@MediaFire ::: AND/OR ::: StainlessS@SendSpace

"Some infinities are bigger than other infinities", but how many of them are infinitely bigger ???

Last edited by StainlessS; 30th May 2015 at 11:04. Reason: Update
StainlessS is offline   Reply With Quote