vapoursynth-vszipcu 1.1.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_vszipcu-1.1.0/.gitignore +24 -0
- vapoursynth_vszipcu-1.1.0/LICENSE +21 -0
- vapoursynth_vszipcu-1.1.0/PKG-INFO +241 -0
- vapoursynth_vszipcu-1.1.0/README.md +231 -0
- vapoursynth_vszipcu-1.1.0/build.zig +50 -0
- vapoursynth_vszipcu-1.1.0/build.zig.zon +17 -0
- vapoursynth_vszipcu-1.1.0/cudaz/LICENSE.md +27 -0
- vapoursynth_vszipcu-1.1.0/cudaz/Readme.md +166 -0
- vapoursynth_vszipcu-1.1.0/cudaz/build.zig +171 -0
- vapoursynth_vszipcu-1.1.0/cudaz/build.zig.zon +35 -0
- vapoursynth_vszipcu-1.1.0/cudaz/src/c.zig +9 -0
- vapoursynth_vszipcu-1.1.0/cudaz/src/compile.zig +163 -0
- vapoursynth_vszipcu-1.1.0/cudaz/src/cufft.zig +264 -0
- vapoursynth_vszipcu-1.1.0/cudaz/src/curand.zig +124 -0
- vapoursynth_vszipcu-1.1.0/cudaz/src/device.zig +114 -0
- vapoursynth_vszipcu-1.1.0/cudaz/src/driver.zig +596 -0
- vapoursynth_vszipcu-1.1.0/cudaz/src/dynlib.zig +218 -0
- vapoursynth_vszipcu-1.1.0/cudaz/src/error.zig +100 -0
- vapoursynth_vszipcu-1.1.0/cudaz/src/launchconfig.zig +15 -0
- vapoursynth_vszipcu-1.1.0/cudaz/src/lib.zig +73 -0
- vapoursynth_vszipcu-1.1.0/cudaz/src/nvrtc.zig +211 -0
- vapoursynth_vszipcu-1.1.0/cudaz/src/path.zig +8 -0
- vapoursynth_vszipcu-1.1.0/cudaz/src/rng.zig +46 -0
- vapoursynth_vszipcu-1.1.0/cudaz/src/utils.zig +45 -0
- vapoursynth_vszipcu-1.1.0/cudaz/src/wrappers.zig +73 -0
- vapoursynth_vszipcu-1.1.0/cudaz/test/driver.zig +178 -0
- vapoursynth_vszipcu-1.1.0/cudaz/test/general.zig +169 -0
- vapoursynth_vszipcu-1.1.0/cudaz/test/rng.zig +12 -0
- vapoursynth_vszipcu-1.1.0/cudaz/test/utils.zig +34 -0
- vapoursynth_vszipcu-1.1.0/hatch_build.py +199 -0
- vapoursynth_vszipcu-1.1.0/pyproject.toml +50 -0
- vapoursynth_vszipcu-1.1.0/src/bilateral.zig +653 -0
- vapoursynth_vszipcu-1.1.0/src/bm3d.cu +922 -0
- vapoursynth_vszipcu-1.1.0/src/bm3d.zig +1501 -0
- vapoursynth_vszipcu-1.1.0/src/bm3d_vagg.cu +48 -0
- vapoursynth_vszipcu-1.1.0/src/cu.zig +45 -0
- vapoursynth_vszipcu-1.1.0/src/deband.cu +199 -0
- vapoursynth_vszipcu-1.1.0/src/deband.zig +623 -0
- vapoursynth_vszipcu-1.1.0/src/dfttest.cu +2043 -0
- vapoursynth_vszipcu-1.1.0/src/dfttest.zig +1221 -0
- vapoursynth_vszipcu-1.1.0/src/eedi3.cu +716 -0
- vapoursynth_vszipcu-1.1.0/src/eedi3.zig +967 -0
- vapoursynth_vszipcu-1.1.0/src/fgrain.cu +169 -0
- vapoursynth_vszipcu-1.1.0/src/fgrain.zig +385 -0
- vapoursynth_vszipcu-1.1.0/src/framecache.zig +115 -0
- vapoursynth_vszipcu-1.1.0/src/gaussblur.cu +167 -0
- vapoursynth_vszipcu-1.1.0/src/gaussblur.zig +567 -0
- vapoursynth_vszipcu-1.1.0/src/kernel.cu +107 -0
- vapoursynth_vszipcu-1.1.0/src/nlmeans.cu +235 -0
- vapoursynth_vszipcu-1.1.0/src/nlmeans.zig +893 -0
- vapoursynth_vszipcu-1.1.0/src/nnedi3.cu +577 -0
- vapoursynth_vszipcu-1.1.0/src/nnedi3.zig +904 -0
- vapoursynth_vszipcu-1.1.0/src/nnedi3_weights.bin +0 -0
- vapoursynth_vszipcu-1.1.0/src/plugin.zig +128 -0
- vapoursynth_vszipcu-1.1.0/src/pool.zig +75 -0
- vapoursynth_vszipcu-1.1.0/src/vsutil.zig +60 -0
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
zig-out/
|
|
2
|
+
.zig-cache/
|
|
3
|
+
.vscode/
|
|
4
|
+
run.bat
|
|
5
|
+
build-help/releases.json
|
|
6
|
+
build-help/zig-*
|
|
7
|
+
build-help/*.zip
|
|
8
|
+
build-help/*.tar.xz
|
|
9
|
+
.vsjet/
|
|
10
|
+
prof/
|
|
11
|
+
*.h
|
|
12
|
+
*.cpp
|
|
13
|
+
*.so
|
|
14
|
+
build/
|
|
15
|
+
zig-pkg/
|
|
16
|
+
dev/
|
|
17
|
+
|
|
18
|
+
# Python packaging (hatchling + ziglang wheel build)
|
|
19
|
+
dist/
|
|
20
|
+
vapoursynth/
|
|
21
|
+
__pycache__/
|
|
22
|
+
*.egg-info/
|
|
23
|
+
*.whl
|
|
24
|
+
*.tar.gz
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Julek
|
|
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,241 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: vapoursynth-vszipcu
|
|
3
|
+
Version: 1.1.0
|
|
4
|
+
Summary: VapourSynth Zig Image Process — CUDA
|
|
5
|
+
License-Expression: MIT
|
|
6
|
+
License-File: LICENSE
|
|
7
|
+
Requires-Python: >=3.12
|
|
8
|
+
Requires-Dist: vapoursynth>=75
|
|
9
|
+
Description-Content-Type: text/markdown
|
|
10
|
+
|
|
11
|
+
# vszipcu
|
|
12
|
+
|
|
13
|
+
VapourSynth CUDA image processing, written in Zig.
|
|
14
|
+
|
|
15
|
+
## Requirements
|
|
16
|
+
|
|
17
|
+
- An NVIDIA GPU + driver. (Only NNEDI3 cares about the architecture: its tensor-core
|
|
18
|
+
predictor needs sm_80+, and it falls back to an fp32 path automatically below that.)
|
|
19
|
+
- **No CUDA toolkit at runtime.** Released wheels ship NVRTC (`nvrtc64_130_0` /
|
|
20
|
+
`libnvrtc.so.13` + builtins) next to the plugin. The host only needs the NVIDIA driver
|
|
21
|
+
(`nvcuda` / `libcuda`).
|
|
22
|
+
|
|
23
|
+
## Install
|
|
24
|
+
|
|
25
|
+
```sh
|
|
26
|
+
pip install vapoursynth-vszipcu # released wheels (Windows / Linux x86_64)
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
The wheel drops the plugin (and NVRTC) where VapourSynth autoloads it.
|
|
30
|
+
|
|
31
|
+
## Build
|
|
32
|
+
|
|
33
|
+
Directly from a clone, via Python (no local Zig needed — pip fetches the `ziglang` wheel;
|
|
34
|
+
the CUDA **toolkit** must be installed and findable via `CUDA_PATH` so the build can
|
|
35
|
+
compile against headers and **copy NVRTC into the install**):
|
|
36
|
+
|
|
37
|
+
```sh
|
|
38
|
+
git clone https://github.com/dnjulek/vapoursynth-zipcu
|
|
39
|
+
cd vapoursynth-zipcu
|
|
40
|
+
pip install . # build + install into site-packages
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
The zig path leaves the DLL in `zig-out` — for a manual install, also copy
|
|
44
|
+
`nvrtc64_130_0.dll` + `nvrtc-builtins64_130.dll` (Windows) or `libnvrtc.so*` +
|
|
45
|
+
`libnvrtc-builtins.so*` (Linux) from the toolkit into the same plugin directory.
|
|
46
|
+
|
|
47
|
+
## Common arguments
|
|
48
|
+
|
|
49
|
+
`device_id=0` selects the GPU. `num_streams` sets how many frames the filter processes
|
|
50
|
+
concurrently — more streams means more VRAM, raising it past 4 rarely helps.
|
|
51
|
+
|
|
52
|
+
## Filters
|
|
53
|
+
|
|
54
|
+
```python
|
|
55
|
+
core.vszipcu.Bilateral(clip clip[,
|
|
56
|
+
float[] sigma_spatial=3.0, # per-plane; chroma default = sigma[0]/sqrt((1<<ssw)*(1<<ssh))
|
|
57
|
+
float[] sigma_color=0.02, # per-plane; operates on the [0,1] domain at every depth
|
|
58
|
+
int[] radius, # per-plane; default max(1, round(sigma_spatial*3))
|
|
59
|
+
int device_id=0, int num_streams=4,
|
|
60
|
+
int use_shared_memory=1, # shared-memory tile kernel while the tile fits 48 KiB
|
|
61
|
+
int block_x=32, int block_y=8,
|
|
62
|
+
clip ref]) # joint/cross bilateral: weights from ref, values from clip
|
|
63
|
+
```
|
|
64
|
+
8/16-bit integer, 16-bit half, 32-bit float.
|
|
65
|
+
|
|
66
|
+
```python
|
|
67
|
+
core.vszipcu.BM3Dv2(clip clip[, # and BM3D, same signature
|
|
68
|
+
clip ref, # empirical Wiener (2nd pass); also drives block matching
|
|
69
|
+
float[] sigma=3.0, # per-plane; < FLT_EPSILON => plane not processed
|
|
70
|
+
int[] block_step=8, # per-plane, 1..8
|
|
71
|
+
int[] bm_range=9, # per-plane, > 0
|
|
72
|
+
int radius=0, # temporal radius; 0 = spatial only
|
|
73
|
+
int[] ps_num=2, int[] ps_range=4,
|
|
74
|
+
bint chroma=False, # CBM3D; YUV444PS only, block matching on Y
|
|
75
|
+
int device_id=0, int num_streams=4,
|
|
76
|
+
int extractor_exp=0, # >= 3 => deterministic (order-independent) sums
|
|
77
|
+
data[] bm_error_s="ssd", # ssd | sad | zssd | zsad | ssd/norm
|
|
78
|
+
data[] transform_2d_s="dct", # dct | haar | wht | bior1.5
|
|
79
|
+
data[] transform_1d_s="dct",
|
|
80
|
+
bint zero_init=True, # BM3D, radius>0 only: zero the unprocessed planes of the
|
|
81
|
+
# stacked output instead of leaving them uninitialized
|
|
82
|
+
bint fast_fused=False]) # see below
|
|
83
|
+
core.vszipcu.VAggregate(clip clip, clip src, int[] planes)
|
|
84
|
+
```
|
|
85
|
+
32-bit float only. `BM3Dv2` is the one-step interface; `BM3D` alone emits the stacked accumulator
|
|
86
|
+
clip if you want to drive `VAggregate` yourself. Per-plane arrays follow upstream's rule: element
|
|
87
|
+
*i*, when absent, falls back to element *i-1*.
|
|
88
|
+
|
|
89
|
+
**`fast_fused`** (BM3Dv2, `radius > 0`): runs the collaborative filter and the temporal
|
|
90
|
+
aggregation as one kernel chain, keeping the accumulator stack on the GPU. Byte-identical output,
|
|
91
|
+
**+63 % to +169 %** faster — but it costs **1.7x to 7.9x** the VRAM (the accumulator cache grows
|
|
92
|
+
quadratically with `radius` and is independent of `num_streams`). Hence opt-in. If the cache does
|
|
93
|
+
not fit it raises an error telling you what it needed; it never silently falls back.
|
|
94
|
+
|
|
95
|
+
Note `extractor_exp=0` (the default, matching upstream) makes the result *nondeterministic* —
|
|
96
|
+
float `atomicAdd` order is arbitrary. Use `extractor_exp >= 3` if you need reproducibility.
|
|
97
|
+
|
|
98
|
+
```python
|
|
99
|
+
core.vszipcu.Deband(clip clip[,
|
|
100
|
+
int[] iterations=1, # debanding passes, 0..32; 0 = grain only
|
|
101
|
+
float[] threshold=3.0, # cut-off threshold. Higher debands harder, but
|
|
102
|
+
# progressively destroys image detail
|
|
103
|
+
float[] radius=16.0, # initial sampling radius (grows each iteration).
|
|
104
|
+
# Higher finds more gradients; lower smooths more aggressively
|
|
105
|
+
float[] grain=4.0, # noise added on top, to cover residual
|
|
106
|
+
# quantization; 0 = none
|
|
107
|
+
int[] planes, # which planes to process at all; default: all
|
|
108
|
+
bint dither=True, # 8-bit output only, first processed plane only
|
|
109
|
+
int dither_algo=0, # 0=blue noise | 1=bayer | 2=ordered fixed | 3=white noise
|
|
110
|
+
int device_id=0, int num_streams=1])
|
|
111
|
+
```
|
|
112
|
+
8/16-bit integer, 16-bit half, 32-bit float.
|
|
113
|
+
|
|
114
|
+
The four strength parameters are **per-plane arrays**.
|
|
115
|
+
`int[] planes` says *which* planes are touched, the arrays say *how*.
|
|
116
|
+
|
|
117
|
+
```python
|
|
118
|
+
core.vszipcu.DFTTest(clip clip[,
|
|
119
|
+
int ftype=0, # 0 = Wiener: mult by max((psd-sigma)/psd, 0) ** f0beta
|
|
120
|
+
# 1 = hard threshold: zero the bin when psd < sigma
|
|
121
|
+
# 2 = mult every bin by sigma
|
|
122
|
+
# 3 = mult by sigma inside [pmin,pmax], else by sigma2
|
|
123
|
+
# 4 = mult by sigma*sqrt(psd*pmax / ((psd+pmin)*(psd+pmax)))
|
|
124
|
+
float sigma=8.0, # main strength (scaled by the window; see slocation/ssx/..)
|
|
125
|
+
float sigma2=8.0, # ftype=3 only: strength outside [pmin,pmax]
|
|
126
|
+
float pmin=0.0, # ftype=3/4 only: PSD bounds
|
|
127
|
+
float pmax=500.0,
|
|
128
|
+
int sbsize=16, # spatial block size; must be 16 in this backend
|
|
129
|
+
int sosize=12, # spatial overlap, 0..15; >50% needs (sbsize-sosize) | sbsize
|
|
130
|
+
int tbsize=3, # temporal block size; ODD, 1..7 (1 = spatial only)
|
|
131
|
+
int swin=0, # spatial window: 0=hanning 1=hamming 2=blackman
|
|
132
|
+
int twin=7, # temporal window: 3=4-term b-harris 4=kaiser-bessel
|
|
133
|
+
# 5=7-term b-harris 6=flat top 7=rectangular 8=bartlett
|
|
134
|
+
# 9=bartlett-hann 10=nuttall 11=blackman-nuttall
|
|
135
|
+
float sbeta=2.5, # kaiser-bessel beta (swin=4 / twin=4)
|
|
136
|
+
float tbeta=2.5,
|
|
137
|
+
bint zmean=True, # subtract the windowed mean before filtering
|
|
138
|
+
float f0beta=1.0, # ftype=0 exponent (1.0 = plain Wiener, 0.5 = sqrt)
|
|
139
|
+
float[] slocation, # frequency-dependent sigma: [freq, sigma, freq, sigma, ...]
|
|
140
|
+
float[] ssx, float[] ssy, float[] sst, # per-axis variants of slocation
|
|
141
|
+
int ssystem=0,
|
|
142
|
+
int[] planes, # default: all
|
|
143
|
+
int device_id=0, int num_streams=1])
|
|
144
|
+
```
|
|
145
|
+
8-16-bit integer and 32-bit float.
|
|
146
|
+
|
|
147
|
+
```python
|
|
148
|
+
core.vszipcu.EEDI3(clip clip, int field[, # and EEDI3H, same args, horizontal
|
|
149
|
+
bint dh=False, # double the height (keep every source line)
|
|
150
|
+
int mdis=20, # max connection radius, 1..40; larger connects shallower
|
|
151
|
+
# lines, but costs speed and risks artifacts
|
|
152
|
+
int nrad=2, # radius used for neighborhood similarity, 0..3
|
|
153
|
+
float alpha=0.2, # 0..1 (alpha+beta <= 1): weight given to connecting similar
|
|
154
|
+
# neighborhoods. Larger = more lines/edges connected
|
|
155
|
+
float beta=0.25, # 0..1: weight given to the vertical difference created by
|
|
156
|
+
# the interpolation. Larger = fewer edges connected
|
|
157
|
+
# (1.0 = no edge directedness at all)
|
|
158
|
+
float gamma=20.0, # >= 0: penalizes changes in interpolation direction.
|
|
159
|
+
# Larger = smoother interpolation field
|
|
160
|
+
bint hp=False, # search half-pixel directions too
|
|
161
|
+
int vcheck=2, # 0..3: strength of the vertical-consistency check
|
|
162
|
+
float vthresh0=32.0, # vcheck thresholds; all must be > 0 when vcheck > 0
|
|
163
|
+
float vthresh1=64.0,
|
|
164
|
+
float vthresh2=4.0,
|
|
165
|
+
clip sclip, # source for the vcheck comparison
|
|
166
|
+
int device_id=0, int num_streams=1])
|
|
167
|
+
```
|
|
168
|
+
8/16-bit integer, 16-bit half, 32-bit float. `field`: 0/1 pick the field to keep, 2/3 are the
|
|
169
|
+
double-rate variants (not allowed with `dh=True`).
|
|
170
|
+
|
|
171
|
+
```python
|
|
172
|
+
core.vszipcu.FGrain(clip clip[,
|
|
173
|
+
int num_iterations=800, # Monte-Carlo samples per pixel; quality vs speed
|
|
174
|
+
float grain_radius_mean=0.1, # mean grain radius, in pixels
|
|
175
|
+
float grain_radius_std=0.0, # grain-radius spread; 0 = every grain the same size
|
|
176
|
+
float sigma=0.8, # std dev of the Gaussian in the grain model
|
|
177
|
+
int seed=0,
|
|
178
|
+
int device_id=0, int num_streams=1])
|
|
179
|
+
```
|
|
180
|
+
32-bit float. Physically-based film grain synthesis. For temporally-varying grain, set the
|
|
181
|
+
integer frame property `FGRAIN_SEED_OFFSET` on the source frame — it is added to `seed`.
|
|
182
|
+
|
|
183
|
+
```python
|
|
184
|
+
core.vszipcu.GaussBlur(clip clip[,
|
|
185
|
+
float[] sigma=0.5, # per-plane; chroma default = sigma[0]/sqrt((1<<ssw)*(1<<ssh))
|
|
186
|
+
int device_id=0, int num_streams=1])
|
|
187
|
+
```
|
|
188
|
+
8/16-bit integer, 16-bit half, 32-bit float. A plane whose sigma is below `FLT_EPSILON` is copied
|
|
189
|
+
through untouched.
|
|
190
|
+
|
|
191
|
+
```python
|
|
192
|
+
core.vszipcu.NLMeans(clip clip[,
|
|
193
|
+
int d=1, # temporal radius; 0 = spatial only
|
|
194
|
+
int a=2, # search-window radius, 1..64
|
|
195
|
+
int s=4, # patch radius, 0..8
|
|
196
|
+
float h=1.2, # > 0: filtering strength
|
|
197
|
+
string channels="auto", # auto | Y | UV | YUV | RGB
|
|
198
|
+
int wmode=0, # weighting function of the patch distance x, 0..3:
|
|
199
|
+
# 0 = exp(-x) | 1 = max(1-x, 0)
|
|
200
|
+
# 2 = max(1-x, 0)**2 | 3 = max(1-x, 0)**8
|
|
201
|
+
float wref=1.0, # >= 0: weight of the pixel itself
|
|
202
|
+
clip rclip, # weights computed from rclip, values from clip
|
|
203
|
+
int device_id=0, int num_streams=1])
|
|
204
|
+
```
|
|
205
|
+
8/16-bit integer, 16-bit half, 32-bit float. `channels="YUV"` requires 4:4:4 — a joint patch
|
|
206
|
+
distance needs one shared pixel lattice — so on subsampled clips run a `"Y"` pass and a `"UV"`
|
|
207
|
+
pass instead.
|
|
208
|
+
|
|
209
|
+
```python
|
|
210
|
+
core.vszipcu.NNEDI3(clip clip, int field[,
|
|
211
|
+
bint dh=False, # double the height (keep every source line)
|
|
212
|
+
int[] planes, # default: all
|
|
213
|
+
int nsize=6, # predictor neighborhood, 0..6: 0=8x6 1=16x6 2=32x6
|
|
214
|
+
# 3=48x6 4=8x4 5=16x4 6=32x4
|
|
215
|
+
int nns=1, # predictor neurons, 0..4: 0=16 1=32 2=64 3=128 4=256
|
|
216
|
+
int qual=1, # 1 or 2: number of predictor passes averaged
|
|
217
|
+
int etype=0, # 0 = weights trained on absolute error, 1 = squared error
|
|
218
|
+
int pscrn=2, # prescreener, 0..4: 0=off (predict every pixel)
|
|
219
|
+
# 1=original, 2..4=new levels 0..2 (higher = fewer pixels
|
|
220
|
+
# left to cubic interpolation: slower, slightly better)
|
|
221
|
+
int device_id=0, int num_streams])
|
|
222
|
+
```
|
|
223
|
+
8/16-bit integer, 16-bit half, 32-bit float. Weights are embedded in the plugin — no external
|
|
224
|
+
`nnedi3_weights.bin` file needed. On sm_80+ the predictor runs on tensor cores (fp16 inputs,
|
|
225
|
+
fp32 accumulation); older cards fall back to an fp32 path automatically.
|
|
226
|
+
|
|
227
|
+
## Credits
|
|
228
|
+
|
|
229
|
+
| filter | ported from |
|
|
230
|
+
|---|---|
|
|
231
|
+
| Bilateral | [VapourSynth-BilateralGPU](https://github.com/WolframRhodium/VapourSynth-BilateralGPU) (rtc variant), by WolframRhodium |
|
|
232
|
+
| BM3D / BM3Dv2 / VAggregate | [VapourSynth-BM3DCUDA](https://github.com/WolframRhodium/VapourSynth-BM3DCUDA), by WolframRhodium |
|
|
233
|
+
| DFTTest | [vs-dfttest2](https://github.com/AmusementClub/vs-dfttest2) (NVRTC backend), by AmusementClub |
|
|
234
|
+
| FGrain | [vs-fgrain-cuda](https://github.com/AmusementClub/vs-fgrain-cuda), by AmusementClub |
|
|
235
|
+
| NNEDI3 | [VapourSynth-nnedi3vk](https://github.com/HolyWu/VapourSynth-nnedi3vk), by HolyWu |
|
|
236
|
+
| Deband | [vs-placebo](https://github.com/Lypheo/vs-placebo), by Lypheo (libplacebo's `pl_shader_deband` + `pl_shader_dither`) |
|
|
237
|
+
| NLMeans | [KNLMeansCL](https://github.com/Khanattila/KNLMeansCL), by Khanattila |
|
|
238
|
+
| EEDI3 / EEDI3H | [VapourSynth-EEDI3](https://github.com/HolyWu/VapourSynth-EEDI3) (by HolyWu) and [vapoursynth-zip](https://github.com/dnjulek/vapoursynth-zip) (by dnjulek) — the CPU references |
|
|
239
|
+
|
|
240
|
+
Built on [cudaz](https://github.com/akhildevelops/cudaz) (CUDA driver API + NVRTC bindings,
|
|
241
|
+
vendored in `cudaz/`).
|
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
# vszipcu
|
|
2
|
+
|
|
3
|
+
VapourSynth CUDA image processing, written in Zig.
|
|
4
|
+
|
|
5
|
+
## Requirements
|
|
6
|
+
|
|
7
|
+
- An NVIDIA GPU + driver. (Only NNEDI3 cares about the architecture: its tensor-core
|
|
8
|
+
predictor needs sm_80+, and it falls back to an fp32 path automatically below that.)
|
|
9
|
+
- **No CUDA toolkit at runtime.** Released wheels ship NVRTC (`nvrtc64_130_0` /
|
|
10
|
+
`libnvrtc.so.13` + builtins) next to the plugin. The host only needs the NVIDIA driver
|
|
11
|
+
(`nvcuda` / `libcuda`).
|
|
12
|
+
|
|
13
|
+
## Install
|
|
14
|
+
|
|
15
|
+
```sh
|
|
16
|
+
pip install vapoursynth-vszipcu # released wheels (Windows / Linux x86_64)
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
The wheel drops the plugin (and NVRTC) where VapourSynth autoloads it.
|
|
20
|
+
|
|
21
|
+
## Build
|
|
22
|
+
|
|
23
|
+
Directly from a clone, via Python (no local Zig needed — pip fetches the `ziglang` wheel;
|
|
24
|
+
the CUDA **toolkit** must be installed and findable via `CUDA_PATH` so the build can
|
|
25
|
+
compile against headers and **copy NVRTC into the install**):
|
|
26
|
+
|
|
27
|
+
```sh
|
|
28
|
+
git clone https://github.com/dnjulek/vapoursynth-zipcu
|
|
29
|
+
cd vapoursynth-zipcu
|
|
30
|
+
pip install . # build + install into site-packages
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
The zig path leaves the DLL in `zig-out` — for a manual install, also copy
|
|
34
|
+
`nvrtc64_130_0.dll` + `nvrtc-builtins64_130.dll` (Windows) or `libnvrtc.so*` +
|
|
35
|
+
`libnvrtc-builtins.so*` (Linux) from the toolkit into the same plugin directory.
|
|
36
|
+
|
|
37
|
+
## Common arguments
|
|
38
|
+
|
|
39
|
+
`device_id=0` selects the GPU. `num_streams` sets how many frames the filter processes
|
|
40
|
+
concurrently — more streams means more VRAM, raising it past 4 rarely helps.
|
|
41
|
+
|
|
42
|
+
## Filters
|
|
43
|
+
|
|
44
|
+
```python
|
|
45
|
+
core.vszipcu.Bilateral(clip clip[,
|
|
46
|
+
float[] sigma_spatial=3.0, # per-plane; chroma default = sigma[0]/sqrt((1<<ssw)*(1<<ssh))
|
|
47
|
+
float[] sigma_color=0.02, # per-plane; operates on the [0,1] domain at every depth
|
|
48
|
+
int[] radius, # per-plane; default max(1, round(sigma_spatial*3))
|
|
49
|
+
int device_id=0, int num_streams=4,
|
|
50
|
+
int use_shared_memory=1, # shared-memory tile kernel while the tile fits 48 KiB
|
|
51
|
+
int block_x=32, int block_y=8,
|
|
52
|
+
clip ref]) # joint/cross bilateral: weights from ref, values from clip
|
|
53
|
+
```
|
|
54
|
+
8/16-bit integer, 16-bit half, 32-bit float.
|
|
55
|
+
|
|
56
|
+
```python
|
|
57
|
+
core.vszipcu.BM3Dv2(clip clip[, # and BM3D, same signature
|
|
58
|
+
clip ref, # empirical Wiener (2nd pass); also drives block matching
|
|
59
|
+
float[] sigma=3.0, # per-plane; < FLT_EPSILON => plane not processed
|
|
60
|
+
int[] block_step=8, # per-plane, 1..8
|
|
61
|
+
int[] bm_range=9, # per-plane, > 0
|
|
62
|
+
int radius=0, # temporal radius; 0 = spatial only
|
|
63
|
+
int[] ps_num=2, int[] ps_range=4,
|
|
64
|
+
bint chroma=False, # CBM3D; YUV444PS only, block matching on Y
|
|
65
|
+
int device_id=0, int num_streams=4,
|
|
66
|
+
int extractor_exp=0, # >= 3 => deterministic (order-independent) sums
|
|
67
|
+
data[] bm_error_s="ssd", # ssd | sad | zssd | zsad | ssd/norm
|
|
68
|
+
data[] transform_2d_s="dct", # dct | haar | wht | bior1.5
|
|
69
|
+
data[] transform_1d_s="dct",
|
|
70
|
+
bint zero_init=True, # BM3D, radius>0 only: zero the unprocessed planes of the
|
|
71
|
+
# stacked output instead of leaving them uninitialized
|
|
72
|
+
bint fast_fused=False]) # see below
|
|
73
|
+
core.vszipcu.VAggregate(clip clip, clip src, int[] planes)
|
|
74
|
+
```
|
|
75
|
+
32-bit float only. `BM3Dv2` is the one-step interface; `BM3D` alone emits the stacked accumulator
|
|
76
|
+
clip if you want to drive `VAggregate` yourself. Per-plane arrays follow upstream's rule: element
|
|
77
|
+
*i*, when absent, falls back to element *i-1*.
|
|
78
|
+
|
|
79
|
+
**`fast_fused`** (BM3Dv2, `radius > 0`): runs the collaborative filter and the temporal
|
|
80
|
+
aggregation as one kernel chain, keeping the accumulator stack on the GPU. Byte-identical output,
|
|
81
|
+
**+63 % to +169 %** faster — but it costs **1.7x to 7.9x** the VRAM (the accumulator cache grows
|
|
82
|
+
quadratically with `radius` and is independent of `num_streams`). Hence opt-in. If the cache does
|
|
83
|
+
not fit it raises an error telling you what it needed; it never silently falls back.
|
|
84
|
+
|
|
85
|
+
Note `extractor_exp=0` (the default, matching upstream) makes the result *nondeterministic* —
|
|
86
|
+
float `atomicAdd` order is arbitrary. Use `extractor_exp >= 3` if you need reproducibility.
|
|
87
|
+
|
|
88
|
+
```python
|
|
89
|
+
core.vszipcu.Deband(clip clip[,
|
|
90
|
+
int[] iterations=1, # debanding passes, 0..32; 0 = grain only
|
|
91
|
+
float[] threshold=3.0, # cut-off threshold. Higher debands harder, but
|
|
92
|
+
# progressively destroys image detail
|
|
93
|
+
float[] radius=16.0, # initial sampling radius (grows each iteration).
|
|
94
|
+
# Higher finds more gradients; lower smooths more aggressively
|
|
95
|
+
float[] grain=4.0, # noise added on top, to cover residual
|
|
96
|
+
# quantization; 0 = none
|
|
97
|
+
int[] planes, # which planes to process at all; default: all
|
|
98
|
+
bint dither=True, # 8-bit output only, first processed plane only
|
|
99
|
+
int dither_algo=0, # 0=blue noise | 1=bayer | 2=ordered fixed | 3=white noise
|
|
100
|
+
int device_id=0, int num_streams=1])
|
|
101
|
+
```
|
|
102
|
+
8/16-bit integer, 16-bit half, 32-bit float.
|
|
103
|
+
|
|
104
|
+
The four strength parameters are **per-plane arrays**.
|
|
105
|
+
`int[] planes` says *which* planes are touched, the arrays say *how*.
|
|
106
|
+
|
|
107
|
+
```python
|
|
108
|
+
core.vszipcu.DFTTest(clip clip[,
|
|
109
|
+
int ftype=0, # 0 = Wiener: mult by max((psd-sigma)/psd, 0) ** f0beta
|
|
110
|
+
# 1 = hard threshold: zero the bin when psd < sigma
|
|
111
|
+
# 2 = mult every bin by sigma
|
|
112
|
+
# 3 = mult by sigma inside [pmin,pmax], else by sigma2
|
|
113
|
+
# 4 = mult by sigma*sqrt(psd*pmax / ((psd+pmin)*(psd+pmax)))
|
|
114
|
+
float sigma=8.0, # main strength (scaled by the window; see slocation/ssx/..)
|
|
115
|
+
float sigma2=8.0, # ftype=3 only: strength outside [pmin,pmax]
|
|
116
|
+
float pmin=0.0, # ftype=3/4 only: PSD bounds
|
|
117
|
+
float pmax=500.0,
|
|
118
|
+
int sbsize=16, # spatial block size; must be 16 in this backend
|
|
119
|
+
int sosize=12, # spatial overlap, 0..15; >50% needs (sbsize-sosize) | sbsize
|
|
120
|
+
int tbsize=3, # temporal block size; ODD, 1..7 (1 = spatial only)
|
|
121
|
+
int swin=0, # spatial window: 0=hanning 1=hamming 2=blackman
|
|
122
|
+
int twin=7, # temporal window: 3=4-term b-harris 4=kaiser-bessel
|
|
123
|
+
# 5=7-term b-harris 6=flat top 7=rectangular 8=bartlett
|
|
124
|
+
# 9=bartlett-hann 10=nuttall 11=blackman-nuttall
|
|
125
|
+
float sbeta=2.5, # kaiser-bessel beta (swin=4 / twin=4)
|
|
126
|
+
float tbeta=2.5,
|
|
127
|
+
bint zmean=True, # subtract the windowed mean before filtering
|
|
128
|
+
float f0beta=1.0, # ftype=0 exponent (1.0 = plain Wiener, 0.5 = sqrt)
|
|
129
|
+
float[] slocation, # frequency-dependent sigma: [freq, sigma, freq, sigma, ...]
|
|
130
|
+
float[] ssx, float[] ssy, float[] sst, # per-axis variants of slocation
|
|
131
|
+
int ssystem=0,
|
|
132
|
+
int[] planes, # default: all
|
|
133
|
+
int device_id=0, int num_streams=1])
|
|
134
|
+
```
|
|
135
|
+
8-16-bit integer and 32-bit float.
|
|
136
|
+
|
|
137
|
+
```python
|
|
138
|
+
core.vszipcu.EEDI3(clip clip, int field[, # and EEDI3H, same args, horizontal
|
|
139
|
+
bint dh=False, # double the height (keep every source line)
|
|
140
|
+
int mdis=20, # max connection radius, 1..40; larger connects shallower
|
|
141
|
+
# lines, but costs speed and risks artifacts
|
|
142
|
+
int nrad=2, # radius used for neighborhood similarity, 0..3
|
|
143
|
+
float alpha=0.2, # 0..1 (alpha+beta <= 1): weight given to connecting similar
|
|
144
|
+
# neighborhoods. Larger = more lines/edges connected
|
|
145
|
+
float beta=0.25, # 0..1: weight given to the vertical difference created by
|
|
146
|
+
# the interpolation. Larger = fewer edges connected
|
|
147
|
+
# (1.0 = no edge directedness at all)
|
|
148
|
+
float gamma=20.0, # >= 0: penalizes changes in interpolation direction.
|
|
149
|
+
# Larger = smoother interpolation field
|
|
150
|
+
bint hp=False, # search half-pixel directions too
|
|
151
|
+
int vcheck=2, # 0..3: strength of the vertical-consistency check
|
|
152
|
+
float vthresh0=32.0, # vcheck thresholds; all must be > 0 when vcheck > 0
|
|
153
|
+
float vthresh1=64.0,
|
|
154
|
+
float vthresh2=4.0,
|
|
155
|
+
clip sclip, # source for the vcheck comparison
|
|
156
|
+
int device_id=0, int num_streams=1])
|
|
157
|
+
```
|
|
158
|
+
8/16-bit integer, 16-bit half, 32-bit float. `field`: 0/1 pick the field to keep, 2/3 are the
|
|
159
|
+
double-rate variants (not allowed with `dh=True`).
|
|
160
|
+
|
|
161
|
+
```python
|
|
162
|
+
core.vszipcu.FGrain(clip clip[,
|
|
163
|
+
int num_iterations=800, # Monte-Carlo samples per pixel; quality vs speed
|
|
164
|
+
float grain_radius_mean=0.1, # mean grain radius, in pixels
|
|
165
|
+
float grain_radius_std=0.0, # grain-radius spread; 0 = every grain the same size
|
|
166
|
+
float sigma=0.8, # std dev of the Gaussian in the grain model
|
|
167
|
+
int seed=0,
|
|
168
|
+
int device_id=0, int num_streams=1])
|
|
169
|
+
```
|
|
170
|
+
32-bit float. Physically-based film grain synthesis. For temporally-varying grain, set the
|
|
171
|
+
integer frame property `FGRAIN_SEED_OFFSET` on the source frame — it is added to `seed`.
|
|
172
|
+
|
|
173
|
+
```python
|
|
174
|
+
core.vszipcu.GaussBlur(clip clip[,
|
|
175
|
+
float[] sigma=0.5, # per-plane; chroma default = sigma[0]/sqrt((1<<ssw)*(1<<ssh))
|
|
176
|
+
int device_id=0, int num_streams=1])
|
|
177
|
+
```
|
|
178
|
+
8/16-bit integer, 16-bit half, 32-bit float. A plane whose sigma is below `FLT_EPSILON` is copied
|
|
179
|
+
through untouched.
|
|
180
|
+
|
|
181
|
+
```python
|
|
182
|
+
core.vszipcu.NLMeans(clip clip[,
|
|
183
|
+
int d=1, # temporal radius; 0 = spatial only
|
|
184
|
+
int a=2, # search-window radius, 1..64
|
|
185
|
+
int s=4, # patch radius, 0..8
|
|
186
|
+
float h=1.2, # > 0: filtering strength
|
|
187
|
+
string channels="auto", # auto | Y | UV | YUV | RGB
|
|
188
|
+
int wmode=0, # weighting function of the patch distance x, 0..3:
|
|
189
|
+
# 0 = exp(-x) | 1 = max(1-x, 0)
|
|
190
|
+
# 2 = max(1-x, 0)**2 | 3 = max(1-x, 0)**8
|
|
191
|
+
float wref=1.0, # >= 0: weight of the pixel itself
|
|
192
|
+
clip rclip, # weights computed from rclip, values from clip
|
|
193
|
+
int device_id=0, int num_streams=1])
|
|
194
|
+
```
|
|
195
|
+
8/16-bit integer, 16-bit half, 32-bit float. `channels="YUV"` requires 4:4:4 — a joint patch
|
|
196
|
+
distance needs one shared pixel lattice — so on subsampled clips run a `"Y"` pass and a `"UV"`
|
|
197
|
+
pass instead.
|
|
198
|
+
|
|
199
|
+
```python
|
|
200
|
+
core.vszipcu.NNEDI3(clip clip, int field[,
|
|
201
|
+
bint dh=False, # double the height (keep every source line)
|
|
202
|
+
int[] planes, # default: all
|
|
203
|
+
int nsize=6, # predictor neighborhood, 0..6: 0=8x6 1=16x6 2=32x6
|
|
204
|
+
# 3=48x6 4=8x4 5=16x4 6=32x4
|
|
205
|
+
int nns=1, # predictor neurons, 0..4: 0=16 1=32 2=64 3=128 4=256
|
|
206
|
+
int qual=1, # 1 or 2: number of predictor passes averaged
|
|
207
|
+
int etype=0, # 0 = weights trained on absolute error, 1 = squared error
|
|
208
|
+
int pscrn=2, # prescreener, 0..4: 0=off (predict every pixel)
|
|
209
|
+
# 1=original, 2..4=new levels 0..2 (higher = fewer pixels
|
|
210
|
+
# left to cubic interpolation: slower, slightly better)
|
|
211
|
+
int device_id=0, int num_streams])
|
|
212
|
+
```
|
|
213
|
+
8/16-bit integer, 16-bit half, 32-bit float. Weights are embedded in the plugin — no external
|
|
214
|
+
`nnedi3_weights.bin` file needed. On sm_80+ the predictor runs on tensor cores (fp16 inputs,
|
|
215
|
+
fp32 accumulation); older cards fall back to an fp32 path automatically.
|
|
216
|
+
|
|
217
|
+
## Credits
|
|
218
|
+
|
|
219
|
+
| filter | ported from |
|
|
220
|
+
|---|---|
|
|
221
|
+
| Bilateral | [VapourSynth-BilateralGPU](https://github.com/WolframRhodium/VapourSynth-BilateralGPU) (rtc variant), by WolframRhodium |
|
|
222
|
+
| BM3D / BM3Dv2 / VAggregate | [VapourSynth-BM3DCUDA](https://github.com/WolframRhodium/VapourSynth-BM3DCUDA), by WolframRhodium |
|
|
223
|
+
| DFTTest | [vs-dfttest2](https://github.com/AmusementClub/vs-dfttest2) (NVRTC backend), by AmusementClub |
|
|
224
|
+
| FGrain | [vs-fgrain-cuda](https://github.com/AmusementClub/vs-fgrain-cuda), by AmusementClub |
|
|
225
|
+
| NNEDI3 | [VapourSynth-nnedi3vk](https://github.com/HolyWu/VapourSynth-nnedi3vk), by HolyWu |
|
|
226
|
+
| Deband | [vs-placebo](https://github.com/Lypheo/vs-placebo), by Lypheo (libplacebo's `pl_shader_deband` + `pl_shader_dither`) |
|
|
227
|
+
| NLMeans | [KNLMeansCL](https://github.com/Khanattila/KNLMeansCL), by Khanattila |
|
|
228
|
+
| EEDI3 / EEDI3H | [VapourSynth-EEDI3](https://github.com/HolyWu/VapourSynth-EEDI3) (by HolyWu) and [vapoursynth-zip](https://github.com/dnjulek/vapoursynth-zip) (by dnjulek) — the CPU references |
|
|
229
|
+
|
|
230
|
+
Built on [cudaz](https://github.com/akhildevelops/cudaz) (CUDA driver API + NVRTC bindings,
|
|
231
|
+
vendored in `cudaz/`).
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
const std = @import("std");
|
|
2
|
+
const builtin = @import("builtin");
|
|
3
|
+
|
|
4
|
+
pub fn build(b: *std.Build) !void {
|
|
5
|
+
// NVIDIA ships MSVC import libs; gnu ABI cannot link them cleanly on Windows.
|
|
6
|
+
const target = b.standardTargetOptions(.{
|
|
7
|
+
.default_target = if (builtin.os.tag == .windows) .{
|
|
8
|
+
.cpu_arch = .x86_64,
|
|
9
|
+
.os_tag = .windows,
|
|
10
|
+
.abi = .msvc,
|
|
11
|
+
} else .{},
|
|
12
|
+
});
|
|
13
|
+
const optimize = b.standardOptimizeOption(.{});
|
|
14
|
+
|
|
15
|
+
const mod = b.createModule(.{
|
|
16
|
+
.root_source_file = b.path("src/plugin.zig"),
|
|
17
|
+
.target = target,
|
|
18
|
+
.optimize = optimize,
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
const lib = b.addLibrary(.{
|
|
22
|
+
.name = "vszipcu",
|
|
23
|
+
.linkage = .dynamic,
|
|
24
|
+
.root_module = mod,
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
const cudaz_dep = b.dependency("cudaz", .{});
|
|
28
|
+
mod.addImport("cudaz", cudaz_dep.module("cudaz"));
|
|
29
|
+
|
|
30
|
+
const vapoursynth_dep = b.dependency("vapoursynth", .{
|
|
31
|
+
.target = target,
|
|
32
|
+
.optimize = optimize,
|
|
33
|
+
});
|
|
34
|
+
mod.addImport("vapoursynth", vapoursynth_dep.module("vapoursynth"));
|
|
35
|
+
|
|
36
|
+
// cuda.lib is a stub that LoadLibrary()s nvcuda.dll. NVRTC is loaded at runtime
|
|
37
|
+
// (VS plugins do not see PATH, so an import of nvrtc64_*.dll would fail).
|
|
38
|
+
mod.link_libc = true;
|
|
39
|
+
mod.linkSystemLibrary("cuda", .{});
|
|
40
|
+
|
|
41
|
+
if (target.result.os.tag == .linux) {
|
|
42
|
+
mod.linkSystemLibrary("dl", .{}); // dynlib.zig uses dladdr
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
if (optimize == .ReleaseFast) {
|
|
46
|
+
mod.strip = true;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
b.installArtifact(lib);
|
|
50
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
.{
|
|
2
|
+
.name = .vszipcu,
|
|
3
|
+
.version = "1.1.0",
|
|
4
|
+
.fingerprint = 0x5060d0605c460ab4,
|
|
5
|
+
.minimum_zig_version = "0.16.0",
|
|
6
|
+
.dependencies = .{
|
|
7
|
+
.cudaz = .{
|
|
8
|
+
.path = "./cudaz/",
|
|
9
|
+
},
|
|
10
|
+
.vapoursynth = .{
|
|
11
|
+
// zig fetch --save git+https://github.com/dnjulek/vapoursynth-zig.git
|
|
12
|
+
.url = "git+https://github.com/dnjulek/vapoursynth-zig.git#b87ff61ce680fa5a4cf7d44a9cb4b605c5037432",
|
|
13
|
+
.hash = "vapoursynth-4.0.0-jLYMQ6DjAgDFOODh8tdhWCw0iGHMPKiR4znTJwSyvV36",
|
|
14
|
+
},
|
|
15
|
+
},
|
|
16
|
+
.paths = .{"."},
|
|
17
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2023 Gurramkonda Reddy Akhil Teja
|
|
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.
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+

|
|
26
|
+
|
|
27
|
+
The above image is licensed under CC BY-SA 4.0
|