Revision 74f2632206090a0dfd2cc7ddb91a875f22a9347b authored by houz on 19 December 2015, 21:41:39 UTC, committed by houz on 19 December 2015, 21:41:39 UTC
Possible fix for the import dialog crash.
clahe.c
/*
This file is part of darktable,
copyright (c) 2010 Henrik Andersson.
darktable is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
darktable is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with darktable. If not, see <http://www.gnu.org/licenses/>.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "common/darktable.h"
#include "common/colorspaces.h"
#include "develop/develop.h"
#include "develop/imageop.h"
#include "control/control.h"
#include "dtgtk/resetlabel.h"
#include "gui/gtk.h"
#include "bauhaus/bauhaus.h"
#include <gtk/gtk.h>
#include <inttypes.h>
#include <stdlib.h>
#include <math.h>
#include <assert.h>
#include <string.h>
#define CLIP(x) ((x < 0) ? 0.0 : (x > 1.0) ? 1.0 : x)
#define ROUND_POSISTIVE(f) ((unsigned int)((f)+0.5))
DT_MODULE(1)
typedef struct dt_iop_rlce_params_t
{
double radius;
double slope;
} dt_iop_rlce_params_t;
typedef struct dt_iop_rlce_gui_data_t
{
GtkBox *vbox1, *vbox2;
GtkWidget *label1, *label2;
GtkWidget *scale1, *scale2; // radie pixels, slope
} dt_iop_rlce_gui_data_t;
typedef struct dt_iop_rlce_data_t
{
double radius;
double slope;
} dt_iop_rlce_data_t;
const char *name()
{
return _("local contrast");
}
int groups()
{
return IOP_GROUP_EFFECT;
}
int flags()
{
return IOP_FLAGS_INCLUDE_IN_STYLES | IOP_FLAGS_DEPRECATED;
}
void process(struct dt_iop_module_t *self, dt_dev_pixelpipe_iop_t *piece, void *ivoid, void *ovoid,
const dt_iop_roi_t *roi_in, const dt_iop_roi_t *roi_out)
{
dt_iop_rlce_data_t *data = (dt_iop_rlce_data_t *)piece->data;
const int ch = piece->colors;
// PASS1: Get a luminance map of image...
float *luminance = (float *)malloc(((size_t)roi_out->width * roi_out->height) * sizeof(float));
// double lsmax=0.0,lsmin=1.0;
#ifdef _OPENMP
#pragma omp parallel for default(none) schedule(static) shared(luminance, roi_in, roi_out, ivoid)
#endif
for(int j = 0; j < roi_out->height; j++)
{
float *in = (float *)ivoid + (size_t)j * roi_out->width * ch;
float *lm = luminance + (size_t)j * roi_out->width;
for(int i = 0; i < roi_out->width; i++)
{
double pmax = CLIP(fmax(in[0], fmax(in[1], in[2]))); // Max value in RGB set
double pmin = CLIP(fmin(in[0], fmin(in[1], in[2]))); // Min value in RGB set
*lm = (pmax + pmin) / 2.0; // Pixel luminocity
in += ch;
lm++;
}
}
// Params
const int rad = data->radius * roi_in->scale / piece->iscale;
const int bins = 256;
const float slope = data->slope;
// CLAHE
#ifdef _OPENMP
#pragma omp parallel for default(none) schedule(static) shared(luminance, roi_in, roi_out, ivoid, ovoid)
#endif
for(int j = 0; j < roi_out->height; j++)
{
int yMin = fmax(0, j - rad);
int yMax = fmin(roi_in->height, j + rad + 1);
int h = yMax - yMin;
int xMin0 = fmax(0, 0 - rad);
int xMax0 = fmin(roi_in->width - 1, rad);
int hist[bins + 1];
int clippedhist[bins + 1];
float dest[roi_out->width];
/* initially fill histogram */
memset(hist, 0, (bins + 1) * sizeof(int));
for(int yi = yMin; yi < yMax; ++yi)
for(int xi = xMin0; xi < xMax0; ++xi)
++hist[ROUND_POSISTIVE(luminance[(size_t)yi * roi_in->width + xi] * (float)bins)];
// Destination row
memset(dest, 0, roi_out->width * sizeof(float));
float *ld = dest;
for(int i = 0; i < roi_out->width; i++)
{
int v = ROUND_POSISTIVE(luminance[(size_t)j * roi_in->width + i] * (float)bins);
int xMin = fmax(0, i - rad);
int xMax = i + rad + 1;
int w = fmin(roi_in->width, xMax) - xMin;
int n = h * w;
int limit = (int)(slope * n / bins + 0.5f);
/* remove left behind values from histogram */
if(xMin > 0)
{
int xMin1 = xMin - 1;
for(int yi = yMin; yi < yMax; ++yi)
--hist[ROUND_POSISTIVE(luminance[(size_t)yi * roi_in->width + xMin1] * (float)bins)];
}
/* add newly included values to histogram */
if(xMax <= roi_in->width)
{
int xMax1 = xMax - 1;
for(int yi = yMin; yi < yMax; ++yi)
++hist[ROUND_POSISTIVE(luminance[(size_t)yi * roi_in->width + xMax1] * (float)bins)];
}
/* clip histogram and redistribute clipped entries */
memcpy(clippedhist, hist, (bins + 1) * sizeof(int));
int ce = 0, ceb = 0;
do
{
ceb = ce;
ce = 0;
for(int b = 0; b <= bins; b++)
{
int d = clippedhist[b] - limit;
if(d > 0)
{
ce += d;
clippedhist[b] = limit;
}
}
int d = (ce / (float)(bins + 1));
int m = ce % (bins + 1);
for(int h = 0; h <= bins; h++) clippedhist[h] += d;
if(m != 0)
{
int s = bins / (float)m;
for(int h = 0; h <= bins; h += s) ++clippedhist[h];
}
} while(ce != ceb);
/* build cdf of clipped histogram */
int hMin = bins;
for(int h = 0; h < hMin; h++)
if(clippedhist[h] != 0) hMin = h;
int cdf = 0;
for(int h = hMin; h <= v; h++) cdf += clippedhist[h];
int cdfMax = cdf;
for(int h = v + 1; h <= bins; h++) cdfMax += clippedhist[h];
int cdfMin = clippedhist[hMin];
*ld = (cdf - cdfMin) / (float)(cdfMax - cdfMin);
ld++;
}
// Apply row
float *in = ((float *)ivoid) + (size_t)j * roi_out->width * ch;
float *out = ((float *)ovoid) + (size_t)j * roi_out->width * ch;
for(int r = 0; r < roi_out->width; r++)
{
float H, S, L;
rgb2hsl(in, &H, &S, &L);
// hsl2rgb(out,H,S,( L / dest[r] ) * (L-lsmin) + lsmin );
hsl2rgb(out, H, S, dest[r]);
out += ch;
in += ch;
ld++;
}
}
// Cleanup
free(luminance);
}
static void radius_callback(GtkWidget *slider, gpointer user_data)
{
dt_iop_module_t *self = (dt_iop_module_t *)user_data;
if(self->dt->gui->reset) return;
dt_iop_rlce_params_t *p = (dt_iop_rlce_params_t *)self->params;
p->radius = dt_bauhaus_slider_get(slider);
dt_dev_add_history_item(darktable.develop, self, TRUE);
}
static void slope_callback(GtkWidget *slider, gpointer user_data)
{
dt_iop_module_t *self = (dt_iop_module_t *)user_data;
if(self->dt->gui->reset) return;
dt_iop_rlce_params_t *p = (dt_iop_rlce_params_t *)self->params;
p->slope = dt_bauhaus_slider_get(slider);
dt_dev_add_history_item(darktable.develop, self, TRUE);
}
void commit_params(struct dt_iop_module_t *self, dt_iop_params_t *p1, dt_dev_pixelpipe_t *pipe,
dt_dev_pixelpipe_iop_t *piece)
{
dt_iop_rlce_params_t *p = (dt_iop_rlce_params_t *)p1;
#ifdef HAVE_GEGL
fprintf(stderr, "[local contrast] TODO: implement gegl version!\n");
// pull in new params to gegl
#else
dt_iop_rlce_data_t *d = (dt_iop_rlce_data_t *)piece->data;
d->radius = p->radius;
d->slope = p->slope;
#endif
}
void init_pipe(struct dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece)
{
#ifdef HAVE_GEGL
// create part of the gegl pipeline
piece->data = NULL;
#else
piece->data = calloc(1, sizeof(dt_iop_rlce_data_t));
self->commit_params(self, self->default_params, pipe, piece);
#endif
}
void cleanup_pipe(struct dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece)
{
#ifdef HAVE_GEGL
// clean up everything again.
(void)gegl_node_remove_child(pipe->gegl, piece->input);
// no free necessary, no data is alloc'ed
#else
free(piece->data);
piece->data = NULL;
#endif
}
void gui_update(struct dt_iop_module_t *self)
{
dt_iop_module_t *module = (dt_iop_module_t *)self;
dt_iop_rlce_gui_data_t *g = (dt_iop_rlce_gui_data_t *)self->gui_data;
dt_iop_rlce_params_t *p = (dt_iop_rlce_params_t *)module->params;
dt_bauhaus_slider_set(g->scale1, p->radius);
dt_bauhaus_slider_set(g->scale2, p->slope);
}
void init(dt_iop_module_t *module)
{
module->params = calloc(1, sizeof(dt_iop_rlce_params_t));
module->default_params = calloc(1, sizeof(dt_iop_rlce_params_t));
module->default_enabled = 0;
module->priority = 916; // module order created by iop_dependencies.py, do not edit!
module->params_size = sizeof(dt_iop_rlce_params_t);
module->gui_data = NULL;
dt_iop_rlce_params_t tmp = (dt_iop_rlce_params_t){ 64, 1.25 };
memcpy(module->params, &tmp, sizeof(dt_iop_rlce_params_t));
memcpy(module->default_params, &tmp, sizeof(dt_iop_rlce_params_t));
}
void cleanup(dt_iop_module_t *module)
{
free(module->params);
module->params = NULL;
}
void gui_init(struct dt_iop_module_t *self)
{
self->gui_data = malloc(sizeof(dt_iop_rlce_gui_data_t));
dt_iop_rlce_gui_data_t *g = (dt_iop_rlce_gui_data_t *)self->gui_data;
dt_iop_rlce_params_t *p = (dt_iop_rlce_params_t *)self->params;
self->widget = GTK_WIDGET(gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0));
g->vbox1 = GTK_BOX(gtk_box_new(GTK_ORIENTATION_VERTICAL, DT_GUI_IOP_MODULE_CONTROL_SPACING));
g->vbox2 = GTK_BOX(gtk_box_new(GTK_ORIENTATION_VERTICAL, DT_GUI_IOP_MODULE_CONTROL_SPACING));
gtk_box_pack_start(GTK_BOX(self->widget), GTK_WIDGET(g->vbox1), FALSE, FALSE, 5);
gtk_box_pack_start(GTK_BOX(self->widget), GTK_WIDGET(g->vbox2), TRUE, TRUE, 5);
g->label1 = dtgtk_reset_label_new(_("radius"), self, &p->radius, sizeof(float));
gtk_box_pack_start(GTK_BOX(g->vbox1), g->label1, TRUE, TRUE, 0);
g->label2 = dtgtk_reset_label_new(_("amount"), self, &p->slope, sizeof(float));
gtk_box_pack_start(GTK_BOX(g->vbox1), g->label2, TRUE, TRUE, 0);
g->scale1 = dt_bauhaus_slider_new_with_range(NULL, 0.0, 256.0, 1.0,
p->radius, 0);
g->scale2 = dt_bauhaus_slider_new_with_range(NULL, 1.0, 3.0, 0.05,
p->slope, 2);
// dtgtk_slider_set_format_type(g->scale2,DARKTABLE_SLIDER_FORMAT_PERCENT);
gtk_box_pack_start(GTK_BOX(g->vbox2), GTK_WIDGET(g->scale1), TRUE, TRUE, 0);
gtk_box_pack_start(GTK_BOX(g->vbox2), GTK_WIDGET(g->scale2), TRUE, TRUE, 0);
g_object_set(G_OBJECT(g->scale1), "tooltip-text", _("size of features to preserve"), (char *)NULL);
g_object_set(G_OBJECT(g->scale2), "tooltip-text", _("strength of the effect"), (char *)NULL);
g_signal_connect(G_OBJECT(g->scale1), "value-changed", G_CALLBACK(radius_callback), self);
g_signal_connect(G_OBJECT(g->scale2), "value-changed", G_CALLBACK(slope_callback), self);
}
void gui_cleanup(struct dt_iop_module_t *self)
{
free(self->gui_data);
self->gui_data = NULL;
}
// modelines: These editor modelines have been set for all relevant files by tools/update_modelines.sh
// vim: shiftwidth=2 expandtab tabstop=2 cindent
// kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-space on;
![swh spinner](/static/img/swh-spinner.gif)
Computing file changes ...