Examples :: Loop through filter settings

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.

In addition, the script demonstrates the debug logging facilities of AVSLib.

The example script applies a set of different filtering parameters combinations to a single (reference) frame of a clip and then joins the filtered frames 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.

First 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")

SetDebugFile("__log__.txt")
SetDebugMode(DBG_LEVEL_1)	# use DBG_NODEBUG to de-activate logging

# load a reference frame clip

global ref = AVISource("reference.avi").Trim(150, -1)

# in this example the filter to loop through is Levels

# 4*3*4*3*3 = 432 frames
global l1 = "0,40,80,120"   # input_low
global l2 = "0.75,1.0,1.5"   # gamma
global l3 = "255,210,165,120"   # input_high
global l4 = "0,80,160"   # output_low
global l5 = "255,190,120"   # output_high

total_loops = 5
global loop_counts = "4,3,4,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 / ldiv) % lcnt
}

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\tindices = %s\tvalues = %s", frame, indices, values)

	# In this example all filter's arguments are contained in 'values' array, so constructing 
	# the command line is particularly easy; if named arguments are wanted, use StrPrint() or 
	# ArrayGetString() 
	
	ret = Eval("ref.Levels(" + values + ")")
	return ret.SubTitle(values)
}

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

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

In order to speed-up the script's execution (previews must be fast) the script does not go through the creation of a combined array of all possible parameters combinations and then a series of trims.

Instead, the combinations are calculated on-the-fly for each frame inside a custom function, which is fed to ScriptClip. The above design uses small array sizes and a much smaller filter chain, thus offering increased performance.

A call to DebugLog inside the conditional environment function (ApplyFilter) prints out a nice log of all the loops' values to assist the script developer in confirming the correct operation of the script. The calls to SetDebugFile and SetDebugMode indicate where (the log's filename) and what will be logged (DebugLog will not print anything if its first argument is less than the one passed to SetDebugMode).