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. |
14th December 2011, 01:27 | #1 | Link |
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. ####################################################################################################################### |
14th December 2011, 01:28 | #2 | Link |
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 |
14th December 2011, 12:04 | #3 | Link | |
Avisynth language lover
Join Date: Dec 2007
Location: Spain
Posts: 3,433
|
Another impressive piece of work, vampiredom.
There are a couple of problems here: Quote:
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. |
|
14th December 2011, 17:32 | #4 | Link | |
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:
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?!) |
|
Tags |
gradient, gscript, transitions, variableblur, wipe |
Thread Tools | Search this Thread |
Display Modes | |
|
|