From b5edfa8d73f7cca1edc6af02814c9831a8b76960 Mon Sep 17 00:00:00 2001 From: wiwaz <105133551+wiwaz@users.noreply.github.com> Date: Tue, 12 Mar 2024 12:35:22 -0400 Subject: [PATCH] fix_telecined_fades: Drop legacy arguments & extend functionality (#26) --- vsdeinterlace/combing.py | 144 +++++++++++++++++---------------------- 1 file changed, 61 insertions(+), 83 deletions(-) diff --git a/vsdeinterlace/combing.py b/vsdeinterlace/combing.py index 2be5c1a..6097216 100644 --- a/vsdeinterlace/combing.py +++ b/vsdeinterlace/combing.py @@ -1,100 +1,78 @@ from __future__ import annotations -from typing import cast, overload +from typing import cast from vsexprtools import ExprVars, complexpr_available, norm_expr from vsrgtools import sbr from vstools import ( - MISSING, ConvMode, CustomEnum, FieldBasedT, FuncExceptT, FunctionUtil, MissingT, PlanesT, + ConvMode, CustomEnum, CustomIntEnum, FuncExceptT, FunctionUtil, PlanesT, core, depth, expect_bits, get_neutral_values, scale_8bit, vs ) __all__ = [ - 'fix_telecined_fades', - + 'fix_interlaced_fades', 'vinverse' ] -@overload -def fix_telecined_fades( - clip: vs.VideoNode, tff: bool | FieldBasedT | None, colors: float | list[float] = 0.0, - planes: PlanesT = None, func: FuncExceptT | None = None -) -> vs.VideoNode: - ... - -@overload -def fix_telecined_fades( - clip: vs.VideoNode, colors: float | list[float] = 0.0, - planes: PlanesT = None, func: FuncExceptT | None = None -) -> vs.VideoNode: - ... - -def fix_telecined_fades( # type: ignore[misc] - clip: vs.VideoNode, tff: bool | FieldBasedT | None | float | list[float] | MissingT = MISSING, - colors: float | list[float] | PlanesT = 0.0, - planes: PlanesT | FuncExceptT = None, func: FuncExceptT | None = None -) -> vs.VideoNode: - """ - Give a mathematically perfect solution to decombing fades made *after* telecining - (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 telecining 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/deinterlacing! - - :param clip: Clip to process. - :param tff: This parameter is deprecated and unused. It will be removed in the future. - :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. - """ - # Gracefully handle positional arguments that either include or - # exclude tff, hopefully without interfering with keyword arguments. - # Remove this block when tff is fully dropped from the parameter list. - if isinstance(tff, (float, list)): - if colors == 0.0: - tff, colors = MISSING, tff - elif planes is None: - tff, colors, planes = MISSING, tff, colors - else: - tff, colors, planes, func = MISSING, tff, colors, planes - - func = func or fix_telecined_fades - - if not complexpr_available: - raise ExprVars._get_akarin_err()(func=func) - - if tff is not MISSING: - print(DeprecationWarning('fix_telecined_fades: The tff parameter is unnecessary and therefore deprecated!')) - - f = FunctionUtil(clip, func, planes, (vs.GRAY, 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)) - } - ) - - fix = norm_expr( - props_clip, 'Y 2 % x.fbAvg{i} x.ftAvg{i} ? AVG! ' - 'AVG@ 0 = x x {color} - x.ftAvg{i} x.fbAvg{i} + 2 / AVG@ / * ? {color} +', - planes, i=f.norm_planes, color=colors, force_akarin=func, - ) - - return f.return_clip(fix) +class fix_interlaced_fades(CustomIntEnum): + Average = 0 + Darken = 1 + Brighten = 2 + + def __call__( # type: ignore[misc] + self, clip: vs.VideoNode, colors: float | list[float] | PlanesT = 0.0, + planes: PlanesT | FuncExceptT = 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.GRAY, 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_mode = ['+ 2 /', 'min', 'max'] + + fix = norm_expr( + props_clip, 'Y 2 % x.fbAvg{i} x.ftAvg{i} ? AVG! ' + 'AVG@ 0 = x x {color} - x.ftAvg{i} x.fbAvg{i} ' + '{expr_mode} AVG@ / * ? {color} +', + planes, i=f.norm_planes, expr_mode=expr_mode[self], + color=colors, force_akarin=func, + ) + + return f.return_clip(fix) class Vinverse(CustomEnum):