83#define HISTN (1 << 11)
162 return _(
"color mapping");
167 return dt_iop_set_description(self, _(
"transfer a color palette and tonal repartition from one image to another"),
169 _(
"linear or non-linear, Lab, display-referred"),
170 _(
"non-linear, Lab"),
171 _(
"non-linear, Lab, display-referred"));
192 memset(hist, 0,
sizeof(
int) *
HISTN);
196 const int bin = CLAMP(
HISTN * col[4 * (
k *
width +
i) + 0] / 100.0, 0,
HISTN - 1);
201 for(
int k = 1;
k <
HISTN;
k++) hist[
k] += hist[
k - 1];
217 inv_hist[
i] = 100.0*
k/(float)
HISTN;
222 for(
int i = 0;
i <= last;
i++) inv_hist[
i] = 100.0f *
i / (
float)
HISTN;
223 for(
int i = last + 1;
i <
HISTN;
i++)
228 inv_hist[
i] = 100.0f *
k / (float)
HISTN;
242 const float dominance,
int *mapio)
244 const float weightscale = 10000.0f;
246 for(
int ki = 0; ki <
n; ki++)
249 float mdist = FLT_MAX;
250 for(
int ko = 0; ko <
n; ko++)
253 const float colordist = (mo[ko][0] - mi[ki][0]) * (mo[ko][0] - mi[ki][0])
254 + (mo[ko][1] - mi[ki][1]) * (mo[ko][1] - mi[ki][1]);
255 const float weightdist = weightscale * (wo[ko] - wi[ki]) * (wo[ko] - wi[ki]);
256 const float dist = colordist * (1.0f - dominance) + weightdist * dominance;
276 float mdist = FLT_MAX;
277 for(
int k = 0;
k <
n;
k++)
279 const float dist2 = (col[1] - mean[
k][0]) * (col[1] - mean[
k][0])
280 + (col[2] - mean[
k][1]) * (col[2] - mean[
k][1]);
281 weight[
k] = dist2 > 1.0e-6f ? 1.0f / dist2 : -1.0f;
282 if(dist2 < mdist) mdist = dist2;
285 for(
int k = 0;
k <
n;
k++)
296 float mdist = FLT_MAX;
298 for(
int k = 0;
k <
n;
k++)
300 const float dist = (col[1] - mean[
k][0]) * (col[1] - mean[
k][0])
301 + (col[2] - mean[
k][1]) * (col[2] - mean[
k][1]);
312 float2 *var_out,
float *weight_out)
319 int *
const cnt = malloc(
sizeof(
int) *
n);
322 float a_min = FLT_MAX, b_min = FLT_MAX, a_max = FLT_MIN, b_max = FLT_MIN;
324 for(
int s = 0; s < samples; s++)
329 const float a = col[4 * (
width * j +
i) + 1];
330 const float b = col[4 * (
width * j +
i) + 2];
332 a_min = fminf(a, a_min);
333 a_max = fmaxf(a, a_max);
334 b_min = fminf(b, b_min);
335 b_max = fmaxf(b, b_max);
339 for(
int k = 0;
k <
n;
k++)
341 mean_out[
k][0] = 0.9f * (a_min + (a_max - a_min) *
dt_points_get());
342 mean_out[
k][1] = 0.9f * (b_min + (b_max - b_min) *
dt_points_get());
343 var_out[
k][0] = var_out[
k][1] = weight_out[
k] = 0.0f;
344 mean[
k][0] = mean[
k][1] = var[
k][0] = var[
k][1] = 0.0f;
346 for(
int it = 0; it < nit; it++)
348 for(
int k = 0;
k <
n;
k++) cnt[
k] = 0;
351 for(
int s = 0; s < samples; s++)
356 for(
int k = 0;
k <
n;
k++)
358 const float L = col[4 * (
width * j +
i)];
370 var[c][0] +=
Lab[1] *
Lab[1];
374 var[c][1] +=
Lab[2] *
Lab[2];
378 mean[c][0] +=
Lab[1];
382 mean[c][1] +=
Lab[2];
386 for(
int k = 0;
k <
n;
k++)
388 if(cnt[
k] == 0)
continue;
389 mean_out[
k][0] = mean[
k][0] / cnt[
k];
390 mean_out[
k][1] = mean[
k][1] / cnt[
k];
391 var_out[
k][0] = var[
k][0] / cnt[
k] - mean_out[
k][0] * mean_out[
k][0];
392 var_out[
k][1] = var[
k][1] / cnt[
k] - mean_out[
k][1] * mean_out[
k][1];
393 mean[
k][0] = mean[
k][1] = var[
k][0] = var[
k][1] = 0.0f;
398 for(
int k = 0;
k <
n;
k++) count += cnt[
k];
399 for(
int k = 0;
k <
n;
k++) weight_out[
k] = (count > 0) ? (float)cnt[
k] / count : 0.0f;
410 for(
int k = 0;
k <
n;
k++)
413 if(var_out[
k][0] == 0.0f || var_out[
k][1] == 0.0f)
414 mean_out[
k][0] = mean_out[
k][1] = var_out[
k][0] = var_out[
k][1] = weight_out[
k] = 0;
417 var_out[
k][0] = sqrtf(var_out[
k][0]);
418 var_out[
k][1] = sqrtf(var_out[
k][1]);
423 for(
int i = 0;
i <
n - 1;
i++)
425 for(
int j = 0; j <
n - 1 -
i; j++)
427 if(weight_out[j] > weight_out[j + 1])
429 float2 temp_mean = { mean_out[j + 1][0], mean_out[j + 1][1] };
430 float2 temp_var = { var_out[j + 1][0], var_out[j + 1][1] };
431 float temp_weight = weight_out[j + 1];
433 mean_out[j + 1][0] = mean_out[j][0];
434 mean_out[j + 1][1] = mean_out[j][1];
435 var_out[j + 1][0] = var_out[j][0];
436 var_out[j + 1][1] = var_out[j][1];
437 weight_out[j + 1] = weight_out[j];
439 mean_out[j][0] = temp_mean[0];
440 mean_out[j][1] = temp_mean[1];
441 var_out[j][0] = temp_var[0];
442 var_out[j][1] = temp_var[1];
443 weight_out[j] = temp_weight;
457 float *
const restrict in = (
float *)ivoid;
458 float *
const restrict
out = (
float *)
ovoid;
464 const float sigma_s = 50.0f / scale;
496 const float dominance = data->dominance / 100.0f;
497 const float equalization = data->equalization / 100.0f;
500 int *
const mapio = malloc(
sizeof(
int) * data->n);
504 data->source_weight, dominance, mapio);
506 float2 *
const var_ratio = malloc(
sizeof(
float2) * data->n);
513 for(
int i = 0;
i < data->n;
i++)
516 = (data->target_var[
i][0] > 0.0f) ? data->source_var[mapio[
i]][0] / data->target_var[
i][0] : 0.0f;
518 = (data->target_var[
i][1] > 0.0f) ? data->source_var[mapio[
i]][1] / data->target_var[
i][1] : 0.0f;
524 for(
size_t k = 0;
k < npixels * 4;
k += 4)
526 const float L = in[
k];
527 out[
k] = 0.5f * ((
L * (1.0f - equalization)
528 + data->source_ihist[data->target_hist[(
int)CLAMP(
529 HISTN *
L / 100.0f, 0.0f, (
float)
HISTN - 1.0f)]] * equalization) -
L) + 50.0f;
530 out[
k] = CLAMP(
out[
k], 0.0f, 100.0f);
533 if(equalization > 0.001f)
563 for(
size_t j = 0; j < 4*npixels; j += 4)
565 const float L = in[j];
569 out[j] = 2.0f * (
out[j] - 50.0f) +
L;
570 out[j] = CLAMP(
out[j], 0.0f, 100.0f);
574 out[j + 1] =
out[j + 2] = 0.0f;
576 for(
int c = 0; c < data->n; c++)
578 out[j + 1] +=
weight[c] * ((
Lab[1] - data->target_mean[c][0]) * var_ratio[c][0]
579 + data->source_mean[mapio[c]][0]);
580 out[j + 2] +=
weight[c] * ((
Lab[2] - data->target_mean[c][1]) * var_ratio[c][1]
581 + data->source_mean[mapio[c]][1]);
584 out[j + 3] = in[j + 3];
605 const float sigma_s = 50.0f / scale;
612 const size_t basebuffer =
sizeof(float) * channels *
width *
height;
640 memset(
p->source_ihist, 0,
sizeof(
float) *
HISTN);
641 memset(
p->source_mean, 0,
sizeof(
float) *
MAXN * 2);
642 memset(
p->source_var, 0,
sizeof(
float) *
MAXN * 2);
643 memset(
p->source_weight, 0,
sizeof(
float) *
MAXN);
644 memset(
p->target_hist, 0,
sizeof(
int) *
HISTN);
645 memset(
p->target_mean, 0,
sizeof(
float) *
MAXN * 2);
646 memset(
p->target_var, 0,
sizeof(
float) *
MAXN * 2);
647 memset(
p->target_weight, 0,
sizeof(
float) *
MAXN);
660 p->flag &= ~HAS_SOURCE;
671 p->flag &= ~HAS_TARGET;
695 memcpy(
d->source_ihist,
g->flowback.hist,
sizeof(
float) *
HISTN);
696 memcpy(
d->source_mean,
g->flowback.mean,
sizeof(
float) *
MAXN * 2);
697 memcpy(
d->source_var,
g->flowback.var,
sizeof(
float) *
MAXN * 2);
698 memcpy(
d->source_weight,
g->flowback.weight,
sizeof(
float) *
MAXN);
699 d->n =
g->flowback.n;
713 if(widget ==
g->source_area)
715 mean =
p->source_mean;
720 mean =
p->target_mean;
725 GtkAllocation allocation;
726 gtk_widget_get_allocation(widget, &allocation);
728 int width = allocation.width,
height = allocation.height;
730 cairo_t *cr = cairo_create(cst);
731 cairo_set_source_rgb(cr, .2, .2, .2);
734 cairo_translate(cr, inset, inset);
740 const float qwd = (
width - (
p->n - 1) * sep) / (float)
p->n;
741 for(
int cl = 0; cl <
p->n; cl++)
744 for(
int j = -1; j <= 1; j++)
745 for(
int i = -1;
i <= 1;
i++)
748 double rgb[3] = { 0.5, 0.5, 0.5 };
751 Lab.a = (mean[cl][0] +
i * var[cl][0]);
752 Lab.b = (mean[cl][1] + j * var[cl][1]);
753 cmsDoTransform(
g->xform, &
Lab,
rgb, 1);
754 cairo_set_source_rgb(cr,
rgb[0],
rgb[1],
rgb[2]);
759 cairo_translate(cr, qwd + sep, 0);
763 cairo_set_source_surface(crf, cst, 0, 0);
765 cairo_surface_destroy(cst);
775 int new_source_clusters = 0;
783 const int width =
g->width;
785 const int ch =
g->ch;
809 new_source_clusters = 1;
828 if(new_source_clusters)
830 memcpy(
g->flowback.hist,
p->source_ihist,
sizeof(
float) *
HISTN);
831 memcpy(
g->flowback.mean,
p->source_mean,
sizeof(
float) *
MAXN * 2);
832 memcpy(
g->flowback.var,
p->source_var,
sizeof(
float) *
MAXN * 2);
833 memcpy(
g->flowback.weight,
p->source_weight,
sizeof(
float) *
MAXN);
834 g->flowback.n =
p->n;
836 FILE *
f = g_fopen(
"/tmp/dt_colormapping_loaded",
"wb");
839 if(fwrite(&
g->flowback,
sizeof(
g->flowback), 1,
f) < 1)
840 fprintf(stderr,
"[colormapping] could not write flowback file /tmp/dt_colormapping_loaded\n");
862 g->xform = cmsCreateTransform(hLab, TYPE_Lab_DBL, hsRGB, TYPE_RGB_DBL, INTENT_PERCEPTUAL, 0);
870 gtk_box_pack_start(GTK_BOX(self->
widget),
g->source_area,
TRUE,
TRUE, 0);
876 gtk_box_pack_start(GTK_BOX(self->
widget),
g->target_area,
TRUE,
TRUE, 0);
885 gtk_label_set_ellipsize(GTK_LABEL(gtk_bin_get_child(GTK_BIN(
g->acquire_source_button))), PANGO_ELLIPSIZE_START);
886 gtk_widget_set_tooltip_text(
g->acquire_source_button, _(
"analyze this image as a source image"));
891 gtk_label_set_ellipsize(GTK_LABEL(gtk_bin_get_child(GTK_BIN(
g->acquire_target_button))), PANGO_ELLIPSIZE_START);
892 gtk_widget_set_tooltip_text(
g->acquire_target_button, _(
"analyze this image as a target image"));
895 gtk_widget_set_tooltip_text(
g->clusters, _(
"number of clusters to find in image. value change resets all clusters"));
898 gtk_widget_set_tooltip_text(
g->dominance, _(
"how clusters are mapped. low values: based on color "
899 "proximity, high values: based on color dominance"));
903 gtk_widget_set_tooltip_text(
g->equalization, _(
"level of histogram equalization"));
910 FILE *
f = g_fopen(
"/tmp/dt_colormapping_loaded",
"rb");
913 if(fread(&
g->flowback,
sizeof(
g->flowback), 1,
f) > 0)
g->flowback_set = 1;
924 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)
static const dt_aligned_pixel_simd_t const dt_adaptation_t const float p
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_direction_t direction)
@ DT_PROFILE_DIRECTION_IN
@ DT_PROFILE_DIRECTION_ANY
static dt_aligned_pixel_t rgb
const dt_aligned_pixel_t f
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 redraw of the workspace. This redraws the whole workspace within a gdk critical section to pr...
#define dt_pixelpipe_cache_alloc_perthread(n, objsize, padded_size)
#define dt_free_align(ptr)
static void * dt_calloc_align(size_t size)
#define DT_MODULE_INTROSPECTION(MODVER, PARAMSTYPE)
#define __OMP_PARALLEL__(...)
#define dt_pixelpipe_cache_free_align(mem)
#define __DT_CLONE_TARGETS__
#define dt_get_perthread(buf, padsize)
#define __OMP_PARALLEL_FOR__(...)
#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_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 cairo_surface_t * dt_cairo_image_surface_create(cairo_format_t format, int width, int height)
#define DT_GUI_BOX_SPACING
#define DT_PIXEL_APPLY_DPI(value)
static GtkWidget * dt_ui_label_new(const gchar *str)
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)
void dt_iop_request_focus(dt_iop_module_t *module)
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)
static void dt_iop_gui_enter_critical_section(dt_iop_module_t *const module) ACQUIRE(&module -> gui_lock)
@ IOP_FLAGS_SUPPORTS_BLENDING
static void dt_iop_gui_leave_critical_section(dt_iop_module_t *const module) RELEASE(&module -> gui_lock)
#define IOP_GUI_ALLOC(module)
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)
float *const restrict const size_t k
float *const restrict const size_t const size_t ch
float dt_aligned_pixel_t[4]
static float dt_points_get()
#define DT_DEBUG_CONTROL_SIGNAL_DISCONNECT(ctlsig, cb, user_data)
@ 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)
struct _GtkWidget GtkWidget
struct dt_gui_gtk_t * gui
struct dt_control_signal_t * signals
struct dt_develop_t * develop
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_develop_t * dev
dt_iop_gui_data_t * gui_data
Region of interest passed through the pixelpipe.