vapoursynth-dotkill 3.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- vapoursynth_dotkill-3.0/.github/workflows/build.yml +99 -0
- vapoursynth_dotkill-3.0/LICENSE +21 -0
- vapoursynth_dotkill-3.0/PKG-INFO +85 -0
- vapoursynth_dotkill-3.0/README.md +62 -0
- vapoursynth_dotkill-3.0/meson.build +27 -0
- vapoursynth_dotkill-3.0/pyproject.toml +35 -0
- vapoursynth_dotkill-3.0/src/dotkill1.cpp +706 -0
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
name: Build
|
|
2
|
+
|
|
3
|
+
on: [push, pull_request, workflow_dispatch]
|
|
4
|
+
|
|
5
|
+
jobs:
|
|
6
|
+
build-non-windows-wheel:
|
|
7
|
+
strategy:
|
|
8
|
+
matrix:
|
|
9
|
+
include:
|
|
10
|
+
- arch: linux_x86_64
|
|
11
|
+
runs-on: ubuntu-latest
|
|
12
|
+
- arch: linux_aarch64
|
|
13
|
+
runs-on: ubuntu-24.04-arm
|
|
14
|
+
- arch: macosx_x86_64
|
|
15
|
+
runs-on: macos-26-intel
|
|
16
|
+
- arch: macosx_arm64
|
|
17
|
+
runs-on: macos-26
|
|
18
|
+
fail-fast: false
|
|
19
|
+
|
|
20
|
+
runs-on: ${{ matrix.runs-on }}
|
|
21
|
+
|
|
22
|
+
steps:
|
|
23
|
+
- uses: actions/checkout@v6
|
|
24
|
+
|
|
25
|
+
- uses: pypa/cibuildwheel@v3.4.0
|
|
26
|
+
env:
|
|
27
|
+
CIBW_BUILD: cp314-*
|
|
28
|
+
CIBW_BUILD_FRONTEND: uv
|
|
29
|
+
with:
|
|
30
|
+
output-dir: dist
|
|
31
|
+
extras: uv
|
|
32
|
+
|
|
33
|
+
- name: Fix tag
|
|
34
|
+
run: |
|
|
35
|
+
cd dist
|
|
36
|
+
for whl in *.whl; do
|
|
37
|
+
pipx run wheel tags --remove --python-tag py3 --abi-tag none $whl
|
|
38
|
+
done
|
|
39
|
+
|
|
40
|
+
- uses: actions/upload-artifact@v7
|
|
41
|
+
with:
|
|
42
|
+
name: descratch-${{ matrix.arch }}
|
|
43
|
+
path: dist
|
|
44
|
+
|
|
45
|
+
build-windows-wheel:
|
|
46
|
+
runs-on: windows-latest
|
|
47
|
+
|
|
48
|
+
defaults:
|
|
49
|
+
run:
|
|
50
|
+
shell: msys2 {0}
|
|
51
|
+
|
|
52
|
+
steps:
|
|
53
|
+
- uses: actions/checkout@v6
|
|
54
|
+
|
|
55
|
+
- uses: msys2/setup-msys2@v2
|
|
56
|
+
with:
|
|
57
|
+
msystem: UCRT64
|
|
58
|
+
update: true
|
|
59
|
+
install: >-
|
|
60
|
+
mingw-w64-ucrt-x86_64-git
|
|
61
|
+
mingw-w64-ucrt-x86_64-ninja
|
|
62
|
+
mingw-w64-ucrt-x86_64-toolchain
|
|
63
|
+
mingw-w64-ucrt-x86_64-uv
|
|
64
|
+
|
|
65
|
+
- name: Build
|
|
66
|
+
run: |
|
|
67
|
+
uv build -Csetup-args="-Dcpp_link_args=-static"
|
|
68
|
+
|
|
69
|
+
- name: Fix tag
|
|
70
|
+
run: |
|
|
71
|
+
cd dist
|
|
72
|
+
for whl in *.whl; do
|
|
73
|
+
uvx wheel tags --remove --python-tag py3 --abi-tag none $whl
|
|
74
|
+
done
|
|
75
|
+
|
|
76
|
+
- uses: actions/upload-artifact@v7
|
|
77
|
+
with:
|
|
78
|
+
name: descratch-sdist
|
|
79
|
+
path: dist/*.tar.gz
|
|
80
|
+
|
|
81
|
+
- uses: actions/upload-artifact@v7
|
|
82
|
+
with:
|
|
83
|
+
name: descratch-win_amd64
|
|
84
|
+
path: dist/*.whl
|
|
85
|
+
|
|
86
|
+
publish:
|
|
87
|
+
permissions:
|
|
88
|
+
id-token: write
|
|
89
|
+
needs: [build-non-windows-wheel, build-windows-wheel]
|
|
90
|
+
if: ${{ startsWith(github.ref, 'refs/tags/') }}
|
|
91
|
+
runs-on: ubuntu-latest
|
|
92
|
+
|
|
93
|
+
steps:
|
|
94
|
+
- uses: actions/download-artifact@v8
|
|
95
|
+
with:
|
|
96
|
+
path: dist
|
|
97
|
+
merge-multiple: true
|
|
98
|
+
|
|
99
|
+
- uses: pypa/gh-action-pypi-publish@release/v1
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2017 Fredrik Mellbin
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: vapoursynth-dotkill
|
|
3
|
+
Version: 3.0
|
|
4
|
+
Summary: Rainbow and dotcrawl removal filter for VapourSynth
|
|
5
|
+
Keywords: video,rainbows,dotcrawl
|
|
6
|
+
Author-Email: Fredrik Mellbin <fredrik.mellbin@gmail.com>
|
|
7
|
+
License-Expression: MIT
|
|
8
|
+
License-File: LICENSE
|
|
9
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
10
|
+
Classifier: Environment :: Plugins
|
|
11
|
+
Classifier: Natural Language :: English
|
|
12
|
+
Classifier: Operating System :: MacOS
|
|
13
|
+
Classifier: Operating System :: Microsoft :: Windows
|
|
14
|
+
Classifier: Operating System :: POSIX
|
|
15
|
+
Classifier: Operating System :: Unix
|
|
16
|
+
Classifier: Programming Language :: C++
|
|
17
|
+
Classifier: Topic :: Multimedia :: Video
|
|
18
|
+
Project-URL: Repository, https://github.com/myrsloik/DotKill.git
|
|
19
|
+
Project-URL: Issues, https://github.com/myrsloik/DotKill/issues
|
|
20
|
+
Requires-Python: >=3.12
|
|
21
|
+
Requires-Dist: VapourSynth
|
|
22
|
+
Description-Content-Type: text/markdown
|
|
23
|
+
|
|
24
|
+
DotKill
|
|
25
|
+
=======
|
|
26
|
+
|
|
27
|
+
Spatio-temporal dotcrawl and rainbow remover for VapourSynth
|
|
28
|
+
|
|
29
|
+
Functions
|
|
30
|
+
=========
|
|
31
|
+
|
|
32
|
+
DotKillS
|
|
33
|
+
--------
|
|
34
|
+
|
|
35
|
+
dotkill.DotKillS(clip clip[, int iterations=1])
|
|
36
|
+
|
|
37
|
+
A purely spatial dotcrawl remover that can be safely used on most material after field matching.
|
|
38
|
+
|
|
39
|
+
iterations: The number of times to apply the internal filter. Usally a number between 1 and 4 will have the best results and using too high values may cause artifacting.
|
|
40
|
+
|
|
41
|
+
usematch: If true then matching hints from VFM are used when processing. This may or may not have a positive effect.
|
|
42
|
+
|
|
43
|
+
DotKillZ
|
|
44
|
+
--------
|
|
45
|
+
|
|
46
|
+
dotkill.DotKillZ(clip clip[, int order=0, int offset=0])
|
|
47
|
+
|
|
48
|
+
A pseudo-spatial dotcrawl and rainbow remover. It only works on NTSC content with rainbows added after 3:2 pulldown. This is true most of the time for anime.
|
|
49
|
+
|
|
50
|
+
Note that due to its nature only every other final frame will have dotcrawl and rainbows removed. Typically never artifacts if all requirements are met.
|
|
51
|
+
|
|
52
|
+
order: Field order. Usually 0, note that 1 hasn't been tested due to a lack of test material.
|
|
53
|
+
|
|
54
|
+
offset: The cycle offset for the pulldown pattern. A number between 0 and 4. Can only be determined by trial and error.
|
|
55
|
+
|
|
56
|
+
DotKillT
|
|
57
|
+
--------
|
|
58
|
+
|
|
59
|
+
dotkill.DotKillT(clip clip[, int order=0, int offset=0, int dupthresh=64, int tratio=3, bint show=False])
|
|
60
|
+
|
|
61
|
+
A full spatioi-temporal dotcrawl and rainbow remover. It only works on NTSC content with rainbows added after 3:2 pulldown. This is true most of the time for anime.
|
|
62
|
+
|
|
63
|
+
May produce extreme artifacting if dupthresh is set too high.
|
|
64
|
+
|
|
65
|
+
order: Field order. Usually 0, note that 1 hasn't been tested due to a lack of test material.
|
|
66
|
+
|
|
67
|
+
offset: The cycle offset for the pulldown pattern. A number between 0 and 4. Can only be determined by trial and error.
|
|
68
|
+
|
|
69
|
+
dupthresh: The threshold for determining if a block has changed between fields. Depending on the source material 32-128 are usually reasonable values. A value of 0 makes the function identical to DotKillZ.
|
|
70
|
+
|
|
71
|
+
tratio: If more than 1/tratio blocks have changed between fields then temporal filtering won't be considered in that direction. Higher values can make high motion sections less likely to artifact.
|
|
72
|
+
|
|
73
|
+
show: Shows which blocks have been determined to contain no change between fields and therefore will be blended to reduce artifacts. White square means that it will blend with the next frame and black square the previous.
|
|
74
|
+
|
|
75
|
+
Usage
|
|
76
|
+
=====
|
|
77
|
+
|
|
78
|
+
clip = core.dotkill.DotKillT(clip, offset=1, dupthresh=64)
|
|
79
|
+
|
|
80
|
+
clip = core.vivtc.VFM(clip)
|
|
81
|
+
|
|
82
|
+
clip = core.dotkill.DotKillS(clip, iterations=4)
|
|
83
|
+
|
|
84
|
+
clip = core.vivtc.VDecimate(clip)
|
|
85
|
+
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
DotKill
|
|
2
|
+
=======
|
|
3
|
+
|
|
4
|
+
Spatio-temporal dotcrawl and rainbow remover for VapourSynth
|
|
5
|
+
|
|
6
|
+
Functions
|
|
7
|
+
=========
|
|
8
|
+
|
|
9
|
+
DotKillS
|
|
10
|
+
--------
|
|
11
|
+
|
|
12
|
+
dotkill.DotKillS(clip clip[, int iterations=1])
|
|
13
|
+
|
|
14
|
+
A purely spatial dotcrawl remover that can be safely used on most material after field matching.
|
|
15
|
+
|
|
16
|
+
iterations: The number of times to apply the internal filter. Usally a number between 1 and 4 will have the best results and using too high values may cause artifacting.
|
|
17
|
+
|
|
18
|
+
usematch: If true then matching hints from VFM are used when processing. This may or may not have a positive effect.
|
|
19
|
+
|
|
20
|
+
DotKillZ
|
|
21
|
+
--------
|
|
22
|
+
|
|
23
|
+
dotkill.DotKillZ(clip clip[, int order=0, int offset=0])
|
|
24
|
+
|
|
25
|
+
A pseudo-spatial dotcrawl and rainbow remover. It only works on NTSC content with rainbows added after 3:2 pulldown. This is true most of the time for anime.
|
|
26
|
+
|
|
27
|
+
Note that due to its nature only every other final frame will have dotcrawl and rainbows removed. Typically never artifacts if all requirements are met.
|
|
28
|
+
|
|
29
|
+
order: Field order. Usually 0, note that 1 hasn't been tested due to a lack of test material.
|
|
30
|
+
|
|
31
|
+
offset: The cycle offset for the pulldown pattern. A number between 0 and 4. Can only be determined by trial and error.
|
|
32
|
+
|
|
33
|
+
DotKillT
|
|
34
|
+
--------
|
|
35
|
+
|
|
36
|
+
dotkill.DotKillT(clip clip[, int order=0, int offset=0, int dupthresh=64, int tratio=3, bint show=False])
|
|
37
|
+
|
|
38
|
+
A full spatioi-temporal dotcrawl and rainbow remover. It only works on NTSC content with rainbows added after 3:2 pulldown. This is true most of the time for anime.
|
|
39
|
+
|
|
40
|
+
May produce extreme artifacting if dupthresh is set too high.
|
|
41
|
+
|
|
42
|
+
order: Field order. Usually 0, note that 1 hasn't been tested due to a lack of test material.
|
|
43
|
+
|
|
44
|
+
offset: The cycle offset for the pulldown pattern. A number between 0 and 4. Can only be determined by trial and error.
|
|
45
|
+
|
|
46
|
+
dupthresh: The threshold for determining if a block has changed between fields. Depending on the source material 32-128 are usually reasonable values. A value of 0 makes the function identical to DotKillZ.
|
|
47
|
+
|
|
48
|
+
tratio: If more than 1/tratio blocks have changed between fields then temporal filtering won't be considered in that direction. Higher values can make high motion sections less likely to artifact.
|
|
49
|
+
|
|
50
|
+
show: Shows which blocks have been determined to contain no change between fields and therefore will be blended to reduce artifacts. White square means that it will blend with the next frame and black square the previous.
|
|
51
|
+
|
|
52
|
+
Usage
|
|
53
|
+
=====
|
|
54
|
+
|
|
55
|
+
clip = core.dotkill.DotKillT(clip, offset=1, dupthresh=64)
|
|
56
|
+
|
|
57
|
+
clip = core.vivtc.VFM(clip)
|
|
58
|
+
|
|
59
|
+
clip = core.dotkill.DotKillS(clip, iterations=4)
|
|
60
|
+
|
|
61
|
+
clip = core.vivtc.VDecimate(clip)
|
|
62
|
+
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
project('DotKill', 'cpp',
|
|
2
|
+
default_options: ['buildtype=release', 'warning_level=2', 'b_lto=true', 'b_ndebug=if-release', 'cpp_std=c++17'],
|
|
3
|
+
license: 'MIT',
|
|
4
|
+
license_files: 'LICENSE',
|
|
5
|
+
meson_version: '>=1.2.3',
|
|
6
|
+
version: '3.0',
|
|
7
|
+
)
|
|
8
|
+
|
|
9
|
+
incdir = include_directories(
|
|
10
|
+
run_command(
|
|
11
|
+
find_program('python', 'python3'),
|
|
12
|
+
'-c',
|
|
13
|
+
'import vapoursynth as vs; print(vs.get_include())',
|
|
14
|
+
check: true,
|
|
15
|
+
).stdout().strip(),
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
py = import('python').find_installation(pure: false)
|
|
19
|
+
|
|
20
|
+
shared_module('dotkill',
|
|
21
|
+
files('src/dotkill1.cpp'),
|
|
22
|
+
gnu_symbol_visibility: 'hidden',
|
|
23
|
+
include_directories: incdir,
|
|
24
|
+
install: true,
|
|
25
|
+
install_dir: py.get_install_dir() / 'vapoursynth/plugins',
|
|
26
|
+
name_prefix: '',
|
|
27
|
+
)
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["meson-python", "VapourSynth>=74"]
|
|
3
|
+
build-backend = "mesonpy"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "vapoursynth-dotkill"
|
|
7
|
+
description = "Rainbow and dotcrawl removal filter for VapourSynth"
|
|
8
|
+
readme = "README.md"
|
|
9
|
+
requires-python = ">=3.12"
|
|
10
|
+
license = "MIT"
|
|
11
|
+
license-files = ["LICENSE"]
|
|
12
|
+
authors = [
|
|
13
|
+
{name = "Fredrik Mellbin", email = "fredrik.mellbin@gmail.com"},
|
|
14
|
+
]
|
|
15
|
+
keywords = ["video", "rainbows", "dotcrawl"]
|
|
16
|
+
classifiers = [
|
|
17
|
+
"Development Status :: 5 - Production/Stable",
|
|
18
|
+
"Environment :: Plugins",
|
|
19
|
+
"Natural Language :: English",
|
|
20
|
+
"Operating System :: MacOS",
|
|
21
|
+
"Operating System :: Microsoft :: Windows",
|
|
22
|
+
"Operating System :: POSIX",
|
|
23
|
+
"Operating System :: Unix",
|
|
24
|
+
"Programming Language :: C++",
|
|
25
|
+
"Topic :: Multimedia :: Video",
|
|
26
|
+
]
|
|
27
|
+
dependencies = ["VapourSynth"]
|
|
28
|
+
dynamic = ["version"]
|
|
29
|
+
|
|
30
|
+
[project.urls]
|
|
31
|
+
Repository = "https://github.com/myrsloik/DotKill.git"
|
|
32
|
+
Issues = "https://github.com/myrsloik/DotKill/issues"
|
|
33
|
+
|
|
34
|
+
[tool.meson-python.wheel]
|
|
35
|
+
exclude = ["*/*.dll.a", "*/*.lib"]
|
|
@@ -0,0 +1,706 @@
|
|
|
1
|
+
#include "VapourSynth4.h"
|
|
2
|
+
#include "VSHelper4.h"
|
|
3
|
+
#include <algorithm>
|
|
4
|
+
#include <memory>
|
|
5
|
+
#include <cstdlib>
|
|
6
|
+
#include <vector>
|
|
7
|
+
|
|
8
|
+
////////////////////////////////////////
|
|
9
|
+
// DotKillS
|
|
10
|
+
|
|
11
|
+
typedef struct {
|
|
12
|
+
VSNode *node;
|
|
13
|
+
const VSVideoInfo *vi;
|
|
14
|
+
int iterations;
|
|
15
|
+
} DotKillSData;
|
|
16
|
+
|
|
17
|
+
static void convHoriz(const uint8_t *src, ptrdiff_t srcStride, int16_t *dst, int width, int height) {
|
|
18
|
+
while (--height) {
|
|
19
|
+
dst[0] = 0;
|
|
20
|
+
dst[1] = 0;
|
|
21
|
+
|
|
22
|
+
for (int x = 2; x < width - 3; x++) {
|
|
23
|
+
int16_t temp = -(src[x - 2] + src[x - 1]) + 2 * (src[x] + src[x + 1]) - (src[x + 2] + src[x + 3]);
|
|
24
|
+
temp += -(src[x - 2 + srcStride] + src[x - 1 + srcStride]) + 2 * (src[x + srcStride] + src[x + 1 + srcStride]) - (src[x + 2 + srcStride] + src[x + 3 + srcStride]);
|
|
25
|
+
dst[x] = temp;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
dst[width - 3] = 0;
|
|
29
|
+
dst[width - 2] = 0;
|
|
30
|
+
dst[width - 1] = 0;
|
|
31
|
+
|
|
32
|
+
src += srcStride;
|
|
33
|
+
dst += width;
|
|
34
|
+
}
|
|
35
|
+
memset(dst, 0, sizeof(int16_t) * width);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
static void convVert(const uint8_t *src, ptrdiff_t srcStride, int16_t *dst, int width, int height) {
|
|
39
|
+
height -= 5;
|
|
40
|
+
|
|
41
|
+
memset(dst, 0, sizeof(int16_t) * width * 2);
|
|
42
|
+
src += 2 * srcStride;
|
|
43
|
+
dst += 2 * width;
|
|
44
|
+
|
|
45
|
+
while (height--) {
|
|
46
|
+
for (int x = 0; x < width - 1; x++) {
|
|
47
|
+
dst[x] = -(src[x - 2 * srcStride] + src[x - 2 * srcStride + 1] + (src[x - 1 * srcStride] + src[x - 1 * srcStride + 1]))
|
|
48
|
+
+ 2 * (src[x] + src[x + 1] + (src[x + 1 * srcStride] + src[x + 1 * srcStride + 1]))
|
|
49
|
+
- (src[x + 2 * srcStride] + src[x + 2 * srcStride + 1] + (src[x + 3 * srcStride] + src[x + 3 * srcStride + 1]));
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
dst[width - 1] = 0;
|
|
53
|
+
|
|
54
|
+
src += srcStride;
|
|
55
|
+
dst += width;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
memset(dst, 0, sizeof(int16_t) * width * 3);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
static void applyMask(const int16_t *maskPtr, uint8_t *dst, ptrdiff_t dstStride, int width, int height, uint8_t *ppMask) {
|
|
62
|
+
maskPtr += width;
|
|
63
|
+
ppMask += width;
|
|
64
|
+
dst += dstStride;
|
|
65
|
+
|
|
66
|
+
int16_t sortArray[8];
|
|
67
|
+
|
|
68
|
+
for (int y = 1; y < height - 1; y++) {
|
|
69
|
+
for (int x = 1; x < width - 2; x++) {
|
|
70
|
+
sortArray[0] = maskPtr[x - width - 1];
|
|
71
|
+
sortArray[1] = maskPtr[x - width];
|
|
72
|
+
sortArray[2] = maskPtr[x - width + 1];
|
|
73
|
+
sortArray[3] = maskPtr[x - 1];
|
|
74
|
+
sortArray[4] = maskPtr[x + 1];
|
|
75
|
+
sortArray[5] = maskPtr[x + width - 1];
|
|
76
|
+
sortArray[6] = maskPtr[x + width];
|
|
77
|
+
sortArray[7] = maskPtr[x + width + 1];
|
|
78
|
+
|
|
79
|
+
std::sort(sortArray, sortArray + 8);
|
|
80
|
+
|
|
81
|
+
int16_t upper = sortArray[7];
|
|
82
|
+
int16_t lower = sortArray[0];
|
|
83
|
+
|
|
84
|
+
int16_t t = maskPtr[x] - std::clamp(maskPtr[x], lower, upper);
|
|
85
|
+
|
|
86
|
+
if (t >= 0)
|
|
87
|
+
t = (t + 4) / 8;
|
|
88
|
+
else
|
|
89
|
+
t = (t - 4) / 8;
|
|
90
|
+
|
|
91
|
+
if (std::abs(t) > 1) {
|
|
92
|
+
ppMask[x] = 255;
|
|
93
|
+
|
|
94
|
+
dst[x] = static_cast<uint8_t>(std::clamp(dst[x] - t, 16, 235));
|
|
95
|
+
dst[x + 1] = static_cast<uint8_t>(std::clamp(dst[x + 1] - t, 16, 235));
|
|
96
|
+
dst[x + dstStride] = static_cast<uint8_t>(std::clamp(dst[x + dstStride] - t, 16, 235));
|
|
97
|
+
dst[x + 1 + dstStride] = static_cast<uint8_t>(std::clamp(dst[x + 1 + dstStride] - t, 16, 235));
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
maskPtr += width;
|
|
102
|
+
ppMask += width;
|
|
103
|
+
dst += dstStride;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
static const VSFrame *VS_CC dotKillSGetFrame(int n, int activationReason, void *instanceData, void **frameData, VSFrameContext *frameCtx, VSCore *core, const VSAPI *vsapi) {
|
|
108
|
+
DotKillSData *d = reinterpret_cast<DotKillSData*>(instanceData);
|
|
109
|
+
|
|
110
|
+
if (activationReason == arInitial) {
|
|
111
|
+
vsapi->requestFrameFilter(n, d->node, frameCtx);
|
|
112
|
+
} else if (activationReason == arAllFramesReady) {
|
|
113
|
+
const VSFrame *inframe = vsapi->getFrameFilter(n, d->node, frameCtx);
|
|
114
|
+
VSFrame *outframe = vsapi->copyFrame(inframe, core);
|
|
115
|
+
|
|
116
|
+
int width = d->vi->width;
|
|
117
|
+
int height = d->vi->height;
|
|
118
|
+
ptrdiff_t dstStride = vsapi->getStride(outframe, 0);
|
|
119
|
+
uint8_t *dstPtr = vsapi->getWritePtr(outframe, 0);
|
|
120
|
+
|
|
121
|
+
int16_t *tempMask = new int16_t[width * height];
|
|
122
|
+
uint8_t *ppMask = new uint8_t[width * height]();
|
|
123
|
+
|
|
124
|
+
for (int i = 0; i < d->iterations; i++) {
|
|
125
|
+
convVert(dstPtr, dstStride, tempMask, width, height);
|
|
126
|
+
applyMask(tempMask, dstPtr, dstStride, width, height, ppMask);
|
|
127
|
+
|
|
128
|
+
convHoriz(dstPtr, dstStride, tempMask, width, height);
|
|
129
|
+
applyMask(tempMask, dstPtr, dstStride, width, height, ppMask);
|
|
130
|
+
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
delete[] tempMask;
|
|
134
|
+
delete[] ppMask;
|
|
135
|
+
|
|
136
|
+
return outframe;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
return nullptr;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
static void VS_CC dotKillSFree(void *instanceData, VSCore *core, const VSAPI *vsapi) {
|
|
143
|
+
DotKillSData *d = reinterpret_cast<DotKillSData*>(instanceData);
|
|
144
|
+
vsapi->freeNode(d->node);
|
|
145
|
+
delete d;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
static void VS_CC dotKillSCreate(const VSMap *in, VSMap *out, void *userData, VSCore *core, const VSAPI *vsapi) {
|
|
149
|
+
std::unique_ptr<DotKillSData> d(new DotKillSData());
|
|
150
|
+
|
|
151
|
+
int err;
|
|
152
|
+
d->node = vsapi->mapGetNode(in, "clip", 0, nullptr);
|
|
153
|
+
d->vi = vsapi->getVideoInfo(d->node);
|
|
154
|
+
d->iterations = vsapi->mapGetIntSaturated(in, "iterations", 0, &err);
|
|
155
|
+
if (d->iterations < 1)
|
|
156
|
+
d->iterations = 1;
|
|
157
|
+
if ((!vsh::isSameVideoPresetFormat(pfYUV420P8, &d->vi->format, core, vsapi) && !vsh::isSameVideoPresetFormat(pfGray8, &d->vi->format, core, vsapi)) || !vsh::isConstantVideoFormat(d->vi)) {
|
|
158
|
+
vsapi->mapSetError(out, "DotKillS: only constant dimension YUV420P8 and GRAY8 supported");
|
|
159
|
+
vsapi->freeNode(d->node);
|
|
160
|
+
return;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
VSFilterDependency deps[] = { {d->node, rpStrictSpatial} };
|
|
164
|
+
vsapi->createVideoFilter(out, "DotKillS", d->vi, dotKillSGetFrame, dotKillSFree, fmParallel, deps, 1, d.get(), core);
|
|
165
|
+
d.release();
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/////////////////////////////////////////////////////////////////////
|
|
169
|
+
// DotKillZ
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
static void applyFieldBlend(const VSFrame *srcc, const VSFrame *srcn, VSFrame *outframe, int order, VSCore *core, const VSAPI *vsapi) {
|
|
173
|
+
const VSVideoFormat *fi = vsapi->getVideoFrameFormat(srcc);
|
|
174
|
+
for (int plane = 0; plane < fi->numPlanes; plane++) {
|
|
175
|
+
int width = vsapi->getFrameWidth(srcc, plane);
|
|
176
|
+
int height = vsapi->getFrameHeight(srcc, plane);
|
|
177
|
+
|
|
178
|
+
ptrdiff_t stride = vsapi->getStride(outframe, plane);
|
|
179
|
+
uint8_t *dstp = vsapi->getWritePtr(outframe, plane);
|
|
180
|
+
const uint8_t *srccp = vsapi->getReadPtr(srcc, plane);
|
|
181
|
+
const uint8_t *srcnp = vsapi->getReadPtr(srcn, plane);
|
|
182
|
+
|
|
183
|
+
if (order) {
|
|
184
|
+
srccp += stride;
|
|
185
|
+
srcnp += stride;
|
|
186
|
+
dstp += stride;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
for (int h = order; h < height; h += 2) {
|
|
190
|
+
for (int w = 0; w < width; w++)
|
|
191
|
+
dstp[w] = (srccp[w] + srcnp[w] + 1) / 2;
|
|
192
|
+
|
|
193
|
+
srccp += 2 * stride;
|
|
194
|
+
srcnp += 2 * stride;
|
|
195
|
+
dstp += 2 * stride;
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
static void applyDotcrawInverse(const VSFrame *srcc, const VSFrame *srcn, VSFrame *outframe, int order, VSCore *core, const VSAPI *vsapi) {
|
|
201
|
+
const VSVideoFormat *fi = vsapi->getVideoFrameFormat(srcc);
|
|
202
|
+
for (int plane = 0; plane < fi->numPlanes; plane++) {
|
|
203
|
+
int width = vsapi->getFrameWidth(srcc, plane);
|
|
204
|
+
int height = vsapi->getFrameHeight(srcc, plane);
|
|
205
|
+
|
|
206
|
+
ptrdiff_t stride = vsapi->getStride(outframe, plane);
|
|
207
|
+
uint8_t *dstp = vsapi->getWritePtr(outframe, plane);
|
|
208
|
+
const uint8_t *srccp = vsapi->getReadPtr(srcc, plane);
|
|
209
|
+
const uint8_t *srcnp = vsapi->getReadPtr(srcn, plane);
|
|
210
|
+
|
|
211
|
+
if (order) {
|
|
212
|
+
srccp += stride;
|
|
213
|
+
srcnp += stride;
|
|
214
|
+
dstp += stride;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
for (int h = order; h < height; h += 2) {
|
|
218
|
+
for (int w = 0; w < width; w++) {
|
|
219
|
+
dstp[w] = (srccp[w] + srcnp[w] + 1) / 2;
|
|
220
|
+
|
|
221
|
+
if (h > 1) {
|
|
222
|
+
uint8_t l0val = dstp[w - 2 * stride];
|
|
223
|
+
uint8_t l2val = dstp[w];
|
|
224
|
+
int l0diff = dstp[w - 2 * stride] - srccp[w - 2 * stride];
|
|
225
|
+
int l2diff = dstp[w] - srccp[w];
|
|
226
|
+
if (plane == 0)
|
|
227
|
+
dstp[w - stride] = std::clamp(srccp[w - stride] + (order ? l0diff : l2diff), 16, 235);
|
|
228
|
+
else
|
|
229
|
+
dstp[w - stride] = (l0val + l2val + 1) / 2; // simply use some kind of interpolation and discard one field?
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
srccp += 2 * stride;
|
|
234
|
+
srcnp += 2 * stride;
|
|
235
|
+
dstp += 2 * stride;
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
typedef struct {
|
|
241
|
+
VSNode *node;
|
|
242
|
+
const VSVideoInfo *vi;
|
|
243
|
+
int order;
|
|
244
|
+
int offset;
|
|
245
|
+
} DotKillZData;
|
|
246
|
+
|
|
247
|
+
static const VSFrame *VS_CC dotKillZGetFrame(int n, int activationReason, void *instanceData, void **frameData, VSFrameContext *frameCtx, VSCore *core, const VSAPI *vsapi) {
|
|
248
|
+
DotKillZData *d = reinterpret_cast<DotKillZData*>(instanceData);
|
|
249
|
+
|
|
250
|
+
if (activationReason == arInitial) {
|
|
251
|
+
vsapi->requestFrameFilter(std::max(n - 1, 0), d->node, frameCtx);
|
|
252
|
+
vsapi->requestFrameFilter(n, d->node, frameCtx);
|
|
253
|
+
vsapi->requestFrameFilter(n + 1, d->node, frameCtx);
|
|
254
|
+
} else if (activationReason == arAllFramesReady) {
|
|
255
|
+
const VSFrame *srcp = vsapi->getFrameFilter(std::max(n - 1, 0), d->node, frameCtx);
|
|
256
|
+
const VSFrame *srcc = vsapi->getFrameFilter(n, d->node, frameCtx);
|
|
257
|
+
const VSFrame *srcn = vsapi->getFrameFilter(n + 1, d->node, frameCtx);
|
|
258
|
+
|
|
259
|
+
/*
|
|
260
|
+
FIELD OFFSETS
|
|
261
|
+
-1 0 1 2 3
|
|
262
|
+
A1 B1 B1 C1 D1
|
|
263
|
+
A2 B2 C2 D2 D2
|
|
264
|
+
*/
|
|
265
|
+
|
|
266
|
+
VSFrame *outframe = vsapi->copyFrame(srcc, core);
|
|
267
|
+
if ((n + d->offset) % 5 == 0) {
|
|
268
|
+
// current and next field are duplicates, complement field is from the same frame so do dotcrawl inverse on that as well
|
|
269
|
+
applyDotcrawInverse(srcc, srcn, outframe, d->order, core, vsapi);
|
|
270
|
+
} else if ((n + d->offset) % 5 == 1) {
|
|
271
|
+
// current and previous field are duplicates so blend them together
|
|
272
|
+
applyFieldBlend(srcc, srcp, outframe, d->order, core, vsapi);
|
|
273
|
+
} else if ((n + d->offset) % 5 == 2) {
|
|
274
|
+
// current and next complement field are duplicates so blend them together
|
|
275
|
+
applyFieldBlend(srcc, srcn, outframe, !d->order, core, vsapi);
|
|
276
|
+
} else if ((n + d->offset) % 5 == 3) {
|
|
277
|
+
// current and previous field are duplicates, complement field is from the same frame so do dotcrawl inverse on that as well
|
|
278
|
+
applyDotcrawInverse(srcc, srcp, outframe, !d->order, core, vsapi);
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
vsapi->freeFrame(srcp);
|
|
282
|
+
vsapi->freeFrame(srcc);
|
|
283
|
+
vsapi->freeFrame(srcn);
|
|
284
|
+
|
|
285
|
+
return outframe;
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
return nullptr;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
static void VS_CC dotKillZFree(void *instanceData, VSCore *core, const VSAPI *vsapi) {
|
|
292
|
+
DotKillZData *d = reinterpret_cast<DotKillZData*>(instanceData);
|
|
293
|
+
vsapi->freeNode(d->node);
|
|
294
|
+
delete d;
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
static void VS_CC dotKillZCreate(const VSMap *in, VSMap *out, void *userData, VSCore *core, const VSAPI *vsapi) {
|
|
298
|
+
std::unique_ptr<DotKillZData> d(new DotKillZData());
|
|
299
|
+
|
|
300
|
+
int err;
|
|
301
|
+
d->node = vsapi->mapGetNode(in, "clip", 0, nullptr);
|
|
302
|
+
d->vi = vsapi->getVideoInfo(d->node);
|
|
303
|
+
d->offset = vsapi->mapGetIntSaturated(in, "offset", 0, &err);
|
|
304
|
+
d->order = !!vsapi->mapGetInt(in, "order", 0, &err);
|
|
305
|
+
|
|
306
|
+
if (!vsh::isSameVideoPresetFormat(pfYUV420P8, &d->vi->format, core, vsapi) || !vsh::isConstantVideoFormat(d->vi)) {
|
|
307
|
+
vsapi->mapSetError(out, "DotKillZ: only constant dimension YUV420P8 supported");
|
|
308
|
+
vsapi->freeNode(d->node);
|
|
309
|
+
return;
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
VSFilterDependency deps[] = { {d->node, rpGeneral} };
|
|
313
|
+
vsapi->createVideoFilter(out, "DotKillZ", d->vi, dotKillZGetFrame, dotKillZFree, fmParallelRequests, deps, 1, d.get(), core);
|
|
314
|
+
d.release();
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
/////////////////////////////////////////////////////////////////////
|
|
318
|
+
// DotKillT
|
|
319
|
+
|
|
320
|
+
constexpr int blockx = 16;
|
|
321
|
+
constexpr int blocky = 8;
|
|
322
|
+
|
|
323
|
+
static void calcDiffMetric(const VSFrame *f1, const VSFrame *f2, int64_t *bdiffs, int nxblocks, int nyblocks,int field, const VSAPI *vsapi) {
|
|
324
|
+
for (int plane = 0; plane < 3; plane++) {
|
|
325
|
+
ptrdiff_t stride = vsapi->getStride(f1, plane);
|
|
326
|
+
const uint8_t *f1p = vsapi->getReadPtr(f1, plane);
|
|
327
|
+
const uint8_t *f2p = vsapi->getReadPtr(f2, plane);
|
|
328
|
+
const VSVideoFormat *fi = vsapi->getVideoFrameFormat(f1);
|
|
329
|
+
|
|
330
|
+
if (field) {
|
|
331
|
+
f1p += stride;
|
|
332
|
+
f2p += stride;
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
int width = vsapi->getFrameWidth(f1, plane);
|
|
336
|
+
int height = vsapi->getFrameHeight(f1, plane);
|
|
337
|
+
int hblockx = blockx / 2;
|
|
338
|
+
int hblocky = blocky / 2;
|
|
339
|
+
// adjust for subsampling
|
|
340
|
+
if (plane > 0) {
|
|
341
|
+
hblockx /= 1 << fi->subSamplingW;
|
|
342
|
+
hblocky /= 1 << fi->subSamplingH;
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
for (int y = 0; y < height / 2; y++) {
|
|
346
|
+
int ydest = y / hblocky;
|
|
347
|
+
int xdest = 0;
|
|
348
|
+
|
|
349
|
+
for (int x = 0; x < width; x += hblockx) {
|
|
350
|
+
int acc = 0;
|
|
351
|
+
int m = VSMIN(width, x + hblockx);
|
|
352
|
+
for (int xl = x; xl < m; xl++) {
|
|
353
|
+
int tmp = f1p[xl] - f2p[xl];
|
|
354
|
+
acc += tmp * tmp;
|
|
355
|
+
}
|
|
356
|
+
bdiffs[ydest * nxblocks + xdest] += acc;
|
|
357
|
+
xdest++;
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
f1p += stride * 2;
|
|
361
|
+
f2p += stride * 2;
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
static int64_t getMaxDiff(int i, int j, const int64_t *bdiffs1, int nxblocks, int nyblocks) {
|
|
367
|
+
int64_t tmp1 = bdiffs1[i * nxblocks + j] + bdiffs1[i * nxblocks + j + 1] + bdiffs1[(i + 1) * nxblocks + j] + bdiffs1[(i + 1) * nxblocks + j + 1];
|
|
368
|
+
int64_t tmp2 = bdiffs1[i * nxblocks + j] + bdiffs1[i * nxblocks + j - 1] + bdiffs1[(i + 1) * nxblocks + j] + bdiffs1[(i + 1) * nxblocks + j - 1];
|
|
369
|
+
int64_t tmp3 = bdiffs1[i * nxblocks + j] + bdiffs1[i * nxblocks - j + 1] + bdiffs1[(i - 1) * nxblocks + j] + bdiffs1[(i - 1) * nxblocks + j + 1];
|
|
370
|
+
int64_t tmp4 = bdiffs1[i * nxblocks + j] + bdiffs1[i * nxblocks - j - 1] + bdiffs1[(i - 1) * nxblocks + j] + bdiffs1[(i - 1) * nxblocks + j - 1];
|
|
371
|
+
return std::max({ tmp1, tmp2, tmp3, tmp4 });
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
static void diffMetricToMask(uint8_t *mask, const int64_t *bdiffs1, const int64_t *bdiffs2, int nxblocks, int nyblocks, int dupthresh, int tratio, const VSAPI *vsapi) {
|
|
375
|
+
int totdiff1 = 0;
|
|
376
|
+
int totdiff2 = 0;
|
|
377
|
+
|
|
378
|
+
for (int i = 1; i < nyblocks - 1; i++) {
|
|
379
|
+
for (int j = 1; j < nxblocks - 1; j++) {
|
|
380
|
+
int64_t diff1 = getMaxDiff(i, j, bdiffs1, nxblocks, nyblocks);
|
|
381
|
+
int64_t diff2 = getMaxDiff(i, j, bdiffs2, nxblocks, nyblocks);
|
|
382
|
+
|
|
383
|
+
if (diff1 >= dupthresh)
|
|
384
|
+
totdiff1++;
|
|
385
|
+
if (diff2 >= dupthresh)
|
|
386
|
+
totdiff2++;
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
// skip temporal processing if more than 1/tratio blocks have changed
|
|
391
|
+
bool skip1 = (totdiff1 * tratio > (nxblocks - 2) * (nyblocks - 2));
|
|
392
|
+
bool skip2 = (totdiff2 * tratio > (nxblocks - 2) * (nyblocks - 2));
|
|
393
|
+
|
|
394
|
+
for (int i = 1; i < nyblocks - 1; i++) {
|
|
395
|
+
for (int j = 1; j < nxblocks - 1; j++) {
|
|
396
|
+
int64_t diff1 = getMaxDiff(i, j, bdiffs1, nxblocks, nyblocks);
|
|
397
|
+
int64_t diff2 = getMaxDiff(i, j, bdiffs2, nxblocks, nyblocks);
|
|
398
|
+
|
|
399
|
+
if (!skip1 && diff1 <= diff2 && diff1 < dupthresh)
|
|
400
|
+
mask[nxblocks * i + j] = 1;
|
|
401
|
+
else if (!skip2 && diff2 < diff1 && diff2 < dupthresh)
|
|
402
|
+
mask[nxblocks * i + j] = 2;
|
|
403
|
+
else
|
|
404
|
+
mask[nxblocks * i + j] = 0;
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
// extend mask left and right
|
|
408
|
+
mask[nxblocks * i] = mask[nxblocks * i + 1];
|
|
409
|
+
mask[nxblocks * i + (nxblocks - 1)] = mask[nxblocks * i + (nxblocks - 2)];
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
// extend mask to top and bottom
|
|
413
|
+
memcpy(mask, mask + nxblocks, nxblocks);
|
|
414
|
+
memcpy(mask + nxblocks * (nyblocks - 1), mask + nxblocks * (nyblocks - 2), nxblocks);
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
static void applyTemporalMask(VSFrame *dst, const VSFrame *f0, const VSFrame *f1, const VSFrame *f2, uint8_t *mask, int nxblocks, int nyblocks, int field, bool show, const VSAPI *vsapi) {
|
|
418
|
+
for (int plane = 0; plane < 3; plane++) {
|
|
419
|
+
ptrdiff_t stride = vsapi->getStride(f1, plane);
|
|
420
|
+
const uint8_t *f0p = vsapi->getReadPtr(f0, plane);
|
|
421
|
+
const uint8_t *f1p = vsapi->getReadPtr(f1, plane);
|
|
422
|
+
const uint8_t *f2p = vsapi->getReadPtr(f2, plane);
|
|
423
|
+
uint8_t *dstp = vsapi->getWritePtr(dst, plane);
|
|
424
|
+
const VSVideoFormat *fi = vsapi->getVideoFrameFormat(f1);
|
|
425
|
+
|
|
426
|
+
if (field) {
|
|
427
|
+
f0p += stride;
|
|
428
|
+
f1p += stride;
|
|
429
|
+
f2p += stride;
|
|
430
|
+
dstp += stride;
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
int width = vsapi->getFrameWidth(f1, plane);
|
|
434
|
+
int height = vsapi->getFrameHeight(f1, plane);
|
|
435
|
+
int hblockx = blockx / 2;
|
|
436
|
+
int hblocky = blocky / 2;
|
|
437
|
+
|
|
438
|
+
if (plane > 0) {
|
|
439
|
+
hblockx /= 1 << fi->subSamplingW;
|
|
440
|
+
hblocky /= 1 << fi->subSamplingH;
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
for (int y = 0; y < height / 2; y++) {
|
|
444
|
+
int ydest = y / hblocky;
|
|
445
|
+
int xdest = 0;
|
|
446
|
+
|
|
447
|
+
for (int x = 0; x < width; x += hblockx) {
|
|
448
|
+
int m = VSMIN(width, x + hblockx);
|
|
449
|
+
|
|
450
|
+
for (int xl = x; xl < m; xl++) {
|
|
451
|
+
if (mask[ydest * nxblocks + xdest] == 1)
|
|
452
|
+
dstp[xl] = ((f1p[xl] + f0p[xl] + 1) / 2);
|
|
453
|
+
else if (mask[ydest * nxblocks + xdest] == 2)
|
|
454
|
+
dstp[xl] = ((f2p[xl] + f0p[xl] + 1) / 2);
|
|
455
|
+
else
|
|
456
|
+
dstp[xl] = dstp[xl];
|
|
457
|
+
}
|
|
458
|
+
xdest++;
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
f0p += stride * 2;
|
|
462
|
+
f1p += stride * 2;
|
|
463
|
+
f2p += stride * 2;
|
|
464
|
+
dstp += stride * 2;
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
// Horrible square drawing code
|
|
469
|
+
if (show) {
|
|
470
|
+
ptrdiff_t stride = vsapi->getStride(dst, 0);
|
|
471
|
+
uint8_t *dstp = vsapi->getWritePtr(dst, 0);
|
|
472
|
+
|
|
473
|
+
int width = vsapi->getFrameWidth(dst, 0);
|
|
474
|
+
int height = vsapi->getFrameHeight(dst, 0);
|
|
475
|
+
int hblockx = blockx / 2;
|
|
476
|
+
|
|
477
|
+
for (int y = 0; y < height; y++) {
|
|
478
|
+
int ydest = y / blocky;
|
|
479
|
+
int xdest = 0;
|
|
480
|
+
|
|
481
|
+
for (int x = 0; x < width; x += hblockx) {
|
|
482
|
+
int m = std::min(width, x + hblockx);
|
|
483
|
+
|
|
484
|
+
if (y % blocky == 0) {
|
|
485
|
+
for (int xl = x; xl < m; xl++) {
|
|
486
|
+
if (mask[ydest * nxblocks + xdest] == 1) {
|
|
487
|
+
dstp[xl] = 0;
|
|
488
|
+
} else if (mask[ydest * nxblocks + xdest] == 2) {
|
|
489
|
+
dstp[xl] = 255;
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
} else if (y % blocky == blocky - 1) {
|
|
493
|
+
for (int xl = x; xl < m; xl++) {
|
|
494
|
+
if (mask[ydest * nxblocks + xdest] == 1) {
|
|
495
|
+
dstp[xl] = 0;
|
|
496
|
+
} else if (mask[ydest * nxblocks + xdest] == 2) {
|
|
497
|
+
dstp[xl] = 255;
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
if (mask[ydest * nxblocks + xdest] == 1) {
|
|
503
|
+
dstp[x] = 0;
|
|
504
|
+
dstp[m - 1] = 0;
|
|
505
|
+
} else if (mask[ydest * nxblocks + xdest] == 2) {
|
|
506
|
+
dstp[x] = 255;
|
|
507
|
+
dstp[m - 1] = 255;
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
xdest++;
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
dstp += stride;
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
typedef struct {
|
|
519
|
+
VSNode *node;
|
|
520
|
+
const VSVideoInfo *vi;
|
|
521
|
+
int order;
|
|
522
|
+
int offset;
|
|
523
|
+
int dupthresh;
|
|
524
|
+
int tratio;
|
|
525
|
+
bool show;
|
|
526
|
+
} DotKillTData;
|
|
527
|
+
|
|
528
|
+
static const VSFrame *VS_CC dotKillTGetFrame(int n, int activationReason, void *instanceData, void **frameData, VSFrameContext *frameCtx, VSCore *core, const VSAPI *vsapi) {
|
|
529
|
+
DotKillTData *d = reinterpret_cast<DotKillTData*>(instanceData);
|
|
530
|
+
|
|
531
|
+
int nxblocks = (d->vi->width + blockx / 2 - 1) / (blockx / 2);
|
|
532
|
+
int nyblocks = (d->vi->height + blocky / 2 - 1) / (blocky / 2);
|
|
533
|
+
|
|
534
|
+
if (activationReason == arInitial) {
|
|
535
|
+
vsapi->requestFrameFilter(std::max(n - 2, 0), d->node, frameCtx);
|
|
536
|
+
vsapi->requestFrameFilter(std::max(n - 1, 0), d->node, frameCtx);
|
|
537
|
+
vsapi->requestFrameFilter(n, d->node, frameCtx);
|
|
538
|
+
vsapi->requestFrameFilter(n + 1, d->node, frameCtx);
|
|
539
|
+
vsapi->requestFrameFilter(n + 2, d->node, frameCtx);
|
|
540
|
+
} else if (activationReason == arAllFramesReady) {
|
|
541
|
+
const VSFrame *srcpp = vsapi->getFrameFilter(std::max(n - 2, 0), d->node, frameCtx);
|
|
542
|
+
const VSFrame *srcp = vsapi->getFrameFilter(std::max(n - 1, 0), d->node, frameCtx);
|
|
543
|
+
const VSFrame *srcc = vsapi->getFrameFilter(n, d->node, frameCtx);
|
|
544
|
+
const VSFrame *srcn = vsapi->getFrameFilter(n + 1, d->node, frameCtx);
|
|
545
|
+
const VSFrame *srcnn = vsapi->getFrameFilter(n + 2, d->node, frameCtx);
|
|
546
|
+
|
|
547
|
+
// first two fields are duplicates, meaning that offset 0, 1 and 5, 6 are used in the various calculations
|
|
548
|
+
// fields 2, 3 and 4 needs to have determined how many blocks are consecutively static
|
|
549
|
+
// note that comparisons are only run on a single since we can in most comparisons can eliminate
|
|
550
|
+
// the dotcrawl from the equation
|
|
551
|
+
|
|
552
|
+
// 0-1 2 3 4 5-6
|
|
553
|
+
|
|
554
|
+
// the complementary field can likewise be used for movement detection
|
|
555
|
+
|
|
556
|
+
/*
|
|
557
|
+
FIELD OFFSETS
|
|
558
|
+
+ - + - + -
|
|
559
|
+
-1 0 1 2 3 4
|
|
560
|
+
A1 B1 B1 C1 D1 E1
|
|
561
|
+
A2 B2 C2 D2 D2 E2
|
|
562
|
+
*/
|
|
563
|
+
|
|
564
|
+
VSFrame *outframe = vsapi->copyFrame(srcc, core);
|
|
565
|
+
|
|
566
|
+
vsapi->mapSetInt(vsapi->getFramePropertiesRW(outframe), "DotKillTOffset", (n + d->offset) % 5, maReplace);
|
|
567
|
+
|
|
568
|
+
if ((n + d->offset) % 5 == 0) {
|
|
569
|
+
// 1
|
|
570
|
+
applyDotcrawInverse(srcc, srcn, outframe, d->order, core, vsapi);
|
|
571
|
+
|
|
572
|
+
// 2
|
|
573
|
+
std::vector<int64_t> maskprev1(nxblocks * nyblocks);
|
|
574
|
+
std::vector<int64_t> masknext1(nxblocks * nyblocks);
|
|
575
|
+
std::vector<uint8_t> mask(nxblocks * nyblocks);
|
|
576
|
+
calcDiffMetric(srcp, srcn, maskprev1.data(), nxblocks, nyblocks, d->order, vsapi);
|
|
577
|
+
calcDiffMetric(srcc, srcnn, masknext1.data(), nxblocks, nyblocks, d->order, vsapi);
|
|
578
|
+
|
|
579
|
+
diffMetricToMask(mask.data(), maskprev1.data(), masknext1.data(), nxblocks, nyblocks, d->dupthresh, d->tratio, vsapi);
|
|
580
|
+
|
|
581
|
+
applyTemporalMask(outframe, srcc, srcp, srcn, mask.data(), nxblocks, nyblocks, !d->order, d->show, vsapi);
|
|
582
|
+
} else if ((n + d->offset) % 5 == 1) {
|
|
583
|
+
// 1
|
|
584
|
+
applyFieldBlend(srcc, srcp, outframe, d->order, core, vsapi);
|
|
585
|
+
|
|
586
|
+
// 2
|
|
587
|
+
std::vector<int64_t> maskprev1(nxblocks * nyblocks);
|
|
588
|
+
std::vector<int64_t> masknext1(nxblocks * nyblocks);
|
|
589
|
+
std::vector<uint8_t> mask(nxblocks * nyblocks);
|
|
590
|
+
calcDiffMetric(srcp, srcn, maskprev1.data(), nxblocks, nyblocks, d->order, vsapi);
|
|
591
|
+
calcDiffMetric(srcc, srcnn, masknext1.data(), nxblocks, nyblocks, !d->order, vsapi);
|
|
592
|
+
|
|
593
|
+
diffMetricToMask(mask.data(), maskprev1.data(), masknext1.data(), nxblocks, nyblocks, d->dupthresh, d->tratio, vsapi);
|
|
594
|
+
|
|
595
|
+
applyTemporalMask(outframe, srcc, srcp, srcn, mask.data(), nxblocks, nyblocks, !d->order, d->show, vsapi);
|
|
596
|
+
} else if ((n + d->offset) % 5 == 2) {
|
|
597
|
+
// 1
|
|
598
|
+
std::vector<int64_t> maskprev1(nxblocks * nyblocks);
|
|
599
|
+
std::vector<int64_t> masknext1(nxblocks * nyblocks);
|
|
600
|
+
std::vector<uint8_t> mask(nxblocks * nyblocks);
|
|
601
|
+
calcDiffMetric(srcc, srcpp, maskprev1.data(), nxblocks, nyblocks, d->order, vsapi);
|
|
602
|
+
calcDiffMetric(srcp, srcn, masknext1.data(), nxblocks, nyblocks, !d->order, vsapi);
|
|
603
|
+
|
|
604
|
+
diffMetricToMask(mask.data(), maskprev1.data(), masknext1.data(), nxblocks, nyblocks, d->dupthresh, d->tratio, vsapi);
|
|
605
|
+
|
|
606
|
+
applyTemporalMask(outframe, srcc, srcp, srcn, mask.data(), nxblocks, nyblocks, d->order, d->show, vsapi);
|
|
607
|
+
|
|
608
|
+
// 2
|
|
609
|
+
applyFieldBlend(srcc, srcn, outframe, !d->order, core, vsapi);
|
|
610
|
+
} else if ((n + d->offset) % 5 == 3) {
|
|
611
|
+
// 2
|
|
612
|
+
applyDotcrawInverse(srcc, srcp, outframe, !d->order, core, vsapi);
|
|
613
|
+
|
|
614
|
+
// 1
|
|
615
|
+
std::vector<int64_t> maskprev1(nxblocks * nyblocks);
|
|
616
|
+
std::vector<int64_t> masknext1(nxblocks * nyblocks);
|
|
617
|
+
std::vector<uint8_t> mask(nxblocks * nyblocks);
|
|
618
|
+
calcDiffMetric(srcc, srcpp, maskprev1.data(), nxblocks, nyblocks, !d->order, vsapi);
|
|
619
|
+
calcDiffMetric(srcp, srcn, masknext1.data(), nxblocks, nyblocks, !d->order, vsapi);
|
|
620
|
+
|
|
621
|
+
diffMetricToMask(mask.data(), maskprev1.data(), masknext1.data(), nxblocks, nyblocks, d->dupthresh, d->tratio, vsapi);
|
|
622
|
+
|
|
623
|
+
applyTemporalMask(outframe, srcc, srcp, srcn, mask.data(), nxblocks, nyblocks, d->order, d->show, vsapi);
|
|
624
|
+
} else if ((n + d->offset) % 5 == 4) {
|
|
625
|
+
// 1
|
|
626
|
+
{
|
|
627
|
+
std::vector<int64_t> maskprev1(nxblocks * nyblocks);
|
|
628
|
+
std::vector<int64_t> masknext1(nxblocks * nyblocks);
|
|
629
|
+
std::vector<uint8_t> mask(nxblocks * nyblocks);
|
|
630
|
+
calcDiffMetric(srcc, srcpp, maskprev1.data(), nxblocks, nyblocks, !d->order, vsapi);
|
|
631
|
+
calcDiffMetric(srcc, srcnn, masknext1.data(), nxblocks, nyblocks, d->order, vsapi);
|
|
632
|
+
|
|
633
|
+
diffMetricToMask(mask.data(), maskprev1.data(), masknext1.data(), nxblocks, nyblocks, d->dupthresh, d->tratio, vsapi);
|
|
634
|
+
|
|
635
|
+
applyTemporalMask(outframe, srcc, srcp, srcn, mask.data(), nxblocks, nyblocks, d->order, d->show, vsapi);
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
// 2
|
|
639
|
+
{
|
|
640
|
+
std::vector<int64_t> maskprev1(nxblocks * nyblocks);
|
|
641
|
+
std::vector<int64_t> masknext1(nxblocks * nyblocks);
|
|
642
|
+
std::vector<uint8_t> mask(nxblocks * nyblocks);
|
|
643
|
+
calcDiffMetric(srcpp, srcc, maskprev1.data(), nxblocks, nyblocks, !d->order, vsapi);
|
|
644
|
+
calcDiffMetric(srcc, srcnn, masknext1.data(), nxblocks, nyblocks, d->order, vsapi);
|
|
645
|
+
|
|
646
|
+
diffMetricToMask(mask.data(), maskprev1.data(), masknext1.data(), nxblocks, nyblocks, d->dupthresh, d->tratio, vsapi);
|
|
647
|
+
|
|
648
|
+
applyTemporalMask(outframe, srcc, srcp, srcn, mask.data(), nxblocks, nyblocks, !d->order, d->show, vsapi);
|
|
649
|
+
}
|
|
650
|
+
}
|
|
651
|
+
|
|
652
|
+
vsapi->freeFrame(srcpp);
|
|
653
|
+
vsapi->freeFrame(srcp);
|
|
654
|
+
vsapi->freeFrame(srcc);
|
|
655
|
+
vsapi->freeFrame(srcn);
|
|
656
|
+
vsapi->freeFrame(srcnn);
|
|
657
|
+
|
|
658
|
+
return outframe;
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
return nullptr;
|
|
662
|
+
}
|
|
663
|
+
|
|
664
|
+
static void VS_CC dotKillTFree(void *instanceData, VSCore *core, const VSAPI *vsapi) {
|
|
665
|
+
DotKillTData *d = reinterpret_cast<DotKillTData*>(instanceData);
|
|
666
|
+
vsapi->freeNode(d->node);
|
|
667
|
+
delete d;
|
|
668
|
+
}
|
|
669
|
+
|
|
670
|
+
static void VS_CC dotKillTCreate(const VSMap *in, VSMap *out, void *userData, VSCore *core, const VSAPI *vsapi) {
|
|
671
|
+
std::unique_ptr<DotKillTData> d(new DotKillTData());
|
|
672
|
+
|
|
673
|
+
int err;
|
|
674
|
+
d->node = vsapi->mapGetNode(in, "clip", 0, nullptr);
|
|
675
|
+
d->vi = vsapi->getVideoInfo(d->node);
|
|
676
|
+
d->offset = vsapi->mapGetIntSaturated(in, "offset", 0, &err);
|
|
677
|
+
d->order = !!vsapi->mapGetInt(in, "order", 0, &err);
|
|
678
|
+
d->dupthresh = vsapi->mapGetIntSaturated(in, "dupthresh", 0, &err);
|
|
679
|
+
if (err || d->dupthresh < 0)
|
|
680
|
+
d->dupthresh = 64;
|
|
681
|
+
d->dupthresh *= d->dupthresh;
|
|
682
|
+
d->tratio = vsapi->mapGetIntSaturated(in, "tratio", 0, &err);
|
|
683
|
+
if (err || d->tratio < 1)
|
|
684
|
+
d->tratio = 3;
|
|
685
|
+
d->show = !!vsapi->mapGetInt(in, "show", 0, &err);
|
|
686
|
+
|
|
687
|
+
if (!vsh::isSameVideoPresetFormat(pfYUV420P8, &d->vi->format, core, vsapi) || !vsh::isConstantVideoFormat(d->vi)) {
|
|
688
|
+
vsapi->mapSetError(out, "DotKillT: only constant dimension YUV420P8 supported");
|
|
689
|
+
vsapi->freeNode(d->node);
|
|
690
|
+
return;
|
|
691
|
+
}
|
|
692
|
+
|
|
693
|
+
VSFilterDependency deps[] = { {d->node, rpGeneral} };
|
|
694
|
+
vsapi->createVideoFilter(out, "DotKillT", d->vi, dotKillTGetFrame, dotKillTFree, fmParallelRequests, deps, 1, d.get(), core);
|
|
695
|
+
d.release();
|
|
696
|
+
}
|
|
697
|
+
|
|
698
|
+
//////////////////////////////////////////
|
|
699
|
+
// Init
|
|
700
|
+
|
|
701
|
+
VS_EXTERNAL_API(void) VapourSynthPluginInit2(VSPlugin *plugin, const VSPLUGINAPI *vspapi) {
|
|
702
|
+
vspapi->configPlugin("com.vapoursynth.dotkill", "dotkill", "VapourSynth DotKill", VS_MAKE_VERSION(4, 0), VAPOURSYNTH_API_VERSION, 0, plugin);
|
|
703
|
+
vspapi->registerFunction("DotKillS", "clip:vnode;iterations:int:opt;", "clip:vnode", dotKillSCreate, 0, plugin);
|
|
704
|
+
vspapi->registerFunction("DotKillZ", "clip:vnode;order:int:opt;offset:int:opt;", "clip:vnode", dotKillZCreate, 0, plugin);
|
|
705
|
+
vspapi->registerFunction("DotKillT", "clip:vnode;order:int:opt;offset:int:opt;dupthresh:int:opt;tratio:int:opt;show:int:opt;", "clip:vnode", dotKillTCreate, 0, plugin);
|
|
706
|
+
}
|