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
2 changed files with 49 additions and 41 deletions
Showing only changes of commit f5a92f9fd6 - Show all commits

View File

@ -43,12 +43,15 @@ void KuwaharaAnisotropicOperation::execute_pixel_sampled(float output[4],
float y,
PixelSampler sampler)
{
BLI_assert(this->get_width() == s_xx_reader_->get_width());
BLI_assert(this->get_height() == s_xx_reader_->get_height());
BLI_assert(this->get_width() == s_yy_reader_->get_width());
BLI_assert(this->get_height() == s_yy_reader_->get_height());
BLI_assert(this->get_width() == s_xy_reader_->get_width());
BLI_assert(this->get_height() == s_xy_reader_->get_height());
const int width = this->get_width();
const int height = this->get_width();
zazizizou marked this conversation as resolved Outdated

Should we consider something from:

  • Pass-through the input as-is
  • Output magenta

So that if someone saves file with the Kuwahara node in it and someone opens it without enabling full-frame compositor we do not leave the output uninitialized?

Should we consider something from: - Pass-through the input as-is - Output magenta So that if someone saves file with the Kuwahara node in it and someone opens it without enabling full-frame compositor we do not leave the output uninitialized?
BLI_assert(width == s_xx_reader_->get_width());
BLI_assert(height == s_xx_reader_->get_height());
BLI_assert(width == s_yy_reader_->get_width());
BLI_assert(height == s_yy_reader_->get_height());
BLI_assert(width == s_xy_reader_->get_width());
BLI_assert(height == s_xy_reader_->get_height());
/* Values recommended by authors in original paper. */
const float angle = 2.0 * M_PI / n_div_;
@ -93,8 +96,8 @@ void KuwaharaAnisotropicOperation::execute_pixel_sampled(float output[4],
continue;
/* Rotate and scale the kernel. This is the "anisotropic" part. */
int dx2 = (int)(sx * (cos(theta) * dx - sin(theta) * dy));
int dy2 = (int)(sy * (sin(theta) * dx + cos(theta) * dy));
int dx2 = int(sx * (cos(theta) * dx - sin(theta) * dy));
int dy2 = int(sy * (sin(theta) * dx + cos(theta) * dy));
int xx = x + dx2;
int yy = y + dy2;
@ -102,20 +105,20 @@ void KuwaharaAnisotropicOperation::execute_pixel_sampled(float output[4],
if (xx < 0) {
zazizizou marked this conversation as resolved Outdated

It might help making it an explicit const int height = this->get_height() outside of the loop. I am not sure compiler is smart enough to do it for us.
Although, also not suer it will give measurable time impact, but still feels like a good thing to do.

It might help making it an explicit `const int height = this->get_height()` outside of the loop. I am not sure compiler is smart enough to do it for us. Although, also not suer it will give measurable time impact, but still feels like a good thing to do.
xx = 0;
}
if (xx >= this->get_width()) {
xx = this->get_width() - 1;
if (xx >= width) {
xx = width - 1;
}
if (yy < 0) {
yy = 0;
}
if (yy >= this->get_height()) {
yy = this->get_height() - 1;
if (yy >= height) {
yy = height - 1;
}
double ddx2 = (double)dx2;
double ddy2 = (double)dy2;
double theta = atan2(ddy2, ddx2) + M_PI;
int t = static_cast<int>(floor(theta / angle)) % n_div_;
const double ddx2 = double(dx2);
zazizizou marked this conversation as resolved Outdated

For the arithmetic types you can do int(foo) https://wiki.blender.org/wiki/Style_Guide/C_Cpp#C.2B.2B_Type_Cast

Probably will make code a bit easier to read.

For the arithmetic types you can do `int(foo)` https://wiki.blender.org/wiki/Style_Guide/C_Cpp#C.2B.2B_Type_Cast Probably will make code a bit easier to read.
const double ddy2 = double(dy2);
const double theta = atan2(ddy2, ddx2) + M_PI;
const int t = int(floor(theta / angle)) % n_div_;
double d2 = dx2 * dx2 + dy2 * dy2;
double g = exp(-d2 / (2.0 * kernel_size_));
float color[4];
@ -135,9 +138,10 @@ void KuwaharaAnisotropicOperation::execute_pixel_sampled(float output[4],
double de = 0.0;
zazizizou marked this conversation as resolved
Review

In other performance critical areas we do

const float weight_inv = 1.0f / weight;
a = ... foo * weight_inv ..;
b = ... bar * weight_inv ..;
c = ... baz * weight_inv ..;

Again, not something which i know for sure will show performance impact, but might worth doing so nevertheless.

In other performance critical areas we do ``` const float weight_inv = 1.0f / weight; a = ... foo * weight_inv ..; b = ... bar * weight_inv ..; c = ... baz * weight_inv ..; ``` Again, not something which i know for sure will show performance impact, but might worth doing so nevertheless.
double nu = 0.0;
for (int i = 0; i < n_div_; i++) {
mean[i] = weight[i] != 0 ? mean[i] / weight[i] : 0.0;
sum[i] = weight[i] != 0 ? sum[i] / weight[i] : 0.0;
var[i] = weight[i] != 0 ? var[i] / weight[i] : 0.0;
double weight_inv = 1.0 / weight[i];
mean[i] = weight[i] != 0 ? mean[i] * weight_inv : 0.0;
sum[i] = weight[i] != 0 ? sum[i] * weight_inv : 0.0;
var[i] = weight[i] != 0 ? var[i] * weight_inv : 0.0;
var[i] = var[i] - sum[i] * sum[i];
var[i] = var[i] > FLT_EPSILON ? sqrt(var[i]) : FLT_EPSILON;
double w = powf(var[i], -q);
@ -147,7 +151,6 @@ void KuwaharaAnisotropicOperation::execute_pixel_sampled(float output[4],
}
double val = nu > EPS ? de / nu : 0.0;
CLAMP_MAX(val, 1.0f);
output[ch] = val;
}
@ -189,12 +192,15 @@ void KuwaharaAnisotropicOperation::update_memory_buffer_partial(MemoryBuffer *ou
MemoryBuffer *s_yy = inputs[2];
MemoryBuffer *s_xy = inputs[3];
BLI_assert(image->get_width() == s_xx->get_width());
BLI_assert(image->get_height() == s_xx->get_height());
BLI_assert(image->get_width() == s_yy->get_width());
BLI_assert(image->get_height() == s_yy->get_height());
BLI_assert(image->get_width() == s_xy->get_width());
BLI_assert(image->get_height() == s_xy->get_height());
const int width = image->get_width();
const int height = image->get_width();
BLI_assert(width == s_xx->get_width());
BLI_assert(height == s_xx->get_height());
BLI_assert(width == s_yy->get_width());
BLI_assert(height == s_yy->get_height());
BLI_assert(width == s_xy->get_width());
BLI_assert(height == s_xy->get_height());
/* Values recommended by authors in original paper. */
const float angle = 2.0 * M_PI / n_div_;
@ -240,8 +246,8 @@ void KuwaharaAnisotropicOperation::update_memory_buffer_partial(MemoryBuffer *ou
continue;
/* Rotate and scale the kernel. This is the "anisotropic" part. */
int dx2 = (int)(sx * (cos(theta) * dx - sin(theta) * dy));
int dy2 = (int)(sy * (sin(theta) * dx + cos(theta) * dy));
int dx2 = int(sx * (cos(theta) * dx - sin(theta) * dy));
int dy2 = int(sy * (sin(theta) * dx + cos(theta) * dy));
int xx = x + dx2;
int yy = y + dy2;
@ -249,23 +255,23 @@ void KuwaharaAnisotropicOperation::update_memory_buffer_partial(MemoryBuffer *ou
if (xx < 0) {
xx = 0;
}
if (xx >= image->get_width()) {
xx = image->get_width() - 1;
if (xx >= width) {
xx = width - 1;
}
if (yy < 0) {
yy = 0;
}
if (yy >= image->get_height()) {
yy = image->get_height() - 1;
if (yy >= height) {
yy = height - 1;
}
double ddx2 = (double)dx2;
double ddy2 = (double)dy2;
double theta = atan2(ddy2, ddx2) + M_PI;
int t = static_cast<int>(floor(theta / angle)) % n_div_;
const double ddx2 = double(dx2);
const double ddy2 = double(dy2);
const double theta = atan2(ddy2, ddx2) + M_PI;
const int t = int(floor(theta / angle)) % n_div_;
double d2 = dx2 * dx2 + dy2 * dy2;
double g = exp(-d2 / (2.0 * kernel_size_));
double v = image->get_value(xx, yy, ch);
const double v = image->get_value(xx, yy, ch);
float color[4];
image->read_elem(xx, yy, color);
/* todo(zazizizou): only compute lum once per region */
@ -282,9 +288,10 @@ void KuwaharaAnisotropicOperation::update_memory_buffer_partial(MemoryBuffer *ou
double de = 0.0;
double nu = 0.0;
for (int i = 0; i < n_div_; i++) {
mean[i] = weight[i] != 0 ? mean[i] / weight[i] : 0.0;
sum[i] = weight[i] != 0 ? sum[i] / weight[i] : 0.0;
var[i] = weight[i] != 0 ? var[i] / weight[i] : 0.0;
double weight_inv = 1.0 / weight[i];
mean[i] = weight[i] != 0 ? mean[i] * weight_inv : 0.0;
sum[i] = weight[i] != 0 ? sum[i] * weight_inv : 0.0;
var[i] = weight[i] != 0 ? var[i] * weight_inv : 0.0;
var[i] = var[i] - sum[i] * sum[i];
var[i] = var[i] > FLT_EPSILON ? sqrt(var[i]) : FLT_EPSILON;
double w = powf(var[i], -q);
@ -294,7 +301,6 @@ void KuwaharaAnisotropicOperation::update_memory_buffer_partial(MemoryBuffer *ou
}
double val = nu > EPS ? de / nu : 0.0;
CLAMP_MAX(val, 1.0f);
it.out[ch] = val;
}

View File

@ -9423,6 +9423,7 @@ static void def_cmp_kuwahara(StructRNA *srna)
prop = RNA_def_property(srna, "size", PROP_INT, PROP_NONE);
RNA_def_property_int_sdna(prop, NULL, "size");
RNA_def_property_range(prop, 1.0, 100.0);
RNA_def_property_ui_range(prop, 1, 100, 1, -1);
RNA_def_property_ui_text(
prop, "Size", "Size of filter. Larger values give stronger stylized effect");
@ -9436,6 +9437,7 @@ static void def_cmp_kuwahara(StructRNA *srna)
prop = RNA_def_property(srna, "smoothing", PROP_INT, PROP_NONE);
RNA_def_property_int_sdna(prop, NULL, "smoothing");
RNA_def_property_range(prop, 0.0, 50.0);
RNA_def_property_ui_range(prop, 0, 50, 1, -1);
RNA_def_property_ui_text(prop,
"Smoothing",