-
Notifications
You must be signed in to change notification settings - Fork 5
Expand file tree
/
Copy pathdenoise.py
More file actions
920 lines (813 loc) · 48.2 KB
/
Copy pathdenoise.py
File metadata and controls
920 lines (813 loc) · 48.2 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
import vapoursynth as vs
from vapoursynth import core
import math
from typing import Optional, Union, Sequence
from helpers import GetPlane, scale_value, scale, cround, DitherLumaRebuild, KNLMeansCL, DFTTest
from misc import MV, MinBlur, SCDetect, mt_expand_multi
####################################################################################################################################
### ###
### Motion-Compensated Temporal Denoise: MCTemporalDenoise() ###
### ###
### v1.4.20 by "LaTo INV." ###
### ###
### 2 July 2010 ###
### ###
####################################################################################################################################
###
###
###
### /!\ Needed filters: MVTools, DFTTest, FFT3DFilter, TTempSmooth, RGVS, Deblock, DCTFilter
### -------------------
###
###
###
### USAGE: MCTemporalDenoise(i, radius, pfMode, sigma, twopass, useTTmpSm, limit, limit2, post, chroma, refine,
### deblock, useQED, quant1, quant2,
### edgeclean, ECrad, ECthr,
### stabilize, maxr, TTstr,
### bwbh, owoh, blksize, overlap,
### bt, ncpu,
### thSAD, thSADC, thSAD2, thSADC2, thSCD1, thSCD2,
### truemotion, MVglobal, pel, pelsearch, search, searchparam, MVsharp, DCT,
### p, settings)
###
###
###
### PARAMETERS:
### -----------
###
### +---------+
### | DENOISE |
### +---------+--------------------------------------------------------------------------------------+
### | radius : Temporal radius [1...6] |
### | pfMode : Pre-filter mode [-1=off,0=FFT3DFilter,1=MinBlur(1),2=MinBlur(2),3=DFTTest] |
### | sigma : FFT3D sigma for the pre-filtering clip (if pfMode=0) |
### | twopass : Do the denoising job in 2 stages (stronger but very slow) |
### | useTTmpSm : Use MDegrain (faster) or MCompensate+TTempSmooth (stronger) |
### | limit : Limit the effect of the first denoising [-1=auto,0=off,1...255] |
### | limit2 : Limit the effect of the second denoising (if twopass=true) [-1=auto,0=off,1...255] |
### | post : Sigma value for post-denoising with FFT3D [0=off,...] |
### | chroma : Process or not the chroma plane |
### | refine : Refine and recalculate motion data of previously estimated motion vectors |
### +------------------------------------------------------------------------------------------------+
###
###
### +---------+
### | DEBLOCK |
### +---------+-----------------------------------------------------------------------------------+
### | deblock : Enable deblocking before the denoising |
### | useQED : If true, use Deblock_QED, else use Deblock (faster & stronger) |
### | quant1 : Deblock_QED "quant1" parameter (Deblock "quant" parameter is "(quant1+quant2)/2") |
### | quant2 : Deblock_QED "quant2" parameter (Deblock "quant" parameter is "(quant1+quant2)/2") |
### +---------------------------------------------------------------------------------------------+
###
###
### +------------------------------+
### | EDGECLEAN: DERING, DEHALO... |
### +------------------------------+-----------------------------------------------------------------------------------------------------+
### | edgeclean : Enable safe edgeclean process after the denoising (only on edges which are in non-detailed areas, so less detail loss) |
### | ECrad : Radius for mask (the higher, the greater distance from the edge is filtered) |
### | ECthr : Threshold for mask (the higher, the less "small edges" are process) [0...255] |
### +------------------------------------------------------------------------------------------------------------------------------------+
###
###
### +-----------+
### | STABILIZE |
### +-----------+------------------------------------------------------------------------------------------------+
### | stabilize : Enable TTempSmooth post processing to stabilize flat areas (background will be less "nervous") |
### | maxr : Temporal radius (the higher, the more stable image) |
### | TTstr : Strength (see TTempSmooth docs) |
### +------------------------------------------------------------------------------------------------------------+
###
###
### +---------------------+
### | BLOCKSIZE / OVERLAP |
### +---------------------+----------------+
### | bwbh : FFT3D blocksize |
### | owoh : FFT3D overlap |
### | - for speed: bwbh/4 |
### | - for quality: bwbh/2 |
### | blksize : MVTools blocksize |
### | overlap : MVTools overlap |
### | - for speed: blksize/4 |
### | - for quality: blksize/2 |
### +--------------------------------------+
###
###
### +-------+
### | FFT3D |
### +-------+--------------------------+
### | bt : FFT3D block temporal size |
### | ncpu : FFT3DFilter ncpu |
### +----------------------------------+
###
###
### +---------+
### | MVTOOLS |
### +---------+------------------------------------------------------+
### | thSAD : MVTools thSAD for the first pass |
### | thSADC : MVTools thSADC for the first pass |
### | thSAD2 : MVTools thSAD for the second pass (if twopass=true) |
### | thSADC2 : MVTools thSADC for the second pass (if twopass=true) |
### | thSCD1 : MVTools thSCD1 |
### | thSCD2 : MVTools thSCD2 |
### +-----------------------------------+----------------------------+
### | truemotion : MVTools truemotion |
### | MVglobal : MVTools global |
### | pel : MVTools pel |
### | pelsearch : MVTools pelsearch |
### | search : MVTools search |
### | searchparam : MVTools searchparam |
### | MVsharp : MVTools sharp |
### | DCT : MVTools DCT |
### +-----------------------------------+
###
###
### +--------+
### | GLOBAL |
### +--------+-----------------------------------------------------+
### | p : Set an external prefilter clip |
### | settings : Global MCTemporalDenoise settings [default="low"] |
### | - "very low" |
### | - "low" |
### | - "medium" |
### | - "high" |
### | - "very high" |
### +--------------------------------------------------------------+
###
###
###
### DEFAULTS:
### ---------
###
### +-------------+----------------------+----------------------+----------------------+----------------------+----------------------+
### | SETTINGS | VERY LOW | LOW | MEDIUM | HIGH | VERY HIGH |
### |-------------+----------------------+----------------------+----------------------+----------------------+----------------------|
### | radius | 1 | 2 | 3 | 2 | 3 |
### | pfMode | 3 | 3 | 3 | 3 | 3 |
### | sigma | 2 | 4 | 8 | 12 | 16 |
### | twopass | false | false | false | true | true |
### | useTTmpSm | false | false | false | false | false |
### | limit | -1 | -1 | -1 | -1 | 0 |
### | limit2 | -1 | -1 | -1 | 0 | 0 |
### | post | 0 | 0 | 0 | 0 | 0 |
### | chroma | false | false | true | true | true |
### |-------------+----------------------+----------------------+----------------------+----------------------+----------------------|
### | deblock | false | false | false | false | false |
### | useQED | true | true | true | false | false |
### | quant1 | 10 | 20 | 30 | 30 | 40 |
### | quant2 | 20 | 40 | 60 | 60 | 80 |
### |-------------+----------------------+----------------------+----------------------+----------------------+----------------------|
### | edgeclean | false | false | false | false | false |
### | ECrad | 1 | 2 | 3 | 4 | 5 |
### | ECthr | 64 | 32 | 32 | 16 | 16 |
### |-------------+----------------------+----------------------+----------------------+----------------------+----------------------|
### | stabilize | false | false | false | true | true |
### | maxr | 1 | 1 | 2 | 2 | 2 |
### | TTstr | 1 | 1 | 1 | 2 | 2 |
### |-------------+----------------------+----------------------+----------------------+----------------------+----------------------|
### | bwbh | HD?16:8 | HD?16:8 | HD?16:8 | HD?16:8 | HD?16:8 |
### | owoh | HD? 8:4 | HD? 8:4 | HD? 8:4 | HD? 8:4 | HD? 8:4 |
### | blksize | HD?16:8 | HD?16:8 | HD?16:8 | HD?16:8 | HD?16:8 |
### | overlap | HD? 8:4 | HD? 8:4 | HD? 8:4 | HD? 8:4 | HD? 8:4 |
### |-------------+----------------------+----------------------+----------------------+----------------------+----------------------|
### | bt | 1 | 3 | 3 | 3 | 4 |
### | ncpu | 1 | 1 | 1 | 1 | 1 |
### |-------------+----------------------+----------------------+----------------------+----------------------+----------------------|
### | thSAD | 200 | 300 | 400 | 500 | 600 |
### | thSADC | thSAD/2 | thSAD/2 | thSAD/2 | thSAD/2 | thSAD/2 |
### | thSAD2 | 200 | 300 | 400 | 500 | 600 |
### | thSADC2 | thSAD2/2 | thSAD2/2 | thSAD2/2 | thSAD2/2 | thSAD2/2 |
### | thSCD1 | 200 | 300 | 400 | 500 | 600 |
### | thSCD2 | 90 | 100 | 100 | 130 | 130 |
### |-------------+----------------------+----------------------+----------------------+----------------------+----------------------|
### | truemotion | false | false | false | false | false |
### | MVglobal | true | true | true | true | true |
### | pel | 1 | 2 | 2 | 2 | 2 |
### | pelsearch | 1 | 2 | 2 | 2 | 2 |
### | search | 4 | 4 | 4 | 4 | 4 |
### | searchparam | 2 | 2 | 2 | 2 | 2 |
### | MVsharp | 2 | 2 | 2 | 1 | 0 |
### | DCT | 0 | 0 | 0 | 0 | 0 |
### +-------------+----------------------+----------------------+----------------------+----------------------+----------------------+
###
####################################################################################################################################
def MCTemporalDenoise(i, radius=None, pfMode=3, sigma=None, twopass=None, useTTmpSm=False, limit=None, limit2=None, post=0, chroma=None, refine=False, deblock=False, useQED=None, quant1=None,
quant2=None, edgeclean=False, ECrad=None, ECthr=None, stabilize=None, maxr=None, TTstr=None, bwbh=None, owoh=None, blksize=None, overlap=None, bt=None, ncpu=1, thSAD=None,
thSADC=None, thSAD2=None, thSADC2=None, thSCD1=None, thSCD2=None, truemotion=False, MVglobal=True, pel=None, pelsearch=None, search=4, searchparam=2, MVsharp=None, DCT=0, p=None,
settings='low', cuda=False):
# cuda: True looks for a GPU DFTTest implementation (vszipcu, then dfttest2), False stays on CPU.
if not isinstance(i, vs.VideoNode):
raise vs.Error('MCTemporalDenoise: this is not a clip')
if p is not None and (not isinstance(p, vs.VideoNode) or p.format.id != i.format.id):
raise vs.Error("MCTemporalDenoise: 'p' must be the same format as input")
isGray = (i.format.color_family == vs.GRAY)
bits = i.format.bits_per_sample
neutral = 1 << (bits - 1)
peak = (1 << bits) - 1
### DEFAULTS
try:
settings_num = ['very low', 'low', 'medium', 'high', 'very high'].index(settings.lower())
except:
raise vs.Error('MCTemporalDenoise: these settings do not exist')
HD = i.width > 1024 or i.height > 576
if radius is None:
radius = [1, 2, 3, 2, 3][settings_num]
if sigma is None:
sigma = [2, 4, 8, 12, 16][settings_num]
if twopass is None:
twopass = [False, False, False, True, True][settings_num]
if limit is None:
limit = [-1, -1, -1, -1, 0][settings_num]
if limit2 is None:
limit2 = [-1, -1, -1, 0, 0][settings_num]
if chroma is None:
chroma = [False, False, True, True, True][settings_num]
if useQED is None:
useQED = [True, True, True, False, False][settings_num]
if quant1 is None:
quant1 = [10, 20, 30, 30, 40][settings_num]
if quant2 is None:
quant2 = [20, 40, 60, 60, 80][settings_num]
if ECrad is None:
ECrad = [1, 2, 3, 4, 5][settings_num]
if ECthr is None:
ECthr = [64, 32, 32, 16, 16][settings_num]
if stabilize is None:
stabilize = [False, False, False, True, True][settings_num]
if maxr is None:
maxr = [1, 1, 2, 2, 2][settings_num]
if TTstr is None:
TTstr = [1, 1, 1, 2, 2][settings_num]
if bwbh is None:
bwbh = 16 if HD else 8
if owoh is None:
owoh = 8 if HD else 4
if blksize is None:
blksize = 16 if HD else 8
if overlap is None:
overlap = 8 if HD else 4
if bt is None:
bt = [1, 3, 3, 3, 4][settings_num]
if thSAD is None:
thSAD = [200, 300, 400, 500, 600][settings_num]
if thSADC is None:
thSADC = thSAD // 2
if thSAD2 is None:
thSAD2 = [200, 300, 400, 500, 600][settings_num]
if thSADC2 is None:
thSADC2 = thSAD2 // 2
if thSCD1 is None:
thSCD1 = [200, 300, 400, 500, 600][settings_num]
if thSCD2 is None:
thSCD2 = [90, 100, 100, 130, 130][settings_num]
if pel is None:
pel = [1, 2, 2, 2, 2][settings_num]
if pelsearch is None:
pelsearch = [1, 2, 2, 2, 2][settings_num]
if MVsharp is None:
MVsharp = [2, 2, 2, 1, 0][settings_num]
sigma *= peak / 255
limit = scale(limit, peak)
limit2 = scale(limit2, peak)
post *= peak / 255
ECthr = scale(ECthr, peak)
planes = [0, 1, 2] if chroma and not isGray else [0]
### INPUT
mod = bwbh if bwbh >= blksize else blksize
xi = i.width
xf = math.ceil(xi / mod) * mod - xi + mod
xf = xf + xf%4
xn = int(xi + xf)
yi = i.height
yf = math.ceil(yi / mod) * mod - yi + mod
yf = yf + yf%4
yn = int(yi + yf)
pointresize_args = dict(width=xn, height=yn, src_left=-xf / 2, src_top=-yf / 2, src_width=xn, src_height=yn)
i = i.resize.Point(**pointresize_args)
### PREFILTERING
fft3d_args = dict(planes=planes, bw=bwbh, bh=bwbh, bt=bt, ow=owoh, oh=owoh, ncpu=ncpu)
if p is not None:
p = p.resize.Point(**pointresize_args)
elif pfMode <= -1:
p = i
elif pfMode == 0:
if hasattr(core, 'neo_fft3d'):
p = i.neo_fft3d.FFT3D(sigma=sigma * 0.8, sigma2=sigma * 0.6, sigma3=sigma * 0.4, sigma4=sigma * 0.2, **fft3d_args)
else:
p = i.fft3dfilter.FFT3DFilter(sigma=sigma * 0.8, sigma2=sigma * 0.6, sigma3=sigma * 0.4, sigma4=sigma * 0.2, **fft3d_args)
elif pfMode >= 3:
p = DFTTest(i, cuda=cuda, tbsize=1,
slocation=[0.0,4.0, 0.2,9.0, 1.0,15.0], planes=planes)
else:
p = MinBlur(i, r=pfMode, planes=planes)
pD = core.std.MakeDiff(i, p, planes=planes)
p = DitherLumaRebuild(p, s0=1, chroma=chroma)
### DEBLOCKING
crop_args = dict(left=xf // 2, right=xf // 2, top=yf // 2, bottom=yf // 2)
if not deblock:
d = i
elif useQED:
d = Deblock_QED(i.std.Crop(**crop_args), quant1=quant1, quant2=quant2, uv=3 if chroma else 2).resize.Point(**pointresize_args)
else:
d = i.std.Crop(**crop_args).deblock.Deblock(quant=(quant1 + quant2) // 2, planes=planes).resize.Point(**pointresize_args)
### PREPARING
super_args = dict(hpad=0, vpad=0, pel=pel, chroma=chroma, sharp=MVsharp, blksize=blksize, overlap=overlap)
pMVS = MV.Super(p, rfilter=4 if refine else 2, **super_args)
if refine:
super_re_args = dict(hpad=0, vpad=0, pel=pel, chroma=chroma, sharp=MVsharp, blksize=max(blksize // 2, 4), overlap=max(overlap // 2, 2))
rMVS = MV.Super(p, levels=1, **super_re_args)
analyse_args = dict(blksize=blksize, search=search, searchparam=searchparam, pelsearch=pelsearch, chroma=chroma, truemotion=truemotion, global_=MVglobal, overlap=overlap, dct=DCT)
recalculate_args = dict(thsad=thSAD // 2, blksize=max(blksize // 2, 4), search=search, chroma=chroma, truemotion=truemotion, overlap=max(overlap // 2, 2), dct=DCT)
f1v = MV.Analyse(pMVS, isb=False, delta=1, **analyse_args)
b1v = MV.Analyse(pMVS, isb=True, delta=1, **analyse_args)
if refine:
f1v = MV.Recalculate(rMVS, f1v, **recalculate_args)
b1v = MV.Recalculate(rMVS, b1v, **recalculate_args)
if radius > 1:
f2v = MV.Analyse(pMVS, isb=False, delta=2, **analyse_args)
b2v = MV.Analyse(pMVS, isb=True, delta=2, **analyse_args)
if refine:
f2v = MV.Recalculate(rMVS, f2v, **recalculate_args)
b2v = MV.Recalculate(rMVS, b2v, **recalculate_args)
if radius > 2:
f3v = MV.Analyse(pMVS, isb=False, delta=3, **analyse_args)
b3v = MV.Analyse(pMVS, isb=True, delta=3, **analyse_args)
if refine:
f3v = MV.Recalculate(rMVS, f3v, **recalculate_args)
b3v = MV.Recalculate(rMVS, b3v, **recalculate_args)
if radius > 3:
f4v = MV.Analyse(pMVS, isb=False, delta=4, **analyse_args)
b4v = MV.Analyse(pMVS, isb=True, delta=4, **analyse_args)
if refine:
f4v = MV.Recalculate(rMVS, f4v, **recalculate_args)
b4v = MV.Recalculate(rMVS, b4v, **recalculate_args)
if radius > 4:
f5v = MV.Analyse(pMVS, isb=False, delta=5, **analyse_args)
b5v = MV.Analyse(pMVS, isb=True, delta=5, **analyse_args)
if refine:
f5v = MV.Recalculate(rMVS, f5v, **recalculate_args)
b5v = MV.Recalculate(rMVS, b5v, **recalculate_args)
if radius > 5:
f6v = MV.Analyse(pMVS, isb=False, delta=6, **analyse_args)
b6v = MV.Analyse(pMVS, isb=True, delta=6, **analyse_args)
if refine:
f6v = MV.Recalculate(rMVS, f6v, **recalculate_args)
b6v = MV.Recalculate(rMVS, b6v, **recalculate_args)
# if useTTmpSm or stabilize:
# mask_args = dict(ml=thSAD, gamma=0.999, kind=1, ysc=255)
# SAD_f1m = MV.Mask(d, f1v, **mask_args)
# SAD_b1m = MV.Mask(d, b1v, **mask_args)
def MCTD_MVD(i, iMVS, thSAD, thSADC):
degrain_args = dict(thsad=thSAD, thsadc=thSADC, plane=4 if chroma else 0, thscd1=thSCD1, thscd2=thSCD2)
if radius <= 1:
sm = MV.Degrain1(i, iMVS, b1v, f1v, **degrain_args)
elif radius == 2:
sm = MV.Degrain2(i, iMVS, b1v, f1v, b2v, f2v, **degrain_args)
elif radius == 3:
sm = MV.Degrain3(i, iMVS, b1v, f1v, b2v, f2v, b3v, f3v, **degrain_args)
elif radius == 4:
mv12 = MV.Degrain2(i, iMVS, b1v, f1v, b2v, f2v, **degrain_args)
mv34 = MV.Degrain2(i, iMVS, b3v, f3v, b4v, f4v, **degrain_args)
sm = core.std.Merge(mv12, mv34, weight=[0.4444])
elif radius == 5:
mv123 = MV.Degrain3(i, iMVS, b1v, f1v, b2v, f2v, b3v, f3v, **degrain_args)
mv45 = MV.Degrain2(i, iMVS, b4v, f4v, b5v, f5v, **degrain_args)
sm = core.std.Merge(mv123, mv45, weight=[0.4545])
else:
mv123 = MV.Degrain3(i, iMVS, b1v, f1v, b2v, f2v, b3v, f3v, **degrain_args)
mv456 = MV.Degrain3(i, iMVS, b4v, f4v, b5v, f5v, b6v, f6v, **degrain_args)
sm = core.std.Merge(mv123, mv456, weight=[0.4615])
return sm
def MCTD_TTSM(i, iMVS, thSAD):
compensate_args = dict(thsad=thSAD, thscd1=thSCD1, thscd2=thSCD2)
f1c = MV.Compensate(i, iMVS, f1v, **compensate_args)
b1c = MV.Compensate(i, iMVS, b1v, **compensate_args)
if radius > 1:
f2c = MV.Compensate(i, iMVS, f2v, **compensate_args)
b2c = MV.Compensate(i, iMVS, b2v, **compensate_args)
# SAD_f2m = MV.Mask(i, f2v, **mask_args)
# SAD_b2m = MV.Mask(i, b2v, **mask_args)
if radius > 2:
f3c = MV.Compensate(i, iMVS, f3v, **compensate_args)
b3c = MV.Compensate(i, iMVS, b3v, **compensate_args)
# SAD_f3m = MV.Mask(i, f3v, **mask_args)
# SAD_b3m = MV.Mask(i, b3v, **mask_args)
if radius > 3:
f4c = MV.Compensate(i, iMVS, f4v, **compensate_args)
b4c = MV.Compensate(i, iMVS, b4v, **compensate_args)
# SAD_f4m = MV.Mask(i, f4v, **mask_args)
# SAD_b4m = MV.Mask(i, b4v, **mask_args)
if radius > 4:
f5c = MV.Compensate(i, iMVS, f5v, **compensate_args)
b5c = MV.Compensate(i, iMVS, b5v, **compensate_args)
# SAD_f5m = MV.Mask(i, f5v, **mask_args)
# SAD_b5m = MV.Mask(i, b5v, **mask_args)
if radius > 5:
f6c = MV.Compensate(i, iMVS, f6v, **compensate_args)
b6c = MV.Compensate(i, iMVS, b6v, **compensate_args)
# SAD_f6m = MV.Mask(i, f6v, **mask_args)
# SAD_b6m = MV.Mask(i, b6v, **mask_args)
# b = i.std.BlankClip(color=[0] if isGray else [0, neutral, neutral])
if radius <= 1:
c = core.std.Interleave([f1c, i, b1c])
# SAD_m = core.std.Interleave([SAD_f1m, b, SAD_b1m])
elif radius == 2:
c = core.std.Interleave([f2c, f1c, i, b1c, b2c])
# SAD_m = core.std.Interleave([SAD_f2m, SAD_f1m, b, SAD_b1m, SAD_b2m])
elif radius == 3:
c = core.std.Interleave([f3c, f2c, f1c, i, b1c, b2c, b3c])
# SAD_m = core.std.Interleave([SAD_f3m, SAD_f2m, SAD_f1m, b, SAD_b1m, SAD_b2m, SAD_b3m])
elif radius == 4:
c = core.std.Interleave([f4c, f3c, f2c, f1c, i, b1c, b2c, b3c, b4c])
# SAD_m = core.std.Interleave([SAD_f4m, SAD_f3m, SAD_f2m, SAD_f1m, b, SAD_b1m, SAD_b2m, SAD_b3m, SAD_b4m])
elif radius == 5:
c = core.std.Interleave([f5c, f4c, f3c, f2c, f1c, i, b1c, b2c, b3c, b4c, b5c])
# SAD_m = core.std.Interleave([SAD_f5m, SAD_f4m, SAD_f3m, SAD_f2m, SAD_f1m, b, SAD_b1m, SAD_b2m, SAD_b3m, SAD_b4m, SAD_b5m])
else:
c = core.std.Interleave([f6c, f5c, f4c, f3c, f2c, f1c, i, b1c, b2c, b3c, b4c, b5c, b6c])
# SAD_m = core.std.Interleave([SAD_f6m, SAD_f5m, SAD_f4m, SAD_f3m, SAD_f2m, SAD_f1m, b, SAD_b1m, SAD_b2m, SAD_b3m, SAD_b4m, SAD_b5m, SAD_b6m])
if hasattr(core,'scd'):
c = core.scd.Detect(c, thresh=0.999)
sm = core.zsmooth.TTempSmooth(c, maxr=radius, thresh=[255], mdiff=[1], strength=radius + 1, scthresh=-1, fp=False, planes=planes)
elif hasattr(core,'zsmooth'):
import misc
c = SCDetect(c, threshold=0.999)
sm = core.zsmooth.TTempSmooth(c, maxr=radius, thresh=[255], mdiff=[1], strength=radius + 1, scthresh=-1, fp=False, planes=planes)
else:
sm = c.ttmpsm.TTempSmooth(maxr=radius, thresh=[255], mdiff=[1], strength=radius + 1, scthresh=99.9, fp=False, planes=planes)
return sm.std.SelectEvery(cycle=radius * 2 + 1, offsets=[radius])
### DENOISING: FIRST PASS
dMVS = MV.Super(d, levels=1, **super_args)
sm = MCTD_TTSM(d, dMVS, thSAD) if useTTmpSm else MCTD_MVD(d, dMVS, thSAD, thSADC)
EXPR = core.akarin.Expr if hasattr(core, 'akarin') else core.cranexpr.Expr if hasattr(core, 'cranexpr') else core.std.Expr
if limit <= -1:
smD = core.std.MakeDiff(i, sm, planes=planes)
expr = f'x {neutral} - abs y {neutral} - abs < x y ?'
DD = EXPR([pD, smD], expr=[expr] if chroma or isGray else [expr, ''])
smL = core.std.MakeDiff(i, DD, planes=planes)
elif limit > 0:
expr = f'x y - abs {limit} <= x x y - 0 < y {limit} - y {limit} + ? ?'
smL = EXPR([sm, i], expr=[expr] if chroma or isGray else [expr, ''])
else:
smL = sm
### DENOISING: SECOND PASS
if twopass:
smLMVS = MV.Super(smL, levels=1, **super_args)
sm = MCTD_TTSM(smL, smLMVS, thSAD2) if useTTmpSm else MCTD_MVD(smL, smLMVS, thSAD2, thSADC2)
if limit2 <= -1:
smD = core.std.MakeDiff(i, sm, planes=planes)
expr = f'x {neutral} - abs y {neutral} - abs < x y ?'
DD = EXPR([pD, smD], expr=[expr] if chroma or isGray else [expr, ''])
smL = core.std.MakeDiff(i, DD, planes=planes)
elif limit2 > 0:
expr = f'x y - abs {limit2} <= x x y - 0 < y {limit2} - y {limit2} + ? ?'
smL = EXPR([sm, i], expr=[expr] if chroma or isGray else [expr, ''])
else:
smL = sm
### POST-DENOISING: FFT3D
if post <= 0:
smP = smL
else:
if hasattr(core, 'neo_fft3d'):
smP = smL.neo_fft3d.FFT3D(sigma=post * 0.8, sigma2=post * 0.6, sigma3=post * 0.4, sigma4=post * 0.2, **fft3d_args)
else:
smP = smL.fft3dfilter.FFT3DFilter(sigma=post * 0.8, sigma2=post * 0.6, sigma3=post * 0.4, sigma4=post * 0.2, **fft3d_args)
### EDGECLEANING
if edgeclean:
PREWITT = core.edgemasks.ExPrewitt if hasattr(core,"edgemasks") else core.std.Prewitt
mP = PREWITT(GetPlane(smP, 0))
mS = mt_expand_multi(mP, sw=ECrad, sh=ECrad).std.Inflate()
mD = EXPR([mS, mP.std.Inflate()], expr=[f'x y - {ECthr} <= 0 x y - ?']).std.Inflate().std.Convolution(matrix=[1, 1, 1, 1, 1, 1, 1, 1, 1])
smoothed = DFTTest(smP, cuda=cuda, tbsize=1, planes=planes)
smP = core.std.MaskedMerge(smP, DeHalo_alpha(smoothed, darkstr=0), mD, planes=planes)
### STABILIZING
if stabilize:
# mM = core.std.Merge(GetPlane(SAD_f1m, 0), GetPlane(SAD_b1m, 0)).std.Lut(function=lambda x: min(cround(x ** 1.6), peak))
PREWITT = core.edgemasks.ExPrewitt if hasattr(core,"edgemasks") else core.std.Prewitt
mE = PREWITT(GetPlane(smP, 0)).std.Lut(function=lambda x: min(cround(x ** 1.8), peak))
has_zsmooth = hasattr(core,'zsmooth');
mE = mE.zsmooth.Median() if has_zsmooth else mE.std.Median()
mE = mE.std.Inflate()
# mF = core.std.Expr([mM, mE], expr=['x y max']).std.Convolution(matrix=[1, 1, 1, 1, 1, 1, 1, 1, 1])
mF = mE.std.Convolution(matrix=[1, 1, 1, 1, 1, 1, 1, 1, 1])
if has_zsmooth:
import misc
smP = SCDetect(smP, threshold=0.12)
TTc = smP.zsmooth.TTempSmooth(maxr=maxr, mdiff=[255], strength=TTstr, scthresh=-1, planes=planes)
else:
TTc = smP.ttmpsm.TTempSmooth(maxr=maxr, mdiff=[255], strength=TTstr, planes=planes)
smP = core.std.MaskedMerge(TTc, smP, mF, planes=planes)
### OUTPUT
return smP.std.Crop(**crop_args)
def mClean(clip, thSAD=400, chroma=True, sharp=10, rn=14, deband=0, depth=0, strength=20, outbits=None, icalc=True, rgmode=18):
"""
From: https://forum.doom9.org/showthread.php?t=174804 by burfadel
mClean spatio/temporal denoiser
+++ Description +++
Typical spatial filters work by removing large variations in the image on a small scale, reducing noise but also making the image less
sharp or temporally stable. mClean removes noise whilst retaining as much detail as possible, as well as provide optional image enhancement.
mClean works primarily in the temporal domain, although there is some spatial limiting.
Chroma is processed a little differently to luma for optimal results.
Chroma processing can be disabled with chroma = False.
+++ Artifacts +++
Spatial picture artifacts may remain as removing them is a fine balance between removing the unwanted artifact whilst not removing detail.
Additional dering/dehalo/deblock filters may be required, but should ONLY be uses if required due the detail loss/artifact removal balance.
+++ Sharpening +++
Applies a modified unsharp mask to edges and major detected detail. Range of normal sharpening is 0-20. There are 4 additional settings,
21-24 that provide 'overboost' sharpening. Overboost sharpening is only suitable typically for high definition, high quality sources.
Actual sharpening calculation is scaled based on resolution.
+++ ReNoise +++
ReNoise adds back some of the removed luma noise. Re-adding original noise would be counterproductive, therefore ReNoise modifies this noise
both spatially and temporally. The result of this modification is the noise becomes much nicer and it's impact on compressibility is greatly
reduced. It is not applied on areas where the sharpening occurs as that would be counterproductive. Settings range from 0 to 20.
The strength of renoise is affected by the the amount of original noise removed and how this noise varies between frames.
It's main purpose is to reduce the 'flatness' that occurs with any form of effective denoising.
+++ Deband +++
This will perceptibly improve the quality of the image by reducing banding effect and adding a small amount of temporally stabilised grain
to both luma and chroma. The settings are not adjustable as the default settings are suitable for most cases without having a large effect
on compressibility. 0 = disabled, 1 = deband only, 2 = deband and veed
+++ Depth +++
This applies a modified warp sharpening on the image that may be useful for certain things, and can improve the perception of image depth.
Settings range up from 0 to 5. This function will distort the image, for animation a setting of 1 or 2 can be beneficial to improve lines.
+++ Strength +++
The strength of the denoising effect can be adjusted using this parameter. It ranges from 20 percent denoising effect with strength 0, up to the
100 percent of the denoising with strength 20. This function works by blending a scaled percentage of the original image with the processed image.
+++ Outbits +++
Specifies the bits per component (bpc) for the output for processing by additional filters. It will also be the bpc that mClean will process.
If you output at a higher bpc keep in mind that there may be limitations to what subsequent filters and the encoder may support.
"""
# New parameter icalc, set to True to enable pure integer processing for faster speed. (Ignored if input is of float sample type)
defH = max(clip.height, clip.width // 4 * 3) # Resolution calculation for auto blksize settings
sharp = min(max(sharp, 0), 24) # Sharp multiplier
rn = min(max(rn, 0), 20) # Luma ReNoise strength
deband = min(max(deband, 0), 5) # Apply deband/veed
depth = min(max(depth, 0), 5) # Depth enhancement
strength = min(max(strength, 0), 20) # Strength of denoising
bd = clip.format.bits_per_sample
isFLOAT = clip.format.sample_type == vs.FLOAT
icalc = False if isFLOAT else icalc
zsmooth = hasattr(core, 'zsmooth')
if hasattr(core, 'mvsf') and isFLOAT:
S = MV.Super if icalc else core.mvsf.Super
A = MV.Analyse if icalc else core.mvsf.Analyse
R = MV.Recalculate if icalc else core.mvsf.Recalculate
else:
S = MV.Super
A = MV.Analyse
R = MV.Recalculate
if not isinstance(clip, vs.VideoNode) or clip.format.color_family != vs.YUV:
raise TypeError("mClean: This is not a YUV clip!")
if outbits is None: # Output bits, default input depth
outbits = bd
if deband or depth:
outbits = min(outbits, 16)
if zsmooth:
RE = core.zsmooth.Repair
RG = core.zsmooth.RemoveGrain
else:
RE = core.rgsf.Repair if outbits == 32 else core.rgvs.Repair
RG = core.rgsf.RemoveGrain if outbits == 32 else core.rgvs.RemoveGrain
sc = 8 if defH > 2880 else 4 if defH > 1440 else 2 if defH > 720 else 1
i = 0.00392 if outbits == 32 else 1 << (outbits - 8)
peak = 1.0 if outbits == 32 else (1 << outbits) - 1
bs = 16 if defH / sc > 360 else 8
ov = 6 if bs > 12 else 2
pel = 1 if defH > 720 else 2
truemotion = False if defH > 720 else True
lampa = 777 * (bs ** 2) // 64
depth2 = -depth*3
depth = depth*2
if sharp > 20:
sharp += 30
elif defH <= 2500:
sharp = 15 + defH * sharp * 0.0007
else:
sharp = 50
# Denoise preparation
if chroma:
if hasattr(core,'zsmooth'):
c = core.zsmooth.Median(clip, radius=2, planes=[1,2])
else:
c = core.vcm.Median(clip, plane=[0, 1, 1])
else:
c = clip
# Temporal luma noise filter
if not (isFLOAT or icalc):
c = c.fmtc.bitdepth(flt=1)
cy = core.std.ShufflePlanes(c, [0], vs.GRAY)
super1 = S(c if chroma else cy, hpad=bs, vpad=bs, pel=pel, rfilter=4, sharp=1, blksize=bs, overlap=ov)
super2 = S(c if chroma else cy, hpad=bs, vpad=bs, pel=pel, rfilter=1, levels=1, blksize=bs, overlap=ov)
analyse_args = dict(blksize=bs, overlap=ov, search=5, truemotion=truemotion)
recalculate_args = dict(blksize=bs, overlap=ov, search=5, truemotion=truemotion, thsad=180, lambda_=lampa)
# Analysis
bvec4 = R(super1, A(super1, isb=True, delta=4, **analyse_args), **recalculate_args) if not icalc else None
bvec3 = R(super1, A(super1, isb=True, delta=3, **analyse_args), **recalculate_args)
bvec2 = R(super1, A(super1, isb=True, delta=2, badsad=1100, lsad=1120, **analyse_args), **recalculate_args)
bvec1 = R(super1, A(super1, isb=True, delta=1, badsad=1500, lsad=980, badrange=27, **analyse_args), **recalculate_args)
fvec1 = R(super1, A(super1, isb=False, delta=1, badsad=1500, lsad=980, badrange=27, **analyse_args), **recalculate_args)
fvec2 = R(super1, A(super1, isb=False, delta=2, badsad=1100, lsad=1120, **analyse_args), **recalculate_args)
fvec3 = R(super1, A(super1, isb=False, delta=3, **analyse_args), **recalculate_args)
fvec4 = R(super1, A(super1, isb=False, delta=4, **analyse_args), **recalculate_args) if not icalc else None
# Applying cleaning
if not icalc:
clean = MV.Degrain4(c if chroma else cy, super2, bvec1, fvec1, bvec2, fvec2, bvec3, fvec3, bvec4, fvec4, thsad=thSAD)
else:
clean = MV.Degrain3(c if chroma else cy, super2, bvec1, fvec1, bvec2, fvec2, bvec3, fvec3, thsad=thSAD)
if c.format.bits_per_sample != outbits:
c = c.fmtc.bitdepth(bits=outbits, dmode=1)
cy = cy.fmtc.bitdepth(bits=outbits, dmode=1)
clean = clean.fmtc.bitdepth(bits=outbits, dmode=1)
TM = core.zsmooth.TemporalMedian if zsmooth else core.tmedian.TemporalMedian
uv = core.std.MergeDiff(clean, TM(core.std.MakeDiff(c, clean, [1, 2]), 1, [1, 2]), [1, 2]) if chroma else c
clean = core.std.ShufflePlanes(clean, [0], vs.GRAY) if clean.format.num_planes != 1 else clean
# Post clean, pre-process deband
filt = core.std.ShufflePlanes([clean, uv], [0, 1, 2], vs.YUV)
if deband:
grainy = defH/15
grainc = defH/16 if chroma else 0
if hasattr(core, 'vszip'):
# vszip.Deband is f3kdb on a 255 scale, f3kdb itself uses a 14 bit one. The preset
# "high" puts every plane at 64, "luma" leaves chroma at 0. See
# https://github.com/dnjulek/vapoursynth-zip/wiki/Deband#how-to-convert-args-from-neo_f3kdb-to-vszip
f3k = 255.0 / ((1 << 14) - 1)
thrc = 64 * f3k if chroma else 0
filt = core.vszip.Deband(filt, range=16, thr=[64 * f3k, thrc, thrc], grain=[grainy * f3k, grainc * f3k])
else:
deband_func = core.neo_f3kdb.Deband if hasattr(core, 'neo_f3kdb') else core.f3kdb.Deband
filt = deband_func(filt, range=16, preset="high" if chroma else "luma", grainy=grainy, grainc=grainc, output_depth=outbits)
clean = core.std.ShufflePlanes(filt, [0], vs.GRAY)
filt = core.vcm.Veed(filt) if deband == 2 else filt
# Spatial luma denoising
clean2 = RG(clean, rgmode)
# Unsharp filter for spatial detail enhancement
if sharp:
if sharp <= 50:
clsharp = core.std.MakeDiff(clean, Blur(clean2, amountH=0.08+0.03*sharp))
else:
if hasattr(core, 'tcanny'):
clsharp = core.std.MakeDiff(clean, clean2.tcanny.TCanny(sigma=(sharp-46)/4, mode=-1))
else:
radius = max(1, round(((sharp-46)/4) * 1.5))
blur = clean2
for _ in range(3):
blur = BoxFilter(blur, radius=radius, radius_v=radius)
clsharp = core.std.MakeDiff(clean, blur)
clsharp = core.std.MergeDiff(clean2, RE(TM(clsharp), clsharp, 12))
# If selected, combining ReNoise
noise_diff = core.std.MakeDiff(clean2, cy)
EXPR = core.akarin.Expr if hasattr(core, 'akarin') else core.cranexpr.Expr if hasattr(core, 'cranexpr') else core.std.Expr
if rn:
import color
expr = "x {a} < 0 x {b} > {p} 0 x {c} - {p} {a} {d} - / * - ? ?".format(a=32*i, b=45*i, c=35*i, d=65*i, p=peak)
clean1 = core.std.Merge(clean2, core.std.MergeDiff(clean2, color.Tweak(TM(noise_diff), cont=1.008+0.00016*rn)), 0.3+rn*0.035)
clean2 = core.std.MaskedMerge(clean2, clean1, EXPR([EXPR([clean, clean.std.Invert()], 'x y min')], [expr]))
# Combining spatial detail enhancement with spatial noise reduction using prepared mask
noise_diff = noise_diff.std.Binarize().std.Invert()
clean2 = core.std.MaskedMerge(clean2, clsharp if sharp else clean, EXPR([noise_diff, clean.std.Sobel()], 'x y max'))
# Combining result of luma and chroma cleaning
output = core.std.ShufflePlanes([clean2, filt], [0, 1, 2], vs.YUV)
output = core.std.Merge(c, output, 0.2+0.04*strength) if strength < 20 else output
if hasattr(core,'warp'):
s1 = output.warp.AWarpSharp2(128, 3, 1, depth2, 1)
s2 = output.warp.AWarpSharp2(128, 2, 1, depth, 1)
else:
import sharpen
s1 = sharpen.AWarpSharp2(output, 128, 3, 1, depth2, 1)
s2 = sharpen.AWarpSharp2(output, 128, 2, 1, depth, 1)
return core.std.MergeDiff(output, core.std.MakeDiff(s1, s2)) if depth else output
# port of Avisynth EZdenoise
def EZDenoise(
clip: vs.VideoNode,
thSAD: int = 150,
thSADC: Optional[int] = None,
tr: int = 3,
blkSize: int = 8,
overlap: int = 4,
pel: int = 1,
chroma: bool = True,
out16: bool = False
) -> vs.VideoNode:
"""
Flexible multi-frame denoising using MVTools.
Parameters:
clip (vs.VideoNode): Input clip.
thSAD (int): Luma threshold.
thSADC (Optional[int]): Chroma threshold (defaults to thSAD).
tr (int): Temporal radius (number of frames to include in motion analysis).
blkSize (int): Block size for motion estimation.
overlap (int): Block overlap.
pel (int): Subpixel precision (1,2,4).
chroma (bool): Whether to denoise chroma planes.
out16 (bool): Convert clip to 16-bit depth before processing.
"""
thSADC = thSAD if thSADC is None else thSADC
plane = 4 if chroma else 1
if out16:
clip = core.fmtc.bitdepth(clip, bits=16)
super_clip = MV.Super(clip, pel=pel, chroma=chroma, hpad=blkSize, vpad=blkSize)
# Analyse motion vectors for each delta up to tr
mv_b = [MV.Analyse(super_clip, isb=True, delta=i, blksize=blkSize, overlap=overlap, chroma=chroma) for i in range(1, tr + 1)]
mv_f = [MV.Analyse(super_clip, isb=False, delta=i, blksize=blkSize, overlap=overlap, chroma=chroma) for i in range(1, tr + 1)]
# Helper to create Degrain clip for a single frame
def degrain_clip(delta: int) -> vs.VideoNode:
if delta == 1:
return MV.Degrain1(clip, super_clip, mv_b[0], mv_f[0], thsad=thSAD, thsadc=thSADC, plane=plane)
elif delta == 2:
return MV.Degrain2(clip, super_clip,
mvbw = mv_b[0], mvfw = mv_f[0],
mvbw2 = mv_b[1], mvfw2 = mv_f[1],
thsad = thSAD, thsadc = thSADC, plane=plane)
else: # delta >= 3
return MV.Degrain3(clip, super_clip,
mvbw = mv_b[0], mvfw = mv_f[0],
mvbw2 = mv_b[1], mvfw2 = mv_f[1],
mvbw3 = mv_b[2], mvfw3 = mv_f[2],
thsad = thSAD, thsadc = thSADC, plane=plane)
# If tr <= 3, just return degrain directly
if tr <= 3:
return degrain_clip(tr)
# For tr > 3, recursively merge additional frames
# Create interleaved Degrain1 frames for extra deltas
extra_clips = [degrain_clip(i+1) for i in range(tr)]
interleaved = core.std.Interleave(extra_clips)
def recursive_merge(start: vs.VideoNode = None, a: int = 2) -> vs.VideoNode:
if start is None:
start = core.std.Merge(core.std.SelectEvery(interleaved, tr, 0),
core.std.SelectEvery(interleaved, tr, 1), 0.5)
merge = core.std.Merge(start, core.std.SelectEvery(interleaved, tr, a), 1 / (a + 1))
a += 1
if a < tr:
return recursive_merge(start=merge, a=a)
else:
return merge
denoised = recursive_merge()
return denoised
########################### HELPER FUNCTIONS ##########################
def Blur(clip: vs.VideoNode, amountH: float = 1.0, amountV: Optional[float] = None,
planes: Optional[Union[int, Sequence[int]]] = None
) -> vs.VideoNode:
"""Avisynth's internel filter Blur()
Simple 3x3-kernel blurring filter.
In fact Blur(n) is just an alias for Sharpen(-n).
Args:
clip: Input clip.
amountH, amountV: (float) Blur uses the kernel is [(1-1/2^amount)/2, 1/2^amount, (1-1/2^amount)/2].
A value of 1.0 gets you a (1/4, 1/2, 1/4) for example.
Negative Blur actually sharpens the image.
The allowable range for Blur is from -1.0 to +1.58.
If \"amountV\" is not set manually, it will be set to \"amountH\".
Default is 1.0.
planes: (int []) Whether to process the corresponding plane. By default, every plane will be processed.
The unprocessed planes will be copied from the source clip, "clip".
"""
funcName = 'Blur'
if not isinstance(clip, vs.VideoNode):
raise TypeError(funcName + ': \"clip\" is not a clip!')
if amountH < -1 or amountH > 1.5849625:
raise ValueError(funcName + ': \'amountH\' have not a correct value! [-1 ~ 1.58]')
if amountV is None:
amountV = amountH
else:
if amountV < -1 or amountV > 1.5849625:
raise ValueError(funcName + ': \'amountV\' have not a correct value! [-1 ~ 1.58]')
return Sharpen(clip, -amountH, -amountV, planes)
def Sharpen(clip: vs.VideoNode, amountH: float = 1.0, amountV: Optional[float] = None,
planes: Optional[Union[int, Sequence[int]]] = None
) -> vs.VideoNode:
"""Avisynth's internel filter Sharpen()
Simple 3x3-kernel sharpening filter.
Args:
clip: Input clip.
amountH, amountV: (float) Sharpen uses the kernel is [(1-2^amount)/2, 2^amount, (1-2^amount)/2].
A value of 1.0 gets you a (-1/2, 2, -1/2) for example.
Negative Sharpen actually blurs the image.
The allowable range for Sharpen is from -1.58 to +1.0.
If \"amountV\" is not set manually, it will be set to \"amountH\".
Default is 1.0.
planes: (int []) Whether to process the corresponding plane. By default, every plane will be processed.
The unprocessed planes will be copied from the source clip, "clip".
"""
funcName = 'Sharpen'
if not isinstance(clip, vs.VideoNode):
raise TypeError(funcName + ': \"clip\" is not a clip!')
if amountH < -1.5849625 or amountH > 1:
raise ValueError(funcName + ': \'amountH\' have not a correct value! [-1.58 ~ 1]')
if amountV is None:
amountV = amountH
else:
if amountV < -1.5849625 or amountV > 1:
raise ValueError(funcName + ': \'amountV\' have not a correct value! [-1.58 ~ 1]')
if planes is None:
planes = list(range(clip.format.num_planes))
center_weight_v = math.floor(2 ** (amountV - 1) * 1023 + 0.5)
outer_weight_v = math.floor((0.25 - 2 ** (amountV - 2)) * 1023 + 0.5)
center_weight_h = math.floor(2 ** (amountH - 1) * 1023 + 0.5)
outer_weight_h = math.floor((0.25 - 2 ** (amountH - 2)) * 1023 + 0.5)
conv_mat_v = [outer_weight_v, center_weight_v, outer_weight_v]
conv_mat_h = [outer_weight_h, center_weight_h, outer_weight_h]
if math.fabs(amountH) >= 0.00002201361136: # log2(1+1/65536)
clip = core.std.Convolution(clip, conv_mat_v, planes=planes, mode='v')
if math.fabs(amountV) >= 0.00002201361136:
clip = core.std.Convolution(clip, conv_mat_h, planes=planes, mode='h')
return clip