Blender V4.3
COM_KuwaharaClassicOperation.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2023 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
5#include "BLI_math_base.hh"
6#include "BLI_math_vector.hh"
8
11
12namespace blender::compositor {
13
24
26 const rcti &area,
28{
29 MemoryBuffer *image = inputs[0];
30 if (image->is_a_single_elem()) {
31 copy_v4_v4(output->get_elem(0, 0), image->get_elem(0, 0));
32 return;
33 }
34 MemoryBuffer *size_image = inputs[1];
35 MemoryBuffer *sat = inputs[2];
36 MemoryBuffer *sat_squared = inputs[3];
37
38 int width = image->get_width();
39 int height = image->get_height();
40
41 for (BuffersIterator<float> it = output->iterate_with(inputs, area); !it.is_end(); ++it) {
42 const int x = it.x;
43 const int y = it.y;
44
45 float4 mean_of_color[4] = {float4(0.0f), float4(0.0f), float4(0.0f), float4(0.0f)};
46 float4 mean_of_squared_color[4] = {float4(0.0f), float4(0.0f), float4(0.0f), float4(0.0f)};
47 int quadrant_pixel_count[4] = {0, 0, 0, 0};
48
49 const float size = *size_image->get_elem(x, y);
50 const int kernel_size = int(math::max(0.0f, size));
51
52 /* For high radii, we accelerate the filter using a summed area table, making the filter
53 * execute in constant time as opposed to having quadratic complexity. Except if high precision
54 * is enabled, since summed area tables are less precise. */
55 if (!high_precision_ && size > 5.0f) {
56 for (int q = 0; q < 4; q++) {
57 /* A fancy expression to compute the sign of the quadrant q. */
58 int2 sign = int2((q % 2) * 2 - 1, ((q / 2) * 2 - 1));
59
60 int2 lower_bound = int2(x, y) -
61 int2(sign.x > 0 ? 0 : kernel_size, sign.y > 0 ? 0 : kernel_size);
62 int2 upper_bound = int2(x, y) +
63 int2(sign.x < 0 ? 0 : kernel_size, sign.y < 0 ? 0 : kernel_size);
64
65 /* Limit the quadrants to the image bounds. */
66 int2 image_bound = int2(width, height) - int2(1);
67 int2 corrected_lower_bound = math::min(image_bound, math::max(int2(0, 0), lower_bound));
68 int2 corrected_upper_bound = math::min(image_bound, math::max(int2(0, 0), upper_bound));
69 int2 region_size = corrected_upper_bound - corrected_lower_bound + int2(1, 1);
70 quadrant_pixel_count[q] = region_size.x * region_size.y;
71
72 rcti kernel_area;
73 kernel_area.xmin = corrected_lower_bound[0];
74 kernel_area.ymin = corrected_lower_bound[1];
75 kernel_area.xmax = corrected_upper_bound[0];
76 kernel_area.ymax = corrected_upper_bound[1];
77
78 mean_of_color[q] = summed_area_table_sum(sat, kernel_area);
79 mean_of_squared_color[q] = summed_area_table_sum(sat_squared, kernel_area);
80 }
81 }
82 else {
83 /* Split surroundings of pixel into 4 overlapping regions. */
84 for (int dy = -kernel_size; dy <= kernel_size; dy++) {
85 for (int dx = -kernel_size; dx <= kernel_size; dx++) {
86
87 int xx = x + dx;
88 int yy = y + dy;
89 if (xx < 0 || yy < 0 || xx >= image->get_width() || yy >= image->get_height()) {
90 continue;
91 }
92
93 float4 color;
94 image->read_elem(xx, yy, &color.x);
95
96 if (dx >= 0 && dy >= 0) {
97 const int quadrant_index = 0;
98 mean_of_color[quadrant_index] += color;
99 mean_of_squared_color[quadrant_index] += color * color;
100 quadrant_pixel_count[quadrant_index]++;
101 }
102
103 if (dx <= 0 && dy >= 0) {
104 const int quadrant_index = 1;
105 mean_of_color[quadrant_index] += color;
106 mean_of_squared_color[quadrant_index] += color * color;
107 quadrant_pixel_count[quadrant_index]++;
108 }
109
110 if (dx <= 0 && dy <= 0) {
111 const int quadrant_index = 2;
112 mean_of_color[quadrant_index] += color;
113 mean_of_squared_color[quadrant_index] += color * color;
114 quadrant_pixel_count[quadrant_index]++;
115 }
116
117 if (dx >= 0 && dy <= 0) {
118 const int quadrant_index = 3;
119 mean_of_color[quadrant_index] += color;
120 mean_of_squared_color[quadrant_index] += color * color;
121 quadrant_pixel_count[quadrant_index]++;
122 }
123 }
124 }
125 }
126
127 /* Choose the region with lowest variance. */
128 float min_var = FLT_MAX;
129 int min_index = 0;
130 for (int i = 0; i < 4; i++) {
131 mean_of_color[i] /= quadrant_pixel_count[i];
132 mean_of_squared_color[i] /= quadrant_pixel_count[i];
133 float4 color_variance = mean_of_squared_color[i] - mean_of_color[i] * mean_of_color[i];
134
135 float variance = math::dot(color_variance.xyz(), float3(1.0f));
136 if (variance < min_var) {
137 min_var = variance;
138 min_index = i;
139 }
140 }
141
142 it.out[0] = mean_of_color[min_index].x;
143 it.out[1] = mean_of_color[min_index].y;
144 it.out[2] = mean_of_color[min_index].z;
145 it.out[3] = mean_of_color[min_index].w; /* Also apply filter to alpha channel. */
146 }
147}
148
149} // namespace blender::compositor
MINLINE void copy_v4_v4(float r[4], const float a[4])
void update_memory_buffer_partial(MemoryBuffer *output, const rcti &area, Span< MemoryBuffer * > inputs) override
a MemoryBuffer contains access to the data
const int get_width() const
get the width of this MemoryBuffer
void add_output_socket(DataType datatype)
void add_input_socket(DataType datatype, ResizeMode resize_mode=ResizeMode::Center)
draw_view push_constant(Type::INT, "radiance_src") .push_constant(Type capture_info_buf storage_buf(1, Qualifier::READ, "ObjectBounds", "bounds_buf[]") .push_constant(Type draw_view int
float4 summed_area_table_sum(MemoryBuffer *buffer, const rcti &area)
typename BuffersIteratorBuilder< T >::Iterator BuffersIterator
T dot(const QuaternionBase< T > &a, const QuaternionBase< T > &b)
T min(const T &a, const T &b)
T max(const T &a, const T &b)
VecBase< float, 4 > float4
VecBase< int32_t, 2 > int2
#define FLT_MAX
Definition stdcycles.h:14
VecBase< T, 3 > xyz() const
int ymin
int ymax
int xmin
int xmax