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. |
|
|
Thread Tools | Search this Thread | Display Modes |
|
10th December 2011, 08:50 | #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 |
10th December 2011, 21:07 | #2 | Link |
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). |
10th December 2011, 21:56 | #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. |
||||
11th December 2011, 12:11 | #4 | Link | |||
Avisynth language lover
Join Date: Dec 2007
Location: Spain
Posts: 3,433
|
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 { ... } |
|||
12th December 2011, 02:44 | #5 | Link | ||
Registered User
Join Date: Aug 2008
Posts: 233
|
Quote:
Quote:
See updated function. Thanks again! |
||
12th February 2012, 07:41 | #6 | 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). |
11th December 2011, 06:10 | #7 | 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! |
12th February 2012, 23:13 | #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 | |
|
|