Examples :: Loop through filter settings - revisited

This script demonstrates the use of the array facilities of AVSLib in order to produce comprehensive previews of the result of varying combinations of a filter's settings to a source clip.

The script builds upon the previous example, extending it to demonstrate how it is possible to preview more complex filters, possibly with named arguments and / or temporal characteristics.

In addition, as in the previous example the script demonstrates the debug logging facilities of AVSLib.

The example script applies a set of different filtering parameters combinations to a single (reference) subrange of a clip and then joins the filtered clips into a single clip. The coding is such that one can easily modify the loop parameters, for example to make a second fine-grained pass of the most promissing subset of parameters combinations.

In addition the script stacks horizontally side-by-side the filtered and original clip to allow for easy review of the filter's effect. For large clips this means that you will need a wide screen display or to modify the script such that only an area of interest is selected (for example using Crop).

Now the script:

LoadModule("avslib", "array", "core")
LoadModule("avslib", "array", "operators")
LoadModule("avslib", "array", "slices")
LoadModule("avslib", "array", "functions")

# use a log file to quickly verify the correctness of looping through indices

LoadModule("avslib", "debug", "logging")

SetDebugMode(DBG_LEVEL_1)	# use DBG_NODEBUG to de-activate logging

# load filter-specific modules (assumes they are at the same folder as the script)


# load a reference clip

global ref = AVISource("reference.avi").Trim(98, -8)
global ref_frames = ref.Framecount

# in this example the filter to loop through is SeeSaw

# 3*3*3*3*3*3*3 = 2187 blocks * 8 frames = 17496 frames
global l1 = "2,4,6"   # NRlimit
global l2 = "3,5,7"   # NRlimit2
global l3 = "1.3,1.5,1.7"   # Sstr
global l4 = "20,24,28"   # SdampHi
global l5 = "42,49,56"   # bias
global l6 = "42,49,56"   # sootheT
global l7 = "0,2,4"   # sootheS

total_loops = 7
global loop_counts = "3,3,3,3,3,3,3"

Function LoopDiv(int idx, string loop_cnt) {
	subrange = loop_cnt.ArrayGetRange(idx)
	return subrange.ArrayLen > 0 ? subrange.ArrayProduct() : 1

total_steps = loop_counts.ArrayProduct()
global loop_ids = ArrayRange(1, total_loops)
global loop_divs = ArrayOpFunc(loop_ids, "LoopDiv", "loop_counts")

Function GetLoopIndex(int lid, int frame) {
	lcnt = loop_counts.ArrayGet(lid - 1)
	ldiv = loop_divs.ArrayGet(lid - 1)
	return ((frame / ref_frames) / ldiv) % lcnt   # int divisions; remainder is trunced

Function GetLoopValue(int loop_id, int index) {
	return Eval("l" + String(loop_id) + ".ArrayGet(" + String(index) + ")")

Function ApplyFilter(int frame)
	indices = ArrayOpFunc(loop_ids, "GetLoopIndex", String(frame))
	values = ArrayOpArrayFunc(loop_ids, indices, "GetLoopValue")

	DebugLog(DBG_LEVEL_1, (frame == 0 ? "Processing frames:\n" : "") + \
		"\tframe = %i\tblock = %i\tindices = %s\tvalues = %s", frame, \
		(frame / ref_frames), indices, values)

	# In this example filter's arguments are named, so constructing the command line is 
	# a bit more tricky
	cmdline = "SeeSaw(ref, NRlimit=" + values.ArrayGetString(0) + \
		", NRLimit2=" + values.ArrayGetString(1) + \
		", Sstr=" + values.ArrayGetString(2) + \
		", sdampHi=" + values.ArrayGetString(3) + \
		", bias=" + values.ArrayGetString(4) + \
		", sootheT=" + values.ArrayGetString(5) + \
		", sootheS=" + values.ArrayGetString(6) + \
	ret = Eval(cmdline)  # run the filter; stack with the original for immediate comparison
	# we tream after evaluating the filter, in order for the correct frame to be returned
	return StackHorizontal( \
		ret.Trim(frame % ref_frames, -1).SubTitle(cmdline, size=12, \
			text_color=color_white, halo_color=color_gray20), \
		ref.Trim(frame % ref_frames, -1).SubTitle("Original", size=12, \
			text_color=color_white, halo_color=color_gray20))

DebugLog(DBG_LEVEL_1, "Starting filter looping\n\tloop_ids = %s\n\tloop_divs = %s", \
	loop_ids, loop_divs)

# in order for stackhorizontal to work, must stack here also

clp = ref.Loop(total_steps)
return ScriptClip(StackHorizontal(clp, clp), "ApplyFilter(current_frame)")

As it is apparent from the code, the overall structure of the previous example script is retained. However, in order to achieve the new functionality a number of changes are made, notably the following:

The above skeleton can be adopted for more elaborate tasks also, with minor changes. For example, one can crop the reference clip before passing it to the conditional environment in order to focus on a specific area of the clip. Or, if a number of filtering operations is desired in one pass, one can write a custom function that will accept an array of values and will call the appropriate filters in sequence.