Skip to content

util

Classes:

  • LinearLight

    Utility class for processing a clip in linear format.

  • NoScale

    A utility scaler class that performs no scaling on the input clip.

Functions:

LinearLight dataclass

LinearLight(
    clip: VideoNode,
    sigmoid: bool | tuple[Slope, Center] = False,
    resampler: ResamplerLike = Catrom,
    out_fmt: int | VideoFormatT | HoldsVideoFormatT | None = None,
)

Bases: AbstractContextManager[LinearLightProcessing], vs_object

Utility class for processing a clip in linear format.

Usage:

with LinearLight(clip, ...) as ll:
    ll.linear = function(ll.linear, ...)
out = ll.out

Methods:

  • from_func

    Decorator version of LinearLight.

Attributes:

clip instance-attribute

clip: VideoNode

Input clip.

out_fmt class-attribute instance-attribute

out_fmt: int | VideoFormatT | HoldsVideoFormatT | None = None

Optional output format.

resampler class-attribute instance-attribute

resampler: ResamplerLike = Catrom

Resampler for converting to linear format and converting back to input clip format.

sigmoid class-attribute instance-attribute

sigmoid: bool | tuple[Slope, Center] = False

Whether to use sigmoid transfer curve. Can be True, False, or a tuple of (slope, center). True applies the defaults values (6.5, 0.75). Keep in mind sigmoid slope has to be in range 1.0-20.0. (inclusive) and sigmoid center has to be in range 0.0-1.0 (inclusive).

from_func classmethod

from_func(
    func: Callable[Concatenate[VideoNode, P], VideoNode],
    /,
    sigmoid: bool | tuple[Slope, Center] = False,
    resampler: ResamplerLike = Catrom,
    out_fmt: int | VideoFormatT | HoldsVideoFormatT | None = None,
) -> Callable[Concatenate[VideoNode, P], VideoNode]
from_func(
    *,
    sigmoid: bool | tuple[Slope, Center] = False,
    resampler: ResamplerLike = Catrom,
    out_fmt: int | VideoFormatT | HoldsVideoFormatT | None = None
) -> Callable[
    [Callable[Concatenate[VideoNode, P], VideoNode]],
    Callable[Concatenate[VideoNode, P], VideoNode],
]
from_func(
    func: Callable[Concatenate[VideoNode, P], VideoNode] | None = None,
    /,
    sigmoid: bool | tuple[Slope, Center] = False,
    resampler: ResamplerLike = Catrom,
    out_fmt: int | VideoFormatT | HoldsVideoFormatT | None = None,
) -> Union[
    Callable[Concatenate[VideoNode, P], VideoNode],
    Callable[
        [Callable[Concatenate[VideoNode, P], VideoNode]],
        Callable[Concatenate[VideoNode, P], VideoNode],
    ],
]

Decorator version of LinearLight.

Source code
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
@classmethod
def from_func(
    cls,
    func: Callable[Concatenate[vs.VideoNode, P], vs.VideoNode] | None = None,
    /,
    sigmoid: bool | tuple[Slope, Center] = False,
    resampler: ResamplerLike = Catrom,
    out_fmt: int | VideoFormatT | HoldsVideoFormatT | None = None
) -> Union[
    Callable[Concatenate[vs.VideoNode, P], vs.VideoNode],
    Callable[
        [Callable[Concatenate[vs.VideoNode, P], vs.VideoNode]],
        Callable[Concatenate[vs.VideoNode, P], vs.VideoNode]
    ]
]:
    """Decorator version of LinearLight."""

    if func is None:
        return partial(cls.from_func, sigmoid=sigmoid, resampler=resampler, out_fmt=out_fmt)

    @wraps(func)
    def _wrapped(clip: vs.VideoNode, *args: P.args, **kwargs: P.kwargs) -> vs.VideoNode:
        with cls(clip, sigmoid, resampler, out_fmt) as ll:
            ll.linear = func(clip, *args, **kwargs)
        return ll.out

    return _wrapped

LinearLightProcessing dataclass

LinearLightProcessing(ll: LinearLight)

Bases: baseclass

Methods:

Attributes:

linear class-attribute instance-attribute

linear = cachedproperty[[Self], VideoNode, Self, VideoNode, ...](
    get_linear, set_linear
)

Cached property to use for linear light processing.

ll instance-attribute

get_linear

get_linear() -> VideoNode

Getter for linear cached property.

Source code
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
def get_linear(self) -> vs.VideoNode:
    """Getter for `linear` cached property."""
    wclip = self.ll._resampler.resample(
        self.ll._wclip,
        vs.RGBS if self.ll._wclip.format.color_family in (vs.YUV, vs.RGB) else vs.GRAYS,
        matrix_in=self.ll._matrix,
        transfer_in=self.ll._curve,
        transfer=Transfer.LINEAR
    )

    if self.ll.sigmoid:
        wclip = norm_expr(
            wclip,
            '{center} 1 {slope} / 1 x 0 max 1 min {scale} * {offset} + / 1 - log * -',
            center=self.ll._scenter, slope=self.ll._sslope,
            scale=self.ll._sscale, offset=self.ll._soffset,
            func=self.__class__
        )

    return wclip

out

out() -> VideoNode
Source code
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
@cachedproperty
def out(self) -> vs.VideoNode:
    if not self.ll._exited:
        raise CustomRuntimeError(
            'You can\'t get .out while still inside of the context manager!', func=self.__class__
        )

    if not hasattr(self, '_linear'):
        raise CustomValueError('You need to set .linear before getting .out!', self.__class__)

    if self.ll.sigmoid:
        processed = norm_expr(
            self._linear,
            '1 1 {slope} {center} x 0 max 1 min - * exp + / {offset} - {scale} /',
            slope=self.ll._sslope, center=self.ll._scenter,
            offset=self.ll._soffset, scale=self.ll._sscale,
            func=self.__class__
        )
    else:
        processed = self._linear

    processed = vs.core.resize2.Point(processed, transfer_in=Transfer.LINEAR, transfer=self.ll._curve)

    return resample_to(processed, self.ll._fmt, self.ll._matrix, self.ll._resampler)

set_linear

set_linear(processed: VideoNode) -> None

Setter for linear cached property.

Source code
131
132
133
134
135
136
137
def set_linear(self, processed: vs.VideoNode) -> None:
    """Setter for `linear` cached property."""
    if self.ll._exited:
        raise CustomRuntimeError(
            'You can\'t set .linear after going out of the context manager!', func=self.__class__
        )
    self._linear = processed

NoScale

NoScale(**kwargs: Any)

Bases: Scaler, Generic[_ScalerT]

A utility scaler class that performs no scaling on the input clip.

If used without a specified scaler, it defaults to inheriting from Catrom.

Initialize the scaler with optional keyword arguments.

These keyword arguments are automatically forwarded to the _implemented_funcs methods but only if the method explicitly accepts them as named parameters. If the same keyword is passed to both __init__ and one of the _implemented_funcs, the one passed to func takes precedence.

Parameters:

  • kwargs

    (Any, default: {} ) –

    Keyword arguments that configure the internal scaling behavior.

Classes:

Methods:

  • ensure_obj

    Ensure that the input is a scaler instance, resolving it if necessary.

  • from_param

    Resolve and return a scaler type from a given input (string, type, or instance).

  • from_scaler

    Create a specialized NoScale class using a specific scaler.

  • get_scale_args

    Generate the keyword arguments used for scaling.

  • kernel_radius

    Return the effective kernel radius for the scaler.

  • multi

    Deprecated alias for supersample.

  • pretty_string

    Cached property returning a user-friendly string representation.

  • scale

    Return the input clip unscaled, validating that the dimensions are consistent.

  • supersample

    Supersample a clip by a given scaling factor.

Attributes:

  • kwargs (dict[str, Any]) –

    Arguments passed to the implemented funcs or internal scale function.

  • scale_function (Callable[..., VideoNode]) –

    Scale function called internally when performing scaling operations.

Source code
281
282
283
284
285
286
287
288
289
290
291
292
def __init__(self, **kwargs: Any) -> None:
    """
    Initialize the scaler with optional keyword arguments.

    These keyword arguments are automatically forwarded to the `_implemented_funcs` methods
    but only if the method explicitly accepts them as named parameters.
    If the same keyword is passed to both `__init__` and one of the `_implemented_funcs`,
    the one passed to `func` takes precedence.

    :param kwargs:  Keyword arguments that configure the internal scaling behavior.
    """
    self.kwargs = kwargs

kwargs instance-attribute

kwargs: dict[str, Any] = kwargs

Arguments passed to the implemented funcs or internal scale function.

scale_function instance-attribute

scale_function: Callable[..., VideoNode]

Scale function called internally when performing scaling operations.

cached_property

cached_property(func: Callable[Concatenate[_BaseScalerT, P], T_co])

Bases: cached_property[T_co]

Read only version of functools.cached_property.

Source code
265
def __init__(self, func: Callable[Concatenate[_BaseScalerT, P], T_co]) -> None: ...

ensure_obj classmethod

ensure_obj(
    scaler: str | type[Self] | Self | None = None,
    /,
    func_except: FuncExceptT | None = None,
) -> Self

Ensure that the input is a scaler instance, resolving it if necessary.

Parameters:

  • scaler

    (str | type[Self] | Self | None, default: None ) –

    Scaler identifier (string, class, or instance).

  • func_except

    (FuncExceptT | None, default: None ) –

    Function returned for custom error handling.

Returns:

  • Self

    Scaler instance.

Source code
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
@classmethod
def ensure_obj(
    cls,
    scaler: str | type[Self] | Self | None = None,
    /,
    func_except: FuncExceptT | None = None,
) -> Self:
    """
    Ensure that the input is a scaler instance, resolving it if necessary.

    :param scaler:          Scaler identifier (string, class, or instance).
    :param func_except:     Function returned for custom error handling.
    :return:                Scaler instance.
    """
    return _base_ensure_obj(cls, scaler, func_except)

from_param classmethod

from_param(
    scaler: str | type[Self] | Self | None = None,
    /,
    func_except: FuncExceptT | None = None,
) -> type[Self]

Resolve and return a scaler type from a given input (string, type, or instance).

Parameters:

  • scaler

    (str | type[Self] | Self | None, default: None ) –

    Scaler identifier (string, class, or instance).

  • func_except

    (FuncExceptT | None, default: None ) –

    Function returned for custom error handling.

Returns:

  • type[Self]

    Resolved scaler type.

Source code
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
@classmethod
def from_param(
    cls,
    scaler: str | type[Self] | Self | None = None,
    /,
    func_except: FuncExceptT | None = None,
) -> type[Self]:
    """
    Resolve and return a scaler type from a given input (string, type, or instance).

    :param scaler:          Scaler identifier (string, class, or instance).
    :param func_except:     Function returned for custom error handling.
    :return:                Resolved scaler type.
    """
    return _base_from_param(cls, scaler, cls._err_class, func_except)

from_scaler classmethod

from_scaler(scaler: ScalerLike) -> type[NoScale[Scaler]]

Create a specialized NoScale class using a specific scaler.

Parameters:

  • scaler

    (ScalerLike) –

    A Scaler instance, type or string used as a base for specialization.

Returns:

  • type[NoScale[Scaler]]

    A dynamically created NoScale subclass based on the given scaler.

Source code
 95
 96
 97
 98
 99
100
101
102
103
@classmethod
def from_scaler(cls, scaler: ScalerLike) -> type[NoScale[Scaler]]:
    """
    Create a specialized NoScale class using a specific scaler.

    :param scaler:  A Scaler instance, type or string used as a base for specialization.
    :return:        A dynamically created NoScale subclass based on the given scaler.
    """
    return NoScale[Scaler.from_param(scaler)]  # type: ignore[return-value,misc]

get_scale_args

get_scale_args(
    clip: VideoNode,
    shift: tuple[TopShift, LeftShift] = (0, 0),
    width: int | None = None,
    height: int | None = None,
    **kwargs: Any
) -> dict[str, Any]

Generate the keyword arguments used for scaling.

Parameters:

  • clip

    (VideoNode) –

    The source clip.

  • shift

    (tuple[TopShift, LeftShift], default: (0, 0) ) –

    Subpixel shift (top, left).

  • width

    (int | None, default: None ) –

    Target width.

  • height

    (int | None, default: None ) –

    Target height.

  • kwargs

    (Any, default: {} ) –

    Extra parameters to merge.

Returns:

  • dict[str, Any]

    Final dictionary of keyword arguments for the scale function.

Source code
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
def get_scale_args(
    self,
    clip: vs.VideoNode,
    shift: tuple[TopShift, LeftShift] = (0, 0),
    width: int | None = None,
    height: int | None = None,
    **kwargs: Any,
) -> dict[str, Any]:
    """
    Generate the keyword arguments used for scaling.

    :param clip:    The source clip.
    :param shift:   Subpixel shift (top, left).
    :param width:   Target width.
    :param height:  Target height.
    :param kwargs:  Extra parameters to merge.
    :return:        Final dictionary of keyword arguments for the scale function.
    """
    return dict(width=width, height=height, src_top=shift[0], src_left=shift[1]) | self.kwargs | kwargs

kernel_radius

kernel_radius() -> int

Return the effective kernel radius for the scaler.

Returns:

  • int

    Kernel radius.

Raises:

  • CustomNotImplementedError

    If no kernel radius is defined.

Source code
347
348
349
350
351
352
353
354
355
@cached_property
def kernel_radius(self) -> int:
    """
    Return the effective kernel radius for the scaler.

    :raises CustomNotImplementedError:  If no kernel radius is defined.
    :return:                            Kernel radius.
    """
    ...

multi

multi(
    clip: VideoNodeT,
    multi: float = 2.0,
    shift: tuple[TopShift, LeftShift] = (0, 0),
    **kwargs: Any
) -> VideoNodeT

Deprecated alias for supersample.

Parameters:

  • clip

    (VideoNodeT) –

    The source clip.

  • multi

    (float, default: 2.0 ) –

    Supersampling factor.

  • shift

    (tuple[TopShift, LeftShift], default: (0, 0) ) –

    Subpixel shift (top, left) applied during scaling.

  • kwargs

    (Any, default: {} ) –

    Additional arguments forwarded to the scale function.

Returns:

Source code
451
452
453
454
455
456
457
458
459
460
461
462
463
464
@deprecated('The "multi" method is deprecated. Use "supersample" instead.', category=DeprecationWarning)
def multi(
    self, clip: VideoNodeT, multi: float = 2.0, shift: tuple[TopShift, LeftShift] = (0, 0), **kwargs: Any
) -> VideoNodeT:
    """
    Deprecated alias for `supersample`.

    :param clip:    The source clip.
    :param multi:   Supersampling factor.
    :param shift:   Subpixel shift (top, left) applied during scaling.
    :param kwargs:  Additional arguments forwarded to the scale function.
    :return:        The supersampled clip.
    """
    return self.supersample(clip, multi, shift, **kwargs)

pretty_string

pretty_string() -> str

Cached property returning a user-friendly string representation.

Returns:

  • str

    Pretty-printed string with arguments.

Source code
368
369
370
371
372
373
374
375
@cached_property
def pretty_string(self) -> str:
    """
    Cached property returning a user-friendly string representation.

    :return: Pretty-printed string with arguments.
    """
    return self._pretty_string()

scale

scale(
    clip: VideoNode,
    width: int | None = None,
    height: int | None = None,
    shift: tuple[TopShift, LeftShift] = (0, 0),
    **kwargs: Any
) -> VideoNode | ConstantFormatVideoNode

Return the input clip unscaled, validating that the dimensions are consistent.

Parameters:

  • clip

    (VideoNode) –

    The source clip.

  • width

    (int | None, default: None ) –

    Optional width to validate against the clip's width.

  • height

    (int | None, default: None ) –

    Optional height to validate against the clip's height.

  • shift

    (tuple[TopShift, LeftShift], default: (0, 0) ) –

    Subpixel shift (top, left).

  • kwargs

    (Any, default: {} ) –

    Additional arguments forwarded to the scale function.

Raises:

  • CustomValueError

    If width or height differ from the clip's dimensions.

Source code
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
def scale(
    self,
    clip: vs.VideoNode,
    width: int | None = None,
    height: int | None = None,
    shift: tuple[TopShift, LeftShift] = (0, 0),
    **kwargs: Any,
) -> vs.VideoNode | ConstantFormatVideoNode:
    """
    Return the input clip unscaled, validating that the dimensions are consistent.

    :param clip:                The source clip.
    :param width:               Optional width to validate against the clip's width.
    :param height:              Optional height to validate against the clip's height.
    :param shift:               Subpixel shift (top, left).
    :param kwargs:              Additional arguments forwarded to the scale function.
    :raises CustomValueError:   If `width` or `height` differ from the clip's dimensions.
    """
    width, height = self._wh_norm(clip, width, height)

    if width != clip.width or height != clip.height:
        raise CustomValueError(
            "When using NoScale, `width` and `height` must match the clip's dimensions.",
            self.__class__, (width, height)
        )

    if shift == (0, 0) and not kwargs and not self.kwargs:
        return clip

    return super().scale(clip, width, height, shift, **kwargs)

supersample

supersample(
    clip: VideoNodeT,
    rfactor: float = 2.0,
    shift: tuple[TopShift, LeftShift] = (0, 0),
    **kwargs: Any
) -> VideoNodeT

Supersample a clip by a given scaling factor.

Parameters:

  • clip

    (VideoNodeT) –

    The source clip.

  • rfactor

    (float, default: 2.0 ) –

    Scaling factor for supersampling.

  • shift

    (tuple[TopShift, LeftShift], default: (0, 0) ) –

    Subpixel shift (top, left) applied during scaling.

  • kwargs

    (Any, default: {} ) –

    Additional arguments forwarded to the scale function.

Returns:

Raises:

  • CustomValueError

    If resulting resolution is non-positive.

Source code
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
def supersample(
    self, clip: VideoNodeT, rfactor: float = 2.0, shift: tuple[TopShift, LeftShift] = (0, 0), **kwargs: Any
) -> VideoNodeT:
    """
    Supersample a clip by a given scaling factor.

    :param clip:                The source clip.
    :param rfactor:             Scaling factor for supersampling.
    :param shift:               Subpixel shift (top, left) applied during scaling.
    :param kwargs:              Additional arguments forwarded to the scale function.
    :raises CustomValueError:   If resulting resolution is non-positive.
    :return:                    The supersampled clip.
    """
    assert check_variable_resolution(clip, self.supersample)

    dst_width, dst_height = ceil(clip.width * rfactor), ceil(clip.height * rfactor)

    if max(dst_width, dst_height) <= 0.0:
        raise CustomValueError(
            'Multiplying the resolution by "rfactor" must result in a positive resolution!',
            self.supersample,
            rfactor,
        )

    return self.scale(clip, dst_width, dst_height, shift, **kwargs)  # type: ignore[return-value]

resample_to

resample_to(
    clip: VideoNode,
    out_fmt: int | VideoFormatT | HoldsVideoFormatT,
    matrix: MatrixT | None = None,
    resampler: ResamplerLike = Catrom,
) -> VideoNode
Source code
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
def resample_to(
    clip: vs.VideoNode,
    out_fmt: int | VideoFormatT | HoldsVideoFormatT,
    matrix: MatrixT | None = None,
    resampler: ResamplerLike = Catrom
) -> vs.VideoNode:
    out_fmt = get_video_format(out_fmt)
    assert clip.format

    resampler = Resampler.from_param(resampler)

    if out_fmt == clip.format:
        return clip

    if out_fmt.color_family is clip.format.color_family:
        return depth(clip, out_fmt)

    if out_fmt.subsampling_w == out_fmt.subsampling_h == 0:
        return Point().resample(clip, out_fmt, matrix)

    return resampler().resample(clip, out_fmt, matrix)