View Full Version : InpaintDelogo - advanced logo removal & hardcoded subtitles extraction
VoodooFX
17th August 2021, 05:58
...Finally, the expanded/swelled frames are AND'ed together for the diagnostic shown DETECT frame.
This detect frame therefore contains white pixels where text/halo were valid colors, and because of the AND'ed swellings, it also shows where both
text and halo swelled 'into' each other ie where they were adjacent to each other (allows to ignore randomly positioned pixels which just happen to be the
sort after colors).
The FindSub routine just checks the scan area using RT_YInRangeLocate() to see if there is a suspect area of white(good) pixels of sufficient 'bulk'.
Are you going to finish it? I think DETECT frames expanded ~3-5 times should be good as a final mask to remove subs in this case (where there is a clear outline around subs), probably such mask wouldn't look good if outline is only at right/bottom or outline is thin.
There could be potential of somehow merging DynMask=3 method from InpaintDelogo with DetectSub_MI's DETECT'ed area cords/range.
thinkin' bout it.
That would be great, I could provide samples for tests if needed.
StainlessS
17th August 2021, 14:58
Are you going to finish it?
Hope so, however bit pushed for time.
Here what I've done so far, [this is really for testing purposes], not sure I've dont it correctly.
###
# DSub_MI, Based on DetectSub.MI.avsi":- https://forum.doom9.org/showthread.php?p=1782036#post1782036
###
# req RT_Stats v2.00 Beta 02, GScript, Grunt, CallCmd, MaskTools v2.0.
# RT_Stats v2.00 Beta2 first posted here:- http://forum.doom9.org/showthread.php?p=1782028#post1782028
#
###
Function Hit_Marker(int W, Int H, Bool "Hit", Int "BW", Int "BH",Int "PW",Int "PH",Bool "YV12",Bool "Mod2") {
Hit=Default(hit,False) BW=Max(Default(BW,W/8),1) BH=Max(Default(BH,H/8),1)
BW2=(W>4)?BW*2:BW BH2=(H>4)?BH*2:BH
PW=Max(Default(PW,BW/8),1) PH=Max(Default(PH,BH/8),1) is26 = VersionNumber>=2.6
YV12=(!is26) ? True : Default(YV12,False) Mod2=Default(Mod2,True) Mod = (YV12||Mod2) ? 2 : 1
CanvasW=(W+Mod-1)/Mod*Mod CanvasH=(H+Mod-1)/Mod*Mod
Rpn= RT_String("x %d < x %d >= | y %d < | y %d >= | x %d < x %d >= | & y %d < y %d >= | & ",BW,W-BW,BH,H-BH,BW2,W-BW2,BH2,H-BH2)
Rpn= (Hit) ? Rpn + RT_String("x %d < x %d >= | y %d < | y %d >= | | ",PW,W-PW,PH,H-PH) : Rpn
Rpn= Rpn + RT_String("x %d < y %d < & & 255 0 ?",W,H)
Blankclip(width=CanvasW,height=CanvasH,Length=1,pixel_type=YV12?"YV12":"Y8").Killaudio
return mt_lutspa(relative = false,yExpr=Rpn, chroma = "-128")
}
Function ChrIsStar(String S) {return RT_Ord(S)==42}
Function ChrIsNul(String S) {return RT_Ord(S)== 0} # End of String
Function ChrIsHash(String S) {return RT_Ord(S)==35} # #
Function ChrIsDigit(String S) {C=RT_Ord(S) return C>=48&&C<=57}
Function ChrEatWhite(String S) {i=1 C=RT_Ord(S,i) While(C==32||C>=8&&C<=13) {i=i+1 C=RT_Ord(S,i)} return i>1?MidStr(S,i):S}
Function ChrEatDigits(String S) {i=1 C=RT_Ord(S,i) While(C>=48&&C<=57) {i=i+1 C=RT_Ord(S,i)} return i>1?MidStr(S,i):S}
Function CnvStr(clip c) {
Return c.IsRGB24?"ConvertToRGB24":c.IsRGB32?"ConvertToRGB32":c.IsYUY2?"ConvertToYUY2":c.IsYV12?"ConvertToYV12"
\ :c.IsYV16?"c.ConvertToYV16":c.IsYV24?"c.ConvertToYV24":c.IsY8?"c.ConvertToY8":""
}
Function DetSub_Override(string Override,string DB) {
if(ChrIsStar(Override)) {OverRide=MidStr(OverRide,2)
} else {
Assert(Exist(OverRide),RT_String("%sOverRide File does not exist",myName))
OverRide=RT_ReadTxtFromFile(Override)
}
LINES=RT_TxtQueryLines(OverRide)
for(i=0,LINES-1) {
SS=ChrEatWhite(RT_TxtGetLine(Override,i))
S=SS
if(!ChrIsNul(S) && !ChrIsHash(S)) {
if(ChrIsDigit(S)) {
StartF = RT_NumberValue(S)
EndF=StartF
S=ChrEatWhite(ChrEatDigits(S))
if(ChrIsComma(S)){S=MidStr(S,2) S=ChrEatWhite(S)}
if(ChrIsDigit(S)) {
EndF=RT_NumberValue(S)
S=ChrEatDigits(S)
if(EndF==0) { EndF=FrameCount-1}
Assert(StartF<=EndF,RT_String("%sError StartFrame(%d) > EndFrame(%d)",myName,StartF,EndF))
}
for(n=StartF,EndF) {RT_DBaseSetField(DB,n,0,3)}
}
S=ChrEatWhite(S)
if(ChrIsHash(S)) {S=""}
Assert(ChrIsNul(S),RT_String("%s *** NON-PARSE *** Error in Override Line %d\n'%s'",myName,i+1,SS))
}
}
return 0
}
Function DetSub(clip c,string "Override",
\ Int "X",Int "Y",Int "W",Int "H",
\ Int "Text_w",Int "Text_h",Int "TextCol",Int "TextTol",
\ Int "Halo_L",Int "Halo_T",Int "Halo_R",Int "Halo_B",Int "HaloCol",Int "HaloTol",
\ Int "THysCNT",Int "HHysCNT",Int "DHysCNT",
\ Int "Baffle_w",Int "Baffle_h",Float "Thresh_w",Float "Thresh_h",Bool "ReScan",
\ String "DB") {
myName="DetSub: "
c
IsPlus=FindStr(VersionString,"AviSynth+")!=0
Assert(RT_FunctionExist("GScriptClip"),myName+"Essential GRunT plugin installed, http://forum.doom9.org/showthread.php?t=139337")
Assert(IsPlus||RT_FunctionExist("GScript"),myName+"Essential GScript plugin installed, http://forum.doom9.org/showthread.php?t=147846")
Assert(RT_FunctionExist("CallCmd"),myName+"Essential CallCmd plugin installed, http://forum.doom9.org/showthread.php?t=166063")
Assert(RT_FunctionExist("mt_expand"),myName+"Essential MaskTools v2 plugin installed, http://forum.doom9.org/showthread.php?t=98985")
Override=Default(OverRide,"")
X=Default(X,10) Y=Default(Y,Height-120) W=Default(W,-10) H=Default(H,-8)
W=(W<=0)?Width-X+W:W H=(H<=0)?Height-Y+H:H
Text_w=Default(Text_w,3) Text_h=Default(Text_h,3)
TextCol=Default(TextCol,$FCFCFC) TextTol=Default(TextTol,$030303)
Halo_L=Default(Halo_L,3) Halo_T=Default(Halo_T,4) Halo_R=Default(Halo_R,5) Halo_B=Default(Halo_B,6)
HaloCol=Default(HaloCol,$030303) HaloTol=Default(HaloTol,$030303)
Text_Tol_B=BitAnd(TextTol,$FF) TextTol=BitRShiftU(TextTol,8) Text_Tol_G=BitAnd(TextTol,$FF) TextTol=BitRShiftU(TextTol,8) Text_Tol_R=BitAnd(TextTol,$FF)
Halo_Tol_B=BitAnd(HaloTol,$FF) HaloTol=BitRShiftU(HaloTol,8) Halo_Tol_G=BitAnd(HaloTol,$FF) TextTol=BitRShiftU(HaloTol,8) Halo_Tol_R=BitAnd(HaloTol,$FF)
THysCNT=Default(THysCNT,0) HHysCNT=Default(HHysCNT,0) DHysCNT=Default(DHysCNT,0)
Baffle_w=Default(Baffle_w,0) Baffle_h=Default(Baffle_h,0)
Thresh_w=Default(Thresh_w,0.0) Thresh_h=Default(Thresh_h,0.0) ReScan=Default(ReScan,False)
DB=Default(DB,"") UserDB=(DB!="")
DB=(!UserDB) ? "~"+RT_LocalTimeString+".DB" : DB
DB=RT_GetFullPathName(DB)
VW=Width VH=Height
CNVS=CnvStr(c)
Assert(CNVS!="",RT_String("%sRGB24, RGB32, YUY2, YV12, YV16, YV24,and Y8 Only",myName))
/* Fields
0) Status, 0=Unknown (unvisited), 1=Sub, 2 Not sub, 3 User OverRide
1) X
2) Y
3) W
4) H
*/
RT_DBaseAlloc(DB,FrameCount,"iiiii")
RT_DBaseSetID(DB,0,c.FrameCount,c.FrameRate,VW,VH,X,Y,W,H)
if(OverRide!="") {DetSet_Override(Override,DB)}
LogoC=crop(0,Y,0,H) # Full width logo area
rgb=LogoC.ConvertToRGB32.Crop(X,0,W,0).ResetMask # Valid Logo Area only, Reset Alpha to White
TxtInRng=rgb.ColorKeyMask(TextCol,Text_Tol_R,Text_Tol_G,Text_Tol_B).ShowAlpha(Pixel_Type="Y8") # Set TextCol in range pixels to black
HalInRng=rgb.ColorKeyMask(HaloCol,Halo_Tol_R,Halo_Tol_G,Halo_Tol_B).ShowAlpha(Pixel_Type="Y8") # Set HaloCol in range pixels to black
TxtInRng=TxtInRng.Invert # Text In-range pixels white, else black
HalInRng=HalInRng.Invert # Halo In-range pixels white, else black
Text2 = TxtInRng
# Swell Text by Halo thickness [Including Halo Drop Shadow]
HALO_MAX=Max(HALO_L,HALO_T,HALO_R,HALO_B)
for(i=1,HALO_MAX) {
HorFlg=(i<=HALO_L?1:0)+(i<=HALO_R?2:0) VerFlg=(i<=HALO_T?1:0)+(i<=HALO_B?2:0)
if(HorFlg+VerFlg==6) {Text2=Text2.Mt_Expand(mode="square")
} else {
if(HorFlg==3) {Text2=Text2.Mt_Expand(mode="horizontal") HorFlg=0}
if(VerFlg==3) {Text2=Text2.Mt_Expand(mode="vertical") VerFlg=0}
if(HorFlg!=0) {Text2=Text2.Mt_Expand(mode=HorFlg==1? " 1 0 0 0" : " -1 0 0 0")}
if(VerFlg!=0) {Text2=Text2.Mt_Expand(mode=VerFlg==1? " 0 1 0 0" : " 0 -1 0 0")}
}
}
Text3=Text2
Text4=Text3
# reduce Text strays a little
if(THysCNT>0) {
For(i=1,THysCNT) {Text3=Text3.MT_Inpand(mode="both")}
Text4=Text3.MT_Hysteresis(Text2)
}
Halo2 = HalInRng
# Swell Halo by Text thickness
TEXT_MAX=Max(TEXT_W,TEXT_H)
for(i=1,TEXT_MAX) {
HorFlg=(i<=TEXT_W?1:0) VerFlg=(i<=TEXT_H?1:0)
if(HorFlg+VerFlg==2) {Halo2=Halo2.Mt_Expand(mode="square")
} else {
if(HorFlg!=0) {Halo2=Halo2.Mt_Expand(mode="horizontal") }
if(VerFlg!=0) {Halo2=Halo2.Mt_Expand(mode="vertical") }
}
}
Halo3=Halo2
Halo4=Halo3
# reduce Halo strays a little
if(HHysCNT>0) {
For(i=1,HHysCNT) {Halo3=Halo3.MT_Inpand(mode="both")}
Halo4=Halo3.MT_Hysteresis(Halo2)
}
Det1=Mt_Logic(Text4,Halo4,"and")
Det2=Det1
Det3=Det2
if(DHysCNT>0) {
For(i=1,DHysCNT) {Det2=Det2.MT_Inpand(mode="both")}
Det3=Det2.MT_Hysteresis(Det1)
}
SSS="""
n = current_frame
Status = RT_DBaseGetField(DB,n,0)
if(Status==0) { # Unknown Status
Status = (Det3.RT_YInRangeLocate(Baffle_w=Baffle_w,Baffle_h=Baffle_h,lo=255,hi=255,Thresh_w=Thresh_w,Thresh_h=Thresh_h,ReScan=ReScan)) ? 1 : 2
if(Status==1) { RT_DBaseSet(DB,n,Status,X+YIRL_X,Y+YIRL_Y,YIRL_W,YIRL_H) }
else { RT_DBaseSetField(DB,n,0,Status) }
}
if(Status==1) {
SUB_X = RT_DBaseGetField(DB,n,1) SUB_Y = RT_DBaseGetField(DB,n,2)
SUB_W = RT_DBaseGetField(DB,n,3) SUB_H = RT_DBaseGetField(DB,n,4)
SUB_W=(SUB_W+1)/2*2 SUB_H=(SUB_H+1)/2*2
OverLay(Last.BlankClip(color=$FF00FF,width=SUB_W,height=SUB_H),x=SUB_X,y=SUB_Y-Y,opacity=0.25)
}
Return Last
"""
LogoC=LogoC.GScriptClip(SSS, local=true, args="Det3,DB,X,Y,W,H,Baffle_w,Baffle_h,Thresh_w,Thresh_h,ReScan")
Mrk=LogoC.BlankClip(Length=1,Width=W,Height=H,Color=$FF00FF)
Msk=Hit_Marker(W,H,True,8,8,1,1)
LogoC=LogoC.Overlay(Mrk,Mask=Msk,x=X,y=0,Opacity=0.5)
LogoC=LogoC.AddBorders(0,22,0,0,$F0F080).Subtitle("LogoArea (Full width)",y=2)
Det3=Det3.Eval(CnvS).AddBorders(X,0,Width-X-W,0).AddBorders(0,22,0,0,$F0F080).Subtitle("Detect3 : DETECT FINAL, [Detect2.MT_Hysteresis(Detect1)]",y=2)
Det2=Det2.Eval(CnvS).AddBorders(X,0,Width-X-W,0).AddBorders(0,22,0,0,$F0F080).Subtitle("Detect2 : [Detect1 Inpand by DHysCNT, reduce stray pixels]",y=2)
Det1=Det1.Eval(CnvS).AddBorders(X,0,Width-X-W,0).AddBorders(0,22,0,0,$F0F080).Subtitle("Detect1 : Text4 AND Halo4",y=2)
Text4=Text4.Eval(CnvS).AddBorders(X,0,Width-X-W,0).AddBorders(0,22,0,0,$F0F080).Subtitle("Text4 : [Text3.MT_Hysteresis(Text2)]",y=2)
Text3=Text3.Eval(CnvS).AddBorders(X,0,Width-X-W,0).AddBorders(0,22,0,0,$F0F080).Subtitle("Text3 : [Text2 Inpand by THysCNT, reduce stray pixels]",y=2)
Text2=Text2.Eval(CnvS).AddBorders(X,0,Width-X-W,0).AddBorders(0,22,0,0,$F0F080).Subtitle("Text2 : [Swell Text1 by Halo thickness]",y=2)
TxtInRng=TxtInRng.Eval(CnvS).AddBorders(X,0,Width-X-W,0).AddBorders(0,22,0,0,$F0F080).Subtitle("Text1 : [TextInRange via TEXTCOL & TEXTTOL]",y=2)
Halo4=Halo4.Eval(CnvS).AddBorders(X,0,Width-X-W,0).AddBorders(0,22,0,0,$F0F080).Subtitle("Halo4 : [Halo3.MT_Hysteresis(Halo2)]",y=2)
Halo3=Halo3.Eval(CnvS).AddBorders(X,0,Width-X-W,0).AddBorders(0,22,0,0,$F0F080).Subtitle("Halo3 : [Halo2 Inpand by HHysCNT, reduce stray pixels]",y=2)
Halo2=Halo2.Eval(CnvS).AddBorders(X,0,Width-X-W,0).AddBorders(0,22,0,0,$F0F080).Subtitle("Halo2 : [Swell Halo1 by Text thickness]",y=2)
HalInRng=HalInRng.Eval(CnvS).AddBorders(X,0,Width-X-W,0).AddBorders(0,22,0,0,$F0F080).Subtitle("Halo1 : [HaloInRange via HALOCOL & HALOTOL]",y=2)
StackVertical(LogoC,Det3,Det2,Det1,Text4,Halo4,Text3,Halo3,Text2,Halo2,TxtInRng,HalInRng)
Return Last
}
############################
HARDFN="hard sub - 01 WEBdlRip 720p, 23.976.mkv.AVI" # converted to AVI via ffmpeg
HARDFN=HARDFN.RT_GetFullPathName
AVISource(HARDFN)
killaudio
# Subtitle coords
X = 40
Y = Height-140
W = -X
H = -8
# Text, Halo color
TEXTCOL = $EDEEF4 # R 230->245[$ED:$8] ::: G 230->247 [$EE:$9]::: B 233->255 [$F4:$B]
TEXTTOL = $08090B # Text RGB channels can vary by as much as TEXTTOL, eg TEXTTOL $010203 is Red Tolerance $01, Grn Tolerance $02, Blue Tolerance $03.
HALOCOL = $10313D # R 7->26[$10:$A] ::: G 37->62[$31:$D] ::: B 55->68[$3D:7]
HALOTOL = $0A0D07 # Halo RGB channels can vary by as much as HALOTOL, eg HALOTOL $010203 is Red Tolerance $01, Grn Tolerance $02, Blue Tolerance $03.
# Text pixel width,height
TEXT_W = 4 # minimum text pixel 'thickness' Horizontal (horizontal thickness of vertical strokes)
TEXT_H = 4 # minimum text pixel 'thickness' Vertical (vertical thickness of horizontal strokes)
# Halo pixel width,height
HALO_L = 2 # Left Halo width in pixels
HALO_R = 2 # Right Halo width in pixels
HALO_T = 2 # Top Halo height in pixels
HALO_B = 2 # Bot Halo height in pixels
# Mt_Hysteresis (reduce stray pixels):- http://forum.doom9.org/showthread.php?p=1780589#post1780589
THYSCNT = 2 # Text Mask Mt_Hysteresis inpand count (prior to mt_hysteresis), Dont Mt_Hysteresis if 0
HHYSCNT = 2 # Halo Mask Mt_Hysteresis inpand count (prior to mt_hysteresis), Dont Mt_Hysteresis if 0
DHYSCNT = 2 # Detect clip Mt_Hysteresis inpand count (prior to mt_hysteresis), Dont Mt_Hysteresis if 0
######
THRESH_W = -(Text_w+(Halo_L+Halo_R)/2) # -ve, specifies Abs(Pixel width) {Else +ve = Percent width 0.0->100.0%) {0.0=any pixel in range)
THRESH_H = -(Text_h+(Halo_T+Halo_B)/2) # -ve, specifies Abs(Pixel Height) {Else +ve = Percent width 0.0->100.0%) {0.0=any pixel in range)
#
BAFFLE_W = Text_w+Halo_L+Halo_R # Width of outermost Vertical edges.
BAFFLE_H = Text_h+Halo_T+Halo_B # Height of outermost Horizontal edges.
#
RESCAN = true # Switching on might get rid of the odd stray pixel (most likely not).
#
DB = "MyDB.DB" # If Named, then DB not deleted on exit. (Maybe DBase used by other scripts later)
OverRide = "" # ""=Not used, "*..." multiline string of ranges, "OverRide.txt"=Text file of ranges.
#OverRide="OverRide.txt"
/*
OverRide="""* # <<=== NOTE Asterisk (STAR). Example String OverRide (1st char is '*'). Ranges OverRidden to NO SUBTITLE
10 100 # A comment, SPACE or COMMA Separator
200,300 # 200 to 300. NOTE -ve frame count NOT supported.
3000,0 # 3000 till last frame.
"""
*/
DetSub(Last,Override=Override,
\ x=X,y=Y,w=W,h=H,
\ Text_w=TEXT_W,Text_h=TEXT_H,TextCol=TEXTCOL,TextTol=TEXTTOL,
\ Halo_L=HALO_L,Halo_T=HALO_T,Halo_R=HALO_R,Halo_B=HALO_B,HaloCol=HALOCOL,HaloTol=HALOTOL,
\ THysCnt=THYSCNT,HHysCnt=HHYSCNT,DHysCnt=DHYSCNT,
\ Baffle_w=BAFFLE_W,Baffle_h=BAFFLE_H,Thresh_w=THRESH_W,Thresh_h=THRESH_H,ReScan=RESCAN,
\ db=DB)
In VDub2, can set to 100% and using mouse scroll image in window. [EDIT: If eg monitor Full HD or less]
https://i.postimg.cc/wRRTbdTZ/Det-Sub-00.png (https://postimg.cc/wRRTbdTZ)
Something to play with. [EDIT: Avs+ only]
EDIT: TEXTTOL and HALOTOL instead of TEXTVAR and HALOVAR, separate tolerance per channel.
Script & image updated.
VoodooFX
18th August 2021, 14:50
Here what I've done so far
Thanks, the script looks more readable now, maybe I'll be able to steal some ideas now. :)
Is there real advantage of TOL over VAR (I kinda liked a simple setting)?
StainlessS
18th August 2021, 22:12
EDIT: Here are the supplementary script functions.
###
# DSub_MI, Based on DetectSub.MI.avsi":- https://forum.doom9.org/showthread.php?p=1782036#post1782036
###
# Req Avs+, RT_Stats v2.00 Beta 02, GScript, Grunt, CallCmd, MaskTools v2.0. RemoveGain or RGTools.
# RT_Stats v2.00 Beta2 first posted here:- http://forum.doom9.org/showthread.php?p=1782028#post1782028
#
###
Function Hit_Marker(int W, Int H, Bool "Hit", Int "BW", Int "BH",Int "PW",Int "PH",Bool "YV12",Bool "Mod2") {
/*
Req RT_Stats & mt_tools v2. http://forum.doom9.org/showthread.php?t=174527
Creates a marker mask WxH, for use with Overlay as Mask arg.
Intended for use via some kind of detector to show detection Hit, or Miss.
Returns Single frame clip with null audio, the marker top left hand side is aligned to 0,0.
Default return clip colorspace is Y8 for Avisynth v2.6 and above, or YV12 if defunct version.
Args:-
W,H, Size of marker mask (can be odd), return clip dimensions are rounded up to next multiple of 2 if YV12 return clip.
Bare minimum usable size is 4x4 with defaulted BW, BH, PW, PH.
HIT, Default False, returns marker as angled corners (Thickness set by BW and BH).
True, returns marker as angled corners + outer perimeter where perimeter thickness set by PW and PH.
BW, Default Max(W/8,1). Horizontal thickness of the marker corners.
BH, Default Max(H/8,1). Vertical thickness of the marker corners.
PW, Default Max(BW/8,1). Horizontal thickness of the vertical perimeter.
PH, Default Max(BH/8,1). Vertical thickness of the horizontal perimeter.
YV12, Default False if Avisynth version 2.6 or greater(Y8), else true(YV12). Selects return clip colorspace.
Mod2, Default True. If Y8 then round odd dimensions up modulo 2 (the mask is still WxH).
Example:-
Import("Hit_Marker.avs")
WW=128 HH=WW HIT=True
BlankClip
Yell=Last.BlankClip(Width=WW,Height=HH,Length=1,Color=$FFFF00)
Return OverLay(Yell,x=(Width-WW)/2,y=(Height-HH)/2,mask=Hit_Marker(WW,HH,HIT))
*/
Hit=Default(hit,False) BW=Max(Default(BW,W/8),1) BH=Max(Default(BH,H/8),1)
BW2=(W>4)?BW*2:BW BH2=(H>4)?BH*2:BH
PW=Max(Default(PW,BW/8),1) PH=Max(Default(PH,BH/8),1) is26 = VersionNumber>=2.6
YV12=(!is26) ? True : Default(YV12,False) Mod2=Default(Mod2,True) Mod = (YV12||Mod2) ? 2 : 1
CanvasW=(W+Mod-1)/Mod*Mod CanvasH=(H+Mod-1)/Mod*Mod
Rpn= RT_String("x %d < x %d >= | y %d < | y %d >= | x %d < x %d >= | & y %d < y %d >= | & ",BW,W-BW,BH,H-BH,BW2,W-BW2,BH2,H-BH2)
Rpn= (Hit) ? Rpn + RT_String("x %d < x %d >= | y %d < | y %d >= | | ",PW,W-PW,PH,H-PH) : Rpn
Rpn= Rpn + RT_String("x %d < y %d < & & 255 0 ?",W,H)
Blankclip(width=CanvasW,height=CanvasH,Length=1,pixel_type=YV12?"YV12":"Y8").Killaudio
return mt_lutspa(relative = false,yExpr=Rpn, chroma = "-128")
}
Function ChrIsStar(String S) {return RT_Ord(S)==42}
Function ChrIsNul(String S) {return RT_Ord(S)== 0} # End of String
Function ChrIsHash(String S) {return RT_Ord(S)==35} # #
Function ChrIsDigit(String S) {C=RT_Ord(S) return C>=48&&C<=57}
Function ChrEatWhite(String S) {i=1 C=RT_Ord(S,i) While(C==32||C>=8&&C<=13) {i=i+1 C=RT_Ord(S,i)} return i>1?MidStr(S,i):S}
Function ChrEatDigits(String S) {i=1 C=RT_Ord(S,i) While(C>=48&&C<=57) {i=i+1 C=RT_Ord(S,i)} return i>1?MidStr(S,i):S}
Function DetSub_Override(string Override,string DB) {
if(ChrIsStar(Override)) {OverRide=MidStr(OverRide,2)
} else {
Assert(Exist(OverRide),RT_String("%sOverRide File does not exist",myName))
OverRide=RT_ReadTxtFromFile(Override)
}
LINES=RT_TxtQueryLines(OverRide)
for(i=0,LINES-1) {
SS=ChrEatWhite(RT_TxtGetLine(Override,i))
S=SS
if(!ChrIsNul(S) && !ChrIsHash(S)) {
if(ChrIsDigit(S)) {
StartF = RT_NumberValue(S)
EndF=StartF
S=ChrEatWhite(ChrEatDigits(S))
if(ChrIsComma(S)){S=MidStr(S,2) S=ChrEatWhite(S)}
if(ChrIsDigit(S)) {
EndF=RT_NumberValue(S)
S=ChrEatDigits(S)
if(EndF==0) { EndF=FrameCount-1}
Assert(StartF<=EndF,RT_String("%sError StartFrame(%d) > EndFrame(%d)",myName,StartF,EndF))
}
for(n=StartF,EndF) {RT_DBaseSetField(DB,n,0,3)}
}
S=ChrEatWhite(S)
if(ChrIsHash(S)) {S=""}
Assert(ChrIsNul(S),RT_String("%s *** NON-PARSE *** Error in Override Line %d\n'%s'",myName,i+1,SS))
}
}
return 0
}
StainlessS
19th August 2021, 14:37
/*
function Prototype:
DetSub(clip c,Int "X"=-10,Int "Y"=Height-120,Int "W"=-10,Int "H"=-8,
\ Int "Text_w"=3,Int "Text_h"=3,Int "TextCol"=$FCFCFC,Int "TextTol"=$030303,
\ Int "Halo_L"=3,Int "Halo_T"=3,Int "Halo_R"=3,Int "Halo_B"=3,Int "HaloCol"=$030303,Int "HaloTol"=$030303,
\ int "BackColor"=$00FF00,Int "ClipIx"=0,string "Matrix"="rec601",String "DB"="",string "Override"="")
Matrix, for src conversion to RGB32.
ClipIx select which sub clip to return, [DetSub, output always RGB32 with Alpha = known state $FF (opaque)]
0) = Full detect Info clip, same width as input clip.
1->15) = Other sub clip, dimensions X,Y,W,H.
*/
Function DetSub(clip c,Int "X",Int "Y",Int "W",Int "H",
\ Int "Text_w",Int "Text_h",Int "TextCol",Int "TextTol",
\ Int "Halo_L",Int "Halo_T",Int "Halo_R",Int "Halo_B",Int "HaloCol",Int "HaloTol",
\ int "BackColor",Int "ClipIx",string "Matrix",String "DB",string "Override") {
myName="DetSub: "
c
IsPlus=FindStr(VersionString,"AviSynth+")!=0
Assert(RT_FunctionExist("GScriptClip"),myName+"Essential GRunT plugin installed, http://forum.doom9.org/showthread.php?t=139337")
Assert(IsPlus||RT_FunctionExist("GScript"),myName+"Essential GScript plugin installed, http://forum.doom9.org/showthread.php?t=147846")
Assert(RT_FunctionExist("CallCmd"),myName+"Essential CallCmd plugin installed, http://forum.doom9.org/showthread.php?t=166063")
Assert(RT_FunctionExist("mt_expand"),myName+"Essential MaskTools v2 plugin installed, http://forum.doom9.org/showthread.php?t=98985")
Assert(RT_FunctionExist("RemoveGrain"),myName+"Essential RemoveGrain installed.")
X=Default(X,10) Y=Default(Y,Height-120) W=Default(W,-10) H=Default(H,-8)
W=(W<=0)?Width-X+W:W H=(H<=0)?Height-Y+H:H
Text_w=Default(Text_w,3) Text_h=Default(Text_h,3)
TextCol=Default(TextCol,$FCFCFC) TextTol=Default(TextTol,$030303)
Halo_L=Default(Halo_L,3) Halo_T=Default(Halo_T,3) Halo_R=Default(Halo_R,3) Halo_B=Default(Halo_B,3)
HaloCol=Default(HaloCol,$030303) HaloTol=Default(HaloTol,$030303)
Text_Tol_B=BitAnd(TextTol,$FF) TextTol=BitRShiftU(TextTol,8) Text_Tol_G=BitAnd(TextTol,$FF) TextTol=BitRShiftU(TextTol,8) Text_Tol_R=BitAnd(TextTol,$FF)
Halo_Tol_B=BitAnd(HaloTol,$FF) HaloTol=BitRShiftU(HaloTol,8) Halo_Tol_G=BitAnd(HaloTol,$FF) HaloTol=BitRShiftU(HaloTol,8) Halo_Tol_R=BitAnd(HaloTol,$FF)
BackColor=Default(BackColor,$00FF00) # Background for Det4, DETECT FINAL
Matrix = Default(Matrix,"Rec601")
Ix = Default(ClipIx,0)
Assert(0 <= Ix <=15,MyName+"0 <= ClipIx <=15")
DB=Default(DB,"") UserDB=(DB!="")
DB=(!UserDB) ? "~"+RT_LocalTimeString+".DB" : DB
DB=RT_GetFullPathName(DB)
Override=Default(OverRide,"")
###
# Mt_Hysteresis (reduce stray pixels):- http://forum.doom9.org/showthread.php?p=1780589#post1780589
# Text Mask Mt_Hysteresis inpand count (prior to mt_hysteresis), Dont Mt_Hysteresis if 0
THYSCNT = Min(TEXT_W,TEXT_H)/2 - 1
# Halo Mask Mt_Hysteresis inpand count (prior to mt_hysteresis), Dont Mt_Hysteresis if 0
HHYSCNT = Min(HALO_L,HALO_R,HALO_T,HALO_B)/2 - 1
# Detect clip Mt_Hysteresis inpand count (prior to mt_hysteresis), Dont Mt_Hysteresis if 0
DHYSCNT = Min(TEXT_W+HALO_L+HALO_R,TEXT_H+HALO_T+HALO_B)/2 - 1
###### Subs detect position
THRESH_W = -(Text_w+(Halo_L+Halo_R)/2) # -ve, specifies Abs(Pixel width) {Else +ve = Percent width 0.0->100.0%) {0.0=any pixel in range)
THRESH_H = -(Text_h+(Halo_T+Halo_B)/2) # -ve, specifies Abs(Pixel Height) {Else +ve = Percent width 0.0->100.0%) {0.0=any pixel in range)
BAFFLE_W = Text_w+Halo_L+Halo_R # Width of outermost Vertical edges.
BAFFLE_H = Text_h+Halo_T+Halo_B # Height of outermost Horizontal edges.
RESCAN = true # Switching on might get rid of the odd stray pixel (most likely not).
###
VW=Width VH=Height
/* Fields
0) Status, 0=Unknown (unvisited), 1=Sub, 2 Not sub, 3 User OverRide
1) X # Coords relative the Subtitles X,Y,W,H Detect Area
2) Y
3) W
4) H
*/
RT_DBaseAlloc(DB,FrameCount,"iiiii")
RT_DBaseSetID(DB,0,c.FrameCount,c.FrameRate,VW,VH,X,Y,W,H)
if(OverRide!="") {DetSet_Override(Override,DB)}
FullWidSubC=ConvertToRGB32(matrix=Matrix).crop(0,Y,0,H) # Full width Subs area
Area=FullWidSubC.Crop(X,0,W,0).ResetMask # Valid Subs Area only, Reset Alpha to White
Text1=Area.ColorKeyMask(TextCol,Text_Tol_R,Text_Tol_G,Text_Tol_B).ShowAlpha(Pixel_Type="Y8") # Set TextCol in range pixels to black
Halo1=Area.ColorKeyMask(HaloCol,Halo_Tol_R,Halo_Tol_G,Halo_Tol_B).ShowAlpha(Pixel_Type="Y8") # Set HaloCol in range pixels to black
Text1=Text1.Invert # Text In-range pixels white, else black
Halo1=Halo1.Invert # Halo In-range pixels white, else black
Text2 = Text1.RemoveGrain(2)
Halo2 = Halo1.RemoveGrain(2)
Text3 = Text2
# Swell Text by Halo thickness [Including Halo Drop Shadow]
HALO_MAX=Max(HALO_L,HALO_T,HALO_R,HALO_B)
for(i=1,HALO_MAX) {
HorFlg=(i<=HALO_L?1:0)+(i<=HALO_R?2:0) VerFlg=(i<=HALO_T?1:0)+(i<=HALO_B?2:0)
if(HorFlg+VerFlg==6) {Text3=Text3.Mt_Expand(mode="square")
} else {
if(HorFlg==3) {Text3=Text3.Mt_Expand(mode="horizontal") HorFlg=0}
if(VerFlg==3) {Text3=Text3.Mt_Expand(mode="vertical") VerFlg=0}
if(HorFlg!=0) {Text3=Text3.Mt_Expand(mode=HorFlg==1? " 1 0 0 0" : " -1 0 0 0")} # ? Expand_Left : Expand_Right
if(VerFlg!=0) {Text3=Text3.Mt_Expand(mode=VerFlg==1? " 0 1 0 0" : " 0 -1 0 0")} # ? Expand_Up : Expand_Down
}
}
Text4=Text2
Text5=Text3
# reduce Text strays a little
if(THysCNT>0) {
For(i=1,THysCNT) {Text4=Text4.MT_Inpand(mode="both")}
Text5=Text4.MT_Hysteresis(Text3)
}
Halo3 = Halo2
# Swell Halo by Text thickness
TEXT_MAX=Max(TEXT_W,TEXT_H)
for(i=1,TEXT_MAX) {
HorFlg=(i<=TEXT_W?1:0) VerFlg=(i<=TEXT_H?1:0)
if(HorFlg+VerFlg==2) {Halo3=Halo3.Mt_Expand(mode="square")
} else {
if(HorFlg!=0) {Halo3=Halo3.Mt_Expand(mode="horizontal") }
if(VerFlg!=0) {Halo3=Halo3.Mt_Expand(mode="vertical") }
}
}
Halo4=Halo2
Halo5=Halo3
# reduce Halo strays a little
if(HHysCNT>0) {
For(i=1,HHysCNT) {Halo4=Halo4.MT_Inpand(mode="both")}
Halo5=Halo4.MT_Hysteresis(Halo3)
}
Detect1=Mt_Logic(Text5,Halo5,"and")
Detect2=Detect1
Detect3=Detect2
Ocr1=Text2
if(DHysCNT>0) {
For(i=1,DHysCNT) {Detect2=Detect2.MT_Inpand(mode="both")}
Detect3=Detect2.MT_Hysteresis(Detect1)
Ocr1=Detect2.MT_Hysteresis(Mt_Logic(Text2,Detect3,"and"))
}
SSS1="""
n = current_frame
Status = RT_DBaseGetField(DB,n,0)
if(Status==0) { # Unknown Status
Status = (Detect3.RT_YInRangeLocate(Baffle_w=Baffle_w,Baffle_h=Baffle_h,lo=255,hi=255,Thresh_w=Thresh_w,Thresh_h=Thresh_h,ReScan=ReScan)) ? 1 : 2
if(Status==1) {
RT_DBaseSet(DB,n,Status,YIRL_X,YIRL_Y,YIRL_W,YIRL_H)
}
RT_DBaseSetField(DB,n,0,Status)
}
Return Last
"""
FullWidSubC=FullWidSubC.GScriptClip(SSS1, local=true, args="Detect3,DB,Baffle_w,Baffle_h,Thresh_w,Thresh_h,ReScan",after_frame=true)
Grn=Detect3.BlankClip(Pixel_Type="RGB32",color=BackColor)
Ocr2=Grn.Overlay(Grn.BlankClip(color=$000000),Mask=Detect3.RemoveGrain(2)).Overlay(Ocr1,Mask=Ocr1)
SSS2="""
n = current_frame
Status = RT_DBaseGetField(DB,n,0)
if(Status==1) {
SUB_X = RT_DBaseGetField(DB,n,1) SUB_Y = RT_DBaseGetField(DB,n,2)
SUB_W = RT_DBaseGetField(DB,n,3) SUB_H = RT_DBaseGetField(DB,n,4)
OverLay(Last.BlankClip(color=$FF00FF,width=SUB_W,height=SUB_H),x=X+SUB_X,y=SUB_Y,opacity=0.25)
}
Return Last
"""
FullWidSubC=FullWidSubC.GScriptClip(SSS2, local=true, args="Detect3,DB,X",after_frame=true)
Mrk=FullWidSubC.BlankClip(Length=1,Width=W,Height=H,Color=$FFFF0000)
Msk=Hit_Marker(W,H,True,8,8,1,1)
FullWidSubC=FullWidSubC.Overlay(Mrk,Mask=Msk,x=X,y=0,Opacity=0.5)
FullWidSubC=FullWidSubC.AddBorders(0,22,0,0,$F0F080).Subtitle("0] SubsArea (Search area in Red, detected subs in purple)",y=2)
Ocr2_T=Ocr2.AddBorders(X,0,Width-X-W,0,BackColor).AddBorders(0,22,0,0,$F0F080).Subtitle("1] OCR2 : [GreenC.Overlay(Blackness.Mask=Detect3.RemoveGrain(2)).Overlay(Ocr1,Mask=Ocr1)]",y=2)
Ocr1_T=Ocr1.ConvertToRGB32.AddBorders(X,0,Width-X-W,0,$000000).AddBorders(0,22,0,0,$F0F080).Subtitle("2] OCR1 : [Detect2.MT_Hysteresis(Mt_Logic(Text2,Detect3,'and'))]",y=2)
Detect3_T=Detect3.ConvertToRGB32.AddBorders(X,0,Width-X-W,0,$000000).AddBorders(0,22,0,0,$F0F080).Subtitle("3] Detect3 : [Detect2.MT_Hysteresis(Detect1), reduce stray pixels]",y=2)
Detect2_T=Detect2.ConvertToRGB32.AddBorders(X,0,Width-X-W,0,$000000).AddBorders(0,22,0,0,$F0F080).Subtitle(String(DHysCNT,"4] Detect2 : [Detect1 Inpand by DHysCNT(%.0f))]"),y=2)
Detect1_T=Detect1.ConvertToRGB32.AddBorders(X,0,Width-X-W,0,$000000).AddBorders(0,22,0,0,$F0F080).Subtitle("5] Detect1 : Text5 AND Halo5",y=2)
Text5_T=Text5.ConvertToRGB32.AddBorders(X,0,Width-X-W,0,$000000).AddBorders(0,22,0,0,$F0F080).Subtitle("6] Text5 : [Text4.MT_Hysteresis(Text3), reduce stray pixels]",y=2)
Text4_T=Text4.ConvertToRGB32.AddBorders(X,0,Width-X-W,0,$000000).AddBorders(0,22,0,0,$F0F080).Subtitle(String(THysCNT,"7] Text4 : [Text2 Inpand by THysCNT(%.0f)]"),y=2)
Text3_T=Text3.ConvertToRGB32.AddBorders(X,0,Width-X-W,0,$000000).AddBorders(0,22,0,0,$F0F080).Subtitle("8] Text3 : [Swell Text2 by Halo thickness]",y=2)
Text2_T=Text2.ConvertToRGB32.AddBorders(X,0,Width-X-W,0,$000000).AddBorders(0,22,0,0,$F0F080).Subtitle("9] Text2 : [UnDot, Text1.RemoveGrain(2)]",y=2)
Text1_T=Text1.ConvertToRGB32.AddBorders(X,0,Width-X-W,0,$000000).AddBorders(0,22,0,0,$F0F080).Subtitle("10] Text1 : [TextInRange via TEXTCOL & TEXTTOL]",y=2)
Halo5_T=Halo5.ConvertToRGB32.AddBorders(X,0,Width-X-W,0,$000000).AddBorders(0,22,0,0,$F0F080).Subtitle("11] Halo5 : [Halo4.MT_Hysteresis(Halo3), reduce stray pixels]",y=2)
Halo4_T=Halo4.ConvertToRGB32.AddBorders(X,0,Width-X-W,0,$000000).AddBorders(0,22,0,0,$F0F080).Subtitle(String(HHysCNT,"12] Halo4 : [Halo2 Inpand by HHysCNT(%.0f)]"),y=2)
Halo3_T=Halo3.ConvertToRGB32.AddBorders(X,0,Width-X-W,0,$000000).AddBorders(0,22,0,0,$F0F080).Subtitle("13] Halo3 : [Swell Halo2 by Text thickness]",y=2)
Halo2_T=Halo2.ConvertToRGB32.AddBorders(X,0,Width-X-W,0,$000000).AddBorders(0,22,0,0,$F0F080).Subtitle("14] Halo2 : [UnDot, Halo1.RemoveGrain(2)]",y=2)
Halo1_T=Halo1.ConvertToRGB32.AddBorders(X,0,Width-X-W,0,$000000).AddBorders(0,22,0,0,$F0F080).Subtitle("15] Halo1 : [HaloInRange via HALOCOL & HALOTOL]",y=2)
C0=StackVertical(FullWidSubC,Ocr2_T,Ocr1_T,Detect3_T,Detect2_T,Detect1_T,Text5_T,Text4_T,Text3_T,Text2_T,Text1_T,Halo5_T,Halo4_T,Halo3_T,Halo2_T,Halo1_T)
Ix==0?C0:Ix==1?Ocr2:Ix==2?Ocr1:Ix==3?Detect3:Ix==4?Detect2:Ix==5?Detect1:Ix==6?Text5:Ix==7?Text4:Ix==8?Text3:Ix==9?Text2:Ix==10?Text1:Ix==11?Halo5:Ix==12?Halo4:Ix==13?Halo3:Ix==14?Halo2:Halo1
Return ConvertToRGB32(Matrix="PC601").ResetMask # Alpha = $FF
}
############################
HARDFN="hard sub - 01 WEBdlRip 720p, 23.976.mkv.AVI" # converted to AVI via ffmpeg
HARDFN=HARDFN.RT_GetFullPathName
AVISource(HARDFN)
killaudio
# Subtitle coords
X = 58
Y = Height-118
W = -X
H = -16
# Text, Halo color
TEXTCOL = $F3F3F3
TEXTTOL = $0E0E0E
HALOCOL = $102A37
HALOTOL = $121212
# Setting all below too narrow[eg all 2 on test sample], produces odd effect where subs shown are actually mid way between text and halo, looks pretty good result but wrong.
# Also has peculiar property where seems to ignore random garbage in result adjacent to subs.
# Text pixel width,height
TEXT_W = 4 # minimum text pixel 'thickness' Horizontal (horizontal thickness of vertical strokes)
TEXT_H = 4 # minimum text pixel 'thickness' Vertical (vertical thickness of horizontal strokes)
# Halo pixel width,height
HALO_L = 3 # Left Halo width in pixels
HALO_R = 3 # Right Halo width in pixels
HALO_T = 3 # Top Halo height in pixels
HALO_B = 3 # Bot Halo height in pixels
BACKCOLOR = $00FF00 # Background for OCR2 bar
MATRIX = "Rec601"
ClipIx = 0 # 0=full metric clip[Full width, H height].
# 1 -> 15, other clip[Size X,Y,W,H]. always RGB32 result.
##################
DB = "MyDB.DB" # If Named, then DB not deleted on exit. (Maybe DBase used by other scripts later)
OverRide = "" # ""=Not used, "*..." multiline string of ranges, "OverRide.txt"=Text file of ranges.
#OverRide="OverRide.txt"
/*
OverRide="""* # <<=== NOTE Asterisk (STAR). Example String OverRide (1st char is '*'). Ranges OverRidden to NO SUBTITLE
10 100 # A comment, SPACE or COMMA Separator
200,300 # 200 to 300. NOTE -ve frame count NOT supported.
3000,0 # 3000 till last frame.
"""
*/
DetSub(x=X,y=Y,w=W,h=H,
\ Text_w=TEXT_W,Text_h=TEXT_H,TextCol=TEXTCOL,TextTol=TEXTTOL,
\ Halo_L=HALO_L,Halo_T=HALO_T,Halo_R=HALO_R,Halo_B=HALO_B,HaloCol=HALOCOL,HaloTol=HALOTOL,
\ backColor=BACKCOLOR,clipIx=ClipIx,Matrix=MATRIX,db=DB,Override=Override)
[Below, click twice]
https://i.postimg.cc/xkzd1GsX/Det-Sub-00.png (https://postimg.cc/xkzd1GsX)
EDIT: function Prototype in code block fixed.
VoodooFX
20th August 2021, 10:44
EDIT: significant re-write.
That is 7th version I've saved... :thanks:
This last one doesn't come with the user part.
So, "Detect3" is a final removal mask and "Detect4" is just to show what it covers? Could you add output for a final text mask without halo (that could be used for OCR purpose)?
Today I'll try to play with this sample too.
StainlessS
20th August 2021, 12:37
Not sure that I understand, outputs whole image set [ClipIx=0] or ClipIx= 1->14, 14 different masks, see either post #157 or #158 images, ClipIx in subclip title text,
eg for green bar [ClipIx=1]
1] Detect4 : DETECT FINAL, [FullWidSubC.BlankClip.Overlay(FullWidSubC.Mask=Detect3.RemoveGrain(2))]
DetSub(clip c,x="10",y="Height-120",w="-10",h="-8",
\ Text_w="3",Text_h="3",TextCol="$FCFCFC",TextTol=$030303",
\ Halo_L="3",Halo_T="3",Halo_R="3",Halo_B="3",HaloCol=HALOCOL,HaloTol=HALOTOL,
\ backColor="$00FF00",ClipIx="0",Matrix="rec601",db="",Override="")
This last one doesn't come with the user part.
What user part are we talkin' bout ?
EDIT:
Damn, I forgot that Green bar, non-green parts, text/halo is original pixels, I need to 'sorta' binarize before making green bar.
VoodooFX
20th August 2021, 13:49
I did few experiments with InpaintDelogo's DynMask=3, probably I can make it from 'not good' to 'usable' for this sample.
Not sure that I understand, outputs whole image set [ClipIx=0] or ClipIx= 1->14, 14 different masks, see either post #157 or #158 images...
First(FINAL) mask doesn't look like a mask...
Text2 looks like best to OCR, but it's not cleaned from all the dots, maybe another "UnDot" pass would help.
What user part are we talkin' bout?
About settings used. I found them at the top, before they were at the bottom.
StainlessS
20th August 2021, 15:57
About settings used. I found them at the top, before they were at the bottom.
OK, sorry.
First(FINAL) mask doesn't look like a mask...
Well the idea is to be able to extract from it both Text and Halo separately, with Green being the 'other guys'.
Posts #157 updated.
EDIT: So long as there is only ever 2 lines of subs, then would be better to reduce search area height to avoid encroaching noise above and below.
https://i.postimg.cc/RVQFXgzJ/Det-Sub-00-00.png (https://postimg.cc/pprRdYrP)
EDIT: Changed to [Images not updated]
# Subtitle coords
X = 58
Y = Height-118
W = -X
H = -16
EDIT: Images updated.
StainlessS
21st August 2021, 14:10
VX,
I have a rough idea how to select best Sub image from each detected Sub range.
Roughly, scan for each image in range, detecting image with least white text, excess white being crud round the edges, choose that image. (there can only be excess crud halo if excess crud text. <sort of>)
Well thats the idea, maybe work, maybe not.
I think we've polluted your important thread a bit with this, so I suggest you delete posts back to about #151, and I'll delete mine back to wherever you choose too.
The only other person posting back to #151, was kedautinh12 with a "thanks Stain" remark, perhaps he/she/they/them/it would be good enough to delete too.
I'll post new thread with current script and continue there if I have any success, will of course require multiple passes scripts.
[Which could possibly be automated into single script. <to export subs images for OCR, or InpaintDelogo>]
EDIT: Maybe also output frames file with Subs range and frameno of best subs in range.
EDIT:
(there can only be excess crud halo if excess crud text. <sort of>)
That aint quite right, but we'll try figure that out when we need to. [at least we know where we are tryin' to get to next]
EDIT: Logical AND of all white over the range [for text] and logical AND of all the black over range [for halo] - should maybe do it.
StainlessS
21st August 2021, 14:33
You can be 'it' if you want, your choice :)
Ever since I was a young child, I have known that I was different.
I'm not really a boy, [gosh! its so hard to say it out aloud], I'm really a helicopter.
There I said it, feel much better now, whizz whizz, whirr whirr.
EDIT: Was 'Cousin It' really an it ? with all that there hair is hard to tell. [Adams family was way ahead of 'its' time :) ]
VoodooFX
21st August 2021, 16:33
StainlessS,
I don't mind, it's related stuff and thread is quiet anyway, plus I don't like when stuff gets deleted, then I get nightmares that something important is gone. A separate thread for a separate script is the good idea, I'll find it. :)
PS:
Today I started injecting the yesterdays experiments to InpaintDelogo, almost done, but a friend crashed in with the firewater, so maybe tomorrow...
PS2:
If sub range can be detected then I would do on it something like:
function AvgAll2(clip c) { c.FrameCount() <= 1 ? c : Merge(AvgAll2(c.SelectEven()), AvgAll2(c.SelectOdd())) }
clp.AvgAll2().mt_binarize(threshold=254)
Roger, out.
StainlessS
21st August 2021, 19:21
Dang, had not realized but aint no clear frames between some subs, nuther spanner in the works.
Gone hunting spanners.
EDIT: Found some spanners.
DetSub - W.I.P:- https://forum.doom9.org/showthread.php?t=183148
StainlessS
22nd August 2021, 19:56
Function Prototype in post #157 fixed [totally messed it up :( ].
Function Prototype:
DetSub(clip c,Int "X"=-10,Int "Y"=Height-120,Int "W"=-10,Int "H"=-8,
\ Int "Text_w"=3,Int "Text_h"=3,Int "TextCol"=$FCFCFC,Int "TextTol"=$030303,
\ Int "Halo_L"=3,Int "Halo_T"=3,Int "Halo_R"=3,Int "Halo_B"=3,Int "HaloCol"=$030303,Int "HaloTol"=$030303,
\ int "BackColor"=$00FF00,Int "ClipIx"=0,string "Matrix"="rec601",String "DB"="",string "Override"="")
But suggest using updated scripts here [DetSub - W.I.P]:- https://forum.doom9.org/showthread.php?t=183148
VoodooFX
22nd August 2021, 22:33
New v1.31 tuned to deal much better with this last anime example:
# Version 1.31 : 2021/08/22. New: "KillBlobs", "preBlobs", "RePass". Updated: "DynInflate", "KillNoise".
Short comparison vs DSub_MI (slowed to 10fps, duration: 54 seconds):
https://i.imgur.com/iQlmSUf.gif
Better quality video instead of gif: https://imgur.com/yb4g2Hi
Full sample, Scripts, User script to run comparison:https://drive.google.com/file/d/1joXmOQtSnKMuYHWie5zbVeZn3jma3132/view?usp=sharing
InpaintDelogo ( RePass=1 ) - 148 FPS
InpaintDelogo ( RePass=2 ) - 112 FPS (insignificantly less artifacts)
DSub_MI (from this thread) - 174 FPS
InpaintDelogo sometimes has artifacts just before subtitle appears/changes because it does temporal shenanigans and this sample is a bad case for it.
Artifacts could be reduced further with some subs detection added, now it doesn't do any detection.
StainlessS
22nd August 2021, 22:53
Impressive :)
VoodooFX
23rd August 2021, 11:11
Noticed that deblob makes letter "M" disappear at ~1975 frame, for comparison use safer "preBlobs=-1" option, there will be a bit more artifacts.
PS:
Forgot to mention that "KillNoise" now needs RgTools.
EDIT:
I've observed a random bug running that comparison example (had one more InpaintDelogo's instance stacked), one or two clips from stacked output starts showing garbled mask, refreshing preview in AvsPmod fixes it.
StainlessS
24th August 2021, 12:19
Just thought I'de point out something I posted [and forgot about] some time ago. [Was IanB idea]
https://forum.doom9.org/showthread.php?p=1912309#post1912309
When using detector on Blended Group Detect clip, a simple Sharpen(1.0) whacks up the flimsey text to Y=255 [or maybe it was R,G,B all up to 255, probably both as used PC.601].
So, in most cases the text should be easily found unless something else somehow survives blending [ie static full white chunk of something].
EDIT: Usually set Lo threshold to about 222, so 255 text big help for detector. [Of course the blurred/blended parts of frame aint got much in the way of edges to sharpen, so almost free of potential probs]
EDIT: I wonder if Delogo type plugs do a Sharpen() when looking for opaque white logo on blended frame average, it would defo be beneficial.
But, will have to point out that any colors/tolerance has also to be provided with source sharpened, kinda awkward. [EDIT: also darken black-ish halo]
EDIT: "Blended Group Detect clip" = ClipBlend.
Maybe less useful if text thickness much greater than 2, only flimsy text.
EDIT:
[Was IanB idea]
Actually, seems like might have been my idea, [dont wanna blame IanB for anything I did], IanB idea was for something else,
and I just forgot and confused the two things.
VoodooFX
24th August 2021, 13:57
Just thought I'de point out something I posted [and forgot about] some time ago. [Was IanB idea]...
I remember it, that is the thread where I got idea for the dynamic mask method 3 aka "DynMask=3", I remember experimenting with Sharpen but I probably didn't find it useful (or universally applicable).
But I forgot that there already you posted some scripts to detect logo, probably because I didn't try them...
VoodooFX
25th August 2021, 17:43
# Version 1.32 : 2021/08/25. Fixed illogical Y8/YV12 conversions in the dynamic mask routines. ~20% faster.
# External dynamic mask expected to be Y8.
I've no idea why it was like that, hopefully I didn't break anything.
Benchmarks re-run with v1.32:
InpaintDelogo ( RePass=1 ) - 177 FPS
InpaintDelogo ( RePass=2 ) - 144 FPS (insignificantly less artifacts)
DSub_MI (from this thread) - 176 FPS
kedautinh12
25th August 2021, 17:56
Thanks, hope InpainDelogo will easy to use in the future :D
VoodooFX
25th August 2021, 18:00
Thanks, hope InpainDelogo will easy to use in the future :D
As you can see with time more parameters gets added, so it can only go to "harder to use" direction. ;)
StainlessS
26th August 2021, 06:16
The more parameters, the more fun :eek:
Btw, I'm interested in detection method you done in "DetectSub_MI", would it be possible to detect subs in a mask and extract them to images? One image per sub with time range in the filename, for example like "0_00_56_958__0_00_57_957.tiff".
something like this?
avisource("c:\clip.avi")
global f=framerate(last)
logfile="c:\smpte.txt"
writefile(logfile,"showsmpte(current_frame,f)")
function showsmpte(int cf,float fr) #cf=current_frame,fr=framerate
{
hh=int(cf/3600/fr)
mm=int((cf-hh*3600*fr)/60/fr)
ss=int((cf-hh*3600*fr-mm*60*fr)/fr)
ff=int((cf-hh*3600*fr-mm*60*fr-ss*fr))
smpte=string(hh,"%02.0f")+":"+string(mm,"%02.0f")+":"+string(ss,"%02.0f")+":"+string(ff,"%0"+string(int(log(fr)/log(10)))+".0f")
smpte
}
EDIT:
Above number of digits in last [frames] section of returned string dont quite work right, below fixes it and mod.
Function Frame2SMPTE(int Frameno,float Fps,int "digits",bool "File") { # ssS Mod of fn by tsp:- https://forum.doom9.org/showthread.php?p=551000#post551000
ch = Default(File,false) ?"_":":"
digits = Default(digits, int(log(max(Fps,1))/log(10))+1) # Log10 v2.60+, [Log10(n) = Log(n) / Log(10)]
hh=int(Frameno/3600/Fps) mm=int((Frameno-hh*3600*Fps)/60/Fps)
ss=int((Frameno-hh*3600*Fps-mm*60*Fps)/Fps) ff=int((Frameno-hh*3600*Fps-mm*60*Fps-ss*Fps))
#return RT_String("%02d%s%02d%s%02d%s%0*d",hh,ch,mm,ch,ss,ch,digits,ff)
return String(hh,"%02.0f")+ch+string(mm,"%02.0f")+ch+string(ss,"%02.0f")+ch+string(ff,"%0"+string(digits)+".0f")
}
blankclip(Length=100000,fps=50)
ShowSMPTE
SSS="""
subtitle(Frame2SMPTE(current_frame,FrameRate,digits=3,File=true)) # Use File format '_' instead of ":", and Force 3 digits in last frames section
"""
Scriptclip(SSS)
EDIT: Handy function
Function nDigits(Int n) { return Int(Log10(Max(n,1)))+1 } # ssS, Log10 v2.60+ [ Log10(n) = Log(n)/Log(10) ]
Function nDigits(Int n) { return int(Log(Max(n,1))/Log(10))+1 } # ssS, v2.58 Ver$
StainlessS
26th August 2021, 19:00
VX,
Any idea how to get rid of them there really helpful frame numbering whotsits output by ImageWriter ?
eg
"0_00_56_958__0_00_57_957.tiff".
instead of
"0_00_56_958__0_00_57_957000123.tiff".
[when frame number is non 0]
Been annoying me since forever.
EDIT: Tryin' to implement your required SMPTE thingy, only problem is above nasty stuff.
EDIT: OK, I managed to do it when frame was 0, so I can frig it up with trim to get it to work ok. [I think]
EDIT: Yeah I can do it, sorry for bothering you.
VoodooFX
26th August 2021, 22:39
I managed to do it only with trim. Like this:
Trim(0,-1).ConvertToRGB().ImageWriter("d:\saved_img" + "%.0d.bmp",0,-1,"bmp")
StainlessS
26th August 2021, 22:55
Thanks, more or less the same here.
T is single frame.
if(SMPTE) {
FNam=Frame2SMPTE(S,fps,3,true) +"__" + Frame2SMPTE(E,fps,3,true)
T.ImageWriter(ImageDir+"\"+FNam+"%.0d.%s",0,-1,ImgType).RT_YankChain(n=0) # Force Write image
} else {
T.Loop.ImageWriter(ImageDir+"\SUBS_%06d"+"."+Imgtype, i, i,ImgType).RT_YankChain(n=i) # Force Write image
}
Got SMPTE working see other thread.
EDIT: Has to be single frame clip and have the "%.0d" thing to work without "000000" bit.
VoodooFX
31st August 2021, 22:20
v1.33 tuned to reduce artifacts in the mask for this anime sample (once again).
New settings to create a mask from halo and it's used to cleanup a mask for text.
# Version 1.33 : 2021/08/31. New: "DynMask3H", "ModeSH", "TuneH1", TuneH2", "TuneH3", "KillNoiseH, "rePassH".
# Updated: "Show", "DynTune".
Benchmark (sample is in some post above):
InpaintDelogo - 152 FPS
DSub_MI 0.0.7 - 148 FPS
InpaintDelogo(Loc="0,0,-0,-0",
\ oPP=0,
\ DynMask=3, DynTune=220,
\ Dyn3Seq=12, ClpBlend=10, DynPostTune=1, Dyn3buffer=0,
\ KillBlobs=-4, preBlobs=-1, KillNoise=5,
\ DynMask3H=50, TuneH1=4, TuneH2=2, TuneH3=1,
\ Show=4)
There is hardly any artifact left, maybe it's not possible to cleanup it further, unless there is a function like magic wand and it would fill contiguous space like that:
From:
https://i.imgur.com/ZuUpEny.png
To:
https://i.imgur.com/a7eUtoT.png
VoodooFX
22nd September 2021, 00:42
# Version 1.34 : 2021/09/21. New: "Extract" parameter to extract subtitles to images for OCR.
# New: 'SubsMask2Img()' func (mod of StainlessS's 'DetSub_Extract()').
# Note: This version needs AviSynth+.
Sample from there: https://drive.google.com/file/d/1joXmOQtSnKMuYHWie5zbVeZn3jma3132
Extraction in one go:
InpaintDelogo(Loc="0,0,-0,-0",
\ oPP=0,
\ DynMask=3, DynTune=220,
\ Dyn3Seq=12, ClpBlend=10, DynPostTune=1, Dyn3buffer=0,
\ KillBlobs=-4, preBlobs=-1, KillNoise=5,
\ DynMask3H=50, TuneH1=4, TuneH2=2, TuneH3=1,
\ Extract=1, ImgDir="D:\Images",
\ Show=4)
https://i.imgur.com/dZeumm7.png
Extraction with intermediate file:
Encoding the mask to lossless x264 ("--preset ultrafast --range pc --cabac -q0") took 28 seconds (set Extract=0 in above example).
Then:
SubsMask2Img(ImgDir="D:\Images")
https://i.imgur.com/wvSmA3S.png
Shinkiro
29th September 2021, 10:18
VoodooFX and StainlessS Thanks! This is a very useful expansion of the script capabilities.
I have a powershel script for converting received images into srt subtitles, using text recognition software Tesseract
Softsabber_Tesseract.ps1
function fmt($name){ $name -replace '_',':' -replace ':(?=\d\d\d)',',' -replace '::',' --> ' }
dir *.png |% {$c=1} {tesseract $_ output -l rus; $c; fmt $_.basename; (gc output.txt); ++$c } >Soft_Sub.srt
someone might need
VoodooFX
29th September 2021, 21:27
VoodooFX Thanks! This is a very useful expansion of the script capabilities.
I have a powershel script for converting received images into srt subtitles, using text recognition software Tesseract
Thx for the script. Don't forget StainlessS, he done heavy lifting to make it happen.
I got few ideas for getting a cleaner mask, need to test them out in practice, maybe I'll implement them in new DynMask=4 method [if it's better or faster].
I found Subtitle Edit's nOCR feature better and faster than Tesseract.
Shinkiro
29th September 2021, 21:50
Thx for the script. Don't forget StainlessS, he done heavy lifting to make it happen.
Corrected that unfortunate inaccuracy.:)
I found Subtitle Edit's nOCR feature better and faster than Tesseract.
Can it recognize italics and render it into text? because Tesseract has a problem with that.
VoodooFX
29th September 2021, 22:25
Can it recognize italics and render it into text? because Tesseract has a problem with that.
Yes, it's like a binary image compare, but different. Tesseract produces many errors and correct them takes lots of time, when after nOCR I got result with no errors.
VoodooFX
5th October 2021, 04:01
# Version 1.36 : 2021/10/04. Added auto generation of the dynamic mask for subtitles with halo (aka "DynMask=4").
# Updated: "KillNoise", and some other tweaks.
Faster, better, simpler - "DynMask=4" method:
InpaintDelogo(Loc="0,0,-0,-0",
\ oPP=0,
\ DynMask=4, DynTune=220, DynMask4H=60, KillNoise=-5,
\ Show=4)
~30% faster than "DynMask=3" + "DynMask3H" method. Artifact at ~3073 frame is gone, one dot is still present at ~3159, so I won't be happy till it's there. :D
...converting received images into srt subtitles, using text recognition software Tesseract...
Could you share to pastebin the output from your Tesseract (OCR subs from that anime sample)?
Shinkiro
5th October 2021, 10:39
Could you share to pastebin the output from your Tesseract (OCR subs from that anime sample)?
https://pastebin.com/kWzxLUMm (model fast)
https://pastebin.com/r9ia78qy (model best) this model gave off some garbage in some places.
Tesseract adds some sort of sign at the end of each line, I manually remove it.
He's having some trouble identifying the letter I
VoodooFX
7th October 2021, 23:52
(model best) this model gave off some garbage in some places.
He's having some trouble identifying the letter I
Are these done with Tesseract 4/5 neural nets LSTM? Did you tried inverted images (SE inverts them automatically when Tesseract 5 is selected)?
Tesseract in Subtitle Edit can detect italics.
Here are my tests (I deleted all SE's dictionaries to prevent auto-correction and to see pure Tesseract):
Tesseract 3:
https://pastebin.com/7BX287Fp
InpaintDelogo(Loc="0,0,-0,-0", oPP=0, DynMask=4, DynTune=220, DynMask4H=60, KillNoise=-5, Show=4, Extract=1, ImgDir="D:\Images")
Tesseract 5 (original, no LSTM):
https://pastebin.com/BLc5b09t
InpaintDelogo(Loc="0,0,-0,-0", oPP=0, DynMask=4, DynTune=220, DynMask4H=60, KillNoise=-5, Show=4, DynMaskUp=2, Extract=1, ImgDir="D:\Images")
I think T5 likes bigger images, I used "DynMaskUp"(new parameter in v1.37) instead of "ImgSize", dunno if it helps, I think that for nOCR it should.
VoodooFX
28th October 2021, 21:10
# Version 1.40 : 2021/10/28. Some speed optimizations. AvsInpaint v1.3 is required.
# Version 1.39 : 2021/10/23. Various improvements.
# Version 1.38 : 2021/10/22. Fixed: Bug with "SubSuspect" parameter.
# Version 1.37 : 2021/10/07. New: "DynMaskUp" parameter.
VoodooFX
8th November 2021, 03:25
# Version 1.45 : 2021/11/07. Fixed: Automask wasn't working if coords were "0,0,0,0".
# Version 1.44 : 2021/11/05. Refactored multipass deblending code.
# Version 1.43 : 2021/11/03. Fixed: One pass Deblend didn't see A/C masks from multi pass. Changed: "Deep".
# Version 1.42 : 2021/11/03. Fixed: A/C masks file name if coord is "-0".
# Version 1.41 : 2021/11/03. Fixed: "Show=1" was broken with "Deblend" since v1.18.
VoodooFX
10th November 2021, 22:41
@StainlessS
Maybe you could spot something why func below doesn't run on Avs 2.6 (it runs OK on Avs+)?
Posting here to not litter your thread. Relevant link: https://forum.doom9.org/showthread.php?p=1956968#post1956968
Error at clp2x2: "Crop: Top and Left must be more than 0".
function Analyze5FX(clip clp, val "mask", int "Mode", int "Analyze5Th", string "Ignore", int "Show") {
mask = default (mask , "")
Mode = default (Mode , 1 )
Analyze5Th = default (Analyze5Th , 15)
Ignore = default (Ignore , "none")
Show = default (Show , 0)
Assert (!(Mode < 1 || Mode > 2), """"Mode" value must be between 1 and 2.""")
Assert (!(Analyze5Th < 1 || Analyze5Th > 255), """"Analyze5Th" value must be between 1 and 255.""")
Assert (!(Show < 0 || Show > 2), """"Show" value must be between 0 and 2.""")
Assert ((Ignore == "top" || Ignore == "bottom" || Ignore == "left" || Ignore == "right" || Ignore == "none"), """"Ignore" string must be 'top', 'bottom', 'left', 'right' or 'none'.""")
VW = clp.Width
VH = clp.Height
Th = Analyze5Th
mask = (IsClip(mask)) ? mask.ConvertToY8(matrix="PC.709").MTballoon2PX(2) : nop
clpS = clp.ScriptClip("subtitle(string(current_frame))")
# Construct a clip to check thresholds (for deblending analysis) #
#================================================================#
if(Mode == 1) {
# Get coords of the logo mask
VFX2="""
Status = (mask.RT_YInRangeLocate(Baffle=1,lo=255,hi=255)) ? 1 : 2
if(Status==1) {
RT_DBaseSet(Analyze5DB,0,Status,YIRL_X,YIRL_Y,YIRL_W,YIRL_H)
} else {
RT_DBaseSetField(Analyze5DB,0,0,Status)
}
return Last
"""
Analyze5DB = ("~InpaintDelogo_Analyze5_"+RT_LocalTimeString+".DB").RT_GetFullPathName
RT_DBaseAlloc(Analyze5DB,1,"iiiii")
Parse = mask.GScriptClip(VFX2, local=true, args="mask,Analyze5DB", after_frame=true)
Parse.RT_ForceProcess
Assert (!(RT_DBaseGetField(Analyze5DB,0,0) == 2), "Mask image is invalid.")
mX = RT_DBaseGetField(Analyze5DB,0,1)
mY = RT_DBaseGetField(Analyze5DB,0,2)
mW = RT_DBaseGetField(Analyze5DB,0,3)
mH = RT_DBaseGetField(Analyze5DB,0,4)
RT_FileDelete(Analyze5DB)
# Convert X,Y,W,H coords to L,T,-R,-B coords
mL = mX
mT = mY
mR = -VW + mW + mX
mB = -VH + mY + mH
Assert (!(mL < 8 && -1*mR < 8 || mT < 8 && -1*mB < 8), "Mask image or Loc is invalid.")
# Coords mod2 round up
mL = mL + (mL % 2)
mT = mT + (mT % 2)
mR = mR - (mR % 2)
mB = mB - (mB % 2)
# Crop 2x2 pxs area from above or below the logo and make a background from it
mid = (VW-mL+mR)/2+mL
mid = mid + (mid % 2)
clp2x2 = (mT >= -1*mB) ? clp.crop(mid-2, mT-2, -1*(VW-mid), -VH+mT+2) : clp.crop(mid-2, VH+mB-2, -1*(VW-mid), mB+2)
clpB = clp2x2.PointResize(VW, VH)
Msk1 = mask.MTballoon2PX(2).mt_logic(mask, mode="xor") # 2px wide mask around logo (+2px offset from the base mask)
clp5 = clpB.Overlay(clp, mask=Msk1)
} else {
# Construct a clip to check thresholds (for Automask=1) #
#=======================================================#
cT = clp.crop(4, 4, -4, -1*(VH-6))
cB = clp.crop(4, VH-6, -4, -4)
cL = clp.crop(4, 4, -1*(VW-6), -4)
cR = clp.crop(VW-6, 4, -4, -4)
if(Ignore == "top") {
cT = cB
} else if (Ignore == "bottom") {
cB = cT
} else if (Ignore == "left") {
cL = cR
} else if (Ignore == "right") {
cR = cL
}
wc = StackHorizontal(cr,cl).TurnLeft
hc = StackVertical(ct,cb)
if(wc.width > hc.width) {
hc = hc.PointResize(wc.width, hc.height)
out = StackVertical(wc,hc)
} else if(wc.width < hc.width) {
wc = wc.PointResize(hc.width, wc.height)
out = StackVertical(wc,hc)
} else {
out = StackVertical(wc,hc)
}
clp5 = (Show == 2) ? out.PointResize(VW, VH) : out
}
# Delete frames above thresholds (that leaves us with the suitable frames for analysis) #
#=======================================================================================#
# Mod of Gavino's DeleteFrames() - https://forum.doom9.org/showthread.php?t=163107
clp5
fc = FrameCount()
res = BlankClip(clp, length=0)
condition = "YPlaneMinMaxDifference >" +String(Th)+ " || UPlaneMinMaxDifference >" +String(Round(Th/2))+ " || VPlaneMinMaxDifference >" +String(Round(Th/2))
current_frame = 0
while (current_frame < fc) {
while (Eval(condition) && current_frame < fc) {
current_frame = current_frame+1
}
if (current_frame < fc) { # not at end
start = current_frame # start of wanted section
while (!Eval(condition) && current_frame < fc) {
current_frame = current_frame+1
}
clpX = (Show == 1) ? clpS : (Show == 2) ? clp5 : clp # 0: Original frames, 1: Original frames + frameNo, 2: Constructed clips (Mode=2 clip is upsized for show)
res = res + clpX.Trim(start, start-current_frame)
}
}
Assert (!(res.FrameCount < 2), """No frames found to analyze, increase "Analyze5Th".""")
return res
}
EDIT:
Clip: https://drive.google.com/file/d/1PdtRWcTztQgxPQalk_FgOJYSCiQKPuRW/view?usp=sharing
Mask: https://i.imgur.com/WieMHwJ.png
Test script:
GImport("c:\Analyze5FX.avs")
x = LSMASHVideoSource("D:\899.mp4")
x = x.crop(1124,24,-52,-636).Trim(30,100) # trimed for speed
mask = ImageSource("D:\mask.bmp",0,0)
# Mode 1 test
m0 = x.Analyze5FX(mask=mask, mode=1, show=0)
m1 = x.Analyze5FX(mask=mask, mode=1, show=1)
m2 = x.Analyze5FX(mask=mask, mode=1, show=2)
mu = Stackhorizontal(m0.AddBorders(0,0,2,0,$000000), m0)
md = Stackhorizontal(m1.AddBorders(0,0,2,0,$000000), m2)
Stackvertical(mu.AddBorders(0,0,0,2,$000000), md)
StainlessS
10th November 2021, 23:41
Thanks for the source files.
Confirm, you are trying to get data for only current_frame, yes, [in Analyze5FX] ?
If so, then dont really want to use RT_ForceProcess, which scans entire clip.
Can use RT_YankChain [force single frame process].
RT_YankChain(clip,int "n"=current_frame,int "delta"=0)
Compile/runtime clip function.
n (default = current_frame) frame number.
delta (default = 0), frame number offset.
Forcibly process the filter graph chain for frame n + delta. Makes intention more explicit than eg RT_AverageLuma(n=i,w=1,h=1).
See also RT_GraphLink().
eg, Below, TmpA plays no part in output clip and so bitmap would not be written. Uncomment the RT_YankChain line and it will.
a=Colorbars.ShowFrameNumber
TmpA = a.subtitle("TestA").ImageWriter("TestA_", type="BMP")
#RT_YankChain(TmpA,n=42) # Uncomment to forcibly write frame 42 of TmpA to bitmap
return MessageClip("All Done")
VoodooFX
10th November 2021, 23:55
Confirm, you are trying to get data for only current_frame, yes, [in Analyze5FX] ?
If so, then dont really want to use RT_ForceProcess, which scans entire clip.
Can use RT_YankChain [force single frame process].
In if(Mode == 1) block only process that one frame (image mask), I'll change that to RT_YankChain.
I think GScriptClip there is not needed, nothing is used there from the return, is it possible to get RT_YInRangeLocate coords vars directly into function without RT_DBase thingy there?
EDIT:
Just realized Analyze5FX is using some stuff from InpaintDelogo.avsi, so you need to load that too.
Or just replace [I]MTballoon2PX(2) with mt_expand().mt_expand()
StainlessS
11th November 2021, 00:08
is it possible
Instead of
if(Mode == 1) {
# Get coords of the logo mask
VFX2="""
Status = (mask.RT_YInRangeLocate(Baffle=1,lo=255,hi=255)) ? 1 : 2
if(Status==1) {
RT_DBaseSet(Analyze5DB,0,Status,YIRL_X,YIRL_Y,YIRL_W,YIRL_H)
} else {
RT_DBaseSetField(Analyze5DB,0,0,Status)
}
return Last
"""
Analyze5DB = ("~InpaintDelogo_Analyze5_"+RT_LocalTimeString+".DB").RT_GetFullPathName
RT_DBaseAlloc(Analyze5DB,1,"iiiii")
Parse = mask.GScriptClip(VFX2, local=true, args="mask,Analyze5DB", after_frame=true)
Parse.RT_ForceProcess
Assert (!(RT_DBaseGetField(Analyze5DB,0,0) == 2), "Mask image is invalid.")
mX = RT_DBaseGetField(Analyze5DB,0,1)
mY = RT_DBaseGetField(Analyze5DB,0,2)
mW = RT_DBaseGetField(Analyze5DB,0,3)
mH = RT_DBaseGetField(Analyze5DB,0,4)
RT_FileDelete(Analyze5DB)
This should be about right
if(Mode == 1) {
# Get coords of the logo mask
if(!(mask.RT_YInRangeLocate(Baffle=1,lo=255,hi=255))) {
Assert (FALSE, "Mask image is invalid.")
}
mX = YIRL_X
mY = YIRL_Y
mW = YIRL_W
mH = YIRL_H
Oops, messed up, fixed.
EDIT: A bit more tinker. [Baffle might be a bit low]
EDIT: Or,
if(Mode == 1) {
# Get coords of the logo mask
Assert (mask.RT_YInRangeLocate(Baffle=1,lo=255,hi=255), "Mask image is invalid.")
mX = YIRL_X
mY = YIRL_Y
mW = YIRL_W
mH = YIRL_H
EDIT: Baffle, about thickness of text + halo. [maybe slightly less]
VoodooFX
11th November 2021, 00:28
This should be about right
But RT_YInRangeLocate must be in runtime script.
StainlessS
11th November 2021, 00:34
Yeah, I'm tryin' to figure out what you are trying to do, give me a bit of time.
VoodooFX
11th November 2021, 00:42
Yeah, I'm tryin' to figure out what you are trying to do, give me a bit of time.
No rush here, I wrote it a month ago, still need to rewrite some InpaintDelogo's conditional madness to fit this func in.
It selects frames to do similar autoanalysis like AviUtl tool, just a bit better as we supply a base mask and scan area very close around logo (mode=1), mode=2 should be almost same as in AviUtl tool (except that it can deal with logos on the border with Ignore parameter. [actually I dunno exactly what AviUtl does, but it should be something similar]
StainlessS
11th November 2021, 01:20
OK, some backward steps here,
You seem to be wantin to handle some stuff in runtime and some other in frameserve periods, maybe you can figure out what you want from this
if(Mode == 1) {
VFX2="""
if(mask.RT_YInRangeLocate(Baffle=1,lo=255,hi=255)) {
# Convert X,Y,W,H coords to L,T,-R,-B coords
mL = YIRL_X
mT = YIRL_Y
mR = -VW + mL + YIRL_W
mB = -VH + mT + YIRL_H
RT_DBaseSet(Analyze5DB,current_frame,1, mL,mT,mR,mB) # Write VoodooFX style coords to DB
} else {
RT_DBaseSetField(Analyze5DB,current_frame,0,2)
Assert (FALSE, "Mask image is invalid.") # Not really a good place for Assert()
}
return Last
"""
Analyze5DB = ("~InpaintDelogo_Analyze5_"+RT_LocalTimeString+".DB").RT_GetFullPathName
RT_DBaseAlloc(Analyze5DB,Mask.FrameCount,"iiiii") # Alloc DBase records, number of frames in mask clip
mask.GScriptClip(VFX2, local=true, args="mask,Analyze5DB", after_frame=true)
Parse.RT_ForceProcess # FORCE PROCESS ENTIRE CLIP
# mL, mT etc not available here in frameserver (non-runtime) section. (so below will not work]
Assert (!(mL < 8 && -1*mR < 8 || mT < 8 && -1*mB < 8), "Mask image or Loc is invalid.") # Not really a good place for Assert()
# Coords mod2 round up # Maybe move into GSCriptClip, before write to DBase, later available in DBase.
mL = mL + (mL % 2)
mT = mT + (mT % 2)
mR = mR - (mR % 2)
mB = mB - (mB % 2)
# Crop 2x2 pxs area from above or below the logo and make a background from it
mid = (VW-mL+mR)/2+mL
mid = mid + (mid % 2)
clp2x2 = (mT >= -1*mB) ? clp.crop(mid-2, mT-2, -1*(VW-mid), -VH+mT+2) : clp.crop(mid-2, VH+mB-2, -1*(VW-mid), mB+2)
clpB = clp2x2.PointResize(VW, VH)
Msk1 = mask.MTballoon2PX(2).mt_logic(mask, mode="xor") # 2px wide mask around logo (+2px offset from the base mask)
clp5 = clpB.Overlay(clp, mask=Msk1)
} else {
The GScriptclip stuff force processes entire length of mask clip,
stuff after that seems to wanna access coords (which are only accessable in runtime script), maybe you can slot some of it into the Gscriptscript part.
Any good at all ?
EDIT: Maybe instead of Assert, write eg -1 as status to DBase, to detect error later, or something ???
EDIT: clp2x2 = (mT >= -1*mB) ? clp.crop(mid-2, mT-2, -1*(VW-mid), -VH+mT+2) : clp.crop(mid-2, VH+mB-2, -1*(VW-mid), mB+2)
Are above two parts same size, and same size as clp ? [EDIT: no not same size as clp, Scriptclip output MUST be same size as input]
If so, then I can maybe slot into GSCriptClip. [if not, then no idea].
Probably gone to bed soon. ZZZZzzzzzzz.....
VoodooFX
11th November 2021, 01:47
I don't see reason why we need to move all this stuff to GScriptClip, I think I would even get rid of GScriptClip...
Is it related to "Crop: Top and Left must be more than 0" error (on Avs 2.6!, func runs OK on Avs+)?
StainlessS
11th November 2021, 02:00
If you need coords, then you need runtime.
Maybe something like this [it dont work, but maybe roughly what is required]
if(Mode == 1) {
VFX2="""
if(!mask.RT_YInRangeLocate(Baffle=1,lo=255,hi=255)) {
RT_DBaseSetField(Analyze5DB,current_frame,0,2)
Assert (FALSE, "Mask image is invalid.") # Not really a good place for Assert()
}
VW = Last.Width
VH = Last.Height
# Convert X,Y,W,H coords to L,T,-R,-B coords
mL = YIRL_X
mT = YIRL_Y
mR = -VW + mL + YIRL_W
mB = -VH + mT + YIRL_H
Assert (!(mL < 8 && -1*mR < 8 || mT < 8 && -1*mB < 8), "Mask image or Loc is invalid.") # Not really a good place for Assert()
# Coords mod2 round up
mL = mL + (mL % 2)
mT = mT + (mT % 2)
mR = mR - (mR % 2)
mB = mB - (mB % 2)
RT_DBaseSet(Analyze5DB,current_frame,1, mL,mT,mR,mB) # Write VoodooFX style coords to DB
# Crop 2x2 pxs area from above or below the logo and make a background from it
mid = (VW-mL+mR)/2+mL
mid = mid + (mid % 2)
clp2x2 = (mT >= -1*mB) ? Last.crop(mid-2, mT-2, -1*(VW-mid), -VH+mT+2) : Last.crop(mid-2, VH+mB-2, -1*(VW-mid), mB+2)
Return clp2x2.PointResize(VW, VH) # Same size as input clip
"""
Analyze5DB = ("~InpaintDelogo_Analyze5_"+RT_LocalTimeString+".DB").RT_GetFullPathName
RT_DBaseAlloc(Analyze5DB,Mask.FrameCount,"iiiii") # Alloc DBase records, number of frames in mask clip
clpB = clp.GScriptClip(VFX2, local=true, args="mask,Analyze5DB", after_frame=true)
clpB.RT_ForceProcess # FORCE PROCESS ENTIRE CLIP
Msk1 = mask.MTballoon2PX(2).mt_logic(mask, mode="xor") # 2px wide mask around logo (+2px offset from the base mask)
clp5 = clpB.Overlay(clp, mask=Msk1)
# RT_FileDelete(Analyze5DB)
} else {
ZZZZZZzzzzz......
EDIT:
Or if you dont need coords again later, then ony 1 frame at a time, no force process, no DB.
if(Mode == 1) {
VFX2="""
if(!mask.RT_YInRangeLocate(Baffle=1,lo=255,hi=255)) {
Assert (FALSE, "Mask image is invalid.") # Not really a good place for Assert()
}
VW = Last.Width
VH = Last.Height
# Convert X,Y,W,H coords to L,T,-R,-B coords
mL = YIRL_X
mT = YIRL_Y
mR = -VW + mL + YIRL_W
mB = -VH + mT + YIRL_H
Assert (!(mL < 8 && -1*mR < 8 || mT < 8 && -1*mB < 8), "Mask image or Loc is invalid.") # Not really a good place for Assert()
# Coords mod2 round up
mL = mL + (mL % 2)
mT = mT + (mT % 2)
mR = mR - (mR % 2)
mB = mB - (mB % 2)
# Crop 2x2 pxs area from above or below the logo and make a background from it
mid = (VW-mL+mR)/2+mL
mid = mid + (mid % 2)
clp2x2 = (mT >= -1*mB) ? Last.crop(mid-2, mT-2, -1*(VW-mid), -VH+mT+2) : Last.crop(mid-2, VH+mB-2, -1*(VW-mid), mB+2)
Return clp2x2.PointResize(VW, VH) # Same size as input clip
"""
clpB = clp.GScriptClip(VFX2, local=true, args="mask,Analyze5DB", after_frame=true)
Msk1 = mask.MTballoon2PX(2).mt_logic(mask, mode="xor") # 2px wide mask around logo (+2px offset from the base mask)
clp5 = clpB.Overlay(clp, mask=Msk1)
} else {
OOps, changed clp to Last inside GSCriptClip's. [using clp as input to GScriptClip]
EDIT: I get "No frames found to analyze, increase "Analyze5Th"." [with the top code block]
VoodooFX
11th November 2021, 02:20
EDIT: I get "No frames found to analyze, increase "Analyze5Th"." [with the top code block]
Yes, same error, no frames on output for some reason.
Btw, I had to remove Analyze5DB from GScriptClip(VFX2, local=true, args="mask,Analyze5DB", after_frame=true).
EDIT:
Your edit run OK on Avs+, but it run OK there before too.
Maybe Gavino's thingy needs to be in it's own GSCriptClip too for Avs 2.6?
Or easier just to say goodbye to 2.6, I don't use it anyway. :devil:
StainlessS
11th November 2021, 02:23
Btw, I had to remove Analyze5DB
Yes of course, forgot that and did not try that block.
So, you dont need coords later then.
EDIT: And does the mod seem OK-ish, for what you want.
EDIT: Tomorrow is another day ... toodle pip.
VoodooFX
11th November 2021, 02:32
EDIT: And does the mod seem OK-ish, for what you want.
Those asserts won't work from there I think, so only that. For me it's not a problem, but newbies do weird stuff and that would be uncatched.
From Mode=1/2 blocks I need only clip as clp5.
vBulletin® v3.8.11, Copyright ©2000-2026, vBulletin Solutions Inc.