[gegl] workshop: add shadows-highlights operation



commit 46f525ad7d761effd5c9662a72ef5d684e3be45c
Author: Thomas Manni <thomas manni free fr>
Date:   Sun Jan 22 06:22:16 2017 +0100

    workshop: add shadows-highlights operation

 operations/workshop/Makefile.am                    |    2 +
 .../workshop/shadows-highlights-correction.c       |  250 ++++++++++++++++++++
 operations/workshop/shadows-highlights.c           |  112 +++++++++
 po/POTFILES.in                                     |    2 +
 4 files changed, 366 insertions(+), 0 deletions(-)
---
diff --git a/operations/workshop/Makefile.am b/operations/workshop/Makefile.am
index cd45023..182df3b 100644
--- a/operations/workshop/Makefile.am
+++ b/operations/workshop/Makefile.am
@@ -25,5 +25,7 @@ op_LTLIBRARIES =    \
        median-blur.la \
        rawbayer-load.la \
        segment-kmeans.la \
+       shadows-highlights.la \
+       shadows-highlights-correction.la \
        unpremul.la \
     vhsfix.la
diff --git a/operations/workshop/shadows-highlights-correction.c 
b/operations/workshop/shadows-highlights-correction.c
new file mode 100644
index 0000000..ce19b9b
--- /dev/null
+++ b/operations/workshop/shadows-highlights-correction.c
@@ -0,0 +1,250 @@
+/* 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/>.
+ *
+ */
+
+#include "config.h"
+
+#include <glib/gi18n-lib.h>
+
+#ifdef GEGL_PROPERTIES
+
+property_double (s_amount, _("shadows amount"), 0.2)
+    value_range (0.0, 1.0)
+
+property_double (s_tonalwidth, _("shadows tonal width"), 0.1)
+    value_range (0.001, 1.0)
+
+property_double (h_amount, _("highlights amount"), 0.2)
+    value_range (0.0, 1.0)
+
+property_double (h_tonalwidth, _("highlights tonal width"), 0.1)
+    value_range (0.001, 1.0)
+
+#else
+
+#define GEGL_OP_POINT_COMPOSER3
+#define GEGL_OP_NAME     shadows_highlights_correction
+#define GEGL_OP_C_SOURCE shadows-highlights-correction.c
+
+#include "gegl-op.h"
+#include <math.h>
+
+#define LUT_SIZE 2048
+
+typedef struct
+{
+  gdouble shadows[LUT_SIZE];
+  gdouble highlights[LUT_SIZE];
+} Luts;
+
+static void
+initialize_luts (GeglProperties *o)
+{
+  gint     i, scale = LUT_SIZE - 1;
+  Luts    *luts = (Luts *) o->user_data;
+  gdouble  s_scaled = o->s_tonalwidth * scale;
+  gdouble  h_scaled = (1.0 - o->h_tonalwidth) * scale;
+
+  for (i = 0; i < LUT_SIZE; i++)
+    {
+      /* shadows */
+      if (i < s_scaled)
+        {
+          luts->shadows[i] = 1.0 - pow((i / s_scaled), 2.0);
+        }
+      else
+        {
+          luts->shadows[i] = 0.0;
+        }
+
+      /* highlights */
+      if (i > h_scaled)
+        {
+          luts->highlights[i] = 1.0 - pow((scale - i) / (scale - h_scaled), 2.0);
+        }
+      else
+        {
+          luts->highlights[i] = 0.0;
+        }
+    }
+}
+
+static void
+prepare (GeglOperation *operation)
+{
+  GeglProperties *o  = GEGL_PROPERTIES (operation);
+  const Babl *rgba_f = babl_format ("R'G'B'A float");
+  const Babl *y_f  = babl_format ("Y' float");
+
+  if (o->user_data == NULL)
+    o->user_data = g_slice_new0 (Luts);
+
+  initialize_luts (o);
+
+  gegl_operation_set_format (operation, "input",  rgba_f);
+  gegl_operation_set_format (operation, "aux",    y_f);
+  gegl_operation_set_format (operation, "aux2",   y_f);
+  gegl_operation_set_format (operation, "output", rgba_f);
+}
+
+static void
+finalize (GObject *object)
+{
+  GeglProperties *o = GEGL_PROPERTIES (object);
+
+  if (o->user_data)
+    {
+      g_slice_free (Luts, o->user_data);
+      o->user_data = NULL;
+    }
+
+  G_OBJECT_CLASS (gegl_op_parent_class)->finalize (object);
+}
+
+static gboolean
+process (GeglOperation       *operation,
+         void                *in_buf,
+         void                *aux_buf,
+         void                *aux2_buf,
+         void                *out_buf,
+         glong                n_pixels,
+         const GeglRectangle *roi,
+         gint                 level)
+{
+  GeglProperties *o = GEGL_PROPERTIES (operation);
+  Luts *luts = (Luts *) o->user_data;
+
+  gfloat *src  = in_buf;
+  gfloat *dst  = out_buf;
+  gfloat *aux  = aux_buf;
+  gfloat *aux2 = aux2_buf;
+
+  if (!aux || !aux2)
+    {
+      memcpy (out_buf, in_buf, sizeof (gfloat) * 4 * n_pixels);
+      return TRUE;
+    }
+
+  while (n_pixels--)
+    {
+      gfloat *src_pix               = src;
+      gfloat  shadows_estimation    = *aux;
+      gfloat  highlights_estimation = *aux2;
+
+      gdouble correction;
+      gdouble lut_value;
+      gint    b;
+
+      /* shadows correction */
+
+      if (o->s_amount)
+        {
+          lut_value = luts->shadows[(gint) (shadows_estimation * (LUT_SIZE - 1))];
+
+          if (lut_value > 0.0)
+            {
+              correction = o->s_amount * lut_value;
+
+              for (b = 0; b < 3; b++)
+                {
+                  gfloat new_shadow = 1.0 - shadows_estimation;
+
+                  if (src_pix[b] < 0.5)
+                    new_shadow = 2.0 * src_pix[b] * new_shadow;
+                  else
+                    new_shadow = 1.0 - 2.0 * (1.0 - src_pix[b]) * (1.0 - new_shadow);
+
+                  src_pix[b] = correction * new_shadow + (1.0 - correction) * src_pix[b];
+                }
+            }
+        }
+
+      /* highlights correction */
+
+      if (o->h_amount)
+        {
+          lut_value = luts->highlights[(gint) (highlights_estimation * (LUT_SIZE - 1))];
+
+          if (lut_value > 0.0)
+            {
+              correction = o->h_amount * lut_value;
+
+              for (b = 0; b < 3; b++)
+                {
+                  gfloat new_highlight = 1.0 - highlights_estimation;
+
+                  if (src_pix[b] < 0.5)
+                    new_highlight = 2.0 * src_pix[b] * new_highlight;
+                  else
+                    new_highlight = 1.0 - 2.0 * (1.0 - src_pix[b]) * (1.0 - new_highlight);
+
+                  src_pix[b] = correction * new_highlight + (1.0 - correction) * src_pix[b];
+                }
+            }
+        }
+
+      dst[0] = src_pix[0];
+      dst[1] = src_pix[1];
+      dst[2] = src_pix[2];
+      dst[3] = src[3];
+
+      src += 4;
+      dst += 4;
+      aux += 1;
+      aux2 += 1;
+    }
+
+  return TRUE;
+}
+
+static GeglRectangle
+get_bounding_box (GeglOperation *self)
+{
+  GeglRectangle  result  = { 0, 0, 0, 0 };
+  GeglRectangle *in_rect = gegl_operation_source_get_bounding_box (self, "input");
+
+  if (! in_rect)
+    return result;
+
+  return *in_rect;
+}
+
+static void
+gegl_op_class_init (GeglOpClass *klass)
+{
+  GObjectClass                     *object_class;
+  GeglOperationClass               *operation_class;
+  GeglOperationPointComposer3Class *point_composer3_class;
+
+  object_class          = G_OBJECT_CLASS (klass);
+  operation_class       = GEGL_OPERATION_CLASS (klass);
+  point_composer3_class = GEGL_OPERATION_POINT_COMPOSER3_CLASS (klass);
+
+  object_class->finalize            = finalize;
+
+  operation_class->prepare          = prepare;
+  operation_class->get_bounding_box = get_bounding_box;
+  operation_class->opencl_support   = FALSE;
+
+  point_composer3_class->process    = process;
+
+  gegl_operation_class_set_keys (operation_class,
+    "name",        "gegl:shadows-highlights-correction",
+    "categories",  "hidden",
+    "description", _("Lighten shadows and darken highlights"),
+    NULL);
+}
+
+#endif
diff --git a/operations/workshop/shadows-highlights.c b/operations/workshop/shadows-highlights.c
new file mode 100644
index 0000000..5e88bf8
--- /dev/null
+++ b/operations/workshop/shadows-highlights.c
@@ -0,0 +1,112 @@
+/* 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/>.
+ *
+ */
+
+#include "config.h"
+#include <glib/gi18n-lib.h>
+
+#ifdef GEGL_PROPERTIES
+
+property_double (s_amount, _("shadows amount"), 0.2)
+    value_range (0.0, 1.0)
+
+property_double (s_tonalwidth, _("shadows tonal width"), 0.1)
+    value_range (0.001, 1.0)
+
+property_double (s_radius, _("shadows radius"), 5.0)
+    value_range (0.0, 100.0)
+
+property_double (h_amount, _("highlights amount"), 0.2)
+    value_range (0.0, 1.0)
+
+property_double (h_tonalwidth, _("highlights tonal width"), 0.1)
+    value_range (0.001, 1.0)
+
+property_double (h_radius, _("highlights radius"), 5.0)
+    value_range (0.0, 100.0)
+
+#else
+
+#define GEGL_OP_META
+#define GEGL_OP_NAME     shadows_highlights
+#define GEGL_OP_C_SOURCE shadows-highlights.c
+
+#include "gegl-op.h"
+
+static void
+attach (GeglOperation *operation)
+{
+  GeglNode *gegl;
+  GeglNode *input;
+  GeglNode *output;
+  GeglNode *s_blur;
+  GeglNode *h_blur;
+  GeglNode *shprocess;
+
+  gegl   = operation->node;
+  input  = gegl_node_get_input_proxy (gegl, "input");
+  output = gegl_node_get_output_proxy (gegl, "output");
+
+  s_blur = gegl_node_new_child (gegl,
+                                "operation",    "gegl:gaussian-blur",
+                                "abyss-policy", 1,
+                                NULL);
+
+  h_blur = gegl_node_new_child (gegl,
+                                "operation",    "gegl:gaussian-blur",
+                                "abyss-policy", 1,
+                                NULL);
+
+  shprocess = gegl_node_new_child (gegl,
+                                   "operation", "gegl:shadows-highlights-correction",
+                                   NULL);
+
+  gegl_node_link (input, s_blur);
+  gegl_node_link (input, h_blur);
+  gegl_node_link_many (input, shprocess, output, NULL);
+
+  gegl_node_connect_to (s_blur, "output", shprocess, "aux");
+  gegl_node_connect_to (h_blur, "output", shprocess, "aux2");
+
+  gegl_operation_meta_redirect (operation, "s-amount", shprocess, "s-amount");
+  gegl_operation_meta_redirect (operation, "s-tonalwidth", shprocess, "s-tonalwidth");
+  gegl_operation_meta_redirect (operation, "s-radius", s_blur, "std-dev-x");
+  gegl_operation_meta_redirect (operation, "s-radius", s_blur, "std-dev-y");
+  gegl_operation_meta_redirect (operation, "h-amount", shprocess, "h-amount");
+  gegl_operation_meta_redirect (operation, "h-tonalwidth", shprocess, "h-tonalwidth");
+  gegl_operation_meta_redirect (operation, "h-radius", h_blur, "std-dev-x");
+  gegl_operation_meta_redirect (operation, "h-radius", h_blur, "std-dev-y");
+
+  gegl_operation_meta_watch_nodes (operation, s_blur, h_blur, shprocess, NULL);
+}
+
+static void
+gegl_op_class_init (GeglOpClass *klass)
+{
+  GeglOperationClass *operation_class;
+
+  operation_class = GEGL_OPERATION_CLASS (klass);
+
+  operation_class->attach = attach;
+
+  gegl_operation_class_set_keys (operation_class,
+    "name",        "gegl:shadows-highlights",
+    "title",       _("Shadows Highlights"),
+    "categories",  "light",
+    "description", _("Performs shadows and highlights correction"),
+    NULL);
+}
+
+#endif
diff --git a/po/POTFILES.in b/po/POTFILES.in
index f5f7913..368d327 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -249,4 +249,6 @@ operations/workshop/mandelbrot.c
 operations/workshop/median-blur.c
 operations/workshop/rawbayer-load.c
 operations/workshop/segment-kmeans.c
+operations/workshop/shadows-highlights.c
+operations/workshop/shadows-highlights-correction.c
 operations/workshop/vhsfix.c


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