Ansel 0.0
A darktable fork - bloat + design vision
Loading...
Searching...
No Matches
colorreconstruction.c
Go to the documentation of this file.
1/*
2 This file is part of darktable,
3 Copyright (C) 2015 Pedro Côrte-Real.
4 Copyright (C) 2015-2016 Roman Lebedev.
5 Copyright (C) 2015-2017, 2019 Tobias Ellinghaus.
6 Copyright (C) 2015-2017 Ulrich Pegelow.
7 Copyright (C) 2016, 2018 johannes hanika.
8 Copyright (C) 2017 Heiko Bauke.
9 Copyright (C) 2018-2023, 2025-2026 Aurélien PIERRE.
10 Copyright (C) 2018 Edgardo Hoszowski.
11 Copyright (C) 2018 Maurizio Paglia.
12 Copyright (C) 2018, 2020-2022 Pascal Obry.
13 Copyright (C) 2018 rawfiner.
14 Copyright (C) 2019 Andreas Schneider.
15 Copyright (C) 2020 Aldric Renaudin.
16 Copyright (C) 2020 Chris Elston.
17 Copyright (C) 2020, 2022 Diederik Ter Rahe.
18 Copyright (C) 2020-2021 Hubert Kowalski.
19 Copyright (C) 2020-2021 Ralf Brown.
20 Copyright (C) 2022 Hanno Schwalm.
21 Copyright (C) 2022 Martin Bařinka.
22 Copyright (C) 2022 Philipp Lutz.
23 Copyright (C) 2023 Luca Zulberti.
24 Copyright (C) 2024 Alynx Zhou.
25
26 darktable is free software: you can redistribute it and/or modify
27 it under the terms of the GNU General Public License as published by
28 the Free Software Foundation, either version 3 of the License, or
29 (at your option) any later version.
30
31 darktable is distributed in the hope that it will be useful,
32 but WITHOUT ANY WARRANTY; without even the implied warranty of
33 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
34 GNU General Public License for more details.
35
36 You should have received a copy of the GNU General Public License
37 along with darktable. If not, see <http://www.gnu.org/licenses/>.
38*/
39
40#ifdef HAVE_CONFIG_H
41#include "system/macros.h"
42#include "system/mem_alloc.h"
44#include "common/logging.h"
45#include "system/openmp.h"
46#include "system/simd.h"
49#include "config.h"
50#endif
51#include "widgets/bauhaus.h"
53#include "common/imagebuf.h"
54#include "common/opencl.h"
56#include "develop/develop.h"
57#include "develop/imageop.h"
58#include "develop/imageop_gui.h"
59#include "develop/tiling.h"
60
61#include "iop/iop_api.h"
62
63#include <assert.h>
64#include <gtk/gtk.h>
65#include <inttypes.h>
66#include <math.h>
67#include <stdlib.h>
68#include <string.h>
69#include "widgets/label.h"
70
71#define DT_COLORRECONSTRUCT_BILATERAL_MAX_RES_S 500
72#define DT_COLORRECONSTRUCT_BILATERAL_MAX_RES_R 100
73#define DT_COLORRECONSTRUCT_SPATIAL_APPROX 100.0f
74
76
78{
79 COLORRECONSTRUCT_PRECEDENCE_NONE, // $DESCRIPTION: "none" same weighting factor for all pixels
80 COLORRECONSTRUCT_PRECEDENCE_CHROMA, // $DESCRIPTION: "saturated colors" use chromaticy as weighting factor -> prefers saturated colors
81 COLORRECONSTRUCT_PRECEDENCE_HUE // $DESCRIPTION: "hue" use a specific hue as weighting factor
83
90
98
100{
101 float threshold; // $MIN: 50.0 $MAX: 150.0 $DEFAULT: 100.0
102 float spatial; // $MIN: 0.0 $MAX: 1000.0 $DEFAULT: 400.0 $DESCRIPTION: "spatial extent"
103 float range; // $MIN: 0.0 $MAX: 50.0 $DEFAULT: 10.0 $DESCRIPTION: "range extent"
104 float hue; // $MIN: 0.0 $MAX: 1.0 $DEFAULT: 0.66
105 dt_iop_colorreconstruct_precedence_t precedence; // $DEFAULT: 0 COLORRECONSTRUCT_PRECEDENCE_NONE
107
115
124
135
144
152
153
154const char *name()
155{
156 return _("color reconstruction");
157}
158
159const char **description(struct dt_iop_module_t *self)
160{
161 return dt_iop_set_description(self, _("recover clipped highlights by propagating surrounding colors"),
162 _("corrective"),
163 _("linear or non-linear, Lab, display-referred"),
164 _("non-linear, Lab"),
165 _("non-linear, Lab, display-referred"));
166}
167
168int flags()
169{
170 // we do not allow tiling. reason: this module needs to see the full surrounding of highlights.
171 // if we would split into tiles, each tile would result in different color corrections
173}
174
176{
177 return IOP_GROUP_REPAIR;
178}
179
181{
182 return IOP_CS_LAB;
183}
184
185int legacy_params(dt_iop_module_t *self, const void *const old_params, const int old_version,
186 void *new_params, const int new_version)
187{
188 if(old_version == 1 && new_version == 3)
189 {
190 const dt_iop_colorreconstruct_params1_t *old = old_params;
191 dt_iop_colorreconstruct_params_t *new = new_params;
192 new->threshold = old->threshold;
193 new->spatial = old->spatial;
194 new->range = old->range;
195 new->precedence = COLORRECONSTRUCT_PRECEDENCE_NONE;
196 new->hue = 0.66f;
197 return 0;
198 }
199 else if(old_version == 2 && new_version == 3)
200 {
201 const dt_iop_colorreconstruct_params2_t *old = old_params;
202 dt_iop_colorreconstruct_params_t *new = new_params;
203 new->threshold = old->threshold;
204 new->spatial = old->spatial;
205 new->range = old->range;
206 new->precedence = old->precedence;
207 new->hue = 0.66f;
208 return 0;
209 }
210 return 1;
211}
212
221
222
223static inline float hue_conversion(const float HSL_Hue)
224{
225 dt_aligned_pixel_t rgb = { 0 };
226 dt_aligned_pixel_t XYZ = { 0 };
227 dt_aligned_pixel_t Lab = { 0 };
228
229 hsl2rgb(rgb, HSL_Hue, 1.0f, 0.5f);
230
231 XYZ[0] = (rgb[0] * 0.4360747f) + (rgb[1] * 0.3850649f) + (rgb[2] * 0.1430804f);
232 XYZ[1] = (rgb[0] * 0.2225045f) + (rgb[1] * 0.7168786f) + (rgb[2] * 0.0606169f);
233 XYZ[2] = (rgb[0] * 0.0139322f) + (rgb[1] * 0.0971045f) + (rgb[2] * 0.7141733f);
234
236
237 // Hue from LCH color space in [-pi, +pi] interval
238 float LCH_hue = atan2f(Lab[2], Lab[1]);
239
240 return LCH_hue;
241}
242
243
244static inline void image_to_grid(const dt_iop_colorreconstruct_bilateral_t *const b, const float i, const float j, const float L, float *x,
245 float *y, float *z)
246{
247 *x = CLAMPS(i / b->sigma_s, 0, b->size_x - 1);
248 *y = CLAMPS(j / b->sigma_s, 0, b->size_y - 1);
249 *z = CLAMPS(L / b->sigma_r, 0, b->size_z - 1);
250}
251
252static inline void grid_rescale(const dt_iop_colorreconstruct_bilateral_t *const b, const int i, const int j, const dt_iop_roi_t *roi,
253 const float scale, float *px, float *py)
254{
255 *px = (roi->x + i) * scale - b->x;
256 *py = (roi->y + j) * scale - b->y;
257}
258
259static inline __attribute__((always_inline)) void dt_iop_colorreconstruct_bilateral_dump(dt_iop_colorreconstruct_bilateral_frozen_t *bf)
260{
261 if(IS_NULL_PTR(bf)) return;
263 dt_free(bf);
264}
265
266static inline __attribute__((always_inline)) void dt_iop_colorreconstruct_bilateral_free(dt_iop_colorreconstruct_bilateral_t *b)
267{
268 if(IS_NULL_PTR(b)) return;
270 dt_free(b);
271}
272
274 const float iscale, // overall scale of input image
275 const float sigma_s, // spatial sigma (blur pixel coords)
276 const float sigma_r) // range sigma (blur luma values)
277{
279 if(IS_NULL_PTR(b))
280 {
281 fprintf(stderr, "[color reconstruction] not able to allocate buffer (a)\n");
282 return NULL;
283 }
284 float _x = roundf(roi->width / sigma_s);
285 float _y = roundf(roi->height / sigma_s);
286 float _z = roundf(100.0f / sigma_r);
287 b->size_x = CLAMPS((int)_x, 4, DT_COLORRECONSTRUCT_BILATERAL_MAX_RES_S) + 1;
288 b->size_y = CLAMPS((int)_y, 4, DT_COLORRECONSTRUCT_BILATERAL_MAX_RES_S) + 1;
289 b->size_z = CLAMPS((int)_z, 4, DT_COLORRECONSTRUCT_BILATERAL_MAX_RES_R) + 1;
290 b->width = roi->width;
291 b->height = roi->height;
292 b->x = roi->x;
293 b->y = roi->y;
294 b->scale = iscale / roi->scale;
295 b->sigma_s = MAX(roi->height / (b->size_y - 1.0f), roi->width / (b->size_x - 1.0f));
296 b->sigma_r = 100.0f / (b->size_z - 1.0f);
298 sizeof(dt_iop_colorreconstruct_Lab_t) * b->size_x * b->size_y * b->size_z,
299 0);
300 if(IS_NULL_PTR(b->buf))
301 {
302 fprintf(stderr, "[color reconstruction] not able to allocate buffer (b)\n");
303 dt_iop_colorreconstruct_bilateral_free(b);
304 return NULL;
305 }
306
307 memset(b->buf, 0, sizeof(dt_iop_colorreconstruct_Lab_t) * b->size_x * b->size_y * b->size_z);
308#if 0
309 fprintf(stderr, "[bilateral] created grid [%d %d %d]"
310 " with sigma (%f %f) (%f %f)\n", b->size_x, b->size_y, b->size_z,
311 b->sigma_s, sigma_s, b->sigma_r, sigma_r);
312#endif
313 return b;
314}
315
317{
318 if(IS_NULL_PTR(b)) return NULL;
319
321 if(IS_NULL_PTR(bf))
322 {
323 fprintf(stderr, "[color reconstruction] not able to allocate buffer (c)\n");
324 return NULL;
325 }
326
327 bf->size_x = b->size_x;
328 bf->size_y = b->size_y;
329 bf->size_z = b->size_z;
330 bf->width = b->width;
331 bf->height = b->height;
332 bf->x = b->x;
333 bf->y = b->y;
334 bf->scale = b->scale;
335 bf->sigma_s = b->sigma_s;
336 bf->sigma_r = b->sigma_r;
338 sizeof(dt_iop_colorreconstruct_Lab_t) * b->size_x * b->size_y * b->size_z,
339 0);
340 if(bf->buf && b->buf)
341 {
342 memcpy(bf->buf, b->buf, sizeof(dt_iop_colorreconstruct_Lab_t) * b->size_x * b->size_y * b->size_z);
343 }
344 else
345 {
346 fprintf(stderr, "[color reconstruction] not able to allocate buffer (d)\n");
347 dt_iop_colorreconstruct_bilateral_dump(bf);
348 return NULL;
349 }
350
351 return bf;
352}
353
354
357 dt_iop_colorreconstruct_precedence_t precedence, const float *params)
358{
359 if(IS_NULL_PTR(b)) return;
360
361 // splat into downsampled grid
363 for(int j = 0; j < b->height; j++)
364 {
365 size_t index = (size_t)4 * j * b->width;
366 for(int i = 0; i < b->width; i++, index += 4)
367 {
368 float x, y, z, weight, m;
369 const float Lin = in[index];
370 const float ain = in[index + 1];
371 const float bin = in[index + 2];
372 // we deliberately ignore pixels above threshold
373 if (Lin > threshold) continue;
374
375 switch(precedence)
376 {
378 weight = sqrtf(ain * ain + bin * bin);
379 break;
380
382 m = atan2f(bin, ain) - params[0];
383 // readjust m into [-pi, +pi] interval
384 m = m > M_PI ? m - 2*M_PI : (m < -M_PI ? m + 2*M_PI : m);
385 weight = expf(-m*m/params[1]);
386 break;
387
389 default:
390 weight = 1.0f;
391 break;
392 }
393
394 image_to_grid(b, i, j, Lin, &x, &y, &z);
395
396 // closest integer splatting:
397 const int xi = CLAMPS((int)round(x), 0, b->size_x - 1);
398 const int yi = CLAMPS((int)round(y), 0, b->size_y - 1);
399 const int zi = CLAMPS((int)round(z), 0, b->size_z - 1);
400 const size_t grid_index = xi + b->size_x * (yi + b->size_y * zi);
401
402#ifdef _OPENMP
403#pragma omp atomic
404#endif
405 b->buf[grid_index].L += Lin * weight;
406
407#ifdef _OPENMP
408#pragma omp atomic
409#endif
410 b->buf[grid_index].a += ain * weight;
411
412#ifdef _OPENMP
413#pragma omp atomic
414#endif
415 b->buf[grid_index].b += bin * weight;
416
417#ifdef _OPENMP
418#pragma omp atomic
419#endif
420 b->buf[grid_index].weight += weight;
421 }
422 }
423}
424
425
427static void blur_line(dt_iop_colorreconstruct_Lab_t *buf, const int offset1, const int offset2, const int offset3, const int size1,
428 const int size2, const int size3)
429{
430 if(IS_NULL_PTR(buf)) return;
431
432 const float w0 = 6.f / 16.f;
433 const float w1 = 4.f / 16.f;
434 const float w2 = 1.f / 16.f;
436 for(int k = 0; k < size1; k++)
437 {
438 size_t index = (size_t)k * offset1;
439 for(int j = 0; j < size2; j++)
440 {
441 dt_iop_colorreconstruct_Lab_t tmp1 = buf[index];
442 buf[index].L = buf[index].L * w0 + w1 * buf[index + offset3].L + w2 * buf[index + 2 * offset3].L;
443 buf[index].a = buf[index].a * w0 + w1 * buf[index + offset3].a + w2 * buf[index + 2 * offset3].a;
444 buf[index].b = buf[index].b * w0 + w1 * buf[index + offset3].b + w2 * buf[index + 2 * offset3].b;
445 buf[index].weight = buf[index].weight * w0 + w1 * buf[index + offset3].weight + w2 * buf[index + 2 * offset3].weight;
446 index += offset3;
447 dt_iop_colorreconstruct_Lab_t tmp2 = buf[index];
448 buf[index].L = buf[index].L * w0 + w1 * (buf[index + offset3].L + tmp1.L) + w2 * buf[index + 2 * offset3].L;
449 buf[index].a = buf[index].a * w0 + w1 * (buf[index + offset3].a + tmp1.a) + w2 * buf[index + 2 * offset3].a;
450 buf[index].b = buf[index].b * w0 + w1 * (buf[index + offset3].b + tmp1.b) + w2 * buf[index + 2 * offset3].b;
451 buf[index].weight = buf[index].weight * w0 + w1 * (buf[index + offset3].weight + tmp1.weight) + w2 * buf[index + 2 * offset3].weight;
452 index += offset3;
453 for(int i = 2; i < size3 - 2; i++)
454 {
455 const dt_iop_colorreconstruct_Lab_t tmp3 = buf[index];
456 buf[index].L = buf[index].L * w0 + w1 * (buf[index + offset3].L + tmp2.L)
457 + w2 * (buf[index + 2 * offset3].L + tmp1.L);
458 buf[index].a = buf[index].a * w0 + w1 * (buf[index + offset3].a + tmp2.a)
459 + w2 * (buf[index + 2 * offset3].a + tmp1.a);
460 buf[index].b = buf[index].b * w0 + w1 * (buf[index + offset3].b + tmp2.b)
461 + w2 * (buf[index + 2 * offset3].b + tmp1.b);
462 buf[index].weight = buf[index].weight * w0 + w1 * (buf[index + offset3].weight + tmp2.weight)
463 + w2 * (buf[index + 2 * offset3].weight + tmp1.weight);
464
465 index += offset3;
466 tmp1 = tmp2;
467 tmp2 = tmp3;
468 }
469 const dt_iop_colorreconstruct_Lab_t tmp3 = buf[index];
470 buf[index].L = buf[index].L * w0 + w1 * (buf[index + offset3].L + tmp2.L) + w2 * tmp1.L;
471 buf[index].a = buf[index].a * w0 + w1 * (buf[index + offset3].a + tmp2.a) + w2 * tmp1.a;
472 buf[index].b = buf[index].b * w0 + w1 * (buf[index + offset3].b + tmp2.b) + w2 * tmp1.b;
473 buf[index].weight = buf[index].weight * w0 + w1 * (buf[index + offset3].weight + tmp2.weight) + w2 * tmp1.weight;
474 index += offset3;
475 buf[index].L = buf[index].L * w0 + w1 * tmp3.L + w2 * tmp2.L;
476 buf[index].a = buf[index].a * w0 + w1 * tmp3.a + w2 * tmp2.a;
477 buf[index].b = buf[index].b * w0 + w1 * tmp3.b + w2 * tmp2.b;
478 buf[index].weight = buf[index].weight * w0 + w1 * tmp3.weight + w2 * tmp2.weight;
479 index += offset3;
480 index += offset2 - offset3 * size3;
481 }
482 }
483}
484
485
486static inline __attribute__((always_inline)) void dt_iop_colorreconstruct_bilateral_blur(dt_iop_colorreconstruct_bilateral_t *b)
487{
488 if(IS_NULL_PTR(b)) return;
489
490 // gaussian up to 3 sigma
491 blur_line(b->buf, b->size_x * b->size_y, b->size_x, 1, b->size_z, b->size_y, b->size_x);
492 // gaussian up to 3 sigma
493 blur_line(b->buf, b->size_x * b->size_y, 1, b->size_x, b->size_z, b->size_x, b->size_y);
494 // gaussian up to 3 sigma
495 blur_line(b->buf, 1, b->size_x, b->size_x * b->size_y, b->size_x, b->size_y, b->size_z);
496}
497
500 const float *const in, float *const out,
501 const float threshold, const dt_iop_roi_t *const roi,
502 const float iscale)
503{
504 if(IS_NULL_PTR(b)) return;
505
506 const float rescale = iscale / (roi->scale * b->scale);
507 const int ox = 1;
508 const int oy = b->size_x;
509 const int oz = b->size_y * b->size_x;
511 for(int j = 0; j < roi->height; j++)
512 {
513 size_t index = (size_t)4 * j * roi->width;
514 for(int i = 0; i < roi->width; i++, index += 4)
515 {
516 float x, y, z;
517 float px, py;
518 const float Lin = out[index + 0] = in[index + 0];
519 const float ain = out[index + 1] = in[index + 1];
520 const float bin = out[index + 2] = in[index + 2];
521 out[index + 3] = in[index + 3];
522 const float blend = CLAMPS(20.0f / threshold * Lin - 19.0f, 0.0f, 1.0f);
523 if (blend == 0.0f) continue;
524 grid_rescale(b, i, j, roi, rescale, &px, &py);
525 image_to_grid(b, px, py, Lin, &x, &y, &z);
526 // trilinear lookup:
527 const int xi = MIN((int)x, b->size_x - 2);
528 const int yi = MIN((int)y, b->size_y - 2);
529 const int zi = MIN((int)z, b->size_z - 2);
530 const float xf = x - xi;
531 const float yf = y - yi;
532 const float zf = z - zi;
533 const size_t gi = xi + b->size_x * (yi + b->size_y * zi);
534
535 const float Lout = b->buf[gi].L * (1.0f - xf) * (1.0f - yf) * (1.0f - zf)
536 + b->buf[gi + ox].L * (xf) * (1.0f - yf) * (1.0f - zf)
537 + b->buf[gi + oy].L * (1.0f - xf) * (yf) * (1.0f - zf)
538 + b->buf[gi + ox + oy].L * (xf) * (yf) * (1.0f - zf)
539 + b->buf[gi + oz].L * (1.0f - xf) * (1.0f - yf) * (zf)
540 + b->buf[gi + ox + oz].L * (xf) * (1.0f - yf) * (zf)
541 + b->buf[gi + oy + oz].L * (1.0f - xf) * (yf) * (zf)
542 + b->buf[gi + ox + oy + oz].L * (xf) * (yf) * (zf);
543
544 const float aout = b->buf[gi].a * (1.0f - xf) * (1.0f - yf) * (1.0f - zf)
545 + b->buf[gi + ox].a * (xf) * (1.0f - yf) * (1.0f - zf)
546 + b->buf[gi + oy].a * (1.0f - xf) * (yf) * (1.0f - zf)
547 + b->buf[gi + ox + oy].a * (xf) * (yf) * (1.0f - zf)
548 + b->buf[gi + oz].a * (1.0f - xf) * (1.0f - yf) * (zf)
549 + b->buf[gi + ox + oz].a * (xf) * (1.0f - yf) * (zf)
550 + b->buf[gi + oy + oz].a * (1.0f - xf) * (yf) * (zf)
551 + b->buf[gi + ox + oy + oz].a * (xf) * (yf) * (zf);
552
553
554 const float bout = b->buf[gi].b * (1.0f - xf) * (1.0f - yf) * (1.0f - zf)
555 + b->buf[gi + ox].b * (xf) * (1.0f - yf) * (1.0f - zf)
556 + b->buf[gi + oy].b * (1.0f - xf) * (yf) * (1.0f - zf)
557 + b->buf[gi + ox + oy].b * (xf) * (yf) * (1.0f - zf)
558 + b->buf[gi + oz].b * (1.0f - xf) * (1.0f - yf) * (zf)
559 + b->buf[gi + ox + oz].b * (xf) * (1.0f - yf) * (zf)
560 + b->buf[gi + oy + oz].b * (1.0f - xf) * (yf) * (zf)
561 + b->buf[gi + ox + oy + oz].b * (xf) * (yf) * (zf);
562
563 const float weight = b->buf[gi].weight * (1.0f - xf) * (1.0f - yf) * (1.0f - zf)
564 + b->buf[gi + ox].weight * (xf) * (1.0f - yf) * (1.0f - zf)
565 + b->buf[gi + oy].weight * (1.0f - xf) * (yf) * (1.0f - zf)
566 + b->buf[gi + ox + oy].weight * (xf) * (yf) * (1.0f - zf)
567 + b->buf[gi + oz].weight * (1.0f - xf) * (1.0f - yf) * (zf)
568 + b->buf[gi + ox + oz].weight * (xf) * (1.0f - yf) * (zf)
569 + b->buf[gi + oy + oz].weight * (1.0f - xf) * (yf) * (zf)
570 + b->buf[gi + ox + oy + oz].weight * (xf) * (yf) * (zf);
571
572 const float lout = fmax(Lout, 0.01f);
573 out[index + 1] = (weight > 0.0f) ? ain * (1.0f - blend) + aout * Lin/lout * blend : ain;
574 out[index + 2] = (weight > 0.0f) ? bin * (1.0f - blend) + bout * Lin/lout * blend : bin;
575 }
576 }
577}
578
579
580int process(struct dt_iop_module_t *self, const dt_dev_pixelpipe_t *pipe, const dt_dev_pixelpipe_iop_t *piece, const void *const ivoid,
581 void *const ovoid)
582{
583 const dt_iop_roi_t *const roi_in = &piece->roi_in;
584 const dt_iop_roi_t *const roi_out = &piece->roi_out;
587 float *in = (float *)ivoid;
588 float *out = (float *)ovoid;
589
590 const float scale = dt_dev_get_module_scale(pipe, roi_in);
591 const float sigma_r = fmax(data->range, 0.1f);
592 const float sigma_s = fmax(data->spatial, 1.0f) / scale;
593 const float hue = hue_conversion(data->hue); // convert to LCH hue which better fits to Lab colorspace
594
595 const dt_aligned_pixel_t params = { hue, M_PI*M_PI/8, 0.0f, 0.0f };
596
598
600 if(IS_NULL_PTR(b)) goto error;
602 dt_iop_colorreconstruct_bilateral_blur(b);
603
604 dt_iop_colorreconstruct_bilateral_slice(b, in, out, data->threshold, roi_in, pipe->iscale);
605
606 // here is where we generate the canned bilateral grid of the preview pipe for later use
607 int err = 0;
608 if(self->dev->gui_attached && !IS_NULL_PTR(g) && dt_dev_pixelpipe_has_preview_output(self->dev, pipe, roi_out))
609 {
610 uint64_t hash = piece->global_hash;
612 dt_iop_colorreconstruct_bilateral_dump(g->can);
614 g->hash = hash;
615 if(!g->can) err = 1;
617 }
618
619 dt_iop_colorreconstruct_bilateral_free(b);
620 if(err) return 1;
621 return 0;
622
623error:
624 dt_control_log(_("module `color reconstruction' failed"));
625 dt_iop_colorreconstruct_bilateral_free(b);
626 dt_iop_image_copy_by_size(ovoid, ivoid, roi_out->width, roi_out->height, piece->dsc_in.channels);
627 return 1;
628}
629
630#ifdef HAVE_OPENCL
643
645{
646 if(IS_NULL_PTR(b)) return;
647 // free device mem
648 dt_opencl_release_mem_object(b->dev_grid);
649 dt_opencl_release_mem_object(b->dev_grid_tmp);
650 dt_free(b);
651}
652
654 const int devid,
656 const dt_iop_roi_t *roi, // dimensions of input image
657 const float iscale, // overall scale of input image
658 const float sigma_s, // spatial sigma (blur pixel coords)
659 const float sigma_r) // range sigma (blur luma values)
660{
661 int blocksizex, blocksizey;
662
664 = (dt_opencl_local_buffer_t){ .xoffset = 0, .xfactor = 1, .yoffset = 0, .yfactor = 1,
665 .cellsize = 4 * sizeof(float) + sizeof(int), .overhead = 0,
666 .sizex = 1 << 6, .sizey = 1 << 6 };
667
669 {
670 blocksizex = locopt.sizex;
671 blocksizey = locopt.sizey;
672 }
673 else
674 blocksizex = blocksizey = 1;
675
676 if(blocksizex * blocksizey < 16 * 16)
677 {
679 "[opencl_colorreconstruction] device %d does not offer sufficient resources to run bilateral grid\n",
680 devid);
681 return NULL;
682 }
683
685 if(IS_NULL_PTR(b))
686 {
687 dt_print(DT_DEBUG_OPENCL, "[opencl_colorreconstruction] not able to allocate host buffer (a)\n");
688 return NULL;
689 }
690
691 float _x = roundf(roi->width / sigma_s);
692 float _y = roundf(roi->height / sigma_s);
693 float _z = roundf(100.0f / sigma_r);
694 b->size_x = CLAMPS((int)_x, 4, DT_COLORRECONSTRUCT_BILATERAL_MAX_RES_S) + 1;
695 b->size_y = CLAMPS((int)_y, 4, DT_COLORRECONSTRUCT_BILATERAL_MAX_RES_S) + 1;
696 b->size_z = CLAMPS((int)_z, 4, DT_COLORRECONSTRUCT_BILATERAL_MAX_RES_R) + 1;
697 b->width = roi->width;
698 b->height = roi->height;
699 b->x = roi->x;
700 b->y = roi->y;
701 b->scale = iscale / roi->scale;
702 b->blocksizex = blocksizex;
703 b->blocksizey = blocksizey;
704 b->sigma_s = MAX(roi->height / (b->size_y - 1.0f), roi->width / (b->size_x - 1.0f));
705 b->sigma_r = 100.0f / (b->size_z - 1.0f);
706 b->devid = devid;
707 b->global = global;
708 b->dev_grid = NULL;
709 b->dev_grid_tmp = NULL;
710
711 // alloc grid buffer:
712 b->dev_grid
713 = dt_opencl_alloc_device_buffer(b->devid, sizeof(float) * 4 * b->size_x * b->size_y * b->size_z);
714 if(!b->dev_grid)
715 {
716 dt_print(DT_DEBUG_OPENCL, "[opencl_colorreconstruction] not able to allocate device buffer (b)\n");
718 return NULL;
719 }
720
721 // alloc temporary grid buffer
722 b->dev_grid_tmp
723 = dt_opencl_alloc_device_buffer(b->devid, sizeof(float) * 4 * b->size_x * b->size_y * b->size_z);
724 if(!b->dev_grid_tmp)
725 {
726 dt_print(DT_DEBUG_OPENCL, "[opencl_colorreconstruction] not able to allocate device buffer (c)\n");
728 return NULL;
729 }
730
731 // zero out grid
732 int wd = 4 * b->size_x, ht = b->size_y * b->size_z;
733 size_t sizes[] = { ROUNDUPDWD(wd, b->devid), ROUNDUPDHT(ht, b->devid), 1 };
734 dt_opencl_set_kernel_arg(b->devid, b->global->kernel_colorreconstruct_zero, 0, sizeof(cl_mem), (void *)&b->dev_grid);
735 dt_opencl_set_kernel_arg(b->devid, b->global->kernel_colorreconstruct_zero, 1, sizeof(int), (void *)&wd);
736 dt_opencl_set_kernel_arg(b->devid, b->global->kernel_colorreconstruct_zero, 2, sizeof(int), (void *)&ht);
737 cl_int err = -666;
738 err = dt_opencl_enqueue_kernel_2d(b->devid, b->global->kernel_colorreconstruct_zero, sizes);
739 if(err != CL_SUCCESS)
740 {
741 dt_print(DT_DEBUG_OPENCL, "[opencl_colorreconstruction] error running kernel colorreconstruct_zero: %d\n", err);
743 return NULL;
744 }
745
746#if 0
747 fprintf(stderr, "[bilateral] created grid [%d %d %d]"
748 " with sigma (%f %f) (%f %f)\n", b->size_x, b->size_y, b->size_z,
749 b->sigma_s, sigma_s, b->sigma_r, sigma_r);
750#endif
751 return b;
752}
753
755{
756 if(IS_NULL_PTR(b)) return NULL;
757
759 if(IS_NULL_PTR(bf))
760 {
761 dt_print(DT_DEBUG_OPENCL, "[opencl_colorreconstruction] not able to allocate host buffer (d)\n");
762 return NULL;
763 }
764
765 bf->size_x = b->size_x;
766 bf->size_y = b->size_y;
767 bf->size_z = b->size_z;
768 bf->width = b->width;
769 bf->height = b->height;
770 bf->x = b->x;
771 bf->y = b->y;
772 bf->scale = b->scale;
773 bf->sigma_s = b->sigma_s;
774 bf->sigma_r = b->sigma_r;
776 sizeof(dt_iop_colorreconstruct_Lab_t) * b->size_x * b->size_y * b->size_z,
777 0);
778 if(bf->buf && b->dev_grid)
779 {
780 // read bilateral grid from device memory to host buffer (blocking)
781 cl_int err = dt_opencl_read_buffer_from_device(b->devid, bf->buf, b->dev_grid, 0,
782 sizeof(dt_iop_colorreconstruct_Lab_t) * b->size_x * b->size_y * b->size_z, CL_TRUE);
783 if(err != CL_SUCCESS)
784 {
786 "[opencl_colorreconstruction] can not read bilateral grid from device %d\n", b->devid);
787 dt_iop_colorreconstruct_bilateral_dump(bf);
788 return NULL;
789 }
790 }
791 else
792 {
793 dt_print(DT_DEBUG_OPENCL, "[opencl_colorreconstruction] not able to allocate host buffer (e)\n");
794 dt_iop_colorreconstruct_bilateral_dump(bf);
795 return NULL;
796 }
797
798 return bf;
799}
800
801
803 dt_iop_colorreconstruct_precedence_t precedence, const float *params)
804{
805 cl_int err = -666;
806 if(IS_NULL_PTR(b)) return err;
807 int pref = precedence;
808 size_t sizes[] = { ROUNDUP(b->width, b->blocksizex), ROUNDUP(b->height, b->blocksizey), 1 };
809 size_t local[] = { b->blocksizex, b->blocksizey, 1 };
810 dt_opencl_set_kernel_arg(b->devid, b->global->kernel_colorreconstruct_splat, 0, sizeof(cl_mem), (void *)&in);
811 dt_opencl_set_kernel_arg(b->devid, b->global->kernel_colorreconstruct_splat, 1, sizeof(cl_mem), (void *)&b->dev_grid);
812 dt_opencl_set_kernel_arg(b->devid, b->global->kernel_colorreconstruct_splat, 2, sizeof(int), (void *)&b->width);
813 dt_opencl_set_kernel_arg(b->devid, b->global->kernel_colorreconstruct_splat, 3, sizeof(int), (void *)&b->height);
814 dt_opencl_set_kernel_arg(b->devid, b->global->kernel_colorreconstruct_splat, 4, sizeof(int), (void *)&b->size_x);
815 dt_opencl_set_kernel_arg(b->devid, b->global->kernel_colorreconstruct_splat, 5, sizeof(int), (void *)&b->size_y);
816 dt_opencl_set_kernel_arg(b->devid, b->global->kernel_colorreconstruct_splat, 6, sizeof(int), (void *)&b->size_z);
817 dt_opencl_set_kernel_arg(b->devid, b->global->kernel_colorreconstruct_splat, 7, sizeof(float), (void *)&b->sigma_s);
818 dt_opencl_set_kernel_arg(b->devid, b->global->kernel_colorreconstruct_splat, 8, sizeof(float), (void *)&b->sigma_r);
819 dt_opencl_set_kernel_arg(b->devid, b->global->kernel_colorreconstruct_splat, 9, sizeof(float), (void *)&threshold);
820 dt_opencl_set_kernel_arg(b->devid, b->global->kernel_colorreconstruct_splat, 10, sizeof(int), (void *)&pref);
821 dt_opencl_set_kernel_arg(b->devid, b->global->kernel_colorreconstruct_splat, 11, 4*sizeof(float), (void *)params);
822 dt_opencl_set_kernel_arg(b->devid, b->global->kernel_colorreconstruct_splat, 12, b->blocksizex * b->blocksizey * sizeof(int),
823 NULL);
824 dt_opencl_set_kernel_arg(b->devid, b->global->kernel_colorreconstruct_splat, 13,
825 b->blocksizex * b->blocksizey * 4 * sizeof(float), NULL);
826 err = dt_opencl_enqueue_kernel_2d_with_local(b->devid, b->global->kernel_colorreconstruct_splat, sizes, local);
827 return err;
828}
829
831{
832 cl_int err = -666;
833 if(IS_NULL_PTR(b)) return err;
834 size_t sizes[3] = { 0, 0, 1 };
835
836 err = dt_opencl_enqueue_copy_buffer_to_buffer(b->devid, b->dev_grid, b->dev_grid_tmp, 0, 0,
837 b->size_x * b->size_y * b->size_z * 4 * sizeof(float));
838 if(err != CL_SUCCESS) return err;
839
840 sizes[0] = ROUNDUPDWD(b->size_z, b->devid);
841 sizes[1] = ROUNDUPDHT(b->size_y, b->devid);
842 int stride1, stride2, stride3;
843 stride1 = b->size_x * b->size_y;
844 stride2 = b->size_x;
845 stride3 = 1;
846 dt_opencl_set_kernel_arg(b->devid, b->global->kernel_colorreconstruct_blur_line, 0, sizeof(cl_mem), (void *)&b->dev_grid_tmp);
847 dt_opencl_set_kernel_arg(b->devid, b->global->kernel_colorreconstruct_blur_line, 1, sizeof(cl_mem), (void *)&b->dev_grid);
848 dt_opencl_set_kernel_arg(b->devid, b->global->kernel_colorreconstruct_blur_line, 2, sizeof(int), (void *)&stride1);
849 dt_opencl_set_kernel_arg(b->devid, b->global->kernel_colorreconstruct_blur_line, 3, sizeof(int), (void *)&stride2);
850 dt_opencl_set_kernel_arg(b->devid, b->global->kernel_colorreconstruct_blur_line, 4, sizeof(int), (void *)&stride3);
851 dt_opencl_set_kernel_arg(b->devid, b->global->kernel_colorreconstruct_blur_line, 5, sizeof(int), (void *)&b->size_z);
852 dt_opencl_set_kernel_arg(b->devid, b->global->kernel_colorreconstruct_blur_line, 6, sizeof(int), (void *)&b->size_y);
853 dt_opencl_set_kernel_arg(b->devid, b->global->kernel_colorreconstruct_blur_line, 7, sizeof(int), (void *)&b->size_x);
854 err = dt_opencl_enqueue_kernel_2d(b->devid, b->global->kernel_colorreconstruct_blur_line, sizes);
855 if(err != CL_SUCCESS) return err;
856
857 stride1 = b->size_x * b->size_y;
858 stride2 = 1;
859 stride3 = b->size_x;
860 sizes[0] = ROUNDUPDWD(b->size_z, b->devid);
861 sizes[1] = ROUNDUPDHT(b->size_x, b->devid);
862 dt_opencl_set_kernel_arg(b->devid, b->global->kernel_colorreconstruct_blur_line, 0, sizeof(cl_mem), (void *)&b->dev_grid);
863 dt_opencl_set_kernel_arg(b->devid, b->global->kernel_colorreconstruct_blur_line, 1, sizeof(cl_mem), (void *)&b->dev_grid_tmp);
864 dt_opencl_set_kernel_arg(b->devid, b->global->kernel_colorreconstruct_blur_line, 2, sizeof(int), (void *)&stride1);
865 dt_opencl_set_kernel_arg(b->devid, b->global->kernel_colorreconstruct_blur_line, 3, sizeof(int), (void *)&stride2);
866 dt_opencl_set_kernel_arg(b->devid, b->global->kernel_colorreconstruct_blur_line, 4, sizeof(int), (void *)&stride3);
867 dt_opencl_set_kernel_arg(b->devid, b->global->kernel_colorreconstruct_blur_line, 5, sizeof(int), (void *)&b->size_z);
868 dt_opencl_set_kernel_arg(b->devid, b->global->kernel_colorreconstruct_blur_line, 6, sizeof(int), (void *)&b->size_x);
869 dt_opencl_set_kernel_arg(b->devid, b->global->kernel_colorreconstruct_blur_line, 7, sizeof(int), (void *)&b->size_y);
870 err = dt_opencl_enqueue_kernel_2d(b->devid, b->global->kernel_colorreconstruct_blur_line, sizes);
871 if(err != CL_SUCCESS) return err;
872
873 stride1 = 1;
874 stride2 = b->size_x;
875 stride3 = b->size_x * b->size_y;
876 sizes[0] = ROUNDUPDWD(b->size_x, b->devid);
877 sizes[1] = ROUNDUPDHT(b->size_y, b->devid);
878 dt_opencl_set_kernel_arg(b->devid, b->global->kernel_colorreconstruct_blur_line, 0, sizeof(cl_mem),
879 (void *)&b->dev_grid_tmp);
880 dt_opencl_set_kernel_arg(b->devid, b->global->kernel_colorreconstruct_blur_line, 1, sizeof(cl_mem), (void *)&b->dev_grid);
881 dt_opencl_set_kernel_arg(b->devid, b->global->kernel_colorreconstruct_blur_line, 2, sizeof(int), (void *)&stride1);
882 dt_opencl_set_kernel_arg(b->devid, b->global->kernel_colorreconstruct_blur_line, 3, sizeof(int), (void *)&stride2);
883 dt_opencl_set_kernel_arg(b->devid, b->global->kernel_colorreconstruct_blur_line, 4, sizeof(int), (void *)&stride3);
884 dt_opencl_set_kernel_arg(b->devid, b->global->kernel_colorreconstruct_blur_line, 5, sizeof(int), (void *)&b->size_x);
885 dt_opencl_set_kernel_arg(b->devid, b->global->kernel_colorreconstruct_blur_line, 6, sizeof(int), (void *)&b->size_y);
886 dt_opencl_set_kernel_arg(b->devid, b->global->kernel_colorreconstruct_blur_line, 7, sizeof(int), (void *)&b->size_z);
887 err = dt_opencl_enqueue_kernel_2d(b->devid, b->global->kernel_colorreconstruct_blur_line, sizes);
888 return err;
889}
890
892 const float threshold, const dt_iop_roi_t *roi, const float iscale)
893{
894 cl_int err = -666;
895 if(IS_NULL_PTR(b)) return err;
896 const int bxy[2] = { b->x, b->y };
897 const int roixy[2] = { roi->x, roi->y };
898 const float rescale = iscale / (roi->scale * b->scale);
899
900 size_t sizes[] = { ROUNDUPDWD(roi->width, b->devid), ROUNDUPDHT(roi->height, b->devid), 1 };
901 dt_opencl_set_kernel_arg(b->devid, b->global->kernel_colorreconstruct_slice, 0, sizeof(cl_mem), (void *)&in);
902 dt_opencl_set_kernel_arg(b->devid, b->global->kernel_colorreconstruct_slice, 1, sizeof(cl_mem), (void *)&out);
903 dt_opencl_set_kernel_arg(b->devid, b->global->kernel_colorreconstruct_slice, 2, sizeof(cl_mem), (void *)&b->dev_grid);
904 dt_opencl_set_kernel_arg(b->devid, b->global->kernel_colorreconstruct_slice, 3, sizeof(int), (void *)&roi->width);
905 dt_opencl_set_kernel_arg(b->devid, b->global->kernel_colorreconstruct_slice, 4, sizeof(int), (void *)&roi->height);
906 dt_opencl_set_kernel_arg(b->devid, b->global->kernel_colorreconstruct_slice, 5, sizeof(int), (void *)&b->size_x);
907 dt_opencl_set_kernel_arg(b->devid, b->global->kernel_colorreconstruct_slice, 6, sizeof(int), (void *)&b->size_y);
908 dt_opencl_set_kernel_arg(b->devid, b->global->kernel_colorreconstruct_slice, 7, sizeof(int), (void *)&b->size_z);
909 dt_opencl_set_kernel_arg(b->devid, b->global->kernel_colorreconstruct_slice, 8, sizeof(float), (void *)&b->sigma_s);
910 dt_opencl_set_kernel_arg(b->devid, b->global->kernel_colorreconstruct_slice, 9, sizeof(float), (void *)&b->sigma_r);
911 dt_opencl_set_kernel_arg(b->devid, b->global->kernel_colorreconstruct_slice, 10, sizeof(float), (void *)&threshold);
912 dt_opencl_set_kernel_arg(b->devid, b->global->kernel_colorreconstruct_slice, 11, 2*sizeof(int), (void *)&bxy);
913 dt_opencl_set_kernel_arg(b->devid, b->global->kernel_colorreconstruct_slice, 12, 2*sizeof(int), (void *)&roixy);
914 dt_opencl_set_kernel_arg(b->devid, b->global->kernel_colorreconstruct_slice, 13, sizeof(float), (void *)&rescale);
915 err = dt_opencl_enqueue_kernel_2d(b->devid, b->global->kernel_colorreconstruct_slice, sizes);
916 return err;
917}
918
919int process_cl(struct dt_iop_module_t *self, const dt_dev_pixelpipe_t *pipe, const dt_dev_pixelpipe_iop_t *piece, cl_mem dev_in, cl_mem dev_out)
920{
921 const dt_iop_roi_t *const roi_in = &piece->roi_in;
922 const dt_iop_roi_t *const roi_out = &piece->roi_out;
926
927 const float scale = dt_dev_get_module_scale(pipe, roi_in);
928 const float sigma_r = fmax(d->range, 0.1f); // does not depend on scale
929 const float sigma_s = fmax(d->spatial, 1.0f) / scale;
930 const float hue = hue_conversion(d->hue); // convert to LCH hue which better fits to Lab colorspace
931
932 const float params[4] = { hue, M_PI*M_PI/8, 0.0f, 0.0f };
933
934 cl_int err = -666;
935
937
939 if(IS_NULL_PTR(b)) goto error;
940 err = dt_iop_colorreconstruct_bilateral_splat_cl(b, dev_in, d->threshold, d->precedence, params);
941 if(err != CL_SUCCESS) goto error;
943 if(err != CL_SUCCESS) goto error;
944
945 err = dt_iop_colorreconstruct_bilateral_slice_cl(b, dev_in, dev_out, d->threshold, roi_in, pipe->iscale);
946 if(err != CL_SUCCESS) goto error;
947
948 if(self->dev->gui_attached && g && dt_dev_pixelpipe_has_preview_output(self->dev, pipe, roi_out))
949 {
950 uint64_t hash = piece->global_hash;
952 dt_iop_colorreconstruct_bilateral_dump(g->can);
954 g->hash = hash;
956 if(!g->can)
957 {
958 err = CL_MEM_OBJECT_ALLOCATION_FAILURE;
959 goto error;
960 }
961 }
962
964 return TRUE;
965
966error:
968 dt_print(DT_DEBUG_OPENCL, "[opencl_colorreconstruction] couldn't enqueue kernel! %d\n", err);
969 return FALSE;
970}
971#endif
972
973
974static size_t dt_iop_colorreconstruct_bilateral_memory_use(const int width, // width of input image
975 const int height, // height of input image
976 const float sigma_s, // spatial sigma (blur pixel coords)
977 const float sigma_r) // range sigma (blur luma values)
978{
979 float _x = roundf(width / sigma_s);
980 float _y = roundf(height / sigma_s);
981 float _z = roundf(100.0f / sigma_r);
982 size_t size_x = CLAMPS((int)_x, 4, DT_COLORRECONSTRUCT_BILATERAL_MAX_RES_S) + 1;
983 size_t size_y = CLAMPS((int)_y, 4, DT_COLORRECONSTRUCT_BILATERAL_MAX_RES_S) + 1;
984 size_t size_z = CLAMPS((int)_z, 4, DT_COLORRECONSTRUCT_BILATERAL_MAX_RES_R) + 1;
985
986 return size_x * size_y * size_z * 4 * sizeof(float) * 2; // in fact only the OpenCL path needs a second tmp buffer
987}
988
989
990static size_t dt_iop_colorreconstruct_bilateral_singlebuffer_size(const int width, // width of input image
991 const int height, // height of input image
992 const float sigma_s, // spatial sigma (blur pixel coords)
993 const float sigma_r) // range sigma (blur luma values)
994{
995 float _x = roundf(width / sigma_s);
996 float _y = roundf(height / sigma_s);
997 float _z = roundf(100.0f / sigma_r);
998 size_t size_x = CLAMPS((int)_x, 4, DT_COLORRECONSTRUCT_BILATERAL_MAX_RES_S) + 1;
999 size_t size_y = CLAMPS((int)_y, 4, DT_COLORRECONSTRUCT_BILATERAL_MAX_RES_S) + 1;
1000 size_t size_z = CLAMPS((int)_z, 4, DT_COLORRECONSTRUCT_BILATERAL_MAX_RES_R) + 1;
1001
1002 return size_x * size_y * size_z * 4 * sizeof(float);
1003}
1004
1005void 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)
1006{
1007 const dt_iop_roi_t *const roi_in = &piece->roi_in;
1009 const float scale = dt_dev_get_module_scale(pipe, roi_in);
1010 const float sigma_r = fmax(d->range, 0.1f);
1011 const float sigma_s = fmax(d->spatial, 1.0f) / scale;
1012
1013 const int width = roi_in->width;
1014 const int height = roi_in->height;
1015 const int channels = piece->dsc_in.channels;
1016
1017 const size_t basebuffer = sizeof(float) * channels * width * height;
1018
1019 tiling->factor = 2.0f + (float)dt_iop_colorreconstruct_bilateral_memory_use(width, height, sigma_s, sigma_r) / basebuffer;
1020 tiling->maxbuf
1022 tiling->overhead = 0;
1023 tiling->overlap = ceilf(4 * sigma_s);
1024 tiling->xalign = 1;
1025 tiling->yalign = 1;
1026 return;
1027}
1028
1029
1030void gui_changed(dt_iop_module_t *self, GtkWidget *w, void *previous)
1031{
1034 if(w == g->precedence)
1035 {
1036 gtk_widget_set_visible(g->hue, p->precedence == COLORRECONSTRUCT_PRECEDENCE_HUE);
1037 }
1038}
1039
1042{
1045
1046 d->threshold = p->threshold;
1047 d->spatial = p->spatial;
1048 d->range = p->range;
1049 d->precedence = p->precedence;
1050 d->hue = p->hue;
1051
1052#ifdef HAVE_OPENCL
1054#endif
1055}
1056
1063
1065{
1066 dt_free_align(piece->data);
1067 piece->data = NULL;
1068}
1069
1070void gui_update(struct dt_iop_module_t *self)
1071{
1072 const gboolean monochrome = dt_image_is_monochrome(&self->dev->image_storage);
1075
1076 self->hide_enable_button = monochrome;
1077 gtk_stack_set_visible_child_name(GTK_STACK(self->gui->widget), !monochrome ? "default" : "monochrome");
1078
1079 gtk_widget_set_visible(g->hue, p->precedence == COLORRECONSTRUCT_PRECEDENCE_HUE);
1080
1082 dt_iop_colorreconstruct_bilateral_dump(g->can);
1083 g->can = NULL;
1084 g->hash = 0;
1086}
1087
1089{
1092 module->data = gd;
1093 const int program = 13; // colorcorrection.cl, from programs.conf
1094 gd->kernel_colorreconstruct_zero = dt_opencl_create_kernel(program, "colorreconstruction_zero");
1095 gd->kernel_colorreconstruct_splat = dt_opencl_create_kernel(program, "colorreconstruction_splat");
1096 gd->kernel_colorreconstruct_blur_line = dt_opencl_create_kernel(program, "colorreconstruction_blur_line");
1097 gd->kernel_colorreconstruct_slice = dt_opencl_create_kernel(program, "colorreconstruction_slice");
1098}
1099
1109
1110
1111void gui_init(struct dt_iop_module_t *self)
1112{
1114
1115 g->can = NULL;
1116 g->hash = 0;
1117
1118 GtkWidget *box_enabled = self->gui->widget = gtk_box_new(GTK_ORIENTATION_VERTICAL, DT_GUI_BOX_SPACING);
1119
1120 g->threshold = dt_bauhaus_slider_from_params(self, N_("threshold"));
1121 g->spatial = dt_bauhaus_slider_from_params(self, N_("spatial"));
1122 g->range = dt_bauhaus_slider_from_params(self, N_("range"));
1123 g->precedence = dt_bauhaus_combobox_from_params(self, N_("precedence"));
1124 g->hue = dt_bauhaus_slider_from_params(self, N_("hue"));
1125 dt_bauhaus_slider_set_factor(g->hue, 360.0f);
1126 dt_bauhaus_slider_set_format(g->hue, "\302\260");
1128 dt_bauhaus_slider_set_stop(g->hue, 0.0f, 1.0f, 0.0f, 0.0f);
1129 dt_bauhaus_slider_set_stop(g->hue, 0.166f, 1.0f, 1.0f, 0.0f);
1130 dt_bauhaus_slider_set_stop(g->hue, 0.322f, 0.0f, 1.0f, 0.0f);
1131 dt_bauhaus_slider_set_stop(g->hue, 0.498f, 0.0f, 1.0f, 1.0f);
1132 dt_bauhaus_slider_set_stop(g->hue, 0.664f, 0.0f, 0.0f, 1.0f);
1133 dt_bauhaus_slider_set_stop(g->hue, 0.830f, 1.0f, 0.0f, 1.0f);
1134 dt_bauhaus_slider_set_stop(g->hue, 1.0f, 1.0f, 0.0f, 0.0f);
1135
1136 gtk_widget_show_all(g->hue);
1137 gtk_widget_set_no_show_all(g->hue, TRUE);
1138
1139 gtk_widget_set_tooltip_text(g->threshold, _("pixels with lightness values above this threshold are corrected"));
1140 gtk_widget_set_tooltip_text(g->spatial, _("how far to look for replacement colors in spatial dimensions"));
1141 gtk_widget_set_tooltip_text(g->range, _("how far to look for replacement colors in the luminance dimension"));
1142 gtk_widget_set_tooltip_text(g->precedence, _("if and how to give precedence to specific replacement colors"));
1143 gtk_widget_set_tooltip_text(g->hue, _("the hue tone which should be given precedence over other hue tones"));
1144
1145 GtkWidget *monochromes = dt_ui_label_new(_("not applicable"));
1146 gtk_widget_set_tooltip_text(monochromes, _("no highlights reconstruction for monochrome images"));
1147
1148 self->gui->widget = gtk_stack_new();
1149 gtk_stack_set_homogeneous(GTK_STACK(self->gui->widget), FALSE);
1150 gtk_stack_add_named(GTK_STACK(self->gui->widget), monochromes, "monochrome");
1151 gtk_stack_add_named(GTK_STACK(self->gui->widget), box_enabled, "default");
1152}
1153
1155{
1157 dt_iop_colorreconstruct_bilateral_dump(g->can);
1158
1160}
1161
1162// clang-format off
1163// modelines: These editor modelines have been set for all relevant files by tools/update_modelines.py
1164// vim: shiftwidth=2 expandtab tabstop=2 cindent
1165// kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-spaces modified;
1166// clang-format on
static void error(char *msg)
Definition ashift_lsd.c:202
#define TRUE
Definition ashift_lsd.c:162
#define FALSE
Definition ashift_lsd.c:158
#define m
Definition basecurve.c:283
void dt_bauhaus_slider_set_stop(GtkWidget *widget, float stop, float r, float g, float b)
Definition bauhaus.c:2161
void dt_bauhaus_slider_set_feedback(GtkWidget *widget, int feedback)
Definition bauhaus.c:3389
void dt_bauhaus_slider_set_format(GtkWidget *widget, const char *format)
Definition bauhaus.c:3407
void dt_bauhaus_slider_set_factor(GtkWidget *widget, float factor)
Definition bauhaus.c:3423
size_t size_x
Definition bilateral.h:0
size_t size_y
Definition bilateral.h:0
float sigma_s
Definition bilateral.h:3
size_t size_z
Definition bilateral.h:0
float sigma_r
Definition bilateral.h:3
@ IOP_CS_LAB
static const float x
static dt_iop_colorreconstruct_bilateral_frozen_t * dt_iop_colorreconstruct_bilateral_freeze_cl(dt_iop_colorreconstruct_bilateral_cl_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)
#define DT_COLORRECONSTRUCT_BILATERAL_MAX_RES_R
static __DT_CLONE_TARGETS__ void dt_iop_colorreconstruct_bilateral_splat(dt_iop_colorreconstruct_bilateral_t *b, const float *const in, const float threshold, dt_iop_colorreconstruct_precedence_t precedence, const float *params)
const char ** description(struct dt_iop_module_t *self)
int default_group()
static __DT_CLONE_TARGETS__ void blur_line(dt_iop_colorreconstruct_Lab_t *buf, const int offset1, const int offset2, const int offset3, const int size1, const int size2, const int size3)
static void image_to_grid(const dt_iop_colorreconstruct_bilateral_t *const b, const float i, const float j, const float L, float *x, float *y, float *z)
static void grid_rescale(const dt_iop_colorreconstruct_bilateral_t *const b, const int i, const int j, const dt_iop_roi_t *roi, const float scale, float *px, float *py)
static cl_int dt_iop_colorreconstruct_bilateral_blur_cl(dt_iop_colorreconstruct_bilateral_cl_t *b)
void init_pipe(struct dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece)
static size_t dt_iop_colorreconstruct_bilateral_memory_use(const int width, const int height, const float sigma_s, const float sigma_r)
static dt_iop_colorreconstruct_bilateral_t * dt_iop_colorreconstruct_bilateral_init(const dt_iop_roi_t *roi, const float iscale, const float sigma_s, const float sigma_r)
const char * name()
static cl_int dt_iop_colorreconstruct_bilateral_splat_cl(dt_iop_colorreconstruct_bilateral_cl_t *b, cl_mem in, const float threshold, dt_iop_colorreconstruct_precedence_t precedence, const float *params)
void gui_update(struct dt_iop_module_t *self)
static __DT_CLONE_TARGETS__ void dt_iop_colorreconstruct_bilateral_slice(const dt_iop_colorreconstruct_bilateral_t *const b, const float *const in, float *const out, const float threshold, const dt_iop_roi_t *const roi, const float iscale)
#define DT_COLORRECONSTRUCT_BILATERAL_MAX_RES_S
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)
void cleanup_global(dt_iop_module_so_t *module)
int default_colorspace(dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, const dt_dev_pixelpipe_iop_t *piece)
int flags()
static size_t dt_iop_colorreconstruct_bilateral_singlebuffer_size(const int width, const int height, const float sigma_s, const float sigma_r)
void gui_cleanup(struct dt_iop_module_t *self)
dt_iop_colorreconstruct_precedence_t
@ COLORRECONSTRUCT_PRECEDENCE_NONE
@ COLORRECONSTRUCT_PRECEDENCE_HUE
@ COLORRECONSTRUCT_PRECEDENCE_CHROMA
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)
static float hue_conversion(const float HSL_Hue)
static dt_iop_colorreconstruct_bilateral_frozen_t * dt_iop_colorreconstruct_bilateral_freeze(dt_iop_colorreconstruct_bilateral_t *b)
void cleanup_pipe(struct dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece)
static dt_iop_colorreconstruct_bilateral_cl_t * dt_iop_colorreconstruct_bilateral_init_cl(const int devid, dt_iop_colorreconstruct_global_data_t *global, const dt_iop_roi_t *roi, const float iscale, const float sigma_s, const float sigma_r)
void init_global(dt_iop_module_so_t *module)
int process_cl(struct dt_iop_module_t *self, const dt_dev_pixelpipe_t *pipe, const dt_dev_pixelpipe_iop_t *piece, cl_mem dev_in, cl_mem dev_out)
static void dt_iop_colorreconstruct_bilateral_free_cl(dt_iop_colorreconstruct_bilateral_cl_t *b)
int legacy_params(dt_iop_module_t *self, const void *const old_params, const int old_version, void *new_params, const int new_version)
static cl_int dt_iop_colorreconstruct_bilateral_slice_cl(dt_iop_colorreconstruct_bilateral_cl_t *b, cl_mem in, cl_mem out, const float threshold, const dt_iop_roi_t *roi, const float iscale)
void hsl2rgb(dt_aligned_pixel_t rgb, float h, float s, float l)
Convert HSL back to RGB. Common helper used by iop modules.
struct _GtkWidget GtkWidget
GtkWidget, opaque, spelled exactly as GTK spells it.
Definition colorspaces.h:98
static dt_aligned_pixel_t rgb
const float threshold
static dt_aligned_pixel_t XYZ
static dt_aligned_pixel_t Lab
const dt_colormatrix_t dt_aligned_pixel_t out
dt_XYZ_to_Lab(XYZ, Lab)
gboolean dt_image_is_monochrome(const dt_image_t *img)
void dt_control_log(const char *msg,...)
Definition control.c:824
void dt_iop_params_t
Definition dev_history.h:43
gboolean dt_dev_pixelpipe_has_preview_output(const dt_develop_t *dev, const dt_dev_pixelpipe_t *pipe, const dt_iop_roi_t *roi)
Definition develop.c:402
static void weight(const float *c1, const float *c2, const float sharpen, dt_aligned_pixel_t weight)
Definition eaw.c:29
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)
Definition imagebuf.h:91
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)
Definition imageop.c:1893
float dt_dev_get_module_scale(const dt_dev_pixelpipe_t *const pipe, const dt_iop_roi_t *const roi_in)
Definition imageop.c:134
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.
@ IOP_FLAGS_INCLUDE_IN_STYLES
Definition imageop.h:185
@ IOP_FLAGS_DEPRECATED
Definition imageop.h:187
@ IOP_FLAGS_SUPPORTS_BLENDING
Definition imageop.h:186
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.
@ IOP_GROUP_REPAIR
Definition imageop.h:159
GtkWidget * dt_bauhaus_slider_from_params(dt_iop_module_t *self, const char *param)
GtkWidget * dt_bauhaus_combobox_from_params(dt_iop_module_t *self, const char *param)
#define IOP_GUI_FREE
Definition imageop_gui.h:96
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...
Definition imageop_gui.h:81
#define IOP_GUI_ALLOC(module)
Definition imageop_gui.h:93
void *const ovoid
GtkWidget * dt_ui_label_new(const gchar *str)
Definition label.c:125
#define w2
Definition lmmse.c:60
#define w1
Definition lmmse.c:59
@ DT_DEBUG_OPENCL
Definition logging.h:57
void dt_print(dt_debug_thread_t thread, const char *msg,...) __attribute__((format(printf
Print to stdout when thread is enabled, prefixed with seconds since startup.
float *const restrict const size_t k
#define IS_NULL_PTR(p)
C is way too permissive with !=, == and if(var) checks, which can mean too many things depending on w...
Definition macros.h:96
#define CLAMPS(A, L, H)
Definition math.h:78
#define M_PI
Definition math.h:47
#define dt_free_align(ptr)
Release memory from dt_alloc_align() and set ptr to NULL.
Definition mem_alloc.h:214
static void * dt_calloc_align(size_t size)
dt_alloc_align() followed by a zero fill.
Definition mem_alloc.h:225
#define dt_free(ptr)
g_free() ptr and set it to NULL, skipping both if it is already NULL.
Definition mem_alloc.h:171
uint32_t width
Definition mipmap_cache.c:0
uint32_t height
Definition mipmap_cache.c:1
float iscale
Definition mipmap_cache.c:2
#define DT_MODULE_INTROSPECTION(MODVER, PARAMSTYPE)
DT_MODULE() for a module whose params struct is introspected.
int dt_opencl_local_buffer_opt(const int devid, const int kernel, dt_opencl_local_buffer_t *factors)
Definition opencl.c:3713
int dt_opencl_enqueue_kernel_2d(const int dev, const int kernel, const size_t *sizes)
Definition opencl.c:2554
void * dt_opencl_alloc_device_buffer(const int devid, const size_t size)
Definition opencl.c:2970
int dt_opencl_create_kernel(const int prog, const char *name)
Definition opencl.c:2448
int dt_opencl_read_buffer_from_device(const int devid, void *host, void *device, const size_t offset, const size_t size, const int blocking)
Definition opencl.c:2727
void dt_opencl_free_kernel(const int kernel)
Definition opencl.c:2491
int dt_opencl_set_kernel_arg(const int dev, const int kernel, const int num, const size_t size, const void *arg)
Definition opencl.c:2545
int dt_opencl_enqueue_kernel_2d_with_local(const int dev, const int kernel, const size_t *sizes, const size_t *local)
Definition opencl.c:2560
int dt_opencl_enqueue_copy_buffer_to_buffer(const int devid, cl_mem src_buffer, cl_mem dst_buffer, size_t srcoffset, size_t dstoffset, size_t size)
Definition opencl.c:2714
void dt_opencl_release_mem_object(cl_mem mem)
Definition opencl.c:2805
int dt_opencl_avoid_atomics(const int devid)
Definition opencl.c:219
#define ROUNDUP(a, n)
Definition opencl.h:82
#define ROUNDUPDHT(a, b)
Definition opencl.h:86
#define ROUNDUPDWD(a, b)
Definition opencl.h:85
#define __OMP_PARALLEL_FOR__(...)
Definition openmp.h:95
#define dt_pixelpipe_cache_alloc_align_cache(size, id)
#define dt_pixelpipe_cache_free_align(mem)
DT_ALIGNED_PIXEL float dt_aligned_pixel_t[4]
Definition simd.h:53
float dt_aligned_pixel_simd_t __attribute__((vector_size(16), aligned(16)))
Apply one channel's tone curve to each of the three colour channels, or pass the channel through unto...
Definition simd.h:55
unsigned __int64 uint64_t
Definition strptime.c:75
dt_iop_buffer_dsc_t dsc_in
struct dt_iop_module_t *void * data
int32_t gui_attached
Definition develop.h:167
dt_image_t image_storage
Definition develop.h:225
unsigned int channels
Definition format.h:83
dt_iop_colorreconstruct_global_data_t * global
dt_iop_colorreconstruct_Lab_t * buf
dt_iop_colorreconstruct_precedence_t precedence
dt_iop_colorreconstruct_bilateral_frozen_t * can
dt_iop_colorreconstruct_precedence_t precedence
dt_iop_colorreconstruct_precedence_t precedence
GtkWidget * widget
Definition imageop_gui.h:47
dt_iop_global_data_t * data
Definition imageop.h:238
int32_t hide_enable_button
Definition imageop.h:270
struct dt_iop_module_gui_t * gui
Definition imageop.h:346
struct dt_develop_t * dev
Definition imageop.h:311
dt_iop_global_data_t * global_data
Definition imageop.h:337
dt_iop_params_t * params
Definition imageop.h:333
Region of interest passed through the pixelpipe.
Definition format.h:49
double scale
Definition format.h:51
int width
Definition format.h:50
int height
Definition format.h:50
#define __DT_CLONE_TARGETS__
#define MIN(a, b)
Definition thinplate.c:32
#define MAX(a, b)
Definition thinplate.c:29
Telling the user something happened.
#define DT_GUI_BOX_SPACING