Welcome to Doom9's Forum, THE in-place to be for everyone interested in DVD conversion.

Before you start posting please read the forum rules. By posting to this forum you agree to abide by the rules.

 

Go Back   Doom9's Forum > Capturing and Editing Video > Avisynth Development

Reply
 
Thread Tools Search this Thread Display Modes
Old 1st December 2016, 12:08   #1  |  Link
spoRv
Registered User
 
Join Date: Nov 2016
Posts: 151
DeSub – automatic hardcoded subtitles removal

DeSub – automatic hardcoded subtitles removal

First post here, so please be patient! Long time lurker, it's time to give my (very little) contribution.

Is it too good to be true? Well, it needs two clips, one with subtitles (or logo), and the other without; will create a mask “on the fly”, and the subtitles (logo) will be replaced with the corresponding part of the image of the clip without logo.

For best result, it’s better to limit usage only to frames with subtitles; clips must be aligned perfectly, both temporally and spatially; must have the same (or very similar) colors; it’s always possible to merge the result and the existing not subbed clip to improve quality.

Save it as DeSub.avsi and put into your AviSynth plugins folder.

Code:

Code:
###########################################################################################
### DeSub 1.0: removes the subtitles with no need to prepare a mask for every line!     ###
###                                                                                     ###
### Two spatially and aligned clips are needed, one with hardcoded subtitles, or logo,  ###
### and the other without; the script creates a mask "on the fly" that will be used to  ###
### cover the subtitles with the corresponding parts of the clip without them.          ###
###                                                                                     ###
### Usage:   DeSub(clipwithsubs,clipwithoutsubs,subcolor)                               ###
###                                                                                     ###
### Example: goodclip.avi has subtitles, badclip.avi has no subtitles, subs color white ###
###                                                                                     ###
###          DeSub(goodclip,badclip,$ffffff)                                            ###
###                                                                                     ###
### It is highly reccomended to use it only on frames with subtitles                    ###
### Possible update: limit the use to a certain area of the frame, perfect for logo!    ###
###                                                                                     ###
### AviSynth script made by spoRv (http://blog.sporv.com) - Created: 2016-11-29         ###
###                                                                                     ###
### Creative Commons 4,0 - Attribution-ShareAlike 4.0 International (CC BY-SA 4.0)      ###
### Link to the licence page: https://creativecommons.org/licenses/by-sa/4.0/           ###
###########################################################################################

function DeSub (clip withsubs, clip nosubs, val "color") {

withsubs=withsubs.converttoyv24
nosubs=nosubs.converttoyv24
color=default(color,$ffffff)

subs = withsubs.converttorgb

mask = ColorKeyMask(subs, color, 20)
blk = blankclip(length=withsubs.FrameCount,width=withsubs.width,height=withsubs.height)

over=Overlay(withsubs, blk, mask=mask.ShowAlpha, mode="blend", opacity=1)

mk=over.levels(235,1,255,1,255)

w=2

a=mk.addborders(0,0,w,w).crop(w,w,0,0)
b=mk.addborders(0,0,0,w).crop(0,w,0,0)
c=mk.addborders(w,0,0,w).crop(0,w,-w,0)
d=mk.addborders(w,0,0,0).crop(0,0,-w,0)
e=mk.addborders(w,w,0,0).crop(0,0,-w,-w)
f=mk.addborders(0,w,0,0).crop(0,0,0,-w)
g=mk.addborders(0,w,w,0).crop(w,0,0,-w)
h=mk.crop(w,0,0,0).addborders(0,0,w,0)

x= overlay(b, f, mode="add")
y= overlay(d, h, mode="add")
xx=overlay(a, e, mode="add")
yy=overlay(c, g, mode="add")
z= overlay(x, y, mode="add")
zz=overlay(xx,yy,mode="add")

nu=overlay(z,zz,mode="add")

mk=nu.levels(235,1,255,1,255)

w=3

a=mk.addborders(0,0,w,w).crop(w,w,0,0)
b=mk.addborders(0,0,0,w).crop(0,w,0,0)
c=mk.addborders(w,0,0,w).crop(0,w,-w,0)
d=mk.addborders(w,0,0,0).crop(0,0,-w,0)
e=mk.addborders(w,w,0,0).crop(0,0,-w,-w)
f=mk.addborders(0,w,0,0).crop(0,0,0,-w)
g=mk.addborders(0,w,w,0).crop(w,0,0,-w)
h=mk.crop(w,0,0,0).addborders(0,0,w,0)

x= overlay(b, f, mode="add")
y= overlay(d, h, mode="add")
xx=overlay(a, e, mode="add")
yy=overlay(c, g, mode="add")
z= overlay(x, y, mode="add")
zz=overlay(xx,yy,mode="add")

nu=overlay(z,zz,mode="add")

mk=nu.levels(235,1,255,1,255)

w=4

a=mk.addborders(0,0,w,w).crop(w,w,0,0)
b=mk.addborders(0,0,0,w).crop(0,w,0,0)
c=mk.addborders(w,0,0,w).crop(0,w,-w,0)
d=mk.addborders(w,0,0,0).crop(0,0,-w,0)
e=mk.addborders(w,w,0,0).crop(0,0,-w,-w)
f=mk.addborders(0,w,0,0).crop(0,0,0,-w)
g=mk.addborders(0,w,w,0).crop(w,0,0,-w)
h=mk.crop(w,0,0,0).addborders(0,0,w,0)

x= overlay(b, f, mode="add")
y= overlay(d, h, mode="add")
xx=overlay(a, e, mode="add")
yy=overlay(c, g, mode="add")
z= overlay(x, y, mode="add")
zz=overlay(xx,yy,mode="add")

nu=overlay(z,zz,mode="add").levels(235,1,255,1,255)

numask = ColorKeyMask(nu.ConvertToRGB32, color, 30)

Overlay(nosubs, withsubs, mask=numask.ShowAlpha, mode="blend", opacity=1)

converttoyv12

}
I tested it, and seems to work well; of course, could still be improved. Waiting for your test results and impressions!

Examples – top DeSub result, bottom original:









My function programming is really basic; I know this script is rough, and could be improved a lot; it should contain the "box" coordinates where the subtitles (or logo) are located, to limit the filter to work just in that area; and I would like to reiterate the paint function, and limit it to three passes - with a variable like strenght, where 1 does one pass with w=2, 2 does two passes with w=2 and then w=3, and 3 does three passes, with w=2 then w=3 then w=4 - like I did in the script.

Any modification, correction or improvement is really welcome!
spoRv is offline   Reply With Quote
Old 1st December 2016, 21:51   #2  |  Link
Gser
Registered User
 
Join Date: Apr 2008
Posts: 418
Would it be possible to do the opposite, meaning adding hardcoded subtitles from a video to a clean video?

And how about using mt_merge from MaskTools2 instead of Overlay to avoid colorspace conversion.

Last edited by Gser; 1st December 2016 at 21:58.
Gser is offline   Reply With Quote
Old 2nd December 2016, 00:26   #3  |  Link
manolito
Registered User
 
manolito's Avatar
 
Join Date: Sep 2003
Location: Berlin, Germany
Posts: 3,078
Sorry, I am sure missing something here, but I do not get the point of this script...

Why in the world do you want to remove hard coded subs from the first clip if you already have a second clip which does not have hardcoded subs? Why are you not just using this second clip?


Cheers
manolito
manolito is offline   Reply With Quote
Old 2nd December 2016, 01:11   #4  |  Link
StainlessS
HeartlessS Usurer
 
StainlessS's Avatar
 
Join Date: Dec 2009
Location: Over the rainbow
Posts: 10,980
Quote:
Originally Posted by manolito View Post
Why in the world do you want to remove hard coded subs from the first clip if you already have a second clip...
Well I guess its that famous Mallory thing, "Because it's there".

https://en.wikiquote.org/wiki/George_Mallory
__________________
I sometimes post sober.
StainlessS@MediaFire ::: AND/OR ::: StainlessS@SendSpace

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

Last edited by StainlessS; 2nd December 2016 at 01:13.
StainlessS is offline   Reply With Quote
Old 2nd December 2016, 03:41   #5  |  Link
spoRv
Registered User
 
Join Date: Nov 2016
Posts: 151
Because:

1) "spatially aligned" could also means the clip with subtitles is HD, while the one without is SD... hence, the superior HD quality will be preserved almost entirely, minus the replaced subtitles, that will be just upscales
2) clip without subs could have lower quality, even if have the same resolution - for example, web clip Vs DVD, highly compressed Vs less compressed, web download Vs BD etc.
3) two clips (or more) averaged have less noise (apart the replaced subtitles, that will be the same for both, of course)
spoRv is offline   Reply With Quote
Old 2nd December 2016, 05:17   #6  |  Link
StainlessS
HeartlessS Usurer
 
StainlessS's Avatar
 
Join Date: Dec 2009
Location: Over the rainbow
Posts: 10,980
Maybe, this should be in the Usage forum, not really a developer thing, really.

Anyways, we are glad to have you, good luck and may your god smile upon you.

We wish you a very good & warm Welcome to the forum
__________________
I sometimes post sober.
StainlessS@MediaFire ::: AND/OR ::: StainlessS@SendSpace

"Some infinities are bigger than other infinities", but how many of them are infinitely bigger ???
StainlessS is offline   Reply With Quote
Old 2nd December 2016, 12:26   #7  |  Link
spoRv
Registered User
 
Join Date: Nov 2016
Posts: 151
Thanks Stainless! I followed you since many years ago, I like your scripts and your will to help!

Don't know if this thread should be moved to the avisynth usage subforum instead; I saw filters and scripts posted here, so I thought it was the right place; but if moderators think it should be moved, it is ok!
spoRv is offline   Reply With Quote
Old 12th August 2021, 15:08   #8  |  Link
Shinkiro
Registered User
 
Join Date: Dec 2012
Posts: 65
Can someone add a mask output function to the script?
I would also like an adjustable blur overlay on the mask and the ability to limit the area of the script using cropping.
__________________
Ryzen 2700x | ASUS ROG Strix GTX 1080 Ti | 16 Gb DDR4
Windows 10 x64 20H2
KD-55XE9005 | Edifier R2800
Shinkiro is offline   Reply With Quote
Old 13th August 2021, 02:03   #9  |  Link
VoodooFX
Banana User
 
VoodooFX's Avatar
 
Join Date: Sep 2008
Posts: 985
Quote:
Originally Posted by Shinkiro View Post
Can someone add a mask output function to the script?
I would also like an adjustable blur overlay on the mask and the ability to limit the area of the script using cropping.
You can do it with InpaintDelogo, if subtitles are white you should get good results.
Can you share snippets from your clips?

Last edited by VoodooFX; 13th August 2021 at 02:07.
VoodooFX is offline   Reply With Quote
Old 13th August 2021, 02:08   #10  |  Link
kedautinh12
Registered User
 
Join Date: Jan 2018
Posts: 2,153
Your InpainDelogo hard to use. I'm stuck at step get .bmp. Maybe you can use video guide for easy to use
kedautinh12 is offline   Reply With Quote
Old 13th August 2021, 06:04   #11  |  Link
Shinkiro
Registered User
 
Join Date: Dec 2012
Posts: 65
Quote:
Originally Posted by VoodooFX View Post
You can do it with InpaintDelogo, if subtitles are white you should get good results.
Can you share snippets from your clips?
I don't really feel like creating 500 bmp masks and timing each one.
I need to remove hurdsub from a good quality video (WEBDL 720p) using a less good quality video (DVDRip 480p). Can InpaintDelogo create a mask based on the difference between two clips and accept a video clip mask as an input to the mask?
__________________
Ryzen 2700x | ASUS ROG Strix GTX 1080 Ti | 16 Gb DDR4
Windows 10 x64 20H2
KD-55XE9005 | Edifier R2800
Shinkiro is offline   Reply With Quote
Old 13th August 2021, 09:14   #12  |  Link
VoodooFX
Banana User
 
VoodooFX's Avatar
 
Join Date: Sep 2008
Posts: 985
Quote:
Originally Posted by kedautinh12 View Post
Your InpainDelogo hard to use. I'm stuck at step get .bmp. Maybe you can use video guide for easy to use
Do you know easy to use delogo app, maybe I should've named it InpainDelogo so users would be prepared...
I was thinking about a video guide as the visual guides are faster to understand, but it basically would be repeating what is already written in the help (inside the script).
At what part of the help did you stuck?

Quote:
Originally Posted by Shinkiro View Post
I don't really feel like creating 500 bmp masks and timing each one.
I need to remove hurdsub from a good quality video (WEBDL 720p) using a less good quality video (DVDRip 480p). Can InpaintDelogo create a mask based on the difference between two clips and accept a video clip mask as an input to the mask?
There is no need to create multiple masks, read about dynamic mask parameter "DynMask".
It can create a clip mask without second video. I did same things as you want to do, and even used it to convert mask to soft subs.

PS:
I see multiple experimental versions of the script on my HDD, looks like I was doing those experiments with subtitles after the last published version, could be that v1.24 is not fine-tuned for subs.
If you can, share ~10mins cuts from your videos so I can check what is what with those versions.

PS2:
Looks like I was stuck on extracting subs: Dynamic mask to images for OCR
I was able to do it anyway with some external program, still would be nice to do it in AvS, maybe @StainlessS would know how to make that happen.

Last edited by VoodooFX; 14th August 2021 at 03:09.
VoodooFX is offline   Reply With Quote
Old 13th August 2021, 10:04   #13  |  Link
Shinkiro
Registered User
 
Join Date: Dec 2012
Posts: 65
Quote:
Originally Posted by VoodooFX View Post
There is no need to create multiple masks, read about dynamic mask parameter "DynMask".
It can create a clip mask without second video. I did same things as you want to do, and even used it to convert mask to soft subs.
Thanks for the tip, I'll see how it works.

Quote:
Originally Posted by VoodooFX View Post
PS:
I see multiple experimental versions of the script on my HDD, looks like I was doing those experiments with subtitles after the last published version, could be that v1.24 is not fine-tuned for subs.
If you can, share ~10mins cuts from your videos so I can check what is what with those version.
sample.zip
the main task is to replace the subtitles using a mask with the background from another video
DeSub does this in principle, but the edge of the mask is hard, which is why the joint is often visible, I added fastBlur() to it and the result became much better, but I do not know how to output the fastBlur() settings to the DeSub parameters so that it is convenient to use.
Also, to eliminate possible errors, it would be good to limit the DeSub application area to the approximate location of subtitles.
__________________
Ryzen 2700x | ASUS ROG Strix GTX 1080 Ti | 16 Gb DDR4
Windows 10 x64 20H2
KD-55XE9005 | Edifier R2800

Last edited by Shinkiro; 13th August 2021 at 10:15.
Shinkiro is offline   Reply With Quote
Old 14th August 2021, 03:03   #14  |  Link
VoodooFX
Banana User
 
VoodooFX's Avatar
 
Join Date: Sep 2008
Posts: 985
Quote:
Originally Posted by Shinkiro View Post
the main task is to replace the subtitles using a mask with the background from another video
Thx for samples, version tuned for subtitles & command to get a dynamic mask you'll find there: https://forum.doom9.org/showthread.php?p=1949752
But you won't be able to get the good results with your video, as subbed sample has wonky decimation, so I dunno how you'll align both videos frame to frame.
Plus, levels are shifted, bright areas are darker when dark areas are brighter in nonsubbed video, but that's a minor problem.

InpaintDelogo mask example:


DeSub mask example:

Last edited by VoodooFX; 14th August 2021 at 03:15.
VoodooFX is offline   Reply With Quote
Old 14th August 2021, 03:11   #15  |  Link
StainlessS
HeartlessS Usurer
 
StainlessS's Avatar
 
Join Date: Dec 2009
Location: Over the rainbow
Posts: 10,980
post #1 of 2

Well, seems that VoodooFX has sorted you out here:- https://forum.doom9.org/showthread.p...52#post1949752

But, I've been playing a little bit with an older script, thats detects subs with halo.

DetectSub_MI.avsi
Code:
###
# DetectSub.MI.avsi":- https://forum.doom9.org/showthread.php?p=1782036#post1782036
###
# DetectSub, MultiInstance script, MultiInstance not necessary here but easy convenient way to use Scriptclip, GScript and double quoted
#   strings in same script. Would have been nice if Avisynth also recognised groups of 5 sets of double quotes as well as groups of 3 and
# single double quotes.
#
# 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 DetectSub_MI(clip c,string "Override",
            \ Int "X",Int "Y",Int "W",Int "H",
            \ Int "Text_w",Int "Text_h",Int "TextCol",Int "TextVar",
            \ Int "Halo_L",Int "Halo_T",Int "Halo_R",Int "Halo_B",Int "HaloCol",Int "HaloVar",
            \ Int "THysCNT",Int "HHysCNT",Int "DHysCNT",Bool "ShowInPand",
             \ Int "Baffle_w",Int "Baffle_h",Float "Thresh_w",Float "Thresh_h",Bool "ReScan",
            \ Bool "Show",Int "stack",Bool "ShowArea",Int "AreaColor",String "DB",Bool "Debug") {
    myName="DetectSub_MI: "
    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,0)                  Y=Default(Y,Height-120)         W=Default(W,0)                      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)    TextVar=Default(TextVar,3)
    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)                                HaloVar=Default(HaloVar,3)
    THysCNT=Default(THysCNT,0)      HHysCNT=Default(HHysCNT,0)      DHysCNT=Default(DHysCNT,0)          ShowInpand=(Show)?Default(ShowInpand,False):False
    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)
    Show=Default(Show,False)                                        Stack=(Show)?default(Stack,0):0
    ShowArea=(Show)?Default(ShowArea,True):False                    AreaColor=Default(AreaColor,$FF00FF)
    DB=Default(DB,"")    UserDB=(DB!="")
    DB=(!UserDB) ? "~"+RT_LocalTimeString+".DB" : DB
    DB=RT_GetFullPathName(DB)
    Debug=Default(Debug,False)
    VW=Width VH=Height
    /*  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")

    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":""
    }
    CNVS=CnvStr(c)
    Assert(CNVS!="",RT_String("%sRGB24, RGB32, YUY2, YV12, YV16, YV24,and Y8 Only",myName))
    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}
    FuncS="""
        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 Fn@@@(clip c,String DB,Int VW,Int VH,
                \ int X,int Y,int W,int H,Int Text_w,Int Text_h,Int Halo_L,Int Halo_T,Int Halo_R,Int Halo_B,
                \ Int Baffle_w,Int Baffle_h,Float Thresh_w,Float Thresh_h,Bool ReScan,
                \ clip dc,bool Show,int Stack,bool ShowArea,Int AreaColor,bool Debug) {
            c    n = current_frame
            Status = RT_DBaseGetField(DB,n,0)
            if(Show || Status <= 1) {                                                 # In here if Show or Status 0 or 1 (ie unvisited or Sub)
                if(Status==0) {                                                       # Unknown Status
                    Bingo = dc.RT_YInRangeLocate(Baffle_w=Baffle_w,Baffle_h=Baffle_h,
                        \ lo=255,hi=255,debug=Debug,Thresh_w=Thresh_w,Thresh_h=Thresh_h,ReScan=ReScan)
                    Status=(Bingo)?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(Show) {
                    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=AreaColor,width=SUB_W,height=SUB_H),x=SUB_X,y=SUB_Y,opacity=0.25)
                        RT_Subtitle("%d] Text @ x=%d y=%d w=%d h=%d",n,SUB_X,SUB_Y,SUB_W,SUB_H)
                    } else if(Status==3) {
                        RT_Subtitle("%d] User OverRide",n)
                    }
                }
            } # End (Show || Status)
            Return Last
        }
        #######################################
        # Unique Global Variables Initialization
        #######################################
        if(OverRide!="") {
            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))
                        }
                        (Debug)?RT_DebugF("OverRiding Range %d,%d to not detected",StartF,EndF,name=myName):NOP
                        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))
                }
            }
        }
        rgb=ConvertToRGB32.Crop(X,Y,W,H).ResetMask    # Reset Alpha to White
        Txt=rgb.ColorKeyMask(TextCol,TextVar+1,TextVar+1,TextVar+1).ShowAlpha(Pixel_Type="Y8")   # Set in range pixels to black
        Hal=rgb.ColorKeyMask(HaloCol,HaloVar+1,HaloVar+1,HaloVar+1).ShowAlpha(Pixel_Type="Y8")
        Txt=Txt.Invert  # In range pixels white, else black
        Hal=Hal.Invert

        CHROMAPROC = "-128"

        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)     {TXT=TXT.Mt_Expand(mode="square",                                   chroma=CHROMAPROC)
            } else {
                if(HorFlg==3)        {TXT=TXT.Mt_Expand(mode="horizontal",                               chroma=CHROMAPROC) HorFlg=0}
                if(VerFlg==3)        {TXT=TXT.Mt_Expand(mode="vertical",                                 chroma=CHROMAPROC) VerFlg=0}
                if(HorFlg!=0)        {TXT=TXT.Mt_Expand(mode=HorFlg==1? " 1  0  0  0" : " -1  0  0  0",  chroma=CHROMAPROC)         }
                if(VerFlg!=0)        {TXT=TXT.Mt_Expand(mode=VerFlg==1? " 0  1  0  0" : "  0 -1  0  0" , chroma=CHROMAPROC)         }
            }
        }

        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)    {HAL=HAL.Mt_Expand(mode="square",                                   chroma=CHROMAPROC)
            } else {
                if(HorFlg!=0)       {HAL=HAL.Mt_Expand(mode="horizontal",                               chroma=CHROMAPROC) }
                if(VerFlg!=0)       {HAL=HAL.Mt_Expand(mode="vertical",                                 chroma=CHROMAPROC) }
            }
        }

        # reduce strays a little
        WeeTxt=Txt
        WeeHal=Hal
        if(THysCNT>0) {
            For(i=1,THysCNT) {WeeTxt=WeeTxt.MT_Inpand(mode="both",chroma=CHROMAPROC)}
            Txt=WeeTxt.MT_Hysteresis(Txt,chroma=CHROMAPROC)
        }
        if(HHysCNT>0) {
            For(i=1,HHysCNT) {WeeHal=WeeHal.MT_Inpand(mode="both",chroma=CHROMAPROC)}
            Hal=WeeHal.MT_Hysteresis(Hal,chroma=CHROMAPROC)
        }

        dc=Mt_Logic(Txt,Hal,"and", chroma=CHROMAPROC)

        dc2=dc
        if(DHysCNT>0) {
            For(i=1,DHysCNT) {dc2=dc2.MT_Inpand(mode="both",chroma=CHROMAPROC)}
            dc=dc2.MT_Hysteresis(dc,chroma=CHROMAPROC)
            dc2 = (ShowInPand) ? dc2 : dc
        }

        if(Show && (Stack>=1 && Stack<=2)) {
            If(ShowInPand) {Txt=WeeTxt Hal=WeeHal}
            dc2=dc2.Eval(CnvS).AddBorders(X,0,Width-X-W,0).AddBorders(0,22,0,0,$C0C0C0).Subtitle("DETECT",y=2)
            if(Stack==2) {
                TXT = TXT.Eval(CnvS).AddBorders(X,0,Width-X-W,0).AddBorders(0,22,0,0,$C0C0C0).Subtitle("TEXT",y=2)
                HAL = HAL.Eval(CnvS).AddBorders(X,0,Width-X-W,0).AddBorders(0,22,0,0,$C0C0C0).Subtitle("HALO",y=2)
                c=StackVertical(Last,dc2,TXT,HAL)
            } else {
                c=StackVertical(Last,dc2)
            }
        } else {c=Last}
        if(Show&&ShowArea) {
            WW=(W+1)/2*2   HH=(H+1)/2*2    # YV12 Legal
            Msk=c.Blankclip(width=WW+2,height=HH+2,Length=1,pixel_type="YV12").Killaudio
            Infix=RT_string("(((x<=1|x>=%d|y<=1|y>=%d)&(x<4|x>=%d)&(y<4|y>=%d)|(x==0|x==%d|y==0|y==%d)))?255:0", W-2,H-2,W-4,H-4,W-1,H-1)
            Msk=Msk.mt_lutspa(relative = false,yExpr=mt_Polish(InFix), chroma = CHROMAPROC)
            Mrk=Msk.BlankClip(Color=AreaColor)
            Msk=Msk.Loop(FrameCount,0,0)    Mrk=Mrk.Loop(FrameCount,0,0)
            c=c.Overlay(Mrk,x=X-1,y=Y-1,Mask=Msk,Mode="Blend",Opacity=0.5)
        }
        RT_DBaseSetID(DB,0,c.FrameCount,c.FrameRate,VW,VH,X,Y,W,H)
        #######################################
        # Unique Runtime Call, GScriptClip must be a one-liner:
        #######################################
        ARGS="DB,VW,VH,X,Y,W,H,Text_w,Text_h,Halo_L,Halo_T,Halo_R,Halo_B,Baffle_w,Baffle_h,Thresh_w,Thresh_h,ReScan,dc,Show,Stack,Showarea,AreaColor,debug"
        c.GScriptClip("Fn@@@(last, "+ARGS+")", local=true, args=ARGS)
        return Last
    """
    #######################################
    # Unique Identifier Definition
    #######################################
    GIFunc="DetectSub_MI"             # Function Name, Supply unique name for your multi-instance function.
    GIName=GIFunc+"_InstanceNumber"    # Name of the Instance number Global
    RT_IncrGlobal(GIName)              # Increment Instance Global (init to 1 if not already exists)
    GID   = GIFunc + "_" + String(Eval(GIName))
    InstS = RT_StrReplace(FuncS,"@@@","_"+GID)
#   RT_WriteFile("DEBUG_"+GID+".TXT","%s",InstS)
    (!IsPlus) ? GEval(InstS) : Eval(InstS)
    (!UserDB)?CallCmd(close=RT_String("""CMD /C chcp 1252 && del "%s" """,DB), hide=true, Synchronous=7):NOP  # Auto delete non-User DB file on clip closure.
    Return Last
}
__________________
I sometimes post sober.
StainlessS@MediaFire ::: AND/OR ::: StainlessS@SendSpace

"Some infinities are bigger than other infinities", but how many of them are infinitely bigger ???
StainlessS is offline   Reply With Quote
Old 14th August 2021, 03:11   #16  |  Link
StainlessS
HeartlessS Usurer
 
StainlessS's Avatar
 
Join Date: Dec 2009
Location: Over the rainbow
Posts: 10,980
post #2 of 2

Client,
DetectSub_MI_Client.avs
Code:
###
# DetectSub.MI_Client.avs"
###

Import("DetectSub_MI.avsi")

HARDFN="hard sub - 01 WEBdlRip 720p, 23.976.mkv.AVI"  # converted to AVI via ffmpeg
HARDFN=HARDFN.RT_GetFullPathName

AVISource(HARDFN)

# Subtitle coords
X        = 40
Y        = Height-140
W        = -X
H        = -8
# Text, Halo color
TEXTCOL   = $F3F3F3
TEXTVAR   = 12                             # Text RGB channels can vary by as much as TEXTVAR, eg $FEFEFE with TEXTVAR=1 ranges $FDFDFD -> $FFFFFF
HALOCOL   = $092943
HALOVAR   = 9                             # Halo RGB channels can vary by as much as HALOVAR, eg $FEFEFE with HALOVAR=1 ranges $FDFDFD -> $FFFFFF
# Text pixel width,height
TEXT_W    = 2                             # minimum text pixel 'thickness' Horizontal (horizontal thickness of vertical strokes)
TEXT_H    = 2                             # 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   = 1                             # Text Mask Mt_Hysteresis inpand count (prior to mt_hysteresis), Dont Mt_Hysteresis if 0
HHYSCNT   = 1                             # Halo Mask Mt_Hysteresis inpand count (prior to mt_hysteresis), Dont Mt_Hysteresis if 0
DHYSCNT   = 1                             # Detect clip Mt_Hysteresis inpand count (prior to mt_hysteresis), Dont Mt_Hysteresis if 0
### Subs Locator###
/*
    RT_YInRangeLocate(), scans all four sides looking for (in this case) white pixels in the Detect Mask clip.
     Function to scan frame for an area where percentage of pixels in luma range Lo to Hi (inclusive) breaks a threshold over at
     least Baffle consecutive number of scanlines (all four sides). In this script case, lo and hi are both 255, ie white pixels.

     RT_YInRangeLocate tests scanlines from outer most scanlines (h and v), towards inner most, stops where it finds Baffle consecutive scanlines
     that contain more than Thresh percent of pixels in range Lo to Hi. Object interiors are not tested and so could be hollow.
     Could also give +ve result for a disjoint shape like this
       --
      |  |
       --
     Args:
      Baffle: Default=8, minimum number of scanlines that must have a percentage of pixels in range Lo to Hi, greater than Thresh. Avoids noise.
      Thresh: Default=0.0 (0.0->100.0). Percentage pixels above which breaks Threshold. Default is any pixel in the range Lo to Hi will break threshold.
      Baffle_W, Default Baffle. Thickness of Left and Right side vertical edges, silently limited to width W of search area.
      Baffle_H, Default Baffle. Thickness of Top and Bottom horizontal edges, silently limited to height H of search area.
      Script Specific:::
        RT_Stats v2.00 Beta 02, Added args Thresh_w and Thresh_h, and Bool ReScan.
        Say we are scanning to find top horizontal edge. We will scan and count number of white pixels in a horizontal scanline.
        If this percenage count EXCEEDS Thresh_w, and the total number of similarly exceeding consecutive scanlines exceeds Baffle_h
        (NOTE Baffle_h NOT Baffle_w, although scanlines are horizontal, we are counting the thickness of consecutive lines vertically)
        then we have found the top edge. Same thing for bottom edge using same variables Thesh_w and Baffle_h.
        Scanning vertical edges uses Thresh_h and Baffle_w.
           NOTE, for RT_Stats v2.00 Beta 02, you can also supply eg Thresh_w = -12 (minus) which is taken as a pixel count of +12, rather
        than as a percentage. This Thresh_x -ve pixel count will have a teeny weeny bit subtracted before use, as the pixel count must be
        greater (something like Thresh_w=Abs(Thresh_w)- 1.0/scanline_width). NOTE, the RT_YInRangeLocator() default of 0.0 detects more
        than 0.0 pixels in scanline percentage count, ie any pixel of 255 (in this script case) breaks the threshold and counted against
        baffle.
        The new Bool ReScan arg, forces a horizontal edge (top and bottom) rescan if a new vertical edge is found, and a vertical edge (left and right)
        rescan if a new horizontal edge is found. If top and bottom scan broke threshold and passed baffle, followed by left and right scans doing
        the same, the top and bottom pixel objects that caused the detections could well have 'moved' outside of the detected area, ie left and right
        edges moving inwards may leave those objects outside because in the left/right scan they did not satisfy thresh and baffle requirements,
        and detection will likely be bad. Rescan forces a further scan until all four sides scanned sides are a detection (or not). (Will not be
        any great overhead if the initial detect was good as it will just recan the same scanlines accessed immediately prior). The internal routine
        that RT_YInRangeLocate() uses, returns percentage of pixels that are in the required range, each time an edge is detected, the number of
        required pixels is recalculated as a percentage of the reduced scanline lengths (whether given as percentage, or as a -ve pixel count).

*/
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).
#
SHOW      = true                          # Show metrics.
AREACOLOR = $8040FF                       # Subtitle area marker color
SHOWAREA  = true                          # Set True to mark subtitle scan area.  (Show=False:No Effect)
STACK     = 2                             # 0=Movie, 1=Movie + detection clip, 2=Movie  detection clip + Text and Halo Mask clips. (Show=False:No Effect)
SHOWINPAND = false                        # Show Mt_Inpand masks without doing Mt_Hysteresis, ie for THysCnt, HHysCnt, DHysCnt. (Show=False:No Effect)
DB        = "MyDB.DB"                     # If Named, then DB not deleted on exit. (Maybe DBase used by other scripts later)
DEBUG     = false                         # To DebugView (Google). RT_ v2.00 beta02 extends RT_YInRangeLocate() debug info a little.
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.
    """
*/

DetectSub_MI(Last,Override=Override,
    \ x=X,y=Y,w=W,h=H,
    \ Text_w=TEXT_W,Text_h=TEXT_H,TextCol=TEXTCOL,TextVar=TEXTVAR,
    \ Halo_L=HALO_L,Halo_T=HALO_T,Halo_R=HALO_R,Halo_B=HALO_B,HaloCol=HALOCOL,HaloVar=HALOVAR,
    \ THysCnt=THYSCNT,HHysCnt=HHYSCNT,DHysCnt=DHYSCNT,ShowInPand=SHOWINPAND,
    \ Baffle_w=BAFFLE_W,Baffle_h=BAFFLE_H,Thresh_w=THRESH_W,Thresh_h=THRESH_H,ReScan=RESCAN,
    \ Show=SHOW,stack=STACK,ShowArea=SHOWAREA,AreaColor=AREACOLOR,db=DB,Debug=DEBUG)

Return Last
Result with detections shown [click me, and click a 2nd time for zoom full size]

Above , clip overlayed with purple block where subs detected [purple wire frame marks search area].
Below the clip, [from bottom up], Halo, Text, and Detect, masks shown. Detect is result from which overlayed purple block was produced.

I had to stop before finishing repair script, as thread had suggestion of rule break:- https://forum.doom9.org/showthread.p...36#post1782036

For the repair script to work [if ever finished] then would require nosubs clip to be EXACTLY SAME SIZE & FRAMERATE as hardsubs clip.

EDIT:
We'll see if we can output a mask for VoodooFX delogo whotsit, if VoodooFX thinks it necessary [and tells me what he wants].
__________________
I sometimes post sober.
StainlessS@MediaFire ::: AND/OR ::: StainlessS@SendSpace

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

Last edited by StainlessS; 14th August 2021 at 05:06.
StainlessS is offline   Reply With Quote
Old 14th August 2021, 18:28   #17  |  Link
Shinkiro
Registered User
 
Join Date: Dec 2012
Posts: 65
Quote:
Originally Posted by VoodooFX View Post
Thx for samples, version tuned for subtitles & command to get a dynamic mask you'll find there: https://forum.doom9.org/showthread.php?p=1949752
Wow cool, in the future I will use your method, but now I went another way, I found softrsub of the same release and picked up the right fonts then made a mask using them
Code:
MaskSub(file="SoftSub.ass", width=1280, height=720, fps=23.976, length=39536).FlipVertical().FastBlur(1.4)




Quote:
Originally Posted by VoodooFX View Post
But you won't be able to get the good results with your video, as subbed sample has wonky decimation, so I dunno how you'll align both videos frame to frame.
Plus, levels are shifted, bright areas are darker when dark areas are brighter in nonsubbed video, but that's a minor problem.
Yes you're right, I noticed this problem too, Webdl has 23,976 fps while the DVD is actually VFR. In those scenes where DVD has 23,976 no problems with synchronization, but where there are real 29,970 appear inconsistency frames, I've tried different modes TDecimate, but it always removes frames not the way they were removed in WebdlRip, need some kind of manual way to select the algorithm for removing unnecessary frames
__________________
Ryzen 2700x | ASUS ROG Strix GTX 1080 Ti | 16 Gb DDR4
Windows 10 x64 20H2
KD-55XE9005 | Edifier R2800
Shinkiro is offline   Reply With Quote
Old 14th August 2021, 18:56   #18  |  Link
Shinkiro
Registered User
 
Join Date: Dec 2012
Posts: 65
Quote:
Originally Posted by StainlessS View Post
Result with detections shown [click me, and click a 2nd time for zoom full size]

Above , clip overlayed with purple block where subs detected [purple wire frame marks search area].
Below the clip, [from bottom up], Halo, Text, and Detect, masks shown. Detect is result from which overlayed purple block was produced.

I had to stop before finishing repair script, as thread had suggestion of rule break:- https://forum.doom9.org/showthread.p...36#post1782036
Thank you, I also saved this option for myself, I'll try how it works.
Quote:
Originally Posted by StainlessS View Post
For the repair script to work [if ever finished] then would require nosubs clip to be EXACTLY SAME SIZE & FRAMERATE as hardsubs clip.
Logically. but as always, what we have does not fit into the framework of an ideal world
__________________
Ryzen 2700x | ASUS ROG Strix GTX 1080 Ti | 16 Gb DDR4
Windows 10 x64 20H2
KD-55XE9005 | Edifier R2800

Last edited by Shinkiro; 14th August 2021 at 19:04.
Shinkiro is offline   Reply With Quote
Old 14th August 2021, 22:18   #19  |  Link
kedautinh12
Registered User
 
Join Date: Jan 2018
Posts: 2,153
Thank Shinkiro, but where you got function masksub()???

Last edited by kedautinh12; 14th August 2021 at 22:31.
kedautinh12 is offline   Reply With Quote
Old 14th August 2021, 22:21   #20  |  Link
Reel.Deel
Registered User
 
Join Date: Mar 2012
Location: Texas
Posts: 1,664
Quote:
Originally Posted by kedautinh12 View Post
Thank Shinkiro, but where you get function masksub()???
Google is your friend: http://avisynth.nl/index.php/Xy-VSFilter#MaskSub

MaskSub has been around for a very long time, starting with the original VSFilter.
Reel.Deel is offline   Reply With Quote
Reply

Thread Tools Search this Thread
Search this Thread:

Advanced Search
Display Modes

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off

Forum Jump


All times are GMT +1. The time now is 23:12.


Powered by vBulletin® Version 3.8.11
Copyright ©2000 - 2024, vBulletin Solutions Inc.