-
-
Notifications
You must be signed in to change notification settings - Fork 13
/
GradePack.avsi
1822 lines (1492 loc) · 85.8 KB
/
GradePack.avsi
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
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
###########################################################
### ##
### ##
### Grade Pack v9.0 (13-04-2023) ##
### ##
### https://forum.doom9.org/showthread.php?t=182881 ##
### ##
### by Dogway (Jose Linares) ##
### ##
### ##
### Functions: ##
### ex_vibrance ##
### ex_contrast ##
### ex_levels ##
### ex_autolevels ##
### ex_blend ##
### ex_glow ##
### ex_posterize ##
### Skin_Qualifier ##
### HSVxHSV ##
### GamutWarning ##
### PseudoColor ##
### Vignette ##
### greyscale_rgb ##
### FindTemp ##
### GreyWorld ##
### WhitePoint ##
### Curve Fitting Evaluator ##
### ##
### ##
###########################################################
###
###
### Pack of artistic look transform functions for grading and look tone adjustments.
###
###
####################################
###
### ex_vibrance() v4.0 (31-03-2022)
###
### Inspired by Motenai Yoda's script (20-11-2011): (https://forum.doom9.org/showthread.php?t=162882)
###
### This function allows to recreate the effect on some programs called "Vibrance".
### That is to enhance saturation where is low.
### There is another mode called "Dullness" which does the opposite, lower saturation where is high.
### Finally a "Recover" mode, to enhance saturation where is lost (ie. after filtering) when a 'Rc' clip is given for reference.
###
### Use 'bias' to weight the internal mask and 'show' for visual feedback.
###
### Dependencies: > ExTools
### ResizersPack (for show=1)
### TransformsPack (for show=1)
### MasksPack (for show=1)
###
### modes:
### Saturation: Simple saturation operator without any masks
### Vibrance: Masks high saturation so you can lower or raise saturation where is low (Default)
### Dullness: Masks low saturation so you can lower or raise saturation where is high
### Recover: Recover lost saturation (full or partial) between source and reference clip.
###
###
### Examples:
### ex_vibrance(1.2, mode="Saturation") # Increase saturation
###
### ex_vibrance(1.5) # Increase vibrance (increase saturation where is low)
###
### ex_vibrance(0.5) # Lower saturation where is low
###
### ex_vibrance(2.0, mode="Dullness") # Lower saturation where is high (could help for out-of-gamut cases)
###
### ex_vibrance(1.0, mode="Recover", Rc=b) # Recover saturation from clip 'b'
###
### ex_vibrance(0.5, mode="Recover", Rc=b) # Recover half the saturation from clip 'b'
###
### Function Definition:
### (
### clip yuv,
### float sat=1.00 (0.00 to 2.00),
### string "mode"="Vibrance" ("Vibrance"/ "Dullness"/ "Saturation"/ "Recover"),
### [clip "Rc"],
### float "bias"=5.0 (0.0 to 10.0 by 0.5),
### int "show"=0 (0 to 2)
### )
###
###########################
function ex_vibrance (clip a, float "sat", string "mode", float "bias", clip "Rc", bool "tv_range", int "show") {
bi = BitsPerComponent(a)
fs = propNumElements (a,"_ColorRange") > 0 ? \
propGetInt (a,"_ColorRange") == 0 : false
bi32 = bi==32
sat = Default(Sat, 1) # From 0 to inf. Default 1 (no-op)
bias = Default(bias, 5) # Adjust saturation mask
md = Default(mode, "Vibrance")
tv = Default(tv_range, !fs)
sh = Default(show, 0) # 0: disabled, 1: show histogram and gamut warning, 2: show mask (resized to luma dimensions) 3: show mask at original size
sat = max(sat, 0)
Rec = FindStr(LCase(md),"recover") > 0
Assert(IsVersionOrGreater(3,7,3), "ex_vibrance: Update AviSynth+ version")
Assert(!isRGB(a), "ex_vibrance: RGB format not supported")
Rec ? Assert(Defined(Rc), "ex_vibrance: 'Recover' mode requires a clip defined in the 'Rc' parameter to recover saturation from") : nop()
rangePCc = tv ? "range_max cmax cmin - / *" : ""
rangeTVc = tv ? "cmax cmin - range_max / * range_half +" : "range_half +"
rhlf = bi32 ? "2 *" : bi > 12 ? "range_half /" : string(1. / ex_bs(128,8,bi,!tv)) + " *"
msk = Format("range_half x range_half - XC@ dup * y range_half - dup * + sqrt - "+rhlf+" {bias} ^ M^ ")
mode = md == "Vibrance" ? Format( msk+" XC "+rangePCc+" dup dup {sat} * swap - M * + " +rangeTVc) : \
md == "Dullness" ? Format( msk+" XC "+rangePCc+" dup dup 1 {sat} / * swap - 1 M - * + " +rangeTVc) : \
md == "Saturation" ? Format("x range_half - "+rangePCc+" {sat} * " +rangeTVc) : \
md == "Recover" ? " XC "+rangePCc+Format(sat==1 ? " * " : " dup dup swap3 * swap - {sat} * + ")+rangeTVc : \
md == "Recover2" ? " XC "+rangePCc+Format(sat==1 ? " * " : " dup dup swap3 * swap - {sat} * + ")+rangeTVc : \
Assert (false, "ex_vibrance: '"+md+"', unsupported mode")
# 'Recover' mode masks
msk1 = "x range_half - XC@ abs y range_half - abs + 0.000001 + "
msk2 = "z range_half - abs a range_half - abs + "
# 'Saturation' blend mode masks
msk21 = "x range_half - XC@ abs y range_half - abs + M max"
msk22 = "z range_half - abs a range_half - abs + ymin 0.5 * M@ max "
msk1 = md == "Recover2" ? msk21 : msk1
msk2 = md == "Recover2" ? msk22 : msk2
if (md == "Saturation" && sh==0) {
sat == 1 ? a : \
ex_lut (a, "", mode, fulls=!tv )
} else \
if (Rec && sh==0) {
ex_lutxyza(a, a.SwapUV(), Rc, Rc.SwapUV(), "", fulls=!tv, cstr=msk2+msk1+" / "+mode)
} else {
if (sat==1 && sh==0) { a } else {
rangePCc = tv ? "range_max cmax cmin - / *" : ""
offset = !bi32 ? " 0.5 - " : ""
bias = bias!=1 ? Format("1 swap - {bias} ^ 1 swap -") : ""
Rec ? \
ex_lutxyza(a, a.SwapUV(), Rc, Rc.SwapUV(), "", fulls=!tv, cstr=msk2+msk1+" / "+mode) : \
ex_lutxy (a, a.SwapUV(), "", fulls=!tv, cstr= sh != 2 ? mode : \
Format("f32 x "+rangePCc+" dup * y "+rangePCc+" dup * + sqrt "+bias+offset+" "), scale_inputs=bi32 ? "floatUV" : tv ? "int" : "intf")
w = a.width()
h = a.height()
if (sh==1) {
bi > 8 ? ConvertBits(8,dither=1,fulls=!tv) : last
lvl = Histogram(mode="levels",keepsource=false)
lvl = StackVertical(lvl,Vectorscope()).RatioResize(h/512.,"%",kernel="gauss",p=100)
lvl = tv?lvl.SMPTE_Legal(false):lvl
StackHorizontal(GamutWarning(),lvl)
} else if (sh>1) {
ExtractU()
propDelete(["_Matrix","_Primaries","_Transfer","_PictType"])
propSet("_ColorRange", 0)
sh==2 ? BicubicResize(w,h,0.679623,0.160189) : last } } }
propNumElements("_SceneStatsU") > 0 ? propDelete(["_SceneStatsU","_SceneStatsV"]) : last }
###
###
### ex_contrast() - v7.2 (29-10-2022)
###
###
### Applies contrast in the "S" (sigmoidal) curve fashion.
###
###
### Dependencies: > ExTools
### ResizersPack (for show=true)
### MasksPack (for show=true)
###
### Apply over gamma encoded clip
###
### Examples:
### ex_contrast(0.7) # Default pivot=125.5 is mid-grey for TV range sources (Color range read from frameprops)
### ex_contrast(0.786,pivot=83) # DCP Tone Curve - Adobe Camera Raw's default fallback film curve
###
### Function Definition:
### (
### clip,
### float cont=0.00 (-1.00 to 1.00),
### float "sat"=1.00 (0.00 to 3.00),
### float "pivot"=125.5 (64.0 to 192.0 by 0.5),
### [int "UV"=3 (1 / 2 / 3)],
### bool "show"=false
### )
###
####################################
function ex_contrast (clip c, float "cont", float "sat", float "pivot", bool "tv_range", int "UV", bool "show") {
rgb = isRGB(c)
isy = isy(c)
bi = BitsPerComponent(c)
fs = propNumElements (c,"_ColorRange") > 0 ? \
propGetInt (c,"_ColorRange") == 0 : rgb
lut = isRunTime(c,rgb) || bi == 32 ? 0 : 1
UV = Default(UV, Defined(sat) && sat!=1 || rgb ? 3 : 1)
cont = Default(cont, 0.0) # [-1.0 to +1.0] can set further though
sat = Default(sat, (cont+3)/3) # [ 0.0 to +inf] 1.0 is noop. By default can't be disabled in RGB
sh = Default(show, false) # Show the contrast curve
tv = Default(tv_range, !fs) # TV or PC. Color range of your source
pivot = Default(pivot, !tv ? 127.5 : 125.5) # Pivot: 64 to 192 (roughly)
si = bi == 32 || tv ? ex_UVf(rgb, bi) : "intf"
# Pointer for 'show'
sc = cont
ss = sat
sp = pivot
ep = 0.000001
pivot = ex_bs(pivot, 8, 32, tv_in=tv || tv && 125.4 < pivot <= 126, tv_out=false) # Pivot to float PC levels
Assert(IsVersionOrGreater(3,7,3), "ex_contrast: Update AviSynth+ version")
rangePC = tv ? "x ymin - ymax ymin - /" : "x "
rangeTV = tv ? "ymax ymin - * ymin +" : ""
rangePCc = tv ? "x range_half - range_max cmax cmin - / *" : "x range_half -"
rangeTVc = tv ? "cmax cmin - range_max / * range_half +" : " range_half +"
cont = pow(cont + sign(cont), 3.)
rcont = 1. / (cont + ep)
knee = 1. / (1. + exp(cont * pivot))
shldr = 1. / (1. + exp(cont * (pivot - 1.)))
shmkn = shldr - knee
rshmkn = 1. / shmkn
ycont = "f32 1 1 {cont} {pivot} "+rangePC+" - * exp + / {knee} - {rshmkn} * "+rangeTV
yconti = "f32 {pivot} 1 "+rangePC+" {shmkn} * {knee} + 0.01 max / 1 - log {rcont} * - "+rangeTV
str = cont != 0. ? ex_dlut(Format(cont > 0. ? ycont : yconti), bi, !tv) : ""
cstr = sat != 1. ? Format(rangePCc+" {sat} * "+rangeTVc) : ""
cstr = rgb ? str : sat==1 ? "" : ex_UVexpr(cstr, UV, bi, rgb, !tv, si)
isy ? Expr(c, str , lut=lut, scale_inputs=si) : \
UV == 1 ? Expr(c, str, "" , lut=lut, scale_inputs=si) : \
Expr(c, str, cstr, lut=lut, scale_inputs=si)
propNumElements("_SceneStats") > 0 ? propDelete("_SceneStats") : last
if (sh) {
bi > 8 ? ConvertBits(8,dither=1,fulls=!tv) : last
w = c.width()
h = c.height()
sw = nmod(w/5.) sh = min(h,sw)
plt = GradientLinear(last,zoom=1, precision=2, positive=true, smooth=true, tv_range=tv)
plt = ex_contrast (plt,sc,ss,sp,tv,128)
plt = HistoCurve(plt,size=2,gradient=false).BicubicResize(sw,sh,-0.5,0.25)
plt = rgb ? plt.ConvertToPlanarRGB() : plt
plt = Merge(PadBorders(plt,w-sw,0,0,h-sh))
ex_merge(plt,BoxMask(w-sw,w,0,sh),UV=3)
} }
###
###
### ex_levels() - v4.4 (19-02-2023)
###
###
### Works like internal Levels() but with HBD support out of the box (using autoscaling)
### ...and proper handling of tv range levels. Includes also a 'show' mode.
###
### Dependencies: > ExTools
### ResizersPack (for show=true)
### TransformsPack (for show=true)
### MasksPack (for show=true)
###
### Example: ex_levels(23, 1.200, 255, 0, 255, tv_range=true)
###
### Formula = ((input - input_low) / (input_high - input_low)) ^ (1/gamma) * (output_high - output_low) + output_low
###
### Function Definition:
### (
### clip,
### float input_low=0.0 (0.0 to 255.0 by 1),
### float gamma=1.00 (0.00 to 3.00),
### float input_high=255.0 (0.0 to 255.0 by 1),
### float output_low=0.0 (0.0 to 255.0 by 1),
### float output_high=255.0 (0.0 to 255.0 by 1),
### int "UV"=3 (1 / 2 / 3),
### [bool "show"=false]
### )
###
### Benchmark:
### 100.0% Levels (10*256, 1.40, 245*256, 10*256, 255*256, false, false) (495fps)
### 93.7% ex_levels (10, 1.40, 245, 10, 255, false, 1)
### 16.0% oSmoothLevels(10, 1.40, 245, 10, 255, 0, 0)
###
####################################
function ex_levels (clip c, float "input_low", float "gamma", float "input_high", float "output_low", float "output_high", bool "tv_range", int "UV", bool "show", bool "reStats", bool "clamp_float") {
rgb = isRGB(c)
isy = isy(c)
bi = BitsPerComponent(c)
ist = propNumElements (c,"_SceneStats") > 0
fs = propNumElements (c,"_ColorRange") > 0 ? \
propGetInt (c,"_ColorRange") == 0 : rgb
bi32 = bi == 32
isrn = isRunTime(c,rgb)
lut = isrn || bi32 ? 0 : 1
ilo = Default(input_low, 0)
gam = Default(gamma, 1.0)
ihi = Default(input_high, 255)
olo = Default(output_low, 0)
ohi = Default(output_high, 255)
tv = Default(tv_range, !fs)
sh = Default(show, false)
rs = Default(reStats,ist&&!isrn) # Recalculate Stats from input args (could save from re-running SceneStats() at the cost of accuracy)
cf = Default(clamp_float, bi32)
UV = Default(UV, rgb ? 3 : 1) # if UV=3, for YUV it will compensate chroma according to levels range expansion/reduction
si = ex_UVf(rgb, bi)
# Storing for 'show'
silo = ilo
sihi = ihi
solo = olo
sohi = ohi
ep = 0.000001
Assert(IsVersionOrGreater(3,7,3), "ex_levels: Update AviSynth+ version")
rangePC = tv ? "x ymin - range_max ymax ymin - / * " : "x "
rangeTV = tv ? " ymax ymin - range_max / * ymin + " : ""
rangePCc = tv ? "x range_half - range_max cmax cmin - / * " : "x range_half - "
rangeTVc = tv ? " cmax cmin - range_max / * range_half + " : " range_half + "
ilo = ex_bs( ilo, 8, bi, tv_in=tv, tv_out=false, flt=true)
ihi = ex_bs( ihi, 8, bi, tv_in=tv, tv_out=false, flt=true)
olo = ex_bs( olo, 8, bi, tv_in=tv, tv_out=false, flt=true)
ohi = ex_bs( ohi, 8, bi, tv_in=tv, tv_out=false, flt=true)
iho = ihi - ilo
oho = ohi - olo
ilo = ilo == 0 ? "" : Format(" {ilo} - ")
olo = olo == 0 ? "" : Format(" {olo} + ")
gm = (gam == 1.0) ? "" : string(1. / gam)+" ^ "
iioo = (gam == 1.0) && (iho == oho) ? gm+string(oho) : Format(" 1 {iho} / * "+gm+" {oho} dup swap2 *")
str = ex_dlut("" + rangePC + ilo + " 0 max " + iioo + " swap1 min " + olo + rangeTV, bi, !tv)
crg = ex_bs( tv ? 224 : 255, 8, bi, fulls=!tv)
oho = oho / (crg + ep)
iho = crg / (iho + ep)
rt = pow(oho * iho, 1./gam)
cstr = rgb ? str : ex_UVexpr(Format(rangePCc + " {rt} * " + rangeTVc), UV, bi, rgb, !tv, si)
isy ? Expr(c, str , lut=lut, clamp_float=bi32) : \
UV != 3 ? Expr(c, str, "" , lut=lut, clamp_float=bi32) : \
Expr(c, str, cstr, scale_inputs=si, lut=lut, clamp_float=bi32)
ST = """ sts = propGetAsArray("_SceneStats")
sts = ArrayOp(sts,1,Format("ex_bs(x,{bi},8,tv_in={tv},tv_out=false,fulls=true,flt=true)"))
rng = (sihi - silo)
rngn = (sohi - solo)
mea = (sts[4] - silo) / rng # New scene average after normalization
med = (sts[5] - silo) / rng
iqm = (sts[6] - silo) / rng
gm1 = (log(sts[4]/255)/log(mea))/gam # Find exponent to keep gamma to scene's average
# New Metrics (workaround to avoid calling SceneStats() again)
pmnn = pow(max(0,(sts[0] - silo)) / rng, gm1)
pmxn = pow( (sts[1] - silo) / rng, gm1)
pmnt = pow(max(0,(sts[2] - silo)) / rng, gm1)
pmxt = pow( (sts[3] - silo) / rng, gm1)
nmea = pow( mea, gm1)
nmed = pow( med, gm1)
nIQM = pow( iqm, gm1)
stsn = [pmnn,pmxn,pmnt,pmxt,nmea,nmed,nIQM]
stsn = ArrayOp(stsn,1,Format("ex_bs(x * {rngn} + {solo},8,{bi},tv_in=false,tv_out={tv},fulls=true,flt=true)"))
propSet("_SceneStats",stsn) """
ist ? rs ? isrn ? Eval(ST) : \
ScriptClip(function [silo,gam,sihi,solo,sohi,bi,tv,ST] () {
Eval(ST)
} ) : propDelete("_SceneStats") : last
if (sh) {
bi > 8 ? ConvertBits(8,dither=1,fulls=!tv) : last
w = c.width()
h = c.height()
lvl = Histogram(mode="levels",keepsource=false)
plt = GradientLinear(last, zoom=1, positive=true, precision=2, smooth=true, tv_range=tv)
plt = ex_levels (plt,silo,gam,sihi,solo,sohi,tv,128,false,false)
plt = HistoCurve(plt,size=1,gradient=false)
pls = StackVertical(rgb ? plt.ConverttoPlanarRGB() : plt,lvl).RatioResize(h/512.,"%",b=-0.5,c=0.25)
plt = PadBorders(tv?pls.SMPTE_Legal(false):pls,w-width(pls),0,0,0)
plt = Merge(plt)
ex_merge(last,plt,BoxMask(w-width(pls),w,0,h),UV=3) } }
###
### ex_autolevels()
###
### Performs luma levels normalization on a per-scene basis (requires '_SceneStats' or at least '_SceneRange' frameprops)
### With 'Deflicker' you can remove luma flicker (see example below).
### 'Deflicker' performs global auto-levels on a per-frame and per-scene basis.
### 'Deflicker' only works if the flicker frequency is higher than the content (ie. high motion), otherwise adjust the 'interval'.
###
### Example for a robust deflicker (double call):
### ex_autolevels(true ,true, true, Deflicker=true)
### ex_autolevels(false,true,false, Deflicker=true) # Second pass (recomputes '_SceneStats') to refine gamma deflicker
###
function ex_autolevels (clip c, bool "lo", bool "gamma", bool "hi", int "lift", bool "Deflicker", float "interval", float "th", bool "tv_in", bool "tv_out", bool "reStats") {
c
rgb = isRGB()
bi = BitsPerComponent()
rn = propNumElements ("_SceneRange") > 0
st = propNumElements ("_SceneStats") > 0
fs = propNumElements ("_ColorRange") > 0 ? \
propGetInt ("_ColorRange") == 0 : rgb
df = Default(Deflicker, false)
lo = Default(lo, true)
gm = Default(gamma, true)
hi = Default(hi, df) # True for deflicker, False for normalization
in = Default(interval, 0.5) # Interval in seconds for fetching frames
th = Default(th, 0.1) # 1/1000 threshold (0.1% or 1 -one per-mille-)
tv = Default(tv_in, !fs)
tvo = Default(tv_out, tv)
lf = Default(lift, df ? 2 : 0) # When using Deflicker thresholded minimum can be too low, use this bias to avoid crushing blacks
rs = Default(reStats, !df) # Numerally recomputes new stats instead of rerunning SceneStats. Hard-coded off when deflicker is used
Assert(IsVersionOrGreater(3,7,3), "ex_autolevels: Update AviSynth+ version")
Assert(rn, "ex_autolevels: '_SceneRange' frame properties are required")
!st ? SceneStats("Stats",in,th) : last
ScriptClip(function [lo,gm,hi,df,tv,tvo,rgb,bi,lf,th,rs] () {
sts = propGetAsArray("_SceneStats")
sts = bi>8 ? ArrayOp(sts,1,Format("ex_bs(x,{bi},8,fulls=true,flt=true)")) : sts
cst = PlaneMinMaxStats(last,th,0,0,false)
cst = bi>8 ? ArrayOp(cst,1,Format("ex_bs(x,{bi},8,fulls=true,flt=true)")) : cst
pmin = lo ? df ? cst[2]-lf : sts[2]-lf : tv ? 16 : 0
pmax = hi ? df ? cst[3] : sts[3] : tv ? 235 : 255 # By default a slight highlights normalization is performed even when hi=false
if (gm) {
rng = (pmax - pmin)
scn = (sts[6] - pmin) / rng # New scene average after normalization
cur = (cst[5] - cst[2]) / (cst[3] - cst[2]) # New frame average after normalization
# Prolly I should replace gamma (+weighting to darks) with a simple midpoint remap function
lscn = log(scn)
lcur = log(cur)
# Keep gamma stable to new normalization
pavg1 = lscn/log(sts[6]/255)
# Keep original IQM gamma (removes flicker gamma)
pavg2 = lscn/lcur
# Second pass refinement ( 1/(pavg2+1) is the new frame average value using integrals with limits(0,1) )
# pavg2 = log((1/(pavg2+1)))/lcur # Commented as only worked on synthetic tests
# Lower the effect on extreme gammas
pavg2 = pavg2*clamp(1.1*sin(-pi*(0.56*pavg2-1.06)),0.7,1)
pavg = df ? pavg1/pavg2 : pavg1 # Exponent to keep gamma to scene's IQM average
} else { pavg = 1 }
tv!=tvo ? tvo ? ex_levels(pmin,pavg,pmax,16,235,false,!rgb&&df?1:3,false,df?false:rs) : \
ex_levels(pmin,pavg,pmax,0 ,255,false,!rgb&&df?1:3,false,df?false:rs) : \
ex_levels(pmin,pavg,pmax,0 ,255,tv, !rgb&&df?1:3,false,df?false:rs)
} )
tv!=tvo && df && !rgb ? tvo ? SMPTE_Legal(true,Y=1) : SMPTE_Legal(false,Y=1) : last
tv!=tvo ? propSet("_ColorRange",tvo?1:0) : last }
###
###
### ex_blend() - v4.5 (02-04-2023)
###
###
### Works like Overlay() but supports more modes (55 in total), better handling of TV range and performs much faster in either 8-bit or HBD (YUV or RGB).
###
###
### Dependencies: > ExTools
### TransformsPack (for mode='hue')
###
### Example: ex_blend(a, mode="multiply", opacity=1.0, tv_range=true, UV=2)
###
### Function Definition:
### (
### clip a,
### clip b,
### string "mode"="multiply" ("normal"/ "blend"/ "interpolation"/ "harmonic"/ "geometric"/ "watermark"/ "--"/ "darker"/ "darken"/ "darkenS"/ "multiply"/ "multiply128"/ "softburn"/ "colorburn"/ "linearburn"/ "--"/ "lighter"/ "lighten"/ "lightenS"/ "screen"/ "softdodge"/ "colordodge"/ "lineardodge"/ "linearadd"/ "--"/ "overlay"/ "softoverlay"/ "hardoverlay"/ "softlight"/ "hardlight"/ "vividlight"/ "linearlight"/ "stamp"/ "pinlight"/ "hardmix"/ "reflect"/ "glow"/ "freeze"/ "heat"/ "--"/ "grainextract"/ "grainmerge"/ "--"/ "difference"/ "softdifference"/ "negation"/ "exclusion"/ "subtract"/ "linearsubtract"/ "from"/ "divide"/ "andn"/ "bleach"/ "extremity"/ "stain"/ "phoenix"/ "--"/ "hue"/ "saturation"/ "color"/ "value"),
### float "opacity"=1.00 (0.00 to 1.00),
### [float "watermark"=1.0 (0.0 to 2.0)],
### [int "UV"=3 (1 / 2 / 3)]
### )
###
####################################
###
### 100% ex_blend (a, mode="multiply")
### 99% OverlayPlus(a, mode="multiply",chroma=false) # only in PC range
### 92% Overlay_MTools(mode="multiply") # only in PC range
### 56% Overlay (mode="multiply") # only in PC range
###
### MODES:
### SYMMETRIC
### normal - Simply returns the Top layer
### blend - Linear interpolation of Base and Top layer by opacity setting
### interpolation - Cosine interpolation. Output is similar to 'blend' mode but more contrasty ('S' shaped) interpolation
### harmonic -
### geometric -
### watermark -
###
### DARK
### darker - (RGB only) Returns the RGB values from the Base or Top layers with the lowest absolute summed R+G+B value
### darken - Keep only the darkest colors from either Base or Top layers
### darkenS - Keep only the darkest colors from either Base or Top layers using a "smooth min" polynomial (from Iñigo Quilez articles)
### multiply - Multiply Base with Top layer (a * b)
### multiply128 -
### softburn - A softer ColorBurn, actually a combination of ColorBurn and the inverse of ColorDodge.
### colorburn - Like an inverted 'screen', but instead of multiplying it by the complementary of Top layer, it's divided directly by it. 1 - ( 1 - a) / b
### linearburn -
###
### LIGHT
### lighter - (RGB only) Returns the RGB values from the Base or Top layers with the highest absolute summed R+G+B value
### lighten - Keep only the lightest colors from either Base or Top layers
### lightenS - Keep only the lightest colors from either Base or Top layers using a "smooth max" polynomial (from Iñigo Quilez articles)
### screen - Multiply the complement (invert) of Base and Top layers and invert back the result. 1 - (1 - a) * (1 - b)
### softdodge - A softer ColorDodge, actually a combination of ColorDodge and the inverse of ColorBurn.
### colordodge - Opposite of Multiply, actually it's a divide with the complementary of the Top layer. a / (1 - b)
### lineardodge -
### linearadd - Resembles 'lineardodge' and 'screen'. The result is not as bright as 'lineardodge', but brighter than 'screen'. Good for reflection on compositing
###
### CONTRAST
### overlay - A combination of Multiply (when Base < 128) and Screen (when Base > 128)
### softoverlay - A combination of Multiply and Screen. Same as Overlay but with half weight
### hardoverlay - A combination of Multiply and Screen. Same as Overlay but with double weight
### hardlight - A combination of Multiply and Screen. Same as Overlay but commuted (Multiply when Top < 128 and Screen when Top > 128)
### softlight - A combination of Multiply and Screen. Same as Hard Light but with half weight (or a commuted softoverlay)
### vividlight - A combination of ColorBurn and ColorDodge
### linearlight - A combination of LinearBurn and LinearDodge
### stamp - Very similar to LinearLight, but more optimized
### pinlight - A combination of Darken and Lighten
### hardmix - Sum of Base and Top layers and thresholded at range max
###
### NON-SYMMETRIC or QUADRATIC
### reflect - A darker version of ColorDodge, looks like a contrasty SoftLight. a^2 / (1 - b)
### glow - Same as Reflect but with Base and Top layers swapped, looks like a contrasty HardLight. b^2 / (1 - a)
### freeze - Same as ColorBurn but with less weight for Base layer, hence the output is brighter. 1 - (1 - a)^2 / b
### heat - Like Freeze but with Base and Top layers swapped, output is very contrasty. 1 - (1 - b)^2 / a
###
### ANTISYMMETRIC or INVERSION
### grainextract - Difference of Base and Top layers centered at range_half
### grainmerge - Blend Top Layer -centered on range_half- with Base Layer
###
### difference - Absolute difference of Base and Top layers
### softdifference -
### negation - The "opposite" of difference mode, calculated with the complements of both layers. 1 - abs(1-(a-b))
### exclusion - A 50% mix of Difference and Negation
### subtract - Difference of Base and Top layers (clamped)
### linearsubtract -
### from - Subtract mode commuted
### divide - Divide Base by Top layer. Opposite of Multiply (can be used to undo it)
### andn - Base and Top layers are combined with a logical (binary) ANDN (and not)
### extremity -
### bleach -
### stain -
### phoenix -
###
### HSL COMPONENT
### hue - Keep BRIGHTNESS and SATURATION of the Base layer and inherit HUE of the Top layer
### saturation - Keep BRIGHTNESS and HUE of the Base layer and inherit SATURATION of the Top layer
### color - Keep BRIGHTNESS of the Base layer and mix with HUE and SATURATION of the Top layer
### value - Keep BRIGHTNESS of the Top layer and mix with HUE and SATURATION of the Base layer
###
function ex_blend(clip a, clip b, string "mode", float "opacity", float "watermark", bool "tv_range", int "UV") {
rgb = isRGB(a)
isy = isy(a)
bi = BitsPerComponent(a)
fs = propNumElements (a,"_ColorRange") > 0 ? \
propGetInt (a,"_ColorRange") == 0 : rgb
lut = isRunTime (a,rgb) || bi > 12 ? 0 : 2
md = Default(mode, "multiply")
op = Default(opacity, md == "blend" ? 0.5 : 1.0)
wm = Default(watermark, 1.0) # 0.0 to 2.0. At default 1.0 behaves like 'blend'
tv = Default(tv_range, !fs) # TV or PC. Color range of your source
UV = Default(UV, rgb ? 3 : md == "normal" ? 4 : 1)
si = ex_UVf(rgb, bi)
op = clamp(op,0,1)
iso = op == 1.0
md = ReplaceStr(md," ","")
md = md == "add" ? "lineardodge" : \
md == "addition" ? "lineardodge" : \
md == "plus" ? "lineardodge" : \
md == "or" ? "lineardodge" : \
md == "xor" ? "difference" : \
md == "addition128" ? "grainmerge" : \
md == "difference128"? "grainextract" : \
md == "dodge" ? "colordodge" : \
md == "burn" ? "colorburn" : \
md == "smoothmin" ? "darkenS" : \
md == "smoothmax" ? "lightenS" : \
md == "average" ? "blend" : \
md == "colour" ? "color" : \
md == "luminosity" ? "value" : \
md == "brightness" ? "value" : \
md == "lightness" ? "value" : \
md == "hypot" ? "linearadd" : \
md == "diagonal" ? "linearadd" : \
md == "minus" ? "subtract" : \
md == "and" ? "multiply" : md
Assert(IsVersionOrGreater(3,7,3), "ex_blend: Update AviSynth+ version")
md == "darker" || md == "lighter" ? Assert(rgb && UV == 3, "ex_blend: 'darker' and 'lighter' modes require RGB inputs and UV = 3") : nop()
md == "color" || md == "value" || \
md == "hue" || md == "saturation" ? Assert(!rgb && !isy, "ex_blend: 'hue', 'saturation', 'color' and 'value' modes require YUV inputs") : nop()
rngmx = tv ? "ymax ymin -" : "range_max"
srcmx = tv ? "ymin -" : ""
V = !iso ? "A@" : ""
# Half the modes ported from HAvsFunc, rest from Overlay_MTools(), Blend_MT_alpha3, ffmpeg, Nuke docs and web
# 'softlight' uses Pegtop's improved formula. Actually many of the more esoteric modes were created by him (Jens Gruschel)
# 'hue' mode is YUV (not RGB) based so it differs from Photoshop's
# 'bleach' and 'stain' modes look the same in ffmpeg (bug?) ('stain' alike here)
# 'hardoverlay' is close but not math exact to original, revise later
# Some modes still need clamping for overflow values, revise later
str = Format( \
\
md == "--" ? "x" : \
md == "normal" ? "y" : \
md == "blend" ? iso ? "y" : "x "+srcmx+" dup y "+srcmx+" swap - {op} * + " : \
md == "interpolation" ? "x "+srcmx+" "+V+" "+rngmx+" / pi * cos 0.25 * 0.5 swap - y "+srcmx+" "+rngmx+" / pi * cos 0.25 * - "+rngmx+" * " : \
md == "harmonic" ? "x "+srcmx+" "+V+" dup y "+srcmx+" dup swap2 * 2 * swap2 + / " : \
md == "geometric" ? "x "+srcmx+" "+V+" y "+srcmx+" * sqrt " : \
md == "watermark" ? "x "+srcmx+" A^ y "+srcmx+" "+rngmx+" B@ / 0 max 1 {wm} {op} ^ / ^ B * " : \
\
md == "darker" ? "see footer" : \
md == "darken" ? "x "+V+" y min " : \
md == "darkenS" ? "0.2 x "+srcmx+" A@ y "+srcmx+" B@ - abs "+rngmx+" / - 0 max 5 * 3 ^ 0.166666 0.2 "+rngmx+" * * * A B min swap - 0 max " : \
md == "multiply" ? "x "+srcmx+" "+V+" y "+srcmx+" * "+rngmx+" / " : \
md == "multiply128" ? "x "+srcmx+" "+V+" range_half "+srcmx+" - y "+srcmx+" ymin 2 * / * range_half "+srcmx+" + 0 "+rngmx+" clip " : \
md == "softburn" ? "y "+srcmx+" B@ x "+srcmx+" A@ + "+rngmx+" < B range_half "+srcmx+" * "+rngmx+" A - 0 max C@ / "+rngmx+" C range_half "+srcmx+" * B / - ? " : \
md == "colorburn" ? ""+rngmx+" dup x "+srcmx+" "+V+" - "+rngmx+" * 0 max y "+srcmx+" 0 max / - 0 max " : \
md == "linearburn" ? "x "+srcmx+" "+V+" y "+srcmx+" + B@ "+rngmx+" < 0 B "+rngmx+" - ? " : \
\
md == "lighter" ? "see footer" : \
md == "lighten" ? "x "+V+" y max " : \
md == "lightenS" ? "0.2 x "+srcmx+" A@ y "+srcmx+" B@ - abs "+rngmx+" / - 0 max 5 * 3 ^ 0.166666 0.2 "+rngmx+" * * * A B max + " : \
md == "screen" ? ""+rngmx+" C@ dup x "+srcmx+" "+V+" - C y "+srcmx+" - * C / - " : \
md == "softdodge" ? "x "+srcmx+" A@ y "+srcmx+" B@ + "+rngmx+" < A range_half "+srcmx+" * "+rngmx+" B - C@ / "+rngmx+" C range_half "+srcmx+" * A / - ? " : \
md == "colordodge" ? "x "+srcmx+" "+V+" "+rngmx+" * "+rngmx+" y "+srcmx+" 0 max - / "+rngmx+" min " : \
md == "lineardodge" ? "x "+srcmx+" "+V+" y "+srcmx+" + "+rngmx+" min " : \
md == "linearadd" ? "x "+srcmx+" "+V+" dup * y "+srcmx+" dup * + sqrt "+rngmx+" min " : \
\
md == "overlay" ? "x "+srcmx+" A@ range_half "+srcmx+" < 2 A y "+srcmx+" B@ * "+rngmx+" / * "+rngmx+" 2 "+rngmx+" A - "+rngmx+" B - * "+rngmx+" / * - ? " : \
md == "softoverlay" ? ""+rngmx+" C@ x "+srcmx+" A@ - A y "+srcmx+" B@ * C / * A C dup A - C B - * C / - * + C / " : \
md == "hardoverlay" ? "x "+srcmx+" A@ "+rngmx+" == "+rngmx+" y "+srcmx+" B@ "+rngmx+" * "+rngmx+" 2 * A 2 * - / 0.3333 * A B * 3 * "+rngmx+" 2 * / + "+rngmx+" min ? " : \
md == "softlight" ? ""+rngmx+" C@ y "+srcmx+" B@ - B x "+srcmx+" A@ * C / * B C dup B - C A - * C / - * + C / " : \
md == "hardlight" ? "y "+srcmx+" B@ range_half "+srcmx+" < 2 B x "+srcmx+" A@ * "+rngmx+" / * "+rngmx+" 2 "+rngmx+" B - "+rngmx+" A - * "+rngmx+" / * - ? " : \
md == "vividlight" ? "y "+srcmx+" B@ range_half "+srcmx+" C@ < B 0 <= 2 B * "+rngmx+" dup x "+srcmx+" A@ - "+rngmx+" * 2 B * / - ? 2 B C - * "+rngmx+" >= 2 B C - * A "+rngmx+" * "+rngmx+" 2 B C - * - / ? ? 0 "+rngmx+" clip ": \
md == "linearlight" ? "x "+srcmx+" A@ range_half "+srcmx+" < A 2 y "+srcmx+" B@ * + "+rngmx+" - A 2 B range_half "+srcmx+" - * + ? 0 "+rngmx+" clip " : \
md == "stamp" ? "x "+srcmx+" "+V+" y "+srcmx+" 2 * + "+rngmx+" - 0 "+rngmx+" clip " : \
md == "pinlight" ? "y "+srcmx+" B@ range_half "+srcmx+" < x "+srcmx+" A@ 2 B * min A 2 B range_half "+srcmx+" - * max ? " : \
md == "hardmix" ? "x "+srcmx+" "+V+" y "+srcmx+" + "+rngmx+" < 0 "+rngmx+" ? " : \
\
md == "reflect" ? "x "+srcmx+" "+V+" dup * "+rngmx+" y "+srcmx+" 0 max - / "+rngmx+" min " : \
md == "glow" ? "y "+srcmx+" dup * "+rngmx+" x "+srcmx+" "+V+" 0 max - / "+rngmx+" min " : \
md == "freeze" ? ""+rngmx+" dup x "+srcmx+" "+V+" - dup * y "+srcmx+" 0 max / "+rngmx+" min - " : \
md == "heat" ? ""+rngmx+" dup y "+srcmx+" - dup * x "+srcmx+" 0 max "+V+" / "+rngmx+" min - " : \
\
md == "grainextract" ? "x "+srcmx+" "+V+" y "+srcmx+" - range_half "+srcmx+" + 0 "+rngmx+" clip " : \
md == "grainmerge" ? "x "+srcmx+" "+V+" y "+srcmx+" + range_half "+srcmx+" - 0 "+rngmx+" clip " : \
\
md == "difference" ? "x "+srcmx+" "+V+" y "+srcmx+" - abs " : \
md == "softdifference"? "x "+srcmx+" A@ y "+srcmx+" B@ > B "+rngmx+" == 0 A B - "+rngmx+" * "+rngmx+" B - / 0 max ? B 0 == 0 B A - "+rngmx+" * B / 0 max ? ? " : \
md == "negation" ? ""+rngmx+" dup x "+srcmx+" "+V+" - y "+srcmx+" - abs - " : \
md == "exclusion" ? "x "+srcmx+" A@ dup y "+srcmx+" B@ + A B * 2 * + + "+rngmx+" / "+rngmx+" min " : \
md == "subtract" ? "x "+srcmx+" "+V+" y "+srcmx+" - 0 max " : \
md == "linearsubtract"? "x "+srcmx+" "+V+" dup * y "+srcmx+" dup * - sqrt " : \
md == "from" ? "x "+srcmx+" "+V+" y "+srcmx+" swap - 0 max " : \
md == "divide" ? ""+rngmx+" x "+srcmx+" "+V+" * y "+srcmx+" 0 max / "+rngmx+" min " : \
md == "andn" ? ""+rngmx+" x "+srcmx+" "+V+" - y "+srcmx+" * "+rngmx+" / " : \
md == "extremity" ? ""+rngmx+" x "+srcmx+" "+V+" - y "+srcmx+" - abs " : \
md == "bleach" ? ""+rngmx+" 2 * x "+srcmx+" "+V+" y "+srcmx+" + - "+rngmx+" - 0 max " : \
md == "stain" ? ""+rngmx+" 2 * x "+srcmx+" "+V+" y "+srcmx+" + - "+rngmx+" % " : \
md == "phoenix" ? "x "+srcmx+" "+V+" y "+srcmx+" dup1 dup1 min swap2 max - "+rngmx+" + " : \
\
md == "hue" ? "see footer " : \
md == "saturation" ? "see footer " : \
md == "color" ? "see footer " : \
md == "value" ? "see footer " : \
\
Assert (false, "ex_blend: '"+md+"', unsupported blend mode") )
off = !tv || md == "--" || md == "normal" || md == "blend" && iso || md == "lighten" || md == "darken" ? "" : " ymin +"
str = iso || md == "--" || md == "normal" || md == "blend" ? str+off : \
str+Format(" A - {op} * A + ")+off
if (md == "darker" || md == "lighter") {
Ra = a.ExtractR() Rb = b.ExtractR()
Ga = a.ExtractG() Gb = b.ExtractG()
Ba = a.ExtractB() Bb = b.ExtractB()
sym = md == "darker" ? "<" : ">"
msk = Expr(Ra, Ga, Ba, Rb, Gb, Bb, ex_dlut("x y z + + a b c + + "+sym+" 0 range_max ?", bi, !tv), lut=0, format=PixelType(a))
ex_merge(a, b, msk, luma=false, UV=3)
} else if (md == "hue" || md == "saturation" || md == "color" || md == "value") {
md == "color" ? CombinePlanes(a, b, planes="YUV").propCopy(b,true,props=["_Matrix","_Primaries","_ChromaLocation"]) : \
md == "value" ? CombinePlanes(b, a, planes="YUV").propCopy(a,true,props=["_Matrix","_Primaries","_ChromaLocation"]) : \
md == "saturation" ? ex_vibrance (a, 1.0, mode="Recover2", Rc=b, tv_range=tv) : \
HSVxHSV(b, 1.0, "HUE", "VAL", 0.0, 1.00, 1.0, tv_range=tv, show=3)
Ch = md == "hue" ? ex_vibrance(a,bias=1,show=3).ex_lut("x range_half - abs") : nop()
md == "hue" ? Expr(last,ch,"","f32 x cos y *","f32 x sin y *",scale_inputs="intf",lut=lut,Format="YUV444P"+string(bi==32?"S":bi)) : last
md == "hue" ? CombinePlanes(a, last, planes="YUV") : last
} else {
cstr = ex_UVexpr(str, UV, bi, rgb, !tv, si)
str = ex_dlut (str, bi, !tv)
isy ? Expr(a, b, str, optSingleMode=true, lut=lut) : \
UV == 1 ? Expr(a, b, str, "", optSingleMode=true, lut=lut) : \
Expr(a, b, str, cstr, scale_inputs=si, optSingleMode=true, lut=lut) } }
###
### ex_glow()
###
### Makes highlights glow into darker areas in a realistic exponential fashion, also known as "light wrap".
###
### modes:
### fib - fibonacci multipass: 1,1,2,3,5,8...
### trib - tribonacci multipass: 1,1,2,4,7,13...
###
function ex_glow(clip a, int "radius", int "thres", float "opacity", bool "tv_range", string "mode", int "UV", bool "fulls") {
rgb = isRGB(a)
isy = isy(a)
bi = BitsPerComponent(a)
rd = Default(radius, 8) # from 0 to ~10 (Expr hard limit). Spread
th = Default(thres, 164) # from 64 to 192 (roughly)
op = Default(opacity, rd/50.+0.1) # from 0.0 to 1.0. Strength
md = Default(mode, "trib")
tv = Default(tv_range, !rgb)
UV = Default(UV, rgb ? 3 : 1)
fs = Default(fulls, rgb)
b = ex_binarize(a, th, smooth=false, UV=UV, fulls=fs).ex_kawase(2,"trib",uv).ex_boxblur(1,1,"weighted",uv)
str = ""
for (i = 1, rd, 1) {
c = i == 1 ? "b," : ""
dot = i == rd ? "" : "."
bop = md == "fib" ? floor(pow((1+sqrt(5))/2,i)/sqrt(5)+0.5) :\
floor(0.618033988749895*pow(1.839286755214161,i)+0.5)
bop = min((1./ bop) * 10, 1)
str = str + Format("""ex_blend("""+c+"""ex_kawase(b,{i},md,{uv}),"screen",{bop},1,false,{uv})""")+dot
}
Eval(str)
d = ex_makediff(last,b,dif=false,UV=uv,fulls=fs).moncurve_r(2.22,0.0,false,tv,uv)
rd != 0 ? ex_blend(a,d,"screen",op,1,tv,uv) : a }
###
###
### ex_posterize() - ported from cretindesalpes':
### https://forum.doom9.org/showthread.php?p=1516096
###
### Dependencies: > ExTools
### fmtconv
###
### Similar to "dither_bits" argument from ConvertBits() but more flexible and more options.
###
###
### Function Definition:
### (
### clip,
### int "bits"=3 (0 to 7),
### int "mode"=-1 (-1 to 7)
### #hint Note: -1: round 0: Bayer OD 1: Floyd-Steinberg ED 2: Sierra-2-4A ED 3: Stucki ED 4: Atkinson ED 5: Ostromoukhov ED 6: Void & Cluster OD 7: Quasirandom OD
### )
###
function ex_posterize (clip c, int "bits", int "mode", int "UV") {
rgb = isRGB(c)
bi = BitsPerComponent(c)
fs = propNumElements (c,"_ColorRange") > 0 ? \
propGetInt (c,"_ColorRange") == 0 : rgb
UV = Default(UV, rgb ? 3 : 2)
bits = Default(bits, 3) # Valid range: 0 - 7
mul = string(Pow (2, 8 - bits - 1))
bits = clamp(bits,0,7)
md = Default(mode, -1) # Valid modes: -1: Round (quantization)
# 0: Bayer OD
# 1: Floyd-Steinberg ED
# 2: Sierra-2-4A ED
# 3: Stucki ED
# 4: Atkinson ED
# 5: Ostromoukhov ED
# 6: Void & Cluster OD
# 7: Quasirandom OD
ConvertBits(c,16, fulls=fs, fulld=fs)
ex_lut ("x "+mul+" /", y=3, UV=UV, fulls=fs)
md < 2 ? ConvertBits (bits=8, dither=md, fulls=fs, fulld=fs) : \
md > 4 ? fmtc_bitdepth(bits=8, dmode =md+2, fulls=fs, fulld=fs) : \
fmtc_bitdepth(bits=8, dmode =md+1, fulls=fs, fulld=fs)
ex_lut ("x "+mul+" *", y=3, UV=UV, fulls=fs)
ConvertBits(bi, fulls=fs, fulld=fs) }
###
###
### Skin_Qualifier() - (28-12-2021)
###
### Dependencies: > ExTools
### TransformsPack
###
### Segmentation of skin tones. Extracts a skin mask to use elsewhere.
### It does so in OkLab Jch space which is perceptually uniform (UCS).
###
###
### Example:
###
### Skin_Qualifier(0.80, Delta=43, Q=0.70, blur=3.0, invert=false)
###
### Function Definition:
### (
### clip,
### float str=0.80 (0.10 to 2.00),
### float "Delta"=43.0 (0.0 to 360.0 by 1),
### float "Q"=0.70 (0.00 to 1.00),
### float "blur"=3.0 (0.0 to 6.0 by 0.5),
### bool "invert"=false
### )
###
function Skin_Qualifier (clip c, float "str", float "Delta", float "Q", float "blur", bool "invert") {
rgb = IsRGB(c)
ID = color_propGet(c)
bi = ID[7]
str = Default (str, 0.8) # Strength of mask
dg = Default (Delta, 43) # Degrees in IPT HUE
bw = Default (Q, 0.7) # 0.0 to 1.0. Bandwidth of mask
bl = Default (blur, 3) # Post-blur
in = Default (invert, false) # invert mask
fs = !ID[6]
Assert(IsVersionOrGreater(3,7,3), "Skin_Qualifier: Update AviSynth+ version")
dg = (-1 * dg) * (pi / 180) # IPT HUE goes reverse so "-1 *"
LO = 1-bw
HI = 1-bw/(3.01-min(str,1))
mat = Color_fuzzy_search(ID[2])
U = !rgb ? ExtractU(c).RatioResize(2,"%",kernel="bicubic") : nop()
V = !rgb ? ExtractV(c).RatioResize(2,"%",kernel="bicubic") : nop()
rgb ? IsPlanar(c) ? c : ConvertToPlanarRGB(c) : c
bi < 14 ? ConvertBits(14, fulls=fs) : last
!rgb ? ConvertToPlanarRGB(matrix=mat[0],chromaresample="bicubic",param1=0,param2=0.5) : last
CCTF(mat[2], true, !rgb ? false : !fs, false)
RGB_to_Oklab(matrix=mat[0], Jch=true, fulls=true)
C = ExtractU()
H = ExtractV()
Cnorm = "x 0.28 - abs 1 swap - 0 swap 0.706 - -3.4 * -" # Centered at skin saturation and normalized
in = in ? "1 swap -" : ""
ex_lutxy(C, H, Format("f32 y 6.283185 * pi - {dg} + cos "+Cnorm+" * 0 swap {LO} - 1 {LO} {HI} - / * - "+in), scale_inputs="intf", fulls=true, clamp_float=true)
ConvertBits(bi, dither= bi==32 ? -1 : 1, fulls=true)
!rgb ? ex_lutxyz(U,V,"y range_max / 0.430 - 4 ^ -40000 * 1 + 0 max
z range_max / 0.588 - 4 ^ -40000 * 1 + 0 max * range_max * x min") : last
!rgb ? ex_inpand(2).ex_expand(2) : false
bl > 0 ? ex_gaussianblur(bl, pad=false, UV=3) : last
propDelete(["_Matrix","_Primaries","_Transfer","_PictType"]) }
###
###
### HSVxHSV() - (11-01-2022)
###
### 3x3 operator matrix. Adjust Hue, Saturation or Value (modeB) based on Hue, Saturation or Value (modeA).
### This is similar to internal Tweak(), but double the speed, easier to use and more modes (+5 modes involving 'VAL')
### RGB not supported.
###
### Dependencies: > ExTools
### TransformsPack
### ResizersPack (for show=1)
### MasksPack (for show=1)
###
### This includes the following 9 operators (modeA x modeB):
### HUExHUE
### HUExSAT
### HUExVAL
### SATxHUE
### SATxSAT (This is similar to ex_vibrance() )
### SATxVAL
### VALxHUE
### VALxSAT
### VALxVAL
###
### Note:
### Include the following function definition in AvsPmod to enable sliders.
### (
### clip yuv,
### float str=1.0 (0.0 to 360.0),
### string "modeA"="HUE" ("HUE"/ "SAT"/ "VAL"),
### string "modeB"="SAT" ("HUE"/ "SAT"/ "VAL"),
### float "Delta"=180.0 (0.0 to 360.0 by 1),
### float "Q"=0.20 (0.01 to 1.00),
### float "Opacity"=1.0 (0.0 to 1.0),
### int "show"=1 (0 to 2)
### )
###
function HSVxHSV (clip c, float "str", string "modeA", string "modeB", float "Delta", float "Q", float "Opacity", bool "tv_range", int "show") {
rgb = IsRGB(c)
isy = isy(c)
ID = color_propGet(c)
bi = ID[7]
bi32= bi==32
str = Default (str, 1.0) # Strength of 'modeB' operator. 0-360 for 'V' and 'H' and 0.0-4.0 for 'S'
mA = Default (modeA, "HUE") # 'modeA' (HUE or H, SAT or S, VAL or L or V). Mask of the operator.
mB = Default (modeB, "SAT") # 'modeB' (HUE or H, SAT or S, VAL or L or V). Mode of the operator.
dA = Default (Delta, 180.0) # Degrees of 'modeA' (isolate this area. 0-255 for 'L', 0-360 for for 'H' and 0.0-100.0 for 'S')
bA = Default (Q, 0.2) # Q-factor (Bandwidth) of 'modeA'. Spread of the isolated area. 0.0-1.0
sA = Default (opacity, 1.0) # Scale of 'modeA'. Scale of the isolated area (typically to scale down since by default it's white). 0.0-1.0
tv = Default (tv_range, ID[6])
sh = Default (show, 0) # 0: disabled, 1: show histogram, 2: show mask (resized to luma dimensions) 3: show mask at original size
sm = sh > 1
si = tv ? "int" : "intf"
Assert(IsVersionOrGreater(3,7,3), "HSVxHSV: Update AviSynth+ version")
Assert(!isy && !rgb, "HSVxHSV: Only YUV format is supported")
U = ExtractU(c)
V = ExtractV(c)
Yf = ExtractY(c)
Ys = is444(c) ? Yf : Yf.BicubicResize(U.width(),U.height(),-0.5,0.25)
c4 = is444(c) ? c : c.ConverttoYUV444(matrix=ID[2],chromaresample="bicubic",param1=0,param2=0.5)
c4U = ExtractU(c4)
c4V = ExtractV(c4)
Uc = YtoUV(U,U,c4U)
Vc = YtoUV(V,V,c4V)
Y = YtoUV(Ys,Ys,Yf)
mA = mA=="H" ? "HUE" : \
mA=="S" ? "SAT" : \
mA=="V" ? "VAL" : \
mA=="L" ? "VAL" : \
mA=="LUM" ? "VAL" : mA
mB = mB=="H" ? "HUE" : \
mB=="S" ? "SAT" : \
mB=="V" ? "VAL" : \
mB=="L" ? "VAL" : \
mB=="LUM" ? "VAL" : mB
nul = mB=="SAT" ? str==1.0 : mB=="VAL" ? str==180. : str==0.0 || str==360.0