Blame operations/common/exp-combine.c

Packit bc1512
/* This file is an image processing operation for GEGL
Packit bc1512
 *
Packit bc1512
 * GEGL is free software; you can redistribute it and/or
Packit bc1512
 * modify it under the terms of the GNU Lesser General Public
Packit bc1512
 * License as published by the Free Software Foundation; either
Packit bc1512
 * version 3 of the License, or (at your option) any later version.
Packit bc1512
 *
Packit bc1512
 * GEGL is distributed in the hope that it will be useful,
Packit bc1512
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit bc1512
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit bc1512
 * Lesser General Public License for more details.
Packit bc1512
 *
Packit bc1512
 * You should have received a copy of the GNU Lesser General Public
Packit bc1512
 * License along with GEGL; if not, see <http://www.gnu.org/licenses/>.
Packit bc1512
 *
Packit bc1512
 * Copyright        2010 Danny Robson      <danny@blubinc.net>
Packit bc1512
 * (pfscalibration) 2006 Grzegorz Krawczyk <gkrawczyk@users.sourceforge.net>
Packit bc1512
 */
Packit bc1512
Packit bc1512
#include "config.h"
Packit bc1512
#include <glib/gi18n-lib.h>
Packit bc1512
Packit bc1512
#ifdef GEGL_CHANT_PROPERTIES
Packit bc1512
Packit bc1512
gegl_chant_string(exposures, _("Exposure Values"), "",
Packit bc1512
                  _("Relative brightness of each exposure in EV"))
Packit bc1512
gegl_chant_int   (steps, _("Discretization Bits"),
Packit bc1512
                  8, 32, 12,
Packit bc1512
                  _("Log2 of source's discretization steps"))
Packit bc1512
gegl_chant_double (sigma, _("Weight Sigma"),
Packit bc1512
                  0.0f, 32.0f, 8.0f,
Packit bc1512
                  _("Weight distrubtion sigma controlling response contributions"))
Packit bc1512
Packit bc1512
#else
Packit bc1512
Packit bc1512
/*#define DEBUG_LINEARIZE*/
Packit bc1512
/*#define DEBUG_SAVE_CURVES*/
Packit bc1512
Packit bc1512
#include <gegl-plugin.h>
Packit bc1512
struct _GeglChant
Packit bc1512
{
Packit bc1512
  GeglOperationFilter parent_instance;
Packit bc1512
  gpointer            properties;
Packit bc1512
};
Packit bc1512
Packit bc1512
typedef struct
Packit bc1512
{
Packit bc1512
  GeglOperationFilterClass parent_class;
Packit bc1512
} GeglChantClass;
Packit bc1512
Packit bc1512
Packit bc1512
#define GEGL_CHANT_C_FILE "exp-combine.c"
Packit bc1512
#include "gegl-chant.h"
Packit bc1512
GEGL_DEFINE_DYNAMIC_OPERATION(GEGL_TYPE_OPERATION_FILTER)
Packit bc1512
Packit bc1512
#include <errno.h>
Packit bc1512
#include <math.h>
Packit bc1512
#include <stdio.h>
Packit bc1512
Packit bc1512
#include "gegl-debug.h"
Packit bc1512
#include "graph/gegl-node.h"
Packit bc1512
#include "graph/gegl-pad.h"
Packit bc1512
Packit bc1512
Packit bc1512
static const gchar *PAD_FORMAT = "R'G'B' float";
Packit bc1512
static const gchar *EXP_PREFIX = "exposure-";
Packit bc1512
Packit bc1512
enum
Packit bc1512
{
Packit bc1512
  PROP_OUTPUT = 1,
Packit bc1512
  PROP_INPUT,
Packit bc1512
  PROP_AUX
Packit bc1512
};
Packit bc1512
Packit bc1512
Packit bc1512
/* maximum iterations after algorithm accepts local minima */
Packit bc1512
static const guint MAXIT = 500;
Packit bc1512
Packit bc1512
/* maximum accepted error */
Packit bc1512
static const gfloat MAX_DELTA  = 1e-5f;
Packit bc1512
static const gfloat MIN_WEIGHT = 1e-3f;
Packit bc1512
Packit bc1512
/* number of pixels used if downscaling for curve estimation */
Packit bc1512
static const guint  MAX_SCALED_PIXELS = 1000 * 1000;
Packit bc1512
Packit bc1512
typedef enum
Packit bc1512
{
Packit bc1512
  PIXELS_ACTIVE,    /* Must be lowest valid ID, zero */
Packit bc1512
  PIXELS_FULL,
Packit bc1512
  PIXELS_SCALED,
Packit bc1512
Packit bc1512
  NUM_PIXEL_BUCKETS
Packit bc1512
} pixel_bucket;
Packit bc1512
Packit bc1512
Packit bc1512
typedef struct _exposure
Packit bc1512
{
Packit bc1512
  /* Immediately higher and lower exposures */
Packit bc1512
  struct _exposure *hi;
Packit bc1512
  struct _exposure *lo;
Packit bc1512
Packit bc1512
  gfloat *pixels[NUM_PIXEL_BUCKETS];
Packit bc1512
Packit bc1512
  /* 'Duration' of exposure. May only be relatively correct against the
Packit bc1512
   * other exposures in sequence.
Packit bc1512
   */
Packit bc1512
  gfloat  ti;
Packit bc1512
} exposure;
Packit bc1512
Packit bc1512
Packit bc1512
static void
Packit bc1512
gegl_expcombine_exposures_set_active (GSList       *exposures,
Packit bc1512
                                      pixel_bucket  bucket)
Packit bc1512
{
Packit bc1512
  g_return_if_fail (bucket > PIXELS_ACTIVE && bucket < NUM_PIXEL_BUCKETS);
Packit bc1512
Packit bc1512
  for (; exposures; exposures = exposures->next)
Packit bc1512
    {
Packit bc1512
      exposure *e              = exposures->data;
Packit bc1512
      e->pixels[PIXELS_ACTIVE] = e->pixels[bucket];
Packit bc1512
    }
Packit bc1512
}
Packit bc1512
Packit bc1512
Packit bc1512
/* Comparator for struct exposure: order by ti */
Packit bc1512
static int
Packit bc1512
gegl_expcombine_exposure_cmp (gconstpointer _a,
Packit bc1512
                              gconstpointer _b)
Packit bc1512
{
Packit bc1512
  const exposure *a = _a,
Packit bc1512
                 *b = _b;
Packit bc1512
Packit bc1512
  if (a->ti > b->ti)
Packit bc1512
    return  1;
Packit bc1512
  else if (a->ti < b->ti)
Packit bc1512
    return -1;
Packit bc1512
  else
Packit bc1512
    return  0;
Packit bc1512
}
Packit bc1512
Packit bc1512
Packit bc1512
static exposure*
Packit bc1512
gegl_expcombine_new_exposure (void)
Packit bc1512
{
Packit bc1512
  exposure *e = g_new (exposure, 1);
Packit bc1512
  e->hi = e->lo = e;
Packit bc1512
Packit bc1512
  memset (e->pixels, 0, sizeof (e->pixels));
Packit bc1512
  e->ti = NAN;
Packit bc1512
Packit bc1512
  return e;
Packit bc1512
}
Packit bc1512
Packit bc1512
Packit bc1512
static void
Packit bc1512
gegl_expcombine_destroy_exposure (exposure *e)
Packit bc1512
{
Packit bc1512
  guint i, j;
Packit bc1512
Packit bc1512
  /* Unlink ourselves from the next hi and lo exposures */
Packit bc1512
  g_return_if_fail (e->lo);
Packit bc1512
  g_return_if_fail (e->hi);
Packit bc1512
Packit bc1512
  e->lo->hi = (e->hi == e) ? e->lo : e->hi;
Packit bc1512
  e->hi->lo = (e->lo == e) ? e->hi : e->lo;
Packit bc1512
Packit bc1512
  /* Free each pixel bucket. Each time a non-null is found, free it then null
Packit bc1512
   * any references to the same pixels in later buckets.
Packit bc1512
   */
Packit bc1512
  for (i = PIXELS_ACTIVE + 1; i < NUM_PIXEL_BUCKETS; ++i)
Packit bc1512
    {
Packit bc1512
      if (e->pixels[i])
Packit bc1512
        {
Packit bc1512
          g_free (e->pixels[i]);
Packit bc1512
Packit bc1512
          for (j = i + 1; j < NUM_PIXEL_BUCKETS; ++j)
Packit bc1512
              if (e->pixels[j] == e->pixels[i])
Packit bc1512
                  e->pixels[j] = NULL;
Packit bc1512
        }
Packit bc1512
    }
Packit bc1512
Packit bc1512
  g_free (e);
Packit bc1512
}
Packit bc1512
Packit bc1512
Packit bc1512
/* Initialise a response curve to linear. The absolute values are probably
Packit bc1512
 * irrelevant as the curve should be normalised later.
Packit bc1512
 */
Packit bc1512
static void
Packit bc1512
gegl_expcombine_response_linear (gfloat *response,
Packit bc1512
                                 guint   steps)
Packit bc1512
{
Packit bc1512
  guint i;
Packit bc1512
  for (i = 0; i < steps; ++i)
Packit bc1512
    response[i] = i / 255.0f;
Packit bc1512
}
Packit bc1512
Packit bc1512
Packit bc1512
/* Generate a weighting curve for the range of pixel values. Weights pixels in
Packit bc1512
 * the centre of the range more highly than the extremes.
Packit bc1512
 *
Packit bc1512
 * -sigma              weights the exponential arguments
Packit bc1512
 * -step_min,step_max  describes the region we provide weighting for
Packit bc1512
 */
Packit bc1512
static void
Packit bc1512
gegl_expcombine_weights_gauss (gfloat *weights,
Packit bc1512
                               guint   steps,
Packit bc1512
                               guint   step_min,
Packit bc1512
                               guint   step_max,
Packit bc1512
                               gfloat  sigma)
Packit bc1512
{
Packit bc1512
  gfloat step_mid1, step_mid2;
Packit bc1512
  gfloat weight;
Packit bc1512
  guint i;
Packit bc1512
Packit bc1512
  g_return_if_fail (weights);
Packit bc1512
  g_return_if_fail (step_min <= step_max);
Packit bc1512
  g_return_if_fail (step_min <  steps);
Packit bc1512
  g_return_if_fail (step_max <  steps);
Packit bc1512
Packit bc1512
  step_mid1 = step_min + (step_max - step_min) / 2.0f - 0.5f;
Packit bc1512
  step_mid2 = (step_mid1 - step_min) * (step_mid1 - step_min);
Packit bc1512
Packit bc1512
  for (i = 0; i < steps; ++i)
Packit bc1512
    {
Packit bc1512
      if (i < step_min || i > step_max)
Packit bc1512
        {
Packit bc1512
          weights[i] = 0.0f;
Packit bc1512
          continue;
Packit bc1512
        }
Packit bc1512
Packit bc1512
      /* gkrawczyk: that's not really a gaussian, but equation is
Packit bc1512
       * taken from Robertson02 paper.
Packit bc1512
       */
Packit bc1512
      weight = exp ( -sigma                 *
Packit bc1512
                    ((gfloat)i - step_mid1) *
Packit bc1512
                    ((gfloat)i - step_mid1) /
Packit bc1512
                     step_mid2);
Packit bc1512
Packit bc1512
      /* ignore very low weights. we rely on these weights being set to zero
Packit bc1512
       * in the response estimation code, thus this should not be removed.
Packit bc1512
       */
Packit bc1512
      if (weight < MIN_WEIGHT)
Packit bc1512
        weights[i] = 0.0f;
Packit bc1512
      else
Packit bc1512
        weights[i] = weight;
Packit bc1512
    }
Packit bc1512
}
Packit bc1512
Packit bc1512
Packit bc1512
/* Normalise the response curve, in place, using the midpoint of the non-zero
Packit bc1512
 * region of the response curve. Returns the mid-point value of the curve.
Packit bc1512
 */
Packit bc1512
static gfloat
Packit bc1512
gegl_expcombine_normalize (gfloat *response,
Packit bc1512
                           guint   steps)
Packit bc1512
{
Packit bc1512
  guint  step_min, step_max, step_mid;
Packit bc1512
  guint  i;
Packit bc1512
  gfloat val_mid;
Packit bc1512
Packit bc1512
  g_return_val_if_fail (response, NAN);
Packit bc1512
  g_return_val_if_fail (steps > 0, NAN);
Packit bc1512
Packit bc1512
  /* Find the first and last non-zero values in response curve */
Packit bc1512
  for (step_min = 0;
Packit bc1512
       step_min < steps && response[step_min] == 0;
Packit bc1512
       ++step_min)
Packit bc1512
    ;
Packit bc1512
  for (step_max = steps - 1;
Packit bc1512
       step_max > 0 && response[step_max] == 0;
Packit bc1512
       --step_max)
Packit bc1512
    ;
Packit bc1512
Packit bc1512
  g_return_val_if_fail (step_max >= step_min, NAN);
Packit bc1512
  step_mid = step_min + (step_max - step_min) / 2;
Packit bc1512
Packit bc1512
  /* Find the non-zero mid-value of the response curve */
Packit bc1512
  val_mid = response[step_mid];
Packit bc1512
  if (val_mid == 0.0f)
Packit bc1512
  {
Packit bc1512
    /* find first non-zero middle response */
Packit bc1512
    while (step_mid < step_max && response[step_mid] == 0.0f)
Packit bc1512
        ++step_mid;
Packit bc1512
    val_mid = response[step_mid];
Packit bc1512
  }
Packit bc1512
Packit bc1512
  /* Normalize response curve values via the mid-value */
Packit bc1512
  g_return_val_if_fail (val_mid != 0.0f, 0.0f);
Packit bc1512
Packit bc1512
  for (i = 0; i < steps; ++i)
Packit bc1512
      response[i] /= val_mid;
Packit bc1512
Packit bc1512
  return val_mid;
Packit bc1512
}
Packit bc1512
Packit bc1512
Packit bc1512
/* Apply debevec model for response curve application. Does not suffer from
Packit bc1512
 * apparent discolouration in some areas of image reconstruction when
Packit bc1512
 * compared with robertson.
Packit bc1512
 */
Packit bc1512
static int
Packit bc1512
gegl_expcombine_apply_debevec  (gfloat              *hdr,
Packit bc1512
                                const guint          components,
Packit bc1512
                                GSList              *imgs,
Packit bc1512
                                gfloat             **response,
Packit bc1512
                                const gfloat        *weighting,
Packit bc1512
                                const guint          steps,
Packit bc1512
                                const GeglRectangle *extent)
Packit bc1512
{
Packit bc1512
  guint  step_min, step_max;
Packit bc1512
  guint  num_imgs    = g_slist_length (imgs);
Packit bc1512
  guint  saturated   = 0;
Packit bc1512
  guint  pixel_count = (guint)(extent->width * extent->height);
Packit bc1512
  guint  i, j;
Packit bc1512
Packit bc1512
  g_return_val_if_fail (hdr,                        G_MAXINT);
Packit bc1512
  g_return_val_if_fail (g_slist_length (imgs) > 0,  G_MAXINT);
Packit bc1512
  g_return_val_if_fail (response,                   G_MAXINT);
Packit bc1512
  g_return_val_if_fail (weighting,                  G_MAXINT);
Packit bc1512
  g_return_val_if_fail (steps > 0,                  G_MAXINT);
Packit bc1512
  g_return_val_if_fail (extent,                     G_MAXINT);
Packit bc1512
  g_return_val_if_fail (extent->width  > 0,         G_MAXINT);
Packit bc1512
  g_return_val_if_fail (extent->height > 0,         G_MAXINT);
Packit bc1512
Packit bc1512
  /* anti saturation: calculate trusted camera output range */
Packit bc1512
  for (step_min = 0, i = step_min; i <    steps; ++i)
Packit bc1512
    if (weighting[i] > 0)
Packit bc1512
      {
Packit bc1512
        step_min = i;
Packit bc1512
        break;
Packit bc1512
      }
Packit bc1512
  for (step_max = steps - 1, i = step_max; i > step_min; --i)
Packit bc1512
    if (weighting[i] > 0)
Packit bc1512
      {
Packit bc1512
        step_max = i;
Packit bc1512
        break;
Packit bc1512
      }
Packit bc1512
  g_return_val_if_fail (step_max >= step_min, G_MAXINT);
Packit bc1512
Packit bc1512
  for (j = 0; j < pixel_count; ++j)
Packit bc1512
    {
Packit bc1512
      gfloat  sum[3]  = { 0.0f, 0.0f, 0.0f },
Packit bc1512
              div     = 0.0f;
Packit bc1512
      gfloat  ti_max  = G_MINFLOAT,
Packit bc1512
              ti_min  = G_MAXFLOAT;
Packit bc1512
      gfloat  average;
Packit bc1512
      guint   white_step[3], black_step[3];
Packit bc1512
Packit bc1512
      /* all exposures for each pixel */
Packit bc1512
      for (i = 0; i < num_imgs; ++i)
Packit bc1512
        {
Packit bc1512
          exposure *exp_i;
Packit bc1512
          guint     step[3], step_hi[3], step_lo[3];
Packit bc1512
Packit bc1512
          exp_i    = g_slist_nth_data (imgs, i);
Packit bc1512
          step[0]  = exp_i->pixels[PIXELS_ACTIVE][0 + j * components];
Packit bc1512
          step[1]  = exp_i->pixels[PIXELS_ACTIVE][1 + j * components];
Packit bc1512
          step[2]  = exp_i->pixels[PIXELS_ACTIVE][2 + j * components];
Packit bc1512
          g_return_val_if_fail (step[0] < steps && step[1] < steps && step[2] < steps, G_MAXINT);
Packit bc1512
Packit bc1512
          /* anti saturation: observe minimum exposure time at which saturated
Packit bc1512
           * value is present, and maximum exp time at which black value is
Packit bc1512
           * present
Packit bc1512
           */
Packit bc1512
          if ((step[0] > step_max ||
Packit bc1512
               step[1] > step_max ||
Packit bc1512
               step[2] > step_max) && exp_i->ti < ti_min)
Packit bc1512
            {
Packit bc1512
              white_step[0] = MIN (step[0], steps);
Packit bc1512
              white_step[1] = MIN (step[1], steps);
Packit bc1512
              white_step[2] = MIN (step[2], steps);
Packit bc1512
              ti_min        = exp_i->ti;
Packit bc1512
            }
Packit bc1512
Packit bc1512
          if ((step[0] < step_min ||
Packit bc1512
               step[1] < step_min ||
Packit bc1512
               step[2] < step_min) && exp_i->ti > ti_max)
Packit bc1512
            {
Packit bc1512
              /* This could be protected by a max (0, step), but we're using
Packit bc1512
               * unsigned ints so it would be worthless currently.
Packit bc1512
               */
Packit bc1512
              black_step[0] = step[0];
Packit bc1512
              black_step[1] = step[1];
Packit bc1512
              black_step[2] = step[2];
Packit bc1512
              ti_max        = exp_i->ti;
Packit bc1512
            }
Packit bc1512
Packit bc1512
          /* anti ghosting: monotonous increase in time should result in
Packit bc1512
           * monotonous increase in intensity; make forward and backward check,
Packit bc1512
           * ignore value if condition not satisfied
Packit bc1512
           */
Packit bc1512
          step_lo[0] = exp_i->lo->pixels[PIXELS_ACTIVE][0 + j * components];
Packit bc1512
          step_lo[1] = exp_i->lo->pixels[PIXELS_ACTIVE][1 + j * components];
Packit bc1512
          step_lo[2] = exp_i->lo->pixels[PIXELS_ACTIVE][2 + j * components];
Packit bc1512
          g_return_val_if_fail (step_lo[0] < steps &&
Packit bc1512
                                step_lo[1] < steps &&
Packit bc1512
                                step_lo[2] < steps, G_MAXINT);
Packit bc1512
Packit bc1512
          step_hi[0] = exp_i->hi->pixels[PIXELS_ACTIVE][0 + j * components];
Packit bc1512
          step_hi[1] = exp_i->hi->pixels[PIXELS_ACTIVE][1 + j * components];
Packit bc1512
          step_hi[2] = exp_i->hi->pixels[PIXELS_ACTIVE][2 + j * components];
Packit bc1512
          g_return_val_if_fail (step_hi[0] < steps &&
Packit bc1512
                                step_hi[1] < steps &&
Packit bc1512
                                step_hi[2] < steps, G_MAXINT);
Packit bc1512
Packit bc1512
          if (step_lo[0] > step[0] ||
Packit bc1512
              step_lo[1] > step[1] ||
Packit bc1512
              step_lo[2] > step[2])
Packit bc1512
            {
Packit bc1512
              white_step[0] = step[0];
Packit bc1512
              white_step[1] = step[1];
Packit bc1512
              white_step[2] = step[2];
Packit bc1512
              /* TODO: This is not an fminf in luminance. Should it be? */
Packit bc1512
              /* ti_min        = fminf (exp_i->ti, ti_min); */
Packit bc1512
              continue;
Packit bc1512
            }
Packit bc1512
Packit bc1512
          if (step_hi[0] < step[0] ||
Packit bc1512
              step_hi[1] < step[1] ||
Packit bc1512
              step_hi[2] < step[2])
Packit bc1512
            {
Packit bc1512
              black_step[0] = step[0];
Packit bc1512
              black_step[1] = step[1];
Packit bc1512
              black_step[2] = step[2];
Packit bc1512
              /* TODO: This is not an fminf in luminance. Should it be? */
Packit bc1512
              /* ti_max        = fmaxf (exp_i->ti, ti_max); */
Packit bc1512
              continue;
Packit bc1512
            }
Packit bc1512
Packit bc1512
          /* Weight the addition into each channel by the average weight of
Packit bc1512
           * the channels and the exposure duration. This is the key
Packit bc1512
           * difference to robertson.
Packit bc1512
           */
Packit bc1512
          average  = weighting [step[0]] +
Packit bc1512
                     weighting [step[1]] +
Packit bc1512
                     weighting [step[2]];
Packit bc1512
          average /= 3.0f;
Packit bc1512
Packit bc1512
          sum[0] += response[0][step[0]] * average / exp_i->ti;
Packit bc1512
          sum[1] += response[1][step[1]] * average / exp_i->ti;
Packit bc1512
          sum[2] += response[2][step[2]] * average / exp_i->ti;
Packit bc1512
Packit bc1512
          div += average;
Packit bc1512
        }
Packit bc1512
Packit bc1512
      g_return_val_if_fail (sum[0] >= 0.0f, G_MAXINT);
Packit bc1512
      g_return_val_if_fail (sum[1] >= 0.0f, G_MAXINT);
Packit bc1512
      g_return_val_if_fail (sum[2] >= 0.0f, G_MAXINT);
Packit bc1512
      g_return_val_if_fail (   div >= 0.0f, G_MAXINT);
Packit bc1512
      g_return_val_if_fail (ti_max <= ti_min, G_MAXINT);
Packit bc1512
Packit bc1512
      /* anti saturation: if a meaningful representation of pixel was not
Packit bc1512
       * found, replace it with information from observed data
Packit bc1512
       */
Packit bc1512
      if (div == 0.0f)
Packit bc1512
          ++saturated;
Packit bc1512
Packit bc1512
      if (div == 0.0f && ti_max != G_MINFLOAT)
Packit bc1512
        {
Packit bc1512
          sum[0] = response[0][black_step[0]] / ti_max;
Packit bc1512
          sum[1] = response[1][black_step[1]] / ti_max;
Packit bc1512
          sum[2] = response[2][black_step[2]] / ti_max;
Packit bc1512
          div = 1.0f;
Packit bc1512
        }
Packit bc1512
      else if (div == 0.0f && ti_min != G_MAXFLOAT)
Packit bc1512
        {
Packit bc1512
          sum[0] = response[0][white_step[0]] / ti_min;
Packit bc1512
          sum[1] = response[1][white_step[1]] / ti_min;
Packit bc1512
          sum[2] = response[2][white_step[2]] / ti_min;
Packit bc1512
          div = 1.0f;
Packit bc1512
        }
Packit bc1512
Packit bc1512
      if (div == 0.0f)
Packit bc1512
        {
Packit bc1512
          hdr[0 + j * components] = 0.0f;
Packit bc1512
          hdr[1 + j * components] = 0.0f;
Packit bc1512
          hdr[2 + j * components] = 0.0f;
Packit bc1512
        }
Packit bc1512
      else
Packit bc1512
        {
Packit bc1512
          hdr[0 + j * components] = sum[0] / div;
Packit bc1512
          hdr[1 + j * components] = sum[1] / div;
Packit bc1512
          hdr[2 + j * components] = sum[2] / div;
Packit bc1512
        }
Packit bc1512
    }
Packit bc1512
Packit bc1512
  return saturated;
Packit bc1512
}
Packit bc1512
Packit bc1512
Packit bc1512
/**
Packit bc1512
 * @brief Create HDR image by applying response curve to given images
Packit bc1512
 * taken with different exposures
Packit bc1512
 *
Packit bc1512
 * @param hdr [out] HDR image
Packit bc1512
 * @param imgs      list of scene exposures
Packit bc1512
 * @param response  camera response function (array size of `steps')
Packit bc1512
 * @param weighting weighting function for camera output values (array size of `steps')
Packit bc1512
 * @param steps     number of camera output levels
Packit bc1512
 * @return          number of saturated pixels in the HDR image (0: all OK)
Packit bc1512
 */
Packit bc1512
static int
Packit bc1512
gegl_expcombine_apply_response (gfloat              *hdr,
Packit bc1512
                                const guint          offset,
Packit bc1512
                                const guint          components,
Packit bc1512
                                GSList              *imgs,
Packit bc1512
                                const gfloat        *response,
Packit bc1512
                                const gfloat        *weighting,
Packit bc1512
                                const guint          steps,
Packit bc1512
                                const GeglRectangle *extent)
Packit bc1512
{
Packit bc1512
  guint  step_min, step_max;
Packit bc1512
  guint  num_imgs  = g_slist_length (imgs);
Packit bc1512
  guint  saturated = 0;
Packit bc1512
  guint  pixel_count = (guint)(extent->width * extent->height);
Packit bc1512
  guint  i, j;
Packit bc1512
Packit bc1512
  g_return_val_if_fail (hdr,                        G_MAXINT);
Packit bc1512
  g_return_val_if_fail (g_slist_length (imgs) > 0,  G_MAXINT);
Packit bc1512
  g_return_val_if_fail (response,                   G_MAXINT);
Packit bc1512
  g_return_val_if_fail (weighting,                  G_MAXINT);
Packit bc1512
  g_return_val_if_fail (steps > 0,                  G_MAXINT);
Packit bc1512
  g_return_val_if_fail (extent,                     G_MAXINT);
Packit bc1512
  g_return_val_if_fail (extent->width  > 0,         G_MAXINT);
Packit bc1512
  g_return_val_if_fail (extent->height > 0,         G_MAXINT);
Packit bc1512
Packit bc1512
  /* anti saturation: calculate trusted camera output range */
Packit bc1512
  for (step_min = 0, i = step_min; i < steps; ++i)
Packit bc1512
    {
Packit bc1512
      if (weighting[i] > 0)
Packit bc1512
        {
Packit bc1512
          step_min = i;
Packit bc1512
          break;
Packit bc1512
        }
Packit bc1512
    }
Packit bc1512
  for (step_max = steps - 1, i = step_max; i > step_min; --i)
Packit bc1512
    {
Packit bc1512
      if (weighting[i] > 0)
Packit bc1512
        {
Packit bc1512
          step_max = i;
Packit bc1512
          break;
Packit bc1512
        }
Packit bc1512
    }
Packit bc1512
Packit bc1512
  g_return_val_if_fail (step_max >= step_min, G_MAXINT);
Packit bc1512
Packit bc1512
  for (j = 0; j < pixel_count; ++j)
Packit bc1512
    {
Packit bc1512
      gfloat  sum    = 0.0f,
Packit bc1512
              div    = 0.0f;
Packit bc1512
      gfloat  ti_max = G_MINFLOAT,
Packit bc1512
              ti_min = G_MAXFLOAT;
Packit bc1512
Packit bc1512
      /* all exposures for each pixel */
Packit bc1512
      for (i = 0; i < num_imgs; ++i)
Packit bc1512
        {
Packit bc1512
          exposure *exp_i;
Packit bc1512
          guint     step, step_hi, step_lo;
Packit bc1512
Packit bc1512
          exp_i = g_slist_nth_data (imgs, i);
Packit bc1512
          step  = exp_i->pixels[PIXELS_ACTIVE][offset + j * components];
Packit bc1512
          g_return_val_if_fail (step < steps, G_MAXINT);
Packit bc1512
Packit bc1512
          /* anti saturation: observe minimum exposure time at which saturated
Packit bc1512
           * value is present, and maximum exp time at which black value is
Packit bc1512
           * present
Packit bc1512
           */
Packit bc1512
          if (step > step_max)
Packit bc1512
              ti_min = fminf (ti_min, exp_i->ti);
Packit bc1512
          if (step < step_min)
Packit bc1512
              ti_max = fmaxf (ti_max, exp_i->ti);
Packit bc1512
Packit bc1512
          /* anti ghosting: monotonous increase in time should result in
Packit bc1512
           * monotonous increase in intensity; make forward and backward check,
Packit bc1512
           * ignore value if condition not satisfied
Packit bc1512
           */
Packit bc1512
          step_lo = exp_i->lo->pixels[PIXELS_ACTIVE][offset + j * components];
Packit bc1512
          step_hi = exp_i->hi->pixels[PIXELS_ACTIVE][offset + j * components];
Packit bc1512
Packit bc1512
          if (step_lo > step || step_hi < step)
Packit bc1512
               continue;
Packit bc1512
Packit bc1512
          sum += weighting[step] * exp_i->ti * response[step];
Packit bc1512
          div += weighting[step] * exp_i->ti * exp_i->ti;
Packit bc1512
        }
Packit bc1512
Packit bc1512
      g_return_val_if_fail (sum >= 0.0f, G_MAXINT);
Packit bc1512
      g_return_val_if_fail (div >= 0.0f, G_MAXINT);
Packit bc1512
      g_return_val_if_fail (ti_max <= ti_min, G_MAXINT);
Packit bc1512
Packit bc1512
      /* anti saturation: if a meaningful representation of pixel was not
Packit bc1512
       * found, replace it with information from observed data
Packit bc1512
       */
Packit bc1512
      if (div == 0.0f)
Packit bc1512
          ++saturated;
Packit bc1512
Packit bc1512
      if (div == 0.0f && ti_max != G_MINFLOAT)
Packit bc1512
        {
Packit bc1512
          sum = response[step_min];
Packit bc1512
          div = ti_max;
Packit bc1512
        }
Packit bc1512
Packit bc1512
      if (div == 0.0f && ti_min != G_MAXFLOAT)
Packit bc1512
        {
Packit bc1512
          sum = response[step_max];
Packit bc1512
          div = ti_min;
Packit bc1512
        }
Packit bc1512
Packit bc1512
      if (div != 0.0f)
Packit bc1512
          hdr[offset + j * components] = sum / div;
Packit bc1512
      else
Packit bc1512
          hdr[offset + j * components] = 0.0f;
Packit bc1512
    }
Packit bc1512
Packit bc1512
  return saturated;
Packit bc1512
}
Packit bc1512
Packit bc1512
Packit bc1512
/**
Packit bc1512
 * @brief Calculate camera response using Robertson02 algorithm
Packit bc1512
 *
Packit bc1512
 * @param luminance [out] estimated luminance values
Packit bc1512
 * @param imgs            list of scene exposures
Packit bc1512
 * @param response  [out] array to put response function
Packit bc1512
 * @param weighting       weights
Packit bc1512
 * @param steps           max camera output (no of discrete steps)
Packit bc1512
 * @return                number of saturated pixels in the HDR image (0: all OK)
Packit bc1512
 */
Packit bc1512
Packit bc1512
static int
Packit bc1512
gegl_expcombine_get_response (gfloat              *hdr,
Packit bc1512
                              const guint          offset,
Packit bc1512
                              const guint          components,
Packit bc1512
                              GSList              *imgs,
Packit bc1512
                              gfloat              *response,
Packit bc1512
                              const gfloat        *weighting,
Packit bc1512
                              guint                steps,
Packit bc1512
                              const GeglRectangle *extent)
Packit bc1512
{
Packit bc1512
  gfloat  *response_old;
Packit bc1512
  gfloat   delta, delta_old;
Packit bc1512
Packit bc1512
  gulong  *card;
Packit bc1512
  gfloat  *sum;
Packit bc1512
Packit bc1512
  guint    i, j, hits;
Packit bc1512
  guint    iterations;
Packit bc1512
  guint    pixel_count = (guint)(extent->height * extent->width);
Packit bc1512
  gulong   saturated  = 0;
Packit bc1512
  gboolean converged;
Packit bc1512
Packit bc1512
  g_return_val_if_fail (hdr,                        G_MAXINT);
Packit bc1512
  g_return_val_if_fail (g_slist_length (imgs) > 1,  G_MAXINT);
Packit bc1512
  g_return_val_if_fail (response,                   G_MAXINT);
Packit bc1512
  g_return_val_if_fail (weighting,                  G_MAXINT);
Packit bc1512
  g_return_val_if_fail (steps > 0,                  G_MAXINT);
Packit bc1512
  g_return_val_if_fail (extent,                     G_MAXINT);
Packit bc1512
  g_return_val_if_fail (extent->width  > 0,         G_MAXINT);
Packit bc1512
  g_return_val_if_fail (extent->height > 0,         G_MAXINT);
Packit bc1512
Packit bc1512
  response_old = g_new (gfloat, steps);
Packit bc1512
Packit bc1512
  /* 0. Initialization */
Packit bc1512
  gegl_expcombine_normalize (response, steps);
Packit bc1512
  for (i = 0; i < steps; ++i)
Packit bc1512
      response_old[i] = response[i];
Packit bc1512
Packit bc1512
  saturated = gegl_expcombine_apply_response (hdr, offset, components, imgs,
Packit bc1512
                                              response, weighting, steps,
Packit bc1512
                                              extent);
Packit bc1512
Packit bc1512
  converged  = FALSE;
Packit bc1512
  card       = g_new (gulong, steps);
Packit bc1512
  sum        = g_new (gfloat, steps);
Packit bc1512
  iterations = 0;
Packit bc1512
  delta_old  = 0.0f;
Packit bc1512
Packit bc1512
  /* Optimization process */
Packit bc1512
  while (!converged)
Packit bc1512
  {
Packit bc1512
    GSList *cursor = imgs;
Packit bc1512
    /* 1. Minimize with respect to response */
Packit bc1512
    for (i = 0; i < steps; ++i)
Packit bc1512
      {
Packit bc1512
        card[i] = 0;
Packit bc1512
        sum [i] = 0.0f;
Packit bc1512
      }
Packit bc1512
Packit bc1512
    for (cursor = imgs; cursor; cursor = cursor->next)
Packit bc1512
      {
Packit bc1512
        exposure *e = cursor->data;
Packit bc1512
Packit bc1512
        for (j = 0; j < pixel_count; ++j)
Packit bc1512
          {
Packit bc1512
            guint step = e->pixels[PIXELS_ACTIVE][offset + j * components];
Packit bc1512
            if (step < steps)
Packit bc1512
              {
Packit bc1512
                sum[step] += e->ti * hdr[offset + j * components];
Packit bc1512
                ++card[step];
Packit bc1512
              }
Packit bc1512
            else
Packit bc1512
              g_warning ("robertson02: m out of range: %u", step);
Packit bc1512
          }
Packit bc1512
      }
Packit bc1512
Packit bc1512
    for (i = 0; i < steps; ++i)
Packit bc1512
      {
Packit bc1512
        if (card[i] != 0)
Packit bc1512
          response[i] = sum[i] / card[i];
Packit bc1512
        else
Packit bc1512
          response[i] = 0.0f;
Packit bc1512
      }
Packit bc1512
Packit bc1512
    /* 2. Apply new response */
Packit bc1512
    gegl_expcombine_normalize (response, steps);
Packit bc1512
    saturated = gegl_expcombine_apply_response (hdr, offset, components, imgs,
Packit bc1512
                                                response, weighting, steps,
Packit bc1512
                                                extent);
Packit bc1512
Packit bc1512
    /* 3. Check stopping condition */
Packit bc1512
    delta = 0.0f;
Packit bc1512
    hits  = 0;
Packit bc1512
Packit bc1512
    for (i = 0; i < steps; ++i)
Packit bc1512
      {
Packit bc1512
        g_return_val_if_fail (response[i] >= 0, G_MAXINT);
Packit bc1512
Packit bc1512
        if (response[i] != 0.0f)
Packit bc1512
          {
Packit bc1512
            gdouble diff     = response[i] - response_old[i];
Packit bc1512
            delta           += diff * diff;
Packit bc1512
            response_old[i]  = response[i];
Packit bc1512
            ++hits;
Packit bc1512
          }
Packit bc1512
      }
Packit bc1512
    delta /= hits;
Packit bc1512
Packit bc1512
Packit bc1512
    GEGL_NOTE (GEGL_DEBUG_PROCESS,
Packit bc1512
               "exp-combine convergence, #%u delta=%f (coverage: %u%%)",
Packit bc1512
               iterations,
Packit bc1512
               delta,
Packit bc1512
               (guint)(100.0f * (gfloat)hits / steps));
Packit bc1512
    if (delta < MAX_DELTA)
Packit bc1512
      converged = TRUE;
Packit bc1512
    else if (isnan (delta) || (iterations > MAXIT && delta_old < delta))
Packit bc1512
      {
Packit bc1512
        g_warning ("exp-combine failed to converge. too noisy data in range.");
Packit bc1512
        break;
Packit bc1512
      }
Packit bc1512
Packit bc1512
    delta_old = delta;
Packit bc1512
    ++iterations;
Packit bc1512
  }
Packit bc1512
Packit bc1512
  g_free (response_old);
Packit bc1512
  g_free (card);
Packit bc1512
  g_free (sum);
Packit bc1512
Packit bc1512
  return saturated;
Packit bc1512
}
Packit bc1512
Packit bc1512
Packit bc1512
/* All exposure pads have the common prefix, followed by a numeric
Packit bc1512
 * identifier.
Packit bc1512
 */
Packit bc1512
static inline gboolean
Packit bc1512
gegl_expcombine_is_exposure_padname (const gchar *p)
Packit bc1512
{
Packit bc1512
  return g_str_has_prefix (p, EXP_PREFIX);
Packit bc1512
}
Packit bc1512
Packit bc1512
Packit bc1512
static inline gboolean
Packit bc1512
gegl_expcombine_is_exposure_pad (GeglPad *p)
Packit bc1512
{
Packit bc1512
  return gegl_expcombine_is_exposure_padname (gegl_pad_get_name (p));
Packit bc1512
}
Packit bc1512
Packit bc1512
Packit bc1512
/* XXX: We create a large quantity of pads for all the exposures, hoping we
Packit bc1512
 * create sufficient numbers and they don't utilise too many resources. This
Packit bc1512
 * is a bad hack, but works for the time being...
Packit bc1512
 */
Packit bc1512
static void
Packit bc1512
gegl_expcombine_attach (GeglOperation *operation)
Packit bc1512
{
Packit bc1512
  GObjectClass  *object_class = G_OBJECT_GET_CLASS (operation);
Packit bc1512
  gchar padname[16];
Packit bc1512
  guint i;
Packit bc1512
Packit bc1512
  g_object_class_install_property (G_OBJECT_GET_CLASS (operation),
Packit bc1512
                                   PROP_INPUT,
Packit bc1512
                                   g_param_spec_object ("output",
Packit bc1512
                                                        "output",
Packit bc1512
                                                        "Output buffer",
Packit bc1512
                                                        GEGL_TYPE_BUFFER,
Packit bc1512
                                                        G_PARAM_READWRITE |
Packit bc1512
                                                        GEGL_PARAM_PAD_OUTPUT));
Packit bc1512
  gegl_operation_create_pad (operation,
Packit bc1512
                             g_object_class_find_property (object_class,
Packit bc1512
                                                           "output"));
Packit bc1512
  for (i = 0; i <= 99; ++i)
Packit bc1512
    {
Packit bc1512
      snprintf (padname, G_N_ELEMENTS (padname), "exposure_%u", i);
Packit bc1512
      g_object_class_install_property (G_OBJECT_GET_CLASS (operation),
Packit bc1512
                                       PROP_INPUT,
Packit bc1512
                                       g_param_spec_object (padname,
Packit bc1512
                                                            padname,
Packit bc1512
                                                            "Exposure input.",
Packit bc1512
                                                            GEGL_TYPE_BUFFER,
Packit bc1512
                                                            G_PARAM_READWRITE |
Packit bc1512
                                                            GEGL_PARAM_PAD_INPUT));
Packit bc1512
      gegl_operation_create_pad (operation,
Packit bc1512
                                 g_object_class_find_property (object_class,
Packit bc1512
                                                               padname));
Packit bc1512
    }
Packit bc1512
}
Packit bc1512
Packit bc1512
Packit bc1512
Packit bc1512
static void
Packit bc1512
gegl_expcombine_prepare (GeglOperation *operation)
Packit bc1512
{
Packit bc1512
  GSList *inputs = gegl_node_get_input_pads (operation->node);
Packit bc1512
Packit bc1512
  /* Set all the pads output formats, and ensure there is an appropriate node
Packit bc1512
   * property for each */
Packit bc1512
  for (; inputs; inputs = inputs->next)
Packit bc1512
    {
Packit bc1512
      GeglPad     *pad     = inputs->data;
Packit bc1512
      const gchar *padname = gegl_pad_get_name (pad);
Packit bc1512
Packit bc1512
      gegl_pad_set_format (pad, babl_format (PAD_FORMAT));
Packit bc1512
Packit bc1512
      /* Create properties for all pads that don't have them, allows arbitrary
Packit bc1512
       * numbers of pads.
Packit bc1512
       */
Packit bc1512
      if (g_object_class_find_property (G_OBJECT_GET_CLASS (operation),
Packit bc1512
                                        padname))
Packit bc1512
          continue;
Packit bc1512
Packit bc1512
      g_warning ("Could not find property for pad '%s'",
Packit bc1512
                 gegl_pad_get_name (pad));
Packit bc1512
    }
Packit bc1512
Packit bc1512
  gegl_operation_set_format (operation, "output", babl_format (PAD_FORMAT));
Packit bc1512
}
Packit bc1512
Packit bc1512
Packit bc1512
/* Order pads beginning with EXP_PREFIX by their trailing exposure value.
Packit bc1512
 * Non-exposure pads come first, then exposure pads without an EV, then
Packit bc1512
 * ordered exposure pads.
Packit bc1512
 */
Packit bc1512
static int
Packit bc1512
gegl_expcombine_pad_cmp (gconstpointer _a, gconstpointer _b)
Packit bc1512
{
Packit bc1512
  const gchar *a = gegl_pad_get_name (GEGL_PAD (_a)),
Packit bc1512
              *b = gegl_pad_get_name (GEGL_PAD (_b));
Packit bc1512
  guint64      x, y;
Packit bc1512
Packit bc1512
  if (!g_str_has_prefix (b, EXP_PREFIX)) return  1;
Packit bc1512
  if (!g_str_has_prefix (a, EXP_PREFIX)) return -1;
Packit bc1512
Packit bc1512
  a = strrchr (a, '-');
Packit bc1512
  b = strrchr (b, '-');
Packit bc1512
Packit bc1512
  g_return_val_if_fail (b, -1);
Packit bc1512
  g_return_val_if_fail (a, -1);
Packit bc1512
Packit bc1512
  y = g_ascii_strtoll (b + 1, NULL, 10);
Packit bc1512
  if (errno) return  1;
Packit bc1512
  x = g_ascii_strtoll (a + 1, NULL, 10);
Packit bc1512
  if (errno) return -1;
Packit bc1512
Packit bc1512
  if (x < y) return -1;
Packit bc1512
  if (x > y) return  1;
Packit bc1512
  return 0;
Packit bc1512
}
Packit bc1512
Packit bc1512
Packit bc1512
/* Extract a list of exposure pixel and metadata from the operation context.
Packit bc1512
 * The data is not guaranteed to be ordered within the list. May return NULL
Packit bc1512
 * for error.
Packit bc1512
 */
Packit bc1512
static GSList *
Packit bc1512
gegl_expcombine_get_exposures (GeglOperation        *operation,
Packit bc1512
                               GeglOperationContext *context,
Packit bc1512
                               const GeglRectangle  *full_roi,
Packit bc1512
                               GeglRectangle        *scaled_roi)
Packit bc1512
{
Packit bc1512
  GeglChantO    *o          = GEGL_CHANT_PROPERTIES (operation);
Packit bc1512
  gchar         *ev_cursor  = o->exposures;
Packit bc1512
  GSList        *exposures  = NULL,
Packit bc1512
                *inputs,
Packit bc1512
                *cursor;
Packit bc1512
  guint          components = babl_format_get_n_components (babl_format (PAD_FORMAT));
Packit bc1512
  gfloat         scale;
Packit bc1512
Packit bc1512
  g_return_val_if_fail (operation, NULL);
Packit bc1512
  g_return_val_if_fail (context, NULL);
Packit bc1512
  g_return_val_if_fail (full_roi, NULL);
Packit bc1512
  g_return_val_if_fail (!gegl_rectangle_is_empty (full_roi), NULL);
Packit bc1512
  g_return_val_if_fail (scaled_roi, NULL);
Packit bc1512
Packit bc1512
  /* Calculate the target dimensions for the downscaled buffer used in the
Packit bc1512
   * curve recovery. Does not enlarge the buffer.
Packit bc1512
   * sqrt does not cause issues with rectangular buffers as it's merely a
Packit bc1512
   * scaling factor for later.
Packit bc1512
   */
Packit bc1512
  {
Packit bc1512
    scale       = MAX_SCALED_PIXELS /
Packit bc1512
                  (gfloat)(full_roi->width * full_roi->height);
Packit bc1512
    scale       = sqrtf (scale);
Packit bc1512
    *scaled_roi = *full_roi;
Packit bc1512
Packit bc1512
    if (scale > 1.0f)
Packit bc1512
      {
Packit bc1512
        scale = 1.0f;
Packit bc1512
      }
Packit bc1512
    else
Packit bc1512
      {
Packit bc1512
        scaled_roi->width  = full_roi->width  * scale;
Packit bc1512
        scaled_roi->height = full_roi->height * scale;
Packit bc1512
      }
Packit bc1512
  }
Packit bc1512
Packit bc1512
  inputs = g_slist_copy (gegl_node_get_input_pads (operation->node));
Packit bc1512
  inputs = g_slist_sort (inputs, gegl_expcombine_pad_cmp);
Packit bc1512
Packit bc1512
  /* Read in each of the exposure buffers */
Packit bc1512
  for (cursor = inputs; cursor; cursor = cursor->next)
Packit bc1512
    {
Packit bc1512
      GeglBuffer *buffer;
Packit bc1512
      GeglPad    *pad = cursor->data;
Packit bc1512
      exposure   *e;
Packit bc1512
Packit bc1512
      /* Weed out non-exposure pads */
Packit bc1512
      if (!gegl_expcombine_is_exposure_pad (pad))
Packit bc1512
        {
Packit bc1512
          if (strcmp (gegl_pad_get_name (pad), "aux"))
Packit bc1512
              g_warning ("Unexpected pad name '%s' in exp-combine",
Packit bc1512
                         gegl_pad_get_name (pad));
Packit bc1512
          continue;
Packit bc1512
        }
Packit bc1512
Packit bc1512
      /* Add exposure to list */
Packit bc1512
      buffer  = gegl_operation_context_get_source (context,
Packit bc1512
                                                   gegl_pad_get_name (pad));
Packit bc1512
      if (!buffer)
Packit bc1512
          continue;
Packit bc1512
Packit bc1512
      e                = gegl_expcombine_new_exposure ();
Packit bc1512
      e->pixels[PIXELS_FULL]   = g_new (gfloat, full_roi->width  *
Packit bc1512
                                                full_roi->height *
Packit bc1512
                                                components);
Packit bc1512
      gegl_buffer_get (buffer, full_roi, 1.0, babl_format (PAD_FORMAT),
Packit bc1512
                       e->pixels[PIXELS_FULL], GEGL_AUTO_ROWSTRIDE,
Packit bc1512
                       GEGL_ABYSS_NONE);
Packit bc1512
Packit bc1512
      g_return_val_if_fail (scale <= 1.0f, NULL);
Packit bc1512
      if (scale == 1.0f)
Packit bc1512
          e->pixels[PIXELS_SCALED] = e->pixels[PIXELS_FULL];
Packit bc1512
      else
Packit bc1512
        {
Packit bc1512
          e->pixels[PIXELS_SCALED] = g_new (gfloat,
Packit bc1512
                                            (scaled_roi->width  *
Packit bc1512
                                             scaled_roi->height *
Packit bc1512
                                             components));
Packit bc1512
          gegl_buffer_get (buffer, scaled_roi, scale, babl_format (PAD_FORMAT),
Packit bc1512
                           e->pixels[PIXELS_SCALED], GEGL_AUTO_ROWSTRIDE,
Packit bc1512
                           GEGL_ABYSS_NONE);
Packit bc1512
        }
Packit bc1512
Packit bc1512
      e->pixels[PIXELS_ACTIVE] = e->pixels[PIXELS_FULL];
Packit bc1512
Packit bc1512
      /* Read the exposure time: relate APEX brightness value only as a
Packit bc1512
       * function of exposure time that is assume aperture = 1 and
Packit bc1512
       * sensitivity = 1
Packit bc1512
       */
Packit bc1512
      e->ti = g_ascii_strtod (ev_cursor, &ev_cursor);
Packit bc1512
      e->ti = 1.0f / powf (2.0f, e->ti);
Packit bc1512
Packit bc1512
      /* Krawczyk: absolute calibration: this magic multiplier is a result
Packit bc1512
       * of my research in internet plus a bit of tweaking with a luminance
Packit bc1512
       * meter. tested with canon 350d, so might be a bit off for other
Packit bc1512
       * cameras.
Packit bc1512
       *   e->ti /= 1.0592f * 11.4f / 3.125f;
Packit bc1512
       */
Packit bc1512
Packit bc1512
      /* For the sake of sanity, we scale the times to produce an image which
Packit bc1512
       * is much closer to the 'normal' 0-1 range. This prevents components
Packit bc1512
       * from going massively outside of our gamut.
Packit bc1512
       *
Packit bc1512
       * In particular this aids visual inspection and use of standard pixel
Packit bc1512
       * value comparison functions in testing.
Packit bc1512
       */
Packit bc1512
      e->ti *= 5e4f;
Packit bc1512
Packit bc1512
      if (errno)
Packit bc1512
        {
Packit bc1512
          g_warning ("Invalid exposure values specified for exp-combine");
Packit bc1512
          g_slist_foreach (exposures,
Packit bc1512
                           (GFunc)gegl_expcombine_destroy_exposure,
Packit bc1512
                           NULL);
Packit bc1512
          g_slist_free    (exposures);
Packit bc1512
Packit bc1512
          return NULL;
Packit bc1512
        }
Packit bc1512
Packit bc1512
      exposures = g_slist_prepend (exposures, e);
Packit bc1512
    }
Packit bc1512
Packit bc1512
  /* Link each exposure's high and low sibling pointers. Simplifies
Packit bc1512
   * anti-ghosting computations later.
Packit bc1512
   */
Packit bc1512
  exposures = g_slist_sort (exposures, gegl_expcombine_exposure_cmp);
Packit bc1512
  {
Packit bc1512
    exposure *prev = exposures->data;
Packit bc1512
    for (cursor = exposures; cursor; cursor = cursor->next)
Packit bc1512
      {
Packit bc1512
        exposure *e = cursor->data;
Packit bc1512
Packit bc1512
        prev->hi = e;
Packit bc1512
        e->lo    = prev;
Packit bc1512
        prev     = e;
Packit bc1512
      }
Packit bc1512
  }
Packit bc1512
Packit bc1512
  if (exposures == NULL)
Packit bc1512
      g_warning ("exp-combine did not find any suitable input pads");
Packit bc1512
  if (*ev_cursor != '\0')
Packit bc1512
      g_warning ("too many supplied EVs for input pads");
Packit bc1512
Packit bc1512
  g_slist_free (inputs);
Packit bc1512
  return exposures;
Packit bc1512
}
Packit bc1512
Packit bc1512
Packit bc1512
#ifdef DEBUG_SAVE_CURVES
Packit bc1512
static void
Packit bc1512
gegl_expcombine_save_curves (const gfloat *response,
Packit bc1512
                             guint         steps,
Packit bc1512
                             const gchar  *path)
Packit bc1512
{
Packit bc1512
  guint i;
Packit bc1512
  FILE *fd = fopen (path, "w");
Packit bc1512
Packit bc1512
  for (i = 0; i < steps; ++i)
Packit bc1512
    fprintf (fd, "%u %f\n", i, response[i]);
Packit bc1512
  fclose (fd);
Packit bc1512
Packit bc1512
  return;
Packit bc1512
}
Packit bc1512
#endif
Packit bc1512
Packit bc1512
Packit bc1512
static gboolean
Packit bc1512
gegl_expcombine_process (GeglOperation        *operation,
Packit bc1512
                         GeglOperationContext *context,
Packit bc1512
                         const gchar          *output_pad,
Packit bc1512
                         const GeglRectangle  *full_roi,
Packit bc1512
                         gint                  level)
Packit bc1512
{
Packit bc1512
  GeglChantO *o           = GEGL_CHANT_PROPERTIES (operation);
Packit bc1512
  GeglBuffer *output      = gegl_operation_context_get_target (context,
Packit bc1512
                                                               output_pad);
Packit bc1512
Packit bc1512
  GeglRectangle   scaled_roi;
Packit bc1512
  GSList         *cursor;
Packit bc1512
  GSList         *exposures   = gegl_expcombine_get_exposures (operation,
Packit bc1512
                                                               context,
Packit bc1512
                                                               full_roi,
Packit bc1512
                                                               &scaled_roi);
Packit bc1512
Packit bc1512
  guint   i;
Packit bc1512
  guint   steps      = 1 << o->steps,
Packit bc1512
          step_max   = 0,
Packit bc1512
          step_min   = steps - 1;
Packit bc1512
Packit bc1512
  guint   components = babl_format_get_n_components (babl_format (PAD_FORMAT));
Packit bc1512
  gfloat *hdr        = g_new (gfloat, full_roi->width  *
Packit bc1512
                                      full_roi->height *
Packit bc1512
                                      components);
Packit bc1512
  gfloat *weights     =   g_new (gfloat, steps);
Packit bc1512
  gfloat *response[3] = { g_new (gfloat, steps),
Packit bc1512
                          g_new (gfloat, steps),
Packit bc1512
                          g_new (gfloat, steps) };
Packit bc1512
Packit bc1512
  guint   saturated  = 0;
Packit bc1512
  gfloat  over       = 0.0f,
Packit bc1512
          under      = 0.0f;
Packit bc1512
Packit bc1512
  g_return_val_if_fail (output,    FALSE);
Packit bc1512
  g_return_val_if_fail (exposures, FALSE);
Packit bc1512
Packit bc1512
  g_return_val_if_fail (steps      > 0, FALSE);
Packit bc1512
  g_return_val_if_fail (components > 0, FALSE);
Packit bc1512
Packit bc1512
  /* Find the highest and lowest valid steps in the output. Remap from the
Packit bc1512
   * 'normal' 0.0-1.0 floats to the range of integer steps.
Packit bc1512
   */
Packit bc1512
  for (cursor = exposures; cursor; cursor = cursor->next)
Packit bc1512
    {
Packit bc1512
      exposure *e = cursor->data;
Packit bc1512
Packit bc1512
      for (i = 0; i < full_roi->width * full_roi->height * components; ++i)
Packit bc1512
        {
Packit bc1512
          /* Clamp the values we receive to [0.0, 1.0) and record the
Packit bc1512
           * magnitude of this over/underflow.
Packit bc1512
           */
Packit bc1512
          if (e->pixels[PIXELS_FULL][i] <= 0.0f)
Packit bc1512
            {
Packit bc1512
              under += fabs (e->pixels[PIXELS_FULL][i]);
Packit bc1512
              e->pixels[PIXELS_FULL][i] = 0.0f;
Packit bc1512
            }
Packit bc1512
          else if (e->pixels[PIXELS_FULL][i] > 1.0f)
Packit bc1512
            {
Packit bc1512
              over  += e->pixels[PIXELS_FULL][i] - 1.0f;
Packit bc1512
              e->pixels[PIXELS_FULL][i] = 1.0f;
Packit bc1512
            }
Packit bc1512
Packit bc1512
          e->pixels[PIXELS_FULL][i] *= (steps - 1);
Packit bc1512
          if (e->pixels[PIXELS_FULL][i] > 0.0f)
Packit bc1512
            {
Packit bc1512
              step_max = MAX (step_max, e->pixels[PIXELS_FULL][i]);
Packit bc1512
              step_min = MIN (step_min, e->pixels[PIXELS_FULL][i]);
Packit bc1512
            }
Packit bc1512
        }
Packit bc1512
Packit bc1512
      if (e->pixels[PIXELS_SCALED] == e->pixels[PIXELS_FULL])
Packit bc1512
          continue;
Packit bc1512
Packit bc1512
      for (i = 0; i < scaled_roi.width * scaled_roi.height * components; ++i)
Packit bc1512
        {
Packit bc1512
          e->pixels[PIXELS_SCALED][i]  = CLAMP (e->pixels[PIXELS_SCALED][i], 0.0f, 1.0f);
Packit bc1512
          e->pixels[PIXELS_SCALED][i] *= (steps - 1);
Packit bc1512
        }
Packit bc1512
Packit bc1512
    }
Packit bc1512
Packit bc1512
  g_return_val_if_fail (step_max >= step_min, FALSE);
Packit bc1512
  if (under || over)
Packit bc1512
      g_warning ("Unexpected pixel bounds. "
Packit bc1512
                 "Overflow error: %f, underflow error: %f",
Packit bc1512
                 over  / components,
Packit bc1512
                 under / components);
Packit bc1512
Packit bc1512
  /* Initialise response curves and weights for estimation. Estimate and
Packit bc1512
   * retrieve the converted output. Apply the response curves to the input
Packit bc1512
   * using the debevec model rather than using the robertson output.
Packit bc1512
   */
Packit bc1512
  gegl_expcombine_weights_gauss (weights, steps, step_min, step_max, o->sigma);
Packit bc1512
  gegl_expcombine_exposures_set_active (exposures, PIXELS_SCALED);
Packit bc1512
Packit bc1512
  for (i = 0; i < components; ++i)
Packit bc1512
    {
Packit bc1512
      gegl_expcombine_response_linear (response[i], steps);
Packit bc1512
      saturated += gegl_expcombine_get_response (hdr, i, components, exposures,
Packit bc1512
                                                 response[i], weights, steps,
Packit bc1512
                                                 &scaled_roi);
Packit bc1512
    }
Packit bc1512
Packit bc1512
#ifdef DEBUG_SAVE_CURVES
Packit bc1512
  gegl_expcombine_save_curves (response[0], steps, "response_r");
Packit bc1512
  gegl_expcombine_save_curves (response[1], steps, "response_g");
Packit bc1512
  gegl_expcombine_save_curves (response[2], steps, "response_b");
Packit bc1512
#endif
Packit bc1512
Packit bc1512
  gegl_expcombine_exposures_set_active (exposures, PIXELS_FULL);
Packit bc1512
  gegl_expcombine_apply_debevec (hdr, components, exposures, response, weights,
Packit bc1512
                            steps, full_roi);
Packit bc1512
Packit bc1512
  g_return_val_if_fail (G_N_ELEMENTS (response) == 3, FALSE);
Packit bc1512
  g_free (response[0]);
Packit bc1512
  g_free (response[1]);
Packit bc1512
  g_free (response[2]);
Packit bc1512
  g_free (weights);
Packit bc1512
Packit bc1512
  if (saturated)
Packit bc1512
    {
Packit bc1512
      GEGL_NOTE (GEGL_DEBUG_PROCESS,
Packit bc1512
                 "exp-combine discovered %u saturated pixels (%f%%), "
Packit bc1512
                 "results may be suboptimal", saturated,
Packit bc1512
                 100.0f * saturated / full_roi->width / full_roi->height);
Packit bc1512
    }
Packit bc1512
Packit bc1512
  /* Rescale the output so we can more easily visually inspect the output
Packit bc1512
   * using LDR outputs.
Packit bc1512
   */
Packit bc1512
#ifdef DEBUG_LINEARIZE
Packit bc1512
  {
Packit bc1512
    gfloat max = G_MINFLOAT;
Packit bc1512
    guint  i;
Packit bc1512
    for (i = 0; i < full_roi->width * full_roi->height * components; ++i)
Packit bc1512
        max = MAX (max, hdr[i]);
Packit bc1512
Packit bc1512
    for (i = 0; i < full_roi->width * full_roi->height * components; ++i)
Packit bc1512
        hdr[i] /= max;
Packit bc1512
  }
Packit bc1512
#endif
Packit bc1512
Packit bc1512
  /* Save the HDR components to the output buffer. */
Packit bc1512
  gegl_buffer_set (output, full_roi, 0, babl_format (PAD_FORMAT), hdr,
Packit bc1512
                   GEGL_AUTO_ROWSTRIDE);
Packit bc1512
  gegl_cache_computed (gegl_node_get_cache (operation->node), full_roi);
Packit bc1512
Packit bc1512
  /* Cleanup */
Packit bc1512
  g_free (hdr);
Packit bc1512
Packit bc1512
  g_slist_foreach (exposures, (GFunc)gegl_expcombine_destroy_exposure, NULL);
Packit bc1512
  g_slist_free    (exposures);
Packit bc1512
Packit bc1512
  return TRUE;
Packit bc1512
}
Packit bc1512
Packit bc1512
Packit bc1512
/* Grab each source's dimensions, and update a running bounding box with the
Packit bc1512
 * union of itself and the current source.
Packit bc1512
 * TODO: properly support inputs of varying dimensions
Packit bc1512
 */
Packit bc1512
static GeglRectangle
Packit bc1512
gegl_expcombine_get_bounding_box (GeglOperation *operation)
Packit bc1512
{
Packit bc1512
  GeglRectangle  result = {0,0,0,0};
Packit bc1512
  GSList        *inputs = gegl_node_get_input_pads (operation->node);
Packit bc1512
Packit bc1512
  for (; inputs; inputs = inputs->next)
Packit bc1512
    {
Packit bc1512
      GeglPad             *pad = inputs->data;
Packit bc1512
      const GeglRectangle *newrect;
Packit bc1512
Packit bc1512
      if (!gegl_expcombine_is_exposure_pad (pad))
Packit bc1512
        continue;
Packit bc1512
Packit bc1512
      /* Get the source bounds and update with the union */
Packit bc1512
      newrect = gegl_operation_source_get_bounding_box (operation,
Packit bc1512
                                                        gegl_pad_get_name (pad));
Packit bc1512
      if (!newrect)
Packit bc1512
        continue;
Packit bc1512
Packit bc1512
      if (!gegl_rectangle_is_empty (&result) &&
Packit bc1512
          !gegl_rectangle_equal (newrect, &result))
Packit bc1512
        {
Packit bc1512
          g_warning ("expcombine inputs are of varying dimensions");
Packit bc1512
        }
Packit bc1512
      gegl_rectangle_bounding_box (&result, newrect, &result);
Packit bc1512
    }
Packit bc1512
Packit bc1512
  if (gegl_rectangle_is_empty (&result))
Packit bc1512
      g_warning ("Bounding box for exp-combine should not be empty");
Packit bc1512
  return result;
Packit bc1512
}
Packit bc1512
Packit bc1512
Packit bc1512
static GeglRectangle
Packit bc1512
gegl_expcombine_get_cached_region (GeglOperation       *operation,
Packit bc1512
                                   const GeglRectangle *roi)
Packit bc1512
{
Packit bc1512
  return gegl_expcombine_get_bounding_box (operation);
Packit bc1512
}
Packit bc1512
Packit bc1512
Packit bc1512
/* We have to use all the input to produce any output */
Packit bc1512
static GeglRectangle
Packit bc1512
gegl_expcombine_get_required_for_output (GeglOperation       *operation,
Packit bc1512
                                         const gchar         *input_pad,
Packit bc1512
                                         const GeglRectangle *output_roi)
Packit bc1512
{
Packit bc1512
  return gegl_expcombine_get_bounding_box (operation);
Packit bc1512
}
Packit bc1512
Packit bc1512
static void
Packit bc1512
gegl_chant_class_init (GeglChantClass *klass)
Packit bc1512
{
Packit bc1512
  GeglOperationClass       *operation_class;
Packit bc1512
Packit bc1512
  operation_class = GEGL_OPERATION_CLASS (klass);
Packit bc1512
Packit bc1512
  operation_class->attach            = gegl_expcombine_attach;
Packit bc1512
  operation_class->process           = gegl_expcombine_process;
Packit bc1512
  operation_class->get_bounding_box  = gegl_expcombine_get_bounding_box;
Packit bc1512
  operation_class->get_cached_region = gegl_expcombine_get_cached_region;
Packit bc1512
Packit bc1512
  operation_class->prepare     = gegl_expcombine_prepare;
Packit bc1512
  operation_class->get_required_for_output = gegl_expcombine_get_required_for_output;
Packit bc1512
Packit bc1512
  gegl_operation_class_set_keys (operation_class,
Packit bc1512
  "name"       , "gegl:exp-combine",
Packit bc1512
  "categories" , "compositors",
Packit bc1512
  "description",
Packit bc1512
      _("Combine multiple scene exposures into one high range buffer"),
Packit bc1512
      NULL);
Packit bc1512
}
Packit bc1512
Packit bc1512
Packit bc1512
#endif
Packit bc1512