Container operators

Table of contents

Introduction

Having a container without a means to conveniently operate on all of its elements is quite disappointing; why then to have it in the first place? That's why all programming and scripting languages that support containers provide a means to effectively operate on them.

In most of these languages the means is special looping language constructs (such as the for, while, do..loop, etc.). In AVSLib, since the underlying script language does not support natively containers at all, it is a set of special AVSLib functions, collectively called container operators.

Container operators give the ability to operate on all elements of a container in a single step. In addition, coupled with the subrange selection functions that AVSLib provides for each container type, they give the ability to operate on subranges of a container in a single step.

Currently AVSLib supports only one container type, the array.

[<< to top]

Array operators

There are five fundamental array operator functions, from which a whole family of array operator functions can be derived. They are presented below:

The first four of the above functions operate on the individual elements of the array (or arrays) passed as arguments and produce a new array. The fifth operates both on the individual elements of the array and the array as a whole producing a single value[1], which is the analog of the integral of the array. Let's have a closer look on them.

[<< to top]

ArrayOpValue

The operator function performs the requested operation between a scalar (a single value) and every array element. In effect the operator performs the [vector] <op> scalar or scalar <op> [vector] action.

The operation, which is provided as a string, can be anyone that Avisynth script language supports for the types of scalar and element (for example "+", "-", "*", "/", "%", "&&", etc.).

The operator function performs the operation with element as the first operand (ie the expression evaluated is [element] <op> scalar). If this is not the desired order, the optional argument array_first can be set to false to reverse the order of the operands (ie to evaluate the expression scalar <op> [element]).

Examples


a1 = ArrayCreate(4, 2, 6, 3, 5, 1, 7)

# lets add 3 to all a1 elements

a2 = ArrayOpValue(a1, 3, "+")   # a2 == "7,5,9,6,8,4,10"

# lets divide all a1 elements by 3

a3 = ArrayOpValue(a1, 3, "/")   # a3 == "1,0,2,1,1,0,2"

# lets divide 3 by each element of a1 (we need the "false", in order this to work)

a4 = ArrayOpValue(a1, 3, "/", false)   # a4 == "0,1,0,1,0,3,0"

# what happened? well all operands are ints and so int division was performed

# now lets force float division by making 3 -> 3.0

a5 = ArrayOpValue(a1, 3.0, "/")   # a5 == "1.3333,0.6667,2.0,1.0,1.6667,0.3333,2.3333"

a6 = ArrayOpValue(a1, 3.0, "/", false)   # a6 == "0.75,1.5,0.5,1.0,0.6,3.0,0.4286"


# lets create an array of clips and add start_logo and end_logo at their

# beggining and end respectively (single clips are also scalar values)

c1 = AVIsource(...)

...

c10 = AVIsource(...)

start_logo = AVIsource(...)

end_logo = AVIsource(...)

ac1 = ArrayCreate(c1, c2, c3, c4, c5, c6, c7, c8, c9, c10)

# since we are interested in the final result we reassign ac1 to reuse identifiers;

# we also use OOP notation, for its better readability

ac1 = ac1.ArrayOpValue(start_logo, "+", false)

ac1 = ac1.ArrayOpValue(end_logo, "+")

# we are done; note the false in the first call, to put start_logo as the left operand

[<< to top]

ArrayOpFunc

The operator function applies a user-defined function to every array element. In effect the operator performs the f([vector]) action.

The function must accept element as its first argument. Additional arguments can be defined as an argument string (a string-representation of the additional arguments passed to the function separated by commas).

Examples


# lets calculate the y's of a curve on the interval [40, 200]

# the curve is y = 2.345x-1 + 3.12x - 4.002x1.5

Function my_curve(float x) {

coef = "5.347,-2.11,1.002"

pows = "-1,1,1.2"

return PowSeriesAA(x, coef, pows)

}

cvx = ArrayCreate(40,80,120,160,200,240,280,320,360,400)

cvy = cvx.ArrayOpFunc("my_curve")

# now pass the cvx, cvy to an animation filter such as PolygonAnim()

...


# a custom filter that resizes a clip to NTSC dimensions, adjusts hue,

# brightness and contrast and softens a little between frames

Function my_filter(clip c) {

ret = c.ConvertToYUY2()

ret = ret.BilinearResize(720,480)

ret = ret.Tweak(hue=15, bright=2.5, cont=1.2, coring=false)

ret = ret.TemporalSoften(4,4,8,15,2)

return ret

}

# lets create an array of clips and then apply my_filter() to them

c1 = AVIsource(...)

...

c10 = AVIsource(...)

ac1 = ArrayCreate(c1, c2, c3, c4, c5, c6, c7, c8, c9, c10)

# lets convert all clips' framerate its easier to do in one step

# after array creation; that's operators are made for

ac1 = ac1.ArrayOpFunc("AssumeFPS", "25")

# now apply your custom filter

ac1 = ac1.ArrayOpFunc("my_filter")

[<< to top]

ArrayOpArray

The operator function performs the requested operation between every element pair of the two arrays supplied as arguments. In effect the operator performs the [vector1] <op> [vector2] action.

The operation, which is provided as a string, can be anyone that Avisynth script language supports for the types of element pairs (for example "+", "-", "*", "/", "%", "&&", etc.).

The order of operands in the expression evaluated ([element] <op> [element]) is decided by the ordering of the arrays in the argument list; the elements of the first array are always the left operand of the expression.

Examples


# titles and movies are arrays of 20 clips each (say a collection of family clips

# produced during your vacations and the intros for each one, respectively)

titles = ArrayCreate(tvac1, ..., tvac20)

movies = ArrayCreate(mvac1, ..., mvac20)

# convert to common format (if all clips have the same, skip this step)

titles = titles.ArrayOpFunc("ConvertToYUY2")

movies = movies.ArrayOpFunc("ConvertToYUY2")

# now lets combine the intros with the respective family clips

stories = ArrayOpArray(titles, movies, "+")

...


# (x0,y0) and (x0,y4) define two curves on our clip coordinates

x0 = "100,180,260,340,420,500,580"

y0 = "0,100,350,480,350,250,0"

y4 = "50,200,450,580,450,350,50"

# lets generate 3 more curves (x0,y1), (x0,y2), (x0,y3) evenly distributed between

# (x0,y0) and (x0,y4), in order to to feed them all later to an animation function

# such as PolygonAnim()

 

# 1st step: calculate the span between curves (y4[i] - y0[i])

ysp = ArrayOpArray(y4, y0, "-")

# 2nd step: divide span by 4 and round to integer

ysp = ysp.ArrayOpValue(4.0, "/").ArrayOpFunc("Round")

# 3rd step: get curve n+1 by adding final ysp to curve n

y1 = ArrayOpArray(y0, ysp, "+")

y2 = ArrayOpArray(y1, ysp, "+")

y3 = ArrayOpArray(y2, ysp, "+")

[<< to top]

ArrayOpArrayFunc

The operator function applies a user-defined function to every element pair of the two arrays supplied as arguments. In effect the operator performs the f([vector1],[vector2]) action.

The function must accept element[vector1], element[vector2] as its two first argument. Additional arguments can be defined as an argument string (a string-representation of the additional arguments passed to the function separated by commas).

Examples


Function my_subtitle(clip c, string s) {

return c.SubTitle(s, size=24, text_color=$ffffff)

}

c1 = AVIsource(...)

...

c4 = AVIsource(...)

# ac1 is a clip array; at1 is a string array containing subtitles

ac1 = ArrayCreate(c1, c2, c3, c4)

at1 = "subtitle 1,subtitle 2,subtitle 3,subtitle 4"

# create an array with subtitled clips

sc = ArrayOpArrayFunc(ac1, at1, "my_subtitle")

...


# return a simple overlay of the clips passed as arguments

Function my_overlay(clip base, clip ovl, string "omode") {

omode = Default(omode, "blend")

opac = omode == "blend" ? 0.5 : 1.0

return Overlay(base, ovl, mode=omode, opacity=opac)

}

# ac1 and ac2 are two clip arrays with 4 elements each

ac1 = ArrayCreate(c1, c2, c3, c4)

ac2 = ArrayCreate(d1, d2, d3, d4)

# lets overlay them

ov1 = ArrayOpArrayFunc(ac1, ac2, "my_overlay")   # this will use 'blend' as mode

# string arguments to user-supplied functions must be quoted

ov2 = ArrayOpArrayFunc(ac1, ac2, "my_overlay", StrQuote("add"))

# lets overlay the results one more time to see what happens

fin = ArrayOpArrayFunc(ov1, ov2, "my_overlay", StrQuote("luma"))

...

[<< to top]

ArraySum

The operator function applies an optional elm_func user-defined function to every array element (if none is supplied it defaults to element itself). It then sums the return values of elm_func with an optional sum_func user-defined function (if none is supplied it defaults to simple addition: "+"). In effect the operator performs the sum_func(elm_func([vector])) action which is the equivalent of integrating over the array elements.

Usually, the precise order of application of sum_func will not be of importance. However, if the outcome of sum_func is not symmetrical to its main operands (ie the first two required arguments which correspond to array elements), it may be. Therefore, the following paragraph presents the internals of the summing process.

The summing of values is always performed in the backward direction, from array end (element with index array.ArrayLen()) to start (element with index 0).

Thus, the full expansion of the sum operation has the form (values inside brackets denote array elements with the designated index, ie [0] means array[0]):

sum_func(elm_func([0], elm_args), sum_func(elm_func([1], elm_args),
sum_func(elm_func([2], elm_args), sum_func( ... sum_func(
elm_func([ArrayLen-2], elm_args), elm_func([ArrayLen-1], elm_args),
sum_args) ... , sum_args), sum_args), sum_args), sum_args)

Therefore, the first argument of sum_func is always the result of the application of elm_func on an array element, while the second is the result of the succesive application of sum_func on all elm_func results for array elements with index > element's index. For the last two array elements sum_func is applied directly to the outcomes of elm_func on them.

Since the order of operation of sum_func is fixed inside the operator function's code, if for any reason a different order is required, the only way to accomplish this is to use the array manipulation functions (such as ArrayInvert()) for setting the desired order of array element's before passing it to ArraySum.

Examples


# lets use the array 'ac1' produced by the second example of ArrayOpValue

# 'ac1' contains 10 clips with a front and back-cover logo

# now lets concatenate all stories to a single clip

my_clip = ac1.ArraySum()


# lets use the array 'stories' produced by the first example of ArrayOpArray

# 'stories' contains 20 clips with a front-cover title

# now lets concatenate all stories to a single clip, using Dissolve

# (with overlap=5) to create a smoother transition

my_movie = stories.ArraySum(sum_func="Dissolve", sum_args="5")


# lets calculate an integral and print the results

 

# the function defining the curve

# y = 2.345x-1 + 3.12x - 4.002x1.5

Function my_f(float x) {

coef = "2.345,3.12,-4.002"

pows = "-1,1,1.5"

return PowSeriesAA(x, coef, pows)

}

 

# the function calculating the area using trapezoid rule

Function t_area(float y1, float y2, float dx) {

Assert(dx > 0, "dx must be greater than zero")

return (y1 + y2) * dx / 2

}

 

# divide [0.5..1] to subintervals with width 0.05

ax = ArrayRange(0.5, 1, step=0.05)

# now calculate the integral of a function f(x) ie the area between y=0 and y=f(x)

# use as f(x) 'my_f'

# to calculate the integral use the trapezoid rule (in sum_func)

area = ax.ArraySum(elm_func="my_f", sum_func="t_area", sum_args="0.05")

return Print("The xs are:", ax, "The ys are:", \

ax.ArrayOpFunc("my_f"), "The area is:", area)

[<< to top]

Usage (what to do and what not to do)

In order to be able to operate on every container regardless of its elements' type(s) without imposing significant overhead, operator functions do not check the validity of the parameters passed to them (except some trivial checks, such as equality of the number of elements of arrays when >1 array are passed as arguments). This has the consequence that common errors made by the user during the coding of the script, for example

etc. may result in an error message box by Avisynth pointing inside the code body of the operator function.

There are also many ways (at least for earlier versions of Avisynth) for a call to an operator to crash Avisynth and the video rendering application, due to the limitations imposed to AVSLib by the host application (Avisynth). Two classes of possible ways are the following:

Thus, the following list of best usage procedures should be followed in order to maximize the benefits from operators and containers altogether.

[<< to top]


[1]: This is the usual case. It is though possible, with proper coding of the elm_func and sum_func arguments of ArraySum function (user-supplied functions) to return an array as a result.