Blender V5.0
recursive_gaussian_blur.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2024 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
5#include "BLI_math_vector.hh"
6
7#include "COM_context.hh"
8#include "COM_result.hh"
9
14
15namespace blender::compositor {
16
17/* Compute the Gaussian sigma from the radius, where the radius is in pixels. Blender's filter is
18 * truncated at |x| > 3 * sigma as can be seen in the R_FILTER_GAUSS case of the RE_filter_value
19 * function, so we divide by three to get the approximate sigma value. Further, ensure the radius
20 * is at least 1 since recursive Gaussian implementations can't handle zero radii. */
22{
23 return math::max(float2(1.0f), radius) / 3.0f;
24}
25
26/* Apply a recursive Gaussian blur algorithm on the input based on the general method outlined
27 * in the following paper:
28 *
29 * Hale, Dave. "Recursive gaussian filters." CWP-546 (2006).
30 *
31 * In particular, based on the table in Section 5 Conclusion, for very low radius blur, we use a
32 * direct separable Gaussian convolution. For medium blur radius, we use the fourth order IIR
33 * Deriche filter based on the following paper:
34 *
35 * Deriche, Rachid. Recursively implementating the Gaussian and its derivatives. Diss. INRIA,
36 * 1993.
37 *
38 * For high radius blur, we use the fourth order IIR Van Vliet filter based on the following paper:
39 *
40 * Van Vliet, Lucas J., Ian T. Young, and Piet W. Verbeek. "Recursive Gaussian derivative
41 * filters." Proceedings. Fourteenth International Conference on Pattern Recognition (Cat. No.
42 * 98EX170). Vol. 1. IEEE, 1998.
43 *
44 * That's because direct convolution is faster and more accurate for very low radius, while the
45 * Deriche filter is more accurate for medium blur radius, while Van Vliet is more accurate for
46 * high blur radius. The criteria suggested by the paper is a sigma value threshold of 3 and 32 for
47 * the Deriche and Van Vliet filters respectively, which we apply on the larger of the two
48 * dimensions. */
50 const Result &input,
52 const float2 &radius)
53{
54 /* The radius is in pixel units, while both recursive implementations expect the sigma value of
55 * the Gaussian function. */
56 const float2 sigma = compute_sigma_from_radius(radius);
57
58 if (math::reduce_max(sigma) < 3.0f) {
59 symmetric_separable_blur(context, input, output, radius);
60 return;
61 }
62
63 if (math::reduce_max(sigma) < 32.0f) {
64 deriche_gaussian_blur(context, input, output, sigma);
65 return;
66 }
67
68 van_vliet_gaussian_blur(context, input, output, sigma);
69}
70
71} // namespace blender::compositor
#define input
#define output
void symmetric_separable_blur(Context &context, const Result &input, Result &output, const float2 &radius, const int filter_type=R_FILTER_GAUSS)
void recursive_gaussian_blur(Context &context, const Result &input, Result &output, const float2 &radius)
void van_vliet_gaussian_blur(Context &context, const Result &input, Result &output, const float2 &sigma)
void deriche_gaussian_blur(Context &context, const Result &input, Result &output, const float2 &sigma)
static float2 compute_sigma_from_radius(float2 radius)
T reduce_max(const VecBase< T, Size > &a)
T max(const T &a, const T &b)
VecBase< float, 2 > float2