SHOW:
|
|
- or go back to the newest paste.
| 1 | ### # | |
| 2 | ### # | |
| 3 | ### Stabilization Tools Pack 2.1 by Dogway (16-09-2015) # | |
| 4 | - | ### mod 2.3 by A.SONY in (18-02-2018) # |
| 4 | + | ### mod 2.4 by A.SONY in (18-02-2018) # |
| 5 | ### # | |
| 6 | ########################################################## | |
| 7 | ### | |
| 8 | ### FilmGateFix() | |
| 9 | ### | |
| 10 | ### (http://forum.videohelp.com/threads/371336) | |
| 11 | ### | |
| 12 | ### Function to fix frames ( by means of FreezeFrame() ) with film gate issues after a scene change. | |
| 13 | ### This is aimed at animation, for live footage frame interpolation should be desired. | |
| 14 | ### | |
| 15 | ### Rationale: | |
| 16 | ### Film Gate is about non-linear vertical stretching, thus we compare | |
| 17 | ### current and next frames in two blocks, a 1/5 block of top and bottom. | |
| 18 | ### The difference in both should be very high for film gate artifacts | |
| 19 | ### between the 2nd and 3rd frame after a Scene Change (also 1st and 2nd are compared). | |
| 20 | ### We mask this through a Scene Detection filter, motion masks, etc. | |
| 21 | ### | |
| 22 | ### EXPERIMENTAL: | |
| 23 | ### It still grabs too many false-positives, and leaves many positives-positives undone | |
| 24 | ### a better approach would be to automatically bookmark every scene change and manually freezeframe them | |
| 25 | ### | |
| 26 | ### | |
| 27 | ### Dependencies: | |
| 28 | ### | |
| 29 | ### Required: | |
| 30 | ### ------------ | |
| 31 | ### | |
| 32 | ### MVTools (v2.5.11.3 or higher) (http://avisynth.org.ru/mvtools/mvtools2.html) | |
| 33 | ### masktools (v2a48 or higher) (http://forum.doom9.org/showthread.php?t=98985) | |
| 34 | ### Average (v0.92 or higher) (http://forum.doom9.org/showthread.php?t=169832) | |
| 35 | ### GRunT (v1.0.1 or higher) (http://forum.doom9.org/showthread.php?t=139337) | |
| 36 | ### | |
| 37 | #################################### | |
| 38 | ||
| 39 | function FilmGateFix(clip c, float "thr", int "window", int "thSCD1", int "thSCD2", bool "debug") {
| |
| 40 | ||
| 41 | thr = Default(thr,1.2) # Main tweak setting, use debug and tweak according 2nd frame after Scene Change | |
| 42 | window = Default(window,5) # Enlarges/reduces detection area, "height()/window" | |
| 43 | thSCD1 = Default(thSCD1,500) # Increase to reduce number of scenes detected | |
| 44 | thSCD2 = Default(thSCD2,145) # Increase to reduce number of scenes detected | |
| 45 | Debug = Default(Debug,false) # Check what frames will be fixed and the difference value of frames for tweaking "thr" | |
| 46 | thr2 = 5.0 # central part of frame must change less than this to consider static scene to fix. | |
| 47 | ||
| 48 | c | |
| 49 | w=width() | |
| 50 | h=height() | |
| 51 | ||
| 52 | # Block for Scene Change detection and Motion Mask | |
| 53 | kind = 0 | |
| 54 | gam = 1.2 # Could probably be exposed | |
| 55 | mvthr = 30 # Threshold, heavily dependent on gamma value above | |
| 56 | super = MSuper (pel=1, sharp=0,vpad=0,hpad=0) | |
| 57 | b1v = MAnalyse(super,isb=true, blksize=16,overlap=8,search=0) | |
| 58 | f1v = MAnalyse(super,isb=false, blksize=16,overlap=8,search=0) | |
| 59 | SADbv1 = MMask (b1v,kind=kind,gamma=gam,thSCD1=thSCD1,thSCD2=thSCD2) | |
| 60 | SADfv1 = MMask (f1v,kind=kind,gamma=gam,thSCD1=thSCD1,thSCD2=thSCD2) | |
| 61 | ||
| 62 | SceneChange = MSCDetection (f1v,thSCD1=thSCD1,thSCD2=thSCD2) | |
| 63 | mvmask = Average(SADbv1,1./2,SADfv1,1./2) | |
| 64 | ||
| 65 | ||
| 66 | # Film Gate happens commonly between 2nd and 3rd frame after SC | |
| 67 | # 1st frame is commonly garbage | |
| 68 | add = round(w/(window*1.0)) | |
| 69 | T = crop(0,0,0,((-h+add)/8)*8) | |
| 70 | B = crop(0,((h-add)/8)*8,0,0) | |
| 71 | ||
| 72 | L = crop(0,0,((-w+add)/8)*8,0) | |
| 73 | R = crop(((w-add)/8)*8,0,0,0) | |
| 74 | ||
| 75 | CTB = crop(0,((add)/8)*8,0,-((add)/8)*8) | |
| 76 | CLR = crop(round(((add*1.5)/8)*8),0,round(-((add*1.5)/8)*8),0) | |
| 77 | ||
| 78 | SC2 = mt_logic(SceneChange,selectevery(SceneChange,1,-1),"max",U=3,V=3) | |
| 79 | ||
| 80 | # Here, if after SC top area changes more than "thr" compared to bottom area | |
| 81 | # then apply one FreezeFrame() for first SC frame, and another FreezeFrame() for the 2nd one, | |
| 82 | # they (1st and 2nd) compute individually. There is a motionmask on top to rule out false positives. | |
| 83 | scriptclip("""(abs(YDifferenceToNext(T)-YDifferenceToNext(B))>"""+string(thr)+""" && abs(YDifferenceToNext(CTB))<"""+
| |
| 84 | \ string(thr2)+""") || (abs(YDifferenceToNext(L)-YDifferenceToNext(R))>"""+string(thr)+ | |
| 85 | \ """ && abs(YDifferenceToNext(CLR))<"""+string(thr2)+""") ?"""+ | |
| 86 | \ """(YplaneMax(SC2)>254?(YDifferenceFromPrevious(SC2)>250?(AverageLuma(mvmask)<"""+string(mvthr)+ | |
| 87 | \ """?(LumaDifference(c,d)<3.7?freezeframe(current_frame,current_frame,current_frame+2):(YDifferenceToNext()<8.0?freezeframe(current_frame,current_frame,current_frame+1):c)):c):"""+ | |
| 88 | \ """(AverageLuma(mvmask)<"""+string(mvthr)+"""?freezeframe(current_frame,current_frame,current_frame+1):c)):c):c""", | |
| 89 | \ args="T,B,L,R,c,SC2,CTB,CLR,mvmask,d=Trim(2,0)") | |
| 90 | ||
| 91 | # Debug block | |
| 92 | debug ? +\ | |
| 93 | eval("""stackhorizontal(scriptclip("Subtitle(c,"+Chr(34)+" Top & Bottom:"+Chr(34)+"+String(YDifferenceToNext(T)-YDifferenceToNext(B))+
| |
| 94 | \ "+Chr(34)+"\n Left & Right: "+Chr(34)+"+String(YDifferenceToNext(L)-YDifferenceToNext(R))+ | |
| 95 | \ "+Chr(34)+"\n Center Horiz: "+Chr(34)+"+String(YDifferenceToNext(CTB))+ | |
| 96 | \ "+Chr(34)+"\n Center Vert: "+Chr(34)+"+String(YDifferenceToNext(CLR))+ | |
| 97 | \ "+Chr(34)+"\n AvgLuma: "+Chr(34)+"+String(LumaDifference(c,d)), | |
| 98 | \ align=7,size=30,lsp=10)",args="T,B,L,R,c,CTB,CLR,d=Trim(2,0)"), | |
| 99 | \ scriptclip("AverageLuma(mvmask)<"""+string(mvthr)+"""?((abs(YDifferenceToNext(T)-YDifferenceToNext(B))>"+string(thr)+
| |
| 100 | \ " && abs(YDifferenceToNext(CTB))<"+string(thr2)+") || (abs(YDifferenceToNext(L)-YDifferenceToNext(R))>"+ | |
| 101 | \ string(thr)+" && abs(YDifferenceToNext(CLR))<"+string(thr2)+") ? "+ | |
| 102 | \ "(YplaneMax(SC2)>254?(LumaDifference(c,d)<3.7?mt_lut(c,"+Chr(34)+"255"+Chr(34)+",U=128,V=128):(YDifferenceToNext()<8.0?mt_lut(c,"+Chr(34)+"255"+Chr(34)+",U=128,V=128):mt_lut(c,"+Chr(34)+"0"+Chr(34)+ | |
| 103 | \ ",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"+ | |
| 104 | \ Chr(34)+",U=128,V=128)",args="T,B,L,R,c,CTB,CLR,SC2,mvmask,d=Trim(2,0)")) | |
| 105 | \ crop(0,0,-(round(w/1.1)/2)*2,0).Subtitle("Frame to Freeze",align=9,size=40)""") : last }
| |
| 106 | ||
| 107 | ||
| 108 | ||
| 109 | ||
| 110 | ||
| 111 | #################################### | |
| 112 | ### | |
| 113 | ### FillBorders() | |
| 114 | ### | |
| 115 | ### (http://forum.videohelp.com/threads/371336) | |
| 116 | ### | |
| 117 | ### Function to fill dark (use threshold) borders, in the vein of old FillMargins() function. | |
| 118 | ### But instead of mirroring or other approaches like resizing, this function fills/interpolates | |
| 119 | ### missing data from surrounding pixels. | |
| 120 | ### Useful to use as a clean pass after stab() and crop(x1,y1,x2,y2) | |
| 121 | ### for the remaining thin black borders (up to 3px) | |
| 122 | ### | |
| 123 | ### For borders of 4px and more you can enable FixFalPos, there you supply a clip | |
| 124 | ### without black borders (ie. before stab() ). It automatically replaces the offended frames. | |
| 125 | ### Some thick black borders aren't "0" black at all, they show garbage and the 3+1 pixel border | |
| 126 | ### may not average to 0, so a "thr2" setting is added as threshold, default 7 should be enough. | |
| 127 | ### | |
| 128 | ### Below you can still use the the FindBlackBorders() function for manual handling of thick black borders. | |
| 129 | ### Use ClipClop() for the stab() results according to the statistics file of FindBlackBorders() | |
| 130 | ### | |
| 131 | ### Mind you, for FillBorders() you need to manually load the AVSInpaint plugin: | |
| 132 | ### LoadCPlugin("AVSInpaint.dll")
| |
| 133 | ### | |
| 134 | ### EXPERIMENTAL: | |
| 135 | ### Some thick black borders aren't value 0 at all, sometimes they average to 16, so one would | |
| 136 | ### need to set thr2 to >16 which will basically bypass the Stab'ed clip in a lot of dark scenes. | |
| 137 | ### So probably you might still want to use this for only <4px borders and FindBlackBorders() to | |
| 138 | ### manually find the most offending borders and problematic areas. | |
| 139 | ### | |
| 140 | ### | |
| 141 | ### Dependencies: | |
| 142 | ### | |
| 143 | ### masktools (v2a48 or higher) (http://forum.doom9.org/showthread.php?t=98985) | |
| 144 | ### GRunT (v1.0.1 or higher) (http://forum.doom9.org/showthread.php?t=139337) | |
| 145 | ### AVSInpaint (v2008.02.23 or higher) (http://forum.doom9.org/showthread.php?t=133682) | |
| 146 | ### | |
| 147 | #################################### | |
| 148 | ||
| 149 | function FillBorders(clip c, int "thr", int "pad", bool "blur", bool "debug", clip "FixFalPos", int "thr2", bool "mirror", float "PAR", bool "subsample" ) {
| |
| 150 | ||
| 151 | thr = Default(thr, 1) # Threshold, pixel values below this will be considered borders | |
| 152 | pad = Default(pad, 0) # Pixels, you can expand the replacement area adding more pixels | |
| 153 | # (to deal with dirty borders) (use "1" to deal with 1px b/w chroma, due to the chroma subsampling nature of video) | |
| 154 | mirror = Default(mirror, false) | |
| 155 | blur = Default(blur, mirror) # Blurs the masking for the replacement area. Currently not supported for InpaintLogo() | |
| 156 | debug = Default(debug,false) # Show the borders that are going to be filled | |
| 157 | FalPos = Defined(FixFalPos) # If you supply a reference clip borders with 4 or more | |
| 158 | # average thr2 pixels will be replaced with the clip's frame | |
| 159 | thr2 = Default(thr2, 7) # Threshold for FalPos, FalPos frames sometimes have garbage borders so you need to increase threshold | |
| 160 | subsampl = Default(subsample,false) | |
| 161 | ||
| 162 | ssispmt = Findstr(VersionString(), "AviSynth+") != 0 && Findstr(VersionString(), "r1576") == 0 | |
| 163 | ||
| 164 | c | |
| 165 | contoy = ssispmt ? !isy() : !isy8() | |
| 166 | contoy ? ssispmt ? converttoy() : converttoy8() : last | |
| 167 | yclip=last | |
| 168 | w=width() | |
| 169 | h=height() | |
| 170 | ||
| 171 | # 4px or greater | |
| 172 | L4=FalPos ? crop(0,0,-w+4,0) : nop() | |
| 173 | R4=FalPos ? crop(w-4,0,0 ,0) : nop() | |
| 174 | T4=FalPos ? crop(0,0,0,-h+4) : nop() | |
| 175 | B4=FalPos ? crop(0,h-4,0 ,0) : nop() | |
| 176 | # 3px | |
| 177 | L3=crop(0,0,-w+3,0) | |
| 178 | R3=crop(w-3,0,0 ,0) | |
| 179 | T3=crop(0,0,0,-h+3) | |
| 180 | B3=crop(0,h-3,0 ,0) | |
| 181 | # 2px | |
| 182 | L2=crop(L3,0,0,-1, 0) | |
| 183 | R2=crop(R3,1,0,0 , 0) | |
| 184 | T2=crop(T3,0,0,0 ,-1) | |
| 185 | B2=crop(B3,0,1,0 , 0) | |
| 186 | # 1px | |
| 187 | L1=crop(L3,0,0,-2,0 ) | |
| 188 | R1=crop(R3,2,0,0 ,0 ) | |
| 189 | T1=crop(T3,0,0,0 ,-2) | |
| 190 | B1=crop(B3,0,2,0 ,0 ) | |
| 191 | ||
| 192 | c | |
| 193 | Fill = gScriptClip("""
| |
| 194 | yclip | |
| 195 | pad= blur ? pad+2 : pad | |
| 196 | ||
| 197 | L1A = AverageLuma(L1) | |
| 198 | R1A = AverageLuma(R1) | |
| 199 | T1A = AverageLuma(T1) | |
| 200 | B1A = AverageLuma(B1) | |
| 201 | ||
| 202 | x1o = L1A < thr ? (AverageLuma(L2) < thr ? (AverageLuma(L3) < thr ? 3 : 2) : 1) : 0 | |
| 203 | x1 = L1A < thr ? x1o+pad : x1o | |
| 204 | x2o = R1A < thr ? (AverageLuma(R2) < thr ? (AverageLuma(R3) < thr ? 3 : 2) : 1) : 0 | |
| 205 | x2 = R1A < thr ? x2o+pad : x2o | |
| 206 | y1o = T1A < thr ? (AverageLuma(T2) < thr ? (AverageLuma(T3) < thr ? 3 : 2) : 1) : 0 | |
| 207 | y1 = T1A < thr ? y1o+pad : y1o | |
| 208 | y2o = B1A < thr ? (AverageLuma(B2) < thr ? (AverageLuma(B3) < thr ? 3 : 2) : 1) : 0 | |
| 209 | y2 = B1A < thr ? y2o+pad : y2o | |
| 210 | ||
| 211 | FalPos = FalPos ? ((x1-pad > 2 || x2-pad > 2 || y1-pad > 2 || y2-pad > 2 ) | |
| 212 | \ ? ((AverageLuma(L4) < thr2) || (AverageLuma(R4) < thr2) || (AverageLuma(T4) < thr2) || (AverageLuma(B4) < thr2) | |
| 213 | \ ? true : false) : false) : false | |
| 214 | ||
| 215 | ||
| 216 | cropnpad = (x1+y1+x2+y2>0) | |
| 217 | ter = !FalPos && cropnpad | |
| 218 | ||
| 219 | x1inp=cropnpad && subsampl ? int(round(x1o/2.0)*2) : x1 | |
| 220 | x2inp=cropnpad && subsampl ? int(round(x2o/2.0)*2) : x2 | |
| 221 | y1inp=cropnpad && subsampl ? int(round(y1o/2.0)*2) : y1 | |
| 222 | y2inp=cropnpad && subsampl ? int(round(y2o/2.0)*2) : y2 | |
| 223 | ||
| 224 | - | PaCl = mirror ? c.crop(x1inp,y1inp,-x2inp,-y2inp,align=true).Padding(x1inp,y1inp,x2inp,y2inp) : c |
| 224 | + | PaCl = mirror && !ter ? c.crop(x1inp,y1inp,-x2inp,-y2inp,align=true).Padding(x1inp,y1inp,x2inp,y2inp) : c |
| 225 | ||
| 226 | msk = LetterBox(BlankClip(last, Color_yuv=$000000),y1,y2,x1,x2,$ffffff) | |
| 227 | msk = !mirror ? msk.mt_lut("x 128 > 255 x ?") : msk
| |
| 228 | msk = !mirror && blur ? msk.mt_convolution("1 1 1 1 1", "1 1 1 1 1") : msk
| |
| 229 | msk = contoy ? ssispmt ? CombinePlanes(msk,c,planes="YUV",sample_clip=c) : YToUV(c.UToY8(),c.VToY8(),msk) : msk | |
| 230 | ||
| 231 | fill = FalPos ? trim(c,0,current_frame-1)++trim(FixFalPos,current_frame,-1)++trim(c,current_frame+1,0) : \ | |
| 232 | - | (ter ? InpaintLogo(PaCl, radius=max(x1,y1,x2,y2)+max(2,pad), mask=msk,ChromaTensor=subsampl,PixelAspect=PAR,PreBlur=mirror && blur ? 2.5 : undefined) : PaCl) |
| 232 | + | (ter ? InpaintLogo(c, radius=max(x1,y1,x2,y2)+max(2,pad), mask=msk,ChromaTensor=subsampl,PixelAspect=PAR,PreBlur=mirror && blur ? 2.5 : undefined) : PaCl) |
| 233 | sub = debug ? subtitle(msk,"Bordered",align=3,size=round(h/20.0)) : nop() | |
| 234 | ||
| 235 | debug ? (ter?sub:msk) : fill | |
| 236 | """,args="yclip,c,ssispmt,contoy,thr,thr2,L4,R4,T4,B4,L3,R3,T3,B3,L2,R2,T2,B2,L1,R1,T1,B1,pad,blur,debug,FalPos,FixFalPos,mirror,subsampl,PAR") | |
| 237 | ||
| 238 | !debug ? Fill : \ | |
| 239 | eval("""
| |
| 240 | box = h/4.0 | |
| 241 | m = 2 | |
| 242 | ||
| 243 | corner = crop(mt_lut("255"),round((w-box)/m)*m,round((h-box)/m)*m,0,0,align=true)
| |
| 244 | corner = corner.addborders(0,0,round((w/2.0-box)/m)*m,round(box/m)*m).mt_lut("x 255 < 0 255 ?")
| |
| 245 | ||
| 246 | horiz = stackhorizontal(corner,corner.fliphorizontal()) | |
| 247 | verti = stackvertical(horiz,horiz.flipvertical()) | |
| 248 | ||
| 249 | mt_merge(c,Fill.pointresize(w,h),verti,luma=true)""") } | |
| 250 | ||
| 251 | ||
| 252 | #################################### | |
| 253 | ### | |
| 254 | ### FindBlackBorders() | |
| 255 | ### | |
| 256 | ### (http://forum.videohelp.com/threads/371336) | |
| 257 | ### | |
| 258 | ### Script to find sources with black borders for example as a result of bad deshaking, run on analysis pass | |
| 259 | ### The output file is formatted to be imported to avspmod as bookmarks | |
| 260 | ### use ClipClop() afterwards on a scene by scene basis to fix this. | |
| 261 | ### | |
| 262 | ### "width" is border thickness for detection | |
| 263 | ### "thr" is threshold, pixel values below this will be considered borders | |
| 264 | ### "path" is the path to store the statistics file, with end backslash. Default is "C:" | |
| 265 | ### "filename" is the statistics file name. In case you don't want to overwrite old ones | |
| 266 | ### | |
| 267 | ### Dependencies: | |
| 268 | ### | |
| 269 | ### Required: | |
| 270 | ### ------------ | |
| 271 | ### | |
| 272 | ### masktools (v2a48 or higher) (http://forum.doom9.org/showthread.php?t=98985) | |
| 273 | ### Dither (v1.26.5 or higher) (http://forum.doom9.org/showthread.php?p=1386559#post1386559) | |
| 274 | ### | |
| 275 | #################################### | |
| 276 | ||
| 277 | function FindBlackBorders(clip c, int "width", int "thr", string "path", string "filename") {
| |
| 278 | ||
| 279 | add = Default(width,1) # Width for detection, normally 1 should suffix to most situations | |
| 280 | thr = Default(thr,1) # Threshold for detection, pixels lower than this value will be considered a border | |
| 281 | path = Default(path, "C:") # This is the path to store the statistics file | |
| 282 | filename = Default(filename, "FindBlackBorders - Statistics.log") # Filename of the statistics file | |
| 283 | ||
| 284 | c | |
| 285 | converttoy8() | |
| 286 | w=width() | |
| 287 | h=height() | |
| 288 | ||
| 289 | L1=crop(0,0,-w+add,0) | |
| 290 | R1=crop(w-add,0,0 ,0) | |
| 291 | T1=crop(0,0,0,-h+add) | |
| 292 | B1=crop(0,h-add,0 ,0) | |
| 293 | ||
| 294 | ScriptClip("""
| |
| 295 | x1 = AverageLuma(L1) < thr ? true : false | |
| 296 | x2 = AverageLuma(R1) < thr ? true : false | |
| 297 | y1 = AverageLuma(T1) < thr ? true : false | |
| 298 | y2 = AverageLuma(B1) < thr ? true : false | |
| 299 | function IsBorder(clip c, bool x1, bool x2, bool y1, bool y2) {return (x1||y1||x2||y2)?true:false}
| |
| 300 | ||
| 301 | q = chr(34)chr(34)chr(34) | |
| 302 | WriteFileIf(""+path+"\"+filename+"", " "+string(IsBorder(x1,y1,x2,y2))+" ", q+"CHAPTER00="+q,
| |
| 303 | \ "FFFormatTime_stabi(round((current_frame * 1000) / framerate()))", "", "") | |
| 304 | """,args="thr,L1,R1,T1,B1,path,filename") | |
| 305 | ||
| 306 | converttoyv12() | |
| 307 | ||
| 308 | # Bug or limitations of 8-bit masktools with Overlay or... | |
| 309 | # mt_merge (masking PC Range masks) so use Dither tools | |
| 310 | Dither_merge16_8(Dither_convert_8_to_16(),Dither_convert_8_to_16(c),mt_lut("255"),luma=true)
| |
| 311 | ditherpost(mode=-1) } | |
| 312 | ||
| 313 | ||
| 314 | ||
| 315 | #################################### | |
| 316 | ### | |
| 317 | ### Stab2() | |
| 318 | ### | |
| 319 | ### (http://forum.videohelp.com/threads/371336) | |
| 320 | ### | |
| 321 | ### For completeness I'm going to list all the shortcomings of Depan plugin | |
| 322 | ### maybe a programmer realises the urge of an avisynth bug-free stabilizer: | |
| 323 | ### -Color tint (hue shift to green) when subpixel>0 to stablizated frames --> addressed here | |
| 324 | ### -Artifacts occasionally on frame borders (1px skew) --> crop out, check example | |
| 325 | ### -No advanced border fill --> addressed above ( FillBorders() ) | |
| 326 | ### -Some false-positives when people clapping, trembling... | |
| 327 | ### -Requires mod 4 inputs? | |
| 328 | ### -No medium jitter fix | |
| 329 | ### -Some thick borders (false positives) aren't 0 black on the inner side | |
| 330 | ### | |
| 331 | ####################################### | |
| 332 | ### | |
| 333 | ### What I improved over original Stab() was to work around the first issue by | |
| 334 | ### counter fixing the planes hue deviation on a frame by frame basis with float point precision | |
| 335 | ### Implemented a FixPalPos to replace frames that present black borders of 3 or more pixels | |
| 336 | ### with source frames, check output just in case, since some borders don't average to 0 black (rare). | |
| 337 | ### Also supplied a more contrasty clip version for more subtle global motion analysis | |
| 338 | ### Prefilter is for the prefilter clip, in case the clip is very grainy/noisy | |
| 339 | ### Finally a few more things were introduced by testing and checking other script versions. | |
| 340 | ### | |
| 341 | ### | |
| 342 | ### Required (*Optional): | |
| 343 | ### ------------ | |
| 344 | ### Depan (v1.10.1 or higher) (http://avisynth.org.ru/depan/depan.html) | |
| 345 | ### DePanEstimate (v1.10.1 or higher) (http://avisynth.org.ru/depan/depan.html) | |
| 346 | ### Dither (v1.26.5 or higher) (http://forum.doom9.org/showthread.php?p=1386559#post1386559) | |
| 347 | ### SmoothAdjust* (v3.0 or higher) (http://forum.doom9.org/showthread.php?t=154971) | |
| 348 | ### AutoAdjust (v2.50 or higher) (http://forum.doom9.org/showthread.php?t=167573) | |
| 349 | ### GRunT (v1.0.1 or higher) (http://forum.doom9.org/showthread.php?t=139337) | |
| 350 | ### Repair (vanilla or tp7's branch) (http://avisynth.nl/index.php/Removegrain) | |
| 351 | ### | |
| 352 | ### Example: | |
| 353 | ### | |
| 354 | ### stab2(UVfix=true,FixFalPos=true) | |
| 355 | ### crop(2,2,-2,-2) # to remove garbage around borders | |
| 356 | ### | |
| 357 | #################################### | |
| 358 | ||
| 359 | function Stab2 (clip clp, int "ts", int "range", int "dxmax", int "dymax", bool "UVfix", bool "FixFalPos", float "zoom", int "mirror", float "PAR", clip "Prefilter") {
| |
| 360 | ||
| 361 | ts = default(ts, 7) #frames to temporal average for better motion estimation (max. 7) | |
| 362 | range = default(range, 3) #frames before/after to estimate motion | |
| 363 | dxmax = default(dxmax, 15) #maximum deviation in pixels (use at least 8 for SD) | |
| 364 | dymax = default(dymax, 15) #x, and y should be the same | |
| 365 | zoom = default(zoom, 1) #maximum zoom factor (1 disabled) | |
| 366 | mirror = default(mirror, 0) #Edge filling. 0 off, 15 everything on | |
| 367 | PAR = default(PAR, 1.0) #PAR of your source | |
| 368 | UVfix = default(UVfix, true) # Fixes the bug of change of HUE in Depan, | |
| 369 | FixFalPos = default(FixFalPos, true) # Fixes borders of 3 or more pixels wide. Use along crop(2,2,-2,-2)... | |
| 370 | # ...after stab2() to get rid of border issues entirely | |
| 371 | ||
| 372 | Pref = Defined(Prefilter) ? Prefilter : clp | |
| 373 | temp = Pref.TemporalSoften(ts,255,255,25,2) # SC thr to 25 otherwise pans will stutter | |
| 374 | rep = temp.Repair(Pref.TemporalSoften(1,255,255,25,2)) | |
| 375 | inter = Interleave(rep,Pref) | |
| 376 | ||
| 377 | # temporal stable auto-contrast (better subpixel detection) | |
| 378 | Luma_Expa = AutoAdjust(inter,temporal_radius=10,auto_balance=false,auto_gain=true,use_interp=true,\ | |
| 379 | avg_safety=0.25,dark_limit=10,bright_limit=10,gamma_limit=1.0,dark_exclude=0.05,bright_exclude=0.05,\ | |
| 380 | chroma_process=0,scd_threshold=16,input_tv=false,output_tv=false,high_bitdepth=false,debug_view=false) | |
| 381 | ||
| 382 | mdata = DePanEstimate(Luma_Expa,range=range,pixaspect=PAR,trust=0,dxmax=dxmax,dymax=dymax,zoommax=zoom,improve=true) | |
| 383 | ||
| 384 | DePan(Defined(Prefilter)?Interleave(rep,clp):inter,data=mdata,offset=-1,mirror=mirror,pixaspect=PAR,matchfields=false,subpixel=2) | |
| 385 | SelectEvery(2,0) | |
| 386 | ||
| 387 | # from depansafe() function | |
| 388 | FixFalPos ? eval("""
| |
| 389 | thick = 2.0 # removing >2px wide borders | |
| 390 | cropx = ceil(dxmax)*2 | |
| 391 | ratiox = "YPlaneMax("+string(ceil(99-thick/cropx*100))+")"
| |
| 392 | ||
| 393 | crop(0,0,0,cropx).conditionalfilter(last,clp,ratiox,">","0") | |
| 394 | crop(0,height-cropx,0,0).conditionalfilter(last,clp,ratiox,">","0") | |
| 395 | crop(0,0,cropx,0).conditionalfilter(last,clp,ratiox,">","0") | |
| 396 | crop(width-cropx,0,0,0).conditionalfilter(last,clp,ratiox,">","0")""") : last | |
| 397 | ||
| 398 | ||
| 399 | UVfix ? eval("""
| |
| 400 | ScriptClip ("
| |
| 401 | blue=round((AverageChromaU(clp) - AverageChromaU()) * 256.0) | |
| 402 | red=round((AverageChromaV(clp) - AverageChromaV()) * 256.0) | |
| 403 | ||
| 404 | Dither_convert_8_to_16() | |
| 405 | SmoothTweak16(saturation=1.0,hue1=min(384,blue),hue2=min(384,red),HQ=true) | |
| 406 | DitherPost(stacked=true,prot=false,mode=6) | |
| 407 | ", args="clp" ) """) : last | |
| 408 | ||
| 409 | } | |
| 410 | ||
| 411 | ||
| 412 | #################################### | |
| 413 | ### | |
| 414 | ### Stab3() | |
| 415 | ### | |
| 416 | ### same as Stab2 but with some changes | |
| 417 | ### | |
| 418 | #################################### | |
| 419 | ||
| 420 | function Stab3 (clip clp, int "ts", int "range", int "dxmax", int "dymax", bool "UVfix", bool "FixFalPos", float "zoom", int "mirror", float "PAR", clip "Prefilter", int "Luma_Exp", bool "Fill3pBorders", clip "FalPosclip") {
| |
| 421 | ||
| 422 | ts = default(ts, 7) #frames to temporal average for better motion estimation (max. 7) | |
| 423 | range = default(range, 1) #frames before/after to estimate motion | |
| 424 | dxmax = default(dxmax, Round(clp.width()/180.0)) #maximum deviation in pixels | |
| 425 | dymax = default(dymax, dxmax) #x, and y should be the same | |
| 426 | zoom = default(zoom, 1) #maximum zoom factor (1 disabled) | |
| 427 | mirror = default(mirror, 0) #Edge filling. 0 off, 15 everything on | |
| 428 | PAR = default(PAR, 1.0) #PAR of your source | |
| 429 | UVfix = default(UVfix, false) # Fixes the bug of change of HUE in Depan, not need in depan 1.13.1 and up | |
| 430 | b3fix = default(Fill3pBorders, true) # Fixes borders of 3 or less pixels wide. | |
| 431 | FixFalPos = default(FixFalPos, true) # Fixes borders of 3 or more pixels wide. Use along crop(2,2,-2,-2)... | |
| 432 | # ...after stab2() to get rid of border issues entirely | |
| 433 | Lumae = default(Luma_Exp, 1) #Luma Rebuild | |
| 434 | ||
| 435 | Pref = Defined(Prefilter) ? Prefilter : clp | |
| 436 | temp = Pref.TemporalSoften(ts,255,255,25,2) # SC thr to 25 otherwise pans will stutter | |
| 437 | rep = temp.Repair(Pref.TemporalSoften(1,255,255,25,2)) | |
| 438 | inter = Interleave(rep,Pref) | |
| 439 | ||
| 440 | # temporal stable auto-contrast (better subpixel detection) | |
| 441 | Luma_Expa = Lumae==2 ? AutoAdjust(inter,temporal_radius=10,auto_balance=false,auto_gain=true,use_interp=true,\ | |
| 442 | avg_safety=0.25,dark_limit=10,bright_limit=10,gamma_limit=1.0,dark_exclude=0.05,bright_exclude=0.05,\ | |
| 443 | chroma_process=0,scd_threshold=16,input_tv=false,output_tv=false,high_bitdepth=false,debug_view=false) : \ | |
| 444 | Lumae==1 ? inter.ColorYUV(levels="TV->PC") : inter | |
| 445 | ||
| 446 | mdata = DePanEstimate(Luma_Expa,range=range,pixaspect=PAR,trust=0,dxmax=dxmax,dymax=dymax,zoommax=zoom) | |
| 447 | ||
| 448 | DePan(Defined(Prefilter)?Interleave(rep,clp):inter,data=mdata,offset=-1,mirror=b3fix ? 0 : mirror,pixaspect=PAR,matchfields=false,subpixel=2) | |
| 449 | SelectEvery(2,0) | |
| 450 | ||
| 451 | stabclip=last | |
| 452 | FalPosclip = !defined(FalPosclip) && FixFalPos && mirror==15 && b3fix ? clp : FalPosclip | |
| 453 | ||
| 454 | # from depansafe() function | |
| 455 | b3fix && defined(FalPosclip) ? FalPosclip : FixFalPos ? eval("""
| |
| 456 | thick = b3fix ? 3.0 : 2.0 # removing >2px wide borders | |
| 457 | cropx = dxmax*2 | |
| 458 | ratiox = "YPlaneMax("+string(ceil(99-thick/cropx*100))+")"
| |
| 459 | ||
| 460 | crop(0,0,0,cropx).conditionalfilter(last,clp,ratiox,">","0") | |
| 461 | crop(0,height-cropx,0,0).conditionalfilter(last,clp,ratiox,">","0") | |
| 462 | crop(0,0,cropx,0).conditionalfilter(last,clp,ratiox,">","0") | |
| 463 | crop(width-cropx,0,0,0).conditionalfilter(last,clp,ratiox,">","0")""") : last | |
| 464 | ||
| 465 | subsampl = b3fix ? Findstr(VersionString(), "AviSynth+") != 0 && Findstr(VersionString(), "r1576") == 0 ? !(clp.is444() || clp.isy()) : VersionNumber() < 2.60 ? clp.isyv12() : !(clp.isyv24() || clp.isy8()) : nop() | |
| 466 | ||
| 467 | b3fix ? stabclip.FillBorders(pad=subsampl && mirror!=15 ? 1 : 0,subsample=subsampl,FixFalPos=defined(FalPosclip) || FixFalPos ? last : Undefined, mirror=mirror==15,PAR=PAR) : last | |
| 468 | ||
| 469 | UVfix ? eval("""
| |
| 470 | lumaf=last | |
| 471 | ScriptClip ("
| |
| 472 | blue=round((AverageChromaU(clp) - AverageChromaU()) * 256.0) | |
| 473 | red=round((AverageChromaV(clp) - AverageChromaV()) * 256.0) | |
| 474 | ||
| 475 | Dither_convert_8_to_16() | |
| 476 | SmoothTweak16(saturation=1.0,hue1=min(384,blue),hue2=min(384,red),HQ=true) | |
| 477 | DitherPost(stacked=true,prot=false,mode=6,y=1,slice=false) | |
| 478 | ", args="clp" ) | |
| 479 | Mergeluma(lumaf) | |
| 480 | """) : last | |
| 481 | ||
| 482 | } | |
| 483 | ||
| 484 | ||
| 485 | ######### HELPER FUNCTIONS ######### | |
| 486 | ||
| 487 | # Helper function for FindBlackBorders() from FFMS2.avsi | |
| 488 | function FFFormatTime_stabi(int ms) {
| |
| 489 | s = ms / 1000 | |
| 490 | ms = ms % 1000 | |
| 491 | m = s / 60 | |
| 492 | s = s % 60 | |
| 493 | h = m / 60 | |
| 494 | m = m % 60 | |
| 495 | return string(h) + ":" + string(m,"%02.0f") + ":" + string(s,"%02.0f") + "." + string(ms,"%03.0f")} |