Welcome to Doom9's Forum, THE in-place to be for everyone interested in DVD conversion. Before you start posting please read the forum rules. By posting to this forum you agree to abide by the rules. |
15th May 2015, 00:32 | #1 | Link |
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 } 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 } 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 |
16th May 2015, 19:56 | #2 | Link |
HeartlessS Usurer
Join Date: Dec 2009
Location: Over the rainbow
Posts: 10,980
|
Post #1 update to v1.0, working pretty good.
Hi there Jmac, wondered when you would be visiting here.
__________________
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 ??? |
30th May 2015, 11:15 | #5 | Link |
HeartlessS Usurer
Join Date: Dec 2009
Location: Over the rainbow
Posts: 10,980
|
@chawl, oops yes, fixed. Musta done that during a post edit.
Foxy, as well as RT_LumaCorrelation, I want to use RT_FrameDifference (which currently supports only full frame, awaits RT_ update). I want to examine two horizontal strips at top and bottom of frame, and two vertical strips @ left and right of frame, and a block in middle (maybe about 16x16), then could probably better estimate zoom as well as pan, guess that Stab/Deshaker already does something like that. I have two 80's movies, one is about 20 mins longer than the other, frame contents can be both zoomed in and out compared with the other clip, and scenes are in totally different order, one is in German, the other French, with different movie names but more or less the same storyline. I shall be trying to do something that can automate alignment and cutting of these two difficult clips. (EDIT: This could be a little over ambitious)
__________________
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 12:01. |
31st May 2015, 06:35 | #6 | Link |
Registered User
Join Date: Feb 2012
Posts: 3
|
How about merging spatial alignment logic into your brilliant MatchFrames script? I guess It might boost its efficiency quite dramatically especially if sources of the two clips are very different like VHS capture versus DVD. So it would be easier to reconstruct your old but lovely VHS cut (English?) of a title by replacing its crumbling frames with the ones from a remastered DVD (German?) which is cut, cropped and/or panned differently. This might be the holy grail of poor man's remastering
I'm sorry if I've said something silly but I'm just a hobbyist |
31st May 2015, 15:25 | #7 | Link | ||
HeartlessS Usurer
Join Date: Dec 2009
Location: Over the rainbow
Posts: 10,980
|
Quote:
Quote:
__________________
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 ??? |
||
6th June 2015, 08:34 | #8 | Link |
Registered User
Join Date: Jan 2006
Posts: 1,867
|
Hey there S
This is awesome. There are uses for this in various versions of movies, for example full frame laserdisc version and 16x9 cropped dvd version. Between cuts, spatial alignment, the last piece of the puzzle to me in terms of matching sources is colour. There's the histogram, but I was thinking of a solver that finds the best match in terms of YUV referenced hue and saturation, as this more closely models physically the differences in capture settings and colour adjustments that were possible in early video editing. I used the FFT based plugin to match widescreen to scan and pan version, it didn't follow so well, but it was neat to see the director moving a box around basically to focus on certain faces. You can also combine these to make better resolution; for example my copy wasn't anamorphic and the pan/scan version had dramatically better resolution in it's region, for a 2.35:1 film. I'd like to see a script to combine two resolutions like that and spit out an ideal sized combined version. |
6th June 2015, 11:15 | #9 | Link |
HeartlessS Usurer
Join Date: Dec 2009
Location: Over the rainbow
Posts: 10,980
|
Hi there JMac,
I'm well snowed under with stuff at the mo, trying not to get into any other projects and I would actually like to get away from AVS and into Android apps, but its hard to let go. This and the sequence replacement (in another thread a bit earlier), and perhaps deshaking, color matching, could all be good additions to the DBSC thing (which already is a bit of a monster). My guess is that my maths are just not up to the color matching thing, I remember mg262 saying something like "I am not completely happy with it" about his ColorLike plug, and if he could not get it working well, not sure I could even come close to anything useful. I've recently started using Martin53 AWB() [auto white balance], its really pretty good and a lot faster than I thought it would be, I spotted a couple of possible problem areas that could be tweaked, hope he comes back soon and continues work on it. Got to dash, be good.
__________________
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 ??? |
6th June 2015, 18:55 | #10 | Link |
Registered User
Join Date: Jan 2007
Posts: 729
|
Would it be possible to somehow adapt this for luma<->chroma matching?
One of the big problems that I never found solution for is sources in which chroma doesn't match luma temporally. The problem happens with telecined video where the pattern for chroma fields is broken, so that after IVTC, you will have chroma that has one of its fields from non-matching frame (combed ghost, basically). Those sources could probably be handled if we had a function or script that would be able to match chroma to luma (basically something like seperately IVTCing chroma and then picking frame that matches luma IVTCed clip). Sadly, I never found a way to do such luma/chroma matching |
7th June 2015, 00:59 | #11 | Link | |
ангел смерти
Join Date: Nov 2004
Location: Lost
Posts: 9,556
|
Quote:
|
|
8th June 2015, 08:01 | #12 | Link |
HeartlessS Usurer
Join Date: Dec 2009
Location: Over the rainbow
Posts: 10,980
|
Best I can suggest is to find scene changes separately for both Luma & Chroma, eg using RT_FrameDifference(ChromaWeight=0.0) [Luma Only] and RT_FrameDifference(ChromaWeight=1.0) [Chroma Only]. I did at one point try detecting scene changes with chroma only, just to see if it worked ok, and it did.
EDIT: Added 'RT_' to FrameDifference. EDIT: OR probably better, find Luma Scene change, and then check against chroma SC a couple of frames either side of detected scene change.
__________________
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; 8th June 2015 at 09:18. |
8th August 2015, 15:36 | #13 | Link |
Registered User
Join Date: Nov 2009
Posts: 2,352
|
Can this output matching metrics? It can work good for detecting tilts and pans, along zooms they trigger some false-positives in my FilmGateFix() function (as in deshakers as well). I guess you can make a better job at it than I did, I always struggle with ScriptClip (updating the function today so you can have a glance of the hacky code).
Last edited by Dogway; 8th August 2015 at 16:12. |
8th August 2015, 17:35 | #14 | Link |
HeartlessS Usurer
Join Date: Dec 2009
Location: Over the rainbow
Posts: 10,980
|
I'll see what I can do when I get time (not soon sorry).
Here, did search on FilmgateFix (save me searching again):- http://forum.videohelp.com/threads/3...409&viewfull=1 and also, Jmac posted somewhere about Shiftclip(), did not find it and so wrote function in 1st post, [was actually called Shift()], found it here:- http://forum.doom9.org/showthread.ph...80#post1518480
__________________
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 ??? |
8th August 2015, 18:01 | #15 | Link |
HeartlessS Usurer
Join Date: Dec 2009
Location: Over the rainbow
Posts: 10,980
|
Further to previous post:
Doggy, perhaps could use Letterbox() instead of PadInwards(). Also for others, Stabilization Tools Pack v1.8 in full here (from above link): Code:
### # ### # ### Stabilization Tools Pack 1.8 by Dogway (08-08-2015) # ### # ########################################################## ### ### FilmGateFix() ### ### Function to fix frames ( by means of FreezeFrame() ) with film gate issues after a scene change. ### This is aimed at animation, for live footage frame interpolation should be desired. ### ### Rationale: ### Film Gate is about non-linear vertical stretching, thus we compare ### current and next frames in two blocks, a 1/5 block of top and bottom. ### The difference in both should be very high for film gate artifacts ### between the 2nd and 3rd frame after a Scene Change (also 1st and 2nd are compared). ### We mask this through a Scene Detection filter, motion masks, etc. ### ### (http://forum.videohelp.com/threads/371336) ### ### ### Dependencies: ### ### Required: ### ------------ ### ### MVTools (v2.5.11.3 or higher) (http://avisynth.org.ru/mvtools/mvtools2.html) ### masktools (v2a48 or higher) (http://forum.doom9.org/showthread.php?t=98985) ### Average (v0.92 or higher) (http://forum.doom9.org/showthread.php?t=169832) ### GRunT (v1.0.1 or higher) (http://forum.doom9.org/showthread.php?t=139337) ### #################################### function FilmGateFix(clip c, float "thr", int "window", int "thSCD1", int "thSCD2", bool "debug") { thr = Default(thr,1.0) # Main tweak setting, use debug and tweak according 2nd frame after Scene Change window = Default(window,5) # Enlarges/reduces detection area, "height()/window" thSCD1 = Default(thSCD1,500) # Increase to reduce number of scenes detected thSCD2 = Default(thSCD2,145) # Increase to reduce number of scenes detected Debug = Default(Debug,false) # Check what frames will be fixed and the difference value of frames for tweaking "thr" thr2 = 5.0 # central part of frame must change less than this to consider static scene to fix. c w=width() h=height() # Block for Scene Change detection and Motion Mask kind = 0 gam = 1.2 # Could probably be exposed mvthr = 30 # Threshold, heavily dependent on gamma value above super = MSuper (pel=1, sharp=0,vpad=0,hpad=0) b1v = MAnalyse(super,isb=true, blksize=16,overlap=8,search=0) f1v = MAnalyse(super,isb=false, blksize=16,overlap=8,search=0) SADbv1 = MMask (b1v,kind=kind,gamma=gam,thSCD1=thSCD1,thSCD2=thSCD2) SADfv1 = MMask (f1v,kind=kind,gamma=gam,thSCD1=thSCD1,thSCD2=thSCD2) SceneChange = MSCDetection (f1v,thSCD1=thSCD1,thSCD2=thSCD2) mvmask = Average(SADbv1,1./2,SADfv1,1./2) # Film Gate happens commonly between 2nd and 3rd frame after SC # 1st frame is commonly garbage add = round(w/(window*1.0)) T = crop(0,0,0,((-h+add)/8)*8) B = crop(0,((h-add)/8)*8,0,0) L = crop(0,0,((-w+add)/8)*8,0) R = crop(((w-add)/8)*8,0,0,0) CTB = crop(0,((add)/8)*8,0,-((add)/8)*8) CLR = crop(round(((add*1.5)/8)*8),0,round(-((add*1.5)/8)*8),0) SC2 = mt_logic(SceneChange,selectevery(SceneChange,1,-1),"max",U=3,V=3) # Here, if after SC top area changes more than "thr" compared to bottom area # then apply one FreezeFrame() for first SC frame, and another FreezeFrame() for the 2nd one, # they (1st and 2nd) compute individually. There is a motionmask on top to rule out false positives. scriptclip("""(abs(YDifferenceToNext(T)-YDifferenceToNext(B))>"""+string(thr)+""" && abs(YDifferenceToNext(CTB))<"""+ \ string(thr2)+""") || (abs(YDifferenceToNext(L)-YDifferenceToNext(R))>"""+string(thr)+ \ """ && abs(YDifferenceToNext(CLR))<"""+string(thr2)+""") ?"""+\ \ """(YplaneMax(SC2)>254?(YDifferenceFromPrevious(SC2)>250?(AverageLuma(mvmask)<"""+string(mvthr)+ \ """?(YDifferenceToNext(1)<9.0?freezeframe(current_frame,current_frame,current_frame+2):c):c):"""+\ \ """(AverageLuma(mvmask)<"""+string(mvthr)+"""?freezeframe(current_frame,current_frame,current_frame+1):c)):c):c""", \ args="T,B,L,R,c,SC2,CTB,CLR,mvmask") # Debug block debug ? +\ eval("""stackhorizontal(scriptclip("Subtitle(c,"+Chr(34)+" Top & Bottom:"+Chr(34)+"+String(YDifferenceToNext(T)-YDifferenceToNext(B))+\ "+Chr(34)+"\n Left & Right: "+Chr(34)+"+String(YDifferenceToNext(L)-YDifferenceToNext(R))+\ "+Chr(34)+"\n Center Horiz: "+Chr(34)+"+String(YDifferenceToNext(CTB))+\ "+Chr(34)+"\n Center Vert: "+Chr(34)+"+String(YDifferenceToNext(CLR)), z align=7,size=30,lsp=10)",args="T,B,L,R,c,CTB,CLR"),+\ scriptclip("AverageLuma(mvmask)<"""+string(mvthr)+"""?((abs(YDifferenceToNext(T)-YDifferenceToNext(B))>"+string(thr)+ \ " && abs(YDifferenceToNext(CTB))<"+string(thr2)+") || (abs(YDifferenceToNext(L)-YDifferenceToNext(R))>"+ \ string(thr)+" && abs(YDifferenceToNext(CLR))<"+string(thr2)+") ? "+\ \ "(YplaneMax(SC2)>254&&YDifferenceToNext(1)<9.0?mt_lut(c,"+Chr(34)+"255"+Chr(34)+",U=128,V=128):mt_lut(c,"+Chr(34)+"0"+Chr(34)+ \ ",U=128,V=128)):mt_lut(c,"+Chr(34)+"0"+Chr(34)+",U=128,V=128)):mt_lut(c,"+Chr(34)+"0"+ \ Chr(34)+",U=128,V=128)",args="T,B,L,R,c,CTB,CLR,SC2,mvmask")) \ crop(0,0,-(round(w/1.1)/2)*2,0).Subtitle("Frame to Freeze",align=9,size=40)""") : last } #################################### ### ### FillBorders() ### ### Function to fill dark (use threshold) borders, in the vein of old FillMargins() function. ### But instead of mirroring or other approaches like resizing, this function fills/interpolates ### missing data from surrounding pixels. ### Useful to use as a clean pass after stab() and crop(x1,y1,x2,y2) ### for the remaining thin black borders (up to 3px) ### ### For borders of 4px and more you can enable FixFalPos, there you supply a clip ### without black borders (ie. before stab() ). It automatically replaces the offended frames. ### Some thick black borders aren't "0" black at all, they show garbage and the 3+1 pixel border ### may not average to 0, so a "thr2" setting is added as threshold, default 7 should be enough. ### ### Below you can still use the the FindBlackBorders() function for manual handling of thick black borders. ### Use ClipClop() for the stab() results according to the statistics file of FindBlackBorders() ### ### Mind you, for FillBorders() you need to manually load the AVSInpaint plugin: ### LoadCPlugin("AVSInpaint.dll") ### ### (http://forum.videohelp.com/threads/371336) ### ### ### Dependencies: ### ### masktools (v2a48 or higher) (http://forum.doom9.org/showthread.php?t=98985) ### GRunT (v1.0.1 or higher) (http://forum.doom9.org/showthread.php?t=139337) ### AVSInpaint (v2008.02.23 or higher) (http://forum.doom9.org/showthread.php?t=133682) ### #################################### function FillBorders(clip c, int "thr", int "pad", bool "blur", bool "debug", clip "FixFalPos", int "thr2") { thr = Default(thr, 1) # Threshold, pixel values below this will be considered borders pad = Default(pad, 0) # Pixels, you can expand the replacement area adding more pixels # (to deal with dirty borders) (use "1" to deal with 1px b/w chroma, due to the chroma subsampling nature of video) blur = Default(blur ,false) # Blurs the masking for the replacement area. Currently not supported for InpaintLogo() debug = Default(debug,false) # Show the borders that are going to be filled FalPos = Defined(FixFalPos) ? true : false # If you supply a reference clip borders with 4 or more # average thr2 pixels will be replaced with the clip's frame thr2 = Default(thr2, 7) # Threshold for FalPos, FalPos frames sometimes have garbage borders so you need to increase # threshold c converttoy8() y8=last w=width() h=height() # 4px or greater L4=FalPos ? crop(0,0,-w+4,0) : nop() R4=FalPos ? crop(w-4,0,0 ,0) : nop() T4=FalPos ? crop(0,0,0,-h+4) : nop() B4=FalPos ? crop(0,h-4,0 ,0) : nop() # 3px L3=crop(0,0,-w+3,0) R3=crop(w-3,0,0 ,0) T3=crop(0,0,0,-h+3) B3=crop(0,h-3,0 ,0) # 2px L2=crop(L3,0,0,-1, 0) R2=crop(R3,1,0,0 , 0) T2=crop(T3,0,0,0 ,-1) B2=crop(B3,0,1,0 , 0) # 1px L1=crop(L3,0,0,-2,0 ) R1=crop(R3,2,0,0 ,0 ) T1=crop(T3,0,0,0 ,-2) B1=crop(B3,0,2,0 ,0 ) c Fill = ScriptClip(""" y8 pad= blur ? pad+2 : pad x1 = AverageLuma(L1) < thr ? (AverageLuma(L2) < thr ? (AverageLuma(L3) < thr ? 3+pad : 2+pad) : 1+pad) : 0 x2 = AverageLuma(R1) < thr ? (AverageLuma(R2) < thr ? (AverageLuma(R3) < thr ? 3+pad : 2+pad) : 1+pad) : 0 y1 = AverageLuma(T1) < thr ? (AverageLuma(T2) < thr ? (AverageLuma(T3) < thr ? 3+pad : 2+pad) : 1+pad) : 0 y2 = AverageLuma(B1) < thr ? (AverageLuma(B2) < thr ? (AverageLuma(B3) < thr ? 3+pad : 2+pad) : 1+pad) : 0 FalPos = FalPos ? ((x1-pad > 2 || x2-pad > 2 || y1-pad > 2 || y2-pad > 2 ) \ ? ((AverageLuma(L4) < thr2) || (AverageLuma(R4) < thr2) || (AverageLuma(T4) < thr2) || (AverageLuma(B4) < thr2) \ ? true : false) : false) : false ter = !FalPos && (x1+y1+x2+y2>0) ? true : false msk = PadInwards(mt_lut("0"),x1,y1,x2,y2,$ffffff).trim(0,-1).FreezeFrame(0, FrameCount(last)-1, 0).mt_lut("x 128 > 255 x ?") msk = blur ? msk.mt_convolution("1 1 1 1 1", "1 1 1 1 1").converttoyv12() : msk.converttoyv12() fill = FalPos ? trim(c,0,current_frame-1)++trim(FixFalPos,current_frame,-1)++trim(c,current_frame+1,0) : \ (ter ? InpaintLogo(c, radius=max(x1,y1,x2,y2)+max(2,pad), mask=msk) : c) sub = debug ? subtitle(msk,"Bordered",align=3,size=round(h/20.0)) : nop() debug ? (ter?sub:msk) : fill """,args="y8,c,w,h,thr,thr2,L4,R4,T4,B4,L3,R3,T3,B3,L2,R2,T2,B2,L1,R1,T1,B1,pad,blur,debug,FalPos,FixFalPos") !debug ? Fill : \ eval(""" box = h/4.0 m = 2 corner = crop(mt_lut("255"),round((w-box)/m)*m,round((h-box)/m)*m,0,0) corner = corner.addborders(0,0,round((w/2.0-box)/m)*m,round(box/m)*m).mt_lut("x 255 < 0 255 ?") horiz = stackhorizontal(corner,corner.fliphorizontal()) verti = stackvertical(horiz,horiz.flipvertical()) mt_merge(c,Fill.pointresize(w,h),verti,luma=true)""") } #################################### ### ### FindBlackBorders() ### ### Script to find sources with black borders for example as a result of bad deshaking, run on analysis pass ### The output file is formatted to be imported to avspmod as bookmarks ### use ClipClop() afterwards on a scene by scene basis to fix this. ### ### "width" is border thickness for detection ### "thr" is threshold, pixel values below this will be considered borders ### "path" is the path to store the statistics file, with end backslash. Default is "C:" ### "filename" is the statistics file name. In case you don't want to overwrite old ones ### ### (http://forum.videohelp.com/threads/371336) ### ### Dependencies: ### ### Required: ### ------------ ### ### masktools (v2a48 or higher) (http://forum.doom9.org/showthread.php?t=98985) ### Dither (v1.26.5 or higher) (http://forum.doom9.org/showthread.php?p=1386559#post1386559) ### #################################### function FindBlackBorders(clip c, int "width", int "thr", string "path", string "filename") { add = Default(width,1) # Width for detection, normally 1 should suffix to most situations thr = Default(thr,1) # Threshold for detection, pixels lower than this value will be considered a border path = Default(path, "C:") # This is the path to store the statistics file filename = Default(filename, "FindBlackBorders - Statistics.log") # Filename of the statistics file c converttoy8() w=width() h=height() L1=crop(0,0,-w+add,0) R1=crop(w-add,0,0 ,0) T1=crop(0,0,0,-h+add) B1=crop(0,h-add,0 ,0) ScriptClip(""" x1 = AverageLuma(L1) < thr ? true : false x2 = AverageLuma(R1) < thr ? true : false y1 = AverageLuma(T1) < thr ? true : false y2 = AverageLuma(B1) < thr ? true : false function IsBorder(clip c, bool x1, bool x2, bool y1, bool y2) {return (x1||y1||x2||y2)?true:false} q = chr(34)chr(34)chr(34) WriteFileIf(""+path+"\"+filename+"", " "+string(IsBorder(x1,y1,x2,y2))+" ", q+"CHAPTER00="+q, \ "FFFormatTime(round((current_frame * 1000) / framerate()))", "", "") """,args="thr,L1,R1,T1,B1,path,filename") converttoyv12() # Bug or limitations of 8-bit masktools with Overlay or... # mt_merge (masking PC Range masks) so use Dither tools Dither_merge16_8(Dither_convert_8_to_16(),Dither_convert_8_to_16(c),mt_lut("255"),luma=true) ditherpost(mode=-1) } ######### HELPER FUNCTIONS ######### # Helper function for FindBlackBorders() from FFMS2.avsi function FFFormatTime(int ms) { s = ms / 1000 ms = ms % 1000 m = s / 60 s = s % 60 h = m / 60 m = m % 60 return string(h) + ":" + string(m,"%02.0f") + ":" + string(s,"%02.0f") + "." + string(ms,"%03.0f")} # Helper function for FillBorders() and FindBlackBorders() function PadInwards(clip clp, int "left", int "top", int "right", int "bottom", int "color") { l = default( left, 0 ) t = default( top, 0 ) r = default( right, 0 ) b = default( bottom,0 ) color = default( color, 000000 ) clp crop (l,t,-r,-b) addborders(l,t, r, b,color) }
__________________
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; 9th August 2015 at 15:44. |
9th August 2015, 14:03 | #16 | Link |
Registered User
Join Date: Nov 2009
Posts: 2,352
|
Thanks, you reposted my script? didn't change anything?
Why letterbox? PadInwards is only a crop() and addborders() line. I added another check to avoid evaluating borders of 4 pixels for every frame, I will keep updating in its original thread. Code:
FalPos = FalPos ? ((x1-pad > 2 || x2-pad > 2 || y1-pad > 2 || y2-pad > 2 ) \ ? ((AverageLuma(L4) < thr2) || (AverageLuma(R4) < thr2) || (AverageLuma(T4) < thr2) || (AverageLuma(B4) < thr2) \ ? true : false) : false) : false |
9th August 2015, 15:40 | #17 | Link | ||
HeartlessS Usurer
Join Date: Dec 2009
Location: Over the rainbow
Posts: 10,980
|
Quote:
LetterBox is only a crop and AddBorders too (but different arg order), from docs. Quote:
__________________
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; 9th August 2015 at 15:46. |
||
4th November 2016, 03:30 | #19 | Link |
HeartlessS Usurer
Join Date: Dec 2009
Location: Over the rainbow
Posts: 10,980
|
Small mod for use with RT_Stats v2.0 Beta 3
RT_QwikScan changed to using Arrays instead of DBase's, RT_QwickScanCreate args PrevDB, NextDB changed to Prev and Next only. Code:
# SpatialAlign.avs Function SpatialAlign(clip Fix,clip Src,Int "MatchLen",Int "Rad",Float "LdTh",String "Prefix") { /* SpatialAlign v1.01: 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). Could take a 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 v2.0 BETA 3, 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.DB",Tim) NDB = RT_String("~NDB_%s.DB",Tim) RT_QwikScanCreate(Src,QDB,prev="",next=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 = QWKS_BM_LD # Best or EXACT exit condition match for LumaDifference 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 }
__________________
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; 4th November 2016 at 03:35. |
Tags |
spatial alignment |
Thread Tools | Search this Thread |
Display Modes | |
|
|