Expr

From Avisynth wiki
(Difference between revisions)
Jump to: navigation, search
(Syntax and Parameters: Expr details v0.1)
(formatting, links, phrasing (WIP))
Line 4: Line 4:
 
</div>
 
</div>
 
<div style="max-width:62em; min-width:42em;" >
 
<div style="max-width:62em; min-width:42em;" >
/// [[TODO]] DESCRIPTION
+
Applies a [[wikipedia:Function_(mathematics)|mathematical function]], defined by an ''expression'' string, on the pixels of the source clip(s). A different expression may be set for each color channel. Users of '''[[MaskTools2|MaskTools]]''' may be familiar with this concept.
 
</div>
 
</div>
 +
__TOC__
  
 
==== Syntax and Parameters ====
 
==== Syntax and Parameters ====
Line 14: Line 15:
 
}}
 
}}
  
:{{Par2||clip|}}
+
:{{Par2|clip|clip|}}
::One or more source clips. Up to 26 input clips can be specified. First three clips are referenced by lowercase letter x, y and z. Use letters 'a', 'b' ... 'w' for the rest.
+
::One or more source clips. Up to 26 input clips can be specified.  
:: Clips has to be of YUV(A), RGB(A), or greyscale 8-16 integer or 32 bit float. Width, height and color subsamping should be the same. Bit depths can be different.
+
::* The first three clips are referenced by lowercase letter x, y and z; use 'a', 'b' ... 'w' for the rest.
 +
::* Clips may be YUV(A), RGB(A), or greyscale; 8-16 bit integer or 32 bit float.  
 +
::* Width, height and [[wikipedia:Chroma_subsampling|color subsampling]] should be the same; bit depths can be different.
  
:{{Par2||string|}}
+
:{{Par2|exp|string|}}
::One or more RPN expressions
+
::One or more [[wikipedia:Reverse Polish Notation|RPN]] expressions
:: up to "number of planes" strings which hold the expression strings. When an expression string is not given, previous one is used. Empty string means the plane is copied.
+
::* A different expression may be set for each color channel (or ''plane'').  
 +
::* When an expression string is not given, the previous one is used.  
 +
::* The empty string ("") is a valid expression; it causes the plane to be copied (see [[#Expressions|Expressions]] below).
  
:{{Par2|format|string|?}}
+
:{{Par2|format|string|""}}
:: Optional named format argument
+
:: Set color format of the returned clip.  
:: This parameter overrides the output format defined by the first clip. Use pixel format strings like "YV12", "YUV420P8", "YUV444P16", "RGBP10".
+
::* Use [[Avisynthplus_color_formats|pixel format strings]] like "YV12", "YUV420P8", "YUV444P16", "RGBP10".
 +
::* By default, the output format is the same as the first clip.
  
 
:{{Par2|optAvx2|bool |(auto)}}
 
:{{Par2|optAvx2|bool |(auto)}}
 
:: If false, disable [[wikipedia:Advanced_Vector_Extensions|AVX2]].
 
:: If false, disable [[wikipedia:Advanced_Vector_Extensions|AVX2]].
:: Enables/Disables AVX2 code generation if available. Do nothing if AVX2 is not supported in Avisynth.
+
::* Enables/Disables AVX2 code generation if available. Do nothing if AVX2 is not supported in Avisynth.
  
 
:{{Par2|optSingleMode|bool |false}}
 
:{{Par2|optSingleMode|bool |false}}
 
:: If ''true'', generate assembly code using only one XMM/YMM register set instead of two; default false.
 
:: If ''true'', generate assembly code using only one XMM/YMM register set instead of two; default false.
:: Expr generates assembly code that normally uses two 128 (SSE2) or 256 bit (AVX2) registers ("lanes"), thus processing 8 (SSE2)/16 (AVX2) pixels per internal cycle.
+
::* '''Expr''' generates assembly code that normally uses two 128 (SSE2) or 256 bit (AVX2) registers ("lanes"), thus processing 8 (SSE2)/16 (AVX2) pixels per internal cycle.
:: Experimental parameter, optSingleMode=true makes the internal compiler to generate instructions for only one register (4/8 pixels - SSE2/AVX2). The parameter was introduced to test the speed of x86 code using one working register. Very-very complex expressions would use too many XMM/YMM registers which are then "swapped" to memory slots, that could be slow. Using optSingleMode = true may result in using less registers with no need for swapping them to memory slots.
+
::* Experimental parameter, {{FuncArg|optSingleMode}}=true makes the internal compiler generate instructions for only one register (4/8 pixels - SSE2/AVX2). The parameter was introduced to test the speed of x86 code using one working register. Very-very complex expressions would use too many XMM/YMM registers which are then "swapped" to memory slots, that could be slow. Using {{FuncArg|optSingleMode}} = true may result in using less registers with no need for swapping them to memory slots.
  
 
:{{Par2|optSSE2|bool |(auto)}}
 
:{{Par2|optSSE2|bool |(auto)}}
 
:: If false, disable [[wikipedia:SSE2|SSE2]].
 
:: If false, disable [[wikipedia:SSE2|SSE2]].
:: Enables/Disables SSE2 code generation when in non-AVX2 mode. Setting optSSE2=false and optAVX2=false forces expression processing in a slow interpreted way (C language)</div>
+
::* Enables/Disables SSE2 code generation when in non-AVX2 mode. Setting {{FuncArg|optSSE2}}=false and {{FuncArg|optAVX2}}=false forces expression processing in a slow interpreted way (C language)
 +
</div>
  
/// [[TODO]]: formatting, sections and titles
+
<div style="max-width:62em" >
 +
==== Expressions ====
  
Expr accepts 1 to 26 clips as inputs and up to four expression strings, an optional video format overrider, and some debug parameters.
+
'''Expr''' accepts 1 to 26 '''source clips''', up to four '''expression''' strings (one per color plane), an optional '''output format''' string, and some debug options.
 
Output video format is inherited from the first clip, when there is no format override.
 
Output video format is inherited from the first clip, when there is no format override.
All clips have to match their dimensions and plane subsamplings.
+
All clips have to match in their width, height and [[wikipedia:Chroma_subsampling|chroma subsampling]].
  
 
Expressions are evaluated on each plane, Y, U, V (and A) or R, G, B (,A).
 
Expressions are evaluated on each plane, Y, U, V (and A) or R, G, B (,A).
When an expression string is not specified, the previous expression is used for that plane. Except for plane A (alpha) which is copied by default.
+
When an expression string is not specified, the previous expression is used for that plane &ndash; except for plane A (alpha) which is copied by default.
 
When an expression is an empty string ("") then the relevant plane will be copied (if the output clip bit depth is similar).
 
When an expression is an empty string ("") then the relevant plane will be copied (if the output clip bit depth is similar).
 
When an expression is a single clip reference letter ("x") and the source/target bit depth is similar, then the relevant plane will be copied.
 
When an expression is a single clip reference letter ("x") and the source/target bit depth is similar, then the relevant plane will be copied.
 
When an expression is constant (after constant folding), then the relevant plane will be filled with an optimized memory fill method.  
 
When an expression is constant (after constant folding), then the relevant plane will be filled with an optimized memory fill method.  
  Example: Expr(clip,"255","128,"128") fills all three planes.
+
* Example: <code>Expr(clip, "255", "128, "128")</code> fills all three planes.
  Example2: Expr(clip,"x","range_half,"range_half") copies luma, fills U and V with 128/512/... (bit depth dependent)
+
* Example: <code>Expr(clip, "x", "range_half, "range_half")</code> copies luma, fills U and V with 128/512/... (bit depth dependent)
 
Other optimizations: do not call GetFrame for input clips that are not referenced or plane-copied
 
Other optimizations: do not call GetFrame for input clips that are not referenced or plane-copied
  
Expressions are written in Reverse Polish Notation (RPN).
+
Expressions are written in [[wikipedia:Reverse Polish Notation|RPN]].
  
 
Expressions use 32 bit float precision internally.
 
Expressions use 32 bit float precision internally.
Line 60: Line 68:
 
For 8..16 bit formats output is rounded and clamped from the internal 32 bit float representation to valid 8, 10, ... 16 bits range. 32 bit float output is not clamped at all.
 
For 8..16 bit formats output is rounded and clamped from the internal 32 bit float representation to valid 8, 10, ... 16 bits range. 32 bit float output is not clamped at all.
  
Expr language/RPN elements
+
===== Expr language/RPN elements =====
: Clips: letters x, y, z, a, ... w. x is the first clip parameter, y is the second one, etc.
+
:* Clips: letters ''x'', ''y'', ''z'', ''a''..''w''. ''x'' is the first clip parameter, ''y'' is the second one, etc.
: Math: * / + -
+
:* Math: <code>* / + -</code>
: % (modulo), like fmod. result = x - trunc(x/d)*d. Note: float can hold only a 24 bit integer number (approximately)
+
:* <code>%</code> (modulo), like fmod. Example: <code>result = x - trunc(x/d)*d</code>. Note: the internal 32-bit float can hold only a 24 bit integer number (approximately)
: Math constant: pi
+
:* Math constant: <code>pi</code>
: Functions: min, max, sqrt, abs, neg, exp, log, pow ^ (synonyms: "pow" and "^")
+
:* Functions: <code>min, max, sqrt, abs, neg, exp, log, pow ^</code> (synonyms: <code>pow</code> and <code></code>)
: Functions: sin cos tan asin acos atan (no SSE2/AVX2 optimization when they appear in Expr)
+
:* Functions: <code>sin cos tan asin acos atan</code> (no SSE2/AVX2 optimization when they appear in Expr)
: Logical: > < = >= <= and or xor not == & | != (synonyms: "==" and "=", "&" and "and", "|" and "or")
+
:* Logical: <code> > < = >= <= and or xor not == & | !=</code> (synonyms: <code>==</code> and <code>=</code>, <code>&</code> and <code>and</code>, <code>|</code> and <code>or</code>)
: Ternary operator: ? [[TODO]]: example
+
:* Ternary operator: <code>?</code> Example: <code>x 128 < x y ?</code>
: Duplicate stack: dup, dupN (dup1, dup2, ...)
+
:* Duplicate stack elements: <code>dup, dup''n''</code> (dup1, dup2, ...)
: Swap stack elements: swap, swapN (swap1, swap2, ...)
+
:* Swap stack elements: <code>swap, swap''n''</code> (swap1, swap2, ...)
: Scale by bit shift: scaleb (operand is treated as being a number in 8 bit range unless i8..i16 or f32 is specified)
+
:* Scale by bit shift: <code>scaleb</code> (operand is treated as being a number in 8 bit range unless i8..i16 or f32 is specified)
 +
: * Scale by full scale stretch: <code>scalef</code> (operand is treated as being a number in 8 bit range unless i8..i16 or f32 is specified)
  
: Scale by full scale stretch: scalef (operand is treated as being a number in 8 bit range unless i8..i16 or f32 is specified)
+
===== Bit-depth aware constants =====
 +
:* <code>ymin, ymax</code> (ymin_a .. ymin_z for individual clips) - the usual luma limits (16..235 or scaled equivalents)
 +
:* <code>cmin, cmax</code> (cmin_a .. cmin_z) - chroma limits (16..240 or scaled equivalents)
 +
:* <code>range_half</code> (range_half_a .. range_half_z) - half of the range, (128 or scaled equivalents)
 +
:* <code>range_size, range_half, range_max</code> (range_size_a .. range_size_z , etc..)
  
: Bit-depth aware constants
+
===== Keywords for modifying base bit depth =====
:: ymin, ymax (ymin_a .. ymin_z for individual clips) - the usual luma limits (16..235 or scaled equivalents)
+
:* <code>i8, i10, i12, i14, i16, f32</code> (used with <code>scaleb</code> and <code>scalef</code>)
:: cmin, cmax (cmin_a .. cmin_z) - chroma limits (16..240 or scaled equivalents)
+
:: range_half (range_half_a .. range_half_z) - half of the range, (128 or scaled equivalents)
+
:: range_size, range_half, range_max (range_size_a .. range_size_z , etc..)
+
 
+
: Keywords for modifying base bit depth for scaleb and scalef: i8, i10, i12, i14, i16, f32
+
 
    
 
    
: Spatial input variables in expr syntax:
+
===== Spatial input variables in expr syntax =====
:: sx, sy (absolute x and y coordinates, 0 to width-1 and 0 to height-1)
+
:: <code>sx, sy</code> (absolute x and y coordinates, 0 to width-1 and 0 to height-1)
:: sxr, syr (relative x and y coordinates, from 0 to 1.0)
+
:: <code>sxr, syr</code> (relative x and y coordinates, from 0 to 1.0)
  
: internal variables uppercase A to Z for storing and loading intermediate results within the expression
+
===== Internal variables =====
:: Store: A@ .. Z@  
+
: Uppercase ''A'' to ''Z'' for storing and loading intermediate results within the expression
:: Store and pop from stack: A^ .. Z^
+
:: Store: <code>A@ .. Z@</code>
 +
:: Store and pop from stack: <code>A^ .. Z^</code>
 
:: Use: A..Z
 
:: Use: A..Z
  Example: "x y - A^ x y 0.5 + + B^ A B / C@ x +"
+
:* Example: <code>"x y - A^ x y 0.5 + + B^ A B / C@ x +"</code>
  
: 'frameno' : use current frame number in expression. 0 <= frameno < clip_frame_count
+
: <code>frameno</code> : use current frame number in expression. 0 <= <code>frameno</code> < <tt>clip_frame_count</tt>
: 'time' : calculation: time = frameno/clip_frame_count. Use relative time position in expression. 0 <= time < frameno/clip_frame_count
+
: <code>time</code> : calculation: time = frameno/clip_frame_count. Use relative time position in expression. 0 <= time < frameno/clip_frame_count
: 'width', 'height': clip width and clip height
+
: <code>width, height</code>: clip width and clip height
  
: Indexable source clip pixels by relative x,y positions.  
+
===== Pixel addressing =====
 +
: Indexed, addressable source clip pixels by relative x,y positions.  
 
:: Syntax: x[a,b] where  
 
:: Syntax: x[a,b] where  
 
:: 'x': source clip letter a..z
 
:: 'x': source clip letter a..z
Line 104: Line 114:
 
:: 'a' and 'b' should be constant. e.g.: "x[-1,-1] x[-1,0] x[-1,1] y[0,-10] + + + 4 /"
 
:: 'a' and 'b' should be constant. e.g.: "x[-1,-1] x[-1,0] x[-1,1] y[0,-10] + + + 4 /"
  
When a resulting pixels would come from off-screen the off-screen pixels are cloned from the edge.
+
:: When an pixel would come from off-screen, the pixels are cloned from the edge.
Optimized version of indexed pixels require SSSE3, and no AVX2 version is available. Non-SSSE3 falls back to C for the whole expression
+
:: Optimized version of indexed pixels require SSSE3, and no AVX2 version is available. Non-SSSE3 falls back to C for the whole expression
  
:Example (original code and idea from here: https://forum.doom9.org/showthread.php?p=1738391#post1738391 )
+
==== Compared to MaskTools ====
:: Mandelbrot zoomer
+
Compared to [[MaskTools2|MaskTools2]] version 2.2.12, '''Expr''' has functionality similar to <tt>mt_lut</tt>, <tt>mt_lutxy</tt>, <tt>mt_lutxyz</tt>, <tt>mt_lutxyza</tt> and <tt>mt_lutspa</tt>.
  a="X dup * Y dup * - A + T^ X Y 2 * * B + 2 min Y^ T 2 min X^ "
+
  b=a+a
+
  c=b+b
+
  blankclip(width=960,height=640,length=1600,pixel_type="YUV420P8")
+
  Expr("sxr 3 * 2 - -1.2947627 - 1.01 frameno ^ / -1.2947627 + A@ X^ syr 2 * 1 - 0.4399695 "+\
+
  "- 1.01 frameno ^ / 0.4399695  + B@ Y^ "+c+c+c+c+c+b+a+"X dup * Y dup * + 4 < 0 255 ?","128","128")
+
  
:: Comparing Expr to current masktools2 (2.2.12) version
+
MaskTools2 however is very slow for 10+ bit clips, when a [[wikipedia:Lookup_table|LUT]] (lookup table) cannot be used. The expression is evaluated/interpreted at runtime for each pixel.
  
Expr has similar functionality as mt_lut, mt_lutxy, mt_lutxyz, mt_lutxyza and mt_lutspa in masktools2.
+
The [[wikipedia:Just-in-time_compilation|JIT compiler]] in '''Expr''' (adapted from [http://www.vapoursynth.com/doc/functions/expr.html VapourSynth]) turns the expression calculation into realtime assembly code which is much faster and basically bit depth independent.
  
Masktools2 however is very slow when no lut (lookup table) can be used for 10+ bit clips. The expression is evaluated in an interpreted way for each pixel.
+
: In '''Expr''':
 +
:* Up to 26 clips are allowed (x,y,z,a,b,...w). Masktools handles only up to 4 clips with its <tt>mt_lut</tt>, <tt>mt_lutxy</tt>, <tt>mt_lutxyz</tt>, <tt>mt_lutxyza</tt>
 +
:* Clips with different bit depths are allowed
 +
:* Works with 32 bit floats instead of 64 bit double internally
 +
:* Less functions (e.g. no bit shifts)
 +
:* No float clamping and float-to-8bit-and-back load/store option
 +
:* Logical 'false' is 0 instead of -1
 +
:* The ymin, ymax, etc built-in constants can have a _X suffix, where X is the corresponding clip designator letter. E.g. cmax_z, range_half_x
 +
:* mt_lutspa-like functionality is available through "sx", "sy", "sxr", "syr" internal predefined variables
 +
:* No y= u= v= parameters with negative values for filling plane with constant value, constant expressions are changed into optimized "fill" mode
  
The JIT compiler in Expr turnes the expression calculation into realtime assembly code which is much faster and basically bit depth indepentent.
+
==== Examples ==== 
 
+
: In Expr:
+
:: Up to 26 clips are allowed (x,y,z,a,b,...w). Masktools handles only up to 4 clips with its mt_lut, my_lutxy, mt_lutxyz, mt_lutxyza
+
:: Clips with different bit depths are allowed
+
:: Works with 32 bit floats instead of 64 bit double internally
+
:: Less functions (e.g. no bit shifts)
+
:: No float clamping and float-to-8bit-and-back load/store option
+
:: Logical 'false' is 0 instead of -1
+
:: The ymin, ymax, etc built-in constants can have a _X suffix, where X is the corresponding clip designator letter. E.g. cmax_z, range_half_x
+
:: mt_lutspa-like functionality is available through "sx", "sy", "sxr", "syr" internal predefined variables
+
:: No y= u= v= parameters with negative values for filling plane with constant value, constant expressions are changed into optimized "fill" mode
+
 
+
:: Example:
+
 
+
 
Average three clips:
 
Average three clips:
 +
<div {{BoxWidthIndent|62|2}} >
 
   c = Expr(clip1, clip2, clip3, "x y + z + 3 /")  
 
   c = Expr(clip1, clip2, clip3, "x y + z + 3 /")  
 +
</div>
  
 
Using spatial feature:
 
Using spatial feature:
 +
<div {{BoxWidthIndent|62|2}} >
 
   c = Expr(clip1, clip2, clip3, "sxr syr 1 sxr - 1 syr - * * * 4096 scaleb *", "", "")
 
   c = Expr(clip1, clip2, clip3, "sxr syr 1 sxr - 1 syr - * * * 4096 scaleb *", "", "")
 +
</div>
  
For other ideas of spatial variables, look at the ideas at http://avisynth.nl/index.php/MaskTools2/mt_lutspa
+
Mandelbrot zoomer (original code and idea from here: https://forum.doom9.org/showthread.php?p=1738391#post1738391 )
 +
<div {{BoxWidthIndent|64|2}} >
 +
a="X dup * Y dup * - A + T^ X Y 2 * * B + 2 min Y^ T 2 min X^ "
 +
b=a+a
 +
c=b+b
 +
blankclip(width=960,height=640,length=1600,pixel_type="YUV420P8")
 +
Expr("sxr 3 * 2 - -1.2947627 - 1.01 frameno ^ / -1.2947627 + A@ X^ syr 2 * 1 - 0.4399695 "
 +
\ + "- 1.01 frameno ^ / 0.4399695 + B@ Y^ "+c+c+c+c+c+b+a+"X dup * Y dup * + 4 < 0 255 ?",
 +
\ "128", "128")
 +
</div>
 +
 
 +
For other ideas of spatial variables, see [http://avisynth.nl/index.php/MaskTools2/mt_lutspa MaskTools2:mt_lutspa]
 +
</div>
  
 
==== Changes ====
 
==== Changes ====

Revision as of 04:05, 12 January 2018

AVS+
This feature is specific to AviSynthPlus.

It is not supported in other AviSynth versions.

Applies a mathematical function, defined by an expression string, on the pixels of the source clip(s). A different expression may be set for each color channel. Users of MaskTools may be familiar with this concept.

Contents


Syntax and Parameters

Expr( [clip clip[, ...], string exp[, ...], string format,
      bool optAvx2, bool optSingleMode, bool optSSE2] )

clip  clip =
One or more source clips. Up to 26 input clips can be specified.
  • The first three clips are referenced by lowercase letter x, y and z; use 'a', 'b' ... 'w' for the rest.
  • Clips may be YUV(A), RGB(A), or greyscale; 8-16 bit integer or 32 bit float.
  • Width, height and color subsampling should be the same; bit depths can be different.
string  exp =
One or more RPN expressions
  • A different expression may be set for each color channel (or plane).
  • When an expression string is not given, the previous one is used.
  • The empty string ("") is a valid expression; it causes the plane to be copied (see Expressions below).
string  format = ""
Set color format of the returned clip.
  • Use pixel format strings like "YV12", "YUV420P8", "YUV444P16", "RGBP10".
  • By default, the output format is the same as the first clip.
bool   optAvx2 = (auto)
If false, disable AVX2.
  • Enables/Disables AVX2 code generation if available. Do nothing if AVX2 is not supported in Avisynth.
bool   optSingleMode = false
If true, generate assembly code using only one XMM/YMM register set instead of two; default false.
  • Expr generates assembly code that normally uses two 128 (SSE2) or 256 bit (AVX2) registers ("lanes"), thus processing 8 (SSE2)/16 (AVX2) pixels per internal cycle.
  • Experimental parameter, optSingleMode=true makes the internal compiler generate instructions for only one register (4/8 pixels - SSE2/AVX2). The parameter was introduced to test the speed of x86 code using one working register. Very-very complex expressions would use too many XMM/YMM registers which are then "swapped" to memory slots, that could be slow. Using optSingleMode = true may result in using less registers with no need for swapping them to memory slots.
bool   optSSE2 = (auto)
If false, disable SSE2.
  • Enables/Disables SSE2 code generation when in non-AVX2 mode. Setting optSSE2=false and optAVX2=false forces expression processing in a slow interpreted way (C language)

Expressions

Expr accepts 1 to 26 source clips, up to four expression strings (one per color plane), an optional output format string, and some debug options. Output video format is inherited from the first clip, when there is no format override. All clips have to match in their width, height and chroma subsampling.

Expressions are evaluated on each plane, Y, U, V (and A) or R, G, B (,A). When an expression string is not specified, the previous expression is used for that plane – except for plane A (alpha) which is copied by default. When an expression is an empty string ("") then the relevant plane will be copied (if the output clip bit depth is similar). When an expression is a single clip reference letter ("x") and the source/target bit depth is similar, then the relevant plane will be copied. When an expression is constant (after constant folding), then the relevant plane will be filled with an optimized memory fill method.

  • Example: Expr(clip, "255", "128, "128") fills all three planes.
  • Example: Expr(clip, "x", "range_half, "range_half") copies luma, fills U and V with 128/512/... (bit depth dependent)

Other optimizations: do not call GetFrame for input clips that are not referenced or plane-copied

Expressions are written in RPN.

Expressions use 32 bit float precision internally.

For 8..16 bit formats output is rounded and clamped from the internal 32 bit float representation to valid 8, 10, ... 16 bits range. 32 bit float output is not clamped at all.

Expr language/RPN elements
  • Clips: letters x, y, z, a..w. x is the first clip parameter, y is the second one, etc.
  • Math: * / + -
  • % (modulo), like fmod. Example: result = x - trunc(x/d)*d. Note: the internal 32-bit float can hold only a 24 bit integer number (approximately)
  • Math constant: pi
  • Functions: min, max, sqrt, abs, neg, exp, log, pow ^ (synonyms: pow and )
  • Functions: sin cos tan asin acos atan (no SSE2/AVX2 optimization when they appear in Expr)
  • Logical: > < = >= <= and or xor not == & | != (synonyms: == and =, & and and, | and or)
  • Ternary operator: ? Example: x 128 < x y ?
  • Duplicate stack elements: dup, dupn (dup1, dup2, ...)
  • Swap stack elements: swap, swapn (swap1, swap2, ...)
  • Scale by bit shift: scaleb (operand is treated as being a number in 8 bit range unless i8..i16 or f32 is specified)
* Scale by full scale stretch: scalef (operand is treated as being a number in 8 bit range unless i8..i16 or f32 is specified)
Bit-depth aware constants
  • ymin, ymax (ymin_a .. ymin_z for individual clips) - the usual luma limits (16..235 or scaled equivalents)
  • cmin, cmax (cmin_a .. cmin_z) - chroma limits (16..240 or scaled equivalents)
  • range_half (range_half_a .. range_half_z) - half of the range, (128 or scaled equivalents)
  • range_size, range_half, range_max (range_size_a .. range_size_z , etc..)
Keywords for modifying base bit depth
  • i8, i10, i12, i14, i16, f32 (used with scaleb and scalef)
Spatial input variables in expr syntax
sx, sy (absolute x and y coordinates, 0 to width-1 and 0 to height-1)
sxr, syr (relative x and y coordinates, from 0 to 1.0)
Internal variables
Uppercase A to Z for storing and loading intermediate results within the expression
Store: A@ .. Z@
Store and pop from stack: A^ .. Z^
Use: A..Z
  • Example: "x y - A^ x y 0.5 + + B^ A B / C@ x +"
frameno : use current frame number in expression. 0 <= frameno < clip_frame_count
time : calculation: time = frameno/clip_frame_count. Use relative time position in expression. 0 <= time < frameno/clip_frame_count
width, height: clip width and clip height
Pixel addressing
Indexed, addressable source clip pixels by relative x,y positions.
Syntax: x[a,b] where
'x': source clip letter a..z
'a': horizontal shift. -width < a < width
'b': vertical shift. -height < b < height
'a' and 'b' should be constant. e.g.: "x[-1,-1] x[-1,0] x[-1,1] y[0,-10] + + + 4 /"
When an pixel would come from off-screen, the pixels are cloned from the edge.
Optimized version of indexed pixels require SSSE3, and no AVX2 version is available. Non-SSSE3 falls back to C for the whole expression

Compared to MaskTools

Compared to MaskTools2 version 2.2.12, Expr has functionality similar to mt_lut, mt_lutxy, mt_lutxyz, mt_lutxyza and mt_lutspa.

MaskTools2 however is very slow for 10+ bit clips, when a LUT (lookup table) cannot be used. The expression is evaluated/interpreted at runtime for each pixel.

The JIT compiler in Expr (adapted from VapourSynth) turns the expression calculation into realtime assembly code which is much faster and basically bit depth independent.

In Expr:
  • Up to 26 clips are allowed (x,y,z,a,b,...w). Masktools handles only up to 4 clips with its mt_lut, mt_lutxy, mt_lutxyz, mt_lutxyza
  • Clips with different bit depths are allowed
  • Works with 32 bit floats instead of 64 bit double internally
  • Less functions (e.g. no bit shifts)
  • No float clamping and float-to-8bit-and-back load/store option
  • Logical 'false' is 0 instead of -1
  • The ymin, ymax, etc built-in constants can have a _X suffix, where X is the corresponding clip designator letter. E.g. cmax_z, range_half_x
  • mt_lutspa-like functionality is available through "sx", "sy", "sxr", "syr" internal predefined variables
  • No y= u= v= parameters with negative values for filling plane with constant value, constant expressions are changed into optimized "fill" mode

Examples

Average three clips:

 c = Expr(clip1, clip2, clip3, "x y + z + 3 /") 

Using spatial feature:

 c = Expr(clip1, clip2, clip3, "sxr syr 1 sxr - 1 syr - * * * 4096 scaleb *", "", "")

Mandelbrot zoomer (original code and idea from here: https://forum.doom9.org/showthread.php?p=1738391#post1738391 )

a="X dup * Y dup * - A + T^ X Y 2 * * B + 2 min Y^ T 2 min X^ "
b=a+a
c=b+b
blankclip(width=960,height=640,length=1600,pixel_type="YUV420P8")
Expr("sxr 3 * 2 - -1.2947627 - 1.01 frameno ^ / -1.2947627 + A@ X^ syr 2 * 1 - 0.4399695 "
\ + "- 1.01 frameno ^ / 0.4399695 + B@ Y^ "+c+c+c+c+c+b+a+"X dup * Y dup * + 4 < 0 255 ?",
\ "128", "128")

For other ideas of spatial variables, see MaskTools2:mt_lutspa

Changes

r2574 (20171219) new: Indexable source clip pixels by relative x,y positions like x[-1,1]

new functions: sin cos tan asin acos atan
new operator: % (modulo)
new: Variables: uppercase letters A..Z for storing and reuse temporary results, frequently used computations.
new: predefined expr variables 'frameno', 'time', 'width', 'height'
fix: jitasm code generation at specific circumstances

r2544 (20171115) optimization; fix scalef
r2542 (20171114) first added
Personal tools