Compositor: add new node: Kuwahara filter #107015
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue