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 "common/darktable.h"
42#include "config.h"
43#endif
44#include "bauhaus/bauhaus.h"
46#include "common/debug.h"
47#include "common/imagebuf.h"
48#include "common/opencl.h"
49#include "control/control.h"
50#include "develop/develop.h"
51#include "develop/imageop.h"
52#include "develop/imageop_gui.h"
53#include "develop/tiling.h"
54
55#include "gui/gtk.h"
56#include "gui/presets.h"
57#include "iop/iop_api.h"
58
59#include <assert.h>
60#include <gtk/gtk.h>
61#include <inttypes.h>
62#include <math.h>
63#include <stdlib.h>
64#include <string.h>
65
66#define DT_COLORRECONSTRUCT_BILATERAL_MAX_RES_S 500
67#define DT_COLORRECONSTRUCT_BILATERAL_MAX_RES_R 100
68#define DT_COLORRECONSTRUCT_SPATIAL_APPROX 100.0f
69
71
73{
74 COLORRECONSTRUCT_PRECEDENCE_NONE, // $DESCRIPTION: "none" same weighting factor for all pixels
75 COLORRECONSTRUCT_PRECEDENCE_CHROMA, // $DESCRIPTION: "saturated colors" use chromaticy as weighting factor -> prefers saturated colors
76 COLORRECONSTRUCT_PRECEDENCE_HUE // $DESCRIPTION: "hue" use a specific hue as weighting factor
78
85
93
95{
96 float threshold; // $MIN: 50.0 $MAX: 150.0 $DEFAULT: 100.0
97 float spatial; // $MIN: 0.0 $MAX: 1000.0 $DEFAULT: 400.0 $DESCRIPTION: "spatial extent"
98 float range; // $MIN: 0.0 $MAX: 50.0 $DEFAULT: 10.0 $DESCRIPTION: "range extent"
99 float hue; // $MIN: 0.0 $MAX: 1.0 $DEFAULT: 0.66
100 dt_iop_colorreconstruct_precedence_t precedence; // $DEFAULT: 0 COLORRECONSTRUCT_PRECEDENCE_NONE
102
110
119
130
139
147
148
149const char *name()
150{
151 return _("color reconstruction");
152}
153
154const char **description(struct dt_iop_module_t *self)
155{
156 return dt_iop_set_description(self, _("recover clipped highlights by propagating surrounding colors"),
157 _("corrective"),
158 _("linear or non-linear, Lab, display-referred"),
159 _("non-linear, Lab"),
160 _("non-linear, Lab, display-referred"));
161}
162
163int flags()
164{
165 // we do not allow tiling. reason: this module needs to see the full surrounding of highlights.
166 // if we would split into tiles, each tile would result in different color corrections
168}
169
171{
172 return IOP_GROUP_REPAIR;
173}
174
176{
177 return IOP_CS_LAB;
178}
179
180int legacy_params(dt_iop_module_t *self, const void *const old_params, const int old_version,
181 void *new_params, const int new_version)
182{
183 if(old_version == 1 && new_version == 3)
184 {
185 const dt_iop_colorreconstruct_params1_t *old = old_params;
186 dt_iop_colorreconstruct_params_t *new = new_params;
187 new->threshold = old->threshold;
188 new->spatial = old->spatial;
189 new->range = old->range;
190 new->precedence = COLORRECONSTRUCT_PRECEDENCE_NONE;
191 new->hue = 0.66f;
192 return 0;
193 }
194 else if(old_version == 2 && new_version == 3)
195 {
196 const dt_iop_colorreconstruct_params2_t *old = old_params;
197 dt_iop_colorreconstruct_params_t *new = new_params;
198 new->threshold = old->threshold;
199 new->spatial = old->spatial;
200 new->range = old->range;
201 new->precedence = old->precedence;
202 new->hue = 0.66f;
203 return 0;
204 }
205 return 1;
206}
207
216
217
218static inline float hue_conversion(const float HSL_Hue)
219{
220 dt_aligned_pixel_t rgb = { 0 };
221 dt_aligned_pixel_t XYZ = { 0 };
222 dt_aligned_pixel_t Lab = { 0 };
223
224 hsl2rgb(rgb, HSL_Hue, 1.0f, 0.5f);
225
226 XYZ[0] = (rgb[0] * 0.4360747f) + (rgb[1] * 0.3850649f) + (rgb[2] * 0.1430804f);
227 XYZ[1] = (rgb[0] * 0.2225045f) + (rgb[1] * 0.7168786f) + (rgb[2] * 0.0606169f);
228 XYZ[2] = (rgb[0] * 0.0139322f) + (rgb[1] * 0.0971045f) + (rgb[2] * 0.7141733f);
229
231
232 // Hue from LCH color space in [-pi, +pi] interval
233 float LCH_hue = atan2f(Lab[2], Lab[1]);
234
235 return LCH_hue;
236}
237
238
239static inline void image_to_grid(const dt_iop_colorreconstruct_bilateral_t *const b, const float i, const float j, const float L, float *x,
240 float *y, float *z)
241{
242 *x = CLAMPS(i / b->sigma_s, 0, b->size_x - 1);
243 *y = CLAMPS(j / b->sigma_s, 0, b->size_y - 1);
244 *z = CLAMPS(L / b->sigma_r, 0, b->size_z - 1);
245}
246
247static inline void grid_rescale(const dt_iop_colorreconstruct_bilateral_t *const b, const int i, const int j, const dt_iop_roi_t *roi,
248 const float scale, float *px, float *py)
249{
250 *px = (roi->x + i) * scale - b->x;
251 *py = (roi->y + j) * scale - b->y;
252}
253
254static inline __attribute__((always_inline)) void dt_iop_colorreconstruct_bilateral_dump(dt_iop_colorreconstruct_bilateral_frozen_t *bf)
255{
256 if(IS_NULL_PTR(bf)) return;
258 dt_free(bf);
259}
260
261static inline __attribute__((always_inline)) void dt_iop_colorreconstruct_bilateral_free(dt_iop_colorreconstruct_bilateral_t *b)
262{
263 if(IS_NULL_PTR(b)) return;
265 dt_free(b);
266}
267
269 const float iscale, // overall scale of input image
270 const float sigma_s, // spatial sigma (blur pixel coords)
271 const float sigma_r) // range sigma (blur luma values)
272{
274 if(IS_NULL_PTR(b))
275 {
276 fprintf(stderr, "[color reconstruction] not able to allocate buffer (a)\n");
277 return NULL;
278 }
279 float _x = roundf(roi->width / sigma_s);
280 float _y = roundf(roi->height / sigma_s);
281 float _z = roundf(100.0f / sigma_r);
282 b->size_x = CLAMPS((int)_x, 4, DT_COLORRECONSTRUCT_BILATERAL_MAX_RES_S) + 1;
283 b->size_y = CLAMPS((int)_y, 4, DT_COLORRECONSTRUCT_BILATERAL_MAX_RES_S) + 1;
284 b->size_z = CLAMPS((int)_z, 4, DT_COLORRECONSTRUCT_BILATERAL_MAX_RES_R) + 1;
285 b->width = roi->width;
286 b->height = roi->height;
287 b->x = roi->x;
288 b->y = roi->y;
289 b->scale = iscale / roi->scale;
290 b->sigma_s = MAX(roi->height / (b->size_y - 1.0f), roi->width / (b->size_x - 1.0f));
291 b->sigma_r = 100.0f / (b->size_z - 1.0f);
293 sizeof(dt_iop_colorreconstruct_Lab_t) * b->size_x * b->size_y * b->size_z,
294 0);
295 if(IS_NULL_PTR(b->buf))
296 {
297 fprintf(stderr, "[color reconstruction] not able to allocate buffer (b)\n");
298 dt_iop_colorreconstruct_bilateral_free(b);
299 return NULL;
300 }
301
302 memset(b->buf, 0, sizeof(dt_iop_colorreconstruct_Lab_t) * b->size_x * b->size_y * b->size_z);
303#if 0
304 fprintf(stderr, "[bilateral] created grid [%d %d %d]"
305 " with sigma (%f %f) (%f %f)\n", b->size_x, b->size_y, b->size_z,
306 b->sigma_s, sigma_s, b->sigma_r, sigma_r);
307#endif
308 return b;
309}
310
312{
313 if(IS_NULL_PTR(b)) return NULL;
314
316 if(IS_NULL_PTR(bf))
317 {
318 fprintf(stderr, "[color reconstruction] not able to allocate buffer (c)\n");
319 return NULL;
320 }
321
322 bf->size_x = b->size_x;
323 bf->size_y = b->size_y;
324 bf->size_z = b->size_z;
325 bf->width = b->width;
326 bf->height = b->height;
327 bf->x = b->x;
328 bf->y = b->y;
329 bf->scale = b->scale;
330 bf->sigma_s = b->sigma_s;
331 bf->sigma_r = b->sigma_r;
333 sizeof(dt_iop_colorreconstruct_Lab_t) * b->size_x * b->size_y * b->size_z,
334 0);
335 if(bf->buf && b->buf)
336 {
337 memcpy(bf->buf, b->buf, sizeof(dt_iop_colorreconstruct_Lab_t) * b->size_x * b->size_y * b->size_z);
338 }
339 else
340 {
341 fprintf(stderr, "[color reconstruction] not able to allocate buffer (d)\n");
342 dt_iop_colorreconstruct_bilateral_dump(bf);
343 return NULL;
344 }
345
346 return bf;
347}
348
349
352 dt_iop_colorreconstruct_precedence_t precedence, const float *params)
353{
354 if(IS_NULL_PTR(b)) return;
355
356 // splat into downsampled grid
358 for(int j = 0; j < b->height; j++)
359 {
360 size_t index = (size_t)4 * j * b->width;
361 for(int i = 0; i < b->width; i++, index += 4)
362 {
363 float x, y, z, weight, m;
364 const float Lin = in[index];
365 const float ain = in[index + 1];
366 const float bin = in[index + 2];
367 // we deliberately ignore pixels above threshold
368 if (Lin > threshold) continue;
369
370 switch(precedence)
371 {
373 weight = sqrtf(ain * ain + bin * bin);
374 break;
375
377 m = atan2f(bin, ain) - params[0];
378 // readjust m into [-pi, +pi] interval
379 m = m > M_PI ? m - 2*M_PI : (m < -M_PI ? m + 2*M_PI : m);
380 weight = expf(-m*m/params[1]);
381 break;
382
384 default:
385 weight = 1.0f;
386 break;
387 }
388
389 image_to_grid(b, i, j, Lin, &x, &y, &z);
390
391 // closest integer splatting:
392 const int xi = CLAMPS((int)round(x), 0, b->size_x - 1);
393 const int yi = CLAMPS((int)round(y), 0, b->size_y - 1);
394 const int zi = CLAMPS((int)round(z), 0, b->size_z - 1);
395 const size_t grid_index = xi + b->size_x * (yi + b->size_y * zi);
396
397#ifdef _OPENMP
398#pragma omp atomic
399#endif
400 b->buf[grid_index].L += Lin * weight;
401
402#ifdef _OPENMP
403#pragma omp atomic
404#endif
405 b->buf[grid_index].a += ain * weight;
406
407#ifdef _OPENMP
408#pragma omp atomic
409#endif
410 b->buf[grid_index].b += bin * weight;
411
412#ifdef _OPENMP
413#pragma omp atomic
414#endif
415 b->buf[grid_index].weight += weight;
416 }
417 }
418}
419
420
422static void blur_line(dt_iop_colorreconstruct_Lab_t *buf, const int offset1, const int offset2, const int offset3, const int size1,
423 const int size2, const int size3)
424{
425 if(IS_NULL_PTR(buf)) return;
426
427 const float w0 = 6.f / 16.f;
428 const float w1 = 4.f / 16.f;
429 const float w2 = 1.f / 16.f;
431 for(int k = 0; k < size1; k++)
432 {
433 size_t index = (size_t)k * offset1;
434 for(int j = 0; j < size2; j++)
435 {
436 dt_iop_colorreconstruct_Lab_t tmp1 = buf[index];
437 buf[index].L = buf[index].L * w0 + w1 * buf[index + offset3].L + w2 * buf[index + 2 * offset3].L;
438 buf[index].a = buf[index].a * w0 + w1 * buf[index + offset3].a + w2 * buf[index + 2 * offset3].a;
439 buf[index].b = buf[index].b * w0 + w1 * buf[index + offset3].b + w2 * buf[index + 2 * offset3].b;
440 buf[index].weight = buf[index].weight * w0 + w1 * buf[index + offset3].weight + w2 * buf[index + 2 * offset3].weight;
441 index += offset3;
442 dt_iop_colorreconstruct_Lab_t tmp2 = buf[index];
443 buf[index].L = buf[index].L * w0 + w1 * (buf[index + offset3].L + tmp1.L) + w2 * buf[index + 2 * offset3].L;
444 buf[index].a = buf[index].a * w0 + w1 * (buf[index + offset3].a + tmp1.a) + w2 * buf[index + 2 * offset3].a;
445 buf[index].b = buf[index].b * w0 + w1 * (buf[index + offset3].b + tmp1.b) + w2 * buf[index + 2 * offset3].b;
446 buf[index].weight = buf[index].weight * w0 + w1 * (buf[index + offset3].weight + tmp1.weight) + w2 * buf[index + 2 * offset3].weight;
447 index += offset3;
448 for(int i = 2; i < size3 - 2; i++)
449 {
450 const dt_iop_colorreconstruct_Lab_t tmp3 = buf[index];
451 buf[index].L = buf[index].L * w0 + w1 * (buf[index + offset3].L + tmp2.L)
452 + w2 * (buf[index + 2 * offset3].L + tmp1.L);
453 buf[index].a = buf[index].a * w0 + w1 * (buf[index + offset3].a + tmp2.a)
454 + w2 * (buf[index + 2 * offset3].a + tmp1.a);
455 buf[index].b = buf[index].b * w0 + w1 * (buf[index + offset3].b + tmp2.b)
456 + w2 * (buf[index + 2 * offset3].b + tmp1.b);
457 buf[index].weight = buf[index].weight * w0 + w1 * (buf[index + offset3].weight + tmp2.weight)
458 + w2 * (buf[index + 2 * offset3].weight + tmp1.weight);
459
460 index += offset3;
461 tmp1 = tmp2;
462 tmp2 = tmp3;
463 }
464 const dt_iop_colorreconstruct_Lab_t tmp3 = buf[index];
465 buf[index].L = buf[index].L * w0 + w1 * (buf[index + offset3].L + tmp2.L) + w2 * tmp1.L;
466 buf[index].a = buf[index].a * w0 + w1 * (buf[index + offset3].a + tmp2.a) + w2 * tmp1.a;
467 buf[index].b = buf[index].b * w0 + w1 * (buf[index + offset3].b + tmp2.b) + w2 * tmp1.b;
468 buf[index].weight = buf[index].weight * w0 + w1 * (buf[index + offset3].weight + tmp2.weight) + w2 * tmp1.weight;
469 index += offset3;
470 buf[index].L = buf[index].L * w0 + w1 * tmp3.L + w2 * tmp2.L;
471 buf[index].a = buf[index].a * w0 + w1 * tmp3.a + w2 * tmp2.a;
472 buf[index].b = buf[index].b * w0 + w1 * tmp3.b + w2 * tmp2.b;
473 buf[index].weight = buf[index].weight * w0 + w1 * tmp3.weight + w2 * tmp2.weight;
474 index += offset3;
475 index += offset2 - offset3 * size3;
476 }
477 }
478}
479
480
481static inline __attribute__((always_inline)) void dt_iop_colorreconstruct_bilateral_blur(dt_iop_colorreconstruct_bilateral_t *b)
482{
483 if(IS_NULL_PTR(b)) return;
484
485 // gaussian up to 3 sigma
486 blur_line(b->buf, b->size_x * b->size_y, b->size_x, 1, b->size_z, b->size_y, b->size_x);
487 // gaussian up to 3 sigma
488 blur_line(b->buf, b->size_x * b->size_y, 1, b->size_x, b->size_z, b->size_x, b->size_y);
489 // gaussian up to 3 sigma
490 blur_line(b->buf, 1, b->size_x, b->size_x * b->size_y, b->size_x, b->size_y, b->size_z);
491}
492
495 const float *const in, float *const out,
496 const float threshold, const dt_iop_roi_t *const roi,
497 const float iscale)
498{
499 if(IS_NULL_PTR(b)) return;
500
501 const float rescale = iscale / (roi->scale * b->scale);
502 const int ox = 1;
503 const int oy = b->size_x;
504 const int oz = b->size_y * b->size_x;
506 for(int j = 0; j < roi->height; j++)
507 {
508 size_t index = (size_t)4 * j * roi->width;
509 for(int i = 0; i < roi->width; i++, index += 4)
510 {
511 float x, y, z;
512 float px, py;
513 const float Lin = out[index + 0] = in[index + 0];
514 const float ain = out[index + 1] = in[index + 1];
515 const float bin = out[index + 2] = in[index + 2];
516 out[index + 3] = in[index + 3];
517 const float blend = CLAMPS(20.0f / threshold * Lin - 19.0f, 0.0f, 1.0f);
518 if (blend == 0.0f) continue;
519 grid_rescale(b, i, j, roi, rescale, &px, &py);
520 image_to_grid(b, px, py, Lin, &x, &y, &z);
521 // trilinear lookup:
522 const int xi = MIN((int)x, b->size_x - 2);
523 const int yi = MIN((int)y, b->size_y - 2);
524 const int zi = MIN((int)z, b->size_z - 2);
525 const float xf = x - xi;
526 const float yf = y - yi;
527 const float zf = z - zi;
528 const size_t gi = xi + b->size_x * (yi + b->size_y * zi);
529
530 const float Lout = b->buf[gi].L * (1.0f - xf) * (1.0f - yf) * (1.0f - zf)
531 + b->buf[gi + ox].L * (xf) * (1.0f - yf) * (1.0f - zf)
532 + b->buf[gi + oy].L * (1.0f - xf) * (yf) * (1.0f - zf)
533 + b->buf[gi + ox + oy].L * (xf) * (yf) * (1.0f - zf)
534 + b->buf[gi + oz].L * (1.0f - xf) * (1.0f - yf) * (zf)
535 + b->buf[gi + ox + oz].L * (xf) * (1.0f - yf) * (zf)
536 + b->buf[gi + oy + oz].L * (1.0f - xf) * (yf) * (zf)
537 + b->buf[gi + ox + oy + oz].L * (xf) * (yf) * (zf);
538
539 const float aout = b->buf[gi].a * (1.0f - xf) * (1.0f - yf) * (1.0f - zf)
540 + b->buf[gi + ox].a * (xf) * (1.0f - yf) * (1.0f - zf)
541 + b->buf[gi + oy].a * (1.0f - xf) * (yf) * (1.0f - zf)
542 + b->buf[gi + ox + oy].a * (xf) * (yf) * (1.0f - zf)
543 + b->buf[gi + oz].a * (1.0f - xf) * (1.0f - yf) * (zf)
544 + b->buf[gi + ox + oz].a * (xf) * (1.0f - yf) * (zf)
545 + b->buf[gi + oy + oz].a * (1.0f - xf) * (yf) * (zf)
546 + b->buf[gi + ox + oy + oz].a * (xf) * (yf) * (zf);
547
548
549 const float bout = b->buf[gi].b * (1.0f - xf) * (1.0f - yf) * (1.0f - zf)
550 + b->buf[gi + ox].b * (xf) * (1.0f - yf) * (1.0f - zf)
551 + b->buf[gi + oy].b * (1.0f - xf) * (yf) * (1.0f - zf)
552 + b->buf[gi + ox + oy].b * (xf) * (yf) * (1.0f - zf)
553 + b->buf[gi + oz].b * (1.0f - xf) * (1.0f - yf) * (zf)
554 + b->buf[gi + ox + oz].b * (xf) * (1.0f - yf) * (zf)
555 + b->buf[gi + oy + oz].b * (1.0f - xf) * (yf) * (zf)
556 + b->buf[gi + ox + oy + oz].b * (xf) * (yf) * (zf);
557
558 const float weight = b->buf[gi].weight * (1.0f - xf) * (1.0f - yf) * (1.0f - zf)
559 + b->buf[gi + ox].weight * (xf) * (1.0f - yf) * (1.0f - zf)
560 + b->buf[gi + oy].weight * (1.0f - xf) * (yf) * (1.0f - zf)
561 + b->buf[gi + ox + oy].weight * (xf) * (yf) * (1.0f - zf)
562 + b->buf[gi + oz].weight * (1.0f - xf) * (1.0f - yf) * (zf)
563 + b->buf[gi + ox + oz].weight * (xf) * (1.0f - yf) * (zf)
564 + b->buf[gi + oy + oz].weight * (1.0f - xf) * (yf) * (zf)
565 + b->buf[gi + ox + oy + oz].weight * (xf) * (yf) * (zf);
566
567 const float lout = fmax(Lout, 0.01f);
568 out[index + 1] = (weight > 0.0f) ? ain * (1.0f - blend) + aout * Lin/lout * blend : ain;
569 out[index + 2] = (weight > 0.0f) ? bin * (1.0f - blend) + bout * Lin/lout * blend : bin;
570 }
571 }
572}
573
574
575int process(struct dt_iop_module_t *self, const dt_dev_pixelpipe_t *pipe, const dt_dev_pixelpipe_iop_t *piece, const void *const ivoid,
576 void *const ovoid)
577{
578 const dt_iop_roi_t *const roi_in = &piece->roi_in;
579 const dt_iop_roi_t *const roi_out = &piece->roi_out;
582 float *in = (float *)ivoid;
583 float *out = (float *)ovoid;
584
585 const float scale = dt_dev_get_module_scale(pipe, roi_in);
586 const float sigma_r = fmax(data->range, 0.1f);
587 const float sigma_s = fmax(data->spatial, 1.0f) / scale;
588 const float hue = hue_conversion(data->hue); // convert to LCH hue which better fits to Lab colorspace
589
590 const dt_aligned_pixel_t params = { hue, M_PI*M_PI/8, 0.0f, 0.0f };
591
593
595 if(IS_NULL_PTR(b)) goto error;
597 dt_iop_colorreconstruct_bilateral_blur(b);
598
599 dt_iop_colorreconstruct_bilateral_slice(b, in, out, data->threshold, roi_in, pipe->iscale);
600
601 // here is where we generate the canned bilateral grid of the preview pipe for later use
602 int err = 0;
603 if(self->dev->gui_attached && !IS_NULL_PTR(g) && dt_dev_pixelpipe_has_preview_output(self->dev, pipe, roi_out))
604 {
605 uint64_t hash = piece->global_hash;
607 dt_iop_colorreconstruct_bilateral_dump(g->can);
609 g->hash = hash;
610 if(!g->can) err = 1;
612 }
613
614 dt_iop_colorreconstruct_bilateral_free(b);
615 if(err) return 1;
616 return 0;
617
618error:
619 dt_control_log(_("module `color reconstruction' failed"));
620 dt_iop_colorreconstruct_bilateral_free(b);
621 dt_iop_image_copy_by_size(ovoid, ivoid, roi_out->width, roi_out->height, piece->dsc_in.channels);
622 return 1;
623}
624
625#ifdef HAVE_OPENCL
638
640{
641 if(IS_NULL_PTR(b)) return;
642 // free device mem
643 dt_opencl_release_mem_object(b->dev_grid);
644 dt_opencl_release_mem_object(b->dev_grid_tmp);
645 dt_free(b);
646}
647
649 const int devid,
651 const dt_iop_roi_t *roi, // dimensions of input image
652 const float iscale, // overall scale of input image
653 const float sigma_s, // spatial sigma (blur pixel coords)
654 const float sigma_r) // range sigma (blur luma values)
655{
656 int blocksizex, blocksizey;
657
659 = (dt_opencl_local_buffer_t){ .xoffset = 0, .xfactor = 1, .yoffset = 0, .yfactor = 1,
660 .cellsize = 4 * sizeof(float) + sizeof(int), .overhead = 0,
661 .sizex = 1 << 6, .sizey = 1 << 6 };
662
664 {
665 blocksizex = locopt.sizex;
666 blocksizey = locopt.sizey;
667 }
668 else
669 blocksizex = blocksizey = 1;
670
671 if(blocksizex * blocksizey < 16 * 16)
672 {
674 "[opencl_colorreconstruction] device %d does not offer sufficient resources to run bilateral grid\n",
675 devid);
676 return NULL;
677 }
678
680 if(IS_NULL_PTR(b))
681 {
682 dt_print(DT_DEBUG_OPENCL, "[opencl_colorreconstruction] not able to allocate host buffer (a)\n");
683 return NULL;
684 }
685
686 float _x = roundf(roi->width / sigma_s);
687 float _y = roundf(roi->height / sigma_s);
688 float _z = roundf(100.0f / sigma_r);
689 b->size_x = CLAMPS((int)_x, 4, DT_COLORRECONSTRUCT_BILATERAL_MAX_RES_S) + 1;
690 b->size_y = CLAMPS((int)_y, 4, DT_COLORRECONSTRUCT_BILATERAL_MAX_RES_S) + 1;
691 b->size_z = CLAMPS((int)_z, 4, DT_COLORRECONSTRUCT_BILATERAL_MAX_RES_R) + 1;
692 b->width = roi->width;
693 b->height = roi->height;
694 b->x = roi->x;
695 b->y = roi->y;
696 b->scale = iscale / roi->scale;
697 b->blocksizex = blocksizex;
698 b->blocksizey = blocksizey;
699 b->sigma_s = MAX(roi->height / (b->size_y - 1.0f), roi->width / (b->size_x - 1.0f));
700 b->sigma_r = 100.0f / (b->size_z - 1.0f);
701 b->devid = devid;
702 b->global = global;
703 b->dev_grid = NULL;
704 b->dev_grid_tmp = NULL;
705
706 // alloc grid buffer:
707 b->dev_grid
708 = dt_opencl_alloc_device_buffer(b->devid, sizeof(float) * 4 * b->size_x * b->size_y * b->size_z);
709 if(!b->dev_grid)
710 {
711 dt_print(DT_DEBUG_OPENCL, "[opencl_colorreconstruction] not able to allocate device buffer (b)\n");
713 return NULL;
714 }
715
716 // alloc temporary grid buffer
717 b->dev_grid_tmp
718 = dt_opencl_alloc_device_buffer(b->devid, sizeof(float) * 4 * b->size_x * b->size_y * b->size_z);
719 if(!b->dev_grid_tmp)
720 {
721 dt_print(DT_DEBUG_OPENCL, "[opencl_colorreconstruction] not able to allocate device buffer (c)\n");
723 return NULL;
724 }
725
726 // zero out grid
727 int wd = 4 * b->size_x, ht = b->size_y * b->size_z;
728 size_t sizes[] = { ROUNDUPDWD(wd, b->devid), ROUNDUPDHT(ht, b->devid), 1 };
729 dt_opencl_set_kernel_arg(b->devid, b->global->kernel_colorreconstruct_zero, 0, sizeof(cl_mem), (void *)&b->dev_grid);
730 dt_opencl_set_kernel_arg(b->devid, b->global->kernel_colorreconstruct_zero, 1, sizeof(int), (void *)&wd);
731 dt_opencl_set_kernel_arg(b->devid, b->global->kernel_colorreconstruct_zero, 2, sizeof(int), (void *)&ht);
732 cl_int err = -666;
733 err = dt_opencl_enqueue_kernel_2d(b->devid, b->global->kernel_colorreconstruct_zero, sizes);
734 if(err != CL_SUCCESS)
735 {
736 dt_print(DT_DEBUG_OPENCL, "[opencl_colorreconstruction] error running kernel colorreconstruct_zero: %d\n", err);
738 return NULL;
739 }
740
741#if 0
742 fprintf(stderr, "[bilateral] created grid [%d %d %d]"
743 " with sigma (%f %f) (%f %f)\n", b->size_x, b->size_y, b->size_z,
744 b->sigma_s, sigma_s, b->sigma_r, sigma_r);
745#endif
746 return b;
747}
748
750{
751 if(IS_NULL_PTR(b)) return NULL;
752
754 if(IS_NULL_PTR(bf))
755 {
756 dt_print(DT_DEBUG_OPENCL, "[opencl_colorreconstruction] not able to allocate host buffer (d)\n");
757 return NULL;
758 }
759
760 bf->size_x = b->size_x;
761 bf->size_y = b->size_y;
762 bf->size_z = b->size_z;
763 bf->width = b->width;
764 bf->height = b->height;
765 bf->x = b->x;
766 bf->y = b->y;
767 bf->scale = b->scale;
768 bf->sigma_s = b->sigma_s;
769 bf->sigma_r = b->sigma_r;
771 sizeof(dt_iop_colorreconstruct_Lab_t) * b->size_x * b->size_y * b->size_z,
772 0);
773 if(bf->buf && b->dev_grid)
774 {
775 // read bilateral grid from device memory to host buffer (blocking)
776 cl_int err = dt_opencl_read_buffer_from_device(b->devid, bf->buf, b->dev_grid, 0,
777 sizeof(dt_iop_colorreconstruct_Lab_t) * b->size_x * b->size_y * b->size_z, CL_TRUE);
778 if(err != CL_SUCCESS)
779 {
781 "[opencl_colorreconstruction] can not read bilateral grid from device %d\n", b->devid);
782 dt_iop_colorreconstruct_bilateral_dump(bf);
783 return NULL;
784 }
785 }
786 else
787 {
788 dt_print(DT_DEBUG_OPENCL, "[opencl_colorreconstruction] not able to allocate host buffer (e)\n");
789 dt_iop_colorreconstruct_bilateral_dump(bf);
790 return NULL;
791 }
792
793 return bf;
794}
795
796
798 dt_iop_colorreconstruct_precedence_t precedence, const float *params)
799{
800 cl_int err = -666;
801 if(IS_NULL_PTR(b)) return err;
802 int pref = precedence;
803 size_t sizes[] = { ROUNDUP(b->width, b->blocksizex), ROUNDUP(b->height, b->blocksizey), 1 };
804 size_t local[] = { b->blocksizex, b->blocksizey, 1 };
805 dt_opencl_set_kernel_arg(b->devid, b->global->kernel_colorreconstruct_splat, 0, sizeof(cl_mem), (void *)&in);
806 dt_opencl_set_kernel_arg(b->devid, b->global->kernel_colorreconstruct_splat, 1, sizeof(cl_mem), (void *)&b->dev_grid);
807 dt_opencl_set_kernel_arg(b->devid, b->global->kernel_colorreconstruct_splat, 2, sizeof(int), (void *)&b->width);
808 dt_opencl_set_kernel_arg(b->devid, b->global->kernel_colorreconstruct_splat, 3, sizeof(int), (void *)&b->height);
809 dt_opencl_set_kernel_arg(b->devid, b->global->kernel_colorreconstruct_splat, 4, sizeof(int), (void *)&b->size_x);
810 dt_opencl_set_kernel_arg(b->devid, b->global->kernel_colorreconstruct_splat, 5, sizeof(int), (void *)&b->size_y);
811 dt_opencl_set_kernel_arg(b->devid, b->global->kernel_colorreconstruct_splat, 6, sizeof(int), (void *)&b->size_z);
812 dt_opencl_set_kernel_arg(b->devid, b->global->kernel_colorreconstruct_splat, 7, sizeof(float), (void *)&b->sigma_s);
813 dt_opencl_set_kernel_arg(b->devid, b->global->kernel_colorreconstruct_splat, 8, sizeof(float), (void *)&b->sigma_r);
814 dt_opencl_set_kernel_arg(b->devid, b->global->kernel_colorreconstruct_splat, 9, sizeof(float), (void *)&threshold);
815 dt_opencl_set_kernel_arg(b->devid, b->global->kernel_colorreconstruct_splat, 10, sizeof(int), (void *)&pref);
816 dt_opencl_set_kernel_arg(b->devid, b->global->kernel_colorreconstruct_splat, 11, 4*sizeof(float), (void *)params);
817 dt_opencl_set_kernel_arg(b->devid, b->global->kernel_colorreconstruct_splat, 12, b->blocksizex * b->blocksizey * sizeof(int),
818 NULL);
819 dt_opencl_set_kernel_arg(b->devid, b->global->kernel_colorreconstruct_splat, 13,
820 b->blocksizex * b->blocksizey * 4 * sizeof(float), NULL);
821 err = dt_opencl_enqueue_kernel_2d_with_local(b->devid, b->global->kernel_colorreconstruct_splat, sizes, local);
822 return err;
823}
824
826{
827 cl_int err = -666;
828 if(IS_NULL_PTR(b)) return err;
829 size_t sizes[3] = { 0, 0, 1 };
830
831 err = dt_opencl_enqueue_copy_buffer_to_buffer(b->devid, b->dev_grid, b->dev_grid_tmp, 0, 0,
832 b->size_x * b->size_y * b->size_z * 4 * sizeof(float));
833 if(err != CL_SUCCESS) return err;
834
835 sizes[0] = ROUNDUPDWD(b->size_z, b->devid);
836 sizes[1] = ROUNDUPDHT(b->size_y, b->devid);
837 int stride1, stride2, stride3;
838 stride1 = b->size_x * b->size_y;
839 stride2 = b->size_x;
840 stride3 = 1;
841 dt_opencl_set_kernel_arg(b->devid, b->global->kernel_colorreconstruct_blur_line, 0, sizeof(cl_mem), (void *)&b->dev_grid_tmp);
842 dt_opencl_set_kernel_arg(b->devid, b->global->kernel_colorreconstruct_blur_line, 1, sizeof(cl_mem), (void *)&b->dev_grid);
843 dt_opencl_set_kernel_arg(b->devid, b->global->kernel_colorreconstruct_blur_line, 2, sizeof(int), (void *)&stride1);
844 dt_opencl_set_kernel_arg(b->devid, b->global->kernel_colorreconstruct_blur_line, 3, sizeof(int), (void *)&stride2);
845 dt_opencl_set_kernel_arg(b->devid, b->global->kernel_colorreconstruct_blur_line, 4, sizeof(int), (void *)&stride3);
846 dt_opencl_set_kernel_arg(b->devid, b->global->kernel_colorreconstruct_blur_line, 5, sizeof(int), (void *)&b->size_z);
847 dt_opencl_set_kernel_arg(b->devid, b->global->kernel_colorreconstruct_blur_line, 6, sizeof(int), (void *)&b->size_y);
848 dt_opencl_set_kernel_arg(b->devid, b->global->kernel_colorreconstruct_blur_line, 7, sizeof(int), (void *)&b->size_x);
849 err = dt_opencl_enqueue_kernel_2d(b->devid, b->global->kernel_colorreconstruct_blur_line, sizes);
850 if(err != CL_SUCCESS) return err;
851
852 stride1 = b->size_x * b->size_y;
853 stride2 = 1;
854 stride3 = b->size_x;
855 sizes[0] = ROUNDUPDWD(b->size_z, b->devid);
856 sizes[1] = ROUNDUPDHT(b->size_x, b->devid);
857 dt_opencl_set_kernel_arg(b->devid, b->global->kernel_colorreconstruct_blur_line, 0, sizeof(cl_mem), (void *)&b->dev_grid);
858 dt_opencl_set_kernel_arg(b->devid, b->global->kernel_colorreconstruct_blur_line, 1, sizeof(cl_mem), (void *)&b->dev_grid_tmp);
859 dt_opencl_set_kernel_arg(b->devid, b->global->kernel_colorreconstruct_blur_line, 2, sizeof(int), (void *)&stride1);
860 dt_opencl_set_kernel_arg(b->devid, b->global->kernel_colorreconstruct_blur_line, 3, sizeof(int), (void *)&stride2);
861 dt_opencl_set_kernel_arg(b->devid, b->global->kernel_colorreconstruct_blur_line, 4, sizeof(int), (void *)&stride3);
862 dt_opencl_set_kernel_arg(b->devid, b->global->kernel_colorreconstruct_blur_line, 5, sizeof(int), (void *)&b->size_z);
863 dt_opencl_set_kernel_arg(b->devid, b->global->kernel_colorreconstruct_blur_line, 6, sizeof(int), (void *)&b->size_x);
864 dt_opencl_set_kernel_arg(b->devid, b->global->kernel_colorreconstruct_blur_line, 7, sizeof(int), (void *)&b->size_y);
865 err = dt_opencl_enqueue_kernel_2d(b->devid, b->global->kernel_colorreconstruct_blur_line, sizes);
866 if(err != CL_SUCCESS) return err;
867
868 stride1 = 1;
869 stride2 = b->size_x;
870 stride3 = b->size_x * b->size_y;
871 sizes[0] = ROUNDUPDWD(b->size_x, b->devid);
872 sizes[1] = ROUNDUPDHT(b->size_y, b->devid);
873 dt_opencl_set_kernel_arg(b->devid, b->global->kernel_colorreconstruct_blur_line, 0, sizeof(cl_mem),
874 (void *)&b->dev_grid_tmp);
875 dt_opencl_set_kernel_arg(b->devid, b->global->kernel_colorreconstruct_blur_line, 1, sizeof(cl_mem), (void *)&b->dev_grid);
876 dt_opencl_set_kernel_arg(b->devid, b->global->kernel_colorreconstruct_blur_line, 2, sizeof(int), (void *)&stride1);
877 dt_opencl_set_kernel_arg(b->devid, b->global->kernel_colorreconstruct_blur_line, 3, sizeof(int), (void *)&stride2);
878 dt_opencl_set_kernel_arg(b->devid, b->global->kernel_colorreconstruct_blur_line, 4, sizeof(int), (void *)&stride3);
879 dt_opencl_set_kernel_arg(b->devid, b->global->kernel_colorreconstruct_blur_line, 5, sizeof(int), (void *)&b->size_x);
880 dt_opencl_set_kernel_arg(b->devid, b->global->kernel_colorreconstruct_blur_line, 6, sizeof(int), (void *)&b->size_y);
881 dt_opencl_set_kernel_arg(b->devid, b->global->kernel_colorreconstruct_blur_line, 7, sizeof(int), (void *)&b->size_z);
882 err = dt_opencl_enqueue_kernel_2d(b->devid, b->global->kernel_colorreconstruct_blur_line, sizes);
883 return err;
884}
885
887 const float threshold, const dt_iop_roi_t *roi, const float iscale)
888{
889 cl_int err = -666;
890 if(IS_NULL_PTR(b)) return err;
891 const int bxy[2] = { b->x, b->y };
892 const int roixy[2] = { roi->x, roi->y };
893 const float rescale = iscale / (roi->scale * b->scale);
894
895 size_t sizes[] = { ROUNDUPDWD(roi->width, b->devid), ROUNDUPDHT(roi->height, b->devid), 1 };
896 dt_opencl_set_kernel_arg(b->devid, b->global->kernel_colorreconstruct_slice, 0, sizeof(cl_mem), (void *)&in);
897 dt_opencl_set_kernel_arg(b->devid, b->global->kernel_colorreconstruct_slice, 1, sizeof(cl_mem), (void *)&out);
898 dt_opencl_set_kernel_arg(b->devid, b->global->kernel_colorreconstruct_slice, 2, sizeof(cl_mem), (void *)&b->dev_grid);
899 dt_opencl_set_kernel_arg(b->devid, b->global->kernel_colorreconstruct_slice, 3, sizeof(int), (void *)&roi->width);
900 dt_opencl_set_kernel_arg(b->devid, b->global->kernel_colorreconstruct_slice, 4, sizeof(int), (void *)&roi->height);
901 dt_opencl_set_kernel_arg(b->devid, b->global->kernel_colorreconstruct_slice, 5, sizeof(int), (void *)&b->size_x);
902 dt_opencl_set_kernel_arg(b->devid, b->global->kernel_colorreconstruct_slice, 6, sizeof(int), (void *)&b->size_y);
903 dt_opencl_set_kernel_arg(b->devid, b->global->kernel_colorreconstruct_slice, 7, sizeof(int), (void *)&b->size_z);
904 dt_opencl_set_kernel_arg(b->devid, b->global->kernel_colorreconstruct_slice, 8, sizeof(float), (void *)&b->sigma_s);
905 dt_opencl_set_kernel_arg(b->devid, b->global->kernel_colorreconstruct_slice, 9, sizeof(float), (void *)&b->sigma_r);
906 dt_opencl_set_kernel_arg(b->devid, b->global->kernel_colorreconstruct_slice, 10, sizeof(float), (void *)&threshold);
907 dt_opencl_set_kernel_arg(b->devid, b->global->kernel_colorreconstruct_slice, 11, 2*sizeof(int), (void *)&bxy);
908 dt_opencl_set_kernel_arg(b->devid, b->global->kernel_colorreconstruct_slice, 12, 2*sizeof(int), (void *)&roixy);
909 dt_opencl_set_kernel_arg(b->devid, b->global->kernel_colorreconstruct_slice, 13, sizeof(float), (void *)&rescale);
910 err = dt_opencl_enqueue_kernel_2d(b->devid, b->global->kernel_colorreconstruct_slice, sizes);
911 return err;
912}
913
914int 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)
915{
916 const dt_iop_roi_t *const roi_in = &piece->roi_in;
917 const dt_iop_roi_t *const roi_out = &piece->roi_out;
921
922 const float scale = dt_dev_get_module_scale(pipe, roi_in);
923 const float sigma_r = fmax(d->range, 0.1f); // does not depend on scale
924 const float sigma_s = fmax(d->spatial, 1.0f) / scale;
925 const float hue = hue_conversion(d->hue); // convert to LCH hue which better fits to Lab colorspace
926
927 const float params[4] = { hue, M_PI*M_PI/8, 0.0f, 0.0f };
928
929 cl_int err = -666;
930
932
934 if(IS_NULL_PTR(b)) goto error;
935 err = dt_iop_colorreconstruct_bilateral_splat_cl(b, dev_in, d->threshold, d->precedence, params);
936 if(err != CL_SUCCESS) goto error;
938 if(err != CL_SUCCESS) goto error;
939
940 err = dt_iop_colorreconstruct_bilateral_slice_cl(b, dev_in, dev_out, d->threshold, roi_in, pipe->iscale);
941 if(err != CL_SUCCESS) goto error;
942
943 if(self->dev->gui_attached && g && dt_dev_pixelpipe_has_preview_output(self->dev, pipe, roi_out))
944 {
945 uint64_t hash = piece->global_hash;
947 dt_iop_colorreconstruct_bilateral_dump(g->can);
949 g->hash = hash;
951 if(!g->can)
952 {
953 err = CL_MEM_OBJECT_ALLOCATION_FAILURE;
954 goto error;
955 }
956 }
957
959 return TRUE;
960
961error:
963 dt_print(DT_DEBUG_OPENCL, "[opencl_colorreconstruction] couldn't enqueue kernel! %d\n", err);
964 return FALSE;
965}
966#endif
967
968
969static size_t dt_iop_colorreconstruct_bilateral_memory_use(const int width, // width of input image
970 const int height, // height of input image
971 const float sigma_s, // spatial sigma (blur pixel coords)
972 const float sigma_r) // range sigma (blur luma values)
973{
974 float _x = roundf(width / sigma_s);
975 float _y = roundf(height / sigma_s);
976 float _z = roundf(100.0f / sigma_r);
977 size_t size_x = CLAMPS((int)_x, 4, DT_COLORRECONSTRUCT_BILATERAL_MAX_RES_S) + 1;
978 size_t size_y = CLAMPS((int)_y, 4, DT_COLORRECONSTRUCT_BILATERAL_MAX_RES_S) + 1;
979 size_t size_z = CLAMPS((int)_z, 4, DT_COLORRECONSTRUCT_BILATERAL_MAX_RES_R) + 1;
980
981 return size_x * size_y * size_z * 4 * sizeof(float) * 2; // in fact only the OpenCL path needs a second tmp buffer
982}
983
984
985static size_t dt_iop_colorreconstruct_bilateral_singlebuffer_size(const int width, // width of input image
986 const int height, // height of input image
987 const float sigma_s, // spatial sigma (blur pixel coords)
988 const float sigma_r) // range sigma (blur luma values)
989{
990 float _x = roundf(width / sigma_s);
991 float _y = roundf(height / sigma_s);
992 float _z = roundf(100.0f / sigma_r);
993 size_t size_x = CLAMPS((int)_x, 4, DT_COLORRECONSTRUCT_BILATERAL_MAX_RES_S) + 1;
994 size_t size_y = CLAMPS((int)_y, 4, DT_COLORRECONSTRUCT_BILATERAL_MAX_RES_S) + 1;
995 size_t size_z = CLAMPS((int)_z, 4, DT_COLORRECONSTRUCT_BILATERAL_MAX_RES_R) + 1;
996
997 return size_x * size_y * size_z * 4 * sizeof(float);
998}
999
1000void 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)
1001{
1002 const dt_iop_roi_t *const roi_in = &piece->roi_in;
1004 const float scale = dt_dev_get_module_scale(pipe, roi_in);
1005 const float sigma_r = fmax(d->range, 0.1f);
1006 const float sigma_s = fmax(d->spatial, 1.0f) / scale;
1007
1008 const int width = roi_in->width;
1009 const int height = roi_in->height;
1010 const int channels = piece->dsc_in.channels;
1011
1012 const size_t basebuffer = sizeof(float) * channels * width * height;
1013
1014 tiling->factor = 2.0f + (float)dt_iop_colorreconstruct_bilateral_memory_use(width, height, sigma_s, sigma_r) / basebuffer;
1015 tiling->maxbuf
1017 tiling->overhead = 0;
1018 tiling->overlap = ceilf(4 * sigma_s);
1019 tiling->xalign = 1;
1020 tiling->yalign = 1;
1021 return;
1022}
1023
1024
1025void gui_changed(dt_iop_module_t *self, GtkWidget *w, void *previous)
1026{
1029 if(w == g->precedence)
1030 {
1031 gtk_widget_set_visible(g->hue, p->precedence == COLORRECONSTRUCT_PRECEDENCE_HUE);
1032 }
1033}
1034
1037{
1040
1041 d->threshold = p->threshold;
1042 d->spatial = p->spatial;
1043 d->range = p->range;
1044 d->precedence = p->precedence;
1045 d->hue = p->hue;
1046
1047#ifdef HAVE_OPENCL
1049#endif
1050}
1051
1058
1060{
1061 dt_free_align(piece->data);
1062 piece->data = NULL;
1063}
1064
1065void gui_update(struct dt_iop_module_t *self)
1066{
1067 const gboolean monochrome = dt_image_is_monochrome(&self->dev->image_storage);
1070
1071 self->hide_enable_button = monochrome;
1072 gtk_stack_set_visible_child_name(GTK_STACK(self->widget), !monochrome ? "default" : "monochrome");
1073
1074 gtk_widget_set_visible(g->hue, p->precedence == COLORRECONSTRUCT_PRECEDENCE_HUE);
1075
1077 dt_iop_colorreconstruct_bilateral_dump(g->can);
1078 g->can = NULL;
1079 g->hash = 0;
1081}
1082
1084{
1087 module->data = gd;
1088 const int program = 13; // colorcorrection.cl, from programs.conf
1089 gd->kernel_colorreconstruct_zero = dt_opencl_create_kernel(program, "colorreconstruction_zero");
1090 gd->kernel_colorreconstruct_splat = dt_opencl_create_kernel(program, "colorreconstruction_splat");
1091 gd->kernel_colorreconstruct_blur_line = dt_opencl_create_kernel(program, "colorreconstruction_blur_line");
1092 gd->kernel_colorreconstruct_slice = dt_opencl_create_kernel(program, "colorreconstruction_slice");
1093}
1094
1104
1105
1106void gui_init(struct dt_iop_module_t *self)
1107{
1109
1110 g->can = NULL;
1111 g->hash = 0;
1112
1113 GtkWidget *box_enabled = self->widget = gtk_box_new(GTK_ORIENTATION_VERTICAL, DT_GUI_BOX_SPACING);
1114
1115 g->threshold = dt_bauhaus_slider_from_params(self, N_("threshold"));
1116 g->spatial = dt_bauhaus_slider_from_params(self, N_("spatial"));
1117 g->range = dt_bauhaus_slider_from_params(self, N_("range"));
1118 g->precedence = dt_bauhaus_combobox_from_params(self, N_("precedence"));
1119 g->hue = dt_bauhaus_slider_from_params(self, N_("hue"));
1120 dt_bauhaus_slider_set_factor(g->hue, 360.0f);
1121 dt_bauhaus_slider_set_format(g->hue, "\302\260");
1123 dt_bauhaus_slider_set_stop(g->hue, 0.0f, 1.0f, 0.0f, 0.0f);
1124 dt_bauhaus_slider_set_stop(g->hue, 0.166f, 1.0f, 1.0f, 0.0f);
1125 dt_bauhaus_slider_set_stop(g->hue, 0.322f, 0.0f, 1.0f, 0.0f);
1126 dt_bauhaus_slider_set_stop(g->hue, 0.498f, 0.0f, 1.0f, 1.0f);
1127 dt_bauhaus_slider_set_stop(g->hue, 0.664f, 0.0f, 0.0f, 1.0f);
1128 dt_bauhaus_slider_set_stop(g->hue, 0.830f, 1.0f, 0.0f, 1.0f);
1129 dt_bauhaus_slider_set_stop(g->hue, 1.0f, 1.0f, 0.0f, 0.0f);
1130
1131 gtk_widget_show_all(g->hue);
1132 gtk_widget_set_no_show_all(g->hue, TRUE);
1133
1134 gtk_widget_set_tooltip_text(g->threshold, _("pixels with lightness values above this threshold are corrected"));
1135 gtk_widget_set_tooltip_text(g->spatial, _("how far to look for replacement colors in spatial dimensions"));
1136 gtk_widget_set_tooltip_text(g->range, _("how far to look for replacement colors in the luminance dimension"));
1137 gtk_widget_set_tooltip_text(g->precedence, _("if and how to give precedence to specific replacement colors"));
1138 gtk_widget_set_tooltip_text(g->hue, _("the hue tone which should be given precedence over other hue tones"));
1139
1140 GtkWidget *monochromes = dt_ui_label_new(_("not applicable"));
1141 gtk_widget_set_tooltip_text(monochromes, _("no highlights reconstruction for monochrome images"));
1142
1143 self->widget = gtk_stack_new();
1144 gtk_stack_set_homogeneous(GTK_STACK(self->widget), FALSE);
1145 gtk_stack_add_named(GTK_STACK(self->widget), monochromes, "monochrome");
1146 gtk_stack_add_named(GTK_STACK(self->widget), box_enabled, "default");
1147}
1148
1150{
1152 dt_iop_colorreconstruct_bilateral_dump(g->can);
1153
1155}
1156
1157// clang-format off
1158// modelines: These editor modelines have been set for all relevant files by tools/update_modelines.py
1159// vim: shiftwidth=2 expandtab tabstop=2 cindent
1160// kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-spaces modified;
1161// 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:278
void dt_bauhaus_slider_set_stop(GtkWidget *widget, float stop, float r, float g, float b)
Definition bauhaus.c:2372
void dt_bauhaus_slider_set_feedback(GtkWidget *widget, int feedback)
Definition bauhaus.c:3580
void dt_bauhaus_slider_set_format(GtkWidget *widget, const char *format)
Definition bauhaus.c:3598
void dt_bauhaus_slider_set_factor(GtkWidget *widget, float factor)
Definition bauhaus.c:3611
int width
Definition bilateral.h:1
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
int height
Definition bilateral.h:1
float sigma_r
Definition bilateral.h:3
static const dt_aligned_pixel_simd_t const dt_adaptation_t const float p
@ IOP_CS_LAB
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)
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:761
void dt_print(dt_debug_thread_t thread, const char *msg,...)
Definition darktable.c:1542
#define dt_free_align(ptr)
Definition darktable.h:481
static void * dt_calloc_align(size_t size)
Definition darktable.h:488
@ DT_DEBUG_OPENCL
Definition darktable.h:722
#define dt_pixelpipe_cache_alloc_align_cache(size, id)
Definition darktable.h:433
float dt_aligned_pixel_simd_t __attribute__((vector_size(16), aligned(16)))
Enable aggressive floating-point arithmetic optimizations, in denormals handling. Set through user pr...
Definition darktable.h:524
#define dt_free(ptr)
Definition darktable.h:456
#define DT_MODULE_INTROSPECTION(MODVER, PARAMSTYPE)
Definition darktable.h:151
#define dt_pixelpipe_cache_free_align(mem)
Definition darktable.h:453
#define __DT_CLONE_TARGETS__
Definition darktable.h:367
#define __OMP_PARALLEL_FOR__(...)
Definition darktable.h:258
#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 darktable.h:281
void dt_iop_params_t
Definition dev_history.h:41
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:367
static void weight(const float *c1, const float *c2, const float sharpen, dt_aligned_pixel_t weight)
Definition eaw.c:30
#define DT_GUI_BOX_SPACING
Definition gtk.h:109
static GtkWidget * dt_ui_label_new(const gchar *str)
Definition gtk.h:461
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:87
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:3141
float dt_dev_get_module_scale(const dt_dev_pixelpipe_t *const pipe, const dt_iop_roi_t *const roi_in)
Definition imageop.c:131
#define IOP_GUI_FREE
Definition imageop.h:602
static void dt_iop_gui_enter_critical_section(dt_iop_module_t *const module) ACQUIRE(&module -> gui_lock)
Definition imageop.h:413
@ IOP_FLAGS_INCLUDE_IN_STYLES
Definition imageop.h:166
@ IOP_FLAGS_DEPRECATED
Definition imageop.h:168
@ IOP_FLAGS_SUPPORTS_BLENDING
Definition imageop.h:167
static void dt_iop_gui_leave_critical_section(dt_iop_module_t *const module) RELEASE(&module -> gui_lock)
Definition imageop.h:419
@ IOP_GROUP_REPAIR
Definition imageop.h:140
#define IOP_GUI_ALLOC(module)
Definition imageop.h:599
GtkWidget * dt_bauhaus_slider_from_params(dt_iop_module_t *self, const char *param)
Definition imageop_gui.c:77
GtkWidget * dt_bauhaus_combobox_from_params(dt_iop_module_t *self, const char *param)
void *const ovoid
static const float x
#define w2
Definition lmmse.c:60
#define w1
Definition lmmse.c:59
float *const restrict const size_t k
#define CLAMPS(A, L, H)
Definition math.h:76
#define M_PI
Definition math.h:45
float iscale
Definition mipmap_cache.c:2
float dt_aligned_pixel_t[4]
int dt_opencl_local_buffer_opt(const int devid, const int kernel, dt_opencl_local_buffer_t *factors)
Definition opencl.c:3156
int dt_opencl_enqueue_kernel_2d(const int dev, const int kernel, const size_t *sizes)
Definition opencl.c:2136
void * dt_opencl_alloc_device_buffer(const int devid, const size_t size)
Definition opencl.c:2544
int dt_opencl_create_kernel(const int prog, const char *name)
Definition opencl.c:2030
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:2309
void dt_opencl_free_kernel(const int kernel)
Definition opencl.c:2073
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:2127
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:2142
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:2296
void dt_opencl_release_mem_object(cl_mem mem)
Definition opencl.c:2383
int dt_opencl_avoid_atomics(const int devid)
Definition opencl.c:171
#define ROUNDUP(a, n)
Definition opencl.h:78
#define ROUNDUPDHT(a, b)
Definition opencl.h:82
#define ROUNDUPDWD(a, b)
Definition opencl.h:81
struct _GtkWidget GtkWidget
Definition splash.h:29
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:162
dt_image_t image_storage
Definition develop.h:259
unsigned int channels
Definition format.h:54
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
dt_iop_global_data_t * data
Definition imageop.h:233
int32_t hide_enable_button
Definition imageop.h:262
GtkWidget * widget
Definition imageop.h:337
struct dt_develop_t * dev
Definition imageop.h:296
dt_iop_gui_data_t * gui_data
Definition imageop.h:311
dt_iop_global_data_t * global_data
Definition imageop.h:314
dt_iop_params_t * params
Definition imageop.h:307
Region of interest passed through the pixelpipe.
Definition imageop.h:72
double scale
Definition imageop.h:74
#define MIN(a, b)
Definition thinplate.c:32
#define MAX(a, b)
Definition thinplate.c:29