[gegl] wavelet-blur: use a 1d filter to perform horizontal and vertical steps



commit 08e7c8ee3f8b5f4d16ca45dcc98276d9cb43f24e
Author: Thomas Manni <thomas manni free fr>
Date:   Mon Sep 25 19:39:09 2017 +0100

    wavelet-blur: use a 1d filter to perform horizontal and vertical steps
    
    Wavelet-blur is now a meta operation, using a new wavelet-blur-1d
    operation which implement the one-dimensional filter.
    
    Use ABYSS_CLAMP policy to avoid signal degradation at buffer boundaries
    when used in a "wavelet decompose" context.
    
    Update reference hash.

 operations/common/Makefile.am       |    3 +-
 operations/common/wavelet-blur-1d.c |  281 +++++++++++++++++++++++++++++++++++
 operations/common/wavelet-blur.c    |  254 ++++----------------------------
 3 files changed, 309 insertions(+), 229 deletions(-)
---
diff --git a/operations/common/Makefile.am b/operations/common/Makefile.am
index 1626c86..4fc847f 100644
--- a/operations/common/Makefile.am
+++ b/operations/common/Makefile.am
@@ -11,7 +11,7 @@ LIBS = $(op_libs)
 
 opdir = $(ext_dir)
 op_LTLIBRARIES = \
-       gegl-common.la 
+       gegl-common.la
 
 gegl_common_la_SOURCES =\
     alien-map.c \
@@ -157,6 +157,7 @@ gegl_common_la_SOURCES =\
        waterpixels.c \
        watershed-transform.c \
        waves.c \
+       wavelet-blur-1d.c \
        wavelet-blur.c \
        weighted-blend.c \
        whirl-pinch.c \
diff --git a/operations/common/wavelet-blur-1d.c b/operations/common/wavelet-blur-1d.c
new file mode 100644
index 0000000..e789a28
--- /dev/null
+++ b/operations/common/wavelet-blur-1d.c
@@ -0,0 +1,281 @@
+/* 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 2016 Miroslav Talasek <miroslav talasek seznam cz>
+ *           2017 Thomas Manni <thomas manni free fr>
+ *
+ * one dimensional wavelet blur used by wavelet-blur operation
+ *
+ */
+
+#include "config.h"
+#include <glib/gi18n-lib.h>
+
+#ifdef GEGL_PROPERTIES
+
+
+property_double (radius, _("Radius"), 1.0)
+    description (_("Radius of the wavelet blur"))
+    value_range (0.0, 1500.0)
+    ui_range    (0.0, 256.0)
+    ui_gamma    (3.0)
+    ui_meta     ("unit", "pixel-distance")
+    ui_meta     ("radius", "blur")
+
+property_enum (orientation, _("Orientation"),
+              GeglOrientation, gegl_orientation,
+              GEGL_ORIENTATION_HORIZONTAL)
+description (_("The orientation of the blur - hor/ver"))
+
+
+#else
+
+#define GEGL_OP_AREA_FILTER
+#define GEGL_OP_NAME     wavelet_blur_1d
+#define GEGL_OP_C_SOURCE wavelet-blur-1d.c
+
+#include "gegl-op.h"
+#include <math.h>
+
+static inline void
+wav_get_mean_pixel_1D (gfloat  *src,
+                       gfloat  *dst,
+                       gint     radius)
+{
+  gint     i, offset;
+  gdouble  weights[3] = {0.25, 0.5, 0.25};
+  gdouble  acc[3]     = {0.0, };
+
+  for (i = 0; i < 3; i++)
+    {
+      offset  = i * radius * 3;
+      acc[0] += src[offset]     * weights[i];
+      acc[1] += src[offset + 1] * weights[i];
+      acc[2] += src[offset + 2] * weights[i];
+    }
+
+  dst[0] = acc[0];
+  dst[1] = acc[1];
+  dst[2] = acc[2];
+}
+
+static void
+wav_hor_blur (GeglBuffer          *src,
+              GeglBuffer          *dst,
+              const GeglRectangle *dst_rect,
+              gint                 radius,
+              const Babl          *format)
+{
+  gint x, y;
+
+  GeglRectangle write_rect = {dst_rect->x, dst_rect->y, dst_rect->width, 1};
+
+  GeglRectangle read_rect = {dst_rect->x - radius, dst_rect->y,
+                             dst_rect->width + 2 * radius, 1};
+
+  gfloat *src_buf = gegl_malloc (read_rect.width * sizeof (gfloat) * 3);
+  gfloat *dst_buf = gegl_malloc (write_rect.width * sizeof (gfloat) * 3);
+
+  for (y = 0; y < dst_rect->height; y++)
+    {
+      gint offset     = 0;
+      read_rect.y     = dst_rect->y + y;
+      write_rect.y    = dst_rect->y + y;
+
+      gegl_buffer_get (src, &read_rect, 1.0, format, src_buf,
+                       GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_CLAMP);
+
+      for (x = 0; x < dst_rect->width; x++)
+        {
+          wav_get_mean_pixel_1D (src_buf + offset,
+                                 dst_buf + offset,
+                                 radius);
+          offset += 3;
+        }
+
+      gegl_buffer_set (dst, &write_rect, 0, format, dst_buf,
+                       GEGL_AUTO_ROWSTRIDE);
+    }
+
+  gegl_free (src_buf);
+  gegl_free (dst_buf);
+}
+
+static void
+wav_ver_blur (GeglBuffer          *src,
+              GeglBuffer          *dst,
+              const GeglRectangle *dst_rect,
+              gint                 radius,
+              const Babl          *format)
+{
+  gint  x, y;
+
+  GeglRectangle write_rect = {dst_rect->x, dst_rect->y, 1, dst_rect->height};
+
+  GeglRectangle read_rect  = {dst_rect->x, dst_rect->y - radius,
+                              1, dst_rect->height + 2 * radius};
+
+  gfloat *src_buf    = gegl_malloc (read_rect.height * sizeof(gfloat) * 3);
+  gfloat *dst_buf    = gegl_malloc (write_rect.height * sizeof(gfloat) * 3);
+
+  for (x = 0; x < dst_rect->width; x++)
+    {
+      gint offset     = 0;
+      read_rect.x     = dst_rect->x + x;
+      write_rect.x    = dst_rect->x + x;
+
+      gegl_buffer_get (src, &read_rect, 1.0, format, src_buf,
+                       GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_CLAMP);
+
+      for (y = 0; y < dst_rect->height; y++)
+        {
+          wav_get_mean_pixel_1D (src_buf + offset,
+                                 dst_buf + offset,
+                                 radius);
+          offset += 3;
+        }
+
+      gegl_buffer_set (dst, &write_rect, 0, format, dst_buf,
+                       GEGL_AUTO_ROWSTRIDE);
+    }
+
+  gegl_free (src_buf);
+  gegl_free (dst_buf);
+}
+
+static void
+prepare (GeglOperation *operation)
+{
+  GeglOperationAreaFilter *area = GEGL_OPERATION_AREA_FILTER (operation);
+
+  GeglProperties *o   = GEGL_PROPERTIES (operation);
+  const Babl *format  = babl_format ("R'G'B' float");
+
+  if (o->orientation == GEGL_ORIENTATION_HORIZONTAL)
+    {
+      area->left = area->right  = ceil (o->radius);
+      area->top  = area->bottom = 0;
+    }
+  else
+    {
+      area->left = area->right  = 0;
+      area->top  = area->bottom = ceil (o->radius);
+    }
+
+  gegl_operation_set_format (operation, "input", format);
+  gegl_operation_set_format (operation, "output", format);
+}
+
+static GeglRectangle
+get_bounding_box (GeglOperation *operation)
+{
+  GeglRectangle   result = { 0, };
+  GeglRectangle  *in_rect;
+
+  in_rect = gegl_operation_source_get_bounding_box (operation,"input");
+
+  if (!in_rect)
+    return result;
+
+  return *in_rect;
+}
+
+static gboolean
+process (GeglOperation       *operation,
+         GeglBuffer          *input,
+         GeglBuffer          *output,
+         const GeglRectangle *result,
+         gint                 level)
+{
+  GeglProperties *o      = GEGL_PROPERTIES (operation);
+  const Babl     *format = gegl_operation_get_format (operation, "output");
+
+  gint radius = ceil (o->radius);
+
+  if (o->orientation == GEGL_ORIENTATION_HORIZONTAL)
+    wav_hor_blur (input, output, result, radius, format);
+  else
+    wav_ver_blur (input, output, result, radius, format);
+
+  return  TRUE;
+}
+
+static gboolean
+operation_process (GeglOperation        *operation,
+                   GeglOperationContext *context,
+                   const gchar          *output_prop,
+                   const GeglRectangle  *result,
+                   gint                  level)
+{
+  GeglOperationClass  *operation_class;
+  GeglProperties      *o = GEGL_PROPERTIES (operation);
+
+  operation_class = GEGL_OPERATION_CLASS (gegl_op_parent_class);
+
+  if (! o->radius)
+    {
+      gpointer in = gegl_operation_context_get_object (context, "input");
+      gegl_operation_context_take_object (context, "output",
+                                          g_object_ref (G_OBJECT (in)));
+      return TRUE;
+    }
+
+  return operation_class->process (operation, context, output_prop, result,
+                                   gegl_operation_context_get_level (context));
+}
+
+static GeglSplitStrategy
+get_split_strategy (GeglOperation        *operation,
+                    GeglOperationContext *context,
+                    const gchar          *output_prop,
+                    const GeglRectangle  *result,
+                    gint                  level)
+{
+  GeglProperties *o = GEGL_PROPERTIES (operation);
+
+  if (o->orientation == GEGL_ORIENTATION_HORIZONTAL)
+    return GEGL_SPLIT_STRATEGY_HORIZONTAL;
+  else
+    return GEGL_SPLIT_STRATEGY_VERTICAL;
+}
+
+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);
+
+  operation_class->get_bounding_box = get_bounding_box;
+  operation_class->prepare          = prepare;
+  operation_class->process          = operation_process;
+  operation_class->opencl_support   = FALSE;
+  operation_class->threaded         = TRUE;
+
+  filter_class->get_split_strategy  = get_split_strategy;
+  filter_class->process             = process;
+
+  gegl_operation_class_set_keys (operation_class,
+    "name",        "gegl:wavelet-blur-1d",
+    "categories",  "hidden:blur",
+    "title",       _("1D Wavelet-blur"),
+    "reference-hash", "822a7c396b93fad84e0416a3bfba7a46",
+    "description", _("This blur is used for the wavelet decomposition filter, "
+                     "each pixel is computed from another by the HAT transform"),
+    NULL);
+}
+
+#endif
diff --git a/operations/common/wavelet-blur.c b/operations/common/wavelet-blur.c
index 1743a14..be136b1 100644
--- a/operations/common/wavelet-blur.c
+++ b/operations/common/wavelet-blur.c
@@ -12,7 +12,7 @@
  * 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 2016 Miroslav Talasek <miroslav talasek seznam cz>
+ * Copyright 2017 Thomas Manni <thomas manni free fr>
  *
  * Wavelet blur used in wavelet decompose filter
  *  theory is from original wavelet plugin
@@ -24,7 +24,6 @@
 
 #ifdef GEGL_PROPERTIES
 
-
 property_double (radius, _("Radius"), 1.0)
     description (_("Radius of the wavelet blur"))
     value_range (0.0, 1500.0)
@@ -33,256 +32,55 @@ property_double (radius, _("Radius"), 1.0)
     ui_meta     ("unit", "pixel-distance")
     ui_meta     ("radius", "blur")
 
-
 #else
 
-#define GEGL_OP_AREA_FILTER
+#define GEGL_OP_META
 #define GEGL_OP_NAME     wavelet_blur
 #define GEGL_OP_C_SOURCE wavelet-blur.c
 
 #include "gegl-op.h"
-#include <math.h>
-#include <stdio.h>
-
-
-static gint
-wav_gen_convolve_matrix (gdouble   radius,
-                         gdouble **cmatrix_p);
-
-
-
-static gint
-wav_calc_convolve_matrix_length (gdouble radius)
-{
-  return ceil (radius) * 2 + 1;
-}
-
-static gint
-wav_gen_convolve_matrix (gdouble   radius,
-                         gdouble **cmatrix_p)
-{
-  gint     matrix_length;
-  gdouble *cmatrix;
-
-  matrix_length = wav_calc_convolve_matrix_length (radius);
-  cmatrix = g_new (gdouble, matrix_length);
-
-  if (matrix_length == 1)
-    {
-      cmatrix[0] = 1;
-    }
-  else
-    {
-      gint i;
-
-      for (i = 0; i < matrix_length; i++)
-        {
-          if (i == 0 || i == matrix_length - 1)
-            {
-              cmatrix[i] = 0.25;
-            }
-          else if (i == matrix_length / 2)
-            {
-              cmatrix[i] = 0.5;
-            }
-          else
-            {
-              cmatrix[i] = 0;
-            }
-        }
-    }
-
-  *cmatrix_p = cmatrix;
-  return matrix_length;
-}
-
-static inline void
-wav_get_mean_pixel_1D (gfloat  *src,
-                       gfloat  *dst,
-                       gint     components,
-                       gdouble *cmatrix,
-                       gint     matrix_length)
-{
-  gint    i, c;
-  gint    offset;
-  gdouble acc[components];
-
-  for (c = 0; c < components; ++c)
-    acc[c] = 0;
-
-  offset = 0;
-
-  for (i = 0; i < matrix_length; i++)
-    {
-      for (c = 0; c < components; ++c)
-        acc[c] += src[offset++] * cmatrix[i];
-    }
-
-  for (c = 0; c < components; ++c)
-    dst[c] = acc[c];
-}
-
-static void
-wav_hor_blur (GeglBuffer          *src,
-              GeglBuffer          *dst,
-              const GeglRectangle *dst_rect,
-              gdouble             *cmatrix,
-              gint                 matrix_length,
-              const Babl          *format)
-{
-  gint        u, v;
-  const gint  radius = matrix_length / 2;
-  const gint  nc = babl_format_get_n_components (format);
-  
-  GeglRectangle write_rect = {dst_rect->x, dst_rect->y, dst_rect->width, 1};
-  gfloat *dst_buf     = gegl_malloc (write_rect.width * sizeof(gfloat) * nc);
-
-  GeglRectangle read_rect = {dst_rect->x - radius, dst_rect->y, dst_rect->width + matrix_length -1, 1};
-  gfloat *src_buf    = gegl_malloc (read_rect.width * sizeof(gfloat) * nc);
-
-  for (v = 0; v < dst_rect->height; v++)
-    {
-      gint offset     = 0;
-      read_rect.y     = dst_rect->y + v;
-      write_rect.y    = dst_rect->y + v;
-      gegl_buffer_get (src, &read_rect, 1.0, format, src_buf, GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
-
-      for (u = 0; u < dst_rect->width; u++)
-        {
-          wav_get_mean_pixel_1D (src_buf + offset,
-                                 dst_buf + offset,
-                                 nc,
-                                 cmatrix,
-                                 matrix_length);
-          offset += nc;
-        }
-
-      gegl_buffer_set (dst, &write_rect, 0, format, dst_buf, GEGL_AUTO_ROWSTRIDE);
-    }
-
-  gegl_free (src_buf);
-  gegl_free (dst_buf);
-}
 
 static void
-wav_ver_blur (GeglBuffer          *src,
-              GeglBuffer          *dst,
-              const GeglRectangle *dst_rect,
-              gdouble             *cmatrix,
-              gint                 matrix_length,
-              const Babl          *format)
+attach (GeglOperation *operation)
 {
-  gint        u,v;
-  const gint  radius = matrix_length / 2;
-  const gint  nc = babl_format_get_n_components (format);
+  GeglNode *gegl   = operation->node;
+  GeglNode *input  = gegl_node_get_input_proxy (gegl, "input");
+  GeglNode *output = gegl_node_get_output_proxy (gegl, "output");
 
-  GeglRectangle write_rect = {dst_rect->x, dst_rect->y, 1, dst_rect->height};
-  gfloat *dst_buf    = gegl_malloc (write_rect.height * sizeof(gfloat) * nc);
+  GeglNode *vblur  = gegl_node_new_child (gegl,
+                                          "operation", "gegl:wavelet-blur-1d",
+                                          "orientation", 1,
+                                          NULL);
 
-  GeglRectangle read_rect  = {dst_rect->x, dst_rect->y - radius , 1, dst_rect->height + matrix_length -1};
-  gfloat *src_buf    = gegl_malloc (read_rect.height * sizeof(gfloat) * nc);
+  GeglNode *hblur  = gegl_node_new_child (gegl,
+                                          "operation", "gegl:wavelet-blur-1d",
+                                          "orientation", 0,
+                                          NULL);
 
-  for (u = 0; u < dst_rect->width; u++)
-    {
-      gint offset     = 0;
-      read_rect.x     = dst_rect->x + u;
-      write_rect.x    = dst_rect->x + u;
-      gegl_buffer_get (src, &read_rect, 1.0, format, src_buf, GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
- 
-      for (v = 0; v < dst_rect->height; v++)
-        {
-          wav_get_mean_pixel_1D (src_buf + offset,
-                                 dst_buf + offset,
-                                 nc,
-                                 cmatrix,
-                                 matrix_length);
-          offset += nc;
-        }
+  gegl_node_link_many (input, hblur, vblur, output, NULL);
 
-      gegl_buffer_set (dst, &write_rect, 0, format, dst_buf, GEGL_AUTO_ROWSTRIDE);
-    }
+  gegl_operation_meta_redirect (operation, "radius", hblur, "radius");
+  gegl_operation_meta_redirect (operation, "radius", vblur, "radius");
 
-  gegl_free (src_buf);
-  gegl_free (dst_buf);
+  gegl_operation_meta_watch_nodes (operation, hblur, vblur, NULL);
 }
 
 static void
-prepare (GeglOperation *operation)
-{
-  GeglOperationAreaFilter *area = GEGL_OPERATION_AREA_FILTER (operation);
-  GeglProperties          *o    = GEGL_PROPERTIES (operation);
-
-
-  /* XXX: these should be calculated exactly considering o->filter, but we just
-   * make sure there is enough space */
-  area->left = area->right = ceil (o->radius);
-  area->top = area->bottom = ceil (o->radius);
-
-  gegl_operation_set_format (operation, "input",
-                             babl_format ("R'G'B' float"));
-  gegl_operation_set_format (operation, "output",
-                             babl_format ("R'G'B' float"));
-}
-
-static gboolean
-process (GeglOperation       *operation,
-         GeglBuffer          *input,
-         GeglBuffer          *output,
-         const GeglRectangle *result,
-         gint                 level)
-{
-  GeglRectangle rect;
-  GeglBuffer *temp;
-  GeglOperationAreaFilter *op_area = GEGL_OPERATION_AREA_FILTER (operation);
-  GeglProperties          *o       = GEGL_PROPERTIES (operation);
-  const Babl *format = gegl_operation_get_format (operation, "output");
-
-  GeglRectangle temp_extend;
-  gdouble      *cmatrix;
-  gint          cmatrix_len;
-
-  rect.x      = result->x - op_area->left;
-  rect.width  = result->width + op_area->left + op_area->right;
-  rect.y      = result->y - op_area->top;
-  rect.height = result->height + op_area->top + op_area->bottom;
-
-
-  gegl_rectangle_intersect (&temp_extend, &rect, gegl_buffer_get_extent (input));
-  temp_extend.x      = result->x;
-  temp_extend.width  = result->width;
-  temp = gegl_buffer_new (&temp_extend, format);
-
-  cmatrix_len = wav_gen_convolve_matrix (o->radius, &cmatrix);
-  wav_hor_blur (input, temp, &temp_extend, cmatrix, cmatrix_len, format);
-  wav_ver_blur (temp, output, result, cmatrix, cmatrix_len, format);
-  g_free (cmatrix);
-
-  g_object_unref (temp);
-  return  TRUE;
-}
-
-
-static void
 gegl_op_class_init (GeglOpClass *klass)
 {
-  GeglOperationClass       *operation_class;
-  GeglOperationFilterClass *filter_class;
+  GeglOperationClass *operation_class;
 
   operation_class = GEGL_OPERATION_CLASS (klass);
-  filter_class    = GEGL_OPERATION_FILTER_CLASS (klass);
-
-  operation_class->prepare        = prepare;
-  operation_class->opencl_support = FALSE;
 
-  filter_class->process           = process;
+  operation_class->attach = attach;
 
   gegl_operation_class_set_keys (operation_class,
-    "name",        "gegl:wavelet-blur",
-    "title",       _("Wavelet Blur"),
-//    "license",     "LGPL3+",
-    "categories",  "blur",
-    "reference-hash", "ae56a8da120f29554bafa08789aa37b6",
-    "description", _("This blur is used for the wavelet decomposition filter, each pixel is computed from 
another by the HAT transform"),
+    "name",           "gegl:wavelet-blur",
+    "title",          "Wavelet Blur",
+    "categories",     "blur",
+    "reference-hash", "49eaf3d22cf5a5999991c4e7a0fbe8e2",
+    "description", _("This blur is used for the wavelet decomposition filter, "
+                     "each pixel is computed from another by the HAT transform"),
     NULL);
 }
 


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]