dfttest2 10__py3-none-any.whl

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.
dfttest2/_dfttest2.py ADDED
@@ -0,0 +1,851 @@
1
+ __version__ = "0.4.0"
2
+
3
+ from dataclasses import dataclass
4
+ import math
5
+ from string import Template
6
+ import typing
7
+
8
+ import vapoursynth as vs
9
+ from vapoursynth import core
10
+
11
+
12
+ __all__ = ["DFTTest", "DFTTest2", "Backend"]
13
+
14
+
15
+ class Backend:
16
+ @dataclass(frozen=False)
17
+ class cuFFT:
18
+ device_id: int = 0
19
+ in_place: bool = True
20
+
21
+ @dataclass(frozen=False)
22
+ class NVRTC:
23
+ device_id: int = 0
24
+ num_streams: int = 1
25
+
26
+ @dataclass(frozen=False)
27
+ class CPU:
28
+ opt: int = 0
29
+
30
+ @dataclass(frozen=False)
31
+ class GCC:
32
+ pass
33
+
34
+ @dataclass(frozen=False)
35
+ class hipFFT:
36
+ device_id: int = 0
37
+ in_place: bool = True
38
+
39
+ @dataclass(frozen=False)
40
+ class HIPRTC:
41
+ device_id: int = 0
42
+ num_streams: int = 1
43
+
44
+ backendT = typing.Union[Backend.cuFFT, Backend.NVRTC, Backend.CPU, Backend.GCC, Backend.hipFFT, Backend.HIPRTC]
45
+
46
+
47
+ def init_backend(backend: backendT) -> backendT:
48
+ if backend is Backend.cuFFT: # type: ignore
49
+ backend = Backend.cuFFT()
50
+ elif backend is Backend.NVRTC: # type: ignore
51
+ backend = Backend.NVRTC()
52
+ elif backend is Backend.CPU: # type: ignore
53
+ backend = Backend.CPU()
54
+ elif backend is Backend.GCC: # type: ignore
55
+ backend = Backend.GCC()
56
+ elif backend is Backend.hipFFT: # type: ignore
57
+ backend = Backend.hipFFT()
58
+ elif backend is Backend.HIPRTC: # type: ignore
59
+ backend = Backend.HIPRTC()
60
+ return backend
61
+
62
+
63
+ # https://github.com/HomeOfVapourSynthEvolution/VapourSynth-DFTTest/blob/
64
+ # bc5e0186a7f309556f20a8e9502f2238e39179b8/DFTTest/DFTTest.cpp#L518
65
+ def normalize(
66
+ window: typing.Sequence[float],
67
+ size: int,
68
+ step: int
69
+ ) -> typing.List[float]:
70
+
71
+ nw = [0.0] * size
72
+ for q in range(size):
73
+ for h in range(q, -1, -step):
74
+ nw[q] += window[h] ** 2
75
+ for h in range(q + step, size, step):
76
+ nw[q] += window[h] ** 2
77
+ return [window[q] / math.sqrt(nw[q]) for q in range(size)]
78
+
79
+
80
+ # https://github.com/HomeOfVapourSynthEvolution/VapourSynth-DFTTest/blob/
81
+ # bc5e0186a7f309556f20a8e9502f2238e39179b8/DFTTest/DFTTest.cpp#L462
82
+ def get_window_value(location: float, size: int, mode: int, beta: float) -> float:
83
+ temp = math.pi * location / size
84
+ if mode == 0: # hanning
85
+ return 0.5 * (1 - math.cos(2 * temp))
86
+ elif mode == 1: # hamming
87
+ return 0.53836 - 0.46164 * math.cos(2 * temp)
88
+ elif mode == 2: # blackman
89
+ return 0.42 - 0.5 * math.cos(2 * temp) + 0.08 * math.cos(4 * temp)
90
+ elif mode == 3: # 4 term blackman-harris
91
+ return (
92
+ 0.35875
93
+ - 0.48829 * math.cos(2 * temp)
94
+ + 0.14128 * math.cos(4 * temp)
95
+ - 0.01168 * math.cos(6 * temp)
96
+ )
97
+ elif mode == 4: # kaiser-bessel
98
+ def i0(p: float) -> float:
99
+ p /= 2
100
+ n = t = d = 1.0
101
+ k = 1
102
+ while True:
103
+ n *= p
104
+ d *= k
105
+ v = n / d
106
+ t += v * v
107
+ k += 1
108
+ if k >= 15 or v <= 1e-8:
109
+ break
110
+ return t
111
+ v = 2 * location / size - 1
112
+ return i0(math.pi * beta * math.sqrt(1 - v * v)) / i0(math.pi * beta)
113
+ elif mode == 5: # 7 term blackman-harris
114
+ return (
115
+ 0.27105140069342415
116
+ - 0.433297939234486060 * math.cos(2 * temp)
117
+ + 0.218122999543110620 * math.cos(4 * temp)
118
+ - 0.065925446388030898 * math.cos(6 * temp)
119
+ + 0.010811742098372268 * math.cos(8 * temp)
120
+ - 7.7658482522509342e-4 * math.cos(10 * temp)
121
+ + 1.3887217350903198e-5 * math.cos(12 * temp)
122
+ )
123
+ elif mode == 6: # flat top
124
+ return (
125
+ 0.2810639
126
+ - 0.5208972 * math.cos(2 * temp)
127
+ + 0.1980399 * math.cos(4 * temp)
128
+ )
129
+ elif mode == 7: # rectangular
130
+ return 1.0
131
+ elif mode == 8: # Bartlett
132
+ return 1 - 2 * abs(location - size / 2) / size
133
+ elif mode == 9: # bartlett-hann
134
+ return 0.62 - 0.48 * (location / size - 0.5) - 0.38 * math.cos(2 * temp)
135
+ elif mode == 10: # nuttall
136
+ return (
137
+ 0.355768
138
+ - 0.487396 * math.cos(2 * temp)
139
+ + 0.144232 * math.cos(4 * temp)
140
+ - 0.012604 * math.cos(6 * temp)
141
+ )
142
+ elif mode == 11: # blackman-nuttall
143
+ return (
144
+ 0.3635819
145
+ - 0.4891775 * math.cos(2 * temp)
146
+ + 0.1365995 * math.cos(4 * temp)
147
+ - 0.0106411 * math.cos(6 * temp)
148
+ )
149
+ else:
150
+ raise ValueError("unknown window")
151
+
152
+
153
+ # https://github.com/HomeOfVapourSynthEvolution/VapourSynth-DFTTest/blob/
154
+ # bc5e0186a7f309556f20a8e9502f2238e39179b8/DFTTest/DFTTest.cpp#L461
155
+ def get_window(
156
+ radius: int,
157
+ block_size: int,
158
+ block_step: int,
159
+ spatial_window_mode: int,
160
+ spatial_beta: float,
161
+ temporal_window_mode: int,
162
+ temporal_beta: float
163
+ ) -> typing.List[float]:
164
+
165
+ temporal_window = [
166
+ get_window_value(
167
+ location = i + 0.5,
168
+ size = 2 * radius + 1,
169
+ mode = temporal_window_mode,
170
+ beta = temporal_beta
171
+ ) for i in range(2 * radius + 1)
172
+ ]
173
+
174
+ spatial_window = [
175
+ get_window_value(
176
+ location = i + 0.5,
177
+ size = block_size,
178
+ mode = spatial_window_mode,
179
+ beta = spatial_beta
180
+ ) for i in range(block_size)
181
+ ]
182
+
183
+ spatial_window = normalize(
184
+ window=spatial_window,
185
+ size=block_size,
186
+ step=block_step
187
+ )
188
+
189
+ window = []
190
+ for t_val in temporal_window:
191
+ for s_val1 in spatial_window:
192
+ for s_val2 in spatial_window:
193
+ value = t_val * s_val1 * s_val2
194
+
195
+ # normalize for unnormalized FFT implementation
196
+ value /= math.sqrt(2 * radius + 1) * block_size
197
+
198
+ window.append(value)
199
+
200
+ return window
201
+
202
+
203
+ # https://github.com/HomeOfVapourSynthEvolution/VapourSynth-DFTTest/blob/
204
+ # bc5e0186a7f309556f20a8e9502f2238e39179b8/DFTTest/DFTTest.cpp#L581
205
+ def get_location(
206
+ position: float,
207
+ length: int
208
+ ) -> float:
209
+
210
+ if length == 1:
211
+ return 0.0
212
+ elif position > length // 2:
213
+ return (length - position) / (length // 2)
214
+ else:
215
+ return position / (length // 2)
216
+
217
+
218
+ # https://github.com/HomeOfVapourSynthEvolution/VapourSynth-DFTTest/blob/
219
+ # bc5e0186a7f309556f20a8e9502f2238e39179b8/DFTTest/DFTTest.cpp#L581
220
+ def get_sigma(
221
+ position: float,
222
+ length: int,
223
+ func: typing.Callable[[float], float]
224
+ ) -> float:
225
+
226
+ if length == 1:
227
+ return 1.0
228
+ else:
229
+ return func(get_location(position, length))
230
+
231
+
232
+ def DFTTest2(
233
+ clip: vs.VideoNode,
234
+ ftype: typing.Literal[0, 1, 2, 3, 4] = 0,
235
+ sigma: typing.Union[float, typing.Sequence[typing.Callable[[float], float]]] = 8.0,
236
+ sigma2: float = 8.0,
237
+ pmin: float = 0.0,
238
+ pmax: float = 500.0,
239
+ sbsize: int = 16,
240
+ sosize: int = 12,
241
+ tbsize: int = 3,
242
+ swin: typing.Literal[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11] = 0,
243
+ twin: typing.Literal[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11] = 7,
244
+ sbeta: float = 2.5,
245
+ tbeta: float = 2.5,
246
+ zmean: bool = True,
247
+ f0beta: float = 1.0,
248
+ ssystem: typing.Literal[0, 1] = 0,
249
+ planes: typing.Optional[typing.Union[int, typing.Sequence[int]]] = None,
250
+ backend: backendT = Backend.cuFFT()
251
+ ) -> vs.VideoNode:
252
+ """ this interface is not stable """
253
+
254
+ # translate parameters
255
+ if ftype == 0:
256
+ if abs(f0beta - 1) < 0.00005:
257
+ filter_type = 0
258
+ elif abs(f0beta - 0.5) < 0.0005:
259
+ filter_type = 6
260
+ else:
261
+ filter_type = 5
262
+ else:
263
+ filter_type = ftype
264
+
265
+ radius = (tbsize - 1) // 2
266
+ block_size = sbsize
267
+ block_step = sbsize - sosize
268
+ spatial_window_mode = swin
269
+ temporal_window_mode = twin
270
+ spatial_beta = sbeta
271
+ temporal_beta = tbeta
272
+ zero_mean = zmean
273
+ backend = init_backend(backend)
274
+
275
+ if isinstance(backend, (Backend.CPU, Backend.NVRTC, Backend.GCC, Backend.HIPRTC)):
276
+ if radius not in range(4):
277
+ raise ValueError("invalid radius (tbsize)")
278
+ if block_size != 16:
279
+ raise ValueError("invalid block_size (sbsize)")
280
+
281
+ # compute constants
282
+ try:
283
+ sigma_scalar = float(sigma) # type: ignore
284
+ sigma_is_scalar = True
285
+ except:
286
+ # compute sigma_array
287
+
288
+ sigma_is_scalar = False
289
+
290
+ sigma_funcs = typing.cast(typing.Sequence[typing.Callable[[float], float]], sigma)
291
+ if callable(sigma_funcs):
292
+ sigma_funcs = [sigma_funcs]
293
+ else:
294
+ sigma_funcs = list(sigma_funcs)
295
+ sigma_funcs.extend([sigma_funcs[-1]] * 3)
296
+ sigma_func_x, sigma_func_y, sigma_func_t = sigma_funcs[:3]
297
+
298
+ sigma_array = []
299
+
300
+ if ssystem == 0:
301
+ for t in range(2 * radius + 1):
302
+ sigma_t = get_sigma(position=t, length=2*radius+1, func=sigma_func_t)
303
+ for y in range(block_size):
304
+ sigma_y = get_sigma(position=y, length=block_size, func=sigma_func_y)
305
+ for x in range(block_size // 2 + 1):
306
+ sigma_x = get_sigma(position=x, length=block_size, func=sigma_func_x)
307
+
308
+ sigma = sigma_t * sigma_y * sigma_x
309
+ sigma_array.append(sigma)
310
+ else:
311
+ for t in range(2 * radius + 1):
312
+ loc_t = get_location(position=t, length=2*radius+1)
313
+ for y in range(block_size):
314
+ loc_y = get_location(position=y, length=block_size)
315
+ for x in range(block_size // 2 + 1):
316
+ loc_x = get_location(position=x, length=block_size)
317
+
318
+ ndim = 3 if radius > 0 else 2
319
+ location = math.sqrt((loc_t * loc_t + loc_y * loc_y + loc_x * loc_x) / ndim)
320
+ sigma = sigma_func_t(location)
321
+ sigma_array.append(sigma)
322
+
323
+ window = get_window(
324
+ radius=radius,
325
+ block_size=block_size,
326
+ block_step=block_step,
327
+ spatial_window_mode=spatial_window_mode,
328
+ temporal_window_mode=temporal_window_mode,
329
+ spatial_beta=spatial_beta,
330
+ temporal_beta=temporal_beta
331
+ )
332
+
333
+ wscale = math.fsum(w * w for w in window)
334
+
335
+ if ftype < 2:
336
+ if sigma_is_scalar:
337
+ sigma_scalar *= wscale
338
+ else:
339
+ sigma_array = [s * wscale for s in sigma_array]
340
+ sigma2 *= wscale
341
+
342
+ pmin *= wscale
343
+ pmax *= wscale
344
+
345
+ if isinstance(backend, Backend.cuFFT):
346
+ rdft = core.dfttest2_cuda.RDFT
347
+ elif isinstance(backend, Backend.NVRTC):
348
+ rdft = core.dfttest2_nvrtc.RDFT
349
+ elif isinstance(backend, Backend.CPU):
350
+ rdft = core.dfttest2_cpu.RDFT
351
+ elif isinstance(backend, Backend.GCC):
352
+ rdft = core.dfttest2_gcc.RDFT
353
+ elif isinstance(backend, Backend.hipFFT):
354
+ rdft = core.dfttest2_hip.RDFT
355
+ elif isinstance(backend, Backend.HIPRTC):
356
+ rdft = core.dfttest2_hiprtc.RDFT
357
+ else:
358
+ raise TypeError("unknown backend")
359
+
360
+ if radius == 0:
361
+ window_freq = rdft(
362
+ data=[w * 255 for w in window],
363
+ shape=(block_size, block_size)
364
+ )
365
+ else:
366
+ window_freq = rdft(
367
+ data=[w * 255 for w in window],
368
+ shape=(2 * radius + 1, block_size, block_size)
369
+ )
370
+
371
+ if isinstance(backend, Backend.CPU):
372
+ return core.dfttest2_cpu.DFTTest(
373
+ clip,
374
+ window=window,
375
+ sigma=[sigma_scalar] * (2 * radius + 1) * block_size * (block_size // 2 + 1) if sigma_is_scalar else sigma_array,
376
+ sigma2=sigma2,
377
+ pmin=pmin,
378
+ pmax=pmax,
379
+ radius=radius,
380
+ block_size=block_size,
381
+ block_step=block_step,
382
+ planes=planes,
383
+ filter_type=filter_type,
384
+ window_freq=window_freq,
385
+ opt=backend.opt
386
+ )
387
+ elif isinstance(backend, Backend.GCC):
388
+ return core.dfttest2_gcc.DFTTest(
389
+ clip,
390
+ window=window,
391
+ sigma=[sigma_scalar] * (2 * radius + 1) * block_size * (block_size // 2 + 1) if sigma_is_scalar else sigma_array,
392
+ sigma2=sigma2,
393
+ pmin=pmin,
394
+ pmax=pmax,
395
+ radius=radius,
396
+ block_size=block_size,
397
+ block_step=block_step,
398
+ planes=planes,
399
+ filter_type=filter_type,
400
+ window_freq=window_freq
401
+ )
402
+
403
+ if isinstance(backend, Backend.cuFFT):
404
+ to_single = core.dfttest2_cuda.ToSingle
405
+ elif isinstance(backend, Backend.NVRTC):
406
+ to_single = core.dfttest2_nvrtc.ToSingle
407
+ elif isinstance(backend, Backend.hipFFT):
408
+ to_single = core.dfttest2_hip.ToSingle
409
+ elif isinstance(backend, Backend.HIPRTC):
410
+ to_single = core.dfttest2_hiprtc.ToSingle
411
+ else:
412
+ raise TypeError("unknown backend")
413
+
414
+ kernel = Template(
415
+ """
416
+ #define FILTER_TYPE ${filter_type}
417
+ #define ZERO_MEAN ${zero_mean}
418
+ #define SIGMA_IS_SCALAR ${sigma_is_scalar}
419
+
420
+ #if ZERO_MEAN
421
+ __device__ static const float window_freq[] { ${window_freq} };
422
+ #endif // ZERO_MEAN
423
+
424
+ __device__ static const float window[] { ${window} };
425
+
426
+ __device__
427
+ static void filter(float2 & value, int x, int y, int t) {
428
+ #if SIGMA_IS_SCALAR
429
+ float sigma = static_cast<float>(${sigma});
430
+ #else // SIGMA_IS_SCALAR
431
+ __device__ static const float sigma_array[] { ${sigma} };
432
+ float sigma = sigma_array[(t * BLOCK_SIZE + y) * (BLOCK_SIZE / 2 + 1) + x];
433
+ #endif // SIGMA_IS_SCALAR
434
+ [[maybe_unused]] float sigma2 = static_cast<float>(${sigma2});
435
+ [[maybe_unused]] float pmin = static_cast<float>(${pmin});
436
+ [[maybe_unused]] float pmax = static_cast<float>(${pmax});
437
+ [[maybe_unused]] float multiplier {};
438
+
439
+ #if FILTER_TYPE == 2
440
+ value.x *= sigma;
441
+ value.y *= sigma;
442
+ return ;
443
+ #endif
444
+
445
+ float psd = value.x * value.x + value.y * value.y;
446
+
447
+ #if FILTER_TYPE == 1
448
+ if (psd < sigma) {
449
+ value.x = 0.0f;
450
+ value.y = 0.0f;
451
+ }
452
+ return ;
453
+ #elif FILTER_TYPE == 0
454
+ multiplier = fmaxf((psd - sigma) / (psd + 1e-15f), 0.0f);
455
+ #elif FILTER_TYPE == 3
456
+ if (psd >= pmin && psd <= pmax) {
457
+ multiplier = sigma;
458
+ } else {
459
+ multiplier = sigma2;
460
+ }
461
+ #elif FILTER_TYPE == 4
462
+ multiplier = sigma * sqrtf(psd * (pmax / ((psd + pmin) * (psd + pmax) + 1e-15f)));
463
+ #elif FILTER_TYPE == 5
464
+ multiplier = powf(fmaxf((psd - sigma) / (psd + 1e-15f), 0.0f), pmin);
465
+ #else
466
+ multiplier = sqrtf(fmaxf((psd - sigma) / (psd + 1e-15f), 0.0f));
467
+ #endif
468
+
469
+ value.x *= multiplier;
470
+ value.y *= multiplier;
471
+ }
472
+ """
473
+ ).substitute(
474
+ sigma_is_scalar=int(sigma_is_scalar),
475
+ sigma=(
476
+ to_single(sigma_scalar)
477
+ if sigma_is_scalar
478
+ else ','.join(str(to_single(x)) for x in sigma_array)
479
+ ),
480
+ sigma2=to_single(sigma2),
481
+ pmin=to_single(pmin),
482
+ pmax=to_single(pmax),
483
+ filter_type=int(filter_type),
484
+ window_freq=','.join(str(to_single(x)) for x in window_freq),
485
+ zero_mean=int(zero_mean),
486
+ window=','.join(str(to_single(x)) for x in window),
487
+ )
488
+
489
+ if isinstance(backend, Backend.cuFFT):
490
+ return core.dfttest2_cuda.DFTTest(
491
+ clip,
492
+ kernel=kernel,
493
+ radius=radius,
494
+ block_size=block_size,
495
+ block_step=block_step,
496
+ planes=planes,
497
+ in_place=backend.in_place,
498
+ device_id=backend.device_id
499
+ )
500
+ elif isinstance(backend, Backend.NVRTC):
501
+ return core.dfttest2_nvrtc.DFTTest(
502
+ clip,
503
+ kernel=kernel,
504
+ radius=radius,
505
+ block_size=block_size,
506
+ block_step=block_step,
507
+ planes=planes,
508
+ in_place=False,
509
+ device_id=backend.device_id,
510
+ num_streams=backend.num_streams
511
+ )
512
+ if isinstance(backend, Backend.hipFFT):
513
+ return core.dfttest2_hip.DFTTest(
514
+ clip,
515
+ kernel=kernel,
516
+ radius=radius,
517
+ block_size=block_size,
518
+ block_step=block_step,
519
+ planes=planes,
520
+ in_place=backend.in_place,
521
+ device_id=backend.device_id
522
+ )
523
+ elif isinstance(backend, Backend.HIPRTC):
524
+ return core.dfttest2_hiprtc.DFTTest(
525
+ clip,
526
+ kernel=kernel,
527
+ radius=radius,
528
+ block_size=block_size,
529
+ block_step=block_step,
530
+ planes=planes,
531
+ in_place=False,
532
+ device_id=backend.device_id,
533
+ num_streams=backend.num_streams
534
+ )
535
+ else:
536
+ raise TypeError("unknown backend")
537
+
538
+
539
+ def select_backend(
540
+ backend: typing.Optional[backendT],
541
+ sbsize: int,
542
+ tbsize: int
543
+ ) -> backendT:
544
+
545
+ if backend is not None:
546
+ return backend
547
+
548
+ if sbsize == 16 and tbsize in [1, 3, 5, 7]:
549
+ if hasattr(core, "dfttest2_nvrtc"):
550
+ return Backend.NVRTC()
551
+ elif hasattr(core, "dfttest2_hiprtc"):
552
+ return Backend.HIPRTC()
553
+ elif hasattr(core, "dfttest2_cuda"):
554
+ return Backend.cuFFT()
555
+ elif hasattr(core, "dfttest2_hip"):
556
+ return Backend.hipFFT()
557
+ elif hasattr(core, "dfttest2_cpu"):
558
+ return Backend.CPU()
559
+ else:
560
+ return Backend.GCC()
561
+ else:
562
+ if hasattr(core, "dfttest2_cuda"):
563
+ return Backend.cuFFT()
564
+ else:
565
+ return Backend.hipFFT()
566
+
567
+
568
+ FREQ = float
569
+ SIGMA = float
570
+ def flatten(
571
+ data: typing.Optional[typing.Union[
572
+ typing.Sequence[typing.Tuple[FREQ, SIGMA]],
573
+ typing.Sequence[float]
574
+ ]]
575
+ ) -> typing.Optional[typing.List[float]]:
576
+
577
+ import itertools as it
578
+ import numbers
579
+
580
+ if data is None:
581
+ return None
582
+ elif isinstance(data[0], numbers.Real):
583
+ return data
584
+ else:
585
+ data = typing.cast(typing.Sequence[typing.Tuple[FREQ, SIGMA]], data)
586
+ return list(it.chain.from_iterable(data))
587
+
588
+
589
+ def to_func(
590
+ data: typing.Optional[typing.Sequence[float]],
591
+ norm: typing.Callable[[float], float],
592
+ sigma: float
593
+ ) -> typing.Callable[[float], float]:
594
+
595
+ if data is None:
596
+ return lambda _: norm(sigma)
597
+
598
+ locations = data[::2]
599
+ sigmas = data[1::2]
600
+ packs = list(zip(locations, sigmas))
601
+ packs = sorted(packs, key=lambda group: group[0])
602
+
603
+ def func(x: float) -> float:
604
+ length = len(packs)
605
+ for i in range(length - 1):
606
+ if x <= packs[i + 1][0]:
607
+ weight = (x - packs[i][0]) / (packs[i + 1][0] - packs[i][0])
608
+ return (1 - weight) * norm(packs[i][1]) + weight * norm(packs[i + 1][1])
609
+ raise ValueError()
610
+
611
+ return func
612
+
613
+
614
+ def DFTTest(
615
+ clip: vs.VideoNode,
616
+ ftype: typing.Literal[0, 1, 2, 3, 4] = 0,
617
+ sigma: float = 8.0,
618
+ sigma2: float = 8.0,
619
+ pmin: float = 0.0,
620
+ pmax: float = 500.0,
621
+ sbsize: int = 16,
622
+ smode: typing.Literal[0, 1] = 1,
623
+ sosize: int = 12,
624
+ tbsize: int = 3,
625
+ # tmode=0, tosize=0
626
+ swin: typing.Literal[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11] = 0,
627
+ twin: typing.Literal[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11] = 7,
628
+ sbeta: float = 2.5,
629
+ tbeta: float = 2.5,
630
+ zmean: bool = True,
631
+ f0beta: float = 1.0,
632
+ nlocation: typing.Optional[typing.Sequence[int]] = None,
633
+ alpha: typing.Optional[float] = None,
634
+ slocation: typing.Optional[typing.Union[
635
+ typing.Sequence[typing.Tuple[FREQ, SIGMA]],
636
+ typing.Sequence[float]
637
+ ]] = None,
638
+ ssx: typing.Optional[typing.Union[
639
+ typing.Sequence[typing.Tuple[FREQ, SIGMA]],
640
+ typing.Sequence[float]
641
+ ]] = None,
642
+ ssy: typing.Optional[typing.Union[
643
+ typing.Sequence[typing.Tuple[FREQ, SIGMA]],
644
+ typing.Sequence[float]
645
+ ]] = None,
646
+ sst: typing.Optional[typing.Union[
647
+ typing.Sequence[typing.Tuple[FREQ, SIGMA]],
648
+ typing.Sequence[float]
649
+ ]] = None,
650
+ ssystem: typing.Literal[0, 1] = 0,
651
+ planes: typing.Optional[typing.Union[int, typing.Sequence[int]]] = None,
652
+ backend: typing.Optional[backendT] = None
653
+ ) -> vs.VideoNode:
654
+ """ 2D/3D frequency domain denoiser
655
+
656
+ The interface is compatible with core.dfttest.DFTTest by HolyWu.
657
+
658
+ Args:
659
+ clip: Clip to process.
660
+
661
+ Any format with either integer sample type of 8-16 bit depth
662
+ or float sample type of 32 bit depth is supported.
663
+
664
+ ftype: Controls the filter type.
665
+
666
+ Possible settings are:
667
+ 0: generalized wiener filter
668
+ mult = max((psd - sigma) / psd, 0) ^ f0beta
669
+
670
+ 1: hard threshold
671
+ mult = psd < sigma ? 0.0 : 1.0
672
+
673
+ 2: multiplier
674
+ mult = sigma
675
+
676
+ 3: multiplier switched based on psd value
677
+ mult = (psd >= pmin && psd <= pmax) ? sigma : sigma2
678
+
679
+ 4: multiplier modified based on psd value and range
680
+ mult = sigma * sqrt((psd * pmax) / ((psd + pmin) * (psd + pmax)))
681
+
682
+ The real and imaginary parts of each complex dft coefficient are multiplied
683
+ by the corresponding 'mult' value.
684
+
685
+ ** psd = magnitude squared = real*real + imag*imag
686
+
687
+ sigma, sigma2: Value of sigma and sigma2.
688
+ If using the slocation parameter then the sigma parameter is ignored.
689
+
690
+ pmin, pmax: Used as described in the ftype parameter description.
691
+
692
+ sbsize: Sets the length of the sides of the spatial window.
693
+ Must be 1 or greater. Must be odd if using smode=0.
694
+
695
+ smode: Sets the mode for spatial operation.
696
+ Currently only tmode=1 is implemented.
697
+
698
+ sosize: Sets the spatial overlap amount.
699
+ Must be in the range 0 to sbsize-1 (inclusive).
700
+ If sosize is greater than sbsize>>1, then sbsize%(sbsize-sosize) must equal 0.
701
+ In other words, overlap greater than 50% requires that sbsize-sosize be a divisor of sbsize.
702
+
703
+ tbsize: Sets the length of the temporal dimension (i.e. number of frames).
704
+ Must be at least 1. Must be odd if using tmode=0.
705
+
706
+ tmode: Sets the mode for temporal operation.
707
+ Currently only tmode=0 is implemented.
708
+
709
+ tosize: Sets the temporal overlap amount.
710
+ Must be in the range 0 to tbsize-1 (inclusive).
711
+ If tosize is greater than tbsize>>1, then tbsize%(tbsize-tosize) must equal 0.
712
+ In other words, overlap greater than 50% requires that tbsize-tosize be a divisor of tbsize.
713
+
714
+ swin, twin: Sets the type of analysis/synthesis window to be used for spatial (swin) and
715
+ temporal (twin) processing. Possible settings:
716
+
717
+ 0: hanning
718
+ 1: hamming
719
+ 2: blackman
720
+ 3: 4 term blackman-harris
721
+ 4: kaiser-bessel
722
+ 5: 7 term blackman-harris
723
+ 6: flat top
724
+ 7: rectangular
725
+ 8: Bartlett
726
+ 9: Bartlett-Hann
727
+ 10: Nuttall
728
+ 11: Blackman-Nuttall
729
+
730
+ sbeta,tbeta: Sets the beta value for kaiser-bessel window type.
731
+ sbeta goes with swin, tbeta goes with twin.
732
+ Not used unless the corresponding window value is set to 4.
733
+
734
+ zmean: Controls whether the window mean is subtracted out (zero'd)
735
+ prior to filtering in the frequency domain.
736
+
737
+ f0beta: Power term in ftype=0.
738
+
739
+ nlocation: Currently not implemented.
740
+
741
+ slocation/ssx/ssy/sst: Used to specify functions of sigma based on frequency.
742
+ Check the original documentation for details.
743
+
744
+ Note that in current implementation,
745
+ "slocation = [(0.0, 1.0), (1.0, 10.0)]"
746
+ is equivalent to
747
+ "slocation = [0.0, 1.0, 1.0, 10.0]"
748
+
749
+ ssystem: Method of sigma computation.
750
+ Check the original documentation for details.
751
+
752
+ planes: Sets which planes will be processed.
753
+ Any unprocessed planes will be simply copied.
754
+
755
+ backend: Backend implementation to use.
756
+ All available backends can be found in the dfttest2.Backend "namespace":
757
+ dfttest2.Backend.{CPU, cuFFT, NVRTC, GCC, hipFFT, HIPRTC}
758
+
759
+ The CPU, NVRTC and GCC backends require sbsize=16.
760
+ The cuFFT and NVRTC backends require a CUDA-enabled system.
761
+ The hipFFT and HIPRTC backends require a CUDA-enabled system.
762
+
763
+ Speed: NVRTC == HIPRTC >> cuFFT > hipFFT > CPU == GCC
764
+ """
765
+
766
+ if (
767
+ not isinstance(clip, vs.VideoNode) or
768
+ clip.width == 0 or
769
+ clip.height == 0 or
770
+ clip.format is None or
771
+ (clip.format.sample_type == vs.INTEGER and clip.format.bits_per_sample > 16) or
772
+ (clip.format.sample_type == vs.FLOAT and clip.format.bits_per_sample != 32)
773
+ ):
774
+ raise ValueError("only constant format 8-16 bit integer and 32 bit float input supported")
775
+
776
+ if ftype < 0 or ftype > 4:
777
+ raise ValueError("ftype must be 0, 1, 2, 3, or 4")
778
+
779
+ if sbsize < 1:
780
+ raise ValueError("sbsize must be greater than or equal to 1")
781
+
782
+ if smode != 1:
783
+ raise ValueError('"smode" must be 1')
784
+
785
+ if sosize > sbsize // 2 and (sbsize % (sbsize - sosize) != 0):
786
+ raise ValueError("spatial overlap greater than 50% requires that sbsize-sosize is a divisor of sbsize")
787
+
788
+ if tbsize < 1:
789
+ raise ValueError('"tbsize" must be at least 1')
790
+
791
+ if swin < 0 or swin > 11:
792
+ raise ValueError("swin must be between 0 and 11 (inclusive)")
793
+
794
+ if twin < 0 or twin > 11:
795
+ raise ValueError("twin must be between 0 and 11 (inclusive)")
796
+
797
+ if nlocation is not None:
798
+ raise ValueError('"nlocation" must be None')
799
+
800
+ if slocation and len(slocation) % 2 != 0:
801
+ raise ValueError("number of elements in slocation must be a multiple of 2")
802
+
803
+ if ssx and len(ssx) % 2 != 0:
804
+ raise ValueError("number of elements in ssx must be a multiple of 2")
805
+
806
+ if ssy and len(ssy) % 2 != 0:
807
+ raise ValueError("number of elements in ssy must be a multiple of 2")
808
+
809
+ if sst and len(sst) % 2 != 0:
810
+ raise ValueError("number of elements in sst must be a multiple of 2")
811
+
812
+ if ssystem < 0 or ssystem > 1:
813
+ raise ValueError("ssystem must be 0 or 1")
814
+
815
+ def norm(x: float) -> float:
816
+ if slocation is not None and ssystem == 1:
817
+ return x
818
+ elif tbsize == 1:
819
+ return math.sqrt(x)
820
+ else:
821
+ return x ** (1 / 3)
822
+
823
+ _sigma: typing.Union[float, typing.Sequence[typing.Callable[[float], float]]]
824
+
825
+ if slocation is not None:
826
+ _sigma = [to_func(flatten(slocation), norm, sigma)] * 3
827
+ elif any(ss is not None for ss in (ssx, ssy, sst)):
828
+ _sigma = [to_func(flatten(ss), norm, sigma) for ss in (ssx, ssy, sst)]
829
+ else:
830
+ _sigma = sigma
831
+
832
+ return DFTTest2(
833
+ clip = clip,
834
+ ftype = ftype,
835
+ sigma = _sigma,
836
+ sigma2 = sigma2,
837
+ pmin = pmin,
838
+ pmax = pmax,
839
+ sbsize = sbsize,
840
+ sosize = sosize,
841
+ tbsize = tbsize,
842
+ swin = swin,
843
+ twin = twin,
844
+ sbeta = sbeta,
845
+ tbeta = tbeta,
846
+ zmean = zmean,
847
+ f0beta = f0beta,
848
+ ssystem = ssystem,
849
+ planes = planes,
850
+ backend = select_backend(backend, sbsize, tbsize)
851
+ )