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
15 changed files with 394 additions and 0 deletions
Showing only changes of commit c1fb210be9 - Show all commits

View File

@ -336,6 +336,7 @@ compositor_node_categories = [
NodeItem("CompositorNodeSunBeams"),
NodeItem("CompositorNodeDenoise"),
NodeItem("CompositorNodeAntiAliasing"),
NodeItem("CompositorNodeKuwahara"),
]),
CompositorNodeCategory("CMP_OP_VECTOR", "Vector", items=[
NodeItem("CompositorNodeNormal"),

View File

@ -1333,6 +1333,7 @@ void BKE_nodetree_remove_layer_n(struct bNodeTree *ntree, struct Scene *scene, i
#define CMP_NODE_INPAINT 272
#define CMP_NODE_DESPECKLE 273
#define CMP_NODE_ANTIALIASING 274
#define CMP_NODE_KUWAHARA 275
#define CMP_NODE_GLARE 301
#define CMP_NODE_TONEMAP 302

View File

@ -321,6 +321,8 @@ if(WITH_COMPOSITOR_CPU)
nodes/COM_FilterNode.h
nodes/COM_InpaintNode.cc
nodes/COM_InpaintNode.h
nodes/COM_KuwaharaNode.h
nodes/COM_KuwaharaNode.cc
nodes/COM_PosterizeNode.cc
nodes/COM_PosterizeNode.h
@ -348,6 +350,8 @@ if(WITH_COMPOSITOR_CPU)
operations/COM_GaussianXBlurOperation.h
operations/COM_GaussianYBlurOperation.cc
operations/COM_GaussianYBlurOperation.h
operations/COM_KuwaharaOperation.h
operations/COM_KuwaharaOperation.cc
operations/COM_MovieClipAttributeOperation.cc
operations/COM_MovieClipAttributeOperation.h
operations/COM_MovieDistortionOperation.cc

View File

@ -61,6 +61,7 @@
#include "COM_InvertNode.h"
#include "COM_KeyingNode.h"
#include "COM_KeyingScreenNode.h"
#include "COM_KuwaharaNode.h"
#include "COM_LensDistortionNode.h"
#include "COM_LuminanceMatteNode.h"
#include "COM_MapRangeNode.h"
@ -435,6 +436,9 @@ Node *COM_convert_bnode(bNode *b_node)
case CMP_NODE_COMBINE_XYZ:
node = new CombineXYZNode(b_node);
break;
case CMP_NODE_KUWAHARA:
node = new KuwaharaNode(b_node);
break;
}
return node;
}

View File

@ -0,0 +1,24 @@
/* SPDX-License-Identifier: GPL-2.0-or-later
* Copyright 2011 Blender Foundation. */
zazizizou marked this conversation as resolved Outdated

Use the current year for the new files added. This also applies to other new files in this PR.

Use the current year for the new files added. This also applies to other new files in this PR.
#include "COM_KuwaharaNode.h"
#include "COM_KuwaharaOperation.h"
namespace blender::compositor {
void KuwaharaNode::convert_to_operations(NodeConverter &converter,
const CompositorContext & /*context*/) const
{
const bNode *node = this->get_bnode();
const NodeKuwaharaData *data = (const NodeKuwaharaData *)node->storage;
KuwaharaOperation *operation = new KuwaharaOperation();
operation->set_kernel_size(data->kernel_size);
operation->set_variation(data->variation);
converter.add_operation(operation);
converter.map_input_socket(get_input_socket(0), operation->get_input_socket(0));
converter.map_output_socket(get_output_socket(0), operation->get_output_socket());
}
} // namespace blender::compositor

View File

@ -0,0 +1,22 @@
/* SPDX-License-Identifier: GPL-2.0-or-later
* Copyright 2011 Blender Foundation. */
zazizizou marked this conversation as resolved Outdated

2023

2023
#pragma once
#include "COM_Node.h"
namespace blender::compositor {
/**
* \brief KuwaharaNode
* \ingroup Node
*/
class KuwaharaNode : public Node {
public:
KuwaharaNode(bNode *editor_node) : Node(editor_node) {}
void convert_to_operations(NodeConverter &converter,
const CompositorContext &context) const override;
};
} // namespace blender::compositor

View File

@ -0,0 +1,159 @@
/* SPDX-License-Identifier: GPL-2.0-or-later
* Copyright 2011 Blender Foundation. */
#include "COM_KuwaharaOperation.h"
namespace blender::compositor {
KuwaharaOperation::KuwaharaOperation()
{
this->add_input_socket(DataType::Color);
this->add_output_socket(DataType::Color);
this->set_kernel_size(4.4f);
zazizizou marked this conversation as resolved Outdated

This is a bit confusing. The kernel size is an integer in the API, and here we pass a floating point of 4.4f. Should this be just 4?

This is a bit confusing. The kernel size is an integer in the API, and here we pass a floating point of `4.4f`. Should this be just 4?

Right, the compiler shows a warning indeed. I was trying to understand where defaults get set. Will fix later

Right, the compiler shows a warning indeed. I was trying to understand where defaults get set. Will fix later
this->flags_.is_fullframe_operation = true;
}
void KuwaharaOperation::init_execution()
{
image_reader_ = this->get_input_socket_reader(0);
}
void KuwaharaOperation::deinit_execution()
{
image_reader_ = nullptr;
}
void KuwaharaOperation::execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler)
{
float input_value[4];
image_reader_->read_sampled(input_value, x, y, sampler);
output[0] = input_value[0] + 1.0;
output[1] = input_value[1] + 2.0;
output[2] = input_value[2] + 3.0;
output[3] = input_value[3] + 4.0;
zazizizou marked this conversation as resolved Outdated

Is this some stub implementation for tiled system, or is it some sort of step of the actual algorithm?

If it's the former, then it's all fine for now. If that's the latter I'd like to understand how it works :)

Is this some stub implementation for tiled system, or is it some sort of step of the actual algorithm? If it's the former, then it's all fine for now. If that's the latter I'd like to understand how it works :)

This is a stub implementation. I was just making sure I understood the channel ordering correctly. Will remove to avoid confusion.

This is a stub implementation. I was just making sure I understood the channel ordering correctly. Will remove to avoid confusion.
}
void KuwaharaOperation::set_kernel_size(int kernel_size)
{
kernel_size_ = kernel_size;
}
int KuwaharaOperation::get_kernel_size()
{
return kernel_size_;
}
void KuwaharaOperation::set_variation(int variation)
{
variation_ = variation;
}
int KuwaharaOperation::get_variation()
{
return variation_;
}
void KuwaharaOperation::update_memory_buffer(MemoryBuffer *output,
const rcti &area,
Span<MemoryBuffer *> inputs)
{
MemoryBuffer *image = inputs[0];
const int bwidth = area.xmax - area.xmin;
const int bheight = area.ymax - area.ymin;
// for (int y = 0; y < bheight; y++)
// {
// for (int x = 0; x < bwidth; x++)
// {
// output->get_value(x, y, 3) = 1;
// }
// }
for (int ch = 0; ch < 3; ch++)
{
for (int y = 0; y < bheight; y++)
{
for (int x = 0; x < bwidth; x++)
{
float sum[4] = {0.0f, 0.0f, 0.0f, 0.0f};
float var[4] = {0.0f, 0.0f, 0.0f, 0.0f};

Suggest using early continue and un-indent the block.

Suggest using early `continue` and un-indent the block.
int cnt[4] = {0, 0, 0, 0};
for (int dy = -kernel_size_; dy <= kernel_size_; dy++)
{
for (int dx = -kernel_size_; dx <= kernel_size_; dx++)
{
int xx = x + dx;
int yy = y + dy;
if (xx >= 0 && yy >= 0 && xx < area.xmax && yy < area.ymax)
{
//double v = temp.at<float>(yy, xx * dim + c);
float v;
v = image->get_value(xx, yy, ch);
if (dx <= 0 && dy <= 0)
{
sum[0] += v;
var[0] += v * v;
cnt[0]++;
}
if (dx >= 0 && dy <= 0)
{
sum[1] += v;
var[1] += v * v;
cnt[1]++;
}
if (dx <= 0 && dy >= 0)
{
sum[2] += v;
var[2] += v * v;
cnt[2]++;
}
if (dx >= 0 && dy >= 0)
{
sum[3] += v;
var[3] += v * v;
cnt[3]++;
}
}
}
}
std::vector<std::pair<double, int>> vec;
for (int i = 0; i < 4; i++)
{
sum[i] = cnt[i] != 0 ? sum[i] / cnt[i] : 0.0f;
var[i] = cnt[i] != 0 ? var[i] / cnt[i] : 0.0f;
var[i] = sqrt(var[i] - sum[i] * sum[i]);
vec.push_back(std::make_pair(var[i], i));
}
sort(vec.begin(), vec.end());
std::cout << "x: " << x << " y: " << y << std::endl;
for (int i = 0; i < 4; i++)
{
std::cout << "\t" << vec[i].first << " " << vec[i].second << std::endl;
}
//out.at<float>(y, x * dim + c) = static_cast<float>(sum[vec[0].second]);
float res = static_cast<float>(sum[vec[0].second]);
output->get_value(x, y, ch) = res;
}
}
}
}
void KuwaharaOperation::get_area_of_interest(const int /*input_idx*/,
const rcti & /*output_area*/,
rcti &r_input_area)
{
r_input_area = this->get_canvas();
}
} // namespace blender::compositor

View File

@ -0,0 +1,47 @@
/* SPDX-License-Identifier: GPL-2.0-or-later
* Copyright 2011 Blender Foundation. */
#pragma once
#include "COM_NodeOperation.h"
namespace blender::compositor {
class KuwaharaOperation : public NodeOperation {
SocketReader *image_reader_;
int kernel_size_;
int variation_;
public:
KuwaharaOperation();
void init_execution() override;
void deinit_execution() override;
void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override;
void set_kernel_size(int kernel_size);
int get_kernel_size();
void set_variation(int variation);
int get_variation();
void update_memory_buffer(MemoryBuffer *output,
const rcti &area,
Span<MemoryBuffer *> inputs) override;
void get_area_of_interest(const int input_idx,
const rcti & output_area,
rcti &r_input_area) override;
//
// bool determine_depending_area_of_interest(rcti *input,
// ReadBufferOperation *read_operation,
// rcti *output) override;
//
// void update_memory_buffer_partial(MemoryBuffer *output,
// const rcti &area,
// Span<MemoryBuffer *> inputs) override;
};
} // namespace blender::compositor

View File

@ -876,6 +876,12 @@ typedef struct NodeBilateralBlurData {
char _pad[2];
} NodeBilateralBlurData;
typedef struct NodeKuwaharaData {
short kernel_size;
short variation;
char _pad[4];
} NodeKuwaharaData;
typedef struct NodeAntiAliasingData {
float threshold;
float contrast_limit;

View File

@ -9242,6 +9242,31 @@ static void def_cmp_denoise(StructRNA *srna)
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
}
static void def_cmp_kuwahara(StructRNA *srna)
{
PropertyRNA *prop;
RNA_def_struct_sdna_from(srna, "NodeKuwaharaData", "storage");
static const EnumPropertyItem variation_items[] = {
{0, "CLASSIC", 0, "Classic", "Fast but less accurate variation"},
{1, "ANISOTROPIC", 0, "Anisotropic", "Accurate but slower variation"},
{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");
RNA_def_property_ui_text(
zazizizou marked this conversation as resolved Outdated

This doesn't seem to work. User can still set values outside the specified range. This is also the case for Vector Blur. Is there another way to specify UI range?

This doesn't seem to work. User can still set values outside the specified range. This is also the case for Vector Blur. Is there another way to specify UI range?

The UI range is the soft limit. It applies when an artist drags the slider, but it is still possible to click on the property and type a value which is within a hard limit.

The hard limit is controlled by RNA_def_property_range.

You can see example in the Subsurf modifier:

RNA_def_property_range(prop, 0, 11);
RNA_def_property_ui_range(prop, 0, 6, 1, -1);
The UI range is the soft limit. It applies when an artist drags the slider, but it is still possible to click on the property and type a value which is within a hard limit. The hard limit is controlled by `RNA_def_property_range`. You can see example in the Subsurf modifier: ``` RNA_def_property_range(prop, 0, 11); RNA_def_property_ui_range(prop, 0, 6, 1, -1); ```

This did solve it, thanks!

This did solve it, thanks!
prop, "Kernel Size", "Kernel size of filter. The larger the stronger the effect");
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
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_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
}
static void def_cmp_antialiasing(StructRNA *srna)
{
PropertyRNA *prop;

View File

@ -223,6 +223,7 @@ DefNode(CompositorNode, CMP_NODE_COMBINE_XYZ, 0, "COMBIN
DefNode(CompositorNode, CMP_NODE_SEPARATE_XYZ, 0, "SEPARATE_XYZ", SeparateXYZ, "Separate XYZ", "" )
DefNode(CompositorNode, CMP_NODE_SEPARATE_COLOR, def_cmp_combsep_color, "SEPARATE_COLOR", SeparateColor, "Separate Color", "" )
DefNode(CompositorNode, CMP_NODE_COMBINE_COLOR, def_cmp_combsep_color, "COMBINE_COLOR", CombineColor, "Combine Color", "" )
DefNode(CompositorNode, CMP_NODE_KUWAHARA, def_cmp_kuwahara, "KUWAHARA", Kuwahara, "Kuwahara", "" )
DefNode(TextureNode, TEX_NODE_OUTPUT, def_tex_output, "OUTPUT", Output, "Output", "" )
DefNode(TextureNode, TEX_NODE_CHECKER, 0, "CHECKER", Checker, "Checker", "" )

View File

@ -76,6 +76,7 @@ set(SRC
nodes/node_composite_invert.cc
nodes/node_composite_keying.cc
nodes/node_composite_keyingscreen.cc
nodes/node_composite_kuwahara.cc
nodes/node_composite_lensdist.cc
nodes/node_composite_levels.cc
nodes/node_composite_luma_matte.cc

View File

@ -62,6 +62,7 @@ void register_composite_nodes()
register_node_type_cmp_invert();
register_node_type_cmp_keying();
register_node_type_cmp_keyingscreen();
register_node_type_cmp_kuwahara();
register_node_type_cmp_lensdist();
register_node_type_cmp_luma_matte();
register_node_type_cmp_map_range();

View File

@ -58,6 +58,7 @@ void register_node_type_cmp_inpaint();
void register_node_type_cmp_invert();
void register_node_type_cmp_keying();
void register_node_type_cmp_keyingscreen();
void register_node_type_cmp_kuwahara();
void register_node_type_cmp_lensdist();
void register_node_type_cmp_luma_matte();
void register_node_type_cmp_map_range();

View File

@ -0,0 +1,97 @@
/* SPDX-License-Identifier: GPL-2.0-or-later
* Copyright 2020 Blender Foundation */
/** \file
* \ingroup cmpnodes
*/
//#include "COM_shader_node.hh"
zazizizou marked this conversation as resolved Outdated

Remove the commented out code.

Remove the commented out code.
#include "COM_node_operation.hh"
#include "node_composite_util.hh"
/* **************** Kuwahara ******************** */
namespace blender::nodes::node_composite_kuwahara_cc {
NODE_STORAGE_FUNCS(NodeKuwaharaData)
static void cmp_node_kuwahara_declare(NodeDeclarationBuilder &b)
{
b.add_input<decl::Color>(N_("Image"))
.default_value({1.0f, 1.0f, 1.0f, 1.0f})
.compositor_domain_priority(0);
b.add_output<decl::Color>(N_("Image"));
}
static void node_composit_init_kuwahara(bNodeTree * /*ntree*/, bNode *node)
{
NodeKuwaharaData *data = MEM_cnew<NodeKuwaharaData>(__func__);
node->storage = data;
data->kernel_size = 4.1f;
}
static void node_composit_buts_kuwahara(uiLayout *layout, bContext * /*C*/, PointerRNA *ptr)
{
uiLayout *col;
col = uiLayoutColumn(layout, false);
uiItemR(col, ptr, "variation", 0, nullptr, ICON_NONE);
uiItemR(col, ptr, "kernel_size", 0, nullptr, ICON_NONE);
}
//using namespace blender::realtime_compositor;
//
//class KuwaharaShaderNode : 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");
// }
// using ShaderNode::ShaderNode;
//
// void compile(GPUMaterial *material) override
// {
// GPUNodeStack *inputs = get_inputs_array();
// GPUNodeStack *outputs = get_outputs_array();
//
// GPU_stack_link(material, &bnode(), "node_composite_kuwahara", inputs, outputs);
// }
//};
//
//static NodeOperation *get_compositor_operation(Context *context, DNode node)
//{
// return new KuwaharaOperation(context, node);
//}
//static ShaderNode *get_compositor_shader_node(DNode node)
//{
// return new KuwaharaShaderNode(node);
//}
} // namespace blender::nodes::node_composite_kuwahara_cc
void register_node_type_cmp_kuwahara()
{
namespace file_ns = blender::nodes::node_composite_kuwahara_cc;
static bNodeType ntype;
cmp_node_type_base(&ntype, CMP_NODE_KUWAHARA, "Kuwahara", NODE_CLASS_OP_COLOR);
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;
nodeRegisterType(&ntype);
}