|
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 |
|