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") SetDebugFile("__log__.txt") 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) LoadPlugin("MaskTools.dll") LoadPlugin("RemoveGrain.dll") LoadPlugin("Repair.dll") Import("SeeSaw.avs") # 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 filter-invocation command is constructed with ArrayGetString() due to the need to assign to named arguments.
- The frame number is divided with reference clip's total frames in GetLoopIndex in order to access the correct index settings for each frame.
- ret and ref clips are trimmed (frame mod ref.Framecount) on each frame in order to achieve the "recycling" of the reference clip in the whole frame sequence.
ArrayGetString is handy for that purposes because it directly returns the proper string representation of the array element (except of quoting of string values; in that case one must use StrQuote() afterwards).
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.