VapourSynth-llvmexpr
Loading...
Searching...
No Matches
nl-means.expr
Go to the documentation of this file.
1# Copyright (C) 2025 yuygfgg
2#
3# This file is part of Vapoursynth-llvmexpr.
4#
5# Vapoursynth-llvmexpr is free software: you can redistribute it and/or modify
6# it under the terms of the GNU General Public License as published by
7# the Free Software Foundation, either version 3 of the License, or
8# (at your option) any later version.
9#
10# Vapoursynth-llvmexpr is distributed in the hope that it will be useful,
11# but WITHOUT ANY WARRANTY; without even the implied warranty of
12# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13# GNU General Public License for more details.
14#
15# You should have received a copy of the GNU General Public License
16# along with Vapoursynth-llvmexpr. If not, see <https://www.gnu.org/licenses/>.
17
18@ifndef __EXPR__
19@error This expr should be used in Expr mode
20@endif
21
22@if __INPUT_NUM__ != 1
23@error This expr requires exactly one input clip
24@endif
25
26@requires meta
27
28# `a` parameter of knlmeanscl
29@ifndef PATCH_RADIUS
30@define PATCH_RADIUS 3
31@endif
32# `s` parameter of knlmeanscl
33@ifndef SEARCH_RADIUS
34@define SEARCH_RADIUS 4
35@endif
36# `h` parameter of knlmeanscl
37@ifndef FILTER_STRENGTH
38@define FILTER_STRENGTH 1.2
39@endif
40# `wmode` parameter of knlmeanscl
41@ifndef WEIGHT_MODE
42@define WEIGHT_MODE 0
43@endif
44
45_ = ASSERT_CONST(PATCH_RADIUS > 0, patch_radius, _must_be_positive)
46_ = ASSERT_CONST(SEARCH_RADIUS > 0, search_radius, _must_be_positive)
47_ = ASSERT_CONST(FILTER_STRENGTH > 0, filter_strength, _must_be_positive)
48_ = ASSERT_CONST(WEIGHT_MODE >= 0 && WEIGHT_MODE <= 3, weight_mode, _must_be_0_1_2_or_3)
49
50@define H_SQUARED (FILTER_STRENGTH * FILTER_STRENGTH)
51
52@define PATCH_DIAMETER (2 * PATCH_RADIUS + 1)
53@define PATCH_SIZE (PATCH_DIAMETER * PATCH_DIAMETER)
54
55@define _GET_PX(i) ((i) % PATCH_DIAMETER - PATCH_RADIUS)
56@define _GET_PY(i) ((i) / PATCH_DIAMETER - PATCH_RADIUS)
57
58@define _GET_SQ_DIFF(i) ((dyn($x, cx1 + _GET_PX(i), cy1 + _GET_PY(i)) - dyn($x, cx2 + _GET_PX(i), cy2 + _GET_PY(i))) ** 2)
59
60function patch_distance(cx1, cy1, cx2, cy2) {
61 return JOIN(PATCH_SIZE, _GET_SQ_DIFF, +)
62}
63
64@define SEARCH_DIAMETER (2 * SEARCH_RADIUS + 1)
65@define SEARCH_AREA (SEARCH_DIAMETER * SEARCH_DIAMETER)
66
67@define _GET_SX(i) ($X + (i) % SEARCH_DIAMETER - SEARCH_RADIUS)
68@define _GET_SY(i) ($Y + (i) / SEARCH_DIAMETER - SEARCH_RADIUS)
69
70@if WEIGHT_MODE == 0
71@define CALCULATE_WEIGHT(dist) (exp(-(dist) / H_SQUARED))
72@else
73@if WEIGHT_MODE == 1
74@define CALCULATE_WEIGHT(dist) (max(0.0, 1.0 - (dist) / H_SQUARED))
75@else
76@if WEIGHT_MODE == 2
77@define CALCULATE_WEIGHT(dist) ((max(0.0, 1.0 - (dist) / H_SQUARED)) * (max(0.0, 1.0 - (dist) / H_SQUARED)))
78@else
79@if WEIGHT_MODE == 3
80@define _BASE_WEIGHT(dist) (max(0.0, 1.0 - (dist) / H_SQUARED))
81@define _WEIGHT2(dist) (_BASE_WEIGHT(dist) * _BASE_WEIGHT(dist))
82@define _WEIGHT4(dist) (_WEIGHT2(dist) * _WEIGHT2(dist))
83@define CALCULATE_WEIGHT(dist) (_WEIGHT4(dist) * _WEIGHT4(dist))
84@endif
85@endif
86@endif
87@endif
88
89@define _MAIN_LOOP_BODY(i) sx = _GET_SX(i); sy = _GET_SY(i); distance = patch_distance($X, $Y, sx, sy); weight = CALCULATE_WEIGHT(distance); search_pixel_value = dyn($x, sx, sy); total_weight = total_weight + weight; weighted_sum = weighted_sum + (search_pixel_value * weight);
90
91weighted_sum = 0.0
92total_weight = 0.0
93
94UNROLL(SEARCH_AREA, _MAIN_LOOP_BODY)
95
96RESULT = total_weight > 0 ? weighted_sum / total_weight : $x