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 Usage

Reply
 
Thread Tools Search this Thread Display Modes
Old 14th December 2011, 01:27   #1  |  Link
vampiredom
Registered User
 
Join Date: Aug 2008
Posts: 233
GradientWipe() | Luminance Map Transitions

Too long for one post ... Here is the description of the function and its parameters. The actual script will be in post #2.

GradientWipe() provides plenty of options to create an endless variety of custom wipe transitions.

Code:
#######################################################################################################################
#
# GradientWipe()    2011/12/13 by vampiredom
# -----------------------------------------------------------------------------------
# Luminance-based animated mask transitions similar to "Gradient Wipe" or "Alpha Wipe" transitions found in NLEs.
#
# Requirements: Gscript.dll
#               VariableBlur.dll
#
#######################################################################################################################
#
# Usage:
# ------
# GradientWipe(clip a, clip b1 [, clip b2..b99], "map",
# \            int "duration", int "softness", bool "backward", bool "clamp"
# \            int "blur_pre", int "blur_post", bool "ease", bool "xfade"
# \)
#
# Parameters:
# ---------------------------
# a, b1 [, b2..b99]     clips
# ---------------------------
# Gradient() requires between 2 and 99 clips as input. It will wipe from a to b1 [to b2, b3..]
# If you want audio output, all clips should have matching sample rate, number of channels, etc.
# If some clips contain audio while others do not, they will be conformed to match those with audio. 
# Audio will cross-fade as the video wipes unless xfade=false
# --------------------------------------------------
# map                   string or clip [default "b"]
# --------------------------------------------------
# The map paramter sets the luminance map used for the transition. It can be a clip or a string.
# By default, map = "b" ... this will use the luminance of the b clip, so as to make a unique transition.
# When map = "a", the luminance of the a clip will be used for the transition. In addition to "a" and "b", you can
# also specify a string that will generate a gradient map as follows: map = "segments,tiles1,tiles2,vertical"
#
# The comma-separated strings are passed to the MakeGradientBands() function and are as follows, in order:
#
#       segments        integer [<1 or >1]
#       tiles1          integer [<1 or >1]
#       tiles2          integer [<1 or >1]
#       vertical        boolean
#
# segments:     This determines how many segments the 256-level gradient will be broken into.
#               The default is 1, which will make a single strip from black->white (0->255)
#               When a value of 2 is used, 2 strips will be created; (0->127) and (128->255)
#               A negative value causes the segments to alternate direction. For example:
#                       A value of -2 will create (0->127), (255->128)
#                       A value of -4 will create (0->63), (127->64), (128->191), (255->192)
#               The segments are stacked in rows or columns [depending on the "vertical" setting]
# tiles1:       Values > 1 will tile each segment (adding columns when vertical=false, rows when vertical=true)
#               Values < 1 will tile each segment; mirroring the segment as it tiles.
# tiles2:       Values > 1 will tile all segments (multiplying rows when vertical=false, columns when vertical=true)
#               Values < 1 will tile all segments; inverting the pattern as it tiles.
# vertical:     When set to true changes the orientation from horizontal to vertical
#
# Some examples of this are:
#       map = "1,1,1,false"       <- This is the default; a single left->right gradient   
#       map = "1,1,1,true"        <- Use a single top->bottom gradient instead
#       map = "1,1,-5,false"      <- A horizontal band wipe with 5 rows
#       map = "1,5,-5,true"       <- A vertical 5x5 checkerboard wipe
#       map = "5,1,1,false"       <- Horizontal wipe with 5 sequential bands
#       map = "-5,1,1,false"      <- Same as above, but each subsequent band reverses direction
#
#       There are no upper / lower limits placed on segments or tiles. Some extreme settings may cause problems,
#       so it is best to keep these values between -16 and + 16 or so.
#
# In addition to "a", "b", and comma-separated strings, you can also pass a clip via the map parameter. In these cases,
# The clip will be converted to grayscale and used to determine the transition. In most cases, you'll want to provide
# a still image, though interesting effects can be generated by moving clips as well. When clips are used for the map,
# they will be trimmed from frame 0 to match the duration of the transition - and the last frame held if they are
# shorter than the duration. When using still images as m, you should apply Trim(0,-1) ahead of time to optimize
# performance. Gradient map clips will be scaled (by brute force) to fit the video, so make sure that these clips
# have the same aspect ratio as the video clips, etc. Colorspace does not matter: all maps are converted to YV12
# -------------------------------------------
# duration      integer, default: [automatic]
# -------------------------------------------
# This is the length of the transition in frames. By default, the wipes are 1 second long
# There is no upper limit, though excessively high values may affect the smoothness.
# of the transition on lower-resolution clips. Duration will be shortened automatically if the underlying clips do not
# have enough frames for the specified transition. If duration < 1, video (& audio) will hard-cut instead.
# ----------------------------------
# softness      integer, default: 16
# ----------------------------------
# Softness determines how many levels are allowed to pass through duration animation of the gradient map.
# Higher softness settings will give a smoother appearance to the transition at the expense of precision.
# -------------------------------------
# backward      boolean, default: false
# -------------------------------------
# When set to true, this will reverse the direction of the wipe. By default, dark areas transition first. A setting of
# true will cause lighter areas to transition first instead.
# -------------------------------------
# clamp         boolean, default: false
# -------------------------------------
# When set to true, this will perform frame-by-frame clamping of the gradient map input to 0->255. This should be left
# at the default (false) unless an underexposed or washed-out image is used as the gradient map.
# ------------------------
# blur_pre      integer, 0
# ------------------------
# This applies blurring (using AverageBlur) to the gradient map prior to animation. This can be useful at lower values
# for obscuring detail of clips to make a more even transition. At higher values (such as 50 or so) it can transform
# the map into abstract shapes, often providing unique and interesting results. 
# ------------------------
# blur_post     integer, 0
# ------------------------
# This applies blurring (using AverageBlur) to the animated result of the map. This can be useful as an alternative to
# or enhancement of the softness parameter - or to otherwise create a desired effect.
# -------------------------------------
# ease          boolean, default: false
# -------------------------------------
# By default, transition progress will be linearly determined by the map. Set ease=true for non-linear (sine) timing.
# -------------------------------------
# xfade         boolean, default: true
# -------------------------------------
# By default, clips with audio will crossfade from a->b over the duration of the tranition.
# Setting xfade=false will cause the audio to hard cut from a->b halfway through the transition.
#######################################################################################################################
vampiredom is offline   Reply With Quote
Old 14th December 2011, 01:28   #2  |  Link
vampiredom
Registered User
 
Join Date: Aug 2008
Posts: 233
The actual function(s):

Code:
function GradientWipe(
\       clip a,  "b1", "b2", "b3", "b4", "b5", "b6", "b7", "b8", "b9","b10","b11","b12","b13","b14","b15","b16","b17","b18","b19",
\         "b20","b21","b22","b23","b24","b25","b26","b27","b28","b29","b30","b31","b32","b33","b34","b35","b36","b37","b38","b39",
\         "b40","b41","b42","b43","b44","b45","b46","b47","b48","b49","b50","b51","b52","b53","b54","b55","b56","b57","b58","b59",
\         "b60","b61","b62","b63","b64","b65","b66","b67","b68","b69","b70","b71","b72","b73","b74","b75","b76","b77","b78","b79",
\         "b80","b81","b82","b83","b84","b85","b86","b87","b88","b89","b90","b91","b92","b93","b94","b95","b96","b97","b98","b99",
\       "map",
\       int "duration", int "softness",
\       bool "backward", bool "clamp",
\       int "blur_pre", int "blur_post", bool "ease", bool "xfade"
\ ) {

        ###############################################################
        # INITIALIZATION
        ###############################################################

        duration  = Default(duration, Round(a.framerate()))
        softness  = Default(softness,     16)
        backward  = Default(backward,  false)
        clamp     = Default(clamp,     false)
        blur_pre  = Default(blur_pre,      0)
        blur_post = Default(blur_post,     0)

        ease      = Default(ease,  false)
        xfade     = Default(xfade,  true)

        map = Default(map, "b")
        map = isClip(map)
        \ ? map.GaussResize(a.width(),a.height()).AssumeFPS(a.FramerateNumerator(), a.FramerateDenominator())
        \ : map
        
        ###############################################################
        # MAIN ACTION
        ###############################################################
        
        a
        i = 1
        Gscript("""
                While (i <= 99 && isClip(Eval("b" + String(i)))) {
                        _DoWipe(last,Eval("b" + String(i)), map,duration,backward,softness,clamp,blur_pre,blur_post,ease,xfade)
                        i = i + 1
                }
        """)
        last

        ###############################################################
        # HELPER FUNCTIONS
        ###############################################################
        
        function _PrepMask(clip m, int d, bool clamp, int blur_pre) {
                m.KillAudio()
                     (isRGB())   ? ConvertToYV12(matrix="pc.601")
                \  : (!isYV12()) ? ConvertToYV12()
                \  : last

                (blur_pre > 0) ? averageblur(radY=blur_pre)                                               : last
                (clamp)        ? ScriptClip(last, "Levels(YPlaneMin(),1,YPlaneMax(),0,255,coring=false)") : last
                
                (framecount() == 1)
                \ ? Loop(d)
                \ : (framecount() < d)
                \       ? last + Trim(framecount()-1,-1).Loop(d-framecount())
                \       : Trim(0,-d)
        }
        
        function _AnimMask(clip m, int t, int d, int s, bool b, bool e) {
                c = (e)
                \ ? Round(_Ease(t,255,-(255+s),d))
                \ : 255 - Round((Float(t) / Float(d)) * (255 + s))

                l1 = Max(c, 0)
                l2 = Min(255, c+s)
                
                (b) ? m : m.Invert()
                Levels(l1,1.0,l2,0,255,coring=false)
        }
        
        function _DoWipe(
        \ clip a, clip b, m,
        \ int duration, bool backward, int softness, bool clamp,
        \ int blur_pre, int blur_post, bool ease, bool xfade
        \ ) {
                a = _FillAudio(a,b)
                b = _FillAudio(b,a)
                
                duration = Min(Floor(Float(a.framecount()) / 2.0), Floor(Float(b.framecount()) / 2.0), duration)
                
                GScript("""
                        if (duration > 0) {
                                (isString(m)) ?
                                \         (m == "a") ? a.Trim(a.framecount()-duration-1,0)
                                \       : (m == "b") ? b.Trim(0,-duration)
                                \       : MakeGradientBands(a.width(), a.height(), settings=m)
                                \ : m
                        
                                _PrepMask(last, duration, clamp, blur_pre)
                                Animate(last,0, duration, "_AnimMask", 1,         duration+1,softness,backward,ease,
                                \                                      duration+1,duration+1,softness,backward,ease)
                                
                                (blur_post > 0) ? averageblur(radY=blur_post) : last
                                
                                a1 = a.KillAudio().Trim(0,-(a.framecount()-duration))
                                a2 = a.KillAudio().Trim(a.framecount()-duration, 0)
                                b1 = b.KillAudio().Trim(0,-duration)
                                b2 = b.KillAudio().Trim(duration, 0)
                                
                                a1 + Overlay(a2,b1,mask=last,pc_range=true) + b2
                                
                                c = (HasAudio(a)) ?
                                \ (xfade)
                                \       ? Dissolve(a,b, duration)
                                \       : (a.Trim(0,-(a.framecount() - Floor(Float(duration) / 2.0)))
                                \       +  b.Trim(Ceil(Float(duration) / 2.0), 0)).KillVideo()
                                \ : NOP()
                
                                (isClip(c)) ? AudioDub(c) : last
                                
                        } else {
                                a + b
                        }
                """)
        }
        
        function _FillAudio(clip b, clip a) {
                c = (HasAudio(a) && !HasAudio(b))
                \ ? BlankClip(a, b.Framecount(), 1, 1, "RGB24", FramerateNumerator(b), FramerateDenominator(b)).KillVideo()
                \ : NOP()
                (isClip(c)) ? AudioDub(b,c) : b
        }
        
        function _Ease(a, b, c, d) {
                Float(-c) / 2.0 * (Cos(3.141592 * Float(a) / Float(d)) - 1.0) + Float(b)
        }
}


function MakeGradientBands(int w, int h, int "segments", int "tiles1", int "tiles2", bool "vertical", string "settings") {

        ###############################################################
        # INITIALIZATION
        ###############################################################

        g = _MakeGradient()
        
        var1 = 2
        var2 = 1
        var3 = 1
        var4 = false
        
        (IsString(settings)) ? Eval(_String_to_Vars(settings)) : NOP()

        segments  = Default(segments, var1)
        tiles1    = Default(tiles1,   var2)
        tiles2    = Default(tiles2,   var3)
        vertical  = Default(vertical, var4)
        
        # no such thing as zero segments or tiles, so make these 1 instead
        segments = (segments == 0) ? 1 : segments
        tiles1   = (tiles1   == 0) ? 1 : tiles1
        tiles2   = (tiles2   == 0) ? 1 : tiles2
        
        r1  = (vertical) ? h : w
        r2  = (vertical) ? w : h
        
        ###############################################################
        # MAIN ACTION
        ###############################################################
        
        NextL = 0
        Gscript("""
                for (i = 1 , abs(segments) , 1) {
                        sW = (i % 2 == 1)             ? Floor(256.0 / Float(abs(segments))) : Ceil(256.0 / Float(abs(segments)))
                        s  = (i + 1 <= abs(segments)) ? g.Crop(NextL,0,sW,2) : g.Crop(NextL,0,0,0)
                        
                        s  = (r1 > s.width() * 2) ? s.PointResize(s.width() * 2, s.height()) : s
                        
                        r = (abs(tiles1) > 1) ? s : NOP()
                        for (j = 2 , abs(tiles1) , 1) {
                                s = StackHorizontal(s, (tiles1 > 1 || (j % 2 == 1)) ? r : r.FlipHorizontal())
                        }

                        s  = (i % 2 == 0 && segments < 0) ? s.FlipHorizontal() : s
                        s = s.GaussResize(r1,s.height())
                        
                        isClip(last) ? StackVertical(s) : s
                        NextL = NextL + sW
                        last
                }
                
                r = (abs(tiles2) > 1) ? last : NOP()
                for ( i = 2 , abs(tiles2) , 1) {
                        StackVertical((tiles2 > 1 || (i % 2 == 1)) ? r : r.Invert())
                }
        """)
        PointResize(width(), r2)
        (vertical) ? TurnRight() : last
        
        ###############################################################
        # HELPER FUNCTIONS
        ###############################################################
        
        function _MakeGradient() {
                GScript("""
                for ( i = 0 , 255 , 1 ) {
                        b = BlankClip(width=1, height=2, length=1, pixel_type="RGB24", color=i*65536 + i*256 + i)
                        isClip(last) ? StackHorizontal(b) : b
                }""")
        }
        
        function _String_to_Vars(string s) {
                i = 1
                r = ""
                Gscript("""
                        while (FindStr(s, ",") > 0) {
                                x = FindStr(s, ",")
                                r = r + "var" + String(i) + "=" + LeftStr(s,x-1) + CHR(10)
                                s = MidStr(s,x+1)
                                i = i+1
                        }
                """)
                r + "var" + String(i) + "=" + s
        }
}

Last edited by vampiredom; 14th December 2011 at 17:24. Reason: fixed a few things in MakeGradientBands
vampiredom is offline   Reply With Quote
Old 14th December 2011, 12:04   #3  |  Link
Gavino
Avisynth language lover
 
Join Date: Dec 2007
Location: Spain
Posts: 3,431
Another impressive piece of work, vampiredom.

There are a couple of problems here:
Quote:
Originally Posted by vampiredom View Post
Code:
function MakeGradientBands(int w, int h, int "segments", int "tiles1", int "repeat2", bool "vertical", string "settings") {
        ...
        var1 = NOP()
        var2 = NOP()
        var3 = NOP()
        var4 = NOP()
        
        (IsString(settings)) ? Eval(_String_to_Vars(settings)) : NOP()

        segments  = Defined(var1) ? var1 : Default(segments,     2)
        tiles1    = Defined(var2) ? var2 : Default(tiles1,       1)
        tiles2    = Defined(var3) ? var3 : Default(tiles2,       1)
        vertical  = Defined(var4) ? var4 : Default(vertical, false)
        ...
Firstly, unlike what you might think/expect/hope, NOP() returns zero, not an 'undefined' value (see this thread).
So var1, etc will always be defined. The easiest solution is to initialise the variables to their default values instead.

Also, 'tiles2' does not exist at the code in red - I assume this should be 'repeat2'.

These problems will only show up if you use a short 'map' string, eg map="1,1", which is probably why you didn't discover them in testing.

Perhaps you should also do some more validation of the map parameter - check that it is either a clip or a string, and check the types of the individual values supplied in the string case. Yes, it's up to the user to get it right, but a specific error message would be more helpful than some hard-to-understand internal failure.
__________________
GScript and GRunT - complex Avisynth scripting made easier
Gavino is offline   Reply With Quote
Old 14th December 2011, 17:32   #4  |  Link
vampiredom
Registered User
 
Join Date: Aug 2008
Posts: 233
Thanks. I figured that, having written SoftWipe(), making other wipe variations would not be so tough. The "Alpha Wipe" is one of the most versatile transitions found in NLEs ... and the implementation of it here is more flexible that that in any NLE I have seen.

Quote:
There are a couple of problems here:
Ugh. I renamed "repeat2" to "tiles2" ... obviously, I missed one! That is interesting (and disappointing!) about NOP(). Boo. I followed your advice and set these defaults more conventionally. The big items should be fixed now.

Next, I will look into better "map" parameter string validation. This would be a lot easier with REGEX. (Nobody's written a regular expressions plugin for AviSynth, have they?!)
vampiredom is offline   Reply With Quote
Reply

Tags
gradient, gscript, transitions, variableblur, wipe

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 12:49.


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