Examples :: Per frame filtering, a position + size + color animation

This series of example scripts demonstrates the capabilities offered by the filters of the new in AVSLib version 1.1.0 filters :: frames module. The filter used in the examples is FrameFilterReader.

The first example script moves a rotating star accross a complex closed x,y curve, while in parallel its dimensions (width and height) oscillate giving the impression of the star tilting accross the horizontal and vertical axes. At the same time the star's color varies constantly due to variation of its RGB components.

Lets now look closer at the first script:

LoadModule("avslib", "base", "constants")
LoadPackage("avslib", "array")
LoadModule("avslib", "clip", "core")
LoadModule("avslib", "filters", "frames")
LoadModule("avslib", "filters", "resize")
LoadModule("avslib", "filters", "utility")

# afford a start/end blank to make code nicer to look
# it will be cleaned by the stuff below
varfiles = """
r.txt
g.txt
b.txt
w.txt
h.txt
x.txt
y.txt
""" 
old = ArrayDelimiterSet(CRLF)
okflag = varfiles.ArrayOpFunc("Exist").ArrayOpFunc("CInt")
varfiles = varfiles.ArrayReduce(okflag)
old = ArrayDelimiterSet(old)

After loading the necessary modules, a CR/LF delimited array of seven text files is declared. They will be read by FrameFilterReader and set the values of seven unique to each filter call global variables that will automatically be created by FrameFilterReader. The format of the files is that of the ConditionalReader standard Avisynth filter.

The lines immediately after clean-up the array from non-existent entries; their purpose here is to allow line breaks to occur inside the filenames' array (useful for visually grouping files and formatting).

FilterScript = """
star_mask=%g
rect_color = MakeRGBColor(${read1}, ${read2}, ${read3})
ovl = BlankClip(last, width=last.SafeWidth(${read4}), \
	height=last.SafeHeight(${read5}), color=rect_color)
star_mask = star_mask.Resize(ovl.Width, ovl.Height).ScaleToPC()
Overlay(last, ovl, ${read6} - Round(ovl.Width / 2), \
	${read7} - Round(ovl.Height / 2), mask=star_mask)
""" 

clp = BlankClip(color=$102005, pixel_type="yv12", length=300)

effmask = ImageSource("star%06d.jpg", 0, 48, 25.0).Reverse().Loop(10).Trim(0,299)
effmask = effmask.ResizeToTarget(clp).ConvertToTarget(clp).ScaleToPC()

Next, the prototype script that will be executed in every frame is assigned to the FilterScript string variable, the base clip of the animation (clp) is created and the rotating star image sequence is loaded and converted to a clip (effmask) with the same colospace and dimensions as the base clip.

The prototype script assings a mask (to be supplied as argument) to the star_mask local variable and an rgb color to rect_color. The later is created by the first three global variables that will be read by FrameFilterReader from the r.txt, g.txt and b.txt text files, respectively. To combine the three integer values (in the range [0..255] the function MakeRGBColor is used.

Then a rect_color-colored clip with dimensions set by the global variables that are read from the w.txt and h.txt text files is created. SafeWidth and SafeHeight ensure that the dimensions will be compatible with base clip's colorspace, thus preventing an error message showing when the script will be passed internally by FrameFilterReader to ScriptClip.

Afterwards, star_mask is resized to the same dimensions as ovl using the Resize filter. The later was selected because it does not hard-code a specific resizer selection inside the script (see below) thus making the script more reusable; for example appropriate for deriving a function that is based on it.

Finally, the colored clip is overlayed on top of the base clip using star_mask as the overlay mask, with its center in in x,y coordinates read by FrameFilterReader from files x.txt and y.txt.

SetDefaultResizer("bicubic")
FrameFilterReader(clp, FilterScript, varfiles, effmask)

The rest of the script selects a specific resizer to be used by the Resize filter (with SetDefaultResizer) and calls FrameFilterReader to do the per frame animation.

The second script example demonstrates the ability to call the filters of the frames module multiple times in our scripts, without the need to manage globals; they are all taken care by them.

The example executes the same animation in a group of clips, with constant x,y offsets from the central coordinates defined inside the x.txt and y.txt files. Thus instead of a single moving rotating star the script now moves a circle of rotating stars.

In order to keep the text short, only the differences with the previous script are presented:

...
LoadPackage("avslib", "string")
...
FilterScript = """
star_mask=%g
x_ofs = %i
y_ofs = %i
rect_color = %b ? color_white : MakeRGBColor(${read1}, ${read2}, ${read3})
ovl = BlankClip(last, width=last.SafeWidth(${read4}), \
	height=last.SafeHeight(${read5}), color=rect_color)
star_mask = star_mask.Resize(ovl.Width, ovl.Height).ScaleToPC()
Overlay(last, ovl, x_ofs + ${read6} - Round(ovl.Width / 2), \
	y_ofs + ${read7} - Round(ovl.Height / 2), mask=star_mask)
""" 
...

The per frame script now contains two more lines to get the x, y offset arguments, a conditional statement to return a white clip if an (argument) boolean flag is set, in order for the clip to be used as mask and a change in Overlay arguments in oder for the x, y offsets to have effect.

...
Function MakeEffect(int xofs, int yofs, clip base, \
	string script, string files, clip effmask, bool is_mask)
{
	return FrameFilterReader(base.BlankClip(), script, files, effmask, xofs, yofs, is_mask)
}

xofs = "0,60,-60,0,0,43,43,-43,-43"
yofs = "0,0,0,60,-60,43,-43,43,-43"

clips = ArrayOpArrayFunc(xofs, yofs, "MakeEffect", \
	StrPrint("%g, %q, %q, %g, %b", clp, FilterScript, varfiles, effmask, false))
masks = ArrayOpArrayFunc(xofs, yofs, "MakeEffect", \
	StrPrint("%g, %q, %q, %g, %b", clp, FilterScript, varfiles, effmask, true))

global ret = clp

Function OverlayEffect(clip clp, clip clpmask)
{
	global ret = Overlay(ret, clp, mask=clpmask.ScaleToPC())
	return true
}

dummy = ArrayOpArrayFunc(clips, masks, "OverlayEffect")
return ret

The body of the script now contains a function to create the moving star effect (MakeEffect) and two arrays to hold the x, y offsets for each rotating star clip.

The clips are created by operating, with ArrayOpArrayFunc, MakeEffect on both x, y offset arrays. StrPrint is used to construct the argument lists of the additional arguments to pass to MakeEffect by the array operator.

The overall effect is created using a global clip as accumulator, to accept all the overlays performed, and a second function (OverlayEffect) to perform each overlay. The function is applied to all elements of the clip and mask arrays again with a call to ArrayOpArrayFunc. After that the global overaly accumulator is return as the script's return value.

The image sequence is provided here.