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. Domains: forum.doom9.org / forum.doom9.net / forum.doom9.se |
|
|||||||
![]() |
|
|
Thread Tools | Search this Thread | Display Modes |
|
|
#1 | Link |
|
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) 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 |
|
|
|
|
|
#2 | Link |
|
Avisynth language lover
Join Date: Dec 2007
Location: Spain
Posts: 3,442
|
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). |
|
|
|
|
|
#3 | Link | ||||
|
Registered User
Join Date: Aug 2008
Posts: 233
|
Quote:
Quote:
I'll look into these in the next revision. Thanks for tips. Quote:
Quote:
** 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. |
||||
|
|
|
|
|
#4 | Link |
|
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) Thanks, Gavino, for taking the time to check this stuff out and for your excellent suggestions! |
|
|
|
|
|
#5 | Link | |||
|
Avisynth language lover
Join Date: Dec 2007
Location: Spain
Posts: 3,442
|
Quote:
Quote:
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:
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 {
...
}
|
|||
|
|
|
|
|
#6 | Link | ||
|
Registered User
Join Date: Aug 2008
Posts: 233
|
Quote:
Quote:
See updated function. Thanks again! |
||
|
|
|
|
|
#7 | Link |
|
Registered User
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). |
|
|
|
|
|
#8 | Link |
|
Registered User
Join Date: Aug 2008
Posts: 233
|
I'm glad you like it! You may also want to try its sibling, GradientWipe().
|
|
|
|
![]() |
| Tags |
| animate, gscript, soft, transition, wipe |
| Thread Tools | Search this Thread |
| Display Modes | |
|
|