View Single Post
Old 23rd December 2007, 05:46   #14  |  Link
Spuds
dumber every day
 
Spuds's Avatar
 
Join Date: Dec 2006
Location: Planet Earth
Posts: 154
The current state of the function after my torture has been applied.

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)
}

Last edited by Spuds; 25th December 2007 at 15:51.
Spuds is offline   Reply With Quote