[gegl] operations: add gegl:edge
- From: Thomas Manni <tmanni src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gegl] operations: add gegl:edge
- Date: Tue, 3 Mar 2015 17:37:59 +0000 (UTC)
commit 5fdd91834b7c6b758088fcea01e71d3c5ec2dbd8
Author: Thomas Manni <thomas manni free fr>
Date: Tue Mar 3 18:00:00 2015 +0100
operations: add gegl:edge
operations/common/Makefile.am | 1 +
operations/common/edge.c | 383 +++++++++++++++++++++++++++++++++
po/POTFILES.in | 1 +
tests/compositions/Makefile.am | 1 +
tests/compositions/edge.xml | 124 +++++++++++
tests/compositions/reference/edge.png | Bin 0 -> 1059896 bytes
6 files changed, 510 insertions(+), 0 deletions(-)
---
diff --git a/operations/common/Makefile.am b/operations/common/Makefile.am
index f4b899b..adc52c2 100644
--- a/operations/common/Makefile.am
+++ b/operations/common/Makefile.am
@@ -43,6 +43,7 @@ op_LTLIBRARIES = \
display.la \
distance-transform.la \
dropshadow.la \
+ edge.la \
edge-laplace.la \
edge-sobel.la \
emboss.la \
diff --git a/operations/common/edge.c b/operations/common/edge.c
new file mode 100644
index 0000000..d81ec0a
--- /dev/null
+++ b/operations/common/edge.c
@@ -0,0 +1,383 @@
+/* This file is an image processing operation for GEGL
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Author: Jef Poskanzer.
+ *
+ * GEGL Port: Thomas Manni <thomas manni free fr>
+ *
+ */
+
+#include "config.h"
+#include <glib/gi18n-lib.h>
+
+
+#ifdef GEGL_PROPERTIES
+
+enum_start (gegl_edge_algo)
+ enum_value (GEGL_EDGE_SOBEL, "sobel", N_("Sobel"))
+ enum_value (GEGL_EDGE_PREWITT, "prewitt", N_("Prewitt compass"))
+ enum_value (GEGL_EDGE_GRADIENT, "gradient", N_("Gradient"))
+ enum_value (GEGL_EDGE_ROBERTS, "roberts", N_("Roberts"))
+ enum_value (GEGL_EDGE_DIFFERENTIAL, "differential", N_("Differential"))
+ enum_value (GEGL_EDGE_LAPLACE, "laplace", N_("Laplace"))
+enum_end (GeglEdgeAlgo)
+
+enum_start (gegl_edge_policy)
+ enum_value (GEGL_EDGE_WRAP, "wrap", N_("Wrap"))
+ enum_value (GEGL_EDGE_SMEAR, "smear", N_("Smear"))
+ enum_value (GEGL_EDGE_BLACK, "black", N_("Black"))
+enum_end (GeglEdgePolicy)
+
+property_enum (algorithm, _("Algorithm"),
+ GeglEdgeAlgo, gegl_edge_algo,
+ GEGL_EDGE_SOBEL)
+ description (_("Edge detection algorithm"))
+
+property_double (amount, _("Amount"), 2.0)
+ description (_("Edge detection amount"))
+ value_range (1.0, 10.0)
+ ui_range (1.0, 10.0)
+
+property_enum (policy, _("Edge behavior"),
+ GeglEdgePolicy, gegl_edge_policy,
+ GEGL_EDGE_SMEAR)
+ description (_("Edge detection behavior"))
+
+#else
+
+#define GEGL_OP_AREA_FILTER
+#define GEGL_OP_C_SOURCE edge.c
+
+#include <math.h>
+#include "gegl-op.h"
+
+static GeglAbyssPolicy
+to_gegl_policy (GeglEdgePolicy policy)
+{
+ switch (policy)
+ {
+ case (GEGL_EDGE_WRAP):
+ return GEGL_ABYSS_LOOP;
+ break;
+ case (GEGL_EDGE_SMEAR):
+ return GEGL_ABYSS_CLAMP;
+ break;
+ case (GEGL_EDGE_BLACK):
+ return GEGL_ABYSS_BLACK;
+ break;
+ default:
+ g_warning ("edge: unsupported abyss policy");
+ return GEGL_ABYSS_NONE;
+ }
+}
+
+static inline gfloat
+edge_sobel (gfloat *pixels, gdouble amount)
+{
+ const gint v_kernel[9] = { -1, 0, 1,
+ -2, 0, 2,
+ -1, 0, 1 };
+ const gint h_kernel[9] = { -1, -2, -1,
+ 0, 0, 0,
+ 1, 2, 1 };
+
+ gint i;
+ gfloat v_grad, h_grad;
+
+ for (i = 0, v_grad = 0.0f, h_grad = 0.0f; i < 9; i++)
+ {
+ v_grad += v_kernel[i] * pixels[i];
+ h_grad += h_kernel[i] * pixels[i];
+ }
+
+ return sqrt (v_grad * v_grad * amount +
+ h_grad * h_grad * amount);
+}
+
+static inline gfloat
+edge_prewitt (gfloat *pixels, gdouble amount)
+{
+ gint k;
+ gfloat max;
+ gfloat m[8];
+
+ m[0] = pixels[0] + pixels[1] + pixels[2]
+ + pixels[3] - 2*pixels[4] + pixels[5]
+ - pixels[6] - pixels[7] - pixels[8];
+ m[1] = pixels[0] + pixels[1] + pixels[2]
+ + pixels[3] - 2*pixels[4] - pixels[5]
+ + pixels[6] - pixels[7] - pixels[8];
+ m[2] = pixels[0] + pixels[1] - pixels[2]
+ + pixels[3] - 2*pixels[4] - pixels[5]
+ + pixels[6] + pixels[7] - pixels[8];
+ m[3] = pixels[0] - pixels[1] - pixels[2]
+ + pixels[3] - 2*pixels[4] - pixels[5]
+ + pixels[6] + pixels[7] + pixels[8];
+ m[4] = - pixels[0] - pixels[1] - pixels[2]
+ + pixels[3] - 2*pixels[4] + pixels[5]
+ + pixels[6] + pixels[7] + pixels[8];
+ m[5] = - pixels[0] - pixels[1] + pixels[2]
+ - pixels[3] - 2*pixels[4] + pixels[5]
+ + pixels[6] + pixels[7] + pixels[8];
+ m[6] = - pixels[0] + pixels[1] + pixels[2]
+ - pixels[3] - 2*pixels[4] + pixels[5]
+ - pixels[6] + pixels[7] + pixels[8];
+ m[7] = pixels[0] + pixels[1] + pixels[2]
+ - pixels[3] - 2*pixels[4] + pixels[5]
+ - pixels[6] - pixels[7] + pixels[8];
+
+ for (k = 0, max = 0.0f; k < 8; k++)
+ if (max < m[k])
+ max = m[k];
+
+ return amount * max;
+}
+
+static inline gfloat
+edge_gradient (gfloat *pixels, gdouble amount)
+{
+ const gint v_kernel[9] = { 0, 0, 0,
+ 0, 4, -4,
+ 0, 0, 0 };
+ const gint h_kernel[9] = { 0, 0, 0,
+ 0, -4, 0,
+ 0, 4, 0 };
+
+ gint i;
+ gfloat v_grad, h_grad;
+
+ for (i = 0, v_grad = 0.0f, h_grad = 0.0f; i < 9; i++)
+ {
+ v_grad += v_kernel[i] * pixels[i];
+ h_grad += h_kernel[i] * pixels[i];
+ }
+
+ return sqrt (v_grad * v_grad * amount +
+ h_grad * h_grad * amount);
+}
+
+static inline gfloat
+edge_roberts (gfloat *pixels, gdouble amount)
+{
+ const gint v_kernel[9] = { 0, 0, 0,
+ 0, 4, 0,
+ 0, 0, -4 };
+ const gint h_kernel[9] = { 0, 0, 0,
+ 0, 0, 4,
+ 0, -4, 0 };
+ gint i;
+ gfloat v_grad, h_grad;
+
+ for (i = 0, v_grad = 0.0f, h_grad = 0.0f; i < 9; i++)
+ {
+ v_grad += v_kernel[i] * pixels[i];
+ h_grad += h_kernel[i] * pixels[i];
+ }
+
+ return sqrt (v_grad * v_grad * amount +
+ h_grad * h_grad * amount);
+}
+
+static inline gfloat
+edge_differential (gfloat *pixels, gdouble amount)
+{
+ const gint v_kernel[9] = { 0, 0, 0,
+ 0, 2, -2,
+ 0, 2, -2 };
+ const gint h_kernel[9] = { 0, 0, 0,
+ 0, -2, -2,
+ 0, 2, 2 };
+ gint i;
+ gfloat v_grad, h_grad;
+
+ for (i = 0, v_grad = 0.0f, h_grad = 0.0f; i < 9; i++)
+ {
+ v_grad += v_kernel[i] * pixels[i];
+ h_grad += h_kernel[i] * pixels[i];
+ }
+
+ return sqrt (v_grad * v_grad * amount +
+ h_grad * h_grad * amount);
+}
+
+static inline gfloat
+edge_laplace (gfloat *pixels, gdouble amount)
+{
+ const gint kernel[9] = { 1, 1, 1,
+ 1, -8, 1,
+ 1, 1, 1 };
+ gint i;
+ gfloat grad;
+
+ for (i = 0, grad = 0.0f; i < 9; i++)
+ grad += kernel[i] * pixels[i];
+
+ return grad * amount;
+}
+
+static void
+prepare (GeglOperation *operation)
+{
+ GeglOperationAreaFilter *area = GEGL_OPERATION_AREA_FILTER (operation);
+
+ const Babl *input_f = gegl_operation_get_source_format (operation, "input");
+ const Babl *format = babl_format ("R'G'B' float");
+
+ area->left =
+ area->right =
+ area->top =
+ area->bottom = 1;
+
+ if (input_f)
+ {
+ if (babl_format_has_alpha (input_f))
+ format = babl_format ("R'G'B'A float");
+ }
+
+ gegl_operation_set_format (operation, "input", format);
+ gegl_operation_set_format (operation, "output", format);
+}
+
+static GeglRectangle
+get_bounding_box (GeglOperation *operation)
+{
+ GeglRectangle result = { 0, 0, 0, 0 };
+ GeglRectangle *in_rect;
+
+ in_rect = gegl_operation_source_get_bounding_box (operation, "input");
+ if (in_rect)
+ {
+ result = *in_rect;
+ }
+
+ return result;
+}
+
+static gboolean
+process (GeglOperation *operation,
+ GeglBuffer *input,
+ GeglBuffer *output,
+ const GeglRectangle *roi,
+ gint level)
+{
+ GeglProperties *o = GEGL_PROPERTIES (operation);
+ const Babl *format = gegl_operation_get_format (operation, "output");
+ gint components = babl_format_get_n_components (format);
+ gboolean has_alpha = babl_format_has_alpha (format);
+
+ gfloat *src_buff;
+ gfloat *dst_buff;
+ GeglRectangle rect;
+ gint x, y, ix, iy, b, idx;
+
+ rect = gegl_operation_get_required_for_output (operation, "input", roi);
+
+ src_buff = g_new (gfloat, rect.width * rect.height * components);
+ dst_buff = g_new0 (gfloat, roi->width * roi->height * components);
+
+ gegl_buffer_get (input, &rect, 1.0, format, src_buff,
+ GEGL_AUTO_ROWSTRIDE, to_gegl_policy (o->policy));
+
+ for (y = 0; y < roi->height; y++)
+ {
+ iy = y + 1;
+ for (x = 0; x < roi->width; x++)
+ {
+ ix = x + 1;
+ for (b = 0; b < 3; b++)
+ {
+
+#define SRCPIX(X,Y,B) src_buff[((X) + (Y) * rect.width) * components + B]
+
+ gfloat window[9];
+ window[0] = SRCPIX(ix - 1, iy - 1, b);
+ window[1] = SRCPIX(ix, iy - 1, b);
+ window[2] = SRCPIX(ix + 1, iy - 1, b);
+ window[3] = SRCPIX(ix - 1, iy, b);
+ window[4] = SRCPIX(ix, iy, b);
+ window[5] = SRCPIX(ix + 1, iy, b);
+ window[6] = SRCPIX(ix - 1, iy + 1, b);
+ window[7] = SRCPIX(ix, iy + 1, b);
+ window[8] = SRCPIX(ix + 1, iy + 1, b);
+
+ idx = (x + y * roi->width) * components + b;
+
+ switch (o->algorithm)
+ {
+ default:
+ case GEGL_EDGE_SOBEL:
+ dst_buff[idx] = edge_sobel (window, o->amount);
+ break;
+
+ case GEGL_EDGE_PREWITT:
+ dst_buff[idx] = edge_prewitt (window, o->amount);
+ break;
+
+ case GEGL_EDGE_GRADIENT:
+ dst_buff[idx] = edge_gradient (window, o->amount);
+ break;
+
+ case GEGL_EDGE_ROBERTS:
+ dst_buff[idx] = edge_roberts (window, o->amount);
+ break;
+
+ case GEGL_EDGE_DIFFERENTIAL:
+ dst_buff[idx] = edge_differential (window, o->amount);
+ break;
+
+ case GEGL_EDGE_LAPLACE:
+ dst_buff[idx] = edge_laplace (window, o->amount);
+ break;
+ }
+ }
+
+ if (has_alpha)
+ dst_buff[idx + 1] = SRCPIX(ix, iy, 3);
+ }
+#undef SRCPIX
+ }
+
+ gegl_buffer_set (output, roi, level, format, dst_buff, GEGL_AUTO_ROWSTRIDE);
+
+ g_free (src_buff);
+ g_free (dst_buff);
+
+ return TRUE;
+}
+
+static void
+gegl_op_class_init (GeglOpClass *klass)
+{
+ GeglOperationClass *operation_class;
+ GeglOperationFilterClass *filter_class;
+
+ operation_class = GEGL_OPERATION_CLASS (klass);
+ filter_class = GEGL_OPERATION_FILTER_CLASS (klass);
+
+ filter_class->process = process;
+ operation_class->prepare = prepare;
+ operation_class->get_bounding_box = get_bounding_box;
+ operation_class->opencl_support = FALSE;
+
+ gegl_operation_class_set_keys (operation_class,
+ "name", "gegl:edge",
+ "title", _("Edge Detection"),
+ "categories", "edge-detect",
+ "license", "GPL3+",
+ "description", _("Several simple methods for detecting edges"),
+ NULL);
+}
+
+#endif
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 13983ec..9f798ad 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -35,6 +35,7 @@ operations/common/diffraction-patterns.c
operations/common/display.c
operations/common/distance-transform.c
operations/common/dropshadow.c
+operations/common/edge.c
operations/common/edge-laplace.c
operations/common/edge-sobel.c
operations/common/emboss.c
diff --git a/tests/compositions/Makefile.am b/tests/compositions/Makefile.am
index 6182948..005d2d6 100644
--- a/tests/compositions/Makefile.am
+++ b/tests/compositions/Makefile.am
@@ -27,6 +27,7 @@ TESTS = \
composite-transform.xml \
contrast-curve.xml \
diffraction-patterns.xml \
+ edge.xml \
edge-laplace.xml \
edge-sobel.xml \
engrave.xml \
diff --git a/tests/compositions/edge.xml b/tests/compositions/edge.xml
new file mode 100644
index 0000000..4adf9d8
--- /dev/null
+++ b/tests/compositions/edge.xml
@@ -0,0 +1,124 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<gegl>
+ <node operation='gegl:over'>
+ <node operation='gegl:crop'>
+ <params>
+ <param name='x'>340.0</param>
+ <param name='y'>192.0</param>
+ <param name='width'>172.0</param>
+ <param name='height'>192.0</param>
+ </params>
+ </node>
+ <node operation='gegl:edge'>
+ <params>
+ <param name='algorithm'>laplace</param>
+ <param name='amount'>1.0</param>
+ </params>
+ </node>
+ <clone ref='clone0'/>
+ </node>
+ <node operation='gegl:over'>
+ <node operation='gegl:crop'>
+ <params>
+ <param name='x'>170.0</param>
+ <param name='y'>192.0</param>
+ <param name='width'>170.0</param>
+ <param name='height'>192.0</param>
+ </params>
+ </node>
+ <node operation='gegl:edge'>
+ <params>
+ <param name='algorithm'>differential</param>
+ <param name='amount'>1.0</param>
+ </params>
+ </node>
+ <clone ref='clone0'/>
+ </node>
+ <node operation='gegl:over'>
+ <node operation='gegl:crop'>
+ <params>
+ <param name='x'>0.0</param>
+ <param name='y'>192.0</param>
+ <param name='width'>170.0</param>
+ <param name='height'>192.0</param>
+ </params>
+ </node>
+ <node operation='gegl:edge'>
+ <params>
+ <param name='algorithm'>roberts</param>
+ <param name='amount'>1.0</param>
+ </params>
+ </node>
+ <clone ref='clone0'/>
+ </node>
+ <node operation='gegl:over'>
+ <node operation='gegl:crop'>
+ <params>
+ <param name='x'>340.0</param>
+ <param name='y'>0.0</param>
+ <param name='width'>172.0</param>
+ <param name='height'>192.0</param>
+ </params>
+ </node>
+ <node operation='gegl:edge'>
+ <params>
+ <param name='algorithm'>gradient</param>
+ <param name='amount'>1.0</param>
+ </params>
+ </node>
+ <clone ref='clone0'/>
+ </node>
+ <node operation='gegl:over'>
+ <node operation='gegl:crop'>
+ <params>
+ <param name='x'>170.0</param>
+ <param name='y'>0.0</param>
+ <param name='width'>170.0</param>
+ <param name='height'>192.0</param>
+ </params>
+ </node>
+ <node operation='gegl:edge'>
+ <params>
+ <param name='algorithm'>prewitt</param>
+ <param name='amount'>1.0</param>
+ </params>
+ </node>
+ <clone ref='clone0'/>
+ </node>
+ <node operation='gegl:over'>
+ <node operation='gegl:crop'>
+ <params>
+ <param name='x'>170.0</param>
+ <param name='y'>0.0</param>
+ <param name='width'>170.0</param>
+ <param name='height'>192.0</param>
+ </params>
+ </node>
+ <node operation='gegl:edge'>
+ <params>
+ <param name='algorithm'>prewitt</param>
+ <param name='amount'>1.0</param>
+ </params>
+ </node>
+ <clone ref='clone0'/>
+ </node>
+ <node operation='gegl:crop'>
+ <params>
+ <param name='x'>0.0</param>
+ <param name='y'>0.0</param>
+ <param name='width'>170.0</param>
+ <param name='height'>192.0</param>
+ </params>
+ </node>
+ <node operation='gegl:edge'>
+ <params>
+ <param name='algorithm'>sobel</param>
+ <param name='amount'>1.0</param>
+ </params>
+ </node>
+ <node operation='gegl:load' id='clone0'>
+ <params>
+ <param name='path'>data/car-stack.png</param>
+ </params>
+ </node>
+</gegl>
diff --git a/tests/compositions/reference/edge.png b/tests/compositions/reference/edge.png
new file mode 100644
index 0000000..5a870e7
Binary files /dev/null and b/tests/compositions/reference/edge.png differ
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]