Skip to content

Commit

Permalink
Add custom kernel support and much more (#28)
Browse files Browse the repository at this point in the history
* Remove all MemeKernels

* Add CustomKernel

* Implement CustomComplexKernel

* Remove ZimgComplexKernel

* Remove fmtconv and reimplement some scalers

* Add ewa placebo scalers

* Add utils

* Add _modify_kernel_func from kwargs options

Added blur modify

* Add AdobeBicubicSharper, AdobeBicubicSmoother

* Merge files

* Fix Spline

* Make all kernel/kernel_radius cached

* Add SampleGridModel

* Fix formatting

* Update spline.py
  • Loading branch information
Setsugennoao authored Aug 4, 2024
1 parent 44cc41a commit d3a474c
Show file tree
Hide file tree
Showing 16 changed files with 626 additions and 831 deletions.
1 change: 1 addition & 0 deletions vskernels/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from . import exceptions, kernels, util # noqa: F401, F403
from .exceptions import * # noqa: F401, F403
from .kernels import * # noqa: F401, F403
from .types import * # noqa: F401, F403
from .util import * # noqa: F401, F403
5 changes: 1 addition & 4 deletions vskernels/kernels/__init__.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,7 @@
from .abstract import * # noqa: F401, F403
from .bicubic import * # noqa: F401, F403
from .complex import * # noqa: F401, F403
from .fmtconv import * # noqa: F401, F403
from .impulse import * # noqa: F401, F403
from .custom import * # noqa: F401, F403
from .placebo import * # noqa: F401, F403
from .resize import * # noqa: F401, F403
from .spline import * # noqa: F401, F403
from .various import * # noqa: F401, F403
from .zimg import * # noqa: F401, F403
29 changes: 21 additions & 8 deletions vskernels/kernels/abstract.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,10 @@
from vstools.enums.color import _norm_props_enums

from ..exceptions import UnknownDescalerError, UnknownKernelError, UnknownResamplerError, UnknownScalerError
from ..types import BotFieldLeftShift, BotFieldTopShift, LeftShift, TopFieldLeftShift, TopFieldTopShift, TopShift
from ..types import (
BorderHandling, BotFieldLeftShift, BotFieldTopShift, LeftShift, SampleGridModel, TopFieldLeftShift,
TopFieldTopShift, TopShift
)

__all__ = [
'Scaler', 'ScalerT',
Expand Down Expand Up @@ -135,7 +138,7 @@ def __init_subclass__(cls) -> None:
return

from ..util import abstract_kernels
from .zimg import ZimgComplexKernel
from .complex import CustomComplexKernel

if cls in abstract_kernels:
return
Expand All @@ -152,7 +155,7 @@ def __init_subclass__(cls) -> None:
if 'kernel_radius' in cls.__dict__.keys():
return

mro = [cls, *({*cls.mro()} - {*ZimgComplexKernel.mro()})]
mro = [cls, *({*cls.mro()} - {*CustomComplexKernel.mro()})]

for sub_cls in mro:
if hasattr(sub_cls, '_static_kernel_radius'):
Expand Down Expand Up @@ -189,7 +192,7 @@ def ensure_obj(
cls, (mro := cls.mro())[mro.index(BaseScaler) - 1], scaler, cls._err_class, [], func_except
)

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

Expand Down Expand Up @@ -272,25 +275,33 @@ def descale( # type: ignore[override]
shift: tuple[TopShift, LeftShift] | tuple[
TopShift | tuple[TopFieldTopShift, BotFieldTopShift],
LeftShift | tuple[TopFieldLeftShift, BotFieldLeftShift]
] = (0, 0), **kwargs: Any
] = (0, 0), *,
border_handling: BorderHandling = BorderHandling.MIRROR,
sample_grid_model: SampleGridModel = SampleGridModel.MATCH_EDGES,
field_based: FieldBased | None = None,
**kwargs: Any
) -> vs.VideoNode:
width, height = self._wh_norm(clip, width, height)

check_correct_subsampling(clip, width, height)

field_based = FieldBased.from_param_or_video(kwargs.pop('field_based', None), clip)
field_based = FieldBased.from_param_or_video(field_based, clip)

clip, bits = expect_bits(clip, 32)

de_base_args = (width, height // (1 + field_based.is_inter))
kwargs |= dict(border_handling=border_handling)

if field_based.is_inter:
shift_y, shift_x = tuple[tuple[float, float], ...](
sh if isinstance(sh, tuple) else (sh, sh) for sh in shift
)

de_kwargs_tf = self.get_descale_args(clip, (shift_y[0], shift_x[0]), *de_base_args, **kwargs)
de_kwargs_bf = self.get_descale_args(clip, (shift_y[1], shift_x[1]), *de_base_args, **kwargs)
kwargs_tf, shift = sample_grid_model.for_descale(clip, width, height, (shift_y[0], shift_x[0]), **kwargs)
kwargs_bf, shift = sample_grid_model.for_descale(clip, width, height, (shift_y[1], shift_x[1]), **kwargs)

de_kwargs_tf = self.get_descale_args(clip, (shift_y[0], shift_x[0]), *de_base_args, **kwargs_tf)
de_kwargs_bf = self.get_descale_args(clip, (shift_y[1], shift_x[1]), *de_base_args, **kwargs_bf)

if height % 2:
raise CustomIndexError('You can\'t descale to odd resolution when crossconverted!', self.descale)
Expand All @@ -311,6 +322,8 @@ def descale( # type: ignore[override]
if any(isinstance(sh, tuple) for sh in shift):
raise CustomValueError('You can\'t descale per-field when the input is progressive!', self.descale)

kwargs, shift = sample_grid_model.for_descale(clip, width, height, shift, **kwargs) # type: ignore

de_kwargs = self.get_descale_args(clip, shift, *de_base_args, **kwargs) # type: ignore

descaled = self.descale_function(clip, **_norm_props_enums(de_kwargs))
Expand Down
90 changes: 42 additions & 48 deletions vskernels/kernels/bicubic.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,10 @@
from math import sqrt
from typing import Any

from vstools import CustomValueError, core, inject_self, vs
from vstools import CustomValueError, inject_self

from .zimg import ZimgComplexKernel
from .complex import CustomComplexKernel
from .helpers import bic_vals, poly3

__all__ = [
'Bicubic',
Expand All @@ -15,6 +16,8 @@
'Catrom',
'FFmpegBicubic',
'AdobeBicubic',
'AdobeBicubicSharper',
'AdobeBicubicSmoother',
'BicubicSharp',
'RobidouxSoft',
'Robidoux',
Expand All @@ -23,37 +26,34 @@
]


class Bicubic(ZimgComplexKernel):
class Bicubic(CustomComplexKernel):
"""
Built-in bicubic resizer.
Default: b=0, c=0.5
Dependencies:
* VapourSynth-descale
:param b: B-param for bicubic kernel
:param c: C-param for bicubic kernel
"""

scale_function = resample_function = core.lazy.resize.Bicubic
descale_function = core.lazy.descale.Debicubic

def __init__(self, b: float = 0, c: float = 1 / 2, **kwargs: Any) -> None:
self.b = b
self.c = c
super().__init__(**kwargs)

def get_params_args(
self, is_descale: bool, clip: vs.VideoNode, width: int | None = None, height: int | None = None, **kwargs: Any
) -> dict[str, Any]:
args = super().get_params_args(is_descale, clip, width, height, **kwargs)
if is_descale:
return args | dict(b=self.b, c=self.c)
return args | dict(filter_param_a=self.b, filter_param_b=self.c)
@inject_self.cached
def kernel(self, *, x: float) -> float: # type: ignore
x, b, c = abs(x), self.b, self.c

if (x < 1.0):
return poly3(x, bic_vals.p0(b, c), 0.0, bic_vals.p2(b, c), bic_vals.p3(b, c))

if (x < 2.0):
return poly3(x, bic_vals.q0(b, c), bic_vals.q1(b, c), bic_vals.q2(b, c), bic_vals.q3(b, c))

return 0.0

@inject_self.property
@inject_self.cached.property
def kernel_radius(self) -> int: # type: ignore
if (self.b, self.c) == (0, 0):
return 1
Expand Down Expand Up @@ -102,6 +102,20 @@ def __init__(self, **kwargs: Any) -> None:
super().__init__(b=0, c=3 / 4, **kwargs)


class AdobeBicubicSharper(Bicubic):
"""Bicubic b=0, c=1, blur=1.05; Adobe's "Bicubic Sharper" interpolation preset."""

def __init__(self, **kwargs: Any) -> None:
super().__init__(b=0, c=1, blur=1.05, **kwargs)


class AdobeBicubicSmoother(Bicubic):
"""Bicubic b=0, c=0.625, blur=1.15; Adobe's "Bicubic Smoother" interpolation preset."""

def __init__(self, **kwargs: Any) -> None:
super().__init__(b=0, c=5 / 8, blur=1.15, **kwargs)


class BicubicSharp(Bicubic):
"""Bicubic b=0, c=1"""

Expand Down Expand Up @@ -136,47 +150,27 @@ def __init__(self, **kwargs: Any) -> None:
super().__init__(b=b, c=c, **kwargs)


class BicubicAuto(ZimgComplexKernel):
class BicubicAuto(Bicubic):
"""
Kernel that follows the rule of:
b + 2c = target
"""

scale_function = resample_function = core.lazy.resize.Bicubic
descale_function = core.lazy.descale.Debicubic

def __init__(self, b: float | None = None, c: float | None = None, target: float = 1.0, **kwargs: Any) -> None:
def __init__(self, b: float | None = None, c: float | None = None, **kwargs: Any) -> None:
if None not in {b, c}:
raise CustomValueError("You can't specify both b and c!", self.__class__)

self.b = b
self.c = c
self.target = target
self.b, self.c = self._get_bc_args(b, c)

super().__init__(**kwargs)

def get_params_args(
self, is_descale: bool, clip: vs.VideoNode, width: int | None = None, height: int | None = None, **kwargs: Any
) -> dict[str, Any]:
args = super().get_params_args(is_descale, clip, width, height, **kwargs)
def _get_bc_args(self, b: float | None, c: float | None) -> tuple[float, float]:
autob = 0.0 if b is None else b
autoc = 0.5 if c is None else c

b, c = self._get_bc_args()

if is_descale:
return args | dict(b=b, c=c)
return args | dict(filter_param_a=b, filter_param_b=c)

def _get_bc_args(self) -> tuple[float, float]:
autob = 0.0 if self.b is None else self.b
autoc = 0.5 if self.c is None else self.c

if self.c is not None and self.b is None:
autob = self.target - 2 * self.c
elif self.c is None and self.b is not None:
autoc = (self.target - self.b) / 2
if c is not None and b is None:
autob = 1.0 - 2 * c
elif c is None and b is not None:
autoc = (1.0 - b) / 2

return autob, autoc

@inject_self.property
def kernel_radius(self) -> int: # type: ignore
return Bicubic(*self._get_bc_args()).kernel_radius
Loading

0 comments on commit d3a474c

Please sign in to comment.