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 10th December 2011, 08:50   #1  |  Link
vampiredom
Registered User
 
Join Date: Aug 2008
Posts: 233
SoftWipe() | Soft-edged horizontal and vertical wipe transitions

Here's a function I wrote to perform non-linear soft-edged wipes on two or more clips (100 clips maximum). Requires GScript

Usage Examples:
Code:
# Wipe down from a->b with a 64-pixel gradient
SoftWipe(a,b,direction=3, softness=64)

# The default duration is 1 second. Set an arbitrary duration (in frames)
SoftWipe(a,b,duration=25)

# When more than 2 clips are provided, SoftWipe() will automatically cycle the wipe direction.
# To instead use the same direction for all clips, use:
SoftWipe(a,b,c,d,e,f,g, direction=1, auto=false)

# To use linear wipe motion instead of sine-eased motion, set ease=false.
SoftWipe(a,b,c,d,e,f,g, ease=false)
SoftWipe.avsi
Code:
#######################################################################################################################
#
# SoftWipe()    2011/12/12 by vampiredom
# -----------------------------------------------------------------------------------
# Soft-edged horizontal and vertical wipe transitions
#
# Requirements: Gscript.dll
#
#######################################################################################################################
#
# Usage:
# ------
# SoftWipe(clip a, clip b1 [, clip b2..b99], int "duration", int "direction", int "softness", bool "ease", bool "auto")
#
# Parameters:
# -----------
# a, b1 [, b2..b99]     clips
# SoftWipe() 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
#
# 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: [automatic]
# Softness determines the size of the gradient used for wiping. Bu default, it is ~1/16 of the source width
# This parameter can not be set lower than 8. Values < 8 will be rounded up to 8, and will always be rounded to mod4
# There is no upper limit on softness, but settings higher than 256 may not look as good.
#
# direction     integer, default: 0
# Direction can be between 0 and 3 and affects the direction of the wipe transition:
# 0 = left
# 1 = right
# 2 = up
# 3 = down
#
# ease          boolean, default: true
# By default, transition progress will be non-linear (sine). Set ease=false for linear wipe timing.
#
# auto          boolean, default: true
# By default, when more than 2 clips are supplied as arguments, transition direction rotates automatically -
# beginning with the value set by the direction parameter and continuing in the sequence: left, down, right, up ...
# auto has no effect when only 2 clips are passed to SoftWipe()
#
# 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.
#
#######################################################################################################################

# This global will hold the basic gradient in memory (256x2 RGB24, 1536 bytes)
global __SoftWipe_gradient__   = ""

function SoftWipe(
\       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",
\       int "duration", int "direction", int "softness", bool "ease", bool "auto", bool "xfade"
\ ) {

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

        w = a.width()
        h = a.height()
        
        duration  = Default(duration, Round(a.framerate()))
        softness  = Default(softness, Round(Float(w / 16.0)  * 4.0 / 4))
        softness  = Max(softness, 8)
        
        direction = Default(direction, 0)
        ease      = Default(ease, true)
        auto      = Default(auto, true)
        xfade     = Default(xfade, true)
        
        Assert((direction >= 0 && direction <=3),"""SoftWipe()
The direction parameter should be an integer between 0 and 3
0 = Wipe Left
1 = Wipe Right
2 = Wipe Up
3 = Wipe Down
        """)
        
        Assert(duration > 0, """SoftWipe()
The duration parameter cannot be less than 1
        """)
        
        global __SoftWipe_gradient__ = (isClip(__SoftWipe_gradient__)) ? __SoftWipe_gradient__ : _MakeGradient()
        
        ###############################################################
        # MAIN ACTION
        ###############################################################
        a
        i = 1
        Gscript("""
                While (i <= 99 && isClip(Eval("b" + String(i)))) {
                        _DoWipe(last,Eval("b" + String(i)), duration, direction, softness, ease, xfade)
                        direction = (auto) ? (direction == 1) ? 2
                        \                  : (direction == 2) ? 0
                        \                  : (direction == 3) ? 1
                        \                  :                    3
                        \ : direction
                        i = i + 1
                }
        """)
        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 _ScaleGradient(int w, int h, bool v) {
                __SoftWipe_gradient__
                PointResize(width(), h)
                GaussResize(w,h,p=20)
                (v) ? TurnRight() : last
        }

        function _MoveMask(
        \       clip m, int target_size,
        \       float t, float d, int direction, bool ease
        \ ) {
        
                last = m
                
                w = width()
                h = height()
                
                c = (direction <= 1) ? w - target_size : h - target_size
                x = (direction <= 1) ? (ease) ? _Ease(t, 0, c, d) : Float(t) / Float(d) * Float(c) : 0
                y = (direction >= 2) ? (ease) ? _Ease(t, 0, c, d) : Float(t) / Float(d) * Float(c) : 0
                
                #x = Round(x)
                #y = Round(y)
                
                #(direction <= 1) ? Crop(x,0,target_size,h) : last
                #(direction >= 2) ? Crop(0,y,w,target_size) : last
                
                (direction <= 1) ? GaussResize(target_size,h, x,0,target_size,h) : last
                (direction >= 2) ? GaussResize(w,target_size, 0,y,w,target_size) : last
                
                (direction == 1) ? FlipHorizontal() : (direction == 3) ? FlipVertical() : last
        }

        function _DoWipe(clip a, clip b, int duration, int direction, int softness, bool ease, bool xfade) {
                w = a.width()
                h = a.height()

                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) {
                                (direction <= 1) ? _ScaleGradient(softness, h, false).AddBorders(w,0,0,0, $000000).AddBorders(0,0,w,0, $FFFFFF)
                                \                : _ScaleGradient(softness, w,  true).AddBorders(0,h,0,0, $000000).AddBorders(0,0,0,h, $FFFFFF)
                                
                                Loop(duration).Animate(0,duration, "_MoveMask",
                                \       (direction <= 1 ? w : h), 1,          duration+1, direction, ease,
                                \       (direction <= 1 ? w : h), duration+1, duration+1, direction, ease)
                                
                                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)
        }
}

Last edited by vampiredom; 12th December 2011 at 02:41. Reason: updated function 2011/12/12
vampiredom is offline   Reply With Quote
Old 10th December 2011, 21:07   #2  |  Link
Gavino
Avisynth language lover
 
Join Date: Dec 2007
Location: Spain
Posts: 3,433
Good stuff, vampiredom, a useful and interesting function, showing the versatility of Avisynth and the script language. It also shows how GScript can be used to process a variable number of arguments in a simple way, a technique I use myself.

Some detailed comments:
1) Perhaps a validity check should be done on duration.
The function only works for duration between 1 and framecount-2.
It can be fixed to work for framecount-1 by changing
Trim(0,a.framecount()-1-duration)
to
Trim(0,-(a.framecount()-duration))
(In functions, you should generally use the form Trim(0, -N) instead of Trim(0, N-1), which gives the wrong result for N=1 - see stickboy's classic Caveats of using Trim in functions.)
It might be nice if it also worked for the limiting cases duration=0 or framecount (as Dissolve does) - this would require additional logic to handle these cases.

2) Perhaps b1 should be mandatory, as your comments suggest (although the function actually works OK in the limiting case of a single clip).

3) An alternative (and more efficient) way of creating the initial gradient mask would be to use mt_lutspa (although this would introduce a dependency on MaskTools).
__________________
GScript and GRunT - complex Avisynth scripting made easier
Gavino is offline   Reply With Quote
Old 10th December 2011, 21:56   #3  |  Link
vampiredom
Registered User
 
Join Date: Aug 2008
Posts: 233
Quote:
Originally Posted by Gavino View Post
Good stuff, vampiredom, a useful and interesting function, showing the versatility of Avisynth and the script language. It also shows how GScript can be used to process a variable number of arguments in a simple way, a technique I use myself.
Thanks! I really get tired of dissolves. I like wipes, but "standard" wipes always remind me of early 1990s video editing software and I much prefer the "soft" versions.

Quote:
Originally Posted by Gavino View Post
1) Perhaps a validity check should be done on duration.
The function only works for duration between 1 and framecount-2.
It can be fixed to work for framecount-1 by changing
Trim(0,a.framecount()-1-duration)
to
Trim(0,-(a.framecount()-duration))
Indeed. You're right, I should write the Trims that way. It is much better. Maybe there should be more sanity checking for clip length, I am not sure ... Then again, it does not make much sense to be passing single-frame clips to a transition function! But I see what you mean. In these cases, maybe I could do a freeze-frame on the last frame of short clips to ensure no clip is ever shorter than "duration".

I'll look into these in the next revision. Thanks for tips.

Quote:
Originally Posted by Gavino View Post
2) Perhaps b1 should be mandatory, as your comments suggest (although the function actually works OK in the limiting case of a single clip).
That was sort-of by design: Of course, it only really makes sense to pass 2+ clips to a transition function ... I suppose I preferred just passing-through clip a rather than throwing an error. It seems like a more graceful way to fail a less-than-critical operation.

Quote:
Originally Posted by Gavino View Post
3) An alternative (and more efficient) way of creating the initial gradient mask would be to use mt_lutspa
That is interesting. I am not sure it matters terribly in this case. The initial gradient is only created once and remains persistent with a global variable (which was the most efficient solution I could come up with).

** EDIT ** Also, mt_lutspa is YV12 only, no? That would require ConvertToRGB(matrix="pc.601") to facilitate smooth animated cropping, which might be a wash efficiency-wise anyway.

Last edited by vampiredom; 10th December 2011 at 22:03.
vampiredom is offline   Reply With Quote
Old 11th December 2011, 06:10   #4  |  Link
vampiredom
Registered User
 
Join Date: Aug 2008
Posts: 233
OK, I have updated to hopefully handle cases where framecount < duration. The function shortens the transition to match the underlying clips in these cases (and switching to hard-cuts should single-frame clips be passed to it). The changes are all in the _DoWipe() sub-function and basically center around...
Code:
duration = Min(Floor(Float(a.framecount()) / 2.0), Floor(Float(b.framecount()) / 2.0), duration)
... and returning alternate results should the effective duration == 0.

Thanks, Gavino, for taking the time to check this stuff out and for your excellent suggestions!
vampiredom is offline   Reply With Quote
Old 11th December 2011, 12:11   #5  |  Link
Gavino
Avisynth language lover
 
Join Date: Dec 2007
Location: Spain
Posts: 3,433
Quote:
Originally Posted by vampiredom View Post
That was sort-of by design: Of course, it only really makes sense to pass 2+ clips to a transition function ... I suppose I preferred just passing-through clip a rather than throwing an error. It seems like a more graceful way to fail a less-than-critical operation.
Yes, I only mentioned it because the code doesn't match the descriptive comments (regarding optionality of b1), so I wasn't sure what your intention was.

Quote:
Also, mt_lutspa is YV12 only, no? That would require ConvertToRGB(matrix="pc.601") to facilitate smooth animated cropping, which might be a wash efficiency-wise anyway.
You're right, as you would then have to crop in steps of 2.
Actually, in similar things I've written, I use a resizer (with float offsets) instead of Crop to get subpixel accuracy, giving smoother movement on slow pans (and also allowing YV12 to be used just as easily as RGB).

Quote:
Originally Posted by vampiredom View Post
OK, I have updated to hopefully handle cases where framecount < duration.
... and returning alternate results should the effective duration == 0.
Since your internal logic now handles duration=0, you could allow it as the external argument value too, making it similar to Dissolve, instead of throwing an error.

Also, given you're already using GScript, why not use the block-if construct instead of testing duration>0 on every line?
Code:
if (duration > 0) {
  ...
}
else {
...
}
Quote:
Originally Posted by vampiredom View Post
Code:
# If some clips contain audio while others do not, they will be conformed to match those with audio.
I see you've slipped in this change too - neat.
__________________
GScript and GRunT - complex Avisynth scripting made easier
Gavino is offline   Reply With Quote
Old 12th December 2011, 02:44   #6  |  Link
vampiredom
Registered User
 
Join Date: Aug 2008
Posts: 233
Quote:
Originally Posted by Gavino View Post
I use a resizer (with float offsets) instead of Crop to get subpixel accuracy, giving smoother movement on slow pans
Good idea...

Quote:
Originally Posted by Gavino View Post
why not use the block-if construct instead of testing duration>0 on every line?
Another good idea... It took me so much time to get used to always using the ternary operators in AviSynth. Scary that now I have to be reminded to use if/then constructs!

See updated function. Thanks again!
vampiredom is offline   Reply With Quote
Old 12th February 2012, 07:41   #7  |  Link
tin3tin
Registered User
 
tin3tin's Avatar
 
Join Date: Mar 2005
Posts: 366
I tested out the SoftWipe transition in DVD slideshow GUI. It really has an elegant feel to it.
http://www.youtube.com/watch?v=mY4ZVnmhMuc

Thank you!
__________________
DVD slideshow GUI(Freeware).
tin3tin is offline   Reply With Quote
Old 12th February 2012, 23:13   #8  |  Link
vampiredom
Registered User
 
Join Date: Aug 2008
Posts: 233
I'm glad you like it! You may also want to try its sibling, GradientWipe().
vampiredom is offline   Reply With Quote
Reply

Tags
animate, gscript, soft, transition, 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 21:12.


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