Code:
# Delogo
# A wrapper function to help use virtualdub delogo in avisynth with some added avisynth processing for improved results. This avisynth
# script builds upon the virtualdub delogo 1.32 filter by doing a deblend and repair, allowing you mix these two
# outputs (via cmix/lix) for better results. Careful mask making of the original logo is essential
# History:
# 2006-12-15 First English translation by jmac698
# 2007-03-25 Ad-hoc Modification for Eastermeyer by Didee (Post processor changes)
# 2007-04-07 Lwidth/LHeight mod4 improvement from manolito, Lmix clarification from Didée, doc updates from jmac698
# 2007-12-20 masktools v2, setup/framenumber function, analyse bmp creation, par updates in vd_delogo,
# overall comments and code structure
#
# Pre-requisites:
# - Avisynth filters: MaskTools v.2, RemoveGrain, MedianBlur (PP=1), FFT3DFilter (PP=2), AddGrain, GradFun2db, ttempsmoothf, temporalsoften
# - Virtualdub filters: delogo
# - Other: bmp files as created with virtualdub delogo
#
FUNCTION delogo( clip clp, int Lwidth, int Lheight, string loc, string name, \
bool "debug", int "par_x", int "par_y", \
float "Cmix", float "Lmix", string "name2", bool "i", int "PP",float "amount", bool "lmask", \
bool "setup", int "framenumber", int "thres", \
bool "serve", int "SnipSize", int "avg")
{
delogo_location = "C:\Program Files\AviSynth 2.5\plugins\virtualdub\" # directory location of the needed virtualdub filter file
# Parameters used to find a clean logo frame and output a starting repair mask
setup = default( setup,false ) # use after you have the logo located, this will create some initial bmp's that you need for delogo
thres = default( thres,190 ) # luma threshold to attempt logo mask creation.
framenumber = default( framenumber,0 )
# Parameters used to frameserve to virtualdub for use in delogo all frames search, needed for transparent logos
serve = default( serve,false ) # use after you have the logo repair bmp created, used to build masks for transparent logos in vdub
SnipSize = default( SnipSize, 56 ) # one out of every SnipSize frames will be returned for searching
avg = default( avg, 3 ) # number of frames to average with temporalsoften. Useful for denoising the logo clip.
# Parameters used in this script
debug = default( debug,false) # show call to vd_delogo to help in debuging on with show and setup = false
par_x = default( par_x,1 ) # pixel aspect ratio X
par_y = default( par_y,1 ) # pixel aspect ratio y
PP = default( PP,2 ) # Post process function 0,1,2 to help reduce damage left behind by logo removal
lmask = default( lmask, true ) # apply post process through a simple mask
amount = default( amount,1.0 ) # how much noise to blend into the removal area to help hide artifacts 1.0 = keep 100 of logo, .1 = 10% logo 90% noise
Lmix = default( Lmix,0.0 ) # Luma blending of results
Cmix = default( Cmix,0.0 ) # Chroma blending of results
name2 = default( name2,"" ) # optional logo mask, needs to be a mask that covers the logo and feathers away from the logo
logomask = ( name2 == "" ) ? clp : imagereader(name2).loop().trim(0,framecount(clp)).converttoyv12(matrix="PC.601")
i = default( i,false ) # Interlaced video true/false
# seperate out the directory and logo name, may not need it but ....
sl = name.revstr().findstr("\") - 1
Assert((sl >= 0),"specify a fully qualified directory and logo name to use")
logo_name = (sl < 0 ) ? "" : rightstr(name,sl)
logo_dir = (sl < 0) ? "" : leftstr(name,strlen(name)-sl)
# based on clip size and user input, cut off a corner that contains the logo, using this small area will speed up processing
Lwidth = m4(Lwidth)
Lheight = m4(Lheight)
loc = ucase(loc)
ox = clp.width
oy = clp.height
x1 = (LeftStr(loc,1)=="L") ? 0 : ox-Lwidth
y1 = (RightStr(loc,1)=="O" || RightStr(loc,1) == "T") ? 0 : oy-Lheight
logo = clp.crop(x1,y1,Lwidth,Lheight)
# retain the rest of the clip of re-integration later.
row = (x1 == 0) ? (y1 == 0) ? clp.crop(Lwidth,0,-0,Lheight) : clp.crop(Lwidth,oy-Lheight,-0,-0) \
: (y1 == 0) ? clp.crop(0,0,x1,Lheight) : clp.crop(0,oy-Lheight,x1,-0)
rest = (y1 == 0) ? clp.crop(0,Lheight,-0,-0) : clp.crop(0,0,-0,oy-Lheight)
logo_rgb = ConvertToRGB(logo)
# If asked show the crop, useful to help zero in on the logo
(setup) ? prepare_Logo(logo,logo_dir,logo_name,framenumber,i, thres) : \
(serve) ? ServeLogo( clp, Lwidth, Lheight, loc, SnipSize, avg, i ) : \
remove_logo(logo_rgb, logo, Lwidth, Lheight, loc, name, logo_dir, logo_name, Cmix, Lmix, name2, i, PP, amount, \
delogo_location, debug, par_x, par_y, lmask)
# re-integrate our delogo-ed corner with our original clip
row2 = (x1 == 0 && setup == false && debug == false && serve == false) ? stackhorizontal(last,row) \
: (setup == false && debug == false && serve == false) ? stackhorizontal(row,last) : nop()
(y1 == 0 && setup == false && debug == false && serve == false) ? stackvertical( row2,rest) \
: (setup == false && debug == false && serve == false) ? stackvertical( rest,row2) : last
RETURN ( last )
}
FUNCTION remove_logo(clip logo_rgb, clip logo, int Lwidth, int Lheight, string loc, string name, string "logo_dir", string "logo_name", \
float "Cmix", float "Lmix", string "name2", bool "i", int "PP",float "amount", string "delogo_location", \
bool "debug", int "par_x", int "par_y", bool "lmask")
{
# set up our delogo names,
alpha_file = logo_name + "_alpha.bmp"
color_file = logo_name + "_color.bmp"
repair_file = logo_name + "_repair.bmp"
deblend_file = logo_name + "_deblend.bmp"
repair_c_file = logo_name + "_repair_c.bmp"
# Set up to make the call to delogo, logodir can be either the supplied name or the supplied logo_dir
log1 = logo_rgb.VD_DeLogo_Mod(false, "", logo_dir + deblend_file, logo_dir + alpha_file, logo_dir + color_file, logo_dir + repair_file, 1.5, 3.0, par_x, par_y, i, delogo_location, debug).ConvertToYV12(interlaced=i)
log2 = (Cmix==0.0 && Lmix==0.0) ? log1 \
: logo_rgb.VD_DeLogo_Mod(false, "", logo_dir + deblend_file, logo_dir + alpha_file, logo_dir + color_file, logo_dir + repair_c_file, 2.5, 3.0, par_x, par_y, i, delogo_location, debug).ConvertToYV12(interlaced=i)
#logo=logo.ConvertToYV12(interlaced=i)
# post processing and masking of post results if requested
postmask = (pp > 0 && lmask) ? mt_lut(mt_edge(log1,mode="min/max",thy2=190).mt_expand.mt_expand.mt_inflate,"x 35 > 0 x ?").mt_invert : blankclip(log1,color=$000000)
post = ( PP == 1 ) ? log1.minblur(1,uv=2).medianblur(3,0,0).removegrain(11) \
: ( pp == 2 ) ? log1.fft3dfilter(sigma=16,sigma2=12,sigma3=8,sigma4=4,bt=3,bw=16,bh=16,ow=8,oh=8,degrid=1) \
: ( pp == 3 && i ) ? log1.mt_convolution("1 8 28 56 76 56 28 8 1","1 0 28 0 76 0 28 0 1",y=3,v=2,u=2) \
: ( pp == 3 ) ? log1.mt_convolution("1 8 28 56 76 56 28 8 1","1 8 28 56 76 56 28 8 1",y=3,v=2,u=2) \
: log1
( pp > 0 ) ? mt_merge(log1,post,postmask) : log1
# Determine how we are mixing in the chroma and luma of a repair and a deblend call to delogo, skip if only one call was made.
LL = string(int(round(Lmix)))
LL1 = string(int(round(Lmix*100.0)))
( Cmix == 0.0 ) ? last : MergeChroma(last,log2,Cmix)
( Lmix == 0.0 ) ? last \
: (Lmix > 1.0) ? mt_lutxy(last,log2,yexpr="x y - abs " + LL + " <= y x y " + LL + " + > x " + LL + " - x y " + LL + " - < x " + LL + " + y ? ? ?",U=2,V=2) \
: mt_lutxy(last,log2,yexpr="x 100 " + LL1 + " - * y " + LL1 + " * + 100 /",U=2,V=2)
# we have now removed, as best we can, the logo via repair and deblend. Now lets "repair" the delogoed area
gradfun2db(3.4)
( pp > 0 ) ? temporalsoften(2,11,11,23,2) : last
# a touch of noise can hide wonders :)
noise = blankclip(logo,color_yuv=$808080).bilinearresize(m4(Lwidth/2.2),m4(Lheight/2.2)).addgrain(4,0,0).gaussresize(Lwidth,Lheight,p=35)
( debug ) ? nop() : mt_adddiff(last,noise,U=2,V=2)
# Extra masking application
( name2 == "" || debug ) ? last : mt_merge(logo,last,logomask.FitY2UV(),Y=3,U=3,V=3)
# how much of our work to keep?
( amount == 1.0 || debug ) ? last : mt_lutxy(last, logo, expr="x"+" "+string(amount)+" "+"* y "+" "+string(1.0-amount)+" * +",y=3,u=3,v=3)
RETURN ( last )
}
FUNCTION m4(float x)
{
# helper function to ensure MOD4 and atleast 16
x < 16 ? 16 : int(round(x/4.0)*4)
}
FUNCTION MinBlur(clip input, int r, int "uv")
{
# Nifty Gauss/Median combination
# Taken from MCBob.avs:
uv = default(uv,3)
# process chroma if uv==3, otherwise just luma
uv2 = (uv==2) ? 1 : uv
rg4 = (uv==3) ? 4 : -1
rg11 = (uv==3) ? 11 : -1
rg20 = (uv==3) ? 20 : -1
medf = (uv==3) ? 1 : -200
# make our blur clips, r controls amount
RG11D = (r==1) ? mt_makediff(input,input.removegrain(11, rg11),U=uv2,V=uv2)
\ : (r==2) ? mt_makediff(input,input.removegrain(11,rg11).removegrain(20,rg20),U=uv2,V=uv2)
\ : mt_makediff(input,input.removegrain(11,rg11).removegrain(20,rg20).removegrain(20,rg20),U=uv2,V=uv2)
RG4D = (r==1) ? mt_makediff(input,input.removegrain(4,rg4),U=uv2,V=uv2)
\ : (r==2) ? mt_makediff(input,input.medianblur(2,2*medf,2*medf),U=uv2,V=uv2)
\ : mt_makediff(input,input.medianblur(3,3*medf,3*medf),U=uv2,V=uv2)
DD = mt_lutxy(RG11D,RG4D,"x 128 - y 128 - * 0 < 128 x 128 - abs y 128 - abs < x y ? ?",U=uv2,V=uv2)
RETURN (input.mt_makediff(DD,U=uv,V=uv))
}
FUNCTION ServeLogo( clip clp, int Lwidth, int Lheight, string loc, int "SnipSize", int "avg", bool "i" )
{
# ServeLogo. Used to produce a clip showing only our logo. Open this clip in virtualdub, add the delogo filter, and process from there.
# You must use the same lwidth/lheight/corner parameters in all calls to delogo functions.
# to use:
# import("delogo.avs") mpeg2source(...) serverlogo(lwidth,lheight,"loc"), save as logoserve.avs, open in virtualdub,add filter
# delogo 1.32, use delogo guide to continue (configure, show preview, save analyze.bmp, color red parts.. etc.)
# Next steps: import("delogo.avs"), mpeg2source(..),delogo(...)
SnipSize = default( SnipSize, 56 ) # one out of every SnipSize frames will be returned
avg = default( avg, 3 ) # number of frames to average with temporalsoften. Useful for denoising the logo clip.
Lwidth = m4(Lwidth)
Lheight = m4(Lheight)
# based on clip size and user input, crop a corner that contains the logo, using only this small section will speed up processing
# loc is the corner location of interest.
loc = ucase(loc)
x1 = (LeftStr(loc,1) == "L") ? 0 : clp.width - Lwidth
y1 = (RightStr(loc,1) == "O" || RightStr(loc,1) == "T") ? 0 : clp.height - Lheight
clp.crop(x1,y1,Lwidth,Lheight)
SelectRangeEvery( SnipSize, 1 )
# RETURN TemporalSoften( avg,256,256,255,2 )
RETURN TTempSmoothF(maxr=avg,lthresh=256,cthresh=256,scthresh=255,interlaced=i)
}
FUNCTION prepare_logo(clip logo,string logo_dir,string logo_name, int "framenumber", bool "i", int "thres")
{
logo = (defined(framenumber) && framenumber > -1) ? trim(converttorgb(logo),framenumber,framenumber).converttoyv12(interlaced=i,matrix="pc.601") : logo.converttoyv12(interlaced=i)
framenumber = (defined(framenumber)) ? framenumber : -1
# Set up some backgrounds for us to use in creating masks
redclip = blankclip(logo,length=1,color=$ff0000)
blackclip = blankclip(logo,length=1,color=$000000)
whiteclip = blankclip(logo,length=1,color=$ffffff)
outputclip = whiteclip++logo
# our assumption is that we have a nice shot of the logo on a dark background, two simple attempts at masking the logo
# in red for a starter in paintshop
logo_mask = mt_lut(logo,expr="x "+" "+string(thres)+" "+"< 0 255 ?").mt_binarize(threshold=thres,upper=false)
logo_outline1 = mt_merge(logo,redclip,logo_mask,luma=true)
logo_mask = mt_edge(logo,mode="min/max",thy2=thres).mt_expand.mt_binarize(threshold=thres,upper=false)
logo_outline2 = mt_merge(logo,redclip,logo_mask,luma=true)
# write out the files, be sure to use 0-255 color range
atemp=(framenumber > -1) ? ImageWriter(logo_outline1.ConvertToRGB24(matrix="pc.601").Levels(16, 1, 235, 0, 255, coring=false), file=logo_dir+logo_name+"1_", start=0,end=0,type="bmp") : nop()
(framenumber > -1) ? overlay(logo,atemp,opacity=0) : nop()
btemp=(framenumber > -1) ? ImageWriter(logo_outline2.ConvertToRGB24(matrix="pc.601").Levels(16, 1, 235, 0, 255, coring=false), file=logo_dir+logo_name+"2_", start=0,end=0,type="bmp") : nop()
(framenumber > -1) ? overlay(logo,btemp,opacity=0) : nop()
ctemp=(framenumber > -1) ? ImageWriter(outputclip.ConvertToRGB24(matrix="pc.601").Levels(16, 1, 235, 0, 255, coring=false), file=logo_dir+logo_name+"_", start=0,end=1,type="bmp") : nop()
(framenumber > -1) ? overlay(logo,ctemp,opacity=0) : nop()
(framenumber > -1) ? stackhorizontal(atemp,btemp,ctemp) : logo
RETURN (last)
}
FUNCTION VD_DeLogo_Mod(clip clip, bool "on_frames", string "range", string "file_deblend", string "file_alpha", string "file_color", \
string "file_repair", float "depth", float "power", int "par_X", int "par_Y", bool "interlaced", string "delogo_location", bool "debug")
{
# range = "100-200, 300-400"; or whole clip range = ""
# pixel aspect ratio: par_X (1-16), par_Y (1-16)
# depth (1.0 - 8.0), power (0.0 - 16.0)
LoadVirtualdubPlugin(delogo_location + "delogo.vdf", "_VD_DeLogo")
X = round(10 * depth)
Y = round(10 * power)
debug = default(debug,false)
# Theoretically: z = 100*log10(par_X/par_Y)
z = round(100*log(float(par_X)/float(par_Y)) / log(10))
# Make the call to delogo via the virtualdub interface or provide debug info
(debug) ? blankclip(clip,width=640,height=480,color=$000000).subtitle("On Frames "+string(on_frames),y=1,size=14).subtitle("Range "+string(range),y=11,size=14) \
.subtitle(file_deblend,y=21,size=14).subtitle(file_alpha,y=31,size=14).subtitle(file_color,y=41,size=14).subtitle(file_repair,y=51,size=14) \
.subtitle("Depth "+string(x),y=61,size=14).subtitle("Power "+string(y),y=71,size=14).subtitle("Interlaced "+string(interlaced),y=81,size=14) \
.subtitle("Z "+string(z),y=91,size=14) \
: clip._VD_DeLogo(default(on_frames,false) ? 1 : 0, default(range,""), default(file_deblend,""), default(file_alpha,""), \
default(file_color,""),default(file_repair,""), default(X,15), default(Y,40), default(interlaced,false) ? 1 : 0, z)
RETURN (last)
}