Compositor: add new node: Kuwahara filter #107015

Merged
Habib Gahbiche merged 22 commits from zazizizou/blender:com-kuwahara-filter-node into main 2023-06-08 16:14:51 +02:00
4 changed files with 26 additions and 28 deletions
Showing only changes of commit d89739f3d5 - Show all commits

View File

@ -3,6 +3,7 @@
* SPDX-License-Identifier: GPL-2.0-or-later */ * SPDX-License-Identifier: GPL-2.0-or-later */
#include "COM_KuwaharaNode.h" #include "COM_KuwaharaNode.h"
#include "COM_FastGaussianBlurOperation.h" #include "COM_FastGaussianBlurOperation.h"
#include "COM_KuwaharaAnisotropicOperation.h" #include "COM_KuwaharaAnisotropicOperation.h"
#include "COM_KuwaharaClassicOperation.h" #include "COM_KuwaharaClassicOperation.h"
@ -82,7 +83,7 @@ void KuwaharaNode::convert_to_operations(NodeConverter &converter,
converter.add_link(sobel_yy->get_output_socket(0), blur_sobel_yy->get_input_socket(0)); converter.add_link(sobel_yy->get_output_socket(0), blur_sobel_yy->get_input_socket(0));
converter.add_link(sobel_xy->get_output_socket(0), blur_sobel_xy->get_input_socket(0)); converter.add_link(sobel_xy->get_output_socket(0), blur_sobel_xy->get_input_socket(0));
/* Apply anisotropic Kuwahara filter */ /* Apply anisotropic Kuwahara filter. */
KuwaharaAnisotropicOperation *aniso = new KuwaharaAnisotropicOperation(); KuwaharaAnisotropicOperation *aniso = new KuwaharaAnisotropicOperation();
aniso->set_kernel_size(data->size + 4); aniso->set_kernel_size(data->size + 4);
converter.map_input_socket(get_input_socket(0), aniso->get_input_socket(0)); converter.map_input_socket(get_input_socket(0), aniso->get_input_socket(0));

View File

@ -3,6 +3,7 @@
* SPDX-License-Identifier: GPL-2.0-or-later */ * SPDX-License-Identifier: GPL-2.0-or-later */
#include "COM_KuwaharaAnisotropicOperation.h" #include "COM_KuwaharaAnisotropicOperation.h"
#include "BLI_math_base.hh" #include "BLI_math_base.hh"
#include "BLI_vector.hh" #include "BLI_vector.hh"
#include "IMB_colormanagement.h" #include "IMB_colormanagement.h"
@ -57,8 +58,8 @@ void KuwaharaAnisotropicOperation::execute_pixel_sampled(float output[4],
const float q = 3.0; const float q = 3.0;
const float EPS = 1.0e-10; const float EPS = 1.0e-10;
/* For now use green channel to compute orientation */ /* For now use green channel to compute orientation. */
/* todo: convert to HSV and compute orientation and strength on luminance channel */ /* TODO: convert to HSV and compute orientation and strength on luminance channel */
float tmp[4]; float tmp[4];
s_xx_reader_->read(tmp, x, y, nullptr); s_xx_reader_->read(tmp, x, y, nullptr);
const float a = tmp[1]; const float a = tmp[1];
@ -67,13 +68,13 @@ void KuwaharaAnisotropicOperation::execute_pixel_sampled(float output[4],
s_yy_reader_->read(tmp, x, y, nullptr); s_yy_reader_->read(tmp, x, y, nullptr);
const float c = tmp[1]; const float c = tmp[1];
/* Compute egenvalues of structure tensor */ /* Compute egenvalues of structure tensor. */
const double tr = a + c; const double tr = a + c;
const double discr = sqrt((a - b) * (a - b) + 4 * b * c); const double discr = sqrt((a - b) * (a - b) + 4 * b * c);
const double lambda1 = (tr + discr) / 2; const double lambda1 = (tr + discr) / 2;
const double lambda2 = (tr - discr) / 2; const double lambda2 = (tr - discr) / 2;
/* Compute orientation and its strength based on structure tensor */ /* Compute orientation and its strength based on structure tensor. */
const double orientation = 0.5 * atan2(2 * b, a - c); const double orientation = 0.5 * atan2(2 * b, a - c);
const double strength = (lambda1 == 0 && lambda2 == 0) ? const double strength = (lambda1 == 0 && lambda2 == 0) ?
0 : 0 :
@ -146,7 +147,7 @@ void KuwaharaAnisotropicOperation::execute_pixel_sampled(float output[4],
output[ch] = val; output[ch] = val;
} }
/* No changes for alpha channel */ /* No changes for alpha channel. */
image_reader_->read_sampled(tmp, x, y, sampler); image_reader_->read_sampled(tmp, x, y, sampler);
output[3] = tmp[3]; output[3] = tmp[3];
} }
@ -172,12 +173,10 @@ void KuwaharaAnisotropicOperation::update_memory_buffer_partial(MemoryBuffer *ou
const rcti &area, const rcti &area,
Span<MemoryBuffer *> inputs) Span<MemoryBuffer *> inputs)
{ {
/* /* Implementation based on Kyprianidis, Jan & Kang, Henry & Döllner, Jürgen. (2009).
Implementation based on Kyprianidis, Jan & Kang, Henry & Döllner, Jürgen. (2009). * "Image and Video Abstraction by Anisotropic Kuwahara Filtering".
"Image and Video Abstraction by Anisotropic Kuwahara Filtering". * Comput. Graph. Forum. 28. 1955-1963. 10.1111/j.1467-8659.2009.01574.x.
Comput. Graph. Forum. 28. 1955-1963. 10.1111/j.1467-8659.2009.01574.x. * Used reference implementation from lime image processing library (MIT license). */
Used reference implementation from lime image processing library (MIT license).
*/
MemoryBuffer *image = inputs[0]; MemoryBuffer *image = inputs[0];
MemoryBuffer *s_xx = inputs[1]; MemoryBuffer *s_xx = inputs[1];
@ -203,8 +202,8 @@ void KuwaharaAnisotropicOperation::update_memory_buffer_partial(MemoryBuffer *ou
const int x = it.x; const int x = it.x;
const int y = it.y; const int y = it.y;
/* For now use green channel to compute orientation */ /* For now use green channel to compute orientation. */
/* todo: convert to HSV and compute orientation and strength on luminance channel */ /* TODO: convert to HSV and compute orientation and strength on luminance channel. */
const float a = s_xx->get_value(x, y, 1); const float a = s_xx->get_value(x, y, 1);
const float b = s_xy->get_value(x, y, 1); const float b = s_xy->get_value(x, y, 1);
const float c = s_yy->get_value(x, y, 1); const float c = s_yy->get_value(x, y, 1);
@ -215,7 +214,7 @@ void KuwaharaAnisotropicOperation::update_memory_buffer_partial(MemoryBuffer *ou
const double lambda1 = (tr + discr) / 2; const double lambda1 = (tr + discr) / 2;
const double lambda2 = (tr - discr) / 2; const double lambda2 = (tr - discr) / 2;
/* Compute orientation and its strength based on structure tensor */ /* Compute orientation and its strength based on structure tensor. */
const double orientation = 0.5 * atan2(2 * b, a - c); const double orientation = 0.5 * atan2(2 * b, a - c);
const double strength = (lambda1 == 0 && lambda2 == 0) ? const double strength = (lambda1 == 0 && lambda2 == 0) ?
0 : 0 :
@ -258,9 +257,9 @@ void KuwaharaAnisotropicOperation::update_memory_buffer_partial(MemoryBuffer *ou
const double v = image->get_value(xx, yy, ch); const double v = image->get_value(xx, yy, ch);
float color[4]; float color[4];
image->read_elem(xx, yy, color); image->read_elem(xx, yy, color);
/* todo(zazizizou): only compute lum once per region */ /* TODO(zazizizou): only compute lum once per region. */
const float lum = IMB_colormanagement_get_luminance(color); const float lum = IMB_colormanagement_get_luminance(color);
/* todo(zazizizou): only compute mean for the selected region */ /* TODO(zazizizou): only compute mean for the selected region. */
mean[t] += g * v; mean[t] += g * v;
sum[t] += g * lum; sum[t] += g * lum;
var[t] += g * lum * lum; var[t] += g * lum * lum;
@ -268,7 +267,7 @@ void KuwaharaAnisotropicOperation::update_memory_buffer_partial(MemoryBuffer *ou
} }
} }
/* Calculate weighted average */ /* Calculate weighted average. */
double de = 0.0; double de = 0.0;
double nu = 0.0; double nu = 0.0;
for (int i = 0; i < n_div_; i++) { for (int i = 0; i < n_div_; i++) {
@ -288,7 +287,7 @@ void KuwaharaAnisotropicOperation::update_memory_buffer_partial(MemoryBuffer *ou
it.out[ch] = val; it.out[ch] = val;
} }
/* No changes for alpha channel */ /* No changes for alpha channel. */
it.out[3] = image->get_value(x, y, 3); it.out[3] = image->get_value(x, y, 3);
} }
} }

View File

@ -4,9 +4,7 @@
#include "COM_KuwaharaClassicOperation.h" #include "COM_KuwaharaClassicOperation.h"
zazizizou marked this conversation as resolved Outdated

No need to `extern "C"`` here, the header does it already.

Generally, we should not be adding such statements around #include statements, as it makes it very easy to break things when things move to C++. Some headers might need the linking specializer, and for those it is to be changed in the header itself.

No need to `extern "C"`` here, the header does it already. Generally, we should not be adding such statements around `#include` statements, as it makes it very easy to break things when things move to C++. Some headers might need the linking specializer, and for those it is to be changed in the header itself.
extern "C" {
#include "IMB_colormanagement.h" #include "IMB_colormanagement.h"
}
namespace blender::compositor { namespace blender::compositor {
@ -40,7 +38,7 @@ void KuwaharaClassicOperation::execute_pixel_sampled(float output[4],
float var[4] = {0.0f, 0.0f, 0.0f, 0.0f}; float var[4] = {0.0f, 0.0f, 0.0f, 0.0f};
int cnt[4] = {0, 0, 0, 0}; int cnt[4] = {0, 0, 0, 0};
/* Split surroundings of pixel into 4 overlapping regions */ /* Split surroundings of pixel into 4 overlapping regions. */
for (int dy = -kernel_size_; dy <= kernel_size_; dy++) { for (int dy = -kernel_size_; dy <= kernel_size_; dy++) {
for (int dx = -kernel_size_; dx <= kernel_size_; dx++) { for (int dx = -kernel_size_; dx <= kernel_size_; dx++) {
@ -83,7 +81,7 @@ void KuwaharaClassicOperation::execute_pixel_sampled(float output[4],
} }
} }
/* Compute region variances */ /* Compute region variances. */
for (int i = 0; i < 4; i++) { for (int i = 0; i < 4; i++) {
mean[i] = cnt[i] != 0 ? mean[i] / cnt[i] : 0.0f; mean[i] = cnt[i] != 0 ? mean[i] / cnt[i] : 0.0f;
sum[i] = cnt[i] != 0 ? sum[i] / cnt[i] : 0.0f; sum[i] = cnt[i] != 0 ? sum[i] / cnt[i] : 0.0f;
@ -92,7 +90,7 @@ void KuwaharaClassicOperation::execute_pixel_sampled(float output[4],
var[i] = var[i] > temp ? sqrt(var[i] - temp) : 0.0f; var[i] = var[i] > temp ? sqrt(var[i] - temp) : 0.0f;
} }
/* Choose the region with lowest variance */ /* Choose the region with lowest variance. */
float min_var = FLT_MAX; float min_var = FLT_MAX;
int min_index = 0; int min_index = 0;
for (int i = 0; i < 4; i++) { for (int i = 0; i < 4; i++) {
@ -132,7 +130,7 @@ void KuwaharaClassicOperation::update_memory_buffer_partial(MemoryBuffer *output
float var[4] = {0.0f, 0.0f, 0.0f, 0.0f}; float var[4] = {0.0f, 0.0f, 0.0f, 0.0f};
int cnt[4] = {0, 0, 0, 0}; int cnt[4] = {0, 0, 0, 0};
/* Split surroundings of pixel into 4 overlapping regions */ /* Split surroundings of pixel into 4 overlapping regions. */
for (int dy = -kernel_size_; dy <= kernel_size_; dy++) { for (int dy = -kernel_size_; dy <= kernel_size_; dy++) {
for (int dx = -kernel_size_; dx <= kernel_size_; dx++) { for (int dx = -kernel_size_; dx <= kernel_size_; dx++) {
@ -175,7 +173,7 @@ void KuwaharaClassicOperation::update_memory_buffer_partial(MemoryBuffer *output
} }
} }
/* Compute region variances */ /* Compute region variances. */
for (int i = 0; i < 4; i++) { for (int i = 0; i < 4; i++) {
mean[i] = cnt[i] != 0 ? mean[i] / cnt[i] : 0.0f; mean[i] = cnt[i] != 0 ? mean[i] / cnt[i] : 0.0f;
sum[i] = cnt[i] != 0 ? sum[i] / cnt[i] : 0.0f; sum[i] = cnt[i] != 0 ? sum[i] / cnt[i] : 0.0f;
@ -184,7 +182,7 @@ void KuwaharaClassicOperation::update_memory_buffer_partial(MemoryBuffer *output
var[i] = var[i] > temp ? sqrt(var[i] - temp) : 0.0f; var[i] = var[i] > temp ? sqrt(var[i] - temp) : 0.0f;
} }
/* Choose the region with lowest variance */ /* Choose the region with lowest variance. */
float min_var = FLT_MAX; float min_var = FLT_MAX;
int min_index = 0; int min_index = 0;
for (int i = 0; i < 4; i++) { for (int i = 0; i < 4; i++) {

View File

@ -9397,7 +9397,7 @@ static void def_cmp_kuwahara(StructRNA *srna)
prop = RNA_def_property(srna, "variation", PROP_ENUM, PROP_NONE); prop = RNA_def_property(srna, "variation", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_sdna(prop, NULL, "variation"); RNA_def_property_enum_sdna(prop, NULL, "variation");
RNA_def_property_enum_items(prop, variation_items); RNA_def_property_enum_items(prop, variation_items);
RNA_def_property_ui_text(prop, "", "Variation of Kuwahara filter to use."); RNA_def_property_ui_text(prop, "", "Variation of Kuwahara filter to use");
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
prop = RNA_def_property(srna, "smoothing", PROP_INT, PROP_NONE); prop = RNA_def_property(srna, "smoothing", PROP_INT, PROP_NONE);