Skip to content

Commit

Permalink
Merge combing and funcs submodules (#38)
Browse files Browse the repository at this point in the history
  • Loading branch information
emotion3459 authored Sep 10, 2024
1 parent 8040afa commit e112ea8
Show file tree
Hide file tree
Showing 4 changed files with 137 additions and 146 deletions.
1 change: 0 additions & 1 deletion vsdeinterlace/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
from .blending import * # noqa: F401, F403
from .combing import * # noqa: F401, F403
from .funcs import * # noqa: F401, F403
from .ivtc import * # noqa: F401, F403
from .utils import * # noqa: F401, F403
2 changes: 1 addition & 1 deletion vsdeinterlace/blending.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
from vsexprtools import complexpr_available, expr_func, norm_expr
from vstools import VSFunction, join, shift_clip, shift_clip_multi, vs

from .combing import vinverse
from .funcs import vinverse
from .utils import telecine_patterns

__all__ = [
Expand Down
141 changes: 0 additions & 141 deletions vsdeinterlace/combing.py

This file was deleted.

139 changes: 136 additions & 3 deletions vsdeinterlace/funcs.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,19 @@
from __future__ import annotations

from typing import Any
from typing import Any, Sequence, cast

from stgpytools import CustomIntEnum
from vsexprtools import ExprVars, complexpr_available, norm_expr
from vsrgtools import BlurMatrix
from vsdenoise import MVTools
from vstools import InvalidFramerateError, check_variable, core, vs
from vstools import (ConvMode, CustomEnum, FormatsMismatchError, InvalidFramerateError,
FunctionUtil, FuncExceptT, GenericVSFunction, KwargsT, PlanesT,
core, vs, scale_8bit, check_variable)

__all__ = [
'telop_resample'
'telop_resample',
'fix_interlaced_fades',
'vinverse'
]


Expand Down Expand Up @@ -133,3 +139,130 @@ def intl(clips: list[vs.VideoNode], toreverse: bool, halv: list[int]) -> vs.Vide
fix2 = MVTools(c2, **mv_args).flow_interpolate()

return intl([fix1, fix2], pattern == 0, [0, 1])


class FixInterlacedFades(CustomEnum):
Average: FixInterlacedFades = object() # type: ignore
Darken: FixInterlacedFades = object() # type: ignore
Brighten: FixInterlacedFades = object() # type: ignore

def __call__(
self, clip: vs.VideoNode, colors: float | list[float] | PlanesT = 0.0,
planes: PlanesT = None, func: FuncExceptT | None = None
) -> vs.VideoNode:
"""
Give a mathematically perfect solution to decombing fades made *after* telecine
(which made perfect IVTC impossible) that start or end in a solid color.
Steps between the frames are not adjusted, so they will remain uneven depending on the telecine pattern,
but the decombing is blur-free, ensuring minimum information loss. However, this may cause small amounts
of combing to remain due to error amplification, especially near the solid-color end of the fade.
This is an improved version of the Fix-Telecined-Fades plugin.
Make sure to run this *after* IVTC!
:param clip: Clip to process.
:param colors: Fade source/target color (floating-point plane averages).
:return: Clip with fades to/from `colors` accurately deinterlaced.
Frames that don't contain such fades may be damaged.
"""
func = func or self.__class__

if not complexpr_available:
raise ExprVars._get_akarin_err()(func=func)

f = FunctionUtil(clip, func, planes, vs.YUV, 32)

fields = f.work_clip.std.Limiter().std.SeparateFields(tff=True)

for i in f.norm_planes:
fields = fields.std.PlaneStats(None, i, f'P{i}')

props_clip = core.akarin.PropExpr(
[f.work_clip, fields[::2], fields[1::2]], lambda: { # type: ignore[misc]
f'f{t}Avg{i}': f'{c}.P{i}Average {color} -' # type: ignore[has-type]
for t, c in ['ty', 'bz']
for i, color in zip(f.norm_planes, f.norm_seq(colors))
}
)

expr_header = 'Y 2 % x.fbAvg{i} x.ftAvg{i} ? AVG! AVG@ 0 = x x {color} - '
expr_footer = ' AVG@ / * ? {color} +'

expr_mode, expr_mode_chroma = (
('+ 2 /', '+ 2 /') if self == self.Average else (('min', '<') if self == self.Darken else ('max', '>'))
)

fix = norm_expr(
props_clip, (
# luma
expr_header + 'x.ftAvg{i} x.fbAvg{i} {expr_mode}' + expr_footer,
# chroma
expr_header + 'x.ftAvg{i} abs x.fbAvg{i} abs {expr_mode} x.ftAvg{i} x.fbAvg{i} ?' + expr_footer
),
planes, i=f.norm_planes, expr_mode=(expr_mode, expr_mode_chroma),
color=colors, force_akarin=func,
)

return f.return_clip(fix)


def vinverse(
clip: vs.VideoNode,
comb_blur: GenericVSFunction | vs.VideoNode | Sequence[int] = [1, 2, 1],
contra_blur: GenericVSFunction | vs.VideoNode | Sequence[int] = [1, 4, 6, 4, 1],
contra_str: float = 2.7, amnt: int = 255, scl: float = 0.25,
thr: int = 0, planes: PlanesT = None,
**kwargs: Any
) -> vs.VideoNode:
"""
A simple but effective plugin to remove residual combing. Based on an AviSynth script by Didée.
:param clip: Clip to process.
:param comb_blur: Filter used to remove combing.
:param contra_blur: Filter used to calculate contra sharpening.
:param contra_str: Strength of contra sharpening.
:param amnt: Change no pixel by more than this in 8bit.
:param thr: Skip processing if abs(clip - comb_blur(clip)) < thr
:param scl: Scale factor for vshrpD * vblurD < 0.
"""

func = FunctionUtil(clip, vinverse, planes)

def_k = KwargsT(mode=ConvMode.VERTICAL)

kwrg_a, kwrg_b = not callable(comb_blur), not callable(contra_blur)

if isinstance(comb_blur, vs.VideoNode):
blurred = comb_blur
else:
if not callable(comb_blur):
comb_blur = BlurMatrix(comb_blur) # type:ignore

blurred = comb_blur(func.work_clip, planes=planes, **((def_k | kwargs) if kwrg_a else kwargs))

if isinstance(contra_blur, vs.VideoNode):
blurred2 = contra_blur
else:
if not callable(contra_blur):
contra_blur = BlurMatrix(contra_blur) # type:ignore

blurred2 = contra_blur(blurred, planes=planes, **((def_k | kwargs) if kwrg_b else kwargs))

FormatsMismatchError.check(func.func, func.work_clip, blurred, blurred2)

combed = norm_expr(
[func.work_clip, blurred, blurred2], # type:ignore
'x y - D1! D1@ abs D1A! D1A@ {thr} < x y z - {sstr} * D2! D2@ abs D2A! '
'D2@ D1@ xor D2A@ D1A@ < D2@ D1@ ? {scl} * D2A@ D1A@ < D2@ D1@ ? ? y + '
'LIM! x {amnt} + LIM@ < x {amnt} + x {amnt} - LIM@ > x {amnt} - LIM@ ? ? ?',
planes, sstr=contra_str, amnt=scale_8bit(func.work_clip, amnt),
scl=scl, thr=scale_8bit(func.work_clip, thr),
)

return func.return_clip(combed)


fix_interlaced_fades = cast(FixInterlacedFades, FixInterlacedFades.Average)

0 comments on commit e112ea8

Please sign in to comment.