[gegl] operations: reinhard05 tone mapping operator



commit 9e77996781dc6b493086b72ad3ddce57a2accbd4
Author: Danny Robson <danny blubinc net>
Date:   Wed Aug 11 19:54:59 2010 +0200

    operations: reinhard05 tone mapping operator
    
    Implements the paper 'Dynamic Range Reduction Inspired by Photoreceptor
    Physiology' by Reinhard and Devlin. It is a global tone mapping operator
    with reasonably intuitive options.
    
    The implementation is a combination of a port from pfstmo based code,
    and from-scratch implementation.

 operations/common/reinhard05.c              |  278 +++++++++++++++++++++++++++
 tests/compositions/reference/reinhard05.png |  Bin 0 -> 188909 bytes
 tests/compositions/reinhard05.xml           |   15 ++
 3 files changed, 293 insertions(+), 0 deletions(-)
---
diff --git a/operations/common/reinhard05.c b/operations/common/reinhard05.c
new file mode 100644
index 0000000..b6ed8f5
--- /dev/null
+++ b/operations/common/reinhard05.c
@@ -0,0 +1,278 @@
+/* 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 2010 Danny Robson      <danny blubinc net>
+ * (pfstmo)  2007 Grzegorz Krawczyk <krawczyk mpi-sb mpg de>
+ */
+
+#include "config.h"
+#include <glib/gi18n-lib.h>
+
+
+#ifdef GEGL_CHANT_PROPERTIES
+
+gegl_chant_double (brightness, _("Brightness"),
+                  -100.0, 100.0, 0.0,
+                  _("Overall brightness of the image"))
+gegl_chant_double (chromatic, _("Chromatic Adaptation"),
+                  0.0, 1.0, 0.0,
+                  _("Adapation to colour variation across the image"))
+gegl_chant_double (light, _("Light Adaptation"),
+                  0.0, 1.0, 1.0,
+                  _("Adapation to light variation across the image"))
+
+
+#else
+
+#define GEGL_CHANT_TYPE_FILTER
+#define GEGL_CHANT_C_FILE       "reinhard05.c"
+
+#include "gegl-chant.h"
+
+
+typedef struct {
+  gfloat min, max, avg, range;
+  guint  num;
+} stats;
+
+
+static const gchar *OUTPUT_FORMAT = "RGBA float";
+
+
+static void
+reinhard05_prepare (GeglOperation *operation)
+{
+  gegl_operation_set_format (operation, "input",  babl_format (OUTPUT_FORMAT));
+  gegl_operation_set_format (operation, "output", babl_format (OUTPUT_FORMAT));
+}
+
+static GeglRectangle
+reinhard05_get_required_for_output (GeglOperation       *operation,
+                                    const gchar         *input_pad,
+                                    const GeglRectangle *roi)
+{
+  GeglRectangle result = *gegl_operation_source_get_bounding_box (operation,
+                                                                  "input");
+  return result;
+}
+
+
+static GeglRectangle
+reinhard05_get_cached_region (GeglOperation       *operation,
+                              const GeglRectangle *roi)
+{
+  return *gegl_operation_source_get_bounding_box (operation, "input");
+}
+
+static void
+reinhard05_stats_start (stats *s)
+{
+  g_return_if_fail (s);
+
+  s->min   = G_MAXFLOAT;
+  s->max   = G_MINFLOAT;
+  s->avg   = 0.0;
+  s->range = NAN;
+  s->num   = 0;
+};
+
+
+static void
+reinhard05_stats_update (stats *s,
+                         gfloat value)
+{
+  g_return_if_fail (s);
+  g_return_if_fail (!isinf (value));
+  g_return_if_fail (!isnan (value));
+
+  s->min  = MIN (s->min, value);
+  s->max  = MAX (s->max, value);
+  s->avg += value;
+  s->num += 1;
+}
+
+
+static void
+reinhard05_stats_finish (stats *s)
+{
+  g_return_if_fail (s->num !=    0.0);
+  g_return_if_fail (s->max >= s->min);
+
+  s->avg   /= s->num;
+  s->range  = s->max - s->min;
+}
+
+
+static gboolean
+reinhard05_process (GeglOperation       *operation,
+                    GeglBuffer          *input,
+                    GeglBuffer          *output,
+                    const GeglRectangle *result)
+{
+  const GeglChantO *o = GEGL_CHANT_PROPERTIES (operation);
+
+  const gint  pix_stride = 4, /* RGBA */
+              RGB        = 3;
+
+  gfloat *lum,
+         *pix;
+  gfloat  key, contrast, intensity,
+          chrom      =       o->chromatic,
+          chrom_comp = 1.0 - o->chromatic,
+          light      =       o->light,
+          light_comp = 1.0 - o->light;
+
+  stats   world_lin,
+          world_log,
+          channel [RGB],
+          normalise;
+
+  gint    i, c;
+
+  g_return_val_if_fail (operation, FALSE);
+  g_return_val_if_fail (input, FALSE);
+  g_return_val_if_fail (output, FALSE);
+  g_return_val_if_fail (result, FALSE);
+
+  g_return_val_if_fail (babl_format_get_n_components (babl_format (OUTPUT_FORMAT)) == pix_stride, FALSE);
+
+  g_return_val_if_fail (chrom      >= 0.0 && chrom      <= 1.0, FALSE);
+  g_return_val_if_fail (chrom_comp >= 0.0 && chrom_comp <= 1.0, FALSE);
+  g_return_val_if_fail (light      >= 0.0 && light      <= 1.0, FALSE);
+  g_return_val_if_fail (light_comp >= 0.0 && light_comp <= 1.0, FALSE);
+
+
+  /* Obtain the pixel data */
+  lum = g_new (gfloat, result->width * result->height),
+  gegl_buffer_get (input, 1.0, result, babl_format ("Y float"),
+                   lum, GEGL_AUTO_ROWSTRIDE);
+
+  pix = g_new (gfloat, result->width * result->height * pix_stride);
+  gegl_buffer_get (input, 1.0, result, babl_format (OUTPUT_FORMAT),
+                   pix, GEGL_AUTO_ROWSTRIDE);
+
+  /* Collect the image stats, averages, etc */
+  reinhard05_stats_start (&world_lin);
+  reinhard05_stats_start (&world_log);
+  reinhard05_stats_start (&normalise);
+  for (i = 0; i < RGB; ++i)
+    {
+      reinhard05_stats_start (channel + i);
+    }
+
+  for (i = 0; i < result->width * result->height; ++i)
+    {
+      reinhard05_stats_update (&world_lin,                 lum[i] );
+      reinhard05_stats_update (&world_log, logf (2.3e-5f + lum[i]));
+
+      for (c = 0; c < RGB; ++c)
+        {
+          reinhard05_stats_update (channel + c, pix[i * pix_stride + c]);
+        }
+    }
+
+  g_return_val_if_fail (world_lin.min >= 0.0, FALSE);
+
+  reinhard05_stats_finish (&world_lin);
+  reinhard05_stats_finish (&world_log);
+  for (i = 0; i < RGB; ++i)
+    {
+      reinhard05_stats_finish (channel + i);
+    }
+
+  /* Calculate key parameters */
+  key       = (logf (world_lin.max) -                 world_log.avg) /
+              (logf (world_lin.max) - logf (2.3e-5f + world_lin.min));
+  contrast  = 0.3 + 0.7 * powf (key, 1.4);
+  intensity = expf (-o->brightness);
+
+  g_return_val_if_fail (contrast >= 0.3 && contrast <= 1.0, FALSE);
+
+  /* Apply the operator */
+  for (i = 0; i < result->width * result->height; ++i)
+    {
+      gfloat local, global, adapt;
+
+      if (lum[i] == 0.0)
+        continue;
+
+      for (c = 0; c < RGB; ++c)
+        {
+          gfloat *_p = pix + i * pix_stride + c,
+                   p = *_p;
+
+          local  = chrom      * p +
+                   chrom_comp * lum[i];
+          global = chrom      * channel[c].avg +
+                   chrom_comp * world_lin.avg;
+          adapt  = light      * local +
+                   light_comp * global;
+
+          p  /= p + powf (intensity * adapt, contrast);
+          *_p = p;
+          reinhard05_stats_update (&normalise, p);
+        }
+    }
+
+  /* Normalise the pixel values */
+  reinhard05_stats_finish (&normalise);
+
+  for (i = 0; i < result->width * result->height; ++i)
+    {
+      for (c = 0; c < pix_stride; ++c)
+        {
+          gfloat *p = pix + i * pix_stride + c;
+          *p        = (*p - normalise.min) / normalise.range;
+        }
+    }
+
+  /* Cleanup and set the output */
+  gegl_buffer_set (output, result, babl_format (OUTPUT_FORMAT), pix,
+                   GEGL_AUTO_ROWSTRIDE);
+  g_free (pix);
+  g_free (lum);
+
+  return TRUE;
+}
+
+
+/*
+ */
+static void
+gegl_chant_class_init (GeglChantClass *klass)
+{
+  GeglOperationClass       *operation_class;
+  GeglOperationFilterClass *filter_class;
+
+  operation_class = GEGL_OPERATION_CLASS (klass);
+  filter_class    = GEGL_OPERATION_FILTER_CLASS (klass);
+
+  filter_class->process = reinhard05_process;
+
+  operation_class->prepare                 = reinhard05_prepare;
+  operation_class->get_required_for_output = reinhard05_get_required_for_output;
+  operation_class->get_cached_region       = reinhard05_get_cached_region;
+
+  operation_class->name        = "gegl:reinhard05";
+  operation_class->categories  = "tonemapping";
+  operation_class->description =
+        _("Adapt an image, which may have a high dynamic range, for "
+	  "presentation using a low dynamic range. This is an efficient "
+          "global operator derived from simple physiological observations, "
+          "producing luminance within the range 0.0-1.0");
+}
+
+#endif
+
diff --git a/tests/compositions/reference/reinhard05.png b/tests/compositions/reference/reinhard05.png
new file mode 100644
index 0000000..92b78c9
Binary files /dev/null and b/tests/compositions/reference/reinhard05.png differ
diff --git a/tests/compositions/reinhard05.xml b/tests/compositions/reinhard05.xml
new file mode 100644
index 0000000..6008d47
--- /dev/null
+++ b/tests/compositions/reinhard05.xml
@@ -0,0 +1,15 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<gegl>
+  <node operation='gegl:reinhard05'>
+    <params>
+      <param name='brightness'>0</param>
+      <param name='chromatic'>0.8</param>
+      <param name='light'>0.3</param>
+    </params>
+  </node>
+  <node operation='gegl:load'>
+    <params>
+      <param name='path'>data/parliament.hdr</param>
+    </params>
+  </node>
+</gegl>



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