[gegl] recursive-transform: add new operation to workshop



commit faa21840b255e268f384fc0b28a974946a008dee
Author: Ell <ell_se yahoo com>
Date:   Wed Feb 28 18:38:24 2018 -0500

    recursive-transform: add new operation to workshop
    
    gegl:recursive-transform applies a transformation matrix
    recursilvely to an image, pasting the results on top of, or below,
    each other.  This can be used to create an arbitrarily-deep image-
    within-image effect.
    
    The op additionally takes brightness/contrast parameters, which are
    applied recursively as well, allowing to attenuate the colors of
    deeper iterations.

 operations/workshop/Makefile.am           |    1 +
 operations/workshop/recursive-transform.c |  248 +++++++++++++++++++++++++++++
 po/POTFILES.in                            |    1 +
 3 files changed, 250 insertions(+), 0 deletions(-)
---
diff --git a/operations/workshop/Makefile.am b/operations/workshop/Makefile.am
index d88437d..10784f1 100644
--- a/operations/workshop/Makefile.am
+++ b/operations/workshop/Makefile.am
@@ -23,5 +23,6 @@ op_LTLIBRARIES = \
        integral-image.la \
        linear-sinusoid.la \
        rawbayer-load.la  \
+       recursive-transform.la \
        segment-kmeans.la \
        spherize.la
diff --git a/operations/workshop/recursive-transform.c b/operations/workshop/recursive-transform.c
new file mode 100644
index 0000000..f60eca3
--- /dev/null
+++ b/operations/workshop/recursive-transform.c
@@ -0,0 +1,248 @@
+/* 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 2018 Ell
+ */
+
+#include "config.h"
+#include <glib/gi18n-lib.h>
+
+#define MAX_ITERATIONS 20
+#define EPSILON        1e-6
+
+#ifdef GEGL_PROPERTIES
+
+property_string  (transform, _("Transform"), "")
+    description  (_("Transformation matrix, using SVG syntax"))
+
+property_int     (first_iteration, _("First iteration"), 0)
+    description  (_("First iteration"))
+    value_range  (0, MAX_ITERATIONS)
+
+property_int     (iterations, _("Iterations"), 3)
+    description  (_("Number of iterations"))
+    value_range  (0, MAX_ITERATIONS)
+
+property_double (contrast, _("Contrast"),  1.0)
+    description  (_("Contrast scaling of each transformed image"))
+    value_range  (-5.0, 5.0)
+    ui_range     (0.5, 1.5)
+
+property_double (brightness, _("Brightness"), 0.0)
+    description  (_("Brightness offset of each transformed image"))
+    value_range  (-3.0, 3.0)
+    ui_range     (-0.5, 0.5)
+
+property_boolean (paste_below, _("Paste below"), FALSE)
+    description  (_("Paste transformed images below each other"))
+
+property_enum    (sampler_type, _("Resampling method"),
+    GeglSamplerType, gegl_sampler_type, GEGL_SAMPLER_LINEAR)
+    description  (_("Mathematical method for reconstructing pixel values"))
+
+#else
+
+#define GEGL_OP_META
+#define GEGL_OP_NAME     recursive_transform
+#define GEGL_OP_C_SOURCE recursive-transform.c
+
+#include "gegl-op.h"
+#include <math.h>
+
+typedef struct
+{
+  GeglNode *transform_node;
+  GeglNode *brightness_contrast_node;
+  GeglNode *over_node;
+} Iteration;
+
+static void
+update_graph (GeglOperation *operation)
+{
+  GeglProperties *o     = GEGL_PROPERTIES (operation);
+  Iteration      *iters = o->user_data;
+  GeglNode       *node  = operation->node;
+  GeglNode       *input;
+  GeglNode       *output;
+  GeglMatrix3     transform;
+  gint            i;
+
+  if (! iters)
+    return;
+
+  input  = gegl_node_get_input_proxy  (node, "input");
+  output = gegl_node_get_output_proxy (node, "output");
+
+  gegl_node_link (input, output);
+
+  for (i = 0; i <= MAX_ITERATIONS; i++)
+    {
+      gegl_node_disconnect (iters[i].transform_node,           "input");
+      gegl_node_disconnect (iters[i].brightness_contrast_node, "input");
+      gegl_node_disconnect (iters[i].over_node,                "input");
+      gegl_node_disconnect (iters[i].over_node,                "aux");
+    }
+
+  if (o->first_iteration == 0 && o->iterations == 0)
+    return;
+
+  gegl_matrix3_parse_string (&transform, o->transform);
+
+  for (i = o->iterations; i >= 0; i--)
+    {
+      GeglNode    *source_node = iters[i].transform_node;
+      GeglMatrix3  matrix;
+      gchar       *matrix_str;
+      gint         n           = o->first_iteration + i;
+      gint         j;
+
+      gegl_matrix3_identity (&matrix);
+
+      for (j = 0; j < n; j++)
+        gegl_matrix3_multiply (&matrix, &transform, &matrix);
+
+      matrix_str = gegl_matrix3_to_string (&matrix);
+
+      gegl_node_set (iters[i].transform_node,
+                     "transform", matrix_str,
+                     "sampler",   o->sampler_type,
+                     NULL);
+
+      g_free (matrix_str);
+
+      gegl_node_link (input, iters[i].transform_node);
+
+      if (n > 0 && (fabs (o->brightness - 0.0) > EPSILON ||
+                    fabs (o->contrast   - 1.0) > EPSILON))
+        {
+          gdouble b = o->brightness;
+          gdouble c = o->contrast;
+
+          if (fabs (c - 1.0) > EPSILON)
+            b *= (pow (c, n) - 1.0) / (c - 1.0);
+          else
+            b *= n;
+
+          c = pow (c, n);
+
+          gegl_node_set (iters[i].brightness_contrast_node,
+                         "brightness", b,
+                         "contrast",   c,
+                         NULL);
+
+          gegl_node_link (source_node, iters[i].brightness_contrast_node);
+
+          source_node = iters[i].brightness_contrast_node;
+        }
+
+      gegl_node_connect_to (source_node,        "output",
+                            iters[i].over_node, ! o->paste_below ? "input" :
+                                                                   "aux");
+
+      if (i == 0)
+        {
+          gegl_node_link (iters[i].over_node, output);
+        }
+      else
+        {
+          gegl_node_connect_to (iters[i].over_node,     "output",
+                                iters[i - 1].over_node, ! o->paste_below ? "aux" :
+                                                                           "input");
+        }
+    }
+}
+
+static void
+attach (GeglOperation *operation)
+{
+  GeglProperties *o     = GEGL_PROPERTIES (operation);
+  Iteration      *iters = o->user_data;
+  GeglNode       *node  = operation->node;
+  gint            i;
+
+  if (! iters)
+    iters = o->user_data = g_new (Iteration, MAX_ITERATIONS + 1);
+
+  for (i = 0; i <= MAX_ITERATIONS; i++)
+    {
+      iters[i].transform_node =
+        gegl_node_new_child (node,
+                             "operation", "gegl:transform",
+                             NULL);
+
+      iters[i].brightness_contrast_node =
+        gegl_node_new_child (node,
+                             "operation", "gegl:brightness-contrast",
+                             NULL);
+
+      iters[i].over_node =
+        gegl_node_new_child (node,
+                             "operation", "gegl:over",
+                             NULL);
+
+      gegl_operation_meta_watch_nodes (operation,
+                                       iters[i].transform_node,
+                                       iters[i].brightness_contrast_node,
+                                       iters[i].over_node,
+                                       NULL);
+    }
+
+  update_graph (operation);
+}
+
+static void
+my_set_property (GObject      *object,
+                 guint         property_id,
+                 const GValue *value,
+                 GParamSpec   *pspec)
+{
+  set_property (object, property_id, value, pspec);
+
+  update_graph (GEGL_OPERATION (object));
+}
+
+static void
+dispose (GObject *object)
+{
+  GeglProperties *o = GEGL_PROPERTIES (object);
+
+  g_clear_pointer (&o->user_data, g_free);
+
+  G_OBJECT_CLASS (gegl_op_parent_class)->dispose (object);
+}
+
+static void
+gegl_op_class_init (GeglOpClass *klass)
+{
+  GObjectClass       *object_class;
+  GeglOperationClass *operation_class;
+
+  object_class    = G_OBJECT_CLASS (klass);
+  operation_class = GEGL_OPERATION_CLASS (klass);
+
+  object_class->dispose      = dispose;
+  object_class->set_property = my_set_property;
+
+  operation_class->attach    = attach;
+
+  gegl_operation_class_set_keys (operation_class,
+    "name",        "gegl:recursive-transform",
+    "title",       _("Recursive Transform"),
+    "categories",  "map",
+    "description", _("Apply a transformation recursively."),
+    NULL);
+}
+
+#endif
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 5686468..3b2bf53 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -255,5 +255,6 @@ operations/workshop/hstack.c
 operations/workshop/integral-image.c
 operations/workshop/linear-sinusoid.c
 operations/workshop/rawbayer-load.c
+operations/workshop/recursive-transform.c
 operations/workshop/segment-kmeans.c
 operations/workshop/spherize.c


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