[gegl] workshop: add a better gaussian-blur implementation, work in progress
- From: Téo Mazars <teom src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gegl] workshop: add a better gaussian-blur implementation, work in progress
- Date: Thu, 5 Dec 2013 21:16:38 +0000 (UTC)
commit b3557301766d1aa3e3a62859968c3c1b898b7e37
Author: Massimo Valentini <mvalentini src gnome org>
Date: Fri Jul 5 20:32:56 2013 +0200
workshop: add a better gaussian-blur implementation, work in progress
- the fir case is missing for small std-dev
- the implementation doesn't allow infinite plane input
- the ouptput extent doesn't grow anymore
operations/workshop/Makefile.am | 2 +
operations/workshop/gaussian-blur-iir.c | 71 ++++++
operations/workshop/gblur-1d.c | 367 +++++++++++++++++++++++++++++++
3 files changed, 440 insertions(+), 0 deletions(-)
---
diff --git a/operations/workshop/Makefile.am b/operations/workshop/Makefile.am
index d770a39..1f1c70b 100644
--- a/operations/workshop/Makefile.am
+++ b/operations/workshop/Makefile.am
@@ -20,6 +20,8 @@ op_LTLIBRARIES = \
demosaic-simple.la \
disc-percentile.la \
ditto.la \
+ gaussian-blur-iir.la \
+ gblur-1d.la \
hstack.la \
kuwahara.la \
linear-gradient.la \
diff --git a/operations/workshop/gaussian-blur-iir.c b/operations/workshop/gaussian-blur-iir.c
new file mode 100644
index 0000000..57b86e6
--- /dev/null
+++ b/operations/workshop/gaussian-blur-iir.c
@@ -0,0 +1,71 @@
+/* This file is an image processing operation for GEGL
+ * GEGL is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * GEGL is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GEGL; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * Copyright 2013 Massimo Valentini <mvalentini src gnome org>
+ */
+
+#include "config.h"
+#include <glib/gi18n-lib.h>
+#include <math.h>
+
+#ifdef GEGL_CHANT_PROPERTIES
+
+gegl_chant_enum (abyss_policy, _("Abyss policy"), GeglAbyssPolicy,
+ gegl_abyss_policy, GEGL_ABYSS_CLAMP, _(""))
+gegl_chant_double_ui (std_dev_x, _("Horizontal Std. Dev."), 0.5, 1000.0, 4.0, 0.5, 1000.0,
+ 2, _("Standard deviation (spatial scale factor)"))
+gegl_chant_double_ui (std_dev_y, _("Vertical Std. Dev."), 0.5, 1000.0, 4.0, 0.5, 1000.0,
+ 2, _("Standard deviation (spatial scale factor)"))
+
+#else
+
+#define GEGL_CHANT_TYPE_META
+#define GEGL_CHANT_C_FILE "gaussian-blur-iir.c"
+
+#include "gegl-chant.h"
+
+static void
+attach (GeglOperation *operation)
+{
+ GeglNode *gegl = operation->node;
+ GeglNode *output = gegl_node_get_output_proxy (gegl, "output");
+ GeglNode *vblur = gegl_node_new_child (gegl, "operation", "gegl:gblur-1d", "orientation", 1,
"abyss-policy", 0, NULL);
+ GeglNode *hblur = gegl_node_new_child (gegl, "operation", "gegl:gblur-1d", "abyss-policy", 0, NULL);
+ GeglNode *input = gegl_node_get_input_proxy (gegl, "input");
+
+ gegl_node_link_many (input, hblur, vblur, output, NULL);
+ gegl_operation_meta_redirect (operation, "std-dev-x" , hblur, "std-dev");
+ gegl_operation_meta_redirect (operation, "abyss-policy", hblur, "abyss-policy");
+ gegl_operation_meta_redirect (operation, "std-dev-y" , vblur, "std-dev");
+ gegl_operation_meta_redirect (operation, "abyss-policy", vblur, "abyss-policy");
+}
+
+static void
+gegl_chant_class_init (GeglChantClass *klass)
+{
+ GeglOperationClass *operation_class;
+
+ operation_class = GEGL_OPERATION_CLASS (klass);
+
+ operation_class->attach = attach;
+
+ gegl_operation_class_set_keys (operation_class,
+ "name", "gegl:gaussian-blur-iir",
+ "categories", "blur",
+ "description", _("Performs an averaging of neighboring pixels with the "
+ "normal distribution as weighting"),
+ NULL);
+}
+
+#endif
diff --git a/operations/workshop/gblur-1d.c b/operations/workshop/gblur-1d.c
new file mode 100644
index 0000000..7dabeaf
--- /dev/null
+++ b/operations/workshop/gblur-1d.c
@@ -0,0 +1,367 @@
+/* This file is an image processing operation for GEGL
+ * GEGL is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * GEGL is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GEGL; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * Copyright 2006 Dominik Ernst <dernst gmx de>
+ * Copyright 2013 Massimo Valentini <mvalentini src gnome org>
+ *
+ * Recursive Gauss IIR Filter as described by Young / van Vliet
+ * in "Signal Processing 44 (1995) 139 - 151"
+ *
+ * NOTE: The IIR filter should not be used for radius < 0.5, since it
+ * becomes very inaccurate.
+ */
+
+#include "config.h"
+#include <glib/gi18n-lib.h>
+
+
+#ifdef GEGL_CHANT_PROPERTIES
+#if 0
+gegl_chant_register_enum (gegl_gblur_1d_policy)
+ enum_value (GEGL_BLUR_1D_ABYSS_NONE, "None")
+ enum_value (GEGL_BLUR_1D_ABYSS_CLAMP, "Clamp")
+// enum_value (GEGL_BLUR_1D_ABYSS_LOOP,
+ enum_value (GEGL_BLUR_1D_ABYSS_BLACK, "Black")
+ enum_value (GEGL_BLUR_1D_ABYSS_WHITE, "White")
+gegl_chant_register_enum_end (GeglGblur1dPolicy)
+#endif
+gegl_chant_register_enum (gegl_gblur_1d_orientation)
+ enum_value (GEGL_BLUR_1D_HORIZONTAL, "Horizontal")
+ enum_value (GEGL_BLUR_1D_VERTICAL, "Vertical")
+gegl_chant_register_enum_end (GeglGblur1dOrientation)
+
+gegl_chant_double_ui (std_dev, _("Size"),
+ 0.5, 1000.0, 4.0, 0.5, 1000.0, 2,
+ _("Standard deviation "
+ "(multiply by ~2 to get radius)"))
+gegl_chant_enum (orientation, _("Orientation"),
+ GeglGblur1dOrientation, gegl_gblur_1d_orientation,
+ GEGL_BLUR_1D_HORIZONTAL,
+ _("The orientation of the blur - hor/ver"))
+gegl_chant_enum (abyss_policy, _("Abyss policy"), GeglAbyssPolicy,
+ gegl_abyss_policy, GEGL_ABYSS_CLAMP, _(""))
+#else
+
+#define GEGL_CHANT_TYPE_FILTER
+#define GEGL_CHANT_C_FILE "gblur-1d.c"
+
+#include "gegl-chant.h"
+#include <math.h>
+#include <stdio.h>
+
+static void
+iir_young_find_constants (gfloat sigma,
+ gdouble *b,
+ gdouble (*m)[3])
+{
+ const gdouble q = sigma >= 2.5 ?
+ 0.98711 * sigma - 0.96330 :
+ 3.97156 - 4.14554 * sqrt (1 - 0.26891 * sigma);
+
+ const gdouble b0 = 1.57825 + q*(2.44413 + q*(1.4281 + q*0.422205));
+ const gdouble b1 = q*(2.44413 + q*(2.85619 + q*1.26661));
+ const gdouble b2 = - q* q*(1.4281 + q*1.26661);
+ const gdouble b3 = q* q* q*0.422205;
+
+ const double a1 = b1 / b0;
+ const double a2 = b2 / b0;
+ const double a3 = b3 / b0;
+ const gdouble c = 1. / ((1+a1-a2+a3) * (1+a2+(a1-a3)*a3));
+
+ m[0][0] = c * (-a3*(a1+a3)-a2 + 1);
+ m[0][1] = c * (a3+a1)*(a2+a3*a1);
+ m[0][2] = c * a3*(a1+a3*a2);
+
+ m[1][0] = c * (a1+a3*a2);
+ m[1][1] = c * (1-a2)*(a2+a3*a1);
+ m[1][2] = c * a3*(1-a3*a1-a3*a3-a2);
+
+ m[2][0] = c * (a3*a1+a2+a1*a1-a2*a2);
+ m[2][1] = c * (a1*a2+a3*a2*a2-a1*a3*a3-a3*a3*a3-a3*a2+a3);
+ m[2][2] = c * a3*(a1+a3*a2);
+
+ b[0] = 1. - (b1 + b2 + b3) / b0;
+ b[1] = a1;
+ b[2] = a2;
+ b[3] = a3;
+}
+
+static void
+fix_right_boundary (gdouble *buf,
+ gdouble (*m)[3],
+ const gdouble uplus)
+{
+ gdouble u[3] = { buf[-1] - uplus, buf[-2] - uplus, buf[-3] - uplus };
+ gint i, k;
+
+ for (i = 0; i < 3; i++)
+ {
+ gdouble tmp = 0.;
+
+ for (k = 0; k < 3; k++)
+ tmp += m[i][k] * u[k];
+
+ buf[i] = tmp + uplus;
+ }
+}
+
+static inline void
+iir_young_blur_1D (gfloat *buf,
+ gdouble *tmp,
+ const gdouble *b,
+ gdouble (*m)[3],
+ const gint len,
+ const gint nc,
+ GeglAbyssPolicy policy)
+{
+ gfloat white[4] = { 1, 1, 1, 1 };
+ gfloat black[4] = { 0, 0, 0, 1 };
+ gfloat none[4] = { 0, };
+ gfloat *uplus, *iminus;
+ gint i, j, c;
+
+ switch (policy)
+ {
+ case GEGL_ABYSS_CLAMP: default:
+ iminus = &buf[nc * 3]; uplus = &buf[nc * (len + 2)]; break;
+
+ case GEGL_ABYSS_NONE:
+ iminus = uplus = &none[0]; break;
+
+ case GEGL_ABYSS_WHITE:
+ iminus = uplus = &white[0]; break;
+
+ case GEGL_ABYSS_BLACK:
+ iminus = uplus = &black[nc == 2 ? 2 : 0]; break;
+ }
+
+ for (c = 0; c < nc; ++c)
+ {
+ for (i = 0; i < 3; ++i)
+ tmp[i] = iminus[c];
+
+ for (i = 3; i < 3 + len; ++i)
+ {
+ tmp[i] = buf[nc * i + c] * b[0];
+
+ for (j = 1; j < 4; ++j)
+ tmp[i] += b[j] * tmp[i - j];
+ }
+
+ fix_right_boundary (&tmp[3 + len], m, uplus[c]);
+
+ for (i = 3 + len - 1; 3 <= i; --i)
+ {
+ gdouble tt = 0.;
+
+ for (j = 0; j < 4; ++j)
+ tt += b[j] * tmp[i + j];
+
+ buf[nc * i + c] = tmp[i] = tt;
+ }
+ }
+}
+
+static void
+iir_young_hor_blur (GeglBuffer *src,
+ const GeglRectangle *rect,
+ GeglBuffer *dst,
+ const gdouble *b,
+ gdouble (*m)[3],
+ GeglAbyssPolicy policy,
+ const Babl *format)
+{
+ GeglRectangle cur_row = *rect;
+ const gint nc = babl_format_get_n_components (format);
+ gfloat *row = g_new (gfloat, (3 + rect->width + 3) * nc);
+ gdouble *tmp = g_new (gdouble, (3 + rect->width + 3));
+ gint v;
+
+ cur_row.height = 1;
+
+ for (v = 0; v < rect->height; v++)
+ {
+ cur_row.y = rect->y + v;
+
+ gegl_buffer_get (src, &cur_row, 1.0, format, &row[3 * nc],
+ GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
+
+ iir_young_blur_1D (row, tmp, b, m, rect->width, nc, policy);
+
+ gegl_buffer_set (dst, &cur_row, 0, format, &row[3 * nc],
+ GEGL_AUTO_ROWSTRIDE);
+ }
+
+ g_free (tmp);
+ g_free (row);
+}
+
+static void
+iir_young_ver_blur (GeglBuffer *src,
+ const GeglRectangle *rect,
+ GeglBuffer *dst,
+ const gdouble *b,
+ gdouble (*m)[3],
+ GeglAbyssPolicy policy,
+ const Babl *format)
+{
+ GeglRectangle cur_col = *rect;
+ const gint nc = babl_format_get_n_components (format);
+ gfloat *col = g_new (gfloat, (3 + rect->height + 3) * nc);
+ gdouble *tmp = g_new (gdouble, (3 + rect->height + 3));
+ gint i;
+
+ cur_col.width = 1;
+
+ for (i = 0; i < rect->width; i++)
+ {
+ cur_col.x = rect->x + i;
+
+ gegl_buffer_get (src, &cur_col, 1.0, format, &col[3 * nc],
+ GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
+
+ iir_young_blur_1D (col, tmp, b, m, rect->height, nc, policy);
+
+ gegl_buffer_set (dst, &cur_col, 0, format, &col[3 * nc],
+ GEGL_AUTO_ROWSTRIDE);
+ }
+
+ g_free (tmp);
+ g_free (col);
+}
+
+static void
+gegl_gblur_1d_prepare (GeglOperation *operation)
+{
+ const Babl *src_format = gegl_operation_get_source_format (operation, "input");
+ const char *format = "RaGaBaA float";
+
+ if (src_format)
+ {
+ const Babl *model = babl_format_get_model (src_format);
+
+ if (model == babl_model ("RGB") || model == babl_model ("R'G'B'"))
+ format = "RGB float";
+ else if (model == babl_model ("Y") || model == babl_model ("Y'"))
+ format = "Y float";
+ else if (model == babl_model ("YA") || model == babl_model ("Y'A") ||
+ model == babl_model ("YaA") || model == babl_model ("Y'aA"))
+ format = "YaA float";
+ }
+
+ gegl_operation_set_format (operation, "output", babl_format (format));
+}
+
+static GeglRectangle
+gegl_gblur_1d_get_required_for_output (GeglOperation *operation,
+ const gchar *input_pad,
+ const GeglRectangle *output_roi)
+{
+ GeglRectangle required_for_output = { 0, };
+ const GeglRectangle *in_rect = gegl_operation_source_get_bounding_box (operation, input_pad);
+
+ if (in_rect && ! gegl_rectangle_is_infinite_plane (in_rect))
+ {
+ GeglChantO *o = GEGL_CHANT_PROPERTIES (operation);
+
+ required_for_output = *output_roi;
+
+ if (o->orientation == GEGL_BLUR_1D_HORIZONTAL)
+ {
+ required_for_output.x = in_rect->x;
+ required_for_output.width = in_rect->width;
+ }
+ else
+ {
+ required_for_output.y = in_rect->y;
+ required_for_output.height = in_rect->height;
+ }
+ }
+
+ return required_for_output;
+}
+
+static GeglRectangle
+gegl_gblur_1d_get_cached_region (GeglOperation *operation,
+ const GeglRectangle *output_roi)
+{
+ GeglRectangle cached_region = { 0, };
+ const GeglRectangle *in_rect = gegl_operation_source_get_bounding_box (operation,
+ "input");
+
+ if (in_rect && ! gegl_rectangle_is_infinite_plane (in_rect))
+ {
+ GeglChantO *o = GEGL_CHANT_PROPERTIES (operation);
+
+ cached_region = *output_roi;
+
+ if (o->orientation == GEGL_BLUR_1D_HORIZONTAL)
+ {
+ cached_region.x = in_rect->x;
+ cached_region.width = in_rect->width;
+ }
+ else
+ {
+ cached_region.y = in_rect->y;
+ cached_region.height = in_rect->height;
+ }
+ }
+
+ return cached_region;
+}
+
+static gboolean
+gegl_gblur_1d_process (GeglOperation *operation,
+ GeglBuffer *input,
+ GeglBuffer *output,
+ const GeglRectangle *result,
+ gint level)
+{
+ GeglChantO *o = GEGL_CHANT_PROPERTIES (operation);
+ const Babl *format = gegl_operation_get_format (operation, "output");
+ gdouble b[4], m[3][3];
+
+ iir_young_find_constants (o->std_dev, b, m);
+
+ if (o->orientation == GEGL_BLUR_1D_HORIZONTAL)
+ iir_young_hor_blur (input, result, output, b, m, o->abyss_policy, format);
+ else
+ iir_young_ver_blur (input, result, output, b, m, o->abyss_policy, format);
+
+ return TRUE;
+}
+
+static void
+gegl_chant_class_init (GeglChantClass *klass)
+{
+ GeglOperationClass *operation_class;
+ GeglOperationFilterClass *filter_class;
+
+ operation_class = GEGL_OPERATION_CLASS (klass);
+ filter_class = GEGL_OPERATION_FILTER_CLASS (klass);
+
+ filter_class->process = gegl_gblur_1d_process;
+ operation_class->prepare = gegl_gblur_1d_prepare;
+ operation_class->get_required_for_output = gegl_gblur_1d_get_required_for_output;
+ operation_class->get_cached_region = gegl_gblur_1d_get_cached_region;
+ gegl_operation_class_set_keys (operation_class,
+ "name", "gegl:gblur-1d",
+ "categories", "hidden:blur",
+ "description",
+ _("Performs an averaging of neighboring pixels with the "
+ "normal distribution as weighting"),
+ NULL);
+}
+
+#endif
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]