colormapping.c 40.6 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
/*
    This file is part of darktable,
    copyright (c) 2009--2013 johannes hanika.
    copyright (c) 2013 Ulrich Pegelow.

    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
22
#include "bauhaus/bauhaus.h"
23 24
#include "common/bilateral.h"
#include "common/bilateralcl.h"
25
#include "common/colorspaces.h"
26 27 28
#include "common/opencl.h"
#include "common/points.h"
#include "control/control.h"
29 30 31
#include "develop/develop.h"
#include "develop/imageop.h"
#include "develop/tiling.h"
32
#include "dtgtk/drawingarea.h"
33
#include "dtgtk/resetlabel.h"
34 35 36
#include "gui/accelerators.h"
#include "gui/gtk.h"
#include "iop/iop_api.h"
37

38 39
#include <gtk/gtk.h>
#include <inttypes.h>
40
#include <math.h>
41
#include <stdlib.h>
42 43 44 45 46 47 48 49 50 51 52 53 54 55 56
#include <string.h>
#include <strings.h>

/**
 * color transfer somewhat based on the glorious paper `color transfer between images'
 * by erik reinhard, michael ashikhmin, bruce gooch, and peter shirley, 2001.
 * chosen because it officially cites the playboy.
 *
 * workflow:
 * - open the target image, press acquire button
 * - right click store as preset
 * - open image you want to transfer the color to
 * - right click and apply the preset
 */

57
DT_MODULE_INTROSPECTION(1, dt_iop_colormapping_params_t)
58

59
#define HISTN (1 << 11)
60 61
#define MAXN 5

62 63
typedef enum dt_iop_colormapping_flags_t
{
64 65 66 67 68 69
  NEUTRAL = 0,
  HAS_SOURCE = 1 << 0,
  HAS_TARGET = 1 << 1,
  ACQUIRE = 1 << 2,
  GET_SOURCE = 1 << 3,
  GET_TARGET = 1 << 4
70
} dt_iop_colormapping_flags_t;
71 72 73 74 75 76

typedef struct dt_iop_colormapping_flowback_t
{
  float hist[HISTN];
  // n-means (max 5?) with mean/variance
  float mean[MAXN][2];
77 78
  float var[MAXN][2];
  float weight[MAXN];
79 80
  // number of gaussians used.
  int n;
81
} dt_iop_colormapping_flowback_t;
82 83 84

typedef struct dt_iop_colormapping_params_t
{
85
  dt_iop_colormapping_flags_t flag;
86 87 88 89 90 91 92 93 94 95 96 97 98
  // number of gaussians used.
  int n;

  // relative importance of color dominance vs. color proximity
  float dominance;

  // level of histogram equalization
  float equalization;

  // hist matching table for source image
  float source_ihist[HISTN];
  // n-means (max 5) with mean/variance for source image
  float source_mean[MAXN][2];
99 100
  float source_var[MAXN][2];
  float source_weight[MAXN];
101 102 103 104 105

  // hist matching table for destination image
  int target_hist[HISTN];
  // n-means (max 5) with mean/variance for source image
  float target_mean[MAXN][2];
106 107 108
  float target_var[MAXN][2];
  float target_weight[MAXN];
} dt_iop_colormapping_params_t;
109 110 111 112 113 114 115 116 117

/** and pixelpipe data is just the same */
typedef struct dt_iop_colormapping_params_t dt_iop_colormapping_data_t;


typedef struct dt_iop_colormapping_gui_data_t
{
  int flag;
  float *buffer;
118
  int width;
119 120 121 122 123 124 125 126 127 128 129 130 131
  int height;
  int ch;
  int flowback_set;
  dt_iop_colormapping_flowback_t flowback;
  GtkWidget *acquire_source_button;
  GtkWidget *acquire_target_button;
  GtkWidget *source_area;
  GtkWidget *target_area;
  GtkWidget *clusters;
  GtkWidget *dominance;
  GtkWidget *equalization;
  cmsHTRANSFORM xform;
  dt_pthread_mutex_t lock;
132
} dt_iop_colormapping_gui_data_t;
133

134 135 136 137
typedef struct dt_iop_colormapping_global_data_t
{
  int kernel_histogram;
  int kernel_mapping;
138
} dt_iop_colormapping_global_data_t;
139

140

141
const char *name()
142 143 144 145
{
  return _("color mapping");
}

146
int groups()
147 148 149 150
{
  return IOP_GROUP_EFFECT;
}

151
int flags()
152
{
153
  return IOP_FLAGS_ONE_INSTANCE | IOP_FLAGS_SUPPORTS_BLENDING;
154 155 156
}


157
void init_key_accels(dt_iop_module_so_t *self)
158
{
159 160
  dt_accel_register_iop(self, FALSE, NC_("accel", "acquire as source"), 0, 0);
  dt_accel_register_iop(self, FALSE, NC_("accel", "acquire as target"), 0, 0);
161 162
}

163
void connect_key_accels(dt_iop_module_t *self)
164
{
165
  dt_iop_colormapping_gui_data_t *g = (dt_iop_colormapping_gui_data_t *)self->gui_data;
166

167 168
  dt_accel_connect_button_iop(self, "acquire as source", g->acquire_source_button);
  dt_accel_connect_button_iop(self, "acquire as target", g->acquire_target_button);
169 170 171
}


172
static void capture_histogram(const float *col, const int width, const int height, int *hist)
173 174
{
  // build separate histogram
175 176 177
  memset(hist, 0, HISTN * sizeof(int));
  for(int k = 0; k < height; k++)
    for(int i = 0; i < width; i++)
178
    {
179
      const int bin = CLAMP(HISTN * col[4 * (k * width + i) + 0] / 100.0, 0, HISTN - 1);
180 181 182 183
      hist[bin]++;
    }

  // accumulated start distribution of G1 G2
184 185 186 187 188
  for(int k = 1; k < HISTN; k++) hist[k] += hist[k - 1];
  for(int k = 0; k < HISTN; k++)
    hist[k] = (int)CLAMP(hist[k] * (HISTN / (float)hist[HISTN - 1]), 0, HISTN - 1);
  // for(int i=0;i<100;i++) printf("#[%d] %d \n", (int)CLAMP(HISTN*i/100.0, 0, HISTN-1),
  // hist[(int)CLAMP(HISTN*i/100.0, 0, HISTN-1)]);
189 190
}

191
static void invert_histogram(const int *hist, float *inv_hist)
192
{
193
// invert non-normalised accumulated hist
194 195 196 197 198 199 200 201 202 203 204
#if 0
  int last = 0;
  for(int i=0; i<HISTN; i++) for(int k=last; k<HISTN; k++)
      if(hist[k] >= i)
      {
        last = k;
        inv_hist[i] = 100.0*k/(float)HISTN;
        break;
      }
#else
  int last = 31;
205 206 207
  for(int i = 0; i <= last; i++) inv_hist[i] = 100.0 * i / (float)HISTN;
  for(int i = last + 1; i < HISTN; i++)
    for(int k = last; k < HISTN; k++)
208 209 210
      if(hist[k] >= i)
      {
        last = k;
211
        inv_hist[i] = 100.0 * k / (float)HISTN;
212 213 214 215
        break;
      }
#endif

216 217 218 219 220
  // printf("inv histogram debug:\n");
  // for(int i=0;i<100;i++) printf("%d => %f\n", i, inv_hist[hist[(int)CLAMP(HISTN*i/100.0, 0, HISTN-1)]]);
  // for(int i=0;i<100;i++) printf("[%d] %f => %f\n", (int)CLAMP(HISTN*i/100.0, 0, HISTN-1),
  // hist[(int)CLAMP(HISTN*i/100.0, 0, HISTN-1)]/(float)HISTN, inv_hist[(int)CLAMP(HISTN*i/100.0, 0,
  // HISTN-1)]);
221 222
}

223 224 225
#pragma GCC diagnostic push
#pragma GCC diagnostic warning "-Wvla"

226 227
static void get_cluster_mapping(const int n, float mi[n][2], float wi[n], float mo[n][2], float wo[n],
                                const float dominance, int mapio[n])
228 229 230
{
  const float weightscale = 10000.0f;

231
  for(int ki = 0; ki < n; ki++)
232 233 234
  {
    // for each input cluster
    float mdist = FLT_MAX;
235
    for(int ko = 0; ko < n; ko++)
236 237
    {
      // find the best target cluster (the same could be used more than once)
238 239 240
      const float colordist = (mo[ko][0] - mi[ki][0]) * (mo[ko][0] - mi[ki][0])
                              + (mo[ko][1] - mi[ki][1]) * (mo[ko][1] - mi[ki][1]);
      const float weightdist = weightscale * (wo[ko] - wi[ki]) * (wo[ko] - wi[ki]);
241 242 243
      const float dist = colordist * (1.0f - dominance) + weightdist * dominance;
      if(dist < mdist)
      {
244 245
        // printf("[%d] => [%d] dominance: %f, colordist: %f, weightdist: %f, dist: %f\n", ki, ko, dominance,
        // colordist, weightdist, dist);
246 247 248 249 250 251
        mdist = dist;
        mapio[ki] = ko;
      }
    }
  }

252 253
  // printf("cluster mapping:\n");
  // for(int i=0;i<n;i++) printf("[%d] => [%d]\n", i, mapio[i]);
254 255
}

256 257

// inverse distant weighting according to D. Shepard's method; with power parameter 2.0
258
static void get_clusters(const float *col, const int n, float mean[n][2], float *weight)
259
{
260
  float mdist = FLT_MAX;
261
  for(int k = 0; k < n; k++)
262
  {
263 264 265
    const float dist2 = (col[1] - mean[k][0]) * (col[1] - mean[k][0])
                        + (col[2] - mean[k][1]) * (col[2] - mean[k][1]); // dist^2
    weight[k] = dist2 > 1.0e-6f ? 1.0f / dist2 : -1.0f;                  // direct hits marked as -1
266
    if(dist2 < mdist) mdist = dist2;
267
  }
268 269 270
  if(mdist < 1.0e-6f)
    for(int k = 0; k < n; k++)
      weight[k] = weight[k] < 0.0f ? 1.0f : 0.0f; // correction in case of direct hits
271
  float sum = 0.0f;
272 273 274
  for(int k = 0; k < n; k++) sum += weight[k];
  if(sum > 0.0f)
    for(int k = 0; k < n; k++) weight[k] /= sum;
275 276
}

277

278
static int get_cluster(const float *col, const int n, float mean[n][2])
279 280 281
{
  float mdist = FLT_MAX;
  int cluster = 0;
282
  for(int k = 0; k < n; k++)
283
  {
284 285
    const float dist = (col[1] - mean[k][0]) * (col[1] - mean[k][0])
                       + (col[2] - mean[k][1]) * (col[2] - mean[k][1]);
286 287 288 289 290 291 292 293 294
    if(dist < mdist)
    {
      mdist = dist;
      cluster = k;
    }
  }
  return cluster;
}

295 296
static void kmeans(const float *col, const int width, const int height, const int n, float mean_out[n][2],
                   float var_out[n][2], float weight_out[n])
297
{
298 299
  const int nit = 40;                       // number of iterations
  const int samples = width * height * 0.2; // samples: only a fraction of the buffer.
300

301 302 303 304
  float(*const mean)[2] = malloc(2 * n * sizeof(float));
  float(*const var)[2] = malloc(2 * n * sizeof(float));
  int *const cnt = malloc(n * sizeof(int));
  int count;
305 306 307

  float a_min = FLT_MAX, b_min = FLT_MAX, a_max = FLT_MIN, b_max = FLT_MIN;

308
  for(int s = 0; s < samples; s++)
309
  {
310 311
    const int j = CLAMP(dt_points_get() * height, 0, height - 1);
    const int i = CLAMP(dt_points_get() * width, 0, width - 1);
312

313 314
    const float a = col[4 * (width * j + i) + 1];
    const float b = col[4 * (width * j + i) + 2];
315 316 317 318 319 320 321 322

    a_min = fmin(a, a_min);
    a_max = fmax(a, a_max);
    b_min = fmin(b, b_min);
    b_max = fmax(b, b_max);
  }

  // init n clusters for a, b channels at random
323
  for(int k = 0; k < n; k++)
324 325 326 327 328 329
  {
    mean_out[k][0] = 0.9f * (a_min + (a_max - a_min) * dt_points_get());
    mean_out[k][1] = 0.9f * (b_min + (b_max - b_min) * dt_points_get());
    var_out[k][0] = var_out[k][1] = weight_out[k] = 0.0f;
    mean[k][0] = mean[k][1] = var[k][0] = var[k][1] = 0.0f;
  }
330
  for(int it = 0; it < nit; it++)
331
  {
332 333
    for(int k = 0; k < n; k++) cnt[k] = 0;
// randomly sample col positions inside roi
334
#ifdef _OPENMP
335
#pragma omp parallel for default(none) schedule(static) shared(col, mean_out)
336
#endif
337
    for(int s = 0; s < samples; s++)
338
    {
339 340
      const int j = CLAMP(dt_points_get() * height, 0, height - 1);
      const int i = CLAMP(dt_points_get() * width, 0, width - 1);
341
      // for each sample: determine cluster, update new mean, update var
342
      for(int k = 0; k < n; k++)
343
      {
344 345
        const float L = col[4 * (width * j + i)];
        const float Lab[3] = { L, col[4 * (width * j + i) + 1], col[4 * (width * j + i) + 2] };
346 347 348
        // determine dist to mean_out
        const int c = get_cluster(Lab, n, mean_out);
#ifdef _OPENMP
349
#pragma omp atomic
350 351
#endif
        cnt[c]++;
352
// update mean, var
353
#ifdef _OPENMP
354
#pragma omp atomic
355
#endif
356
        var[c][0] += Lab[1] * Lab[1];
357
#ifdef _OPENMP
358
#pragma omp atomic
359
#endif
360
        var[c][1] += Lab[2] * Lab[2];
361
#ifdef _OPENMP
362
#pragma omp atomic
363 364 365
#endif
        mean[c][0] += Lab[1];
#ifdef _OPENMP
366
#pragma omp atomic
367 368 369 370 371
#endif
        mean[c][1] += Lab[2];
      }
    }
    // swap old/new means
372
    for(int k = 0; k < n; k++)
373
    {
374 375 376 377 378
      if(cnt[k] == 0) continue;
      mean_out[k][0] = mean[k][0] / cnt[k];
      mean_out[k][1] = mean[k][1] / cnt[k];
      var_out[k][0] = var[k][0] / cnt[k] - mean_out[k][0] * mean_out[k][0];
      var_out[k][1] = var[k][1] / cnt[k] - mean_out[k][1] * mean_out[k][1];
379 380 381 382 383
      mean[k][0] = mean[k][1] = var[k][0] = var[k][1] = 0.0f;
    }

    // determine weight of clusters
    count = 0;
384 385
    for(int k = 0; k < n; k++) count += cnt[k];
    for(int k = 0; k < n; k++) weight_out[k] = (count > 0) ? (float)cnt[k] / count : 0.0f;
386 387

    // printf("it %d  %d means:\n", it, n);
388 389
    // for(int k=0;k<n;k++) printf("mean %f %f -- var %f %f -- weight %f\n", mean_out[k][0], mean_out[k][1],
    // var_out[k][0], var_out[k][1], weight_out[k]);
390 391
  }

392 393 394 395
  free(cnt);
  free(var);
  free(mean);

396
  for(int k = 0; k < n; k++)
397 398 399 400 401 402 403 404 405 406
  {
    // "eliminate" clusters with a variance of zero
    if(var_out[k][0] == 0.0f || var_out[k][1] == 0.0f)
      mean_out[k][0] = mean_out[k][1] = var_out[k][0] = var_out[k][1] = weight_out[k] = 0;

    // we actually want the std deviation.
    var_out[k][0] = sqrtf(var_out[k][0]);
    var_out[k][1] = sqrtf(var_out[k][1]);
  }

407 408 409
  // simple bubblesort of clusters in order of ascending weight: just a convenience for the user to keep
  // cluster display a bit more consistent in GUI
  for(int i = 0; i < n - 1; i++)
410
  {
411
    for(int j = 0; j < n - 1 - i; j++)
412
    {
413
      if(weight_out[j] > weight_out[j + 1])
414
      {
415 416 417
        float temp_mean[2] = { mean_out[j + 1][0], mean_out[j + 1][1] };
        float temp_var[2] = { var_out[j + 1][0], var_out[j + 1][1] };
        float temp_weight = weight_out[j + 1];
418

419 420 421 422 423
        mean_out[j + 1][0] = mean_out[j][0];
        mean_out[j + 1][1] = mean_out[j][1];
        var_out[j + 1][0] = var_out[j][0];
        var_out[j + 1][1] = var_out[j][1];
        weight_out[j + 1] = weight_out[j];
Tobias Ellinghaus's avatar
Tobias Ellinghaus committed
424

425 426 427 428 429 430 431 432 433 434
        mean_out[j][0] = temp_mean[0];
        mean_out[j][1] = temp_mean[1];
        var_out[j][0] = temp_var[0];
        var_out[j][1] = temp_var[1];
        weight_out[j] = temp_weight;
      }
    }
  }
}

435 436
#pragma GCC diagnostic pop

437 438
void process(struct dt_iop_module_t *self, dt_dev_pixelpipe_iop_t *piece, const void *const ivoid,
             void *const ovoid, const dt_iop_roi_t *const roi_in, const dt_iop_roi_t *const roi_out)
439 440 441
{
  dt_iop_colormapping_data_t *data = (dt_iop_colormapping_data_t *)piece->data;
  dt_iop_colormapping_gui_data_t *g = (dt_iop_colormapping_gui_data_t *)self->gui_data;
442
  float *in = (float *)ivoid;
443 444 445 446 447 448
  float *out = (float *)ovoid;

  const int width = roi_in->width;
  const int height = roi_in->height;
  const int ch = piece->colors;

449
  const float scale = piece->iscale / roi_in->scale;
450 451 452 453 454 455 456
  const float sigma_s = 50.0f / scale;
  const float sigma_r = 8.0f; // does not depend on scale

  // save a copy of preview input buffer so we can get histogram and color statistics out of it
  if(self->dev->gui_attached && g && piece->pipe->type == DT_DEV_PIXELPIPE_PREVIEW && (data->flag & ACQUIRE))
  {
    dt_pthread_mutex_lock(&g->lock);
457
    if(g->buffer) free(g->buffer);
458

459
    g->buffer = malloc((size_t)width * height * ch * sizeof(float));
460 461 462 463
    g->width = width;
    g->height = height;
    g->ch = ch;

464
    if(g->buffer) memcpy(g->buffer, in, (size_t)width * height * ch * sizeof(float));
465 466 467 468 469 470 471 472 473

    dt_pthread_mutex_unlock(&g->lock);
  }

  // process image if all mapping information is present in the parameter set
  if(data->flag & HAS_TARGET && data->flag & HAS_SOURCE)
  {
    // for all pixels: find input cluster, transfer to mapped target cluster and apply histogram

474 475
    float dominance = data->dominance / 100.0f;
    float equalization = data->equalization / 100.0f;
476 477

    // get mapping from input clusters to target clusters
478 479
    int *const mapio = malloc(data->n * sizeof(int));

480 481
    get_cluster_mapping(data->n, data->target_mean, data->target_weight, data->source_mean,
                        data->source_weight, dominance, mapio);
482

483 484
    float(*const var_ratio)[2] = malloc(2 * data->n * sizeof(float));

485
    for(int i = 0; i < data->n; i++)
486
    {
487 488 489 490
      var_ratio[i][0]
          = (data->target_var[i][0] > 0.0f) ? data->source_var[mapio[i]][0] / data->target_var[i][0] : 0.0f;
      var_ratio[i][1]
          = (data->target_var[i][1] > 0.0f) ? data->source_var[mapio[i]][1] / data->target_var[i][1] : 0.0f;
491 492
    }

493
// first get delta L of equalized L minus original image L, scaled to fit into [0 .. 100]
494
#ifdef _OPENMP
495
#pragma omp parallel for default(none) schedule(static) shared(data, in, out, equalization)
496
#endif
497
    for(int k = 0; k < height; k++)
498
    {
499 500
      size_t j = (size_t)ch * width * k;
      for(int i = 0; i < width; i++)
501 502
      {
        const float L = in[j];
503 504 505
        out[j] = 0.5f * ((L * (1.0f - equalization)
                          + data->source_ihist[data->target_hist[(int)CLAMP(
                                HISTN * L / 100.0f, 0.0f, (float)HISTN - 1.0f)]] * equalization) - L) + 50.0f;
506
        out[j] = CLAMP(out[j], 0.0f, 100.0f);
507
        j += ch;
508 509 510
      }
    }

511 512 513 514
    if(equalization > 0.001f)
    {
      // bilateral blur of delta L to avoid artifacts caused by limited histogram resolution
      dt_bilateral_t *b = dt_bilateral_init(width, height, sigma_s, sigma_r);
515 516 517 518 519 520
      if(!b)
      {
        free(var_ratio);
        free(mapio);
        return;
      }
521 522 523 524 525
      dt_bilateral_splat(b, out);
      dt_bilateral_blur(b);
      dt_bilateral_slice(b, out, out, -1.0f);
      dt_bilateral_free(b);
    }
526

527 528
    float *const weight_buf = malloc(data->n * dt_get_num_threads() * sizeof(float));

529
#ifdef _OPENMP
530
#pragma omp parallel for default(none) schedule(static) shared(data, in, out, equalization)
531
#endif
532
    for(int k = 0; k < height; k++)
533
    {
534
      float *weight = weight_buf + data->n * dt_get_thread_num();
535 536
      size_t j = (size_t)ch * width * k;
      for(int i = 0; i < width; i++)
537 538
      {
        const float L = in[j];
539
        const float Lab[3] = { L, in[j + 1], in[j + 2] };
540 541

        // transfer back scaled and blurred delta L to output L
542
        out[j] = 2.0f * (out[j] - 50.0f) + L;
543 544
        out[j] = CLAMP(out[j], 0.0f, 100.0f);

545 546 547
        get_clusters(in + j, data->n, data->target_mean, weight);
        out[j + 1] = out[j + 2] = 0.0f;
        for(int c = 0; c < data->n; c++)
548
        {
549 550 551 552
          out[j + 1] += weight[c] * ((Lab[1] - data->target_mean[c][0]) * var_ratio[c][0]
                                     + data->source_mean[mapio[c]][0]);
          out[j + 2] += weight[c] * ((Lab[2] - data->target_mean[c][1]) * var_ratio[c][1]
                                     + data->source_mean[mapio[c]][1]);
553
        }
554 555
        out[j + 3] = in[j + 3];
        j += ch;
556 557
      }
    }
558 559 560 561

    free(weight_buf);
    free(var_ratio);
    free(mapio);
562 563 564 565
  }
  // incomplete parameter set -> do nothing
  else
  {
566
    memcpy(out, in, (size_t)sizeof(float) * ch * width * height);
567 568 569 570
  }
}


571
#ifdef HAVE_OPENCL
572
int process_cl(struct dt_iop_module_t *self, dt_dev_pixelpipe_iop_t *piece, cl_mem dev_in, cl_mem dev_out,
573
               const dt_iop_roi_t *const roi_in, const dt_iop_roi_t *const roi_out)
574 575 576
{
  dt_iop_colormapping_data_t *data = (dt_iop_colormapping_data_t *)piece->data;
  dt_iop_colormapping_global_data_t *gd = (dt_iop_colormapping_global_data_t *)self->data;
577
  dt_iop_colormapping_gui_data_t *g = (dt_iop_colormapping_gui_data_t *)self->gui_data;
578 579 580 581 582 583

  cl_int err = -999;
  const int devid = piece->pipe->devid;

  const int width = roi_in->width;
  const int height = roi_in->height;
584
  const int ch = piece->colors;
585

586
  const float scale = piece->iscale / roi_in->scale;
587 588 589
  const float sigma_s = 50.0f / scale;
  const float sigma_r = 8.0f; // does not depend on scale

590 591
  float dominance = data->dominance / 100.0f;
  float equalization = data->equalization / 100.0f;
592 593

  dt_bilateral_cl_t *b = NULL;
594
  cl_mem dev_tmp = NULL;
595 596 597 598 599 600 601
  cl_mem dev_target_hist = NULL;
  cl_mem dev_source_ihist = NULL;
  cl_mem dev_target_mean = NULL;
  cl_mem dev_source_mean = NULL;
  cl_mem dev_var_ratio = NULL;
  cl_mem dev_mapio = NULL;

602 603 604 605 606

  // save a copy of preview input buffer so we can get histogram and color statistics out of it
  if(self->dev->gui_attached && g && piece->pipe->type == DT_DEV_PIXELPIPE_PREVIEW && (data->flag & ACQUIRE))
  {
    dt_pthread_mutex_lock(&g->lock);
607
    free(g->buffer);
608

609
    g->buffer = malloc(width * height * ch * sizeof(float));
610 611 612 613 614
    g->width = width;
    g->height = height;
    g->ch = ch;

    if(g->buffer)
615
      err = dt_opencl_copy_device_to_host(devid, g->buffer, dev_in, width, height, ch * sizeof(float));
616 617 618 619 620 621 622

    dt_pthread_mutex_unlock(&g->lock);

    if(err != CL_SUCCESS) goto error;
  }


623 624 625 626 627
  // process image if all mapping information is present in the parameter set
  if(data->flag & HAS_TARGET && data->flag & HAS_SOURCE)
  {
    // get mapping from input clusters to target clusters
    int mapio[MAXN];
628 629
    get_cluster_mapping(data->n, data->target_mean, data->target_weight, data->source_mean,
                        data->source_weight, dominance, mapio);
630 631

    float var_ratio[MAXN][2];
632
    for(int i = 0; i < data->n; i++)
633
    {
634 635 636 637
      var_ratio[i][0]
          = (data->target_var[i][0] > 0.0f) ? data->source_var[mapio[i]][0] / data->target_var[i][0] : 0.0f;
      var_ratio[i][1]
          = (data->target_var[i][1] > 0.0f) ? data->source_var[mapio[i]][1] / data->target_var[i][1] : 0.0f;
638 639
    }

640 641 642
    dev_tmp = dt_opencl_alloc_device(devid, width, height, 4 * sizeof(float));
    if(dev_tmp == NULL) goto error;

643 644
    dev_target_hist = dt_opencl_copy_host_to_device_constant(devid, sizeof(int) * HISTN, data->target_hist);
    if(dev_target_hist == NULL) goto error;
645

646 647 648
    dev_source_ihist
        = dt_opencl_copy_host_to_device_constant(devid, sizeof(float) * HISTN, data->source_ihist);
    if(dev_source_ihist == NULL) goto error;
649

650 651 652
    dev_target_mean
        = dt_opencl_copy_host_to_device_constant(devid, sizeof(float) * MAXN * 2, data->target_mean);
    if(dev_target_mean == NULL) goto error;
653

654 655 656
    dev_source_mean
        = dt_opencl_copy_host_to_device_constant(devid, sizeof(float) * MAXN * 2, data->source_mean);
    if(dev_source_mean == NULL) goto error;
657

658 659
    dev_var_ratio = dt_opencl_copy_host_to_device_constant(devid, sizeof(float) * MAXN * 2, var_ratio);
    if(dev_var_ratio == NULL) goto error;
660

661 662
    dev_mapio = dt_opencl_copy_host_to_device_constant(devid, sizeof(int) * MAXN, mapio);
    if(dev_var_ratio == NULL) goto error;
663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680

    size_t sizes[3] = { ROUNDUPWD(width), ROUNDUPHT(height), 1 };

    dt_opencl_set_kernel_arg(devid, gd->kernel_histogram, 0, sizeof(cl_mem), (void *)&dev_in);
    dt_opencl_set_kernel_arg(devid, gd->kernel_histogram, 1, sizeof(cl_mem), (void *)&dev_out);
    dt_opencl_set_kernel_arg(devid, gd->kernel_histogram, 2, sizeof(int), (void *)&width);
    dt_opencl_set_kernel_arg(devid, gd->kernel_histogram, 3, sizeof(int), (void *)&height);
    dt_opencl_set_kernel_arg(devid, gd->kernel_histogram, 4, sizeof(float), (void *)&equalization);
    dt_opencl_set_kernel_arg(devid, gd->kernel_histogram, 5, sizeof(cl_mem), (void *)&dev_target_hist);
    dt_opencl_set_kernel_arg(devid, gd->kernel_histogram, 6, sizeof(cl_mem), (void *)&dev_source_ihist);
    err = dt_opencl_enqueue_kernel_2d(devid, gd->kernel_histogram, sizes);
    if(err != CL_SUCCESS) goto error;

    if(equalization > 0.001f)
    {
      b = dt_bilateral_init_cl(devid, width, height, sigma_s, sigma_r);
      if(!b) goto error;
      err = dt_bilateral_splat_cl(b, dev_out);
681
      if(err != CL_SUCCESS) goto error;
682
      err = dt_bilateral_blur_cl(b);
683
      if(err != CL_SUCCESS) goto error;
684
      err = dt_bilateral_slice_cl(b, dev_out, dev_tmp, -1.0f);
685
      if(err != CL_SUCCESS) goto error;
686 687 688
      dt_bilateral_free_cl(b);
      b = NULL; // make sure we don't clean it up twice
    }
689 690 691 692 693 694 695
    else
    {
      size_t origin[] = { 0, 0, 0 };
      size_t region[] = { width, height, 1 };
      err = dt_opencl_enqueue_copy_image(devid, dev_out, dev_tmp, origin, origin, region);
      if(err != CL_SUCCESS) goto error;
    }
696 697

    dt_opencl_set_kernel_arg(devid, gd->kernel_mapping, 0, sizeof(cl_mem), (void *)&dev_in);
698
    dt_opencl_set_kernel_arg(devid, gd->kernel_mapping, 1, sizeof(cl_mem), (void *)&dev_tmp);
699 700 701 702 703 704 705 706 707 708 709
    dt_opencl_set_kernel_arg(devid, gd->kernel_mapping, 2, sizeof(cl_mem), (void *)&dev_out);
    dt_opencl_set_kernel_arg(devid, gd->kernel_mapping, 3, sizeof(int), (void *)&width);
    dt_opencl_set_kernel_arg(devid, gd->kernel_mapping, 4, sizeof(int), (void *)&height);
    dt_opencl_set_kernel_arg(devid, gd->kernel_mapping, 5, sizeof(int), (void *)&data->n);
    dt_opencl_set_kernel_arg(devid, gd->kernel_mapping, 6, sizeof(cl_mem), (void *)&dev_target_mean);
    dt_opencl_set_kernel_arg(devid, gd->kernel_mapping, 7, sizeof(cl_mem), (void *)&dev_source_mean);
    dt_opencl_set_kernel_arg(devid, gd->kernel_mapping, 8, sizeof(cl_mem), (void *)&dev_var_ratio);
    dt_opencl_set_kernel_arg(devid, gd->kernel_mapping, 9, sizeof(cl_mem), (void *)&dev_mapio);
    err = dt_opencl_enqueue_kernel_2d(devid, gd->kernel_mapping, sizes);
    if(err != CL_SUCCESS) goto error;

710
    dt_opencl_release_mem_object(dev_tmp);
711 712 713 714 715 716 717 718 719 720
    dt_opencl_release_mem_object(dev_target_hist);
    dt_opencl_release_mem_object(dev_source_ihist);
    dt_opencl_release_mem_object(dev_target_mean);
    dt_opencl_release_mem_object(dev_source_mean);
    dt_opencl_release_mem_object(dev_var_ratio);
    dt_opencl_release_mem_object(dev_mapio);
    return TRUE;
  }
  else
  {
721 722
    size_t origin[] = { 0, 0, 0 };
    size_t region[] = { width, height, 1 };
723
    err = dt_opencl_enqueue_copy_image(devid, dev_in, dev_out, origin, origin, region);
724
    if(err != CL_SUCCESS) goto error;
725 726 727 728
    return TRUE;
  }

error:
729
  if(b != NULL) dt_bilateral_free_cl(b);
730 731 732 733 734 735 736
  dt_opencl_release_mem_object(dev_tmp);
  dt_opencl_release_mem_object(dev_target_hist);
  dt_opencl_release_mem_object(dev_source_ihist);
  dt_opencl_release_mem_object(dev_target_mean);
  dt_opencl_release_mem_object(dev_source_mean);
  dt_opencl_release_mem_object(dev_var_ratio);
  dt_opencl_release_mem_object(dev_mapio);
737 738 739 740 741 742
  dt_print(DT_DEBUG_OPENCL, "[opencl_colormapping] couldn't enqueue kernel! %d\n", err);
  return FALSE;
}
#endif


743 744 745
void tiling_callback(struct dt_iop_module_t *self, struct dt_dev_pixelpipe_iop_t *piece,
                     const dt_iop_roi_t *roi_in, const dt_iop_roi_t *roi_out,
                     struct dt_develop_tiling_t *tiling)
746
{
747
  const float scale = piece->iscale / roi_in->scale;
748 749 750 751 752 753 754
  const float sigma_s = 50.0f / scale;
  const float sigma_r = 8.0f; // does not depend on scale

  const int width = roi_in->width;
  const int height = roi_in->height;
  const int channels = piece->colors;

755
  const size_t basebuffer = width * height * channels * sizeof(float);
756

757
  tiling->factor = 3.0f + (float)dt_bilateral_memory_use(width, height, sigma_s, sigma_r) / basebuffer;
758 759
  tiling->maxbuf
      = fmax(1.0f, (float)dt_bilateral_singlebuffer_size(width, height, sigma_s, sigma_r) / basebuffer);
760
  tiling->overhead = 0;
761
  tiling->overlap = ceilf(4 * sigma_s);
762 763 764 765 766
  tiling->xalign = 1;
  tiling->yalign = 1;
  return;
}

767 768
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)
769 770 771 772 773 774 775 776 777 778 779
{
  dt_iop_colormapping_params_t *p = (dt_iop_colormapping_params_t *)p1;
  dt_iop_colormapping_data_t *d = (dt_iop_colormapping_data_t *)piece->data;

  memcpy(d, p, sizeof(dt_iop_colormapping_params_t));
#ifdef HAVE_OPENCL
  if(d->equalization > 0.1f)
    piece->process_cl_ready = (piece->process_cl_ready && !(darktable.opencl->avoid_atomics));
#endif
}

780

781
static void clusters_changed(GtkWidget *slider, dt_iop_module_t *self)
782 783 784
{
  if(darktable.gui->reset) return;
  dt_iop_colormapping_params_t *p = (dt_iop_colormapping_params_t *)self->params;
785
  dt_iop_colormapping_gui_data_t *g = (dt_iop_colormapping_gui_data_t *)self->gui_data;
786 787 788 789 790

  int new = (int)dt_bauhaus_slider_get(slider);
  if(new != p->n)
  {
    p->n = new;
791 792 793 794 795 796 797 798
    memset(p->source_ihist, 0, sizeof(float) * HISTN);
    memset(p->source_mean, 0, sizeof(float) * MAXN * 2);
    memset(p->source_var, 0, sizeof(float) * MAXN * 2);
    memset(p->source_weight, 0, sizeof(float) * MAXN);
    memset(p->target_hist, 0, sizeof(int) * HISTN);
    memset(p->target_mean, 0, sizeof(float) * MAXN * 2);
    memset(p->target_var, 0, sizeof(float) * MAXN * 2);
    memset(p->target_weight, 0, sizeof(float) * MAXN);
799 800
    p->flag = NEUTRAL;
    dt_dev_add_history_item(darktable.develop, self, TRUE);
801 802
    dt_control_queue_redraw_widget(g->source_area);
    dt_control_queue_redraw_widget(g->target_area);
803
  }
804 805
}

806
static void dominance_changed(GtkWidget *slider, dt_iop_module_t *self)
807 808 809 810 811 812 813
{
  if(self->dt->gui->reset) return;
  dt_iop_colormapping_params_t *p = (dt_iop_colormapping_params_t *)self->params;
  p->dominance = dt_bauhaus_slider_get(slider);
  dt_dev_add_history_item(darktable.develop, self, TRUE);
}

814
static void equalization_changed(GtkWidget *slider, dt_iop_module_t *self)
815 816 817 818 819 820 821
{
  if(self->dt->gui->reset) return;
  dt_iop_colormapping_params_t *p = (dt_iop_colormapping_params_t *)self->params;
  p->equalization = dt_bauhaus_slider_get(slider);
  dt_dev_add_history_item(darktable.develop, self, TRUE);
}

822
static void acquire_source_button_pressed(GtkButton *button, dt_iop_module_t *self)
823 824 825 826 827 828 829 830 831 832
{
  if(darktable.gui->reset) return;
  dt_iop_colormapping_params_t *p = (dt_iop_colormapping_params_t *)self->params;
  p->flag |= ACQUIRE;
  p->flag |= GET_SOURCE;
  p->flag &= ~HAS_SOURCE;
  dt_iop_request_focus(self);
  dt_dev_add_history_item(darktable.develop, self, TRUE);
}

833
static void acquire_target_button_pressed(GtkButton *button, dt_iop_module_t *self)
834 835 836 837 838 839 840 841 842 843
{
  if(darktable.gui->reset) return;
  dt_iop_colormapping_params_t *p = (dt_iop_colormapping_params_t *)self->params;
  p->flag |= ACQUIRE;
  p->flag |= GET_TARGET;
  p->flag &= ~HAS_TARGET;
  dt_iop_request_focus(self);
  dt_dev_add_history_item(darktable.develop, self, TRUE);
}

844
void init_pipe(struct dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece)
845 846 847 848 849
{
  piece->data = malloc(sizeof(dt_iop_colormapping_data_t));
  self->commit_params(self, self->default_params, pipe, piece);
}

850
void cleanup_pipe(struct dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece)
851 852
{
  free(piece->data);
853
  piece->data = NULL;
854 855
}

856
void gui_update(struct dt_iop_module_t *self)
857 858 859 860 861 862 863 864 865
{
  dt_iop_colormapping_params_t *p = (dt_iop_colormapping_params_t *)self->params;
  dt_iop_colormapping_gui_data_t *g = (dt_iop_colormapping_gui_data_t *)self->gui_data;
  dt_bauhaus_slider_set(g->clusters, p->n);
  dt_bauhaus_slider_set(g->dominance, p->dominance);
  dt_bauhaus_slider_set(g->equalization, p->equalization);
  dt_control_queue_redraw_widget(self->widget);
}

866
void init(dt_iop_module_t *module)
867
{
868 869
  module->params = calloc(1, sizeof(dt_iop_colormapping_params_t));
  module->default_params = calloc(1, sizeof(dt_iop_colormapping_params_t));
870
  module->default_enabled = 0;
Heiko Bauke's avatar
Heiko Bauke committed
871
  module->priority = 499; // module order created by iop_dependencies.py, do not edit!
872 873 874 875
  module->params_size = sizeof(dt_iop_colormapping_params_t);
  module->gui_data = NULL;
}

876
void init_global(dt_iop_module_so_t *module)
877 878
{
  const int program = 8; // extended.cl, from programs.conf
879 880
  dt_iop_colormapping_global_data_t *gd
      = (dt_iop_colormapping_global_data_t *)malloc(sizeof(dt_iop_colormapping_global_data_t));
881 882 883 884 885
  module->data = gd;
  gd->kernel_histogram = dt_opencl_create_kernel(program, "colormapping_histogram");
  gd->kernel_mapping = dt_opencl_create_kernel(program, "colormapping_mapping");
}

886
void cleanup(dt_iop_module_t *module)
887 888 889 890 891
{
  free(module->params);
  module->params = NULL;
}

892
void cleanup_global(dt_iop_module_so_t *module)
893 894 895 896 897 898 899
{
  dt_iop_colormapping_global_data_t *gd = (dt_iop_colormapping_global_data_t *)module->data;
  dt_opencl_free_kernel(gd->kernel_histogram);
  dt_opencl_free_kernel(gd->kernel_mapping);
  free(module->data);
  module->data = NULL;
}
900

901
void reload_defaults(dt_iop_module_t *module)
902
{
903 904
  dt_iop_colormapping_params_t tmp
      = (dt_iop_colormapping_params_t){ .flag = NEUTRAL, .n = 3, .dominance = 100.0f, .equalization = 50.0f };
905

906 907
  // we might be called from presets update infrastructure => there is no image
  if(!module->dev) goto end;
908

909
  dt_iop_colormapping_gui_data_t *g = (dt_iop_colormapping_gui_data_t *)module->gui_data;
910 911
  if(module->dev->gui_attached && g && g->flowback_set)
  {
912 913 914 915
    memcpy(tmp.source_ihist, g->flowback.hist, sizeof(float) * HISTN);
    memcpy(tmp.source_mean, g->flowback.mean, sizeof(float) * MAXN * 2);
    memcpy(tmp.source_var, g->flowback.var, sizeof(float) * MAXN * 2);
    memcpy(tmp.source_weight, g->flowback.weight, sizeof(float) * MAXN);
916 917 918
    tmp.n = g->flowback.n;
    tmp.flag = HAS_SOURCE;
  }
919
  module->default_enabled = 0;
920

921
end:
922 923 924 925 926
  memcpy(module->default_params, &tmp, sizeof(dt_iop_colormapping_params_t));
  memcpy(module->params, &tmp, sizeof(dt_iop_colormapping_params_t));
}


927
static gboolean cluster_preview_draw(GtkWidget *widget, cairo_t *crf, dt_iop_module_t *self)
928 929 930 931
{
  dt_iop_colormapping_params_t *p = (dt_iop_colormapping_params_t *)self->params;
  dt_iop_colormapping_gui_data_t *g = (dt_iop_colormapping_gui_data_t *)self->gui_data;

932 933
  float(*mean)[2];
  float(*var)[2];
934

935
  if(widget == g->source_area)
936
  {
937 938 939 940 941 942 943
    mean = p->source_mean;
    var = p->source_var;
  }
  else
  {
    mean = p->target_mean;
    var = p->target_var;
944 945 946
  }


947 948
  GtkAllocation allocation;
  gtk_widget_get_allocation(widget, &allocation);
949
  const int inset = 5;
950
  int width = allocation.width, height = allocation.height;
951 952
  cairo_surface_t *cst = dt_cairo_image_surface_create(CAIRO_FORMAT_ARGB32, width, height);
  cairo_t *cr = cairo_create(cst);
953
  cairo_set_source_rgb(cr, .2, .2, .2);
954 955 956
  cairo_paint(cr);

  cairo_translate(cr, inset, inset);
957 958
  width -= 2 * inset;
  height -= 2 * inset;
959 960


961
  const float sep = DT_PIXEL_APPLY_DPI(2.0);
962 963
  const float qwd = (width - (p->n - 1) * sep) / (float)p->n;
  for(int cl = 0; cl < p->n; cl++)
964 965
  {
    // draw cluster
966 967
    for(int j = -1; j <= 1; j++)
      for(int i = -1; i <= 1; i++)
968 969
      {
        // draw 9x9 grid showing mean and variance of this cluster.
970
        double rgb[3] = { 0.5, 0.5, 0.5 };
971
        cmsCIELab Lab;
972 973 974
        Lab.L = 5.0; // 53.390011;
        Lab.a = (mean[cl][0] + i * var[cl][0]); // / Lab.L;
        Lab.b = (mean[cl][1] + j * var[cl][1]); // / Lab.L;
975 976
        Lab.L = 53.390011;
        cmsDoTransform(g->xform, &Lab, rgb, 1);
977 978 979
        cairo_set_source_rgb(cr, rgb[0], rgb[1], rgb[2]);
        cairo_rectangle(cr, qwd * (i + 1) / 3.0, height * (j + 1) / 3.0, qwd / 3.0 - DT_PIXEL_APPLY_DPI(.5),
                        height / 3.0 - DT_PIXEL_APPLY_DPI(.5));
980 981
        cairo_fill(cr);
      }
982
    cairo_translate(cr, qwd + sep, 0);
983 984
  }

985 986 987 988
  cairo_destroy(cr);
  cairo_set_source_surface(crf, cst, 0, 0);
  cairo_paint(crf);
  cairo_surface_destroy(cst);
989 990 991 992
  return TRUE;
}


993
static void process_clusters(gpointer instance, gpointer user_data)
994
{
995
  dt_iop_module_t *self = (dt_iop_module_t *)user_data;
996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008
  dt_iop_colormapping_params_t *p = (dt_iop_colormapping_params_t *)self->params;
  dt_iop_colormapping_gui_data_t *g = (dt_iop_colormapping_gui_data_t *)self->gui_data;
  int new_source_clusters = 0;

  if(!g || !g->buffer) return;
  if(!(p->flag & ACQUIRE)) return;

  darktable.gui->reset = 1;

  dt_pthread_mutex_lock(&g->lock);
  const int width = g->width;
  const int height = g->height;
  const int ch = g->ch;
1009
  float *buffer = malloc(width * height * ch * sizeof(float));
1010 1011 1012 1013 1014
  if(!buffer)
  {
    dt_pthread_mutex_unlock(&g->lock);
    return;
  }
1015
  memcpy(buffer, g->buffer, width * height * ch * sizeof(float));
1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032
  dt_pthread_mutex_unlock(&g->lock);

  if(p->flag & GET_SOURCE)
  {
    int hist[HISTN];

    // get histogram of L
    capture_histogram(buffer, width, height, hist);

    // invert histogram
    invert_histogram(hist, p->source_ihist);

    // get n color clusters
    kmeans(buffer, width, height, p->n, p->source_mean, p->source_var, p->source_weight);

    p->flag |= HAS_SOURCE;
    new_source_clusters = 1;
1033 1034

    dt_control_queue_redraw_widget(g->source_area);
1035 1036 1037 1038 1039 1040 1041
  }
  else if(p->flag & GET_TARGET)
  {
    // get histogram of L
    capture_histogram(buffer, width, height, p->target_hist);

    // get n color clusters
1042
    kmeans(buffer, width, height, p->n, p->target_mean, p->target_var, p->target_weight);
1043 1044

    p->flag |= HAS_TARGET;
1045 1046

    dt_control_queue_redraw_widget(g->target_area);
Tobias Ellinghaus's avatar
Tobias Ellinghaus committed
1047
  }
1048 1049 1050 1051 1052

  free(buffer);

  if(new_source_clusters)
  {
1053 1054 1055 1056
    memcpy(g->flowback.hist, p->source_ihist, sizeof(float) * HISTN);
    memcpy(g->flowback.mean, p->source_mean, sizeof(float) * MAXN * 2);
    memcpy(g->flowback.var, p->source_var, sizeof(float) * MAXN * 2);
    memcpy(g->flowback.weight, p->source_weight, sizeof(float) * MAXN);
1057 1058
    g->flowback.n = p->n;
    g->flowback_set = 1;
1059
    FILE *f = g_fopen("/tmp/dt_colormapping_loaded", "wb");
1060 1061
    if(f)
    {
1062 1063
      if(fwrite(&g->flowback, sizeof(g->flowback), 1, f) < 1)
        fprintf(stderr, "[colormapping] could not write flowback file /tmp/dt_colormapping_loaded\n");
1064 1065 1066 1067 1068 1069 1070
      fclose(f);
    }
  }

  p->flag &= ~(GET_TARGET | GET_SOURCE | ACQUIRE);
  darktable.gui->reset = 0;

1071
  if(p->flag & HAS_SOURCE) dt_dev_add_history_item(darktable.develop, self, TRUE);
1072 1073 1074 1075 1076

  dt_control_queue_redraw();
}


1077
void gui_init(struct dt_iop_module_t *self)
1078 1079 1080 1081 1082 1083 1084
{
  self->gui_data = malloc(sizeof(dt_iop_colormapping_gui_data_t));
  dt_iop_colormapping_gui_data_t *g = (dt_iop_colormapping_gui_data_t *)self->gui_data;
  dt_iop_colormapping_params_t *p = (dt_iop_colormapping_params_t *)self->params;

  g->flag = NEUTRAL;
  g->flowback_set = 0;
1085 1086 1087
  cmsHPROFILE hsRGB = dt_colorspaces_get_profile(DT_COLORSPACE_SRGB, "", DT_PROFILE_DIRECTION_IN)->profile;
  cmsHPROFILE hLab = dt_colorspaces_get_profile(DT_COLORSPACE_LAB, "", DT_PROFILE_DIRECTION_ANY)->profile;
  g->xform = cmsCreateTransform(hLab, TYPE_Lab_DBL, hsRGB, TYPE_RGB_DBL, INTENT_PERCEPTUAL, 0);
1088 1089 1090 1091
  g->buffer = NULL;

  dt_pthread_mutex_init(&g->lock, NULL);

1092
  self->widget = GTK_WIDGET(gtk_box_new(GTK_ORIENTATION_VERTICAL, DT_BAUHAUS_SPACE));
1093

1094
  GtkBox *hbox1 = GTK_BOX(gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0));
1095 1096 1097 1098
  GtkWidget *source = gtk_label_new(_("source clusters:"));
  gtk_box_pack_start(GTK_BOX(hbox1), GTK_WIDGET(source), FALSE, FALSE, 0);
  gtk_box_pack_start(GTK_BOX(self->widget), GTK_WIDGET(hbox1), TRUE, TRUE, 0);

1099
  g->source_area = dtgtk_drawing_area_new_with_aspect_ratio(1.0 / 3.0);
1100
  gtk_box_pack_start(GTK_BOX(self->widget), g->source_area, TRUE, TRUE, 0);
1101
  g_signal_connect(G_OBJECT(g->source_area), "draw", G_CALLBACK(cluster_preview_draw), self);
1102

1103
  GtkBox *hbox2 = GTK_BOX(gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0));
1104 1105 1106 1107
  GtkWidget *target = gtk_label_new(_("target clusters:"));
  gtk_box_pack_start(GTK_BOX(hbox2), GTK_WIDGET(target), FALSE, FALSE, 0);
  gtk_box_pack_start(GTK_BOX(self->widget), GTK_WIDGET(hbox2), TRUE, TRUE, 0);

1108
  g->target_area = dtgtk_drawing_area_new_with_aspect_ratio(1.0 / 3.0);
1109
  gtk_box_pack_start(GTK_BOX(self->widget), g->target_area, TRUE, TRUE, 0);
1110
  g_signal_connect(G_OBJECT(g->target_area), "draw", G_CALLBACK(cluster_preview_draw), self);
1111 1112


1113
  GtkBox *box = GTK_BOX(gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 5));
1114 1115 1116
  gtk_box_pack_start(GTK_BOX(self->widget), GTK_WIDGET(box), TRUE, TRUE, 0);
  GtkWidget *button;

1117
  button = gtk_button_new_with_label(_("acquire as source"));
1118
  g->acquire_source_button = button;
1119
  gtk_widget_set_tooltip_text(button, _("analyze this image as a source image"));
1120 1121 1122
  gtk_box_pack_start(box, button, TRUE, TRUE, 0);
  g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(acquire_source_button_pressed), (gpointer)self);

1123
  button = gtk_button_new_with_label(_("acquire as target"));
1124
  g->acquire_target_button = button;
1125
  gtk_widget_set_tooltip_text(button, _("analyze this image as a target image"));
1126 1127 1128
  gtk_box_pack_start(box, button, TRUE, TRUE, 0);
  g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(acquire_target_button_pressed), (gpointer)self);

1129
  g->clusters = dt_bauhaus_slider_new_with_range(self, 1.0f, 5.0f, 1., p->n, 0);
1130
  dt_bauhaus_widget_set_label(g->clusters, NULL, _("number of clusters"));
1131
  dt_bauhaus_slider_set_format(g->clusters, "%.0f");
1132
  gtk_widget_set_tooltip_text(g->clusters, _("number of clusters to find in image. value change resets all clusters"));
1133 1134 1135
  gtk_box_pack_start(GTK_BOX(self->widget), GTK_WIDGET(g->clusters), TRUE, TRUE, 0);
  g_signal_connect(G_OBJECT(g->clusters), "value-changed", G_CALLBACK(clusters_changed), (gpointer)self);

1136
  g->dominance = dt_bauhaus_slider_new_with_range(self, 0, 100.0, 2., p->dominance, 2);
1137
  dt_bauhaus_widget_set_label(g->dominance, NULL, _("color dominance"));
1138 1139
  gtk_widget_set_tooltip_text(g->dominance, _("how clusters are mapped. low values: based on color "
                                              "proximity, high values: based on color dominance"));
1140
  dt_bauhaus_slider_set_format(g->dominance, "%.02f%%");
1141
  gtk_box_pack_start(GTK_BOX(self->widget), GTK_WIDGET(g->dominance), TRUE, TRUE, 0);
1142
  g_signal_connect(G_OBJECT(g->dominance), "value-changed", G_CALLBACK(dominance_changed), self);
1143

1144
  g->equalization = dt_bauhaus_slider_new_with_range(self, 0, 100.0, 2., p->equalization, 2);
1145
  dt_bauhaus_widget_set_label(g->equalization, NULL, _("histogram equalization"));
1146
  gtk_widget_set_tooltip_text(g->equalization, _("level of histogram equalization"));
1147
  dt_bauhaus_slider_set_format(g->equalization, "%.02f%%");
1148
  gtk_box_pack_start(GTK_BOX(self->widget), GTK_WIDGET(g->equalization), TRUE, TRUE, 0);
1149
  g_signal_connect(G_OBJECT(g->equalization), "value-changed", G_CALLBACK(equalization_changed), self);
1150

1151 1152 1153 1154
  /* add signal handler for preview pipe finished: process clusters if requested */
  dt_control_signal_connect(darktable.signals, DT_SIGNAL_DEVELOP_PREVIEW_PIPE_FINISHED,
                            G_CALLBACK(process_clusters), self);

1155

Tobias Ellinghaus's avatar