Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix_telecined_fades: Drop legacy arguments & extend functionality #26

Merged
merged 1 commit into from Mar 12, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
144 changes: 61 additions & 83 deletions vsdeinterlace/combing.py
Original file line number Diff line number Diff line change
@@ -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):
Expand Down
Loading