50#include <glib/gstdio.h>
87#define HISTN (1 << 11)
166 return _(
"color mapping");
171 return dt_iop_set_description(self, _(
"transfer a color palette and tonal repartition from one image to another"),
173 _(
"linear or non-linear, Lab, display-referred"),
174 _(
"non-linear, Lab"),
175 _(
"non-linear, Lab, display-referred"));
196 memset(hist, 0,
sizeof(
int) *
HISTN);
200 const int bin = CLAMP(
HISTN * col[4 * (
k *
width +
i) + 0] / 100.0, 0,
HISTN - 1);
205 for(
int k = 1;
k <
HISTN;
k++) hist[
k] += hist[
k - 1];
221 inv_hist[
i] = 100.0*
k/(float)
HISTN;
226 for(
int i = 0;
i <= last;
i++) inv_hist[
i] = 100.0f *
i / (
float)
HISTN;
227 for(
int i = last + 1;
i <
HISTN;
i++)
232 inv_hist[
i] = 100.0f *
k / (float)
HISTN;
246 const float dominance,
int *mapio)
248 const float weightscale = 10000.0f;
250 for(
int ki = 0; ki <
n; ki++)
253 float mdist = FLT_MAX;
254 for(
int ko = 0; ko <
n; ko++)
257 const float colordist = (mo[ko][0] - mi[ki][0]) * (mo[ko][0] - mi[ki][0])
258 + (mo[ko][1] - mi[ki][1]) * (mo[ko][1] - mi[ki][1]);
259 const float weightdist = weightscale * (wo[ko] - wi[ki]) * (wo[ko] - wi[ki]);
260 const float dist = colordist * (1.0f - dominance) + weightdist * dominance;
280 float mdist = FLT_MAX;
281 for(
int k = 0;
k <
n;
k++)
283 const float dist2 = (col[1] - mean[
k][0]) * (col[1] - mean[
k][0])
284 + (col[2] - mean[
k][1]) * (col[2] - mean[
k][1]);
285 weight[
k] = dist2 > 1.0e-6f ? 1.0f / dist2 : -1.0f;
286 if(dist2 < mdist) mdist = dist2;
289 for(
int k = 0;
k <
n;
k++)
300 float mdist = FLT_MAX;
302 for(
int k = 0;
k <
n;
k++)
304 const float dist = (col[1] - mean[
k][0]) * (col[1] - mean[
k][0])
305 + (col[2] - mean[
k][1]) * (col[2] - mean[
k][1]);
316 float2 *var_out,
float *weight_out)
323 int *
const cnt = malloc(
sizeof(
int) *
n);
326 float a_min = FLT_MAX, b_min = FLT_MAX, a_max = FLT_MIN, b_max = FLT_MIN;
328 for(
int s = 0; s < samples; s++)
333 const float a = col[4 * (
width * j +
i) + 1];
334 const float b = col[4 * (
width * j +
i) + 2];
336 a_min = fminf(a, a_min);
337 a_max = fmaxf(a, a_max);
338 b_min = fminf(b, b_min);
339 b_max = fmaxf(b, b_max);
343 for(
int k = 0;
k <
n;
k++)
345 mean_out[
k][0] = 0.9f * (a_min + (a_max - a_min) *
dt_points_get());
346 mean_out[
k][1] = 0.9f * (b_min + (b_max - b_min) *
dt_points_get());
347 var_out[
k][0] = var_out[
k][1] = weight_out[
k] = 0.0f;
348 mean[
k][0] = mean[
k][1] = var[
k][0] = var[
k][1] = 0.0f;
350 for(
int it = 0; it < nit; it++)
352 for(
int k = 0;
k <
n;
k++) cnt[
k] = 0;
355 for(
int s = 0; s < samples; s++)
360 for(
int k = 0;
k <
n;
k++)
362 const float L = col[4 * (
width * j +
i)];
374 var[c][0] +=
Lab[1] *
Lab[1];
378 var[c][1] +=
Lab[2] *
Lab[2];
382 mean[c][0] +=
Lab[1];
386 mean[c][1] +=
Lab[2];
390 for(
int k = 0;
k <
n;
k++)
392 if(cnt[
k] == 0)
continue;
393 mean_out[
k][0] = mean[
k][0] / cnt[
k];
394 mean_out[
k][1] = mean[
k][1] / cnt[
k];
395 var_out[
k][0] = var[
k][0] / cnt[
k] - mean_out[
k][0] * mean_out[
k][0];
396 var_out[
k][1] = var[
k][1] / cnt[
k] - mean_out[
k][1] * mean_out[
k][1];
397 mean[
k][0] = mean[
k][1] = var[
k][0] = var[
k][1] = 0.0f;
402 for(
int k = 0;
k <
n;
k++) count += cnt[
k];
403 for(
int k = 0;
k <
n;
k++) weight_out[
k] = (count > 0) ? (float)cnt[
k] / count : 0.0f;
414 for(
int k = 0;
k <
n;
k++)
417 if(var_out[
k][0] == 0.0f || var_out[
k][1] == 0.0f)
418 mean_out[
k][0] = mean_out[
k][1] = var_out[
k][0] = var_out[
k][1] = weight_out[
k] = 0;
421 var_out[
k][0] = sqrtf(var_out[
k][0]);
422 var_out[
k][1] = sqrtf(var_out[
k][1]);
427 for(
int i = 0;
i <
n - 1;
i++)
429 for(
int j = 0; j <
n - 1 -
i; j++)
431 if(weight_out[j] > weight_out[j + 1])
433 float2 temp_mean = { mean_out[j + 1][0], mean_out[j + 1][1] };
434 float2 temp_var = { var_out[j + 1][0], var_out[j + 1][1] };
435 float temp_weight = weight_out[j + 1];
437 mean_out[j + 1][0] = mean_out[j][0];
438 mean_out[j + 1][1] = mean_out[j][1];
439 var_out[j + 1][0] = var_out[j][0];
440 var_out[j + 1][1] = var_out[j][1];
441 weight_out[j + 1] = weight_out[j];
443 mean_out[j][0] = temp_mean[0];
444 mean_out[j][1] = temp_mean[1];
445 var_out[j][0] = temp_var[0];
446 var_out[j][1] = temp_var[1];
447 weight_out[j] = temp_weight;
461 float *
const restrict in = (
float *)ivoid;
462 float *
const restrict
out = (
float *)
ovoid;
468 const float sigma_s = 50.0f / scale;
500 const float dominance = data->dominance / 100.0f;
501 const float equalization = data->equalization / 100.0f;
504 int *
const mapio = malloc(
sizeof(
int) * data->n);
508 data->source_weight, dominance, mapio);
510 float2 *
const var_ratio = malloc(
sizeof(
float2) * data->n);
517 for(
int i = 0;
i < data->n;
i++)
520 = (data->target_var[
i][0] > 0.0f) ? data->source_var[mapio[
i]][0] / data->target_var[
i][0] : 0.0f;
522 = (data->target_var[
i][1] > 0.0f) ? data->source_var[mapio[
i]][1] / data->target_var[
i][1] : 0.0f;
528 for(
size_t k = 0;
k < npixels * 4;
k += 4)
530 const float L = in[
k];
531 out[
k] = 0.5f * ((
L * (1.0f - equalization)
532 + data->source_ihist[data->target_hist[(
int)CLAMP(
533 HISTN *
L / 100.0f, 0.0f, (
float)
HISTN - 1.0f)]] * equalization) -
L) + 50.0f;
534 out[
k] = CLAMP(
out[
k], 0.0f, 100.0f);
537 if(equalization > 0.001f)
567 for(
size_t j = 0; j < 4*npixels; j += 4)
569 const float L = in[j];
573 out[j] = 2.0f * (
out[j] - 50.0f) +
L;
574 out[j] = CLAMP(
out[j], 0.0f, 100.0f);
578 out[j + 1] =
out[j + 2] = 0.0f;
580 for(
int c = 0; c < data->n; c++)
582 out[j + 1] +=
weight[c] * ((
Lab[1] - data->target_mean[c][0]) * var_ratio[c][0]
583 + data->source_mean[mapio[c]][0]);
584 out[j + 2] +=
weight[c] * ((
Lab[2] - data->target_mean[c][1]) * var_ratio[c][1]
585 + data->source_mean[mapio[c]][1]);
588 out[j + 3] = in[j + 3];
609 const float sigma_s = 50.0f / scale;
616 const size_t basebuffer =
sizeof(float) * channels *
width *
height;
644 memset(
p->source_ihist, 0,
sizeof(
float) *
HISTN);
645 memset(
p->source_mean, 0,
sizeof(
float) *
MAXN * 2);
646 memset(
p->source_var, 0,
sizeof(
float) *
MAXN * 2);
647 memset(
p->source_weight, 0,
sizeof(
float) *
MAXN);
648 memset(
p->target_hist, 0,
sizeof(
int) *
HISTN);
649 memset(
p->target_mean, 0,
sizeof(
float) *
MAXN * 2);
650 memset(
p->target_var, 0,
sizeof(
float) *
MAXN * 2);
651 memset(
p->target_weight, 0,
sizeof(
float) *
MAXN);
664 p->flag &= ~HAS_SOURCE;
675 p->flag &= ~HAS_TARGET;
699 memcpy(
d->source_ihist,
g->flowback.hist,
sizeof(
float) *
HISTN);
700 memcpy(
d->source_mean,
g->flowback.mean,
sizeof(
float) *
MAXN * 2);
701 memcpy(
d->source_var,
g->flowback.var,
sizeof(
float) *
MAXN * 2);
702 memcpy(
d->source_weight,
g->flowback.weight,
sizeof(
float) *
MAXN);
703 d->n =
g->flowback.n;
717 if(widget ==
g->source_area)
719 mean =
p->source_mean;
724 mean =
p->target_mean;
729 GtkAllocation allocation;
730 gtk_widget_get_allocation(widget, &allocation);
732 int width = allocation.width,
height = allocation.height;
734 cairo_t *cr = cairo_create(cst);
735 cairo_set_source_rgb(cr, .2, .2, .2);
738 cairo_translate(cr, inset, inset);
744 const float qwd = (
width - (
p->n - 1) * sep) / (float)
p->n;
745 for(
int cl = 0; cl <
p->n; cl++)
748 for(
int j = -1; j <= 1; j++)
749 for(
int i = -1;
i <= 1;
i++)
752 double rgb[3] = { 0.5, 0.5, 0.5 };
755 Lab.a = (mean[cl][0] +
i * var[cl][0]);
756 Lab.b = (mean[cl][1] + j * var[cl][1]);
757 cmsDoTransform(
g->xform, &
Lab,
rgb, 1);
758 cairo_set_source_rgb(cr,
rgb[0],
rgb[1],
rgb[2]);
763 cairo_translate(cr, qwd + sep, 0);
767 cairo_set_source_surface(crf, cst, 0, 0);
769 cairo_surface_destroy(cst);
779 int new_source_clusters = 0;
791 const int width =
g->width;
793 const int ch =
g->ch;
817 new_source_clusters = 1;
836 if(new_source_clusters)
838 memcpy(
g->flowback.hist,
p->source_ihist,
sizeof(
float) *
HISTN);
839 memcpy(
g->flowback.mean,
p->source_mean,
sizeof(
float) *
MAXN * 2);
840 memcpy(
g->flowback.var,
p->source_var,
sizeof(
float) *
MAXN * 2);
841 memcpy(
g->flowback.weight,
p->source_weight,
sizeof(
float) *
MAXN);
842 g->flowback.n =
p->n;
844 FILE *
f = g_fopen(
"/tmp/dt_colormapping_loaded",
"wb");
847 if(fwrite(&
g->flowback,
sizeof(
g->flowback), 1,
f) < 1)
848 fprintf(stderr,
"[colormapping] could not write flowback file /tmp/dt_colormapping_loaded\n");
870 g->xform = cmsCreateTransform(hLab, TYPE_Lab_DBL, hsRGB, TYPE_RGB_DBL, INTENT_PERCEPTUAL, 0);
893 gtk_label_set_ellipsize(GTK_LABEL(gtk_bin_get_child(GTK_BIN(
g->acquire_source_button))), PANGO_ELLIPSIZE_START);
894 gtk_widget_set_tooltip_text(
g->acquire_source_button, _(
"analyze this image as a source image"));
899 gtk_label_set_ellipsize(GTK_LABEL(gtk_bin_get_child(GTK_BIN(
g->acquire_target_button))), PANGO_ELLIPSIZE_START);
900 gtk_widget_set_tooltip_text(
g->acquire_target_button, _(
"analyze this image as a target image"));
903 gtk_widget_set_tooltip_text(
g->clusters, _(
"number of clusters to find in image. value change resets all clusters"));
906 gtk_widget_set_tooltip_text(
g->dominance, _(
"how clusters are mapped. low values: based on color "
907 "proximity, high values: based on color dominance"));
911 gtk_widget_set_tooltip_text(
g->equalization, _(
"level of histogram equalization"));
918 FILE *
f = g_fopen(
"/tmp/dt_colormapping_loaded",
"rb");
921 if(fread(&
g->flowback,
sizeof(
g->flowback), 1,
f) > 0)
g->flowback_set = 1;
932 cmsDeleteTransform(
g->xform);
static double dist(double x1, double y1, double x2, double y2)
void dt_bauhaus_slider_set_format(GtkWidget *widget, const char *format)
void dt_bilateral_free(dt_bilateral_t *b)
__DT_CLONE_TARGETS__ void dt_bilateral_splat(const dt_bilateral_t *b, const float *const in)
size_t dt_bilateral_memory_use(const int width, const int height, const float sigma_s, const float sigma_r)
dt_bilateral_t * dt_bilateral_init(const int width, const int height, const float sigma_s, const float sigma_r)
size_t dt_bilateral_singlebuffer_size(const int width, const int height, const float sigma_s, const float sigma_r)
__DT_CLONE_TARGETS__ void dt_bilateral_slice(const dt_bilateral_t *const b, const float *const in, float *out, const float detail)
void dt_bilateral_blur(const dt_bilateral_t *b)
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)
static __DT_CLONE_TARGETS__ void get_clusters(const float *col, const int n, float2 *mean, float *weight)
struct dt_iop_colormapping_params_t dt_iop_colormapping_data_t
const char ** description(struct dt_iop_module_t *self)
__DT_CLONE_TARGETS__ int process(struct dt_iop_module_t *self, const dt_dev_pixelpipe_t *pipe, const dt_dev_pixelpipe_iop_t *piece, const void *const ivoid, void *const ovoid)
void reload_defaults(dt_iop_module_t *module)
static void invert_histogram(const int *hist, float *inv_hist)
static void process_clusters(gpointer instance, gpointer user_data)
static gboolean cluster_preview_draw(GtkWidget *widget, cairo_t *crf, dt_iop_module_t *self)
static int get_cluster(const float *col, const int n, float2 *mean)
void init_pipe(struct dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece)
static void acquire_target_button_pressed(GtkButton *button, dt_iop_module_t *self)
void gui_init(struct dt_iop_module_t *self)
void gui_changed(dt_iop_module_t *self, GtkWidget *w, void *previous)
void tiling_callback(struct dt_iop_module_t *self, const struct dt_dev_pixelpipe_t *pipe, const struct dt_dev_pixelpipe_iop_t *piece, struct dt_develop_tiling_t *tiling)
int default_colorspace(dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, const dt_dev_pixelpipe_iop_t *piece)
static void capture_histogram(const float *col, const int width, const int height, int *hist)
void gui_cleanup(struct dt_iop_module_t *self)
static __DT_CLONE_TARGETS__ void get_cluster_mapping(const int n, float2 *mi, const float *wi, float2 *mo, const float *wo, const float dominance, int *mapio)
void cleanup_pipe(struct dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece)
dt_iop_colormapping_flags_t
static void kmeans(const float *col, const int width, const int height, const int n, float2 *mean_out, float2 *var_out, float *weight_out)
static void acquire_source_button_pressed(GtkButton *button, dt_iop_module_t *self)
const dt_colorspaces_color_profile_t * dt_colorspaces_get_profile(dt_colorspaces_color_profile_type_t type, const char *filename, dt_colorspaces_profile_role_t role)
Resolve a profile identity to its registered entry.
The colour-profile module's API: which profiles exist, and how to apply one.
struct _GtkWidget GtkWidget
GtkWidget, opaque, spelled exactly as GTK spells it.
static dt_aligned_pixel_t rgb
static dt_aligned_pixel_t Lab
const dt_colormatrix_t dt_aligned_pixel_t out
void dt_control_queue_redraw_widget(GtkWidget *widget)
threadsafe request of redraw of specific widget. Use this function if you need to redraw a specific w...
void dt_control_queue_redraw()
Request a redraw of the whole workspace.
#define dt_dev_add_history_item(dev, module, enable, redraw)
gboolean dt_dev_pixelpipe_has_preview_output(const dt_develop_t *dev, const dt_dev_pixelpipe_t *pipe, const dt_iop_roi_t *roi)
GtkWidget * dtgtk_drawing_area_new_with_aspect_ratio(double aspect)
static void weight(const float *c1, const float *c2, const float sharpen, dt_aligned_pixel_t weight)
static void dt_iop_image_copy_by_size(float *const __restrict__ out, const float *const __restrict__ in, const size_t width, const size_t height, const size_t ch)
static float *__restrict__ dt_iop_image_alloc(const size_t width, const size_t height, const size_t ch)
const char ** dt_iop_set_description(dt_iop_module_t *module, const char *main_text, const char *purpose, const char *input, const char *process, const char *output)
float dt_dev_get_module_scale(const dt_dev_pixelpipe_t *const pipe, const dt_iop_roi_t *const roi_in)
void dt_iop_gui_leave_critical_section(dt_iop_module_t *const module)
Release what dt_iop_gui_enter_critical_section() took. Also a no-op headless.
void dt_iop_request_focus(dt_iop_module_t *module)
Move darkroom focus to module, or clear it with NULL.
@ IOP_FLAGS_SUPPORTS_BLENDING
void dt_iop_gui_enter_critical_section(dt_iop_module_t *const module)
Take the module's GUI lock, serialising access to its dt_iop_gui_data_t.
GtkWidget * dt_iop_button_new(dt_iop_module_t *self, const gchar *label, GCallback callback, gboolean local, guint accel_key, GdkModifierType mods, DTGTKCairoPaintIconFunc paint, gint paintflags, GtkWidget *box)
GtkWidget * dt_bauhaus_slider_from_params(dt_iop_module_t *self, const char *param)
static dt_iop_gui_data_t * dt_iop_gui_data(const struct dt_iop_module_t *m)
The module's GUI data blob, NULL-safe for headless callers: IOP process() implementations read it for...
#define IOP_GUI_ALLOC(module)
GtkWidget * dt_ui_label_new(const gchar *str)
float *const restrict const size_t k
float *const restrict const size_t const size_t ch
#define IS_NULL_PTR(p)
C is way too permissive with !=, == and if(var) checks, which can mean too many things depending on w...
#define dt_free_align(ptr)
Release memory from dt_alloc_align() and set ptr to NULL.
static void * dt_calloc_align(size_t size)
dt_alloc_align() followed by a zero fill.
#define dt_free(ptr)
g_free() ptr and set it to NULL, skipping both if it is already NULL.
#define DT_MODULE_INTROSPECTION(MODVER, PARAMSTYPE)
DT_MODULE() for a module whose params struct is introspected.
#define __OMP_PARALLEL__(...)
#define __OMP_PARALLEL_FOR__(...)
#define dt_pixelpipe_cache_alloc_perthread(n, objsize, padded_size)
#define dt_pixelpipe_cache_free_align(mem)
#define dt_get_perthread(buf, padsize)
static float dt_points_get()
@ DT_COLORSPACE_SRGB
sRGB – registered TWICE, and the two entries are not interchangeable.
@ DT_PROFILE_ROLE_INPUT
Listed in the input-profile combo (colorin).
@ DT_PROFILE_ROLE_ANY
All four roles, in registration order.
static cairo_surface_t * dt_cairo_image_surface_create(cairo_format_t format, int width, int height)
#define DT_DEBUG_CONTROL_SIGNAL_DISCONNECT(ctlsig, cb, user_data)
struct dt_control_signal_t * dt_control_signal_get_global(void)
@ DT_SIGNAL_DEVELOP_PREVIEW_PIPE_FINISHED
This signal is raised when develop preview pipe process is finished no param, no returned value.
#define DT_DEBUG_CONTROL_SIGNAL_CONNECT(ctlsig, signal, cb, user_data)
DT_ALIGNED_PIXEL float dt_aligned_pixel_t[4]
cmsHPROFILE profile
the actual profile; NULL for the three category entries
dt_iop_buffer_dsc_t dsc_in
struct dt_iop_module_t *void * data
dt_iop_colormapping_flowback_t flowback
GtkWidget * acquire_source_button
GtkWidget * acquire_target_button
float source_ihist[(1<< 11)]
int target_hist[(1<< 11)]
dt_iop_colormapping_flags_t flag
struct dt_iop_module_gui_t * gui
struct dt_develop_t * dev
Region of interest passed through the pixelpipe.
#define __DT_CLONE_TARGETS__