Compositor: add new node: Kuwahara filter #107015
|
@ -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));
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,9 +4,7 @@
|
||||||
|
|
||||||
#include "COM_KuwaharaClassicOperation.h"
|
#include "COM_KuwaharaClassicOperation.h"
|
||||||
|
|
||||||
zazizizou marked this conversation as resolved
Outdated
|
|||||||
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++) {
|
||||||
|
|
|
@ -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);
|
||||||
|
|
Loading…
Reference in New Issue
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.