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 */
#include "COM_KuwaharaNode.h"
#include "COM_FastGaussianBlurOperation.h"
#include "COM_KuwaharaAnisotropicOperation.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_xy->get_output_socket(0), blur_sobel_xy->get_input_socket(0));
/* Apply anisotropic Kuwahara filter */
/* Apply anisotropic Kuwahara filter. */
KuwaharaAnisotropicOperation *aniso = new KuwaharaAnisotropicOperation();
aniso->set_kernel_size(data->size + 4);
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 */
#include "COM_KuwaharaAnisotropicOperation.h"
#include "BLI_math_base.hh"
#include "BLI_vector.hh"
#include "IMB_colormanagement.h"
@ -57,8 +58,8 @@ void KuwaharaAnisotropicOperation::execute_pixel_sampled(float output[4],
const float q = 3.0;
const float EPS = 1.0e-10;
/* For now use green channel to compute orientation */
/* todo: convert to HSV and compute orientation and strength on luminance channel */
/* For now use green channel to compute orientation. */
/* TODO: convert to HSV and compute orientation and strength on luminance channel */
float tmp[4];
s_xx_reader_->read(tmp, x, y, nullptr);
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);
const float c = tmp[1];
/* Compute egenvalues of structure tensor */
/* Compute egenvalues of structure tensor. */
const double tr = a + c;
const double discr = sqrt((a - b) * (a - b) + 4 * b * c);
const double lambda1 = (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 strength = (lambda1 == 0 && lambda2 == 0) ?
0 :
@ -146,7 +147,7 @@ void KuwaharaAnisotropicOperation::execute_pixel_sampled(float output[4],
output[ch] = val;
}
/* No changes for alpha channel */
/* No changes for alpha channel. */
image_reader_->read_sampled(tmp, x, y, sampler);
output[3] = tmp[3];
}
@ -172,12 +173,10 @@ void KuwaharaAnisotropicOperation::update_memory_buffer_partial(MemoryBuffer *ou
const rcti &area,
Span<MemoryBuffer *> inputs)
{
/*
Implementation based on Kyprianidis, Jan & Kang, Henry & Döllner, Jürgen. (2009).
"Image and Video Abstraction by Anisotropic Kuwahara Filtering".
Comput. Graph. Forum. 28. 1955-1963. 10.1111/j.1467-8659.2009.01574.x.
Used reference implementation from lime image processing library (MIT license).
*/
/* Implementation based on Kyprianidis, Jan & Kang, Henry & Döllner, Jürgen. (2009).
* "Image and Video Abstraction by Anisotropic Kuwahara Filtering".
* Comput. Graph. Forum. 28. 1955-1963. 10.1111/j.1467-8659.2009.01574.x.
* Used reference implementation from lime image processing library (MIT license). */
MemoryBuffer *image = inputs[0];
MemoryBuffer *s_xx = inputs[1];
@ -203,8 +202,8 @@ void KuwaharaAnisotropicOperation::update_memory_buffer_partial(MemoryBuffer *ou
const int x = it.x;
const int y = it.y;
/* For now use green channel to compute orientation */
/* todo: convert to HSV and compute orientation and strength on luminance channel */
/* For now use green channel to compute orientation. */
/* TODO: convert to HSV and compute orientation and strength on luminance channel. */
const float a = s_xx->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);
@ -215,7 +214,7 @@ void KuwaharaAnisotropicOperation::update_memory_buffer_partial(MemoryBuffer *ou
const double lambda1 = (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 strength = (lambda1 == 0 && lambda2 == 0) ?
0 :
@ -258,9 +257,9 @@ void KuwaharaAnisotropicOperation::update_memory_buffer_partial(MemoryBuffer *ou
const double v = image->get_value(xx, yy, ch);
float color[4];
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);
/* todo(zazizizou): only compute mean for the selected region */
/* TODO(zazizizou): only compute mean for the selected region. */
mean[t] += g * v;
sum[t] += g * 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 nu = 0.0;
for (int i = 0; i < n_div_; i++) {
@ -288,7 +287,7 @@ void KuwaharaAnisotropicOperation::update_memory_buffer_partial(MemoryBuffer *ou
it.out[ch] = val;
}
/* No changes for alpha channel */
/* No changes for alpha channel. */
it.out[3] = image->get_value(x, y, 3);
}
}

View File

@ -4,9 +4,7 @@
#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"
}
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};
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 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++) {
mean[i] = cnt[i] != 0 ? mean[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;
}
/* Choose the region with lowest variance */
/* Choose the region with lowest variance. */
float min_var = FLT_MAX;
int min_index = 0;
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};
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 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++) {
mean[i] = cnt[i] != 0 ? mean[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;
}
/* Choose the region with lowest variance */
/* Choose the region with lowest variance. */
float min_var = FLT_MAX;
int min_index = 0;
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);
RNA_def_property_enum_sdna(prop, NULL, "variation");
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");
prop = RNA_def_property(srna, "smoothing", PROP_INT, PROP_NONE);