###################################################
###                                             ###
###                 LumaDBLite                  ###
###                                             ###
###       by 06_taro - astrataro@gmail.com      ###
###                                             ###
###             v0.7 - 06 April 2012            ###
###                                             ###
###################################################
###
### Fast luma debanding filter in 16-bit precision
###
### Original used by tp7, scripted by 06_taro as LumaDB, implemented 16-bit precision in LumaDBLite
### 
###
### +---------+
### |  USAGE  |
### +---------+
###
### !!!Add "SetMemoryMax(1024)" at the beginning of your script if you find it extremely slow, usually happening on high resolution processing!!!
###
### 8-bit input, 8-bit output, using internal dither down method
### LumaDBL()
### 
### 8-bit input, 16-bit output
### LumaDBL(lsb=true)
### 
### 16-bit input, 16-bit output
### LumaDBL(lsb=true, lsb_in=true)
### 
### 16-bit input, 8-bit output, using internal dithering down method
### LumaDBL(lsb_in=true)
### 
### 16-bit input, 8-bit output, using self defined dithering f3kdb_dither
### LumaDBL(lsb_in=true, ditherC="f3kdb_dither()")
###
### See below for explanation of parameters
###
### +----------------+
### |  REQUIREMENTS  |
### +----------------+
###
### -> AddGrainC         [v1.6.1]
### -> MaskTools2       [v2.0a48]
### -> dither           [v1.15.0]
### -> RemoveGrain      [v1.0pre]
### -> TEdgeMask           [v0.9]
### -> GrainFactoryLite    [v1.2]
### -> f3kdb             [v1.5.0]
### -> f3kgrain            [v0.3]
###
### +-----------+
### | CHANGELOG |
### +-----------+
###
### v0.7 - 06 April 2012
###      - Doc: added support for generating f3kdb based grain ( set negative value for g*str to use f3kdb based grain )
###
### v0.6-1 - 27 March 2012
###        - Updated required version of dither package to 1.15.0, which fixed a regressive bug in Dither_resize16
###
### v0.6 - 13 March 2012
###      - Added "mclip" to define custom edge mask
###      - Added chroma grain support
###      - Changed "nop" to "none" or "nop", the former is LumaDB style
###      - Added some noise in final dithering down process by defualt when lsb=false
###        otherwise the dither/grain generated by LumaDBL in 16-bit precision may sometimes be ruined, giving you a worse result than LumaDB
###      - Synchronized version number with LumaDB
###
### v0.2 - 01 October 2011
###      - Fixed: wrong "g*soft" type
###      - Added: solution to slow processing in document
###
### v0.1 - 26 September 2011
###      - First script
###

Function LumaDBL( clip   input,
                \ float  "thr",      int   "radius",   float "ampn",
                \ float  "g1str",    float "g2str",    float "g3str",
                \ float  "g1cstr",   float "g2cstr",   float "g3cstr",
                \ int    "g1shrp",   int   "g2shrp",   int   "g3shrp",
                \ float  "g1size",   float "g2size",   float "g3size",
                \ int    "g1soft",   int   "g2soft",   int   "g3soft",
                \ int    "gth1",     int   "gth2",     int   "gth3",     int  "gth4",
                \ bool   "mask",     int   "gf3mask",       
                \ bool   "preblur",  bool  "deflate",  bool  "blur",
                \ bool   "chroma",   bool  "mluma",         
                \ string "dither",   bool  "d_lsb",    bool  "d_lsb_in",
                \ string "grain",    bool  "g_lsb",    bool  "g_lsb_in",
                \ bool   "lsb",      bool  "lsb_in", 
                \ clip   "mclip",                    
                \ int    "ditherD",  float "ampoD",    float "ampnD",
                \ bool   "ddDyn",    bool "gdDyn",
                \ string "ditherC"  )
{  
  
  #-----------------------------------------------------------------------------------
  
  lsb_in   = Default( lsb_in,   false )  # Input clip is 16-bit stacked or not
  lsb      = Default( lsb,      false )  # Output clip is 16-bit stacked or not
  
  thr      = Default( thr,       0.35 )  # GradFun3 "thr" parameter
  ampn     = Default( ampn,         0 )  # GradFun3 "ampn" parameter
  
  g1str    = Default( g1str,       12 )  # [ 0 - ???] strength of luma grain / for dark areas, the strength of this value is different from 8-bit LumaDB
                                         # positive value for add_grain16 strength, negative value for f3kgrain strength
  g2str    = Default( g2str,        6 )  # [ 0 - ???] strength of luma grain / for midtone areas, the strength of this value is different from 8-bit LumaDB
                                         # positive value for add_grain16 strength, negative value for f3kgrain strength
  g3str    = Default( g3str,        0 )  # [ 0 - ???] strength of luma grain / for bright areas, the strength of this value is different from 8-bit LumaDB
                                         # positive value for add_grain16 strength, negative value for f3kgrain strength
  
  g1cstr   = Default( g1cstr,       0 )  # [ 0 - ???] strength of chroma grain / for dark areas, the strength of this value is different from 8-bit LumaDB
                                         # positive value for add_grain16 strength, negative value for f3kgrain strength
  g2cstr   = Default( g2cstr,       0 )  # [ 0 - ???] strength of chroma grain / for midtone areas, the strength of this value is different from 8-bit LumaDB
                                         # positive value for add_grain16 strength, negative value for f3kgrain strength
  g3cstr   = Default( g3cstr,       0 )  # [ 0 - ???] strength of chroma grain / for bright areas, the strength of this value is different from 8-bit LumaDB
                                         # positive value for add_grain16 strength, negative value for f3kgrain strength
  
  g1shrp   = Default( g1shrp,      60 )  # [ 0 - 100] sharpness of grain / for dark areas (NO EFFECT when g1size=1.0 !!)
  g2shrp   = Default( g2shrp,      66 )  # [ 0 - 100] sharpness of grain / for midtone areas (NO EFFECT when g2size=1.0 !!)
  g3shrp   = Default( g3shrp,      80 )  # [ 0 - 100] sharpness of grain / for bright areas (NO EFFECT when g3size=1.0 !!)
  
  g1size   = Default( g1size,    0.90 )  # [0.5 - 4.0] size of grain / for dark areas
  g2size   = Default( g2size,    0.70 )  # [0.5 - 4.0] size of grain / for mid-tone areas
  g3size   = Default( g3size,    0.60 )  # [0.5 - 4.0] size of grain / for bright areas
  
  g1soft   = Default( g1soft,      20 )  # Noise temporal stability of grain / for dark areas, completely static if set to 100 or above
  g2soft   = Default( g2soft,      40 )  # Noise temporal stability of grain / for midtone areas, completely static if set to 100 or above
  g3soft   = Default( g3soft,      60 )  # Noise temporal stability of grain / for bright areas, completely static if set to 100 or above
  
  gth1     = Default( gth1,        24 )  # Start of dark->midtone   mixing zone as defined in 8-bit precision
  gth2     = Default( gth2,        56 )  # End   of dark->midtone   mixing zone as defined in 8-bit precision
  gth3     = Default( gth3,       128 )  # Start of midtone->bright mixing zone as defined in 8-bit precision
  gth4     = Default( gth4,       160 )  # End   of midtone->bright mixing zone as defined in 8-bit precision
  
  mask     = Default( mask,     true )  # Indicates whether to use edge mask to protect edges
  gf3mask  = Default( gf3mask,     0 )  # Indicates whether to use GradFun3's internal mask
  
  preblur  = Default( preblur,   true )  # Indicates whether to apply a 3x3 guassian blur to the input image prior to generating the edge map
  deflate  = Default( deflate,   true )  # Indicates whether to apply a deflation to the mask clip
  blur     = Default( blur,      true )  # Indicates whether to apply a blur using RemoveGrain to the mask clip
  
  chroma   = ( g1cstr == 0 ) && ( g2cstr == 0 ) && ( g3cstr == 0 ) ? Default( chroma, false )
  \                                                                : Default( chroma,  true ) # Indicates whether to process chroma or not
  mluma    = Default( mluma,   false )  # Indicate whether to use luma edge mask to process uv plane (read mt_merge's doc of "luma" for more details)
  
  ###### "dither"  : custom debanding method in string, "nop" for no processing
  d_lsb    = Default( d_lsb,    false )  # custom debanding method outputs 16-bit stacked clip or not
  d_lsb_in = Default( d_lsb_in, false )  # custom debanding method accepts 16-bit stacked clip or not
  ######             e.g., using flash3kyuu_deband with 16-bit input clip and 16-bit output clip as custom debanding filter: 
  ######                   LumaDBL(dither="flash3kyuu_deband(input_depth=16, output_depth=16)", d_lsb=true, d_lsb_in=true)
  
  ###### "grain"   : custom grain generator in string, "nop" for no processing
  g_lsb    = Default( g_lsb,    false )  # custom grain generator outputs 16-bit stacked clip or not
  g_lsb_in = Default( g_lsb_in, false )  # custom grain generator accepts 16-bit stacked clip or not
  ######             e.g., using Dither_add_grain16 with 16-bit input clip and 16-bit output clip as custom grain filter,
  ######                   and use ALMerge to make it luma adaptive:
  ######                   Function AGL16(clip c){ return ALMerge( c.Dither_add_grain16, c, c.DitherPost(-1) ) }
  ######                   LumaDBL(grain="AGL16()", g_lsb=true, g_lsb_in=true)
  
  ###### "mclip"   : custom mask clip, e.g., mclip="tcanny()"

  ditherD  = Default( ditherD,      0 )  # 16-bit to 8-bit result dithering method when lsb=false, same as "mode" in DitherPost
  ampoD    = Default( ampoD,      1.5 )  # 16-bit to 8-bit result ordered dithering peak-to-peak amplitude when lsb=false, same as "ampo" in DitherPost
  ampnD    = Default( ampnD,        1 )  # 16-bit to 8-bit result dithering grain generating strength when lsb=false, same as "ampn" in DitherPost
  ddDyn    = Default( ddDyn,    false )  # 16-bit to 8-bit result ordered dithering pattern is dynamic or not when lsb=false, same as "dyn" in DitherPost
  gdDyn    = Default( gdDyn,     true )  # 16-bit to 8-bit result dithering grain generating is dynamic or not when lsb=false, contrast with "staticnoise" in DitherPost
  ###### "ditherC" : custom dithering method in string, e.g., ditherC="DitherPost(mode=2)" gives same result as ditherD=2
  
  src16    = lsb_in  ? input : input.Dither_convert_8_to_16()
  src8     = !lsb_in ? input : input.DitherPost(mode=ditherD, ampo=ampoD, ampn=ampnD, dyn=ddDyn, staticnoise=!gdDyn)
  last     = src8
  res      = width * height
  
  radius   = ( res <= 589824 ) ? Default( radius, 12 ) :
           \ ( res <= 921600 ) ? Default( radius, 14 ) :
           \                     Default( radius, 16 )
  ######   GradFun3 "radius" parameter, resolution adaptive   ######
  
  
  #-----------------------------------------------------------------------------------
  
  chroma_pre = chroma && !mluma
  
  msk  = TEdgeMask( link=2, preblur=preblur, valon=-1, u=chroma_pre?1:0, v=chroma_pre?1:0 ).mt_lut( "x 255 *", u=chroma_pre?3:1, v=chroma_pre?3:1 )
  msk  = deflate ? msk.mt_deflate(u=chroma_pre?3:1, v=chroma_pre?3:1) : msk
  msk  = blur ? msk.RemoveGrain(20, chroma_pre?20:-1) : msk
  
  #-----------------------------------------------------------------------------------
  
  gf   = !Defined(dither) ? src16.GradFun3( thr=thr, elast=2.5, ampn=ampn, radius=radius, ampo=0.8, dthr=0.011, lsb=true, lsb_in=true, mask=gf3mask )
  \                       : ( dither=="nop" || dither=="none" || dither=="nop()" || dither=="none()" ) ? src16
  \                       : d_lsb_in ? d_lsb ? Eval( "src16." + dither )
  \                                          : Eval( "src16." + dither + ".Dither_convert_8_to_16()" )
  \                                  : d_lsb ? Eval( "src8."  + dither )
  \                                          : Eval( "src8."  + dither + ".Dither_convert_8_to_16()" )
  
  #-----------------------------------------------------------------------------------
  
  gf8  = gf.DitherPost(mode=ditherD, ampo=ampoD, ampn=ampnD, dyn=ddDyn, staticnoise=!gdDyn)
  gf   = !Defined(grain) ? gf.GrainFL( g1str=g1str,   g2str=g2str,    g3str=g3str, 
  \                                    g1cstr=g1cstr, g2cstr=g2cstr,  g3cstr=g3cstr,
  \                                    g1shrp=g1shrp, g2shrp=g2shrp,  g3shrp=g3shrp,
  \                                    g1size=g1size, g2size=g3size,  g3size=g3size, 
  \                                    g1soft=g1soft, g2soft=g2soft,  g3soft=g3soft,
  \                                    th1=gth1,      th2=gth3,       th3=gth3,      th4=gth4,
  \                                    ontop_grain=0, ontop_cgrain=0, lsb=true,      lsb_in=true            )
  \                      : ( grain=="nop" || grain=="none" || grain=="nop()" || grain=="none()" ) ? gf
  \                      : g_lsb_in ? g_lsb ? Eval( "gf."  + grain )
  \                                         : Eval( "gf."  + grain + ".Dither_convert_8_to_16()" )
  \                                 : g_lsb ? Eval( "gf8." + grain )
  \                                         : Eval( "gf8." + grain + ".Dither_convert_8_to_16()" )

  #-----------------------------------------------------------------------------------
  
  result16 = mask ? Defined(mclip) ? GrainFL_merge16_8( gf, src16, ( (src16.width==mclip.width) ? mclip.GrainFL_get_msb : mclip ), luma=mluma, u=chroma?3:4, v=chroma?3:4 )
  \                                : GrainFL_merge16_8( gf, src16, msk, luma=mluma, u=chroma?3:4, v=chroma?3:4 )
  \               : gf
  result   = lsb ? result16 : Defined(ditherC) ? Eval("result16."+ditherC) : result16.DitherPost(mode=ditherD, ampo=ampoD, ampn=ampnD, dyn=ddDyn, staticnoise=!gdDyn)
  
  return result
}
