manager ¶
This module provides a Pythonic interface for building and evaluating complex VapourSynth expressions using standard Python syntax.
Functions:
-
inline_expr
–A context manager for building and evaluating VapourSynth expressions in a Pythonic way.
InlineExprWrapper ¶
InlineExprWrapper(
clips: Sequence[VideoNode],
format: HoldsVideoFormatT | VideoFormatT | None = None,
)
Bases: tuple[Sequence[ClipVar], Operators, 'InlineExprWrapper']
, vs_object
A wrapper class for constructing and evaluating VapourSynth expressions inline using Python syntax.
This class is intended to be used within the inline_expr context manager and serves as the interface through which you build expressions using overloaded Python operators and expressive constructs.
It provides access to input clips as ClipVar instances, expression operators, and the final output clip.
All expressions are constructed in a high-level, readable Python syntax that is internally translated to VapourSynth-compatible expression strings.
Usage:
with inline_expr([clip_a, clip_b]) as ie:
avg = (ie.vars[0] + ie.vars[1]) / 2
ie.out = avg
result = ie.clip
Note
The InlineExprWrapper
also behaves like a tuple containing:
- The clip variables (
vars
). - Expression operator functions (
op
). - The wrapper itself (
ie
).
This allows unpacking like:
with inline_expr([clip_a, clip_b]) as (vars, op, ie):
...
Initializes a new InlineExprWrapper instance.
Parameters:
-
clips
¶Sequence[VideoNode]
) –Input clip(s).
-
format
¶HoldsVideoFormatT | VideoFormatT | None
, default:None
) –Output format, defaults to the first clip format.
Methods:
-
as_var
–Converts an expression variable to a ComputedVar.
Attributes:
-
clip
(VideoNode
) –The output VapourSynth clip generated from the final expression.
-
op
–Operators object providing access to all
Expr
operators. -
out
(ComputedVar
) –The final expression node representing the result of the expression.
-
tk
–Tokens object providing access to all
Expr
tokens. -
vars
(Sequence[ClipVar]
) –Sequence of ClipVar objects, one for each input clip.
Source code
344 345 346 347 348 349 350 351 352 353 354 355 356 |
|
clip property
¶
clip: VideoNode
The output VapourSynth clip generated from the final expression.
This is only accessible after the context block has exited.
Raises:
-
CustomValueError
–If accessed inside the context manager.
Returns:
-
VideoNode
–The resulting clip after evaluating the expression.
op class-attribute
instance-attribute
¶
op = Operators()
Operators object providing access to all Expr
operators.
out property
writable
¶
out: ComputedVar
The final expression node representing the result of the expression.
This is the computed expression that will be translated into a VapourSynth expression string. It must be assigned inside the context using ie.out = ...
.
tk class-attribute
instance-attribute
¶
tk = Tokens()
Tokens object providing access to all Expr
tokens.
vars cached
property
¶
Sequence of ClipVar objects, one for each input clip.
These objects overload standard Python operators (+
, -
, *
, /
, **
, ==
, <
, >
etc.) to build the expression. They also provide relative pixel access through the __getitem__
dunder method (e.g., x[1, 0]
for the pixel to the right), arbitrary access to frame properties (e.g. x.props.PlaneStatsMax
or x.props["PlaneStatsMax"]
) and bit-depth aware constants access (e.g., x.RangeMax
or x.Neutral
).
Returns:
as_var staticmethod
¶
as_var(x: ExprVarLike | Iterable[ExprVarLike] = '') -> ComputedVar
Converts an expression variable to a ComputedVar.
Parameters:
-
x
¶ExprVarLike | Iterable[ExprVarLike]
, default:''
) –A single ExprVarLike or an Iterable of ExprVarLike.
Returns:
-
ComputedVar
–A ComputedVar.
Source code
379 380 381 382 383 384 385 386 387 388 389 390 |
|
inline_expr ¶
inline_expr(
clips: VideoNode | Sequence[VideoNode],
format: HoldsVideoFormatT | VideoFormatT | None = None,
*,
enable_polyfills: bool = False,
**kwargs: Any
) -> Iterator[InlineExprWrapper]
A context manager for building and evaluating VapourSynth expressions in a Pythonic way.
This function allows you to write complex VapourSynth expressions using standard Python operators and syntax, abstracting away the underlying RPN (Reverse Polish Notation) string.
- https://www.vapoursynth.com/doc/functions/video/expr.html
- https://github.com/AkarinVS/vapoursynth-plugin/wiki/Expr
The context manager is initialized with one or more VapourSynth clips and yields a InlineExprWrapper object containing clip variables and operators.
Usage:
with inline_expr(clips) as ie:
# ... build your expression here ...
ie.out = ...
# The final, processed clip is available after the context block.
result_clip = ie.clip
-
Example (simple): Averaging two clips
from vsexprtools import inline_expr from vstools import core, vs clip_a = core.std.BlankClip(format=vs.YUV420P8, color=[255, 0, 0]) clip_b = core.std.BlankClip(format=vs.YUV420P8, color=[0, 255, 0]) with inline_expr([clip_a, clip_b]) as ie: # ie.vars[0] is clip_a, ie.vars[1] is clip_b average = (ie.vars[0] + ie.vars[1]) / 2 ie.out = average result = ie.clip
-
Example (simple): Averaging 20 random clips
from vsexprtools import inline_expr from vstools import core, vs import random def spawn_random(amount: int) -> list[vs.VideoNode]: clips = list[vs.VideoNode]() for _ in range(amount): r = random.randint(0, 255) g = random.randint(0, 255) b = random.randint(0, 255) clips.append(core.std.BlankClip(format=vs.RGB24, color=[r, g, b])) return clips with inline_expr(spawn_random(20)) as ie: ie.out = sum(ie.vars) / len(ie.vars) # -> "x y + z + a + b + c + d + e + f + g + h + i + j + k + l + m + n + o + p + q + 20 /" result = ie.clip
-
Example (advanced): prefilter_to_full_range implemented with
inline_expr
from vsexprtools import inline_expr from vstools import ColorRange, vs def pf_full(clip: vs.VideoNode, slope: float = 2.0, smooth: float = 0.0625) -> vs.VideoNode: with inline_expr(clip) as ie: x, *_ = ie.vars # Normalize luma to 0-1 range norm_luma = (x - x.LumaRangeInMin) * (ie.as_var(1) / (x.LumaRangeInMax - x.LumaRangeInMin)) # Ensure normalized luma stays within bounds norm_luma = ie.op.clamp(norm_luma, 0, 1) # Curve factor controls non-linearity based on slope and smoothing curve_strength = (slope - 1) * smooth # Slope increases contrast in darker regions # Compute a non-linear boost that emphasizes dark details without crushing blacks nonlinear_boost = curve_strength * ((1 + smooth) - (ie.op.sin(smooth) / (norm_luma + smooth))) # Combine the non-linear boost with the normalized luma # Boosts shadows while preserving highlights weight_mul = nonlinear_boost + norm_luma * (1 - curve_strength) # Scale the final result back to the output range weight_mul *= x.RangeMax # Assign the processed luma to the Y plane ie.out.y = weight_mul # Round only if the format is integer if clip.format.sample_type is vs.INTEGER: ie.out.y = ie.op.round(ie.out.y) if ColorRange.from_video(clip).is_full or clip.format.sample_type is vs.FLOAT: ie.out.uv = x else: # Scale chroma values from limited to full range: # - Subtract neutral chroma (e.g., 128) to center around zero # - Scale based on the input chroma range (e.g., 240 - 16) # - Add half of full range to re-center in full output range chroma_mult = x.RangeMax / (x.ChromaRangeInMax - x.ChromaRangeInMin) chroma_boosted = (x - x.Neutral) * chroma_mult + x.RangeHalf # Apply the adjusted chroma values to U and V planes ie.out.uv = ie.op.round(chroma_boosted) # Final output is flagged as full-range video return ColorRange.FULL.apply(ie.clip)
-
Example (complex): Unsharp mask implemented in inline_expr. Extended with configurable anti-ringing and anti-aliasing, and frequency-based limiting.
import functools import itertools from dataclasses import dataclass from vsexprtools import inline_expr from vsmasktools import Sobel from vstools import ConvMode, vs @dataclass class LowFreqSettings: freq_limit: float = 0.1 freq_ratio_scale: float = 5.0 max_reduction: float = 0.95 def unsharp_limited( clip: vs.VideoNode, strength: float = 1.5, limit: float = 0.3, low_freq: LowFreqSettings = LowFreqSettings(freq_limit=0.1), ) -> vs.VideoNode: with inline_expr(clip) as ie: x = ie.vars[0] # Calculate blur for sharpening base blur = ie.op.convolution(x, [1] * 9) # Calculate sharpening amount sharp_diff = (x - blur) * strength effective_sharp_diff = sharp_diff # Apply low-frequency only processing if parameter > 0 if low_freq.freq_limit > 0: # Calculate high-frequency component by comparing local variance to a larger area wider_blur = sum(x[i, j] for i, j in itertools.product([-2, 0, 2], repeat=2) if (i, j) != (0, 0)) wider_blur = ie.as_var(wider_blur) / 9 high_freq_indicator = abs(blur - wider_blur) # Calculate texture complexity (higher in detailed areas, # lower in flat areas) texture_complexity = ie.op.max(abs(x - blur), abs(blur - wider_blur)) # Reduce sharpening in areas with high frequency content # but low texture complexity freq_ratio = ie.op.max(high_freq_indicator / (texture_complexity + 0.01), 0) low_freq_factor = 1.0 - ie.op.min( freq_ratio * low_freq.freq_ratio_scale * low_freq.freq_limit, low_freq.max_reduction ) # Apply additional limiting for high-frequency content # to effective_sharp_diff effective_sharp_diff = effective_sharp_diff * low_freq_factor # Get horizontal neighbors from the original clip neighbors = [ie.as_var(x) for x in ie.op.matrix(x, 1, ConvMode.SQUARE, [(0, 0)])] # Calculate minimum local_min = functools.reduce(ie.op.min, neighbors) # Calculate maximum local_max = functools.reduce(ie.op.max, neighbors) # Only calculate adaptive limiting if limit > 0 if limit > 0: # Calculate local variance to detect edges (high variance = potential aliasing) variance = sum(((n - x) ** 2 for n in neighbors)) / 8 # Calculate edge detection using Sobel-like operators h_conv, v_conv = Sobel.matrices h_edge = ie.op.convolution(x, h_conv, divisor=False, saturate=False) v_edge = ie.op.convolution(x, v_conv, divisor=False, saturate=False) edge_strength = ie.op.sqrt(h_edge**2 + v_edge**2) # Adaptive sharpening strength based on edge detection and variance # Reduce sharpening in high-variance areas to prevent aliasing edge_factor = 1.0 - ie.op.min(edge_strength * 0.01, limit) var_factor = 1.0 - ie.op.min(variance * 0.005, limit) adaptive_strength = edge_factor * var_factor # Apply adaptive sharpening to the effective_sharp_diff effective_sharp_diff = effective_sharp_diff * adaptive_strength # Clamp the sharp_diff to the local min and max to prevent ringing final_output = ie.op.clamp(x + effective_sharp_diff, local_min, local_max) else: # If limit is 0 or less, use the effective_sharp_diff (which might be basic or low-freq adjusted) final_output = x + effective_sharp_diff # Set the final output ie.out = final_output return ie.clip
Parameters:
-
clips
¶VideoNode | Sequence[VideoNode]
) –Input clip(s).
-
format
¶HoldsVideoFormatT | VideoFormatT | None
, default:None
) –format: Output format, defaults to the first clip format.
-
enable_polyfills
¶bool
, default:False
) –Enable monkey-patching built-in methods. Maybe more than that, nobody knows.
-
**kwargs
¶Any
, default:{}
) –Additional keyword arguments passed to norm_expr.
Yields:
-
InlineExprWrapper
–InlineExprWrapper object containing clip variables and operators.
-
The vars attribute is a sequence of ClipVar objects, one for each input clip. These objects overload standard Python operators (
+
,-
,*
,/
,**
,==
,<
,>
etc.) to build the expression. They also provide relative pixel access through the__getitem__
dunder method (e.g.,x[1, 0]
for the pixel to the right), arbitrary access to frame properties (e.g.x.props.PlaneStatsMax
orx.props["PlaneStatsMax"]
) and bit-depth aware constants access (e.g.,x.RangeMax
orx.Neutral
). -
The op attribute is an object providing access to all
Expr
operators such asop.clamp(value, min, max)
,op.sqrt(value)
,op.tern(condition, if_true, if_false)
, etc.
You must assign the final ComputedVar (the result of your expression) to
ie.out
.Additionnaly, you can use
print(ie.out)
to see the computed expression string orprint(ie.out.to_str_per_plane())
orprint(ie.out.to_str(plane=...))
to see the expression per plane. -
Source code
24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 |
|