[gegl] operations: reinhard05 tone mapping operator
- From: Martin Nordholts <martinn src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gegl] operations: reinhard05 tone mapping operator
- Date: Wed, 11 Aug 2010 18:03:26 +0000 (UTC)
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]