Skip to content

onnx

ArtCNN dataclass

ArtCNN(
    backend: Any | None = None,
    chroma_scaler: KernelT | None = None,
    tiles: int | tuple[int, int] | None = None,
    tilesize: int | tuple[int, int] | None = None,
    overlap: int | tuple[int, int] | None = None,
    *,
    kernel: KernelT | None = None,
    scaler: ScalerT | None = None,
    shifter: KernelT | None = None
)

Bases: BaseArtCNN

Super-Resolution Convolutional Neural Networks optimised for anime.

Defaults to C16F64.

backend class-attribute instance-attribute

backend: Any | None = None

vs-mlrt backend. Will attempt to autoselect the most suitable one with fp16=True if None.

In order of trt > cuda > directml > nncn > cpu.

chroma_scaler class-attribute instance-attribute

chroma_scaler: KernelT | None = None

Scaler to upscale the chroma with.

Necessary if you're trying to use one of the chroma models but aren't passing a 444 clip.

Bilinear is probably the safe option to use.

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

overlap class-attribute instance-attribute

overlap: int | tuple[int, int] | None = None

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.

tiles class-attribute instance-attribute

tiles: int | tuple[int, int] | None = None

Splits up the frame into multiple tiles. Helps if you're lacking in vram but models may behave differently.

tilesize class-attribute instance-attribute

tilesize: int | tuple[int, int] | None = None

C16F64 dataclass

C16F64(
    backend: Any | None = None,
    chroma_scaler: KernelT | None = None,
    tiles: int | tuple[int, int] | None = None,
    tilesize: int | tuple[int, int] | None = None,
    overlap: int | tuple[int, int] | None = None,
    *,
    kernel: KernelT | None = None,
    scaler: ScalerT | None = None,
    shifter: KernelT | None = None
)

Bases: BaseArtCNN

The current default model. Looks decent and very fast. Good for AA purposes.

This has 16 internal convolution layers with 64 filters each.

backend class-attribute instance-attribute

backend: Any | None = None

vs-mlrt backend. Will attempt to autoselect the most suitable one with fp16=True if None.

In order of trt > cuda > directml > nncn > cpu.

chroma_scaler class-attribute instance-attribute

chroma_scaler: KernelT | None = None

Scaler to upscale the chroma with.

Necessary if you're trying to use one of the chroma models but aren't passing a 444 clip.

Bilinear is probably the safe option to use.

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

overlap class-attribute instance-attribute

overlap: int | tuple[int, int] | None = None

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.

tiles class-attribute instance-attribute

tiles: int | tuple[int, int] | None = None

Splits up the frame into multiple tiles. Helps if you're lacking in vram but models may behave differently.

tilesize class-attribute instance-attribute

tilesize: int | tuple[int, int] | None = None

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
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
@inject_self
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:
    try:
        from vsmlrt import ArtCNN as mlrt_ArtCNN
        from vsmlrt import ArtCNNModel
    except ImportError:
        raise DependencyNotFoundError("vsmlrt", self._func)

    clip_format = get_video_format(clip)
    chroma_model = self._model in [4, 5, 9]

    # The chroma models aren't supposed to change the video dimensions and API wise this is more comfortable.
    if width is None or height is None:
        if chroma_model:
            width = clip.width
            height = clip.height
        else:
            raise CustomValueError("You have to pass height and width if not using a chroma model.", self._func)

    if chroma_model and clip_format.color_family != vs.YUV:
        raise CustomValueError("ArtCNN Chroma models need YUV input.", self._func)

    if not chroma_model and clip_format.color_family not in (vs.YUV, vs.GRAY):
        raise CustomValueError("Regular ArtCNN models need YUV or GRAY input.", self._func)

    if chroma_model and (clip_format.subsampling_h != 0 or clip_format.subsampling_w != 0):
        if self.chroma_scaler is None:
            raise CustomValueError(
                "ArtCNN needs a non subsampled clip. Either pass one or set `chroma_scaler`.", self._func
            )

        clip = Kernel.ensure_obj(self.chroma_scaler).resample(
            clip, clip_format.replace(subsampling_h=0, subsampling_w=0)
        )

    if self._model not in ArtCNNModel.__members__.values():
        raise NotFoundEnumValue(f'Invalid model: \'{self._model}\'. Please update \'vsmlrt\'!', self._func)

    wclip = get_y(clip) if not chroma_model else clip

    if self.backend is None:
        self.backend = autoselect_backend()

    scaled = mlrt_ArtCNN(
        limiter(depth(wclip, 32), func=self._func),
        self.tiles,
        self.tilesize,
        self.overlap,
        ArtCNNModel(self._model),
        backend=self.backend,
    )

    return self._finish_scale(scaled, wclip, width, height, shift)

C16F64_Chroma dataclass

C16F64_Chroma(
    backend: Any | None = None,
    chroma_scaler: KernelT | None = None,
    tiles: int | tuple[int, int] | None = None,
    tilesize: int | tuple[int, int] | None = None,
    overlap: int | tuple[int, int] | None = None,
    *,
    kernel: KernelT | None = None,
    scaler: ScalerT | None = None,
    shifter: KernelT | None = None
)

Bases: BaseArtCNN

The bigger of the two chroma models.

These don't double the input clip and rather just try to enhance the chroma using luma information.

backend class-attribute instance-attribute

backend: Any | None = None

vs-mlrt backend. Will attempt to autoselect the most suitable one with fp16=True if None.

In order of trt > cuda > directml > nncn > cpu.

chroma_scaler class-attribute instance-attribute

chroma_scaler: KernelT | None = None

Scaler to upscale the chroma with.

Necessary if you're trying to use one of the chroma models but aren't passing a 444 clip.

Bilinear is probably the safe option to use.

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

overlap class-attribute instance-attribute

overlap: int | tuple[int, int] | None = None

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.

tiles class-attribute instance-attribute

tiles: int | tuple[int, int] | None = None

Splits up the frame into multiple tiles. Helps if you're lacking in vram but models may behave differently.

tilesize class-attribute instance-attribute

tilesize: int | tuple[int, int] | None = None

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
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
@inject_self
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:
    try:
        from vsmlrt import ArtCNN as mlrt_ArtCNN
        from vsmlrt import ArtCNNModel
    except ImportError:
        raise DependencyNotFoundError("vsmlrt", self._func)

    clip_format = get_video_format(clip)
    chroma_model = self._model in [4, 5, 9]

    # The chroma models aren't supposed to change the video dimensions and API wise this is more comfortable.
    if width is None or height is None:
        if chroma_model:
            width = clip.width
            height = clip.height
        else:
            raise CustomValueError("You have to pass height and width if not using a chroma model.", self._func)

    if chroma_model and clip_format.color_family != vs.YUV:
        raise CustomValueError("ArtCNN Chroma models need YUV input.", self._func)

    if not chroma_model and clip_format.color_family not in (vs.YUV, vs.GRAY):
        raise CustomValueError("Regular ArtCNN models need YUV or GRAY input.", self._func)

    if chroma_model and (clip_format.subsampling_h != 0 or clip_format.subsampling_w != 0):
        if self.chroma_scaler is None:
            raise CustomValueError(
                "ArtCNN needs a non subsampled clip. Either pass one or set `chroma_scaler`.", self._func
            )

        clip = Kernel.ensure_obj(self.chroma_scaler).resample(
            clip, clip_format.replace(subsampling_h=0, subsampling_w=0)
        )

    if self._model not in ArtCNNModel.__members__.values():
        raise NotFoundEnumValue(f'Invalid model: \'{self._model}\'. Please update \'vsmlrt\'!', self._func)

    wclip = get_y(clip) if not chroma_model else clip

    if self.backend is None:
        self.backend = autoselect_backend()

    scaled = mlrt_ArtCNN(
        limiter(depth(wclip, 32), func=self._func),
        self.tiles,
        self.tilesize,
        self.overlap,
        ArtCNNModel(self._model),
        backend=self.backend,
    )

    return self._finish_scale(scaled, wclip, width, height, shift)

C16F64_DS dataclass

C16F64_DS(
    backend: Any | None = None,
    chroma_scaler: KernelT | None = None,
    tiles: int | tuple[int, int] | None = None,
    tilesize: int | tuple[int, int] | None = None,
    overlap: int | tuple[int, int] | None = None,
    *,
    kernel: KernelT | None = None,
    scaler: ScalerT | None = None,
    shifter: KernelT | None = None
)

Bases: BaseArtCNN

The same as C16F64 but intended to also sharpen and denoise.

backend class-attribute instance-attribute

backend: Any | None = None

vs-mlrt backend. Will attempt to autoselect the most suitable one with fp16=True if None.

In order of trt > cuda > directml > nncn > cpu.

chroma_scaler class-attribute instance-attribute

chroma_scaler: KernelT | None = None

Scaler to upscale the chroma with.

Necessary if you're trying to use one of the chroma models but aren't passing a 444 clip.

Bilinear is probably the safe option to use.

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

overlap class-attribute instance-attribute

overlap: int | tuple[int, int] | None = None

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.

tiles class-attribute instance-attribute

tiles: int | tuple[int, int] | None = None

Splits up the frame into multiple tiles. Helps if you're lacking in vram but models may behave differently.

tilesize class-attribute instance-attribute

tilesize: int | tuple[int, int] | None = None

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
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
@inject_self
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:
    try:
        from vsmlrt import ArtCNN as mlrt_ArtCNN
        from vsmlrt import ArtCNNModel
    except ImportError:
        raise DependencyNotFoundError("vsmlrt", self._func)

    clip_format = get_video_format(clip)
    chroma_model = self._model in [4, 5, 9]

    # The chroma models aren't supposed to change the video dimensions and API wise this is more comfortable.
    if width is None or height is None:
        if chroma_model:
            width = clip.width
            height = clip.height
        else:
            raise CustomValueError("You have to pass height and width if not using a chroma model.", self._func)

    if chroma_model and clip_format.color_family != vs.YUV:
        raise CustomValueError("ArtCNN Chroma models need YUV input.", self._func)

    if not chroma_model and clip_format.color_family not in (vs.YUV, vs.GRAY):
        raise CustomValueError("Regular ArtCNN models need YUV or GRAY input.", self._func)

    if chroma_model and (clip_format.subsampling_h != 0 or clip_format.subsampling_w != 0):
        if self.chroma_scaler is None:
            raise CustomValueError(
                "ArtCNN needs a non subsampled clip. Either pass one or set `chroma_scaler`.", self._func
            )

        clip = Kernel.ensure_obj(self.chroma_scaler).resample(
            clip, clip_format.replace(subsampling_h=0, subsampling_w=0)
        )

    if self._model not in ArtCNNModel.__members__.values():
        raise NotFoundEnumValue(f'Invalid model: \'{self._model}\'. Please update \'vsmlrt\'!', self._func)

    wclip = get_y(clip) if not chroma_model else clip

    if self.backend is None:
        self.backend = autoselect_backend()

    scaled = mlrt_ArtCNN(
        limiter(depth(wclip, 32), func=self._func),
        self.tiles,
        self.tilesize,
        self.overlap,
        ArtCNNModel(self._model),
        backend=self.backend,
    )

    return self._finish_scale(scaled, wclip, width, height, shift)

C4F32 dataclass

C4F32(
    backend: Any | None = None,
    chroma_scaler: KernelT | None = None,
    tiles: int | tuple[int, int] | None = None,
    tilesize: int | tuple[int, int] | None = None,
    overlap: int | tuple[int, int] | None = None,
    *,
    kernel: KernelT | None = None,
    scaler: ScalerT | None = None,
    shifter: KernelT | None = None
)

Bases: BaseArtCNN

This has 4 internal convolution layers with 32 filters each.

If you need an even faster model.

backend class-attribute instance-attribute

backend: Any | None = None

vs-mlrt backend. Will attempt to autoselect the most suitable one with fp16=True if None.

In order of trt > cuda > directml > nncn > cpu.

chroma_scaler class-attribute instance-attribute

chroma_scaler: KernelT | None = None

Scaler to upscale the chroma with.

Necessary if you're trying to use one of the chroma models but aren't passing a 444 clip.

Bilinear is probably the safe option to use.

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

overlap class-attribute instance-attribute

overlap: int | tuple[int, int] | None = None

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.

tiles class-attribute instance-attribute

tiles: int | tuple[int, int] | None = None

Splits up the frame into multiple tiles. Helps if you're lacking in vram but models may behave differently.

tilesize class-attribute instance-attribute

tilesize: int | tuple[int, int] | None = None

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
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
@inject_self
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:
    try:
        from vsmlrt import ArtCNN as mlrt_ArtCNN
        from vsmlrt import ArtCNNModel
    except ImportError:
        raise DependencyNotFoundError("vsmlrt", self._func)

    clip_format = get_video_format(clip)
    chroma_model = self._model in [4, 5, 9]

    # The chroma models aren't supposed to change the video dimensions and API wise this is more comfortable.
    if width is None or height is None:
        if chroma_model:
            width = clip.width
            height = clip.height
        else:
            raise CustomValueError("You have to pass height and width if not using a chroma model.", self._func)

    if chroma_model and clip_format.color_family != vs.YUV:
        raise CustomValueError("ArtCNN Chroma models need YUV input.", self._func)

    if not chroma_model and clip_format.color_family not in (vs.YUV, vs.GRAY):
        raise CustomValueError("Regular ArtCNN models need YUV or GRAY input.", self._func)

    if chroma_model and (clip_format.subsampling_h != 0 or clip_format.subsampling_w != 0):
        if self.chroma_scaler is None:
            raise CustomValueError(
                "ArtCNN needs a non subsampled clip. Either pass one or set `chroma_scaler`.", self._func
            )

        clip = Kernel.ensure_obj(self.chroma_scaler).resample(
            clip, clip_format.replace(subsampling_h=0, subsampling_w=0)
        )

    if self._model not in ArtCNNModel.__members__.values():
        raise NotFoundEnumValue(f'Invalid model: \'{self._model}\'. Please update \'vsmlrt\'!', self._func)

    wclip = get_y(clip) if not chroma_model else clip

    if self.backend is None:
        self.backend = autoselect_backend()

    scaled = mlrt_ArtCNN(
        limiter(depth(wclip, 32), func=self._func),
        self.tiles,
        self.tilesize,
        self.overlap,
        ArtCNNModel(self._model),
        backend=self.backend,
    )

    return self._finish_scale(scaled, wclip, width, height, shift)

C4F32_Chroma dataclass

C4F32_Chroma(
    backend: Any | None = None,
    chroma_scaler: KernelT | None = None,
    tiles: int | tuple[int, int] | None = None,
    tilesize: int | tuple[int, int] | None = None,
    overlap: int | tuple[int, int] | None = None,
    *,
    kernel: KernelT | None = None,
    scaler: ScalerT | None = None,
    shifter: KernelT | None = None
)

Bases: BaseArtCNN

The smaller of the two chroma models.

These don't double the input clip and rather just try to enhance the chroma using luma information.

backend class-attribute instance-attribute

backend: Any | None = None

vs-mlrt backend. Will attempt to autoselect the most suitable one with fp16=True if None.

In order of trt > cuda > directml > nncn > cpu.

chroma_scaler class-attribute instance-attribute

chroma_scaler: KernelT | None = None

Scaler to upscale the chroma with.

Necessary if you're trying to use one of the chroma models but aren't passing a 444 clip.

Bilinear is probably the safe option to use.

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

overlap class-attribute instance-attribute

overlap: int | tuple[int, int] | None = None

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.

tiles class-attribute instance-attribute

tiles: int | tuple[int, int] | None = None

Splits up the frame into multiple tiles. Helps if you're lacking in vram but models may behave differently.

tilesize class-attribute instance-attribute

tilesize: int | tuple[int, int] | None = None

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
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
@inject_self
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:
    try:
        from vsmlrt import ArtCNN as mlrt_ArtCNN
        from vsmlrt import ArtCNNModel
    except ImportError:
        raise DependencyNotFoundError("vsmlrt", self._func)

    clip_format = get_video_format(clip)
    chroma_model = self._model in [4, 5, 9]

    # The chroma models aren't supposed to change the video dimensions and API wise this is more comfortable.
    if width is None or height is None:
        if chroma_model:
            width = clip.width
            height = clip.height
        else:
            raise CustomValueError("You have to pass height and width if not using a chroma model.", self._func)

    if chroma_model and clip_format.color_family != vs.YUV:
        raise CustomValueError("ArtCNN Chroma models need YUV input.", self._func)

    if not chroma_model and clip_format.color_family not in (vs.YUV, vs.GRAY):
        raise CustomValueError("Regular ArtCNN models need YUV or GRAY input.", self._func)

    if chroma_model and (clip_format.subsampling_h != 0 or clip_format.subsampling_w != 0):
        if self.chroma_scaler is None:
            raise CustomValueError(
                "ArtCNN needs a non subsampled clip. Either pass one or set `chroma_scaler`.", self._func
            )

        clip = Kernel.ensure_obj(self.chroma_scaler).resample(
            clip, clip_format.replace(subsampling_h=0, subsampling_w=0)
        )

    if self._model not in ArtCNNModel.__members__.values():
        raise NotFoundEnumValue(f'Invalid model: \'{self._model}\'. Please update \'vsmlrt\'!', self._func)

    wclip = get_y(clip) if not chroma_model else clip

    if self.backend is None:
        self.backend = autoselect_backend()

    scaled = mlrt_ArtCNN(
        limiter(depth(wclip, 32), func=self._func),
        self.tiles,
        self.tilesize,
        self.overlap,
        ArtCNNModel(self._model),
        backend=self.backend,
    )

    return self._finish_scale(scaled, wclip, width, height, shift)

C4F32_DS dataclass

C4F32_DS(
    backend: Any | None = None,
    chroma_scaler: KernelT | None = None,
    tiles: int | tuple[int, int] | None = None,
    tilesize: int | tuple[int, int] | None = None,
    overlap: int | tuple[int, int] | None = None,
    *,
    kernel: KernelT | None = None,
    scaler: ScalerT | None = None,
    shifter: KernelT | None = None
)

Bases: BaseArtCNN

The same as C4F32 but intended to also sharpen and denoise.

backend class-attribute instance-attribute

backend: Any | None = None

vs-mlrt backend. Will attempt to autoselect the most suitable one with fp16=True if None.

In order of trt > cuda > directml > nncn > cpu.

chroma_scaler class-attribute instance-attribute

chroma_scaler: KernelT | None = None

Scaler to upscale the chroma with.

Necessary if you're trying to use one of the chroma models but aren't passing a 444 clip.

Bilinear is probably the safe option to use.

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

overlap class-attribute instance-attribute

overlap: int | tuple[int, int] | None = None

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.

tiles class-attribute instance-attribute

tiles: int | tuple[int, int] | None = None

Splits up the frame into multiple tiles. Helps if you're lacking in vram but models may behave differently.

tilesize class-attribute instance-attribute

tilesize: int | tuple[int, int] | None = None

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
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
@inject_self
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:
    try:
        from vsmlrt import ArtCNN as mlrt_ArtCNN
        from vsmlrt import ArtCNNModel
    except ImportError:
        raise DependencyNotFoundError("vsmlrt", self._func)

    clip_format = get_video_format(clip)
    chroma_model = self._model in [4, 5, 9]

    # The chroma models aren't supposed to change the video dimensions and API wise this is more comfortable.
    if width is None or height is None:
        if chroma_model:
            width = clip.width
            height = clip.height
        else:
            raise CustomValueError("You have to pass height and width if not using a chroma model.", self._func)

    if chroma_model and clip_format.color_family != vs.YUV:
        raise CustomValueError("ArtCNN Chroma models need YUV input.", self._func)

    if not chroma_model and clip_format.color_family not in (vs.YUV, vs.GRAY):
        raise CustomValueError("Regular ArtCNN models need YUV or GRAY input.", self._func)

    if chroma_model and (clip_format.subsampling_h != 0 or clip_format.subsampling_w != 0):
        if self.chroma_scaler is None:
            raise CustomValueError(
                "ArtCNN needs a non subsampled clip. Either pass one or set `chroma_scaler`.", self._func
            )

        clip = Kernel.ensure_obj(self.chroma_scaler).resample(
            clip, clip_format.replace(subsampling_h=0, subsampling_w=0)
        )

    if self._model not in ArtCNNModel.__members__.values():
        raise NotFoundEnumValue(f'Invalid model: \'{self._model}\'. Please update \'vsmlrt\'!', self._func)

    wclip = get_y(clip) if not chroma_model else clip

    if self.backend is None:
        self.backend = autoselect_backend()

    scaled = mlrt_ArtCNN(
        limiter(depth(wclip, 32), func=self._func),
        self.tiles,
        self.tilesize,
        self.overlap,
        ArtCNNModel(self._model),
        backend=self.backend,
    )

    return self._finish_scale(scaled, wclip, width, height, shift)

R16F96 dataclass

R16F96(
    backend: Any | None = None,
    chroma_scaler: KernelT | None = None,
    tiles: int | tuple[int, int] | None = None,
    tilesize: int | tuple[int, int] | None = None,
    overlap: int | tuple[int, int] | None = None,
    *,
    kernel: KernelT | None = None,
    scaler: ScalerT | None = None,
    shifter: KernelT | None = None
)

Bases: BaseArtCNN

The biggest model. Can compete with or outperform Waifu2x Cunet.

Also quite a bit slower but is less heavy on vram.

backend class-attribute instance-attribute

backend: Any | None = None

vs-mlrt backend. Will attempt to autoselect the most suitable one with fp16=True if None.

In order of trt > cuda > directml > nncn > cpu.

chroma_scaler class-attribute instance-attribute

chroma_scaler: KernelT | None = None

Scaler to upscale the chroma with.

Necessary if you're trying to use one of the chroma models but aren't passing a 444 clip.

Bilinear is probably the safe option to use.

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

overlap class-attribute instance-attribute

overlap: int | tuple[int, int] | None = None

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.

tiles class-attribute instance-attribute

tiles: int | tuple[int, int] | None = None

Splits up the frame into multiple tiles. Helps if you're lacking in vram but models may behave differently.

tilesize class-attribute instance-attribute

tilesize: int | tuple[int, int] | None = None

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
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
@inject_self
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:
    try:
        from vsmlrt import ArtCNN as mlrt_ArtCNN
        from vsmlrt import ArtCNNModel
    except ImportError:
        raise DependencyNotFoundError("vsmlrt", self._func)

    clip_format = get_video_format(clip)
    chroma_model = self._model in [4, 5, 9]

    # The chroma models aren't supposed to change the video dimensions and API wise this is more comfortable.
    if width is None or height is None:
        if chroma_model:
            width = clip.width
            height = clip.height
        else:
            raise CustomValueError("You have to pass height and width if not using a chroma model.", self._func)

    if chroma_model and clip_format.color_family != vs.YUV:
        raise CustomValueError("ArtCNN Chroma models need YUV input.", self._func)

    if not chroma_model and clip_format.color_family not in (vs.YUV, vs.GRAY):
        raise CustomValueError("Regular ArtCNN models need YUV or GRAY input.", self._func)

    if chroma_model and (clip_format.subsampling_h != 0 or clip_format.subsampling_w != 0):
        if self.chroma_scaler is None:
            raise CustomValueError(
                "ArtCNN needs a non subsampled clip. Either pass one or set `chroma_scaler`.", self._func
            )

        clip = Kernel.ensure_obj(self.chroma_scaler).resample(
            clip, clip_format.replace(subsampling_h=0, subsampling_w=0)
        )

    if self._model not in ArtCNNModel.__members__.values():
        raise NotFoundEnumValue(f'Invalid model: \'{self._model}\'. Please update \'vsmlrt\'!', self._func)

    wclip = get_y(clip) if not chroma_model else clip

    if self.backend is None:
        self.backend = autoselect_backend()

    scaled = mlrt_ArtCNN(
        limiter(depth(wclip, 32), func=self._func),
        self.tiles,
        self.tilesize,
        self.overlap,
        ArtCNNModel(self._model),
        backend=self.backend,
    )

    return self._finish_scale(scaled, wclip, width, height, shift)

R8F64 dataclass

R8F64(
    backend: Any | None = None,
    chroma_scaler: KernelT | None = None,
    tiles: int | tuple[int, int] | None = None,
    tilesize: int | tuple[int, int] | None = None,
    overlap: int | tuple[int, int] | None = None,
    *,
    kernel: KernelT | None = None,
    scaler: ScalerT | None = None,
    shifter: KernelT | None = None
)

Bases: BaseArtCNN

A smaller and faster version of R16F96 but very competitive.

backend class-attribute instance-attribute

backend: Any | None = None

vs-mlrt backend. Will attempt to autoselect the most suitable one with fp16=True if None.

In order of trt > cuda > directml > nncn > cpu.

chroma_scaler class-attribute instance-attribute

chroma_scaler: KernelT | None = None

Scaler to upscale the chroma with.

Necessary if you're trying to use one of the chroma models but aren't passing a 444 clip.

Bilinear is probably the safe option to use.

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

overlap class-attribute instance-attribute

overlap: int | tuple[int, int] | None = None

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.

tiles class-attribute instance-attribute

tiles: int | tuple[int, int] | None = None

Splits up the frame into multiple tiles. Helps if you're lacking in vram but models may behave differently.

tilesize class-attribute instance-attribute

tilesize: int | tuple[int, int] | None = None

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
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
@inject_self
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:
    try:
        from vsmlrt import ArtCNN as mlrt_ArtCNN
        from vsmlrt import ArtCNNModel
    except ImportError:
        raise DependencyNotFoundError("vsmlrt", self._func)

    clip_format = get_video_format(clip)
    chroma_model = self._model in [4, 5, 9]

    # The chroma models aren't supposed to change the video dimensions and API wise this is more comfortable.
    if width is None or height is None:
        if chroma_model:
            width = clip.width
            height = clip.height
        else:
            raise CustomValueError("You have to pass height and width if not using a chroma model.", self._func)

    if chroma_model and clip_format.color_family != vs.YUV:
        raise CustomValueError("ArtCNN Chroma models need YUV input.", self._func)

    if not chroma_model and clip_format.color_family not in (vs.YUV, vs.GRAY):
        raise CustomValueError("Regular ArtCNN models need YUV or GRAY input.", self._func)

    if chroma_model and (clip_format.subsampling_h != 0 or clip_format.subsampling_w != 0):
        if self.chroma_scaler is None:
            raise CustomValueError(
                "ArtCNN needs a non subsampled clip. Either pass one or set `chroma_scaler`.", self._func
            )

        clip = Kernel.ensure_obj(self.chroma_scaler).resample(
            clip, clip_format.replace(subsampling_h=0, subsampling_w=0)
        )

    if self._model not in ArtCNNModel.__members__.values():
        raise NotFoundEnumValue(f'Invalid model: \'{self._model}\'. Please update \'vsmlrt\'!', self._func)

    wclip = get_y(clip) if not chroma_model else clip

    if self.backend is None:
        self.backend = autoselect_backend()

    scaled = mlrt_ArtCNN(
        limiter(depth(wclip, 32), func=self._func),
        self.tiles,
        self.tilesize,
        self.overlap,
        ArtCNNModel(self._model),
        backend=self.backend,
    )

    return self._finish_scale(scaled, wclip, width, height, shift)

R8F64_Chroma dataclass

R8F64_Chroma(
    backend: Any | None = None,
    chroma_scaler: KernelT | None = None,
    tiles: int | tuple[int, int] | None = None,
    tilesize: int | tuple[int, int] | None = None,
    overlap: int | tuple[int, int] | None = None,
    *,
    kernel: KernelT | None = None,
    scaler: ScalerT | None = None,
    shifter: KernelT | None = None
)

Bases: BaseArtCNN

The new and fancy big chroma model. These don't double the input clip and rather just try to enhance the chroma using luma information.

backend class-attribute instance-attribute

backend: Any | None = None

vs-mlrt backend. Will attempt to autoselect the most suitable one with fp16=True if None.

In order of trt > cuda > directml > nncn > cpu.

chroma_scaler class-attribute instance-attribute

chroma_scaler: KernelT | None = None

Scaler to upscale the chroma with.

Necessary if you're trying to use one of the chroma models but aren't passing a 444 clip.

Bilinear is probably the safe option to use.

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

overlap class-attribute instance-attribute

overlap: int | tuple[int, int] | None = None

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.

tiles class-attribute instance-attribute

tiles: int | tuple[int, int] | None = None

Splits up the frame into multiple tiles. Helps if you're lacking in vram but models may behave differently.

tilesize class-attribute instance-attribute

tilesize: int | tuple[int, int] | None = None

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
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
@inject_self
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:
    try:
        from vsmlrt import ArtCNN as mlrt_ArtCNN
        from vsmlrt import ArtCNNModel
    except ImportError:
        raise DependencyNotFoundError("vsmlrt", self._func)

    clip_format = get_video_format(clip)
    chroma_model = self._model in [4, 5, 9]

    # The chroma models aren't supposed to change the video dimensions and API wise this is more comfortable.
    if width is None or height is None:
        if chroma_model:
            width = clip.width
            height = clip.height
        else:
            raise CustomValueError("You have to pass height and width if not using a chroma model.", self._func)

    if chroma_model and clip_format.color_family != vs.YUV:
        raise CustomValueError("ArtCNN Chroma models need YUV input.", self._func)

    if not chroma_model and clip_format.color_family not in (vs.YUV, vs.GRAY):
        raise CustomValueError("Regular ArtCNN models need YUV or GRAY input.", self._func)

    if chroma_model and (clip_format.subsampling_h != 0 or clip_format.subsampling_w != 0):
        if self.chroma_scaler is None:
            raise CustomValueError(
                "ArtCNN needs a non subsampled clip. Either pass one or set `chroma_scaler`.", self._func
            )

        clip = Kernel.ensure_obj(self.chroma_scaler).resample(
            clip, clip_format.replace(subsampling_h=0, subsampling_w=0)
        )

    if self._model not in ArtCNNModel.__members__.values():
        raise NotFoundEnumValue(f'Invalid model: \'{self._model}\'. Please update \'vsmlrt\'!', self._func)

    wclip = get_y(clip) if not chroma_model else clip

    if self.backend is None:
        self.backend = autoselect_backend()

    scaled = mlrt_ArtCNN(
        limiter(depth(wclip, 32), func=self._func),
        self.tiles,
        self.tilesize,
        self.overlap,
        ArtCNNModel(self._model),
        backend=self.backend,
    )

    return self._finish_scale(scaled, wclip, width, height, shift)

R8F64_DS dataclass

R8F64_DS(
    backend: Any | None = None,
    chroma_scaler: KernelT | None = None,
    tiles: int | tuple[int, int] | None = None,
    tilesize: int | tuple[int, int] | None = None,
    overlap: int | tuple[int, int] | None = None,
    *,
    kernel: KernelT | None = None,
    scaler: ScalerT | None = None,
    shifter: KernelT | None = None
)

Bases: BaseArtCNN

The same as R8F64 but intended to also sharpen and denoise.

backend class-attribute instance-attribute

backend: Any | None = None

vs-mlrt backend. Will attempt to autoselect the most suitable one with fp16=True if None.

In order of trt > cuda > directml > nncn > cpu.

chroma_scaler class-attribute instance-attribute

chroma_scaler: KernelT | None = None

Scaler to upscale the chroma with.

Necessary if you're trying to use one of the chroma models but aren't passing a 444 clip.

Bilinear is probably the safe option to use.

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

overlap class-attribute instance-attribute

overlap: int | tuple[int, int] | None = None

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.

tiles class-attribute instance-attribute

tiles: int | tuple[int, int] | None = None

Splits up the frame into multiple tiles. Helps if you're lacking in vram but models may behave differently.

tilesize class-attribute instance-attribute

tilesize: int | tuple[int, int] | None = None

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
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
@inject_self
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:
    try:
        from vsmlrt import ArtCNN as mlrt_ArtCNN
        from vsmlrt import ArtCNNModel
    except ImportError:
        raise DependencyNotFoundError("vsmlrt", self._func)

    clip_format = get_video_format(clip)
    chroma_model = self._model in [4, 5, 9]

    # The chroma models aren't supposed to change the video dimensions and API wise this is more comfortable.
    if width is None or height is None:
        if chroma_model:
            width = clip.width
            height = clip.height
        else:
            raise CustomValueError("You have to pass height and width if not using a chroma model.", self._func)

    if chroma_model and clip_format.color_family != vs.YUV:
        raise CustomValueError("ArtCNN Chroma models need YUV input.", self._func)

    if not chroma_model and clip_format.color_family not in (vs.YUV, vs.GRAY):
        raise CustomValueError("Regular ArtCNN models need YUV or GRAY input.", self._func)

    if chroma_model and (clip_format.subsampling_h != 0 or clip_format.subsampling_w != 0):
        if self.chroma_scaler is None:
            raise CustomValueError(
                "ArtCNN needs a non subsampled clip. Either pass one or set `chroma_scaler`.", self._func
            )

        clip = Kernel.ensure_obj(self.chroma_scaler).resample(
            clip, clip_format.replace(subsampling_h=0, subsampling_w=0)
        )

    if self._model not in ArtCNNModel.__members__.values():
        raise NotFoundEnumValue(f'Invalid model: \'{self._model}\'. Please update \'vsmlrt\'!', self._func)

    wclip = get_y(clip) if not chroma_model else clip

    if self.backend is None:
        self.backend = autoselect_backend()

    scaled = mlrt_ArtCNN(
        limiter(depth(wclip, 32), func=self._func),
        self.tiles,
        self.tilesize,
        self.overlap,
        ArtCNNModel(self._model),
        backend=self.backend,
    )

    return self._finish_scale(scaled, wclip, width, height, shift)

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
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
@inject_self
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:
    try:
        from vsmlrt import ArtCNN as mlrt_ArtCNN
        from vsmlrt import ArtCNNModel
    except ImportError:
        raise DependencyNotFoundError("vsmlrt", self._func)

    clip_format = get_video_format(clip)
    chroma_model = self._model in [4, 5, 9]

    # The chroma models aren't supposed to change the video dimensions and API wise this is more comfortable.
    if width is None or height is None:
        if chroma_model:
            width = clip.width
            height = clip.height
        else:
            raise CustomValueError("You have to pass height and width if not using a chroma model.", self._func)

    if chroma_model and clip_format.color_family != vs.YUV:
        raise CustomValueError("ArtCNN Chroma models need YUV input.", self._func)

    if not chroma_model and clip_format.color_family not in (vs.YUV, vs.GRAY):
        raise CustomValueError("Regular ArtCNN models need YUV or GRAY input.", self._func)

    if chroma_model and (clip_format.subsampling_h != 0 or clip_format.subsampling_w != 0):
        if self.chroma_scaler is None:
            raise CustomValueError(
                "ArtCNN needs a non subsampled clip. Either pass one or set `chroma_scaler`.", self._func
            )

        clip = Kernel.ensure_obj(self.chroma_scaler).resample(
            clip, clip_format.replace(subsampling_h=0, subsampling_w=0)
        )

    if self._model not in ArtCNNModel.__members__.values():
        raise NotFoundEnumValue(f'Invalid model: \'{self._model}\'. Please update \'vsmlrt\'!', self._func)

    wclip = get_y(clip) if not chroma_model else clip

    if self.backend is None:
        self.backend = autoselect_backend()

    scaled = mlrt_ArtCNN(
        limiter(depth(wclip, 32), func=self._func),
        self.tiles,
        self.tilesize,
        self.overlap,
        ArtCNNModel(self._model),
        backend=self.backend,
    )

    return self._finish_scale(scaled, wclip, width, height, shift)

BaseArtCNN dataclass

BaseArtCNN(
    backend: Any | None = None,
    chroma_scaler: KernelT | None = None,
    tiles: int | tuple[int, int] | None = None,
    tilesize: int | tuple[int, int] | None = None,
    overlap: int | tuple[int, int] | None = None,
    *,
    kernel: KernelT | None = None,
    scaler: ScalerT | None = None,
    shifter: KernelT | None = None
)

Bases: _BaseArtCNN, GenericScaler

backend class-attribute instance-attribute

backend: Any | None = None

vs-mlrt backend. Will attempt to autoselect the most suitable one with fp16=True if None.

In order of trt > cuda > directml > nncn > cpu.

chroma_scaler class-attribute instance-attribute

chroma_scaler: KernelT | None = None

Scaler to upscale the chroma with.

Necessary if you're trying to use one of the chroma models but aren't passing a 444 clip.

Bilinear is probably the safe option to use.

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

overlap class-attribute instance-attribute

overlap: int | tuple[int, int] | None = None

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.

tiles class-attribute instance-attribute

tiles: int | tuple[int, int] | None = None

Splits up the frame into multiple tiles. Helps if you're lacking in vram but models may behave differently.

tilesize class-attribute instance-attribute

tilesize: int | tuple[int, int] | None = None

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
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
@inject_self
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:
    try:
        from vsmlrt import ArtCNN as mlrt_ArtCNN
        from vsmlrt import ArtCNNModel
    except ImportError:
        raise DependencyNotFoundError("vsmlrt", self._func)

    clip_format = get_video_format(clip)
    chroma_model = self._model in [4, 5, 9]

    # The chroma models aren't supposed to change the video dimensions and API wise this is more comfortable.
    if width is None or height is None:
        if chroma_model:
            width = clip.width
            height = clip.height
        else:
            raise CustomValueError("You have to pass height and width if not using a chroma model.", self._func)

    if chroma_model and clip_format.color_family != vs.YUV:
        raise CustomValueError("ArtCNN Chroma models need YUV input.", self._func)

    if not chroma_model and clip_format.color_family not in (vs.YUV, vs.GRAY):
        raise CustomValueError("Regular ArtCNN models need YUV or GRAY input.", self._func)

    if chroma_model and (clip_format.subsampling_h != 0 or clip_format.subsampling_w != 0):
        if self.chroma_scaler is None:
            raise CustomValueError(
                "ArtCNN needs a non subsampled clip. Either pass one or set `chroma_scaler`.", self._func
            )

        clip = Kernel.ensure_obj(self.chroma_scaler).resample(
            clip, clip_format.replace(subsampling_h=0, subsampling_w=0)
        )

    if self._model not in ArtCNNModel.__members__.values():
        raise NotFoundEnumValue(f'Invalid model: \'{self._model}\'. Please update \'vsmlrt\'!', self._func)

    wclip = get_y(clip) if not chroma_model else clip

    if self.backend is None:
        self.backend = autoselect_backend()

    scaled = mlrt_ArtCNN(
        limiter(depth(wclip, 32), func=self._func),
        self.tiles,
        self.tilesize,
        self.overlap,
        ArtCNNModel(self._model),
        backend=self.backend,
    )

    return self._finish_scale(scaled, wclip, width, height, shift)

GenericOnnxScaler dataclass

GenericOnnxScaler(
    model: SPathLike,
    backend: Any | None = None,
    tiles: int | tuple[int, int] | None = None,
    tilesize: int | tuple[int, int] | None = None,
    overlap: int | tuple[int, int] | None = None,
    *,
    kernel: KernelT | None = None,
    scaler: ScalerT | None = None,
    shifter: KernelT | None = None
)

Bases: GenericScaler

Generic scaler class for an onnx model.

backend class-attribute instance-attribute

backend: Any | None = None

vs-mlrt backend. Will attempt to autoselect the most suitable one with fp16=True if None.

In order of trt > cuda > directml > nncn > cpu.

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

model instance-attribute

model: SPathLike

Path to the model.

overlap class-attribute instance-attribute

overlap: int | tuple[int, int] | None = None

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.

tiles class-attribute instance-attribute

tiles: int | tuple[int, int] | None = None

Splits up the frame into multiple tiles. Helps if you're lacking in vram but models may behave differently.

tilesize class-attribute instance-attribute

tilesize: int | tuple[int, int] | None = None

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

    wclip, _ = expect_bits(clip, 32)

    from vsmlrt import calc_tilesize, inference, init_backend  #type: ignore[import-untyped]

    if self.overlap is None:
        overlap_w = overlap_h = 8
    else:
        overlap_w, overlap_h = (self.overlap, self.overlap) if isinstance(self.overlap, int) else self.overlap

    (tile_w, tile_h), (overlap_w, overlap_h) = calc_tilesize(
        tiles=self.tiles,
        tilesize=self.tilesize,
        width=wclip.width,
        height=wclip.height,
        multiple=1,
        overlap_w=overlap_w,
        overlap_h=overlap_h,
    )

    if tile_w % 1 != 0 or tile_h % 1 != 0:
        raise CustomValueError(f"Tile size must be divisible by 1 ({tile_w}, {tile_h})", self.__class__)

    backend = init_backend(backend=self.backend, trt_opt_shapes=(tile_w, tile_h))

    scaled = inference(
        limiter(wclip, func=self.__class__),
        network_path=str(SPath(self.model).resolve()),
        backend=backend,
        overlap=(overlap_w, overlap_h),
        tilesize=(tile_w, tile_h),
    )
    return self._finish_scale(scaled, clip, width, height, shift)

autoselect_backend

autoselect_backend(trt_args: KwargsT = {}, **kwargs: Any) -> Any
Source code
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
def autoselect_backend(trt_args: KwargsT = {}, **kwargs: Any) -> Any:
    import os

    from vsmlrt import Backend

    fp16 = kwargs.pop("fp16", True)

    cuda = get_nvidia_version() is not None
    if cuda:
        if hasattr(core, "trt"):
            kwargs.update(trt_args)
            return Backend.TRT(fp16=fp16, **trt_args)
        elif hasattr(core, "ort"):
            return Backend.ORT_CUDA(fp16=fp16, **kwargs)
        else:
            return Backend.OV_GPU(fp16=fp16, **kwargs)
    else:
        if hasattr(core, "ort") and os.name == "nt":
            return Backend.ORT_DML(fp16=fp16, **kwargs)
        elif hasattr(core, "ncnn"):
            return Backend.NCNN_VK(fp16=fp16, **kwargs)

        return Backend.ORT_CPU(fp16=fp16, **kwargs) if hasattr(core, "ort") else Backend.OV_CPU(fp16=fp16, **kwargs)