[gegl] operations: add gegl:edge



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]