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
5 changed files with 55 additions and 49 deletions
Showing only changes of commit b76e5025bf - Show all commits

View File

@ -19,7 +19,7 @@ void KuwaharaNode::convert_to_operations(NodeConverter &converter,
switch (data->variation) {
case CMP_NODE_KUWAHARA_CLASSIC: {
KuwaharaClassicOperation *operation = new KuwaharaClassicOperation();
operation->set_kernel_size(data->kernel_size);
operation->set_kernel_size(data->size);
converter.add_operation(operation);
converter.map_input_socket(get_input_socket(0), operation->get_input_socket(0));
@ -61,8 +61,8 @@ void KuwaharaNode::convert_to_operations(NodeConverter &converter,
converter.add_link(sobel_x->get_output_socket(0), sobel_xy->get_input_socket(0));
converter.add_link(sobel_y->get_output_socket(0), sobel_xy->get_input_socket(1));
/* blurring for more robustness. */
const int sigma = data->sigma;
/* Blurring for more robustness. */
const int sigma = data->smoothing;
auto blur_sobel_xx = new FastGaussianBlurOperation();
auto blur_sobel_yy = new FastGaussianBlurOperation();
@ -80,13 +80,9 @@ 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));
// For now, orientation is part of kuwahara operation.
// todo: implement orientation as a separate operation
// auto orientation = new OrientationOperation(); // OrientationOperation
/* Apply anisotropic Kuwahara filter */
KuwaharaAnisotropicOperation *aniso = new KuwaharaAnisotropicOperation();
aniso->set_kernel_size(data->kernel_size);
aniso->set_kernel_size(data->size);
converter.map_input_socket(get_input_socket(0), aniso->get_input_socket(0));
converter.add_operation(aniso);
@ -96,11 +92,6 @@ void KuwaharaNode::convert_to_operations(NodeConverter &converter,
converter.map_output_socket(get_output_socket(0), aniso->get_output_socket(0));
// For debug. Todo: remove
// converter.map_output_socket(get_output_socket(1), sobel_xx->get_output_socket(0));
// converter.map_output_socket(get_output_socket(2), blur_sobel_xx->get_output_socket(0));
// converter.map_output_socket(get_output_socket(3), blur_sobel_xy->get_output_socket(0));
break;
}
}

View File

@ -13,7 +13,7 @@ namespace blender::compositor {
KuwaharaAnisotropicOperation::KuwaharaAnisotropicOperation()
{
this->add_input_socket(DataType::Color);
this->add_input_socket(DataType::Color);
this->add_input_socket(DataType::Color);
this->add_input_socket(DataType::Color);
this->add_input_socket(DataType::Color);
@ -44,7 +44,7 @@ void KuwaharaAnisotropicOperation::execute_pixel_sampled(float output[4],
float y,
PixelSampler sampler)
{
/* Not implemented */
/* Not implemented */
}
void KuwaharaAnisotropicOperation::set_kernel_size(int kernel_size)
@ -65,7 +65,7 @@ void KuwaharaAnisotropicOperation::update_memory_buffer_partial(MemoryBuffer *ou
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).
Used reference implementation from lime image processing library (MIT license).
*/
MemoryBuffer *image = inputs[0];
@ -80,12 +80,11 @@ void KuwaharaAnisotropicOperation::update_memory_buffer_partial(MemoryBuffer *ou
BLI_assert(image->get_width() == s_xy->get_width());
BLI_assert(image->get_height() == s_xy->get_height());
const int n_div = 8; // recommended by authors in original paper
const int n_div = 8; // recommended by authors in original paper
const float angle = 2.0 * M_PI / n_div;
const float q = 3.0;
const float EPS = 1.0e-10;
for (BuffersIterator<float> it = output->iterate_with(inputs, area); !it.is_end(); ++it) {
const int x = it.x;
const int y = it.y;
@ -96,18 +95,19 @@ void KuwaharaAnisotropicOperation::update_memory_buffer_partial(MemoryBuffer *ou
const float b = s_xy->get_value(x, y, 1);
const float c = s_yy->get_value(x, y, 1);
/* Compute egenvalues of structure tensor */
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 lambda2 = (tr - discr) / 2;
/* 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 : (lambda1 - lambda2) / (lambda1 + lambda2);
const double strength = (lambda1 == 0 && lambda2 == 0) ?
0 :
(lambda1 - lambda2) / (lambda1 + lambda2);
for(int ch = 0; ch < 3; ch++) {
for (int ch = 0; ch < 3; ch++) {
Vector<float> mean(n_div, 0.0f);
Vector<float> sum(n_div, 0.0f);
@ -120,7 +120,8 @@ void KuwaharaAnisotropicOperation::update_memory_buffer_partial(MemoryBuffer *ou
for (int dy = -kernel_size_; dy <= kernel_size_; dy++) {
for (int dx = -kernel_size_; dx <= kernel_size_; dx++) {
if (dx == 0 && dy == 0) continue;
if (dx == 0 && dy == 0)
continue;
// rotate and scale the kernel. This is the "anisotropic" part.
int dx2 = static_cast<int>(sx * (cos(theta) * dx - sin(theta) * dy));
@ -174,5 +175,4 @@ void KuwaharaAnisotropicOperation::update_memory_buffer_partial(MemoryBuffer *ou
}
}
} // namespace blender::compositor

View File

@ -877,9 +877,9 @@ typedef struct NodeBilateralBlurData {
} NodeBilateralBlurData;
typedef struct NodeKuwaharaData {
short kernel_size;
short size;
short variation;
int sigma;
int smoothing;
} NodeKuwaharaData;
typedef struct NodeAntiAliasingData {

View File

@ -9254,11 +9254,11 @@ static void def_cmp_kuwahara(StructRNA *srna)
{0, NULL, 0, NULL, NULL},
};
prop = RNA_def_property(srna, "kernel_size", PROP_INT, PROP_NONE);
RNA_def_property_int_sdna(prop, NULL, "kernel_size");
prop = RNA_def_property(srna, "size", PROP_INT, PROP_NONE);
RNA_def_property_int_sdna(prop, NULL, "size");
RNA_def_property_ui_range(prop, 4, 50, 1, -1);
RNA_def_property_ui_text(
prop, "Kernel Size", "Kernel size of filter. Larger values give stronger stylized effect");
prop, "Size", "Size of filter. Larger values give stronger stylized effect");
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
prop = RNA_def_property(srna, "variation", PROP_ENUM, PROP_NONE);
@ -9267,13 +9267,13 @@ static void def_cmp_kuwahara(StructRNA *srna)
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, "sigma", PROP_INT, PROP_NONE);
RNA_def_property_int_sdna(prop, NULL, "sigma");
prop = RNA_def_property(srna, "smoothing", PROP_INT, PROP_NONE);
RNA_def_property_int_sdna(prop, NULL, "smoothing");
RNA_def_property_ui_range(prop, 0, 50, 1, -1);
RNA_def_property_ui_text(
prop,
"Sigma",
"Smoothing degree before applying filter. Higher values remove details and give smoother edges");
RNA_def_property_ui_text(prop,
"Smoothing",
"Smoothing degree before applying filter. Higher values remove details "
"and give smoother edges");
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
}

View File

@ -5,10 +5,8 @@
* \ingroup cmpnodes
*/
//#include "COM_shader_node.hh"
#include "COM_node_operation.hh"
#include "node_composite_util.hh"
//#include "node_composite_util.hh"
/* **************** Kuwahara ******************** */
@ -22,11 +20,6 @@ static void cmp_node_kuwahara_declare(NodeDeclarationBuilder &b)
.default_value({1.0f, 1.0f, 1.0f, 1.0f})
.compositor_domain_priority(0);
b.add_output<decl::Color>(N_("Image"));
// For debug. Todo:remove
// b.add_output<decl::Color>(N_("Sobel x"));
// b.add_output<decl::Color>(N_("Sobel xx blurred"));
// b.add_output<decl::Color>(N_("Sobel xy blurred"));
}
static void node_composit_init_kuwahara(bNodeTree * /*ntree*/, bNode *node)
@ -34,7 +27,9 @@ static void node_composit_init_kuwahara(bNodeTree * /*ntree*/, bNode *node)
NodeKuwaharaData *data = MEM_cnew<NodeKuwaharaData>(__func__);
node->storage = data;
data->kernel_size = 4;
/* Set defaults. */
data->size = 4;
data->smoothing = 2;
}
static void node_composit_buts_kuwahara(uiLayout *layout, bContext * /*C*/, PointerRNA *ptr)
@ -44,15 +39,33 @@ static void node_composit_buts_kuwahara(uiLayout *layout, bContext * /*C*/, Poin
col = uiLayoutColumn(layout, false);
uiItemR(col, ptr, "variation", 0, nullptr, ICON_NONE);
uiItemR(col, ptr, "kernel_size", 0, nullptr, ICON_NONE);
uiItemR(col, ptr, "size", 0, nullptr, ICON_NONE);
const int variation = RNA_enum_get(ptr, "variation");
if(variation == CMP_NODE_KUWAHARA_ANISOTROPIC) {
uiItemR(col, ptr, "sigma", 0, nullptr, ICON_NONE);
if (variation == CMP_NODE_KUWAHARA_ANISOTROPIC) {
uiItemR(col, ptr, "smoothing", 0, nullptr, ICON_NONE);
}
}
using namespace blender::realtime_compositor;
class ConvertKuwaharaOperation : public NodeOperation {
public:
using NodeOperation::NodeOperation;
void execute() override
{
get_input("Image").pass_through(get_result("Image"));
context().set_info_message("Viewport compositor setup not fully supported");
}
};
static NodeOperation *get_compositor_operation(Context &context, DNode node)
{
return new ConvertKuwaharaOperation(context, node);
}
} // namespace blender::nodes::node_composite_kuwahara_cc
void register_node_type_cmp_kuwahara()
@ -61,13 +74,15 @@ void register_node_type_cmp_kuwahara()
static bNodeType ntype;
cmp_node_type_base(&ntype, CMP_NODE_KUWAHARA, "Kuwahara", NODE_CLASS_OP_COLOR);
cmp_node_type_base(&ntype, CMP_NODE_KUWAHARA, "Kuwahara", NODE_CLASS_OP_FILTER);
ntype.declare = file_ns::cmp_node_kuwahara_declare;
ntype.draw_buttons = file_ns::node_composit_buts_kuwahara;
ntype.initfunc = file_ns::node_composit_init_kuwahara;
node_type_storage(
&ntype, "NodeKuwaharaData", node_free_standard_storage, node_copy_standard_storage);
// ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node;
ntype.get_compositor_operation = file_ns::get_compositor_operation;
ntype.realtime_compositor_unsupported_message = N_(
"Node not supported in the Viewport compositor");
nodeRegisterType(&ntype);
}