Skip to content

helpers

BottomCrop module-attribute

BottomCrop: TypeAlias = int

LeftCrop module-attribute

LeftCrop: TypeAlias = int

RightCrop module-attribute

RightCrop: TypeAlias = int

TopCrop module-attribute

TopCrop: TypeAlias = int

CropAbs

Bases: NamedTuple

height instance-attribute

height: int

left class-attribute instance-attribute

left: int = 0

top class-attribute instance-attribute

top: int = 0

width instance-attribute

width: int

to_rel

to_rel(base_clip: VideoNode) -> CropRel
Source code
240
241
242
243
244
245
246
def to_rel(self, base_clip: vs.VideoNode) -> CropRel:
    return CropRel(
        self.left,
        base_clip.width - self.width - self.left,
        self.top,
        base_clip.height - self.height - self.top
    )

CropRel

Bases: NamedTuple

bottom class-attribute instance-attribute

bottom: int = 0

left class-attribute instance-attribute

left: int = 0

right class-attribute instance-attribute

right: int = 0

top class-attribute instance-attribute

top: int = 0

GenericScaler dataclass

GenericScaler(
    func: (
        _GeneriScaleNoShift | _GeneriScaleWithShift | Callable[..., VideoNode]
    ),
    **kwargs: Any
)

Bases: Scaler

Generic Scaler base class. Inherit from this to create more complex scalers with built-in utils. Instantiate with a callable taking at least a VideoNode, width, and height to use that as a Scaler in functions taking that.

Source code
71
72
73
74
75
def __init__(
    self, func: _GeneriScaleNoShift | _GeneriScaleWithShift | Callable[..., vs.VideoNode], **kwargs: Any
) -> None:
    self.func = func
    self.kwargs = kwargs

func instance-attribute

func = func

kernel class-attribute instance-attribute

kernel: KernelT | None = field(default=None, kw_only=True)

Base kernel to be used for certain scaling/shifting/resampling operations. Must be specified and defaults to catrom

kwargs instance-attribute

kwargs = kwargs

scale_function instance-attribute

scale_function: GenericVSFunction

Scale function called internally when scaling

scaler class-attribute instance-attribute

scaler: ScalerT | None = field(default=None, kw_only=True)

Scaler used for scaling operations. Defaults to kernel.

shifter class-attribute instance-attribute

shifter: KernelT | None = field(default=None, kw_only=True)

Kernel used for shifting operations. Defaults to kernel.

ensure_obj classmethod

ensure_obj(
    scaler: str | type[BaseScalerT] | BaseScalerT | None = None,
    /,
    func_except: FuncExceptT | None = None,
) -> BaseScalerT
Source code
186
187
188
189
190
191
192
193
@classmethod
def ensure_obj(
    cls: type[BaseScalerT], scaler: str | type[BaseScalerT] | BaseScalerT | None = None, /,
    func_except: FuncExceptT | None = None
) -> BaseScalerT:
    return _base_ensure_obj(
        cls, (mro := cls.mro())[mro.index(BaseScaler) - 1], scaler, cls._err_class, [], func_except
    )

ensure_scaler

ensure_scaler(scaler: ScalerT) -> Scaler
Source code
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
def ensure_scaler(self, scaler: ScalerT) -> Scaler:
    from dataclasses import is_dataclass, replace

    scaler_obj = Scaler.ensure_obj(scaler, self.__class__)

    if is_dataclass(scaler_obj):
        from inspect import Signature  #type: ignore[unreachable]

        kwargs = dict[str, ScalerT]()

        init_keys = Signature.from_callable(scaler_obj.__init__).parameters.keys()

        if 'kernel' in init_keys:
            kwargs.update(kernel=self.kernel or scaler_obj.kernel)

        if 'scaler' in init_keys:
            kwargs.update(scaler=self.scaler or scaler_obj.scaler)

        if 'shifter' in init_keys:
            kwargs.update(shifter=self.shifter or scaler_obj.shifter)

        if kwargs:
            scaler_obj = replace(scaler_obj, **kwargs)

    return scaler_obj

from_param classmethod

from_param(
    scaler: str | type[BaseScalerT] | BaseScalerT | None = None,
    /,
    func_except: FuncExceptT | None = None,
) -> type[BaseScalerT]
Source code
177
178
179
180
181
182
183
184
@classmethod
def from_param(
    cls: type[BaseScalerT], scaler: str | type[BaseScalerT] | BaseScalerT | None = None, /,
    func_except: FuncExceptT | None = None
) -> type[BaseScalerT]:
    return _base_from_param(
        cls, (mro := cls.mro())[mro.index(BaseScaler) - 1], scaler, cls._err_class, [], func_except
    )

get_clean_kwargs

get_clean_kwargs(*funcs: Callable[..., Any] | None) -> KwargsT
Source code
199
200
def get_clean_kwargs(self, *funcs: Callable[..., Any] | None) -> KwargsT:
    return _clean_self_kwargs(funcs, self)

get_implemented_funcs

get_implemented_funcs() -> tuple[Callable[..., Any], ...]
Source code
270
271
def get_implemented_funcs(self) -> tuple[Callable[..., Any], ...]:
    return (self.scale, self.multi)

get_scale_args

get_scale_args(
    clip: VideoNode,
    shift: tuple[TopShift, LeftShift] = (0, 0),
    width: int | None = None,
    height: int | None = None,
    *funcs: Callable[..., Any],
    **kwargs: Any
) -> KwargsT
Source code
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
@inject_kwargs_params
def get_scale_args(
    self, clip: vs.VideoNode, shift: tuple[TopShift, LeftShift] = (0, 0),
    width: int | None = None, height: int | None = None,
    *funcs: Callable[..., Any], **kwargs: Any
) -> KwargsT:
    return (
        dict(
            src_top=shift[0],
            src_left=shift[1]
        )
        | self.get_clean_kwargs(*funcs)
        | dict(width=width, height=height)
        | kwargs
    )

kernel_radius

kernel_radius() -> int
Source code
195
196
197
@inject_self.cached.property
def kernel_radius(self) -> int:
    return _default_kernel_radius(__class__, self)  # type: ignore

multi

multi(
    clip: VideoNode,
    multi: float = 2,
    shift: tuple[TopShift, LeftShift] = (0, 0),
    **kwargs: Any
) -> VideoNode
Source code
239
240
241
242
243
244
245
246
247
248
249
250
251
252
@inject_self.cached
def multi(
    self, clip: vs.VideoNode, multi: float = 2, shift: tuple[TopShift, LeftShift] = (0, 0), **kwargs: Any
) -> vs.VideoNode:
    assert check_variable_resolution(clip, self.multi)

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

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

    return self.scale(clip, dst_width, dst_height, shift, **kwargs)

pretty_string

pretty_string() -> str
Source code
202
203
204
205
206
207
208
209
210
211
212
213
214
@inject_self.cached.property
def pretty_string(self) -> str:
    attrs = {}

    if hasattr(self, 'b'):
        attrs.update(b=self.b, c=self.c)
    elif hasattr(self, 'taps'):
        attrs['taps'] = self.taps

    if hasattr(self, 'kwargs'):
        attrs.update(self.kwargs)

    return f"{self.__class__.__name__}{' (' + ', '.join(f'{k}={v}' for k, v in attrs.items()) + ')' if attrs else ''}"

scale

scale(
    clip: VideoNode,
    width: int | None = None,
    height: int | None = None,
    shift: tuple[float, float] = (0, 0),
    **kwargs: Any
) -> VideoNode
Source code
 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
def scale(  # type: ignore
    self, clip: vs.VideoNode, width: int | None = None, height: int | None = None,
    shift: tuple[float, float] = (0, 0), **kwargs: Any
) -> vs.VideoNode:
    width, height = self._wh_norm(clip, width, height)

    kwargs = self.kwargs | kwargs

    output = None

    if shift != (0, 0):
        try:
            output = self.func(clip, width, height, shift, **kwargs)
        except BaseException:
            try:
                output = self.func(clip, width=width, height=height, shift=shift, **kwargs)
            except BaseException:
                pass

    if output is None:
        try:
            output = self.func(clip, width, height, **kwargs)
        except BaseException:
            output = self.func(clip, width=width, height=height, **kwargs)

    return self._finish_scale(output, clip, width, height, shift)

ScalingArgs dataclass

ScalingArgs(
    width: int,
    height: int,
    src_width: float,
    src_height: float,
    src_top: float,
    src_left: float,
    mode: str = "hw",
)

height instance-attribute

height: int

mode class-attribute instance-attribute

mode: str = 'hw'

src_height instance-attribute

src_height: float

src_left instance-attribute

src_left: float

src_top instance-attribute

src_top: float

src_width instance-attribute

src_width: float

width instance-attribute

width: int

from_args classmethod

from_args(
    base_clip: VideoNode,
    height: int,
    width: int | None = None,
    /,
    *,
    src_top: float = ...,
    src_left: float = ...,
    mode: str = "hw",
) -> Self
from_args(
    base_clip: VideoNode,
    height: float,
    width: float | None = ...,
    /,
    base_height: int | None = ...,
    base_width: int | None = ...,
    src_top: float = ...,
    src_left: float = ...,
    crop: (
        tuple[LeftCrop, RightCrop, TopCrop, BottomCrop] | CropRel | CropAbs
    ) = ...,
    mode: str = "hw",
) -> Self
from_args(
    base_clip: VideoNode,
    height: int | float,
    width: int | float | None = None,
    base_height: int | None = None,
    base_width: int | None = None,
    src_top: float = 0,
    src_left: float = 0,
    crop: (
        tuple[LeftCrop, RightCrop, TopCrop, BottomCrop]
        | CropRel
        | CropAbs
        | None
    ) = None,
    mode: str = "hw",
) -> Self
Source code
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
@classmethod
def from_args(
    cls,
    base_clip: vs.VideoNode,
    height: int | float, width: int | float | None = None,
    base_height: int | None = None, base_width: int | None = None,
    src_top: float = 0, src_left: float = 0,
    crop: tuple[LeftCrop, RightCrop, TopCrop, BottomCrop] | CropRel | CropAbs | None = None,
    mode: str = 'hw'
) -> Self:
    if crop:
        if isinstance(crop, CropAbs):
            crop = crop.to_rel(base_clip)
        elif isinstance(crop, CropRel):
            pass
        else:
            crop = CropRel(*crop)
    else:
        crop = CropRel()

    ratio = height / base_clip.height

    if width is None:
        if isinstance(height, int):
            width = get_w(height, base_clip, 2)
        else:
            width = ratio * base_clip.width

    if all([
        isinstance(height, int),
        isinstance(width, int),
        base_height is None,
        base_width is None,
        crop == (0, 0, 0, 0)
    ]):
        return cls(int(width), int(height), int(width), int(height), src_top, src_left, mode)

    if base_height is None:
        base_height = mod2(ceil(height))

    if base_width is None:
        base_width = mod2(ceil(width))

    margin_left = (base_width - width) / 2 + ratio * crop.left
    margin_right = (base_width - width) / 2 + ratio * crop.right
    cropped_width = base_width - floor(margin_left) - floor(margin_right)

    margin_top = (base_height - height) / 2 + ratio * crop.top
    margin_bottom = (base_height - height) / 2 + ratio * crop.bottom
    cropped_height = base_height - floor(margin_top) - floor(margin_bottom)

    if isinstance(width, int) and crop.left == crop.right == 0:
        cropped_src_width = float(cropped_width)
    else:
        cropped_src_width = ratio * (base_clip.width - crop.left - crop.right)

    cropped_src_left = margin_left - floor(margin_left) + src_left

    if isinstance(height, int) and crop.top == crop.bottom == 0:
        cropped_src_height = float(cropped_height)
    else:
        cropped_src_height = ratio * (base_clip.height - crop.top - crop.bottom)

    cropped_src_top = margin_top - floor(margin_top) + src_top

    return cls(
        cropped_width, cropped_height,
        cropped_src_width, cropped_src_height,
        cropped_src_top, cropped_src_left,
        mode
    )

kwargs

kwargs(clip_or_rate: VideoNode | float | None = None) -> KwargsT
Source code
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
def kwargs(self, clip_or_rate: vs.VideoNode | float | None = None, /) -> KwargsT:
    kwargs = dict[str, Any]()

    do_h, do_w = self._do()

    if isinstance(clip_or_rate, (vs.VideoNode, NoneType)):
        up_rate_h, up_rate_w = self._up_rate(clip_or_rate)
    else:
        up_rate_h, up_rate_w = clip_or_rate, clip_or_rate

    if do_h:
        kwargs.update(
            src_height=self.src_height * up_rate_h,
            src_top=self.src_top * up_rate_h
        )

    if do_w:
        kwargs.update(
            src_width=self.src_width * up_rate_w,
            src_left=self.src_left * up_rate_w
        )

    return kwargs

descale_args

descale_args(
    clip: VideoNode,
    src_height: float,
    src_width: float | None = None,
    base_height: int | None = None,
    base_width: int | None = None,
    crop_top: int = 0,
    crop_bottom: int = 0,
    crop_left: int = 0,
    crop_right: int = 0,
    mode: str = "hw",
) -> ScalingArgs
Source code
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
def descale_args(
    clip: vs.VideoNode,
    src_height: float, src_width: float | None = None,
    base_height: int | None = None, base_width: int | None = None,
    crop_top: int = 0, crop_bottom: int = 0,
    crop_left: int = 0, crop_right: int = 0,
    mode: str = 'hw'
) -> ScalingArgs:
    # warnings
    return ScalingArgs.from_args(
        clip.std.AddBorders(crop_left, crop_right, crop_top, crop_bottom),
        src_height, src_width,
        base_height, base_width,
        0, 0,
        CropRel(crop_left, crop_right, crop_top, crop_bottom),
        mode
    )

fdescale_args

fdescale_args(
    clip: VideoNode,
    src_height: float,
    base_height: int | None = None,
    base_width: int | None = None,
    src_top: float | None = None,
    src_left: float | None = None,
    src_width: float | None = None,
    mode: str = "hw",
    up_rate: float = 2.0,
) -> tuple[KwargsT, KwargsT]
Source code
416
417
418
419
420
421
422
423
424
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
450
def fdescale_args(
    clip: vs.VideoNode, src_height: float,
    base_height: int | None = None, base_width: int | None = None,
    src_top: float | None = None, src_left: float | None = None,
    src_width: float | None = None, mode: str = 'hw', up_rate: float = 2.0
) -> tuple[KwargsT, KwargsT]:
    base_height = fallback(base_height, mod2(ceil(src_height)))
    base_width = fallback(base_width, get_w(base_height, clip, 2))

    src_width = fallback(src_width, src_height * clip.width / clip.height)

    cropped_width = base_width - 2 * floor((base_width - src_width) / 2)
    cropped_height = base_height - 2 * floor((base_height - src_height) / 2)

    do_h, do_w = 'h' in mode.lower(), 'w' in mode.lower()

    de_args = dict[str, Any](
        width=cropped_width if do_w else clip.width,
        height=cropped_height if do_h else clip.height
    )

    up_args = dict[str, Any]()

    src_top = fallback(src_top, (cropped_height - src_height) / 2)
    src_left = fallback(src_left, (cropped_width - src_width) / 2)

    if do_h:
        de_args.update(src_height=src_height, src_top=src_top)
        up_args.update(src_height=src_height * up_rate, src_top=src_top * up_rate)

    if do_w:
        de_args.update(src_width=src_width, src_left=src_left)
        up_args.update(src_width=src_width * up_rate, src_left=src_left * up_rate)

    return de_args, up_args

scale_var_clip

scale_var_clip(
    clip: VideoNode,
    width: int | Callable[[Resolution], int] | None,
    height: int | Callable[[Resolution], int],
    shift: tuple[float, float] | Callable[[Resolution], tuple[float, float]] = (
        0,
        0,
    ),
    scaler: Scaler | Callable[[Resolution], Scaler] = Nnedi3(),
    debug: bool = False,
) -> VideoNode

Scale a variable clip to constant or variable resolution.

Source code
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
def scale_var_clip(
    clip: vs.VideoNode,
    width: int | Callable[[Resolution], int] | None, height: int | Callable[[Resolution], int],
    shift: tuple[float, float] | Callable[[Resolution], tuple[float, float]] = (0, 0),
    scaler: Scaler | Callable[[Resolution], Scaler] = Nnedi3(), debug: bool = False
) -> vs.VideoNode:
    """Scale a variable clip to constant or variable resolution."""
    if not debug:
        try:
            return scaler.scale(clip, width, height, shift)  # type: ignore
        except BaseException:
            pass

    _cached_clips = dict[str, vs.VideoNode]()

    no_accepts_var = list[Scaler]()

    def _eval_scale(f: vs.VideoFrame, n: int) -> vs.VideoNode:
        key = f'{f.width}_{f.height}'

        if key not in _cached_clips:
            res = Resolution(f.width, f.height)

            norm_scaler = scaler(res) if callable(scaler) else scaler
            norm_shift = shift(res) if callable(shift) else shift
            norm_height = height(res) if callable(height) else height

            if width is None:
                norm_width = get_w(norm_height, res.width / res.height)
            else:
                norm_width = width(res) if callable(width) else width

            part_scaler = partial(  #type: ignore[misc]
                norm_scaler.scale, width=norm_width, height=norm_height, shift=norm_shift
            )

            scaled = clip
            if (scaled.width, scaled.height) != (norm_width, norm_height):
                if norm_scaler not in no_accepts_var:
                    try:
                        scaled = part_scaler(clip)
                    except BaseException:
                        no_accepts_var.append(norm_scaler)

                if norm_scaler in no_accepts_var:
                    const_clip = clip.resize.Point(res.width, res.height)

                    scaled = part_scaler(const_clip)

            if debug:
                scaled = scaled.std.SetFrameProps(var_width=res.width, var_height=res.height)

            _cached_clips[key] = scaled

        return _cached_clips[key]

    if callable(width) or callable(height):
        out_clip = clip
    else:
        out_clip = clip.std.BlankClip(width, height)

    return out_clip.std.FrameEval(_eval_scale, clip, clip)