1# Copyright (C) 2025 yuygfgg
3# This file is part of Vapoursynth-llvmexpr.
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.
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.
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/>.
19@error This expr should be used in Expr mode
23@error This expr requires exactly one input clip
28# `a` parameter of knlmeanscl
32# `s` parameter of knlmeanscl
34@define SEARCH_RADIUS 4
36# `h` parameter of knlmeanscl
37@ifndef FILTER_STRENGTH
38@define FILTER_STRENGTH 1.2
40# `wmode` parameter of knlmeanscl
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)
50@define H_SQUARED (FILTER_STRENGTH * FILTER_STRENGTH)
52@define PATCH_DIAMETER (2 * PATCH_RADIUS + 1)
53@define PATCH_SIZE (PATCH_DIAMETER * PATCH_DIAMETER)
55@define _GET_PX(i) ((i) % PATCH_DIAMETER - PATCH_RADIUS)
56@define _GET_PY(i) ((i) / PATCH_DIAMETER - PATCH_RADIUS)
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)
60function patch_distance(cx1, cy1, cx2, cy2) {
61 return JOIN(PATCH_SIZE, _GET_SQ_DIFF, +)
64@define SEARCH_DIAMETER (2 * SEARCH_RADIUS + 1)
65@define SEARCH_AREA (SEARCH_DIAMETER * SEARCH_DIAMETER)
67@define _GET_SX(i) ($X + (i) % SEARCH_DIAMETER - SEARCH_RADIUS)
68@define _GET_SY(i) ($Y + (i) / SEARCH_DIAMETER - SEARCH_RADIUS)
71@define CALCULATE_WEIGHT(dist) (exp(-(dist) / H_SQUARED))
74@define CALCULATE_WEIGHT(dist) (max(0.0, 1.0 - (dist) / H_SQUARED))
77@define CALCULATE_WEIGHT(dist) ((max(0.0, 1.0 - (dist) / H_SQUARED)) * (max(0.0, 1.0 - (dist) / H_SQUARED)))
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))
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);
94UNROLL(SEARCH_AREA, _MAIN_LOOP_BODY)
96RESULT = total_weight > 0 ? weighted_sum / total_weight : $x