Compositor: add new node: Kuwahara filter #107015
|
@ -336,6 +336,7 @@ compositor_node_categories = [
|
|||
NodeItem("CompositorNodeSunBeams"),
|
||||
NodeItem("CompositorNodeDenoise"),
|
||||
NodeItem("CompositorNodeAntiAliasing"),
|
||||
NodeItem("CompositorNodeKuwahara"),
|
||||
]),
|
||||
CompositorNodeCategory("CMP_OP_VECTOR", "Vector", items=[
|
||||
NodeItem("CompositorNodeNormal"),
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
* Copyright 2011 Blender Foundation. */
|
||||
zazizizou marked this conversation as resolved
Outdated
|
||||
|
||||
#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
|
|
@ -0,0 +1,22 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
* Copyright 2011 Blender Foundation. */
|
||||
zazizizou marked this conversation as resolved
Outdated
Sergey Sharybin
commented
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
|
|
@ -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
Sergey Sharybin
commented
This is a bit confusing. The kernel size is an integer in the API, and here we pass a floating point of 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?
Habib Gahbiche
commented
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
Sergey Sharybin
commented
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 :)
Habib Gahbiche
commented
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};
|
||||
Sergey Sharybin
commented
Suggest using early 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
|
|
@ -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
|
|
@ -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;
|
||||
|
|
|
@ -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
Habib Gahbiche
commented
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?
Sergey Sharybin
commented
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 You can see example in the Subsurf modifier:
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);
```
Habib Gahbiche
commented
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;
|
||||
|
|
|
@ -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", "" )
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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
Sergey Sharybin
commented
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);
|
||||
}
|
Use the current year for the new files added. This also applies to other new files in this PR.