Skip to content

Commit

Permalink
rewrite debandshit
Browse files Browse the repository at this point in the history
  • Loading branch information
LightArrowsEXE committed May 31, 2021
1 parent d5529c9 commit 43f6e95
Show file tree
Hide file tree
Showing 16 changed files with 5,324 additions and 162 deletions.
7 changes: 7 additions & 0 deletions .flake8
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
[flake8]
count = True
ignore = W503,E226
max-line-length = 120
exclude = stubs/*
show-source = True
statistics = True
29 changes: 17 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,23 +1,28 @@
debandshit, a collection of wrappers and functions
for debanding and masking.
# debandshit, a collection of wrappers and functions for debanding

This repository is intended to be an updated version of debandshit
until Z4STIN continues updating it himself.
This repository is intended to be a maintained version of `debandshit` until [Z4STIN](https://github.com/kgrabs) continues maintaining it himself.

The original v0.3.0 of debandshit.py
that this is built on from
can be found [here](https://pastebin.com/NGFBhCCn).
The original v0.3.0 of `debandshit.py` that this is built on from can be found [here](https://pastebin.com/NGFBhCCn).

## How to install

If you have the old `debandshit.py` module,
remove that from your system first.

Install `debandshit` with the following command:

```sh
$ pip install debandshit
```

## Dependencies

debandshit depends on the following third-party scripts:

- [fvsfunc](https://github.com/Irrational-Encoding-Wizardry/fvsfunc)
- [mvsfunc](https://github.com/HomeOfVapourSynthEvolution/mvsfunc)
- [muvsfunc](https://github.com/WolframRhodium/muvsfunc)
- [vardefunc](https://pypi.org/project/vardefunc/)
- [vsutil](https://pypi.org/project/vsutil/)

The following Vapoursynth libraries are also required for full functionality:
And the following VapourSynth libraries:

- [neo_f3kdb](https://github.com/HomeOfAviSynthPlusEvolution/neo_f3kdb) or [f3kdb](https://f3kdb.readthedocs.io/en/latest/)
- [fmtconv](https://github.com/EleonoreMizo/fmtconv)
- [rgsf](https://github.com/IFeelBloated/RGSF)
8 changes: 8 additions & 0 deletions debandshit/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# flake8: noqa

from . import debanders

f3kbilateral = debanders.f3kbilateral
f3kpf = debanders.f3kpf
lfdeband = debanders.lfdeband
dither_bilateral = debanders.dither_bilateral
198 changes: 198 additions & 0 deletions debandshit/debanders.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,198 @@
"""
Various functions used for debanding.
This used to be the `debandshit` module written by Z4ST1N,
with some functions that were rarely (if ever) used removed because I can't reasonably maintain them.
"""
from typing import Any, List, Optional, Union

import vapoursynth as vs
from vardefunc.deband import dumb3kdb
from vsutil import depth

core = vs.core


def f3kbilateral(clip: vs.VideoNode,
radius: int = 16,
y: int = 64, c: int = 1,
**kwargs: Any) -> vs.VideoNode:
"""
f3kbilateral: f3kdb multistage bilateral-esque filter from debandshit.
This function is more of a last resort for extreme banding.
Recommend values are ~40-60 for y and c strengths.
:param clip: Input clip
:param radius: Banding detection range
:param y: Banding detection threshold for luma
:param c: Banding detection threshold for chroma
:param kwargs: Arguments passed to mvsfunc.LimitFilter
:return: Debanded clip
"""
try:
from mvsfunc import LimitFilter
except ModuleNotFoundError:
raise ModuleNotFoundError("f3kbilateral: missing dependency 'mvsfunc'")

if clip.format is None:
raise ValueError("f3kbilateral: 'Variable-format clips not supported'")

bits = clip.format.bits_per_sample

if bits < 16:
clip = depth(clip, 16)

lf_args: Any = dict(thr=0.6, elast=3.0, thrc=None)
lf_args.update(kwargs)

r1 = round(radius * 4 / 3)
r2 = round(radius * 2 / 3)
r3 = round(radius / 3)
y1, y2, y3 = y // 2, y, y
c1, c2, c3 = max(1, c // 2), c, c

flt1 = dumb3kdb(clip, radius=r1, threshold=[y1, c1, c1])
flt2 = dumb3kdb(flt1, radius=r2, threshold=[y2, c2, c2])
flt3 = dumb3kdb(flt2, radius=r3, threshold=[y3, c3, c3])

lf = LimitFilter(flt3, flt2, ref=clip, **lf_args)
return depth(lf, bits) if bits < 16 else lf


def f3kpf(clip: vs.VideoNode,
radius: int = 16,
threshold: Union[int, List[int]] = 30,
**kwargs: Any) -> vs.VideoNode:
"""
f3kdb with a simple prefilter by mawen1250 - https://www.nmm-hd.org/newbbs/viewtopic.php?f=7&t=1495#p12163.
Since the prefilter is a straight gaussian+average blur, f3kdb's effect becomes very strong, very fast.
Functions more or less like gradfun3 without the detail mask.
:param clip: Input clip
:param radius: Banding detection range
:param threshold: Banding detection thresholds for multiple planes
:param kwargs: Arguments passed to mvsfunc.LimitFilter
:return: Debanded clip
"""
try:
from mvsfunc import LimitFilter
except ModuleNotFoundError:
raise ModuleNotFoundError("f3kpf: missing dependency 'mvsfunc'")

if clip.format is None:
raise ValueError("f3kpf: 'Variable-format clips not supported'")

lf_args: Any = dict(thr=0.3, elast=2.5, thrc=None)
lf_args.update(kwargs)

bits = clip.format.bits_per_sample

if bits != 32:
clip = depth(clip, 32)

blur32 = core.std.Convolution(clip, [1, 2, 1, 2, 4, 2, 1, 2, 1]).std.Convolution([1] * 9, planes=0)
blur16 = depth(blur32, 16)

diff = core.std.MakeDiff(clip, blur32)
f3k = dumb3kdb(blur16, radius, threshold)
f3k = LimitFilter(f3k, blur16, **lf_args)
f3k = depth(f3k, 32)

out = core.std.MergeDiff(f3k, diff)
return depth(out, bits)


def lfdeband(clip: vs.VideoNode) -> vs.VideoNode:
"""
A simple debander ported from AviSynth.
:param clip: Input clip
:return: Debanded clip
"""
if clip.format is None:
raise ValueError("lfdeband: 'Variable-format clips not supported'")

bits = clip.format.bits_per_sample
wss = 1 << clip.format.subsampling_w
hss = 1 << clip.format.subsampling_h
w, h = clip.width, clip.height
dw, dh = round(w / 2), round(h / 2)

clip = depth(clip, 32)
dsc = core.resize.Spline64(clip, dw-dw % wss, dh-dh % hss)

d3kdb_down = depth(dsc, 16)
d3kdb = dumb3kdb(d3kdb_down, radius=30, threshold=80, grain=0)
d3kdb_up = depth(d3kdb, 32)

ddif = core.std.MakeDiff(d3kdb_up, dsc)

dif = core.resize.Spline64(ddif, w, h)
out = core.std.MergeDiff(clip, dif)
return depth(out, bits)


def dither_bilateral(clip: vs.VideoNode, ref: Optional[vs.VideoNode] = None,
radius: Optional[float] = None,
thr: float = 0.35, wmin: float = 1.0,
subspl: float = 0) -> vs.VideoNode:
"""
Dither's Gradfun3 mode 2, for Vapoursynth
Not much different from using core.avsw.Eval with Dither_bilateral_multistage,
just with normal value rounding and without Dither_limit_dif16
If you want the rounding to be exactly the same,
replace the applicable lines from here: https://pastebin.com/raw/gZKHCFkd
Default setting of 'radius' changes to reflect the resolution
Radius auto-adjust: 480p -> 9
720p -> 12
810p -> 13
900p -> 14
1080p -> 16
2K -> 20
4K -> 32
Basic usage: flt = db.Dither_bilateral(clip, thr=1/3)
flt = mvf.LimitFilter(flt, clip, thr=1/3)
"""
if clip.format is None:
raise ValueError("Dither_bilateral: 'Variable-format clips not supported'")

if radius is None:
radius = max((clip.width - 1280) / 160 + 12, (clip.height - 720) / 90 + 12)

planes = list(range(clip.format.num_planes))
y, u, v = [3 if x in planes else 1 for x in range(3)]

thr_1 = round(max(thr * 4.5, 1.25), 1)
thr_2 = round(max(thr * 9, 5), 1)
subspl_2 = subspl if subspl in (0, 1) else subspl / 2
r4 = round(max(radius * 4 / 3, 4))
r2 = round(max(radius * 2 / 3, 3))
r1 = round(max(radius / 3, 2))

clips = [clip.fmtc.nativetostack16()]
clip_names = ["c"]
ref_t = "c"
if ref is not None:
ref_s = ref.fmtc.nativetostack16()
clips += [ref_s]
clip_names += ["ref"]
ref_t = "ref"

avs_stuff = "c.Dither_bilateral16(radius={r4}, thr={thr_1}, flat=0.75, wmin={wmin}, ref={ref_t}, subspl={subspl}, y={y}, u={u}, v={v})" # noqa
avs_stuff += ".Dither_bilateral16(radius={r2}, thr={thr_2}, flat=0.25, wmin={wmin}, ref={ref_t}, subspl={subspl_2}, y={y}, u={u}, v={v})" # noqa
avs_stuff += ".Dither_bilateral16(radius={r1}, thr={thr_2}, flat=0.50, wmin={wmin}, ref={ref_t}, subspl={subspl_2}, y={y}, u={u}, v={v})" # noqa
avs_stuff = avs_stuff.format(ref_t=ref_t, r4=r4, r2=r2, r1=r1, thr_1=thr_1, thr_2=thr_2, wmin=wmin, subspl=subspl, subspl_2=subspl_2, y=y, u=u, v=v) # noqa

return core.avsw.Eval(avs_stuff, clips=clips, clip_names=clip_names).fmtc.stack16tonative()
# TO-DO: Fix this function. Current error: Avisynth 32-bit proxy: command failed

# TO-DO: Port all the other functions in ../legacy/debandshit.py
Empty file added debandshit/py.typed
Empty file.
Loading

0 comments on commit 43f6e95

Please sign in to comment.