Ansel 0.0
A darktable fork - bloat + design vision
Loading...
Searching...
No Matches
denoiseprofile.c
Go to the documentation of this file.
1/*
2 This file is part of darktable,
3 Copyright (C) 2010-2011 Bruce Guenter.
4 Copyright (C) 2010-2014, 2016, 2018 johannes hanika.
5 Copyright (C) 2011 Henrik Andersson.
6 Copyright (C) 2011 Kanstantsin Shautsou.
7 Copyright (C) 2011 Pascal de Bruijn.
8 Copyright (C) 2011 Robert Bieber.
9 Copyright (C) 2011-2019 Tobias Ellinghaus.
10 Copyright (C) 2011-2017, 2020 Ulrich Pegelow.
11 Copyright (C) 2012 Jean-Sébastien Pédron.
12 Copyright (C) 2012 Jesper Pedersen.
13 Copyright (C) 2012, 2018-2022 Pascal Obry.
14 Copyright (C) 2012 Richard Wonka.
15 Copyright (C) 2013 Dennis Gnad.
16 Copyright (C) 2013 Edouard Gomez.
17 Copyright (C) 2013, 2021 parafin.
18 Copyright (C) 2013-2016 Roman Lebedev.
19 Copyright (C) 2013 Simon Spannagel.
20 Copyright (C) 2013 Thomas Pryds.
21 Copyright (C) 2015 Jan Kundrát.
22 Copyright (C) 2015 Pedro Côrte-Real.
23 Copyright (C) 2017-2018 Heiko Bauke.
24 Copyright (C) 2017, 2019, 2022 luzpaz.
25 Copyright (C) 2018-2020, 2022-2023, 2025-2026 Aurélien PIERRE.
26 Copyright (C) 2018 Bill Ferguson.
27 Copyright (C) 2018-2019 Edgardo Hoszowski.
28 Copyright (C) 2018 Maurizio Paglia.
29 Copyright (C) 2018-2021 rawfiner.
30 Copyright (C) 2019 Andreas Schneider.
31 Copyright (C) 2019-2022 Diederik Ter Rahe.
32 Copyright (C) 2019 Diederik ter Rahe.
33 Copyright (C) 2019 Hartmut Knaack.
34 Copyright (C) 2019 Sam Smith.
35 Copyright (C) 2020 Aldric Renaudin.
36 Copyright (C) 2020-2021 Chris Elston.
37 Copyright (C) 2020-2021 Hubert Kowalski.
38 Copyright (C) 2020-2021 Ralf Brown.
39 Copyright (C) 2021 Dan Torop.
40 Copyright (C) 2022 Hanno Schwalm.
41 Copyright (C) 2022 Martin Bařinka.
42 Copyright (C) 2022 Philipp Lutz.
43
44 darktable is free software: you can redistribute it and/or modify
45 it under the terms of the GNU General Public License as published by
46 the Free Software Foundation, either version 3 of the License, or
47 (at your option) any later version.
48
49 darktable is distributed in the hope that it will be useful,
50 but WITHOUT ANY WARRANTY; without even the implied warranty of
51 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
52 GNU General Public License for more details.
53
54 You should have received a copy of the GNU General Public License
55 along with darktable. If not, see <http://www.gnu.org/licenses/>.
56*/
57#ifdef HAVE_CONFIG_H
58#include "common/darktable.h"
59#include "config.h"
60#endif
61#include "bauhaus/bauhaus.h"
62#include "common/eaw.h"
63#include "common/exif.h"
64#include "common/imagebuf.h"
65#include "common/nlmeans_core.h"
67#include "common/opencl.h"
68#include "control/control.h"
69#include "develop/blend.h"
70#include "develop/imageop.h"
72#include "develop/imageop_gui.h"
74#include "develop/tiling.h"
75#include "dtgtk/drawingarea.h"
76
77#include "gui/draw.h"
78#include "gui/gtk.h"
79#include "gui/presets.h"
80#include "iop/iop_api.h"
81
82#include <gtk/gtk.h>
83#include <math.h>
84#include <stdlib.h>
85// which version of the non-local means code should be used? 0=old (this file), 1=new (src/common/nlmeans_core.c)
86#define USE_NEW_IMPL_CL 0
87
88// should we dump the generated wavelet scales to files in /tmp ?
89//#define DEBUG_SCALES
90
91#define REDUCESIZE 64
92// number of intermediate buffers used by OpenCL code path. Needs to match value in src/common/nlmeans_core.c
93// to correctly compute tiling
94#define NUM_BUCKETS 4
95
96#define DT_IOP_DENOISE_PROFILE_INSET DT_PIXEL_APPLY_DPI(5)
97#define DT_IOP_DENOISE_PROFILE_RES 64
98#define DT_IOP_DENOISE_PROFILE_V8_BANDS 5
99#define DT_IOP_DENOISE_PROFILE_BANDS 7
100
101// the following fulcrum is used to help user to set shadows and strength
102// parameters.
103// applying precondition on this value will give the same value even
104// if shadows slider is changed, as strength will be adjusted to
105// guarantee that.
106// from a user point of view, it separates "shadows" area from the rest
107// of the image.
108#define DT_IOP_DENOISE_PROFILE_P_FULCRUM 0.05f
109
117
119 MODE_RGB = 0, // $DESCRIPTION: "RGB"
120 MODE_Y0U0V0 = 1 // $DESCRIPTION: "Y0U0V0"
122
123#define DT_DENOISE_PROFILE_NONE_V9 4
134
135// this is the version of the modules parameters,
136// and includes version information about compile-time dt
138
140{
141 float radius; // search radius
142 float strength; // noise level after equalization
143 float a[3], b[3]; // fit for poissonian-gaussian noise per color channel.
144 dt_iop_denoiseprofile_mode_t mode; // switch between nlmeans and wavelets
146
148{
149 float radius; // search radius
150 float strength; // noise level after equalization
151 float a[3], b[3]; // fit for poissonian-gaussian noise per color channel.
152 dt_iop_denoiseprofile_mode_t mode; // switch between nlmeans and wavelets
154 float y[DT_DENOISE_PROFILE_NONE_V9][DT_IOP_DENOISE_PROFILE_V8_BANDS]; // values to change wavelet force by frequency
156
158{
159 float radius; // patch size
160 float nbhood; // search radius
161 float strength; // noise level after equalization
162 float a[3], b[3]; // fit for poissonian-gaussian noise per color channel.
163 dt_iop_denoiseprofile_mode_t mode; // switch between nlmeans and wavelets
165 float y[DT_DENOISE_PROFILE_NONE_V9][DT_IOP_DENOISE_PROFILE_V8_BANDS]; // values to change wavelet force by frequency
167
169{
170 float radius; // patch size
171 float nbhood; // search radius
172 float strength; // noise level after equalization
173 float scattering; // spread the patch search zone without increasing number of patches
174 float a[3], b[3]; // fit for poissonian-gaussian noise per color channel.
175 dt_iop_denoiseprofile_mode_t mode; // switch between nlmeans and wavelets
177 float y[DT_DENOISE_PROFILE_NONE_V9][DT_IOP_DENOISE_PROFILE_V8_BANDS]; // values to change wavelet force by frequency
179
181{
182 float radius; // patch size
183 float nbhood; // search radius
184 float strength; // noise level after equalization
185 float scattering; // spread the patch search zone without increasing number of patches
186 float central_pixel_weight; // increase central pixel's weight in patch comparison
187 float a[3], b[3]; // fit for poissonian-gaussian noise per color channel.
188 dt_iop_denoiseprofile_mode_t mode; // switch between nlmeans and wavelets
190 float y[DT_DENOISE_PROFILE_NONE_V9][DT_IOP_DENOISE_PROFILE_V8_BANDS]; // values to change wavelet force by frequency
191 gboolean wb_adaptive_anscombe; // whether to adapt anscombe transform to wb coeffs
192 // backward compatibility options
195
197{
198 float radius; // patch size
199 float nbhood; // search radius
200 float strength; // noise level after equalization
201 float shadows; // control the impact on shadows
202 float bias; // allows to reduce backtransform bias
203 float scattering; // spread the patch search zone without increasing number of patches
204 float central_pixel_weight; // increase central pixel's weight in patch comparison
205 float overshooting; // adjusts the way parameters are autoset
206 float a[3], b[3]; // fit for poissonian-gaussian noise per color channel.
207 dt_iop_denoiseprofile_mode_t mode; // switch between nlmeans and wavelets
209 float y[DT_DENOISE_PROFILE_NONE_V9][DT_IOP_DENOISE_PROFILE_V8_BANDS]; // values to change wavelet force by frequency
210 gboolean wb_adaptive_anscombe; // whether to adapt anscombe transform to wb coeffs
211 // backward compatibility options
213 gboolean use_new_vst;
215
217{
218 float radius; // patch size
219 float nbhood; // search radius
220 float strength; // noise level after equalization
221 float shadows; // control the impact on shadows
222 float bias; // allows to reduce backtransform bias
223 float scattering; // spread the patch search zone without increasing number of patches
224 float central_pixel_weight; // increase central pixel's weight in patch comparison
225 float overshooting; // adjusts the way parameters are autoset
226 float a[3], b[3]; // fit for poissonian-gaussian noise per color channel.
227 dt_iop_denoiseprofile_mode_t mode; // switch between nlmeans and wavelets
229 float y[DT_DENOISE_PROFILE_NONE_V9][DT_IOP_DENOISE_PROFILE_BANDS]; // values to change wavelet force by frequency
230 gboolean wb_adaptive_anscombe; // whether to adapt anscombe transform to wb coeffs
231 // backward compatibility options
233 gboolean use_new_vst;
235
237{
238 float radius; /* patch size
239 $MIN: 0.0 $MAX: 12.0 $DEFAULT: 1.0 $DESCRIPTION: "patch size" */
240 float nbhood; /* search radius
241 $MIN: 1.0 $MAX: 30.0 $DEFAULT: 7.0 $DESCRIPTION: "search radius" */
242 float strength; /* noise level after equalization
243 $MIN: 0.001 $MAX: 1000.0 $DEFAULT: 1.0 */
244 float shadows; /* control the impact on shadows
245 $MIN: 0.0 $MAX: 1.8 $DEFAULT: 1.0 $DESCRIPTION: "preserve shadows" */
246 float bias; /* allows to reduce backtransform bias
247 $MIN: -1000.0 $MAX: 100.0 $DEFAULT: 0.0 $DESCRIPTION: "bias correction" */
248 float scattering; /* spread the patch search zone without increasing number of patches
249 $MIN: 0.0 $MAX: 20.0 $DEFAULT: 0.0 $DESCRIPTION: "scattering" */
250 float central_pixel_weight; /* increase central pixel's weight in patch comparison
251 $MIN: 0.0 $MAX: 10.0 $DEFAULT: 0.1 $DESCRIPTION: "central pixel weight" */
252 float overshooting; /* adjusts the way parameters are autoset
253 $MIN: 0.001 $MAX: 1000.0 $DEFAULT: 1.0 $DESCRIPTION: "adjust autoset parameters" */
254 float a[3], b[3]; // fit for poissonian-gaussian noise per color channel.
255 dt_iop_denoiseprofile_mode_t mode; /* switch between nlmeans and wavelets
256 $DEFAULT: MODE_NLMEANS */
258 float y[DT_DENOISE_PROFILE_NONE][DT_IOP_DENOISE_PROFILE_BANDS]; /* values to change wavelet force by frequency
259 $DEFAULT: 0.5 */
260 gboolean wb_adaptive_anscombe; // $DEFAULT: TRUE $DESCRIPTION: "whitebalance-adaptive transform" whether to adapt anscombe transform to wb coeffs
261 gboolean fix_anscombe_and_nlmeans_norm; // $DEFAULT: TRUE $DESCRIPTION: "fix various bugs in algorithm" backward compatibility options
262 gboolean use_new_vst; // $DEFAULT: TRUE $DESCRIPTION: "upgrade profiled transform" backward compatibility options
263 dt_iop_denoiseprofile_wavelet_mode_t wavelet_color_mode; /* switch between RGB and Y0U0V0 modes.
264 $DEFAULT: MODE_Y0U0V0 $DESCRIPTION: "color mode"*/
266
268{
269 float radius; /* patch size
270 $MIN: 0.0 $MAX: 12.0 $DEFAULT: 1.0 $DESCRIPTION: "patch size" */
271 float nbhood; /* search radius
272 $MIN: 1.0 $MAX: 30.0 $DEFAULT: 7.0 $DESCRIPTION: "search radius" */
273 float strength; /* noise level after equalization
274 $MIN: 0.001 $MAX: 1000.0 $DEFAULT: 1.0 */
275 float shadows; /* control the impact on shadows
276 $MIN: 0.0 $MAX: 1.8 $DEFAULT: 1.0 $DESCRIPTION: "preserve shadows" */
277 float bias; /* allows to reduce backtransform bias
278 $MIN: -1000.0 $MAX: 100.0 $DEFAULT: 0.0 $DESCRIPTION: "bias correction" */
279 float scattering; /* spread the patch search zone without increasing number of patches
280 $MIN: 0.0 $MAX: 20.0 $DEFAULT: 0.0 $DESCRIPTION: "scattering" */
281 float central_pixel_weight; /* increase central pixel's weight in patch comparison
282 $MIN: 0.0 $MAX: 10.0 $DEFAULT: 0.1 $DESCRIPTION: "central pixel weight" */
283 float overshooting; /* adjusts the way parameters are autoset
284 $MIN: 0.001 $MAX: 1000.0 $DEFAULT: 1.0 $DESCRIPTION: "adjust autoset parameters" */
285 float a[3], b[3]; // fit for poissonian-gaussian noise per color channel.
286 dt_iop_denoiseprofile_mode_t mode; /* switch between nlmeans and wavelets
287 $DEFAULT: MODE_WAVELETS */
289 float y[DT_DENOISE_PROFILE_NONE][DT_IOP_DENOISE_PROFILE_BANDS]; /* values to change wavelet force by frequency
290 $DEFAULT: 0.5 */
291 gboolean wb_adaptive_anscombe; // $DEFAULT: TRUE $DESCRIPTION: "whitebalance-adaptive transform" whether to adapt anscombe transform to wb coeffs
292 gboolean fix_anscombe_and_nlmeans_norm; // $DEFAULT: TRUE $DESCRIPTION: "fix various bugs in algorithm" backward compatibility options
293 gboolean use_new_vst; // $DEFAULT: TRUE $DESCRIPTION: "upgrade profiled transform" backward compatibility options
294 dt_iop_denoiseprofile_wavelet_mode_t wavelet_color_mode; /* switch between RGB and Y0U0V0 modes.
295 $DEFAULT: MODE_Y0U0V0 $DESCRIPTION: "color mode"*/
297
299{
311 dt_noiseprofile_t interpolated; // don't use name, maker or model, they may point to garbage
312 GList *profiles;
316 dt_draw_curve_t *transition_curve; // curve for gui to draw
317 GtkDrawingArea *area;
318 GtkNotebook *channel_tabs;
330 GtkLabel *label_var;
332 GtkLabel *label_var_R;
334 GtkLabel *label_var_G;
336 GtkLabel *label_var_B;
337 // backward compatibility options
341
343{
344 float radius; // patch radius
345 float nbhood; // search radius
346 float strength; // noise level after equalization
347 float shadows; // controls noise reduction in shadows
348 float bias; // controls bias in backtransform
349 float scattering; // spread the search zone without changing number of patches
350 float central_pixel_weight; // increase central pixel's weight in patch comparison
351 float overshooting; // adjusts the way parameters are autoset
352 float a[3], b[3]; // fit for poissonian-gaussian noise per color channel.
353 dt_iop_denoiseprofile_mode_t mode; // switch between nlmeans and wavelets
357 gboolean wb_adaptive_anscombe; // whether to adapt anscombe transform to wb coeffs
358 gboolean fix_anscombe_and_nlmeans_norm; // backward compatibility options
359 gboolean use_new_vst; // backward compatibility options
360 dt_iop_denoiseprofile_wavelet_mode_t wavelet_color_mode; // switch between RGB and Y0U0V0 modes.
362
383
385
386#ifdef DEBUG_SCALES
388static void debug_dump_PFM(const dt_dev_pixelpipe_iop_t *const piece, const char *const namespec,
389 const float* const restrict buf, const int width, const int height, const int scale)
390{
391 if(!dt_dev_pixelpipe_has_preview_output(piece->module->dev, self->dev->preview_pipe, NULL))
392 {
393 char filename[512];
394 snprintf(filename, sizeof(filename), namespec, scale);
395 FILE *f = g_fopen(filename, "wb");
396 if (f)
397 {
398 fprintf(f, "PF\n%d %d\n-1.0\n", width, height);
399 const size_t n = (size_t)width * height;
400 for(size_t k=0; k<n; k++)
401 fwrite(buf+4U*k, sizeof(float), 3, f);
402 fclose(f);
403 }
404 }
405}
406#else
407#define debug_dump_PFM(p,n,b,w,h,s)
408#endif
409
410int legacy_params(dt_iop_module_t *self, const void *const old_params, const int old_version,
411 void *new_params, const int new_version)
412{
413 if((old_version == 1 || old_version == 2 || old_version == 3) && new_version == 4)
414 {
415 const dt_iop_denoiseprofile_params_v1_t *o = old_params;
417 if(old_version == 1)
418 {
420 }
421 else
422 {
423 n->mode = o->mode;
424 }
425 n->radius = o->radius;
426 n->strength = o->strength;
427 memcpy(n->a, o->a, sizeof(float) * 3);
428 memcpy(n->b, o->b, sizeof(float) * 3);
429 // init curves coordinates
430 for(int b = 0; b < DT_IOP_DENOISE_PROFILE_V8_BANDS; b++)
431 {
432 for(int c = 0; c < DT_DENOISE_PROFILE_NONE_V9; c++)
433 {
434 n->x[c][b] = b / (DT_IOP_DENOISE_PROFILE_V8_BANDS - 1.0f);
435 n->y[c][b] = 0.5f;
436 }
437 }
438 // autodetect current profile:
439 if(IS_NULL_PTR(self->dev))
440 {
441 // we are probably handling a style or preset, do nothing for them, we can't do anything to detect if
442 // autodetection was used or not
443 return 0;
444 }
446 // if the profile in old_version is an autodetected one (this would mean a+b params match the interpolated
447 // one, AND
448 // the profile is actually the first selected one - however we can only detect the params, but most people
449 // did probably
450 // not set the exact ISO on purpose instead of the "found match" - they probably still want
451 // autodetection!)
452 if(!memcmp(interpolated.a, o->a, sizeof(float) * 3) && !memcmp(interpolated.b, o->b, sizeof(float) * 3))
453 {
454 // set the param a[0] to -1.0 to signal the autodetection
455 n->a[0] = -1.0f;
456 }
457 return 0;
458 }
459 else if(new_version == 5)
460 {
462 if(old_version < 4)
463 {
464 // first update to v4
465 if(legacy_params(self, old_params, old_version, &v4, 4))
466 return 1;
467 }
468 else
469 memcpy(&v4, old_params, sizeof(v4)); // was v4 already
470
471 dt_iop_denoiseprofile_params_v5_t *v5 = new_params;
472 v5->radius = v4.radius;
473 v5->strength = v4.strength;
474 v5->mode = v4.mode;
475 for(int k=0;k<3;k++)
476 {
477 v5->a[k] = v4.a[k];
478 v5->b[k] = v4.b[k];
479 }
480 for(int b = 0; b < DT_IOP_DENOISE_PROFILE_V8_BANDS; b++)
481 {
482 for(int c = 0; c < DT_DENOISE_PROFILE_NONE_V9; c++)
483 {
484 v5->x[c][b] = v4.x[c][b];
485 v5->y[c][b] = v4.y[c][b];
486 }
487 }
488 v5->nbhood = 7; // set to old hardcoded default
489 return 0;
490 }
491 else if(new_version == 6)
492 {
494 if(old_version < 5)
495 {
496 // first update to v5
497 if(legacy_params(self, old_params, old_version, &v5, 5)) return 1;
498 }
499 else
500 memcpy(&v5, old_params, sizeof(v5)); // was v5 already
501
502 dt_iop_denoiseprofile_params_v6_t *v6 = new_params;
503 v6->radius = v5.radius;
504 v6->strength = v5.strength;
505 v6->mode = v5.mode;
506 v6->nbhood = v5.nbhood;
507 for(int k = 0; k < 3; k++)
508 {
509 v6->a[k] = v5.a[k];
510 v6->b[k] = v5.b[k];
511 }
512 for(int b = 0; b < DT_IOP_DENOISE_PROFILE_V8_BANDS; b++)
513 {
514 for(int c = 0; c < DT_DENOISE_PROFILE_NONE_V9; c++)
515 {
516 v6->x[c][b] = v5.x[c][b];
517 v6->y[c][b] = v5.y[c][b];
518 }
519 }
520 v6->scattering = 0.0; // no scattering
521 return 0;
522 }
523 else if(new_version == 7)
524 {
526 if(old_version < 6)
527 {
528 // first update to v6
529 if(legacy_params(self, old_params, old_version, &v6, 6)) return 1;
530 }
531 else
532 memcpy(&v6, old_params, sizeof(v6)); // was v6 already
533 dt_iop_denoiseprofile_params_v7_t *v7 = new_params;
534 v7->radius = v6.radius;
535 v7->strength = v6.strength;
536 v7->mode = v6.mode;
537 v7->nbhood = v6.nbhood;
538 for(int k = 0; k < 3; k++)
539 {
540 v7->a[k] = v6.a[k];
541 v7->b[k] = v6.b[k];
542 }
543 for(int b = 0; b < DT_IOP_DENOISE_PROFILE_V8_BANDS; b++)
544 {
545 for(int c = 0; c < DT_DENOISE_PROFILE_NONE_V9; c++)
546 {
547 v7->x[c][b] = v6.x[c][b];
548 v7->y[c][b] = v6.y[c][b];
549 }
550 }
551 v7->scattering = v6.scattering;
552 v7->central_pixel_weight = 0.0;
553 v7->fix_anscombe_and_nlmeans_norm = FALSE; // don't fix anscombe and norm to ensure backward compatibility
555 return 0;
556 }
557 else if(new_version == 8)
558 {
560 if(old_version < 7)
561 {
562 // first update to v7
563 if(legacy_params(self, old_params, old_version, &v7, 7)) return 1;
564 }
565 else
566 memcpy(&v7, old_params, sizeof(v7)); // was v7 already
567 dt_iop_denoiseprofile_params_v8_t *v8 = new_params;
568 v8->radius = v7.radius;
569 v8->strength = v7.strength;
570 v8->mode = v7.mode;
571 v8->nbhood = v7.nbhood;
572 for(int k = 0; k < 3; k++)
573 {
574 v8->a[k] = v7.a[k];
575 v8->b[k] = v7.b[k];
576 }
577 for(int b = 0; b < DT_IOP_DENOISE_PROFILE_V8_BANDS; b++)
578 {
579 for(int c = 0; c < DT_DENOISE_PROFILE_NONE_V9; c++)
580 {
581 v8->x[c][b] = v7.x[c][b];
582 v8->y[c][b] = v7.y[c][b];
583 }
584 }
585 v8->scattering = v7.scattering;
589 v8->shadows = 1.0f;
590 v8->bias = 0.0f;
591 v8->use_new_vst = FALSE;
592 v8->overshooting = 1.0f;
593 return 0;
594 }
595 else if(new_version == 9)
596 {
598 if(old_version < 8)
599 {
600 // first update to v8
601 if(legacy_params(self, old_params, old_version, &v8, 8)) return 1;
602 }
603 else
604 memcpy(&v8, old_params, sizeof(v8)); // was v8 already
605 dt_iop_denoiseprofile_params_t *v9 = new_params;
606 v9->radius = v8.radius;
607 v9->strength = v8.strength;
608 v9->mode = v8.mode;
609 v9->nbhood = v8.nbhood;
610 for(int k = 0; k < 3; k++)
611 {
612 v9->a[k] = v8.a[k];
613 v9->b[k] = v8.b[k];
614 }
615 for(int b = 0; b < DT_IOP_DENOISE_PROFILE_BANDS; b++)
616 {
617 for(int c = 0; c < DT_DENOISE_PROFILE_NONE_V9; c++)
618 {
619 v9->x[c][b] = b / (DT_IOP_DENOISE_PROFILE_BANDS - 1.0f);
620 v9->y[c][b] = 0.0f;
621 }
622 }
623 for(int b = 0; b < DT_IOP_DENOISE_PROFILE_V8_BANDS; b++)
624 {
625 for(int c = 0; c < DT_DENOISE_PROFILE_NONE_V9; c++)
626 {
628 }
629 }
630 v9->scattering = v8.scattering;
634 v9->shadows = v8.shadows;
635 v9->bias = v8.bias;
636 v9->use_new_vst = v8.use_new_vst;
637 v9->overshooting = v8.overshooting;
638 return 0;
639 }
640 else if(new_version == 10)
641 {
643 if(old_version < 9)
644 {
645 // first update to v9
646 if(legacy_params(self, old_params, old_version, &v9, 9)) return 1;
647 }
648 else
649 memcpy(&v9, old_params, sizeof(v9)); // was v9 already
650 dt_iop_denoiseprofile_params_t *v10 = new_params;
651
652 // start with a clean default
654 *v10 = *d;
655
656 v10->radius = v9.radius;
657 v10->strength = v9.strength;
658 v10->mode = v9.mode;
659 v10->nbhood = v9.nbhood;
660 for(int k = 0; k < 3; k++)
661 {
662 v10->a[k] = v9.a[k];
663 v10->b[k] = v9.b[k];
664 }
665 for(int b = 0; b < DT_IOP_DENOISE_PROFILE_BANDS; b++)
666 {
667 for(int c = 0; c < DT_DENOISE_PROFILE_NONE_V9; c++)
668 {
669 v10->x[c][b] = v9.x[c][b];
670 v10->y[c][b] = v9.y[c][b];
671 }
673 {
674 v10->x[c][b] = b / (DT_IOP_DENOISE_PROFILE_BANDS - 1.0f);
675 v10->y[c][b] = 0.5f;
676 }
677 }
678 v10->scattering = v9.scattering;
682 v10->shadows = v9.shadows;
683 v10->bias = v9.bias;
684 v10->use_new_vst = v9.use_new_vst;
685 v10->overshooting = v9.overshooting;
687 return 0;
688 }
689 else if(new_version == 11)
690 {
691 // v11 and v10 are the same, just need to update strength when needed.
692 dt_iop_denoiseprofile_params_t *v11 = new_params;
693 if(old_version < 10)
694 {
695 if(legacy_params(self, old_params, old_version, v11, 10)) return 1;
696 }
697 else
698 memcpy(v11, old_params, sizeof(*v11)); // was v10 already
699
700 if((v11->mode == MODE_WAVELETS || v11->mode == MODE_WAVELETS_AUTO) && v11->wavelet_color_mode == MODE_Y0U0V0)
701 {
702 // in Y0U0V0, in v11, we always increase strength in the algorithm, so that
703 // the amount of smoothing is closer to what we get with the other modes.
704 const float compensate_strength = 2.5f;
705 v11->strength /= compensate_strength;
706 }
707 return 0;
708 }
709 return 1;
710}
711
713{
715 memset(&p, 0, sizeof(p));
716
717 // set some default values
718 p.radius = 1.0;
719 p.nbhood = 7.0;
720
721 // then the wavelet ones
722 p.mode = MODE_WAVELETS;
723 p.wavelet_color_mode = MODE_Y0U0V0;
724 p.strength = 1.2f;
725 p.use_new_vst = TRUE;
726 // disable variance stabilization transform to avoid any bias
727 // (wavelets perform well even without the VST):
728 p.shadows = 0.0f;
729 p.bias = 0.0f;
730 // this influences as well the way Y0U0V0 is computed:
731 p.wb_adaptive_anscombe = TRUE;
732 p.a[0] = -1.0f; // autodetect profile
733 p.central_pixel_weight = 0.1f;
734 p.overshooting = 1.0f;
735 p.fix_anscombe_and_nlmeans_norm = TRUE;
736 for(int b = 0; b < DT_IOP_DENOISE_PROFILE_BANDS; b++)
737 {
738 for(int c = 0; c < DT_DENOISE_PROFILE_NONE; c++)
739 {
740 p.x[c][b] = b / (DT_IOP_DENOISE_PROFILE_BANDS - 1.0f);
741 p.y[c][b] = 0.5f;
742 }
744 p.y[DT_DENOISE_PROFILE_Y0][b] = 0.0f;
745 }
746 dt_gui_presets_add_generic(_("wavelets: chroma only"), self->op, 11, &p,
747 sizeof(p), 1, DEVELOP_BLEND_CS_RGB_SCENE);
748}
749
750const char *name()
751{
752 return _("de_noise (profiled)");
753}
754
755const char **description(struct dt_iop_module_t *self)
756{
757 return dt_iop_set_description(self,
758 _("denoise using noise statistics profiled on sensors"),
759 _("corrective"),
760 _("linear, RGB, scene-referred"),
761 _("linear, RGB"),
762 _("linear, RGB, scene-referred"));
763}
764
766{
767 return IOP_GROUP_REPAIR;
768}
769
774
776{
777 return IOP_CS_RGB;
778}
779
780typedef union floatint_t
781{
782 float f;
783 uint32_t i;
785
786void 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)
787{
788 const dt_iop_roi_t *const roi_in = &piece->roi_in;
790
791 if(d->mode == MODE_NLMEANS || d->mode == MODE_NLMEANS_AUTO)
792 {
793 const int P = ceilf(d->radius * fminf(fminf(roi_in->scale, 2.0f), 1.0f)); // pixel filter size
794 const int K = ceilf(d->nbhood * fminf(fminf(roi_in->scale, 2.0f), 1.0f)); // nbhood
795 const int K_scattered = ceilf(d->scattering * (K * K * K + 7.0 * K * sqrt(K)) / 6.0) + K;
796
797 tiling->factor = 2.0f + 0.25f; // in + out + tmp
798 tiling->factor_cl = 4.0f + 0.25f * NUM_BUCKETS; // in + out + (2 + NUM_BUCKETS * 0.25) tmp
799 tiling->maxbuf = 1.0f;
800 tiling->overhead = 0;
801 tiling->overlap = P + K_scattered;
802 tiling->xalign = 1;
803 tiling->yalign = 1;
804 }
805 else
806 {
807 const int max_max_scale = DT_IOP_DENOISE_PROFILE_BANDS; // hard limit
808 int max_scale = 0;
809 const float scale = fminf(roi_in->scale, 1.0f);
810 // largest desired filter on input buffer (20% of input dim)
811 const float supp0
812 = fminf(2 * (2u << (max_max_scale - 1)) + 1,
813 fmaxf(piece->buf_in.height, piece->buf_in.width) * 0.2f);
814 const float i0 = dt_log2f((supp0 - 1.0f) * .5f);
815 for(; max_scale < max_max_scale; max_scale++)
816 {
817 // actual filter support on scaled buffer
818 const float supp = 2 * (2u << max_scale) + 1;
819 // approximates this filter size on unscaled input image:
820 const float supp_in = supp * (1.0f / scale);
821 const float i_in = dt_log2f((supp_in - 1) * .5f) - 1.0f;
822 // i_in = max_scale .. .. .. 0
823 const float t = 1.0f - (i_in + .5f) / i0;
824 if(t < 0.0f) break;
825 }
826
827 const int max_filter_radius = (1u << max_scale); // 2 * 2^max_scale
828
829 tiling->factor = 5.0f; // in + out + precond + tmp + reducebuffer
830 tiling->factor_cl = 3.5f + max_scale; // in + out + tmp + reducebuffer + scale buffers
831 tiling->maxbuf = 1.0f;
832 tiling->maxbuf_cl = 1.0f;
833 tiling->overhead = 0;
834 tiling->overlap = max_filter_radius;
835 tiling->xalign = 1;
836 tiling->yalign = 1;
837 }
838
839}
840
842static inline void precondition(const float *const in, float *const buf, const int wd, const int ht,
843 const dt_aligned_pixel_t a, const dt_aligned_pixel_t b)
844{
845 const dt_aligned_pixel_t sigma2_plus_3_8
846 = { (b[0] / a[0]) * (b[0] / a[0]) + 3.f / 8.f,
847 (b[1] / a[1]) * (b[1] / a[1]) + 3.f / 8.f,
848 (b[2] / a[2]) * (b[2] / a[2]) + 3.f / 8.f,
849 0.0f };
850 const size_t npixels = (size_t)wd * ht;
852 for(size_t j = 0; j < 4U * npixels; j += 4)
853 {
854 for_each_channel(c,aligned(in,buf,a,sigma2_plus_3_8))
855 {
856 const float d = fmaxf(0.0f, in[j+c] / a[c] + sigma2_plus_3_8[c]);
857 buf[j+c] = 2.0f * sqrtf(d);
858 }
859 }
860}
861
863static inline void backtransform(float *const buf, const int wd, const int ht, const dt_aligned_pixel_t a,
864 const dt_aligned_pixel_t b)
865{
866 const dt_aligned_pixel_t sigma2_plus_1_8
867 = { (b[0] / a[0]) * (b[0] / a[0]) + 1.f / 8.f,
868 (b[1] / a[1]) * (b[1] / a[1]) + 1.f / 8.f,
869 (b[2] / a[2]) * (b[2] / a[2]) + 1.f / 8.f,
870 0.0f };
871 const size_t npixels = (size_t)wd * ht;
872 const float sqrt_3_2 = sqrtf(3.0f / 2.0f);
874 for(size_t j = 0; j < 4U * npixels; j += 4)
875 {
876 for_each_channel(c,aligned(buf,sigma2_plus_1_8))
877 {
878 const float x = buf[j+c], x2 = x * x;
879 // closed form approximation to unbiased inverse (input range was 0..200 for fit, not 0..1)
880 buf[j+c] = (x < 0.5f)
881 ? 0.0f
882 : a[c] * (1.f / 4.f * x2 + 1.f / 4.f * sqrt_3_2 / x - 11.f / 8.f / x2
883 + 5.f / 8.f * sqrt_3_2 / (x * x2) - sigma2_plus_1_8[c]);
884 // asymptotic form:
885 // buf[j+c] = fmaxf(0.0f, 1./4.*x*x - 1./8. - sigma2[c]);
886 // buf[j+c] *= a[c];
887 }
888 }
889}
890
891// the "v2" variance stabilizing transform is an extension of the generalized
892// anscombe transform.
893// In the generalized anscombe transform, the profiles gives a and b such as:
894// V(X) = a * E[X] + b
895// In this new transform, we have an additional parameter, p, such as:
896// V(X) = a * (E[X] + b) ^ p
897// When p == 1, we get back the equation of generalized anscombe transform.
898// Now, let's see how we derive the precondition.
899// The goal of a VST f is to make variance constant: V(f(X)) = constant
900// Using a Taylor expansion, we have:
901// V(f(X)) ~= V(f(E[X])+f'(X)(X-E[X]))
902// = V(f'(X)(X-E[X]))
903// = f'(X)^2 * V(X-E[X])
904// = f'(X)^2 * V(X)
905// So the condition V(f(X)) = constant gives us the following condition:
906// V(X) = constant / f'(X)^2
907// Usually, we take constant = 1
908// If we have V(X) = a * (E[X] + b) ^ p
909// then: f'(X) = 1 / sqrt(a) * (E[X] + b) ^ (-p / 2)
910// then: f(x) = 1 / (sqrt(a) * (1 - p / 2)) * (x + b) ^ (1 - p / 2)
911// = 2 * (x + b) ^ (1 - p / 2) / (sqrt(a) * (2 - p))
912// is a suitable function.
913// This is the function we use here.
915static inline void precondition_v2(const float *const in, float *const buf, const int wd, const int ht,
916 const float a, const dt_aligned_pixel_t p, const float b,
917 const dt_aligned_pixel_t wb)
918{
919 const size_t npixels = (size_t)wd * ht;
920 const dt_aligned_pixel_t expon = { -p[0] / 2 + 1, -p[1] / 2 + 1, -p[2] / 2 + 1, 1.0f };
921 const dt_aligned_pixel_t denom = { (-p[0] + 2) * sqrtf(a), (-p[1] + 2) * sqrtf(a),
922 (-p[2] + 2) * sqrtf(a), 1.0f };
924 for(size_t j = 0; j < 4U * npixels; j += 4)
925 {
926 for_each_channel(c,aligned(in,buf,wb))
927 {
928 buf[j+c] = 2.0f * powf(MAX(in[j+c] / wb[c] + b, 0.0f), expon[c]) / denom[c];
929 }
930 }
931}
932
933// this backtransform aims at being a low bias backtransform
934// you can see that it is not equal to f-1
935// this is because E[X] != f-1(E[f(X)])
936// so let's try to find a better backtransform than f-1:
937// we want to find E[X] knowing E[f(X)]
938// let's apply Taylor expansion to E[f(X)] to see if we can get something better:
939// E[f(X)] ~= E[f(E[X]) + f'(E[X])(X-E[X])]
940// = E[f(E[X]) + f'(E[X]) * X - f'(E[X]) * E[X]]
941// = f(E[X]) + f'(E[X]) * E[X] - f'(E[X]) * E[X]
942// = f(E[X])
943// so first order Taylor expansion is not useful.
944// going to the second order:
945// E[f(X)] ~= E[f(E[X]) + f'(E[X])(X-E[X]) + f"(E[X])/2 * (X-E[X])^2]
946// = f(E[X]) + f"(E[X])/2 * E[(X-E[X])^2]
947// = f(E[X]) + f"(E[X])/2 * V(X)
948// and we know that V(X) = constant / f'(X)^2
949// the constant here is not 1, due to problems in noise profiling tool
950// so in fact constant depends on the image (and is approximately in [10;15])
951// so:
952// E[f(X)] ~= f(E[X]) + f"(E[X])/2 * constant / f'(E[X])^2
953// we have:
954// f(x) = 2 * (x + b) ^ (1 - p / 2) / (sqrt(a) * (2 - p))
955// f'(x) = 1 / sqrt(a) * (x + b) ^ (-p / 2)
956// 1/f'(x)^2 = a * (x + b) ^ p
957// f"(x) = 1 / sqrt(a) * (-p / 2) * (x + b) ^ (- p / 2 - 1)
958// let's replace f, f', and f" by their analytical expressions in our equation:
959// let x = E[X]
960// E[f(X)] ~= 2 * (x + b) ^ (1 - p / 2) / (sqrt(a) * (2 - p))
961// + constant / 2 * (1 / sqrt(a) * (-p / 2) * (x + b) ^ (- p / 2 - 1)) * (a * (x + b) ^ p)
962// = 2 * (x + b) ^ (1 - p / 2) / (sqrt(a) * (2 - p))
963// + constant / 2 * 1 / sqrt(a) * (-p / 2) * a * (x + b) ^ (p / 2 - 1)
964// = 2 * (x + b) ^ (1 - p / 2) / (sqrt(a) * (2 - p))
965// - constant / 4 * sqrt(a) * p * (x + b) ^ (p / 2 - 1)
966// let z = (x + b) ^ (1 - p / 2)
967// E[f(X)] ~= 2 / (sqrt(a) * (2 - p)) * z
968// - constant / 4 * sqrt(a) * p * z^(-1)
969// let y = E[f(X)]
970// y ~= 2 / (sqrt(a) * (2 - p)) * z - constant / 4 * sqrt(a) * p * z^(-1)
971// y * z = 2 / (sqrt(a) * (2 - p)) * z^2 - constant / 4 * sqrt(a) * p
972// 0 = 2 / (sqrt(a) * (2 - p)) * z^2 - y * z - constant / 4 * sqrt(a) * p
973// let's solve this equation:
974// delta = y ^ 2 - 4 * 2 / (sqrt(a) * (2 - p)) * (- constant / 4 * sqrt(a))
975// = y ^ 2 + 2 * p * constant / (2 - p)
976// delta >= 0
977// the 2 solutions are:
978// z0 = (y - sqrt(delta)) / (2 * 2 / (sqrt(a) * (2 - p)))
979// z1 = (y + sqrt(delta)) / (2 * 2 / (sqrt(a) * (2 - p)))
980// as delta > y^2, sqrt(delta) > y, so z0 is negative
981// so z1 is the only possible solution.
982// Then, to find E[X], we only have to do:
983// z = (x + b) ^ (1 - p / 2) <=> x = z ^ (1 / (1 - p / 2)) - b
984//
985// What we see here is that a bias compensation term is in delta:
986// the term: 2 * p * constant / (2 - p)
987// But we are not sure at all what the value of the constant is
988// That's why we introduce a user-controled bias parameter to be able to
989// control the bias:
990// we replace the 2 * p * constant / (2 - p) part of delta by user
991// defined bias controller.
993static inline void backtransform_v2(float *const buf, const int wd, const int ht, const float a,
994 const dt_aligned_pixel_t p, const float b, const float bias,
995 const dt_aligned_pixel_t wb)
996{
997 const size_t npixels = (size_t)wd * ht;
998 const dt_aligned_pixel_t expon = { 1.0f / (1.0f - p[0] / 2.0f), 1.0f / (1.0f - p[1] / 2.0f),
999 1.0f / (1.0f - p[2] / 2.0f), 1.0f };
1000 const dt_aligned_pixel_t denom = { 4.0f / (sqrtf(a) * (2.0f - p[0])), 4.0f / (sqrtf(a) * (2.0f - p[1])),
1001 4.0f / (sqrtf(a) * (2.0f - p[2])), 1.0f };
1003 for(size_t j = 0; j < 4U * npixels; j += 4)
1004 {
1005 for_each_channel(c,aligned(buf,wb))
1006 {
1007 const float x = MAX(buf[j+c], 0.0f);
1008 const float delta = x * x + bias;
1009 const float z1 = (x + sqrtf(MAX(delta, 0.0f))) / denom[c];
1010 buf[j+c] = wb[c] * (powf(z1, expon[c]) - b);
1011 }
1012 }
1013}
1014
1016static inline void precondition_Y0U0V0(const float *const in, float *const buf, const int wd, const int ht,
1017 const float a, const dt_aligned_pixel_t p, const float b,
1018 const dt_colormatrix_t toY0U0V0)
1019{
1020 const dt_aligned_pixel_t expon = { -p[0] / 2 + 1, -p[1] / 2 + 1, -p[2] / 2 + 1, 1.0f };
1021 const dt_aligned_pixel_t scale = { 2.0f / ((-p[0] + 2) * sqrtf(a)),
1022 2.0f / ((-p[1] + 2) * sqrtf(a)),
1023 2.0f / ((-p[2] + 2) * sqrtf(a)),
1024 1.0f };
1026 for(size_t j = 0; j < (size_t)4 * ht * wd; j += 4)
1027 {
1028 dt_aligned_pixel_t tmp; // "unused" fourth element enables vectorization
1029 for_each_channel(c,aligned(in))
1030 {
1031 tmp[c] = powf(MAX(in[j+c] + b, 0.0f), expon[c]) * scale[c];
1032 }
1033 for(int c = 0; c < 3; c++)
1034 {
1035 float sum = 0.0f;
1036 for_each_channel(k,aligned(toY0U0V0))
1037 {
1038 sum += toY0U0V0[c][k] * tmp[k];
1039 }
1040 buf[j+c] = sum;
1041 }
1042 buf[j+3] = 0;
1043 }
1044}
1045
1047static inline void backtransform_Y0U0V0(float *const buf, const int wd, const int ht, const float a,
1048 const dt_aligned_pixel_t p, const float b, const float bias,
1049 const dt_aligned_pixel_t wb, const dt_colormatrix_t toRGB)
1050{
1051 const dt_aligned_pixel_t bias_wb = { bias * wb[0], bias * wb[1], bias * wb[2], 0.0f };
1052 const dt_aligned_pixel_t expon = { 1.0f / (1.0f - p[0] / 2.0f),
1053 1.0f / (1.0f - p[1] / 2.0f),
1054 1.0f / (1.0f - p[2] / 2.0f),
1055 1.0f };
1056 const dt_aligned_pixel_t scale = { (sqrtf(a) * (2.0f - p[0])) / 4.0f,
1057 (sqrtf(a) * (2.0f - p[1])) / 4.0f,
1058 (sqrtf(a) * (2.0f - p[2])) / 4.0f,
1059 1.0f };
1061 for(size_t j = 0; j < (size_t)4 * ht * wd; j += 4)
1062 {
1063 dt_aligned_pixel_t rgb = { 0.0f }; // "unused" fourth element enables vectorization
1064 for(int k = 0; k < 3; k++)
1065 {
1066 for_each_channel(c,aligned(toRGB,buf))
1067 {
1068 rgb[k] += toRGB[k][c] * buf[j+c];
1069 }
1070 }
1071 for_each_channel(c,aligned(buf))
1072 {
1073 const float x = MAX(rgb[c], 0.0f);
1074 const float delta = x * x + bias_wb[c];
1075 const float z1 = (x + sqrtf(MAX(delta, 0.0f))) * scale[c];
1076 buf[j+c] = powf(z1, expon[c]) - b;
1077 }
1078 }
1079}
1080
1081// =====================================================================================
1082// begin common functions
1083// =====================================================================================
1084
1085// called by: process_wavelets, nlmeans_precondition, nlmeans_precondition_cl, process_variance,
1086// process_wavelets_cl
1089 const dt_dev_pixelpipe_iop_t *const piece, const dt_aligned_pixel_t weights)
1090{
1091 const float wb_mean = (piece->dsc_in.temperature.coeffs[0] + piece->dsc_in.temperature.coeffs[1]
1092 + piece->dsc_in.temperature.coeffs[2])
1093 / 3.0f;
1094 // we init wb by the mean of the coeffs, which corresponds to the mean
1095 // amplification that is done in addition to the "ISO" related amplification
1096 wb[0] = wb[1] = wb[2] = wb[3] = wb_mean;
1097 if(d->fix_anscombe_and_nlmeans_norm)
1098 {
1099 if(wb_mean != 0.0f && d->wb_adaptive_anscombe)
1100 {
1101 for(int i = 0; i < 3; i++) wb[i] = piece->dsc_in.temperature.coeffs[i];
1102 }
1103 else if(wb_mean == 0.0f)
1104 {
1105 // temperature coeffs are equal to 0 if we open a JPG image.
1106 // in this case consider them equal to 1.
1108 wb[i] = 1.0f;
1109 }
1110 // else, wb_adaptive_anscombe is false and our wb array is
1111 // filled with the wb_mean
1112 }
1113 else
1114 {
1116 wb[i] = weights[i] * piece->dsc_in.processed_maximum[i];
1117 }
1118 return;
1119}
1120
1121// =====================================================================================
1122
1123static inline __attribute__((always_inline)) gboolean invert_matrix(const dt_colormatrix_t in, dt_colormatrix_t out)
1124{
1125 // use same notation as https://en.wikipedia.org/wiki/Invertible_matrix#Inversion_of_3_%C3%97_3_matrices
1126 const float biga = in[1][1] * in[2][2] - in[1][2] * in[2][1];
1127 const float bigb = -in[1][0] * in[2][2] + in[1][2] * in[2][0];
1128 const float bigc = in[1][0] * in[2][1] - in[1][1] * in[2][0];
1129 const float bigd = -in[0][1] * in[2][2] + in[0][2] * in[2][1];
1130 const float bige = in[0][0] * in[2][2] - in[0][2] * in[2][0];
1131 const float bigf = -in[0][0] * in[2][1] + in[0][1] * in[2][0];
1132 const float bigg = in[0][1] * in[1][2] - in[0][2] * in[1][1];
1133 const float bigh = -in[0][0] * in[1][2] + in[0][2] * in[1][0];
1134 const float bigi = in[0][0] * in[1][1] - in[0][1] * in[1][0];
1135
1136 const float det = in[0][0] * biga + in[0][1] * bigb + in[0][2] * bigc;
1137 if(det == 0.0f)
1138 {
1139 return FALSE;
1140 }
1141
1142 out[0][0] = 1.0f / det * biga;
1143 out[0][1] = 1.0f / det * bigd;
1144 out[0][2] = 1.0f / det * bigg;
1145 out[0][3] = 0.0f;
1146 out[1][0] = 1.0f / det * bigb;
1147 out[1][1] = 1.0f / det * bige;
1148 out[1][2] = 1.0f / det * bigh;
1149 out[1][3] = 0.0f;
1150 out[2][0] = 1.0f / det * bigc;
1151 out[2][1] = 1.0f / det * bigf;
1152 out[2][2] = 1.0f / det * bigi;
1153 out[2][3] = 0.0f;
1154 return TRUE;
1155}
1156
1157// create the white balance adaptative conversion matrices
1158// supposes toY0U0V0 already contains the "normal" conversion matrix
1161 const dt_aligned_pixel_t wb)
1162{
1163 // for an explanation of the spirit of the choice of the coefficients of the
1164 // Y0U0V0 conversion matrix, see part 12.3.3 page 190 of
1165 // "From Theory to Practice, a Tour of Image Denoising"
1166 // https://hal.archives-ouvertes.fr/tel-01114299
1167 // we adapt a bit the coefficients, in a way that follows the same spirit.
1168
1169 float sum_invwb = 1.0f/wb[0] + 1.0f/wb[1] + 1.0f/wb[2];
1170 // we change the coefs to Y0, but keeping the goal of making SNR higher:
1171 // these were all equal to 1/3 to get the Y0 the least noisy possible, assuming
1172 // that all channels have equal noise variance.
1173 // as white balance influences noise variance, we do a weighted mean depending
1174 // on white balance. Note that it is equivalent to keeping the 1/3 coefficients
1175 // if we divide by the white balance coefficients beforehand.
1176 // we then normalize the line so that variance becomes equal to 1:
1177 // var(Y0) = 1/9 * (var(R) + var(G) + var(B)) = 1/3
1178 // var(sqrt(3)Y0) = 1
1179 sum_invwb *= sqrtf(3);
1180 toY0U0V0[0][0] = sum_invwb / wb[0];
1181 toY0U0V0[0][1] = sum_invwb / wb[1];
1182 toY0U0V0[0][2] = sum_invwb / wb[2];
1183 toY0U0V0[0][3] = 0.0f;
1184 // we also normalize the other line in a way that should give a variance of 1
1185 // if var(B/wb[B]) == 1, then var(B) = wb[B]^2
1186 // note that we don't change the coefs of U0 and V0 depending on white balance,
1187 // apart of the normalization: these coefficients do differences of RGB channels
1188 // to try to reduce or cancel the signal. If we change these depending on white
1189 // balance, we will not reduce/cancel the signal anymore.
1190 const float stddevU0 = sqrtf(0.5f * 0.5f * wb[0] * wb[0] + 0.5f * 0.5f * wb[2] * wb[2]);
1191 const float stddevV0 = sqrtf(0.25f * 0.25f * wb[0] * wb[0] + 0.5f * 0.5f * wb[1] * wb[1] + 0.25f * 0.25f * wb[2] * wb[2]);
1192 toY0U0V0[1][0] /= stddevU0;
1193 toY0U0V0[1][1] /= stddevU0;
1194 toY0U0V0[1][2] /= stddevU0;
1195 toY0U0V0[1][3] = 0.0f;
1196 toY0U0V0[2][0] /= stddevV0;
1197 toY0U0V0[2][1] /= stddevV0;
1198 toY0U0V0[2][2] /= stddevV0;
1199 toY0U0V0[2][3] = 0.0f;
1200 const gboolean is_invertible = invert_matrix(toY0U0V0, toRGB);
1201 if(!is_invertible)
1202 {
1203 // use standard form if whitebalance adapted matrix is not invertible
1204 float stddevY0 = sqrtf(1.0f / 9.0f * (wb[0] * wb[0] + wb[1] * wb[1] + wb[2] * wb[2]));
1205 toY0U0V0[0][0] = 1.0f / (3.0f * stddevY0);
1206 toY0U0V0[0][1] = 1.0f / (3.0f * stddevY0);
1207 toY0U0V0[0][2] = 1.0f / (3.0f * stddevY0);
1208 toY0U0V0[0][3] = 0.0f;
1209 invert_matrix(toY0U0V0, toRGB);
1210 }
1211}
1212
1213static inline __attribute__((always_inline)) void variance_stabilizing_xform(dt_aligned_pixel_t thrs, const int scale, const int max_scale, const size_t npixels,
1214 const float *const sum_y2, const dt_iop_denoiseprofile_data_t *const d)
1215{
1216 // variance stabilizing transform maps sigma to unity.
1217 const float sigma = 1.0f;
1218 // it is then transformed by wavelet scales via the 5 tap a-trous filter:
1219 const float varf = sqrtf(2.0f + 2.0f * 4.0f * 4.0f + 6.0f * 6.0f) / 16.0f; // about 0.5
1220 const float sigma_band = powf(varf, scale) * sigma;
1221 // determine thrs as bayesshrink
1222 const float sb2 = sigma_band * sigma_band;
1223 const dt_aligned_pixel_t var_y = { sum_y2[0] / (npixels - 1.0f), sum_y2[1] / (npixels - 1.0f),
1224 sum_y2[2] / (npixels - 1.0f), 0.0f };
1225 const dt_aligned_pixel_t std_x = { sqrtf(MAX(1e-6f, var_y[0] - sb2)), sqrtf(MAX(1e-6f, var_y[1] - sb2)),
1226 sqrtf(MAX(1e-6f, var_y[2] - sb2)), 1.0f };
1227 // add 8.0 here because it seemed a little weak
1228 dt_aligned_pixel_t adjt = { 8.0f, 8.0f, 8.0f, 0.0f };
1229
1230 const int offset_scale = DT_IOP_DENOISE_PROFILE_BANDS - max_scale;
1231 const int band_index = DT_IOP_DENOISE_PROFILE_BANDS - (scale + offset_scale + 1);
1232
1233 if(d->wavelet_color_mode == MODE_RGB)
1234 {
1235 // current scale number is scale+offset_scale
1236 // for instance, largest scale is DT_IOP_DENOISE_PROFILE_BANDS
1237 // max_scale only indicates the number of scales to process at THIS
1238 // zoom level, it does NOT corresponds to the the maximum number of scales.
1239 // in other words, max_scale is the maximum number of VISIBLE scales.
1240 // That is why we have this "scale+offset_scale"
1241 float band_force_exp_2
1242 = d->force[DT_DENOISE_PROFILE_ALL][band_index];
1243 band_force_exp_2 *= band_force_exp_2;
1244 band_force_exp_2 *= 4;
1246 {
1247 adjt[ch] *= band_force_exp_2;
1248 }
1249 band_force_exp_2 = d->force[DT_DENOISE_PROFILE_R][band_index];
1250 band_force_exp_2 *= band_force_exp_2;
1251 band_force_exp_2 *= 4;
1252 adjt[0] *= band_force_exp_2;
1253 band_force_exp_2 = d->force[DT_DENOISE_PROFILE_G][band_index];
1254 band_force_exp_2 *= band_force_exp_2;
1255 band_force_exp_2 *= 4;
1256 adjt[1] *= band_force_exp_2;
1257 band_force_exp_2 = d->force[DT_DENOISE_PROFILE_B][band_index];
1258 band_force_exp_2 *= band_force_exp_2;
1259 band_force_exp_2 *= 4;
1260 adjt[2] *= band_force_exp_2;
1261 }
1262 else
1263 {
1264 float band_force_exp_2 = d->force[DT_DENOISE_PROFILE_Y0][band_index];
1265 band_force_exp_2 *= band_force_exp_2;
1266 band_force_exp_2 *= 4;
1267 adjt[0] *= band_force_exp_2;
1268 band_force_exp_2 = d->force[DT_DENOISE_PROFILE_U0V0][band_index];
1269 band_force_exp_2 *= band_force_exp_2;
1270 band_force_exp_2 *= 4;
1271 adjt[1] *= band_force_exp_2;
1272 adjt[2] *= band_force_exp_2;
1273 }
1275 thrs[c] = adjt[c] * sb2 / std_x[c];
1276}
1277
1279static int process_wavelets(struct dt_iop_module_t *self, const dt_dev_pixelpipe_t *pipe,
1280 const dt_dev_pixelpipe_iop_t *piece,
1281 const void *const ivoid, void *const ovoid, const dt_iop_roi_t *const roi_in,
1282 const dt_iop_roi_t *const roi_out, const eaw_dn_decompose_t decompose,
1283 const eaw_synthesize_t synthesize)
1284{
1285 // this is called for preview and full pipe separately, each with its own pixelpipe piece.
1286 // get our data struct:
1288
1289#define MAX_MAX_SCALE DT_IOP_DENOISE_PROFILE_BANDS // hard limit
1290
1291 int max_scale = 0;
1292 const float in_scale = fminf(roi_in->scale, 1.0f);
1293 // largest desired filter on input buffer (20% of input dim)
1294 const float supp0 = MIN(2 * (2u << (MAX_MAX_SCALE - 1)) + 1,
1295 MAX(piece->buf_in.height, piece->buf_in.width) * 0.2f);
1296 const float i0 = dt_log2f((supp0 - 1.0f) * .5f);
1297 for(; max_scale < MAX_MAX_SCALE; max_scale++)
1298 {
1299 // actual filter support on scaled buffer
1300 const float supp = 2 * (2u << max_scale) + 1;
1301 // approximates this filter size on unscaled input image:
1302 const float supp_in = supp * (1.0f / in_scale);
1303 const float i_in = dt_log2f((supp_in - 1) * .5f) - 1.0f;
1304 // i_in = max_scale .. .. .. 0
1305 const float t = 1.0f - (i_in + .5f) / i0;
1306 if(t < 0.0f) break;
1307 }
1308
1309 const int max_mult = 1u << (max_scale - 1);
1310 const int width = roi_in->width, height = roi_in->height;
1311 const size_t npixels = (size_t)width*height;
1312 const float *const restrict in = (const float*)ivoid;
1313 float *const restrict out = (float*)ovoid;
1314
1315 // corner case of extremely small image. this is not really likely to happen but would
1316 // lead to out of bounds memory access
1317 if(width < 2 * max_mult || height < 2 * max_mult)
1318 {
1319 memcpy(out, in, sizeof(float) * 4 * npixels);
1320 return 0;
1321 }
1322
1323 float *buf = NULL;
1324 float *restrict precond = NULL;
1325 float *restrict tmp = NULL;
1326
1327 if (dt_iop_alloc_image_buffers(self, roi_in, roi_out, 4, &precond, 4, &tmp, 4, &buf, 0))
1328 {
1329 dt_iop_copy_image_roi(out, in, piece->dsc_in.channels, roi_in, roi_out, TRUE);
1330 return 1;
1331 }
1332
1333 dt_aligned_pixel_t wb; // the "unused" fourth element enables vectorization
1334 const dt_aligned_pixel_t wb_weights = { 2.0f, 1.0f, 2.0f, 0.0f };
1335 compute_wb_factors(wb,d,piece,wb_weights);
1336
1337 // adaptive p depending on white balance (the "unused" fourth element enables vectorization
1338 const dt_aligned_pixel_t p = { MAX(d->shadows + 0.1 * logf(in_scale / wb[0]), 0.0f),
1339 MAX(d->shadows + 0.1 * logf(in_scale / wb[1]), 0.0f),
1340 MAX(d->shadows + 0.1 * logf(in_scale / wb[2]), 0.0f),
1341 0.0f };
1342
1343 const float compensate_p = DT_IOP_DENOISE_PROFILE_P_FULCRUM / powf(DT_IOP_DENOISE_PROFILE_P_FULCRUM, d->shadows);
1344
1345 // conversion to Y0U0V0 space as defined in Secrets of image denoising cuisine
1346 dt_colormatrix_t toY0U0V0 = { { 1.0f/3.0f, 1.0f/3.0f, 1.0f/3.0f },
1347 { 0.5f, 0.0f, -0.5f },
1348 { 0.25f, -0.5f, 0.25f } };
1349 dt_colormatrix_t toRGB = { { 0.0f, 0.0f, 0.0f }, // "unused" fourth element enables vectorization
1350 { 0.0f, 0.0f, 0.0f },
1351 { 0.0f, 0.0f, 0.0f } };
1352 set_up_conversion_matrices(toY0U0V0, toRGB, wb);
1353
1354 // more strength in Y0U0V0 in order to get a similar smoothing as in other modes
1355 // otherwise, result was much less denoised in Y0U0V0 mode.
1356 const float compensate_strength = (d->wavelet_color_mode == MODE_RGB) ? 1.0f : 2.5f;
1357 // update the coeffs with strength and scale
1358 for(size_t k = 0; k < 3; k++)
1360 {
1361 toY0U0V0[k][c] /= (d->strength * compensate_strength * in_scale);
1362 toRGB[k][c] *= (d->strength * compensate_strength * in_scale);
1363 }
1364 for_each_channel(i) wb[i] *= d->strength * compensate_strength * in_scale;
1365
1366 // only use green channel + wb for now: (the "unused" fourth element enables vectorization)
1367 const dt_aligned_pixel_t aa = { d->a[1] * wb[0], d->a[1] * wb[1], d->a[1] * wb[2], 0.0f };
1368 const dt_aligned_pixel_t bb = { d->b[1] * wb[0], d->b[1] * wb[1], d->b[1] * wb[2], 0.0f };
1369
1370 if(!d->use_new_vst)
1371 {
1372 precondition(in, precond, width, height, aa, bb);
1373 }
1374 else if(d->wavelet_color_mode == MODE_RGB)
1375 {
1376 precondition_v2(in, precond, width, height, d->a[1] * compensate_p, p, d->b[1], wb);
1377 }
1378 else
1379 {
1380 precondition_Y0U0V0(in, precond, width, height, d->a[1] * compensate_p, p, d->b[1], toY0U0V0);
1381 }
1382
1383 debug_dump_PFM(piece,"/tmp/transformed.pfm",precond,width,height,0);
1384
1385 float *restrict buf1 = precond;
1386 float *restrict buf2 = tmp;
1387
1388 // clear the output buffer, which will be accumulating all of the detail scales
1389 memset(out, 0, sizeof(float) * 4 * npixels);
1390
1391 for(int scale = 0; scale < max_scale; scale++)
1392 {
1393 const float sigma = 1.0f;
1394 const float varf = sqrtf(2.0f + 2.0f * 4.0f * 4.0f + 6.0f * 6.0f) / 16.0f; // about 0.5
1395 const float sigma_band = powf(varf, scale) * sigma;
1396 dt_aligned_pixel_t sum_y2;
1397 decompose(buf2, buf1, buf, sum_y2, scale, 1.0f / (sigma_band * sigma_band), width, height);
1398 debug_dump_PFM(piece,"/tmp/coarse_%d.pfm",buf2,width,height,scale);
1399 debug_dump_PFM(piece,"/tmp/detail_%d.pfm",buf,width,height,scale);
1400
1401 const dt_aligned_pixel_t boost = { 1.0f, 1.0f, 1.0f, 1.0f };
1403 variance_stabilizing_xform(thrs, scale, max_scale, npixels, sum_y2, d);
1404 synthesize(out, out, buf, thrs, boost, width, height);
1405
1406 float *buf3 = buf2;
1407 buf2 = buf1;
1408 buf1 = buf3;
1409 }
1410
1411 // add in the final residue
1412 __OMP_SIMD__(aligned(buf1, out : 64))
1413 for (size_t k = 0; k < 4U * npixels; k++)
1414 out[k] += buf1[k];
1415
1416 if(!d->use_new_vst)
1417 {
1418 backtransform(out, width, height, aa, bb);
1419 }
1420 else if(d->wavelet_color_mode == MODE_RGB)
1421 {
1422 backtransform_v2(out, width, height, d->a[1] * compensate_p, p, d->b[1], d->bias - 0.5 * logf(in_scale), wb);
1423 }
1424 else
1425 {
1426 backtransform_Y0U0V0(out, width, height, d->a[1] * compensate_p, p, d->b[1], d->bias - 0.5 * logf(in_scale), wb, toRGB);
1427 }
1428
1432
1433 if(pipe->mask_display & DT_DEV_PIXELPIPE_DISPLAY_MASK) dt_iop_alpha_copy(ivoid, ovoid, width, height);
1434
1435#undef MAX_MAX_SCALE
1436 return 0;
1437}
1438
1439#if defined(HAVE_OPENCL) && !USE_NEW_IMPL_CL
1440static int sign(int a)
1441{
1442 return (a > 0) - (a < 0);
1443}
1444#endif
1445
1446// called by: process_nlmeans_cpu, process_nlmeans_cl
1447static inline __attribute__((always_inline)) float nlmeans_norm(const int P, const dt_iop_denoiseprofile_data_t *const d)
1448{
1449 // Each patch has a width of 2P+1 and a height of 2P+1
1450 // thus, divide by (2P+1)^2.
1451 // The 0.045 was derived from the old formula, to keep the
1452 // norm identical when P=1, as the norm for P=1 seemed
1453 // to work quite well: 0.045 = 0.015 * (2 * P + 1) with P=1.
1454 float norm = .045f / ((2 * P + 1) * (2 * P + 1));
1455 if(!d->fix_anscombe_and_nlmeans_norm)
1456 {
1457 // use old formula
1458 norm = .015f / (2 * P + 1);
1459 }
1460 return norm;
1461}
1462
1463// adjust the user-specified scattering factor and search radius to account for the type of pixelpipe
1464// called by: process_nlmeans_cpu, process_nlmeans_cl
1465static inline __attribute__((always_inline)) float nlmeans_scattering(const dt_dev_pixelpipe_t *pipe, int *nbhood,
1466 const dt_iop_denoiseprofile_data_t *const d,
1467 const dt_dev_pixelpipe_iop_t *const piece, const float scale)
1468{
1469 int K = *nbhood;
1470 float scattering = d->scattering;
1471
1472 if(dt_dev_pixelpipe_has_preview_output(piece->module->dev, pipe, NULL)
1473 || pipe->type == DT_DEV_PIXELPIPE_THUMBNAIL)
1474 {
1475 // much faster slightly more inaccurate preview
1476 const int maxk = (K * K * K + 7.0 * K * sqrt(K)) * scattering / 6.0 + K;
1477 K = MIN(3, K);
1478 scattering = (maxk - K) * 6.0 / (K * K * K + 7.0 * K * sqrt(K));
1479 }
1480 if(!dt_dev_pixelpipe_has_preview_output(piece->module->dev, pipe, NULL))
1481 {
1482 // much faster slightly more inaccurate preview
1483 const int maxk = (K * K * K + 7.0 * K * sqrt(K)) * scattering / 6.0 + K;
1484 K = MAX(MIN(4, K), K * scale);
1485 scattering = (maxk - K) * 6.0 / (K * K * K + 7.0 * K * sqrt(K));
1486 }
1487 *nbhood = K;
1488 return scattering;
1489}
1490
1491// called by process_nlmeans_cpu
1492// must keep synchronized with nlmeans_precondition_cl below
1493static inline __attribute__((always_inline)) float nlmeans_precondition(const dt_iop_denoiseprofile_data_t *const d,
1494 const dt_dev_pixelpipe_iop_t *const piece, dt_aligned_pixel_t wb,
1495 const void *const ivoid, const dt_iop_roi_t *const roi_in,
1496 float scale, float *in, dt_aligned_pixel_t aa,
1498{
1499 // the "unused" fourth array element enables vectorization
1500 const dt_aligned_pixel_t wb_weights = { 1.0f, 1.0f, 1.0f, 0.0f };
1501 compute_wb_factors(wb,d,piece,wb_weights);
1502
1503 // adaptive p depending on white balance
1504 p[0] = MAX(d->shadows + 0.1 * logf(scale / wb[0]), 0.0f);
1505 p[1] = MAX(d->shadows + 0.1 * logf(scale / wb[1]), 0.0f);
1506 p[2] = MAX(d->shadows + 0.1 * logf(scale / wb[2]), 0.0f);
1507 p[3] = 0.0f;
1508
1509 // update the coeffs with strength and scale
1510 for_each_channel(i,aligned(wb,aa,bb))
1511 {
1512 wb[i] *= d->strength * scale;
1513 // only use green channel + wb for now:
1514 aa[i] = d->a[1] * wb[i];
1515 bb[i] = d->b[1] * wb[i];
1516 }
1517 const float compensate_p = DT_IOP_DENOISE_PROFILE_P_FULCRUM / powf(DT_IOP_DENOISE_PROFILE_P_FULCRUM, d->shadows);
1518 if(!d->use_new_vst)
1519 {
1520 precondition((float *)ivoid, in, roi_in->width, roi_in->height, aa, bb);
1521 }
1522 else
1523 {
1524 precondition_v2((float *)ivoid, in, roi_in->width, roi_in->height, d->a[1] * compensate_p, p, d->b[1], wb);
1525 }
1526 return compensate_p;
1527}
1528
1529#ifdef HAVE_OPENCL
1530// called by process_nlmeans_cl
1531// must keep synchronized with nlmeans_precondition above
1533 const dt_dev_pixelpipe_iop_t *const piece, dt_aligned_pixel_t wb,
1534 float scale, dt_aligned_pixel_t aa, dt_aligned_pixel_t bb,
1536{
1537 // the "unused" fourth element enables vectorization
1538 const dt_aligned_pixel_t wb_weights = { 1.0f, 1.0f, 1.0f, 0.0f };
1539 compute_wb_factors(wb,d,piece,wb_weights);
1540 wb[3] = 0.0;
1541
1542 // adaptive p depending on white balance
1543 p[0] = MAX(d->shadows + 0.1 * logf(scale / wb[0]), 0.0f);
1544 p[1] = MAX(d->shadows + 0.1 * logf(scale / wb[1]), 0.0f);
1545 p[2] = MAX(d->shadows + 0.1 * logf(scale / wb[2]), 0.0f);
1546 p[3] = 1.0f;
1547
1548 // update the coeffs with strength and scale
1549 for_each_channel(i,aligned(wb,aa,bb))
1550 {
1551 wb[i] *= d->strength * scale;
1552 // only use green channel + wb for now:
1553 aa[i] = d->a[1] * wb[i];
1554 bb[i] = d->b[1] * wb[i];
1555 }
1556 aa[3] = 1.0f;
1557 bb[3] = 1.0f;
1558 const float compensate_p = DT_IOP_DENOISE_PROFILE_P_FULCRUM / powf(DT_IOP_DENOISE_PROFILE_P_FULCRUM, d->shadows);
1559 if(d->use_new_vst)
1560 {
1561 for_each_channel(c,aligned(aa,bb))
1562 {
1563 aa[c] = d->a[1] * compensate_p;
1564 bb[c] = d->b[1];
1565 }
1566 }
1567 return compensate_p;
1568}
1569#endif /* HAVE_OPENCL */
1570
1571// called by process_nlmeans_cpu
1572static inline __attribute__((always_inline)) void nlmeans_backtransform(const dt_iop_denoiseprofile_data_t *const d, float *ovoid,
1573 const dt_iop_roi_t *const roi_in, const float scale,
1574 const float compensate_p, const dt_aligned_pixel_t wb,
1575 const dt_aligned_pixel_t aa, const dt_aligned_pixel_t bb,
1576 const dt_aligned_pixel_t p)
1577{
1578 if(!d->use_new_vst)
1579 {
1580 backtransform((float *)ovoid, roi_in->width, roi_in->height, aa, bb);
1581 }
1582 else
1583 {
1584 backtransform_v2((float *)ovoid, roi_in->width, roi_in->height, d->a[1] * compensate_p, p, d->b[1], d->bias - 0.5 * logf(scale), wb);
1585 }
1586 return;
1587}
1588
1589static inline __attribute__((always_inline)) int process_nlmeans_cpu(const dt_dev_pixelpipe_t *pipe, const dt_dev_pixelpipe_iop_t *piece,
1590 const void *const ivoid, void *const ovoid, const dt_iop_roi_t *const roi_in,
1591 const dt_iop_roi_t *const roi_out,
1592 void (*denoiser)(const float *const inbuf, float *const outbuf,
1593 const dt_iop_roi_t *const roi_in, const dt_iop_roi_t *const roi_out,
1594 const dt_nlmeans_param_t *const params))
1595{
1596 // this is called for preview and full pipe separately, each with its own pixelpipe piece.
1597 // get our data struct:
1599
1600 float *restrict in;
1601 if (dt_iop_alloc_image_buffers(piece->module, roi_in, roi_out, 4 | DT_IMGSZ_INPUT, &in, 0))
1602 return 1;
1603
1604 // adjust to zoom size:
1605 const float scale = fminf(fminf(roi_in->scale, 2.0f), 1.0f);
1606 const int P = ceilf(d->radius * scale); // pixel filter size
1607 int K = d->nbhood; // nbhood
1608 const float scattering = nlmeans_scattering(pipe, &K, d, piece, scale);
1609 const float norm = nlmeans_norm(P,d);
1610 const float central_pixel_weight = d->central_pixel_weight * scale;
1611
1612 // P == 0 : this will degenerate to a (fast) bilateral filter.
1613
1614 dt_aligned_pixel_t wb; // the "unused" fourth array element enables vectorization
1616 dt_aligned_pixel_t aa, bb;
1617 const float compensate_p = nlmeans_precondition(d,piece,wb,ivoid,roi_in,scale,in,aa,bb,p);
1618
1619 const dt_aligned_pixel_t norm2 = { 1.0f, 1.0f, 1.0f, 1.0f };
1620 const dt_nlmeans_param_t params = { .scattering = scattering,
1621 .scale = scale,
1622 .luma = 1.0, //no blending
1623 .chroma = 1.0,
1624 .center_weight = central_pixel_weight,
1625 .sharpness = norm,
1626 .patch_radius = P,
1627 .search_radius = K,
1628 .decimate = 0,
1629 .norm = norm2 };
1630 denoiser(in,ovoid,roi_in,roi_out,&params);
1631
1633 nlmeans_backtransform(d,ovoid,roi_in,scale,compensate_p,wb,aa,bb,p);
1634
1636 dt_iop_alpha_copy(ivoid, ovoid, roi_out->width, roi_out->height);
1637 return 0;
1638}
1639
1640static inline __attribute__((always_inline)) int process_nlmeans(const dt_dev_pixelpipe_t *pipe, const dt_dev_pixelpipe_iop_t *piece,
1641 const void *const ivoid, void *const ovoid, const dt_iop_roi_t *const roi_in,
1642 const dt_iop_roi_t *const roi_out)
1643{
1644 return process_nlmeans_cpu(pipe, piece, ivoid, ovoid, roi_in, roi_out, nlmeans_denoise);
1645}
1646
1648static void sum_rec(const size_t npixels, const float *in, float *out)
1649{
1650 if(npixels <= 3)
1651 {
1652 for_each_channel(c,aligned(out))
1653 {
1654 out[c] = 0.0;
1655 }
1656 for(size_t i = 0; i < npixels; i++)
1657 {
1658 for_each_channel(c,aligned(in,out))
1659 {
1660 out[c] += in[i * 4 + c];
1661 }
1662 }
1663 return;
1664 }
1665
1666 const size_t npixels_first_half = npixels >> 1;
1667 const size_t npixels_second_half = npixels - npixels_first_half;
1668 sum_rec(npixels_first_half, in, out);
1669 sum_rec(npixels_second_half, in + 4U * npixels_first_half, out + 4U * npixels_first_half);
1670 for_each_channel(c,aligned(out))
1671 {
1672 out[c] += out[4U * npixels_first_half + c];
1673 }
1674}
1675
1676/* this gives (npixels-1)*V[X] */
1678static void variance_rec(const size_t npixels, const float *in, float *out, const dt_aligned_pixel_t mean)
1679{
1680 if(npixels <= 3)
1681 {
1682 for_each_channel(c,aligned(out))
1683 {
1684 out[c] = 0.0;
1685 }
1686 for(size_t i = 0; i < npixels; i++)
1687 {
1688 for_each_channel(c,aligned(in,out))
1689 {
1690 const float diff = in[i * 4 + c] - mean[c];
1691 out[c] += diff * diff;
1692 }
1693 }
1694 return;
1695 }
1696
1697 const size_t npixels_first_half = npixels >> 1;
1698 const size_t npixels_second_half = npixels - npixels_first_half;
1699 variance_rec(npixels_first_half, in, out, mean);
1700 variance_rec(npixels_second_half, in + 4U * npixels_first_half, out + 4U * npixels_first_half, mean);
1701 for_each_channel(c,aligned(out))
1702 {
1703 out[c] += out[4U * npixels_first_half + c];
1704 }
1705}
1706
1707static inline __attribute__((always_inline)) int process_variance(struct dt_iop_module_t *self, const dt_dev_pixelpipe_t *pipe,
1708 const dt_dev_pixelpipe_iop_t *piece, const void *const ivoid,
1709 void *const ovoid, const dt_iop_roi_t *const roi_in,
1710 const dt_iop_roi_t *const roi_out)
1711{
1712 const dt_iop_denoiseprofile_data_t *const d = piece->data;
1714
1715 const int width = roi_in->width, height = roi_in->height;
1716 size_t npixels = (size_t)width * height;
1717
1718 memcpy(ovoid, ivoid, sizeof(float) * 4 * npixels);
1719 if(dt_dev_pixelpipe_has_preview_output(self->dev, pipe, roi_out) || (IS_NULL_PTR(g)))
1720 {
1721 return 0;
1722 }
1723
1724 float *restrict in;
1725 if (dt_iop_alloc_image_buffers(self, roi_in, roi_out, 4 | DT_IMGSZ_INPUT, &in, 0))
1726 return 1;
1727
1728 dt_aligned_pixel_t wb; // the "unused" fourth element enables vectorization
1729 const dt_aligned_pixel_t wb_weights = { 1.0f, 1.0f, 1.0f, 0.0f };
1730 compute_wb_factors(wb,d,piece,wb_weights);
1731
1732 // adaptive p depending on white balance
1733 const dt_aligned_pixel_t p = { MAX(d->shadows - 0.1 * logf(wb[0]), 0.0f),
1734 MAX(d->shadows - 0.1 * logf(wb[1]), 0.0f),
1735 MAX(d->shadows - 0.1 * logf(wb[2]), 0.0f),
1736 0.0f };
1737
1738 // update the coeffs with strength
1739 for_each_channel(i) wb[i] *= d->strength;
1740
1741 const float compensate_p = DT_IOP_DENOISE_PROFILE_P_FULCRUM / powf(DT_IOP_DENOISE_PROFILE_P_FULCRUM, d->shadows);
1742 precondition_v2((float *)ivoid, (float *)ovoid, roi_in->width, roi_in->height, d->a[1] * compensate_p, p, d->b[1], wb);
1743
1744 float *out = (float *)ovoid;
1745 // we use out as a temporary buffer here
1746 // compute mean
1747 sum_rec(npixels, in, out);
1748 dt_aligned_pixel_t mean; // the "unused" fourth array element enables vectorization
1749 for_each_channel(c,aligned(out))
1750 {
1751 mean[c] = out[c] / npixels;
1752 }
1753 variance_rec(npixels, in, out, mean);
1754 dt_aligned_pixel_t var; // the "unused" fourth array element enables vectorization
1755 for_each_channel(c,aligned(out))
1756 {
1757 var[c] = out[c] / (npixels - 1);
1758 }
1759 g->variance_R = var[0];
1760 g->variance_G = var[1];
1761 g->variance_B = var[2];
1762
1763 memcpy(ovoid, ivoid, sizeof(float) * 4 * npixels);
1764 return 0;
1765}
1766
1767#if defined(HAVE_OPENCL) && !USE_NEW_IMPL_CL
1768static int bucket_next(unsigned int *state, unsigned int max)
1769{
1770 unsigned int current = *state;
1771 unsigned int next = (current >= max - 1 ? 0 : current + 1);
1772
1773 *state = next;
1774
1775 return next;
1776}
1777#endif
1778
1779#if defined(HAVE_OPENCL)
1780static int process_nlmeans_cl(struct dt_iop_module_t *self, const dt_dev_pixelpipe_t *pipe,
1781 const dt_dev_pixelpipe_iop_t *piece, cl_mem dev_in, cl_mem dev_out,
1782 const dt_iop_roi_t *const roi_in, const dt_iop_roi_t *const roi_out)
1783{
1786#if USE_NEW_IMPL_CL
1787 const int width = roi_in->width;
1788 const int height = roi_in->height;
1789
1790 cl_int err = -999;
1791
1792 const float scale = fminf(fminf(roi_in->scale, 2.0f), 1.0f);
1793 const int P = ceilf(d->radius * scale); // pixel filter size
1794 int K = d->nbhood; // nbhood
1795 const float scattering = nlmeans_scattering(pipe, &K, d, piece, scale);
1796 const float norm = nlmeans_norm(P,d);
1797 const float central_pixel_weight = d->central_pixel_weight * scale;
1798
1803 (void)nlmeans_precondition_cl(d,piece,wb,scale,aa,bb,p);
1804
1805 // allocate a buffer for a preconditioned copy of the image
1806 const int devid = pipe->devid;
1807 cl_mem dev_tmp = dt_opencl_alloc_device(devid, width, height, sizeof(float) * 4);
1808 if(IS_NULL_PTR(dev_tmp))
1809 {
1810 dt_print(DT_DEBUG_OPENCL, "[opencl_denoiseprofile] couldn't allocate GPU buffer\n");
1811 return FALSE;
1812 }
1813
1814 const size_t sizes[] = { ROUNDUPDWD(width, devid), ROUNDUPDHT(height, devid), 1 };
1815 const float sigma2[4] = { (bb[0] / aa[0]) * (bb[0] / aa[0]), (bb[1] / aa[1]) * (bb[1] / aa[1]),
1816 (bb[2] / aa[2]) * (bb[2] / aa[2]), 0.0f };
1817
1818 if(!d->use_new_vst)
1819 {
1820 dt_opencl_set_kernel_arg(devid, gd->kernel_denoiseprofile_precondition, 0, sizeof(cl_mem), (void *)&dev_in);
1821 dt_opencl_set_kernel_arg(devid, gd->kernel_denoiseprofile_precondition, 1, sizeof(cl_mem), (void *)&dev_tmp);
1822 dt_opencl_set_kernel_arg(devid, gd->kernel_denoiseprofile_precondition, 2, sizeof(int), (void *)&width);
1823 dt_opencl_set_kernel_arg(devid, gd->kernel_denoiseprofile_precondition, 3, sizeof(int), (void *)&height);
1824 dt_opencl_set_kernel_arg(devid, gd->kernel_denoiseprofile_precondition, 4, 4 * sizeof(float), (void *)&aa);
1825 dt_opencl_set_kernel_arg(devid, gd->kernel_denoiseprofile_precondition, 5, 4 * sizeof(float), (void *)&sigma2);
1827 }
1828 else
1829 {
1830 dt_opencl_set_kernel_arg(devid, gd->kernel_denoiseprofile_precondition_v2, 0, sizeof(cl_mem), (void *)&dev_in);
1831 dt_opencl_set_kernel_arg(devid, gd->kernel_denoiseprofile_precondition_v2, 1, sizeof(cl_mem), (void *)&dev_tmp);
1832 dt_opencl_set_kernel_arg(devid, gd->kernel_denoiseprofile_precondition_v2, 2, sizeof(int), (void *)&width);
1833 dt_opencl_set_kernel_arg(devid, gd->kernel_denoiseprofile_precondition_v2, 3, sizeof(int), (void *)&height);
1834 dt_opencl_set_kernel_arg(devid, gd->kernel_denoiseprofile_precondition_v2, 4, 4 * sizeof(float), (void *)&aa);
1835 dt_opencl_set_kernel_arg(devid, gd->kernel_denoiseprofile_precondition_v2, 5, 4 * sizeof(float), (void *)&p);
1836 dt_opencl_set_kernel_arg(devid, gd->kernel_denoiseprofile_precondition_v2, 6, 4 * sizeof(float), (void *)&bb);
1837 dt_opencl_set_kernel_arg(devid, gd->kernel_denoiseprofile_precondition_v2, 7, 4 * sizeof(float), (void *)&wb);
1839 }
1840
1841 // allocate a buffer to receive the denoised image
1842 cl_mem dev_U2 = dt_opencl_alloc_device_buffer(devid, sizeof(float) * 4 * width * height);
1843 if(IS_NULL_PTR(dev_U2)) err = -999;
1844
1845 if (err == CL_SUCCESS)
1846 {
1847 const dt_aligned_pixel_t norm2 = { 1.0f, 1.0f, 1.0f, 1.0f };
1848 const dt_nlmeans_param_t params =
1849 {
1850 .scattering = scattering,
1851 .scale = scale,
1852 .luma = 1.0f,
1853 .chroma = 1.0f,
1854 .center_weight = central_pixel_weight,
1855 .sharpness = norm,
1856 .patch_radius = P,
1857 .search_radius = K,
1858 .decimate = 0,
1859 .norm = norm2,
1860 .pipetype = pipe->type,
1861 .kernel_init = gd->kernel_denoiseprofile_init,
1862 .kernel_dist = gd->kernel_denoiseprofile_dist,
1863 .kernel_horiz = gd->kernel_denoiseprofile_horiz,
1864 .kernel_vert = gd->kernel_denoiseprofile_vert,
1865 .kernel_accu = gd->kernel_denoiseprofile_accu
1866 };
1867 err = nlmeans_denoiseprofile_cl(&params, devid, dev_tmp, dev_U2, roi_in);
1868 }
1869 if (err == CL_SUCCESS)
1870 {
1871 if(!d->use_new_vst)
1872 {
1873 dt_opencl_set_kernel_arg(devid, gd->kernel_denoiseprofile_finish, 0, sizeof(cl_mem), (void *)&dev_in);
1874 dt_opencl_set_kernel_arg(devid, gd->kernel_denoiseprofile_finish, 1, sizeof(cl_mem), (void *)&dev_U2);
1875 dt_opencl_set_kernel_arg(devid, gd->kernel_denoiseprofile_finish, 2, sizeof(cl_mem), (void *)&dev_out);
1876 dt_opencl_set_kernel_arg(devid, gd->kernel_denoiseprofile_finish, 3, sizeof(int), (void *)&width);
1877 dt_opencl_set_kernel_arg(devid, gd->kernel_denoiseprofile_finish, 4, sizeof(int), (void *)&height);
1878 dt_opencl_set_kernel_arg(devid, gd->kernel_denoiseprofile_finish, 5, 4 * sizeof(float), (void *)&aa);
1879 dt_opencl_set_kernel_arg(devid, gd->kernel_denoiseprofile_finish, 6, 4 * sizeof(float), (void *)&sigma2);
1881 }
1882 else
1883 {
1884 const float bias = d->bias - 0.5 * logf(scale);
1885 dt_opencl_set_kernel_arg(devid, gd->kernel_denoiseprofile_finish_v2, 0, sizeof(cl_mem), (void *)&dev_in);
1886 dt_opencl_set_kernel_arg(devid, gd->kernel_denoiseprofile_finish_v2, 1, sizeof(cl_mem), (void *)&dev_U2);
1887 dt_opencl_set_kernel_arg(devid, gd->kernel_denoiseprofile_finish_v2, 2, sizeof(cl_mem), (void *)&dev_out);
1888 dt_opencl_set_kernel_arg(devid, gd->kernel_denoiseprofile_finish_v2, 3, sizeof(int), (void *)&width);
1889 dt_opencl_set_kernel_arg(devid, gd->kernel_denoiseprofile_finish_v2, 4, sizeof(int), (void *)&height);
1890 dt_opencl_set_kernel_arg(devid, gd->kernel_denoiseprofile_finish_v2, 5, 4 * sizeof(float), (void *)&aa);
1891 dt_opencl_set_kernel_arg(devid, gd->kernel_denoiseprofile_finish_v2, 6, 4 * sizeof(float), (void *)&p);
1892 dt_opencl_set_kernel_arg(devid, gd->kernel_denoiseprofile_finish_v2, 7, 4 * sizeof(float), (void *)&bb);
1893 dt_opencl_set_kernel_arg(devid, gd->kernel_denoiseprofile_finish_v2, 8, sizeof(float), (void *)&bias);
1894 dt_opencl_set_kernel_arg(devid, gd->kernel_denoiseprofile_finish_v2, 9, 4 * sizeof(float), (void *)&wb);
1896 }
1897 }
1900 if (err == CL_SUCCESS)
1901 return TRUE;
1902 dt_print(DT_DEBUG_OPENCL, "[opencl_denoiseprofile] couldn't enqueue kernel! %d\n", err);
1903 return FALSE;
1904
1905#else
1906 const int width = roi_in->width;
1907 const int height = roi_in->height;
1908
1909 cl_int err = -999;
1910
1911 const float scale = fminf(fminf(roi_in->scale, 2.0f), 1.0f);
1912 const int P = ceilf(d->radius * scale); // pixel filter size
1913 int K = d->nbhood; // nbhood
1914 const float scattering = nlmeans_scattering(pipe, &K, d, piece, scale);
1915 const float norm = nlmeans_norm(P,d);
1916 const float central_pixel_weight = d->central_pixel_weight * scale;
1917
1922 (void)nlmeans_precondition_cl(d,piece,wb,scale,aa,bb,p);
1923
1924 const dt_aligned_pixel_t sigma2 = { (bb[0] / aa[0]) * (bb[0] / aa[0]), (bb[1] / aa[1]) * (bb[1] / aa[1]),
1925 (bb[2] / aa[2]) * (bb[2] / aa[2]), 0.0f };
1926
1927 const int devid = pipe->devid;
1928 cl_mem dev_tmp = dt_opencl_alloc_device(devid, width, height, sizeof(float) * 4);
1929 if(IS_NULL_PTR(dev_tmp)) goto error;
1930
1931 cl_mem dev_U2 = dt_opencl_alloc_device_buffer(devid, sizeof(float) * 4 * width * height);
1932 if(IS_NULL_PTR(dev_U2)) goto error;
1933
1934 cl_mem buckets[NUM_BUCKETS] = { NULL };
1935 unsigned int state = 0;
1936 for(int k = 0; k < NUM_BUCKETS; k++)
1937 {
1938 buckets[k] = dt_opencl_alloc_device_buffer(devid, sizeof(float) * width * height);
1939 if(buckets[k] == NULL) goto error;
1940 }
1941
1942 int hblocksize;
1944 = (dt_opencl_local_buffer_t){ .xoffset = 2 * P, .xfactor = 1, .yoffset = 0, .yfactor = 1,
1945 .cellsize = sizeof(float), .overhead = 0,
1946 .sizex = 1u << 16, .sizey = 1 };
1947
1949 hblocksize = hlocopt.sizex;
1950 else
1951 hblocksize = 1;
1952
1953 int vblocksize;
1955 = (dt_opencl_local_buffer_t){ .xoffset = 1, .xfactor = 1, .yoffset = 2 * P, .yfactor = 1,
1956 .cellsize = sizeof(float), .overhead = 0,
1957 .sizex = 1, .sizey = 1u << 16 };
1958
1960 vblocksize = vlocopt.sizey;
1961 else
1962 vblocksize = 1;
1963
1964
1965 const size_t sizes[] = { ROUNDUPDWD(width, devid), ROUNDUPDHT(height, devid), 1 };
1966 size_t sizesl[3];
1967 size_t local[3];
1968
1969 if(!d->use_new_vst)
1970 {
1971 dt_opencl_set_kernel_arg(devid, gd->kernel_denoiseprofile_precondition, 0, sizeof(cl_mem), (void *)&dev_in);
1972 dt_opencl_set_kernel_arg(devid, gd->kernel_denoiseprofile_precondition, 1, sizeof(cl_mem), (void *)&dev_tmp);
1973 dt_opencl_set_kernel_arg(devid, gd->kernel_denoiseprofile_precondition, 2, sizeof(int), (void *)&width);
1974 dt_opencl_set_kernel_arg(devid, gd->kernel_denoiseprofile_precondition, 3, sizeof(int), (void *)&height);
1975 dt_opencl_set_kernel_arg(devid, gd->kernel_denoiseprofile_precondition, 4, 4 * sizeof(float), (void *)&aa);
1976 dt_opencl_set_kernel_arg(devid, gd->kernel_denoiseprofile_precondition, 5, 4 * sizeof(float), (void *)&sigma2);
1978 if(err != CL_SUCCESS) goto error;
1979 }
1980 else
1981 {
1982 dt_opencl_set_kernel_arg(devid, gd->kernel_denoiseprofile_precondition_v2, 0, sizeof(cl_mem), (void *)&dev_in);
1983 dt_opencl_set_kernel_arg(devid, gd->kernel_denoiseprofile_precondition_v2, 1, sizeof(cl_mem), (void *)&dev_tmp);
1984 dt_opencl_set_kernel_arg(devid, gd->kernel_denoiseprofile_precondition_v2, 2, sizeof(int), (void *)&width);
1985 dt_opencl_set_kernel_arg(devid, gd->kernel_denoiseprofile_precondition_v2, 3, sizeof(int), (void *)&height);
1986 dt_opencl_set_kernel_arg(devid, gd->kernel_denoiseprofile_precondition_v2, 4, 4 * sizeof(float), (void *)&aa);
1987 dt_opencl_set_kernel_arg(devid, gd->kernel_denoiseprofile_precondition_v2, 5, 4 * sizeof(float), (void *)&p);
1988 dt_opencl_set_kernel_arg(devid, gd->kernel_denoiseprofile_precondition_v2, 6, 4 * sizeof(float), (void *)&bb);
1989 dt_opencl_set_kernel_arg(devid, gd->kernel_denoiseprofile_precondition_v2, 7, 4 * sizeof(float), (void *)&wb);
1991 if(err != CL_SUCCESS) goto error;
1992 }
1993
1994 dt_opencl_set_kernel_arg(devid, gd->kernel_denoiseprofile_init, 0, sizeof(cl_mem), (void *)&dev_U2);
1995 dt_opencl_set_kernel_arg(devid, gd->kernel_denoiseprofile_init, 1, sizeof(int), (void *)&width);
1996 dt_opencl_set_kernel_arg(devid, gd->kernel_denoiseprofile_init, 2, sizeof(int), (void *)&height);
1998 if(err != CL_SUCCESS) goto error;
1999
2000 const size_t bwidth = ROUNDUP(width, hblocksize);
2001 const size_t bheight = ROUNDUP(height, vblocksize);
2002
2003 for(int kj_index = -K; kj_index <= 0; kj_index++)
2004 {
2005 for(int ki_index = -K; ki_index <= K; ki_index++)
2006 {
2007 // This formula is made for:
2008 // - ensuring that j = kj_index and i = ki_index when d->scattering is 0
2009 // - ensuring that no patch can appear twice (provided that d->scattering is in 0,1 range)
2010 // - avoiding grid artifacts by trying to take patches on various lines and columns
2011 const int abs_kj = abs(kj_index);
2012 const int abs_ki = abs(ki_index);
2013 const int j = scale * ((abs_kj * abs_kj * abs_kj + 7.0 * abs_kj * sqrt(abs_ki)) * sign(kj_index) * scattering / 6.0 + kj_index);
2014 const int i = scale * ((abs_ki * abs_ki * abs_ki + 7.0 * abs_ki * sqrt(abs_kj)) * sign(ki_index) * scattering / 6.0 + ki_index);
2015 int q[2] = { i, j };
2016
2017 cl_mem dev_U4 = buckets[bucket_next(&state, NUM_BUCKETS)];
2018 dt_opencl_set_kernel_arg(devid, gd->kernel_denoiseprofile_dist, 0, sizeof(cl_mem), (void *)&dev_tmp);
2019 dt_opencl_set_kernel_arg(devid, gd->kernel_denoiseprofile_dist, 1, sizeof(cl_mem), (void *)&dev_U4);
2020 dt_opencl_set_kernel_arg(devid, gd->kernel_denoiseprofile_dist, 2, sizeof(int), (void *)&width);
2021 dt_opencl_set_kernel_arg(devid, gd->kernel_denoiseprofile_dist, 3, sizeof(int), (void *)&height);
2022 dt_opencl_set_kernel_arg(devid, gd->kernel_denoiseprofile_dist, 4, 2 * sizeof(int), (void *)&q);
2024 if(err != CL_SUCCESS) goto error;
2025
2026 sizesl[0] = bwidth;
2027 sizesl[1] = ROUNDUPDHT(height, devid);
2028 sizesl[2] = 1;
2029 local[0] = hblocksize;
2030 local[1] = 1;
2031 local[2] = 1;
2032 cl_mem dev_U4_t = buckets[bucket_next(&state, NUM_BUCKETS)];
2033 dt_opencl_set_kernel_arg(devid, gd->kernel_denoiseprofile_horiz, 0, sizeof(cl_mem), (void *)&dev_U4);
2034 dt_opencl_set_kernel_arg(devid, gd->kernel_denoiseprofile_horiz, 1, sizeof(cl_mem), (void *)&dev_U4_t);
2035 dt_opencl_set_kernel_arg(devid, gd->kernel_denoiseprofile_horiz, 2, sizeof(int), (void *)&width);
2036 dt_opencl_set_kernel_arg(devid, gd->kernel_denoiseprofile_horiz, 3, sizeof(int), (void *)&height);
2037 dt_opencl_set_kernel_arg(devid, gd->kernel_denoiseprofile_horiz, 4, 2 * sizeof(int), (void *)&q);
2038 dt_opencl_set_kernel_arg(devid, gd->kernel_denoiseprofile_horiz, 5, sizeof(int), (void *)&P);
2039 dt_opencl_set_kernel_arg(devid, gd->kernel_denoiseprofile_horiz, 6, sizeof(float) * (hblocksize + 2 * P),
2040 NULL);
2042 if(err != CL_SUCCESS) goto error;
2043
2044 sizesl[0] = ROUNDUPDWD(width, devid);
2045 sizesl[1] = bheight;
2046 sizesl[2] = 1;
2047 local[0] = 1;
2048 local[1] = vblocksize;
2049 local[2] = 1;
2050 cl_mem dev_U4_tt = buckets[bucket_next(&state, NUM_BUCKETS)];
2051 dt_opencl_set_kernel_arg(devid, gd->kernel_denoiseprofile_vert, 0, sizeof(cl_mem), (void *)&dev_U4_t);
2052 dt_opencl_set_kernel_arg(devid, gd->kernel_denoiseprofile_vert, 1, sizeof(cl_mem), (void *)&dev_U4_tt);
2053 dt_opencl_set_kernel_arg(devid, gd->kernel_denoiseprofile_vert, 2, sizeof(int), (void *)&width);
2054 dt_opencl_set_kernel_arg(devid, gd->kernel_denoiseprofile_vert, 3, sizeof(int), (void *)&height);
2055 dt_opencl_set_kernel_arg(devid, gd->kernel_denoiseprofile_vert, 4, 2 * sizeof(int), (void *)&q);
2056 dt_opencl_set_kernel_arg(devid, gd->kernel_denoiseprofile_vert, 5, sizeof(int), (void *)&P);
2057 dt_opencl_set_kernel_arg(devid, gd->kernel_denoiseprofile_vert, 6, sizeof(float), (void *)&norm);
2058 dt_opencl_set_kernel_arg(devid, gd->kernel_denoiseprofile_vert, 7, sizeof(float) * (vblocksize + 2 * P),
2059 NULL);
2060 dt_opencl_set_kernel_arg(devid, gd->kernel_denoiseprofile_vert, 8, sizeof(float),
2061 (void *)&central_pixel_weight);
2062 dt_opencl_set_kernel_arg(devid, gd->kernel_denoiseprofile_vert, 9, sizeof(cl_mem), ((void *)&dev_U4));
2064 if(err != CL_SUCCESS) goto error;
2065
2066
2067 dt_opencl_set_kernel_arg(devid, gd->kernel_denoiseprofile_accu, 0, sizeof(cl_mem), (void *)&dev_tmp);
2068 dt_opencl_set_kernel_arg(devid, gd->kernel_denoiseprofile_accu, 1, sizeof(cl_mem), (void *)&dev_U2);
2069 dt_opencl_set_kernel_arg(devid, gd->kernel_denoiseprofile_accu, 2, sizeof(cl_mem), (void *)&dev_U4_tt);
2070 dt_opencl_set_kernel_arg(devid, gd->kernel_denoiseprofile_accu, 3, sizeof(int), (void *)&width);
2071 dt_opencl_set_kernel_arg(devid, gd->kernel_denoiseprofile_accu, 4, sizeof(int), (void *)&height);
2072 dt_opencl_set_kernel_arg(devid, gd->kernel_denoiseprofile_accu, 5, 2 * sizeof(int), (void *)&q);
2074 if(err != CL_SUCCESS) goto error;
2075 }
2076 }
2077
2078 if(!d->use_new_vst)
2079 {
2080 dt_opencl_set_kernel_arg(devid, gd->kernel_denoiseprofile_finish, 0, sizeof(cl_mem), (void *)&dev_in);
2081 dt_opencl_set_kernel_arg(devid, gd->kernel_denoiseprofile_finish, 1, sizeof(cl_mem), (void *)&dev_U2);
2082 dt_opencl_set_kernel_arg(devid, gd->kernel_denoiseprofile_finish, 2, sizeof(cl_mem), (void *)&dev_out);
2083 dt_opencl_set_kernel_arg(devid, gd->kernel_denoiseprofile_finish, 3, sizeof(int), (void *)&width);
2084 dt_opencl_set_kernel_arg(devid, gd->kernel_denoiseprofile_finish, 4, sizeof(int), (void *)&height);
2085 dt_opencl_set_kernel_arg(devid, gd->kernel_denoiseprofile_finish, 5, 4 * sizeof(float), (void *)&aa);
2086 dt_opencl_set_kernel_arg(devid, gd->kernel_denoiseprofile_finish, 6, 4 * sizeof(float), (void *)&sigma2);
2088 if(err != CL_SUCCESS) goto error;
2089 }
2090 else
2091 {
2092 const float bias = d->bias - 0.5 * logf(scale);
2093 dt_opencl_set_kernel_arg(devid, gd->kernel_denoiseprofile_finish_v2, 0, sizeof(cl_mem), (void *)&dev_in);
2094 dt_opencl_set_kernel_arg(devid, gd->kernel_denoiseprofile_finish_v2, 1, sizeof(cl_mem), (void *)&dev_U2);
2095 dt_opencl_set_kernel_arg(devid, gd->kernel_denoiseprofile_finish_v2, 2, sizeof(cl_mem), (void *)&dev_out);
2096 dt_opencl_set_kernel_arg(devid, gd->kernel_denoiseprofile_finish_v2, 3, sizeof(int), (void *)&width);
2097 dt_opencl_set_kernel_arg(devid, gd->kernel_denoiseprofile_finish_v2, 4, sizeof(int), (void *)&height);
2098 dt_opencl_set_kernel_arg(devid, gd->kernel_denoiseprofile_finish_v2, 5, 4 * sizeof(float), (void *)&aa);
2099 dt_opencl_set_kernel_arg(devid, gd->kernel_denoiseprofile_finish_v2, 6, 4 * sizeof(float), (void *)&p);
2100 dt_opencl_set_kernel_arg(devid, gd->kernel_denoiseprofile_finish_v2, 7, 4 * sizeof(float), (void *)&bb);
2101 dt_opencl_set_kernel_arg(devid, gd->kernel_denoiseprofile_finish_v2, 8, sizeof(float), (void *)&bias);
2102 dt_opencl_set_kernel_arg(devid, gd->kernel_denoiseprofile_finish_v2, 9, 4 * sizeof(float), (void *)&wb);
2104 if(err != CL_SUCCESS) goto error;
2105 }
2106
2107 for(int k = 0; k < NUM_BUCKETS; k++)
2108 {
2110 }
2113 return TRUE;
2114
2115error:
2116 for(int k = 0; k < NUM_BUCKETS; k++)
2117 {
2119 }
2122 dt_print(DT_DEBUG_OPENCL, "[opencl_denoiseprofile] couldn't enqueue kernel! %d\n", err);
2123 return FALSE;
2124#endif /* USE_NEW_IMPL_CL */
2125}
2126
2127
2128static int process_wavelets_cl(struct dt_iop_module_t *self, const dt_dev_pixelpipe_t *pipe,
2129 const dt_dev_pixelpipe_iop_t *piece, cl_mem dev_in, cl_mem dev_out,
2130 const dt_iop_roi_t *const roi_in, const dt_iop_roi_t *const roi_out)
2131{
2134
2135 const int max_max_scale = DT_IOP_DENOISE_PROFILE_BANDS; // hard limit
2136 int max_scale = 0;
2137 const float scale = fminf(roi_in->scale, 1.0f);
2138 // largest desired filter on input buffer (20% of input dim)
2139 const float supp0
2140 = MIN(2 * (2u << (max_max_scale - 1)) + 1,
2141 MAX(piece->buf_in.height, piece->buf_in.width) * 0.2f);
2142 const float i0 = dt_log2f((supp0 - 1.0f) * .5f);
2143 for(; max_scale < max_max_scale; max_scale++)
2144 {
2145 // actual filter support on scaled buffer
2146 const float supp = 2 * (2u << max_scale) + 1;
2147 // approximates this filter size on unscaled input image:
2148 const float supp_in = supp * (1.0f / scale);
2149 const float i_in = dt_log2f((supp_in - 1) * .5f) - 1.0f;
2150 // i_in = max_scale .. .. .. 0
2151 const float t = 1.0f - (i_in + .5f) / i0;
2152 if(t < 0.0f) break;
2153 }
2154
2155 const int devid = pipe->devid;
2156 cl_int err = -999;
2157 const int width = roi_in->width;
2158 const int height = roi_in->height;
2159 const size_t npixels = (size_t)width * height;
2160
2161 cl_mem dev_tmp = NULL;
2162 cl_mem dev_buf1 = NULL;
2163 cl_mem dev_buf2 = NULL;
2164 cl_mem dev_m = NULL;
2165 cl_mem dev_r = NULL;
2166 cl_mem dev_filter = NULL;
2167 cl_mem *dev_detail = calloc(max_max_scale, sizeof(cl_mem));
2168 float *sumsum = NULL;
2169
2170 // corner case of extremely small image. this is not really likely to happen but would cause issues later
2171 // when we divide by (n-1). so let's be prepared
2172 if(npixels < 2)
2173 {
2174 // copy original input from dev_in -> dev_out
2175 size_t origin[] = { 0, 0, 0 };
2176 size_t region[] = { width, height, 1 };
2177 err = dt_opencl_enqueue_copy_image(devid, dev_in, dev_out, origin, origin, region);
2178 if(err != CL_SUCCESS) goto error;
2179 dt_free(dev_detail);
2180 return TRUE;
2181 }
2182
2184 = (dt_opencl_local_buffer_t){ .xoffset = 0, .xfactor = 1, .yoffset = 0, .yfactor = 1,
2185 .cellsize = 4 * sizeof(float), .overhead = 0,
2186 .sizex = 1u << 4, .sizey = 1u << 4 };
2187
2189 goto error;
2190
2191 const size_t bwidth = ROUNDUP(width, flocopt.sizex);
2192 const size_t bheight = ROUNDUP(height, flocopt.sizey);
2193
2194 const int bufsize = (bwidth / flocopt.sizex) * (bheight / flocopt.sizey);
2195
2197 = (dt_opencl_local_buffer_t){ .xoffset = 0, .xfactor = 1, .yoffset = 0, .yfactor = 1,
2198 .cellsize = 4 * sizeof(float), .overhead = 0,
2199 .sizex = 1u << 16, .sizey = 1 };
2200
2202 goto error;
2203
2204 const int reducesize = MIN(REDUCESIZE, ROUNDUP(bufsize, slocopt.sizex) / slocopt.sizex);
2205
2206 dev_m = dt_opencl_alloc_device_buffer(devid, sizeof(float) * 4 * bufsize);
2207 if(IS_NULL_PTR(dev_m)) goto error;
2208
2209 dev_r = dt_opencl_alloc_device_buffer(devid, sizeof(float) * 4 * reducesize);
2210 if(IS_NULL_PTR(dev_r)) goto error;
2211
2212 sumsum = (float *)dt_pixelpipe_cache_alloc_align(sizeof(float) * 4 * (size_t)reducesize, pipe);
2213 if(IS_NULL_PTR(sumsum)) goto error;
2214 sumsum = (float *)__builtin_assume_aligned(sumsum, DT_CACHELINE_BYTES);
2215
2216 dev_tmp = dt_opencl_alloc_device(devid, width, height, sizeof(float) * 4);
2217 if(IS_NULL_PTR(dev_tmp)) goto error;
2218
2219 float m[] = { 0.0625f, 0.25f, 0.375f, 0.25f, 0.0625f }; // 1/16, 4/16, 6/16, 4/16, 1/16
2220 float mm[5][5];
2221 for(int j = 0; j < 5; j++)
2222 for(int i = 0; i < 5; i++) mm[j][i] = m[i] * m[j];
2223
2224 dev_filter = dt_opencl_copy_host_to_device_constant(devid, sizeof(float) * 25, mm);
2225 if(IS_NULL_PTR(dev_filter)) goto error;
2226
2227 for(int k = 0; k < max_scale; k++)
2228 {
2229 dev_detail[k] = dt_opencl_alloc_device(devid, width, height, sizeof(float) * 4);
2230 if(dev_detail[k] == NULL) goto error;
2231 }
2232
2233 dt_aligned_pixel_t wb; // the "unused" fourth element enables vectorization
2234 const dt_aligned_pixel_t wb_weights = { 2.0f, 1.0f, 2.0f, 0.0f };
2235 compute_wb_factors(wb,d,piece,wb_weights);
2236 wb[3] = 0.0f;
2237
2238 // adaptive p depending on white balance
2239 const dt_aligned_pixel_t p = { MAX(d->shadows + 0.1 * logf(scale / wb[0]), 0.0f),
2240 MAX(d->shadows + 0.1 * logf(scale / wb[1]), 0.0f),
2241 MAX(d->shadows + 0.1 * logf(scale / wb[2]), 0.0f), 1.0f};
2242
2243 // conversion to Y0U0V0 space as defined in Secrets of image denoising cuisine
2244 dt_colormatrix_t toY0U0V0_tmp = { { 1.0f/3.0f, 1.0f/3.0f, 1.0f/3.0f },
2245 { 0.5f, 0.0f, -0.5f },
2246 { 0.25f, -0.5f, 0.25f } };
2247 dt_colormatrix_t toRGB_tmp = { { 0.0f, 0.0f, 0.0f }, // "unused" fourth element enables vectorization
2248 { 0.0f, 0.0f, 0.0f },
2249 { 0.0f, 0.0f, 0.0f } };
2250 set_up_conversion_matrices(toY0U0V0_tmp, toRGB_tmp, wb);
2251
2252 // more strength in Y0U0V0 in order to get a similar smoothing as in other modes
2253 // otherwise, result was much less denoised in Y0U0V0 mode.
2254 const float compensate_strength = (d->wavelet_color_mode == MODE_RGB) ? 1.0f : 2.5f;
2255
2256 // update the coeffs with strength and scale
2257 float toY0U0V0[9]; //TODO: change OpenCL kernels to use 3x4 matrices
2258 float toRGB[9] ;
2259 for(size_t k = 0; k < 3; k++)
2260 for(size_t c = 0; c < 3; c++)
2261 //(we can't use for_each_channel here because it can iterate over four elements)
2262 {
2263 toRGB[3*k+c] = toRGB_tmp[k][c] * d->strength * compensate_strength * scale;
2264 toY0U0V0[3*k+c] = toY0U0V0_tmp[k][c] / (d->strength * compensate_strength * scale);
2265 }
2266
2267 // update the coeffs with strength and scale
2268 for_each_channel(i) wb[i] *= d->strength * compensate_strength * scale;
2269
2270 dt_aligned_pixel_t aa = { d->a[1] * wb[0], d->a[1] * wb[1], d->a[1] * wb[2], 1.0f };
2271 dt_aligned_pixel_t bb = { d->b[1] * wb[0], d->b[1] * wb[1], d->b[1] * wb[2], 1.0f };
2272 const dt_aligned_pixel_t sigma2 = { (bb[0] / aa[0]) * (bb[0] / aa[0]), (bb[1] / aa[1]) * (bb[1] / aa[1]),
2273 (bb[2] / aa[2]) * (bb[2] / aa[2]), 0.0f };
2274 const float compensate_p = DT_IOP_DENOISE_PROFILE_P_FULCRUM / powf(DT_IOP_DENOISE_PROFILE_P_FULCRUM, d->shadows);
2275 if(d->use_new_vst)
2276 {
2278 {
2279 aa[c] = d->a[1] * compensate_p;
2280 bb[c] = d->b[1];
2281 }
2282 }
2283
2284 size_t sizes[] = { ROUNDUPDWD(width, devid), ROUNDUPDHT(height, devid), 1 };
2285
2286 if(!d->use_new_vst)
2287 {
2288 dt_opencl_set_kernel_arg(devid, gd->kernel_denoiseprofile_precondition, 0, sizeof(cl_mem), (void *)&dev_in);
2289 dt_opencl_set_kernel_arg(devid, gd->kernel_denoiseprofile_precondition, 1, sizeof(cl_mem), (void *)&dev_out);
2290 dt_opencl_set_kernel_arg(devid, gd->kernel_denoiseprofile_precondition, 2, sizeof(int), (void *)&width);
2291 dt_opencl_set_kernel_arg(devid, gd->kernel_denoiseprofile_precondition, 3, sizeof(int), (void *)&height);
2292 dt_opencl_set_kernel_arg(devid, gd->kernel_denoiseprofile_precondition, 4, 4 * sizeof(float), (void *)&aa);
2293 dt_opencl_set_kernel_arg(devid, gd->kernel_denoiseprofile_precondition, 5, 4 * sizeof(float), (void *)&sigma2);
2295 if(err != CL_SUCCESS) goto error;
2296 }
2297 else if(d->wavelet_color_mode == MODE_RGB)
2298 {
2299 dt_opencl_set_kernel_arg(devid, gd->kernel_denoiseprofile_precondition_v2, 0, sizeof(cl_mem), (void *)&dev_in);
2300 dt_opencl_set_kernel_arg(devid, gd->kernel_denoiseprofile_precondition_v2, 1, sizeof(cl_mem), (void *)&dev_out);
2301 dt_opencl_set_kernel_arg(devid, gd->kernel_denoiseprofile_precondition_v2, 2, sizeof(int), (void *)&width);
2302 dt_opencl_set_kernel_arg(devid, gd->kernel_denoiseprofile_precondition_v2, 3, sizeof(int), (void *)&height);
2303 dt_opencl_set_kernel_arg(devid, gd->kernel_denoiseprofile_precondition_v2, 4, 4 * sizeof(float), (void *)&aa);
2304 dt_opencl_set_kernel_arg(devid, gd->kernel_denoiseprofile_precondition_v2, 5, 4 * sizeof(float), (void *)&p);
2305 dt_opencl_set_kernel_arg(devid, gd->kernel_denoiseprofile_precondition_v2, 6, 4 * sizeof(float), (void *)&bb);
2306 dt_opencl_set_kernel_arg(devid, gd->kernel_denoiseprofile_precondition_v2, 7, 4 * sizeof(float), (void *)&wb);
2308 if(err != CL_SUCCESS) goto error;
2309 }
2310 else
2311 {
2312 cl_mem dev_Y0U0V0 = NULL;
2313 dev_Y0U0V0 = dt_opencl_copy_host_to_device_constant(devid, sizeof(float) * 9, toY0U0V0);
2314 if(!IS_NULL_PTR(dev_Y0U0V0))
2315 {
2316 dt_opencl_set_kernel_arg(devid, gd->kernel_denoiseprofile_precondition_Y0U0V0, 0, sizeof(cl_mem), (void *)&dev_in);
2317 dt_opencl_set_kernel_arg(devid, gd->kernel_denoiseprofile_precondition_Y0U0V0, 1, sizeof(cl_mem), (void *)&dev_out);
2318 dt_opencl_set_kernel_arg(devid, gd->kernel_denoiseprofile_precondition_Y0U0V0, 2, sizeof(int), (void *)&width);
2320 dt_opencl_set_kernel_arg(devid, gd->kernel_denoiseprofile_precondition_Y0U0V0, 4, 4 * sizeof(float), (void *)&aa);
2321 dt_opencl_set_kernel_arg(devid, gd->kernel_denoiseprofile_precondition_Y0U0V0, 5, 4 * sizeof(float), (void *)&p);
2322 dt_opencl_set_kernel_arg(devid, gd->kernel_denoiseprofile_precondition_Y0U0V0, 6, 4 * sizeof(float), (void *)&bb);
2323 dt_opencl_set_kernel_arg(devid, gd->kernel_denoiseprofile_precondition_Y0U0V0, 7, sizeof(cl_mem), (void *)&dev_Y0U0V0);
2325 dt_opencl_release_mem_object(dev_Y0U0V0);
2326 if(err != CL_SUCCESS) goto error;
2327 }
2328 else
2329 {
2330 dt_opencl_release_mem_object(dev_Y0U0V0);
2331 goto error;
2332 }
2333 }
2334
2335 dev_buf1 = dev_out;
2336 dev_buf2 = dev_tmp;
2337
2338 /* decompose image into detail scales and coarse */
2339 for(int s = 0; s < max_scale; s++)
2340 {
2341 const float sigma = 1.0f;
2342 const float varf = sqrtf(2.0f + 2.0f * 4.0f * 4.0f + 6.0f * 6.0f) / 16.0f; // about 0.5
2343 const float sigma_band = powf(varf, s) * sigma;
2344 const float inv_sigma2 = 1.0f / (sigma_band * sigma_band);
2345
2346 dt_opencl_set_kernel_arg(devid, gd->kernel_denoiseprofile_decompose, 0, sizeof(cl_mem), (void *)&dev_buf1);
2347 dt_opencl_set_kernel_arg(devid, gd->kernel_denoiseprofile_decompose, 1, sizeof(cl_mem), (void *)&dev_buf2);
2348 dt_opencl_set_kernel_arg(devid, gd->kernel_denoiseprofile_decompose, 2, sizeof(cl_mem),
2349 (void *)&dev_detail[s]);
2350 dt_opencl_set_kernel_arg(devid, gd->kernel_denoiseprofile_decompose, 3, sizeof(int), (void *)&width);
2351 dt_opencl_set_kernel_arg(devid, gd->kernel_denoiseprofile_decompose, 4, sizeof(int), (void *)&height);
2352 dt_opencl_set_kernel_arg(devid, gd->kernel_denoiseprofile_decompose, 5, sizeof(unsigned int),
2353 (void *)&s);
2354 dt_opencl_set_kernel_arg(devid, gd->kernel_denoiseprofile_decompose, 6, sizeof(float),
2355 (void *)&inv_sigma2);
2356 dt_opencl_set_kernel_arg(devid, gd->kernel_denoiseprofile_decompose, 7, sizeof(cl_mem),
2357 (void *)&dev_filter);
2359 if(err != CL_SUCCESS) goto error;
2360
2361 // swap buffers
2362 cl_mem dev_buf3 = dev_buf2;
2363 dev_buf2 = dev_buf1;
2364 dev_buf1 = dev_buf3;
2365 }
2366
2367 /* now synthesize again */
2368 for(int s = max_scale - 1; s >= 0; s--)
2369 {
2370 // variance stabilizing transform maps sigma to unity.
2371 const float sigma = 1.0f;
2372 // it is then transformed by wavelet scales via the 5 tap a-trous filter:
2373 const float varf = sqrtf(2.0f + 2.0f * 4.0f * 4.0f + 6.0f * 6.0f) / 16.0f; // about 0.5
2374 const float sigma_band = powf(varf, s) * sigma;
2375
2376 // determine thrs as bayesshrink
2377 dt_aligned_pixel_t sum_y2 = { 0.0f };
2378
2379 size_t lsizes[3];
2380 size_t llocal[3];
2381
2382 lsizes[0] = bwidth;
2383 lsizes[1] = bheight;
2384 lsizes[2] = 1;
2385 llocal[0] = flocopt.sizex;
2386 llocal[1] = flocopt.sizey;
2387 llocal[2] = 1;
2389 &(dev_detail[s]));
2392 dt_opencl_set_kernel_arg(devid, gd->kernel_denoiseprofile_reduce_first, 3, sizeof(cl_mem), &dev_m);
2394 sizeof(float) * 4 * flocopt.sizex * flocopt.sizey, NULL);
2396 llocal);
2397 if(err != CL_SUCCESS) goto error;
2398
2399
2400 lsizes[0] = (size_t)reducesize * slocopt.sizex;
2401 lsizes[1] = 1;
2402 lsizes[2] = 1;
2403 llocal[0] = slocopt.sizex;
2404 llocal[1] = 1;
2405 llocal[2] = 1;
2406 dt_opencl_set_kernel_arg(devid, gd->kernel_denoiseprofile_reduce_second, 0, sizeof(cl_mem), &dev_m);
2407 dt_opencl_set_kernel_arg(devid, gd->kernel_denoiseprofile_reduce_second, 1, sizeof(cl_mem), &dev_r);
2408 dt_opencl_set_kernel_arg(devid, gd->kernel_denoiseprofile_reduce_second, 2, sizeof(int), &bufsize);
2409 dt_opencl_set_kernel_arg(devid, gd->kernel_denoiseprofile_reduce_second, 3, sizeof(float) * 4 * slocopt.sizex,
2410 NULL);
2412 llocal);
2413 if(err != CL_SUCCESS) goto error;
2414
2415 err = dt_opencl_read_buffer_from_device(devid, (void *)sumsum, dev_r, 0,
2416 sizeof(float) * 4 * reducesize, CL_TRUE);
2417 if(err != CL_SUCCESS)
2418 goto error;
2419
2420 for(int k = 0; k < reducesize; k++)
2421 {
2423 {
2424 sum_y2[c] += sumsum[4 * k + c];
2425 }
2426 }
2427
2428 const float sb2 = sigma_band * sigma_band;
2429 const dt_aligned_pixel_t var_y = { sum_y2[0] / (npixels - 1.0f), sum_y2[1] / (npixels - 1.0f),
2430 sum_y2[2] / (npixels - 1.0f), 0.0f };
2431 const dt_aligned_pixel_t std_x = { sqrtf(MAX(1e-6f, var_y[0] - sb2)), sqrtf(MAX(1e-6f, var_y[1] - sb2)),
2432 sqrtf(MAX(1e-6f, var_y[2] - sb2)), 1.0f };
2433 // add 8.0 here because it seemed a little weak
2434 dt_aligned_pixel_t adjt = { 8.0f, 8.0f, 8.0f, 0.0f };
2435
2436 const int offset_scale = DT_IOP_DENOISE_PROFILE_BANDS - max_scale;
2437 const int band_index = DT_IOP_DENOISE_PROFILE_BANDS - (s + offset_scale + 1);
2438
2439 if(d->wavelet_color_mode == MODE_RGB)
2440 {
2441 // current scale number is s+offset_scale
2442 // for instance, largest s is DT_IOP_DENOISE_PROFILE_BANDS
2443 // max_scale only indicates the number of scales to process at THIS
2444 // zoom level, it does NOT corresponds to the the maximum number of scales.
2445 // in other words, max_s is the maximum number of VISIBLE scales.
2446 // That is why we have this "s+offset_scale"
2447 float band_force_exp_2 = d->force[DT_DENOISE_PROFILE_ALL][band_index];
2448 band_force_exp_2 *= band_force_exp_2;
2449 band_force_exp_2 *= 4;
2451 {
2452 adjt[ch] *= band_force_exp_2;
2453 }
2454 band_force_exp_2 = d->force[DT_DENOISE_PROFILE_R][band_index];
2455 band_force_exp_2 *= band_force_exp_2;
2456 band_force_exp_2 *= 4;
2457 adjt[0] *= band_force_exp_2;
2458 band_force_exp_2 = d->force[DT_DENOISE_PROFILE_G][band_index];
2459 band_force_exp_2 *= band_force_exp_2;
2460 band_force_exp_2 *= 4;
2461 adjt[1] *= band_force_exp_2;
2462 band_force_exp_2 = d->force[DT_DENOISE_PROFILE_B][band_index];
2463 band_force_exp_2 *= band_force_exp_2;
2464 band_force_exp_2 *= 4;
2465 adjt[2] *= band_force_exp_2;
2466 }
2467 else
2468 {
2469 float band_force_exp_2 = d->force[DT_DENOISE_PROFILE_Y0][band_index];
2470 band_force_exp_2 *= band_force_exp_2;
2471 band_force_exp_2 *= 4;
2472 adjt[0] *= band_force_exp_2;
2473 band_force_exp_2 = d->force[DT_DENOISE_PROFILE_U0V0][band_index];
2474 band_force_exp_2 *= band_force_exp_2;
2475 band_force_exp_2 *= 4;
2476 adjt[1] *= band_force_exp_2;
2477 adjt[2] *= band_force_exp_2;
2478 }
2479
2480 const dt_aligned_pixel_t thrs = { adjt[0] * sb2 / std_x[0], adjt[1] * sb2 / std_x[1],
2481 adjt[2] * sb2 / std_x[2], 0.0f };
2482 // fprintf(stderr, "scale %d thrs %f %f %f\n", s, thrs[0], thrs[1], thrs[2]);
2483
2484 const dt_aligned_pixel_t boost = { 1.0f, 1.0f, 1.0f, 1.0f };
2485
2486 dt_opencl_set_kernel_arg(devid, gd->kernel_denoiseprofile_synthesize, 0, sizeof(cl_mem),
2487 (void *)&dev_buf1);
2488 dt_opencl_set_kernel_arg(devid, gd->kernel_denoiseprofile_synthesize, 1, sizeof(cl_mem),
2489 (void *)&dev_detail[s]);
2490 dt_opencl_set_kernel_arg(devid, gd->kernel_denoiseprofile_synthesize, 2, sizeof(cl_mem),
2491 (void *)&dev_buf2);
2492 dt_opencl_set_kernel_arg(devid, gd->kernel_denoiseprofile_synthesize, 3, sizeof(int), (void *)&width);
2493 dt_opencl_set_kernel_arg(devid, gd->kernel_denoiseprofile_synthesize, 4, sizeof(int), (void *)&height);
2494 dt_opencl_set_kernel_arg(devid, gd->kernel_denoiseprofile_synthesize, 5, sizeof(float), (void *)&thrs[0]);
2495 dt_opencl_set_kernel_arg(devid, gd->kernel_denoiseprofile_synthesize, 6, sizeof(float), (void *)&thrs[1]);
2496 dt_opencl_set_kernel_arg(devid, gd->kernel_denoiseprofile_synthesize, 7, sizeof(float), (void *)&thrs[2]);
2497 dt_opencl_set_kernel_arg(devid, gd->kernel_denoiseprofile_synthesize, 8, sizeof(float), (void *)&thrs[3]);
2498 dt_opencl_set_kernel_arg(devid, gd->kernel_denoiseprofile_synthesize, 9, sizeof(float), (void *)&boost[0]);
2499 dt_opencl_set_kernel_arg(devid, gd->kernel_denoiseprofile_synthesize, 10, sizeof(float),
2500 (void *)&boost[1]);
2501 dt_opencl_set_kernel_arg(devid, gd->kernel_denoiseprofile_synthesize, 11, sizeof(float),
2502 (void *)&boost[2]);
2503 dt_opencl_set_kernel_arg(devid, gd->kernel_denoiseprofile_synthesize, 12, sizeof(float),
2504 (void *)&boost[3]);
2506 if(err != CL_SUCCESS) goto error;
2507
2508 // swap buffers
2509 cl_mem dev_buf3 = dev_buf2;
2510 dev_buf2 = dev_buf1;
2511 dev_buf1 = dev_buf3;
2512 }
2513
2514 // copy output of last run of synthesize kernel to dev_tmp (if not already there)
2515 // note: we need to take swap of buffers into account, so current output lies in dev_buf1
2516 if(dev_buf1 != dev_tmp)
2517 {
2518 size_t origin[] = { 0, 0, 0 };
2519 size_t region[] = { width, height, 1 };
2520 err = dt_opencl_enqueue_copy_image(devid, dev_buf1, dev_tmp, origin, origin, region);
2521 if(err != CL_SUCCESS) goto error;
2522 }
2523
2524 if(!d->use_new_vst)
2525 {
2526 dt_opencl_set_kernel_arg(devid, gd->kernel_denoiseprofile_backtransform, 0, sizeof(cl_mem), (void *)&dev_tmp);
2527 dt_opencl_set_kernel_arg(devid, gd->kernel_denoiseprofile_backtransform, 1, sizeof(cl_mem), (void *)&dev_out);
2528 dt_opencl_set_kernel_arg(devid, gd->kernel_denoiseprofile_backtransform, 2, sizeof(int), (void *)&width);
2529 dt_opencl_set_kernel_arg(devid, gd->kernel_denoiseprofile_backtransform, 3, sizeof(int), (void *)&height);
2530 dt_opencl_set_kernel_arg(devid, gd->kernel_denoiseprofile_backtransform, 4, 4 * sizeof(float), (void *)&aa);
2531 dt_opencl_set_kernel_arg(devid, gd->kernel_denoiseprofile_backtransform, 5, 4 * sizeof(float), (void *)&sigma2);
2533 if(err != CL_SUCCESS) goto error;
2534 }
2535 else if(d->wavelet_color_mode == MODE_RGB)
2536 {
2537 const float bias = d->bias - 0.5 * logf(scale);
2538 dt_opencl_set_kernel_arg(devid, gd->kernel_denoiseprofile_backtransform_v2, 0, sizeof(cl_mem), (void *)&dev_tmp);
2539 dt_opencl_set_kernel_arg(devid, gd->kernel_denoiseprofile_backtransform_v2, 1, sizeof(cl_mem), (void *)&dev_out);
2540 dt_opencl_set_kernel_arg(devid, gd->kernel_denoiseprofile_backtransform_v2, 2, sizeof(int), (void *)&width);
2541 dt_opencl_set_kernel_arg(devid, gd->kernel_denoiseprofile_backtransform_v2, 3, sizeof(int), (void *)&height);
2542 dt_opencl_set_kernel_arg(devid, gd->kernel_denoiseprofile_backtransform_v2, 4, 4 * sizeof(float), (void *)&aa);
2543 dt_opencl_set_kernel_arg(devid, gd->kernel_denoiseprofile_backtransform_v2, 5, 4 * sizeof(float), (void *)&p);
2544 dt_opencl_set_kernel_arg(devid, gd->kernel_denoiseprofile_backtransform_v2, 6, 4 * sizeof(float), (void *)&bb);
2545 dt_opencl_set_kernel_arg(devid, gd->kernel_denoiseprofile_backtransform_v2, 7, sizeof(float), (void *)&bias);
2546 dt_opencl_set_kernel_arg(devid, gd->kernel_denoiseprofile_backtransform_v2, 8, 4 * sizeof(float), (void *)&wb);
2548 if(err != CL_SUCCESS) goto error;
2549 }
2550 else
2551 {
2552 cl_mem dev_RGB = NULL;
2553 dev_RGB = dt_opencl_copy_host_to_device_constant(devid, sizeof(float) * 9, toRGB);
2554 if(!IS_NULL_PTR(dev_RGB))
2555 {
2556 const float bias = d->bias - 0.5 * logf(scale);
2557 dt_opencl_set_kernel_arg(devid, gd->kernel_denoiseprofile_backtransform_Y0U0V0, 0, sizeof(cl_mem), (void *)&dev_tmp);
2558 dt_opencl_set_kernel_arg(devid, gd->kernel_denoiseprofile_backtransform_Y0U0V0, 1, sizeof(cl_mem), (void *)&dev_out);
2561 dt_opencl_set_kernel_arg(devid, gd->kernel_denoiseprofile_backtransform_Y0U0V0, 4, 4 * sizeof(float), (void *)&aa);
2562 dt_opencl_set_kernel_arg(devid, gd->kernel_denoiseprofile_backtransform_Y0U0V0, 5, 4 * sizeof(float), (void *)&p);
2563 dt_opencl_set_kernel_arg(devid, gd->kernel_denoiseprofile_backtransform_Y0U0V0, 6, 4 * sizeof(float), (void *)&bb);
2564 dt_opencl_set_kernel_arg(devid, gd->kernel_denoiseprofile_backtransform_Y0U0V0, 7, sizeof(float), (void *)&bias);
2565 dt_opencl_set_kernel_arg(devid, gd->kernel_denoiseprofile_backtransform_Y0U0V0, 8, 4 * sizeof(float), (void *)&wb);
2566 dt_opencl_set_kernel_arg(devid, gd->kernel_denoiseprofile_backtransform_Y0U0V0, 9, sizeof(cl_mem), (void *)&dev_RGB);
2569 if(err != CL_SUCCESS) goto error;
2570 }
2571 else
2572 {
2574 goto error;
2575 }
2576 }
2577
2581 dt_opencl_release_mem_object(dev_filter);
2582 for(int k = 0; k < max_scale; k++)
2583 dt_opencl_release_mem_object(dev_detail[k]);
2584 dt_free(dev_detail);
2586 return TRUE;
2587
2588error:
2592 dt_opencl_release_mem_object(dev_filter);
2593 for(int k = 0; k < max_scale; k++)
2594 dt_opencl_release_mem_object(dev_detail[k]);
2595 dt_free(dev_detail);
2597 dt_print(DT_DEBUG_OPENCL, "[opencl_denoiseprofile] couldn't enqueue kernel! %d, devid %d\n", err, devid);
2598 return FALSE;
2599}
2600
2601int 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)
2602{
2603 const dt_iop_roi_t *const roi_in = &piece->roi_in;
2604 const dt_iop_roi_t *const roi_out = &piece->roi_out;
2606
2607 if(d->mode == MODE_NLMEANS || d->mode == MODE_NLMEANS_AUTO)
2608 {
2609 return process_nlmeans_cl(self, pipe, piece, dev_in, dev_out, roi_in, roi_out);
2610 }
2611 else if(d->mode == MODE_WAVELETS || d->mode == MODE_WAVELETS_AUTO)
2612 {
2613 return process_wavelets_cl(self, pipe, piece, dev_in, dev_out, roi_in, roi_out);
2614 }
2615 else
2616 {
2617 dt_print(DT_DEBUG_OPENCL, "[opencl_denoiseprofile] compute variance not yet supported by opencl code\n");
2618 return FALSE;
2619 }
2620}
2621#endif // HAVE_OPENCL
2622
2623int process(struct dt_iop_module_t *self, const dt_dev_pixelpipe_t *pipe, const dt_dev_pixelpipe_iop_t *piece, const void *const ivoid,
2624 void *const ovoid)
2625{
2626 const dt_iop_roi_t *const roi_in = &piece->roi_in;
2627 const dt_iop_roi_t *const roi_out = &piece->roi_out;
2629 int err = 0;
2630 if(d->mode == MODE_NLMEANS || d->mode == MODE_NLMEANS_AUTO)
2631 err = process_nlmeans(pipe, piece, ivoid, ovoid, roi_in, roi_out);
2632 else if(d->mode == MODE_WAVELETS || d->mode == MODE_WAVELETS_AUTO)
2633 err = process_wavelets(self, pipe, piece, ivoid, ovoid, roi_in, roi_out, eaw_dn_decompose, eaw_synthesize);
2634 else
2635 err = process_variance(self, pipe, piece, ivoid, ovoid, roi_in, roi_out);
2636 return err;
2637}
2638
2639static inline unsigned infer_radius_from_profile(const float a)
2640{
2641 return MIN((unsigned)(1.0f + a * 15000.0f + a * a * 300000.0f), 8);
2642}
2643
2644static inline float infer_scattering_from_profile(const float a)
2645{
2646 return MIN(3000.0f * a, 1.0f);
2647}
2648
2649static inline float infer_shadows_from_profile(const float a)
2650{
2651 return MIN(MAX(0.1f - 0.1 * logf(a), 0.7f), 1.8f);
2652}
2653
2654static inline float infer_bias_from_profile(const float a)
2655{
2656 return -MAX(5 + 0.5 * logf(a), 0.0);
2657}
2658
2660{
2661 dt_iop_default_init(module);
2662
2663 dt_iop_denoiseprofile_params_t *d = module->default_params;
2664
2665 for(int k = 0; k < DT_IOP_DENOISE_PROFILE_BANDS; k++)
2666 {
2667 for(int ch = 0; ch < DT_DENOISE_PROFILE_NONE; ch++)
2668 {
2669 d->x[ch][k] = k / (DT_IOP_DENOISE_PROFILE_BANDS - 1.f);
2670 }
2671 }
2672}
2673
2675// Per-image default noise profile, gui-free (mirrors get_auto_profile but keeps the historical
2676// a[0] = -1 sentinel that signals commit_params to re-autodetect at pipe time when a real per-ISO
2677// match/interpolation is found). Used by reload_defaults() so defaults are computed even on
2678// export/thumbnail devs that have no gui_data.
2680{
2681 GList *profiles = dt_noiseprofile_get_matching(&module->dev->image_storage);
2682 dt_noiseprofile_t interpolated = dt_noiseprofile_generic; // default to generic poissonian
2683
2684 const int iso = module->dev->image_storage.exif_iso;
2685 dt_noiseprofile_t *last = NULL;
2686 for(GList *iter = profiles; iter; iter = g_list_next(iter))
2687 {
2688 dt_noiseprofile_t *current = (dt_noiseprofile_t *)iter->data;
2689 if(current->iso == iso)
2690 {
2691 interpolated = *current;
2692 interpolated.a[0] = -1.0f; // signal later autodetection in commit_params
2693 break;
2694 }
2695 if(last && last->iso < iso && current->iso > iso)
2696 {
2697 interpolated.iso = iso;
2698 dt_noiseprofile_interpolate(last, current, &interpolated);
2699 interpolated.a[0] = -1.0f;
2700 break;
2701 }
2702 last = current;
2703 }
2704 g_list_free_full(profiles, dt_noiseprofile_free);
2705 return interpolated;
2706}
2707
2709{
2710 // Params only. The matching-profiles combobox, g->profiles/g->interpolated and the slider defaults
2711 // are set in gui_update() (GUI thread, widgets present): reload_defaults() also runs on
2712 // export/thumbnail devs with no gui_data, where the old `if(g)` guard left defaults uncomputed.
2713 const dt_noiseprofile_t interpolated = _default_noise_profile(module);
2714
2715 // all these formulas were "guessed" and are completely empirical
2716 const float a = interpolated.a[1];
2717 dt_iop_denoiseprofile_params_t *d = module->default_params;
2718
2720 d->scattering = infer_scattering_from_profile(a);
2721 d->shadows = infer_shadows_from_profile(a);
2722 d->bias = infer_bias_from_profile(a);
2723
2724 for(int k = 0; k < 3; k++)
2725 {
2726 d->a[k] = interpolated.a[k];
2727 d->b[k] = interpolated.b[k];
2728 }
2729}
2730
2732{
2733 const int program = 11; // denoiseprofile.cl, from programs.conf
2736 module->data = gd;
2737 gd->kernel_denoiseprofile_precondition = dt_opencl_create_kernel(program, "denoiseprofile_precondition");
2738 gd->kernel_denoiseprofile_precondition_v2 = dt_opencl_create_kernel(program, "denoiseprofile_precondition_v2");
2739 gd->kernel_denoiseprofile_precondition_Y0U0V0 = dt_opencl_create_kernel(program, "denoiseprofile_precondition_Y0U0V0");
2740 gd->kernel_denoiseprofile_init = dt_opencl_create_kernel(program, "denoiseprofile_init");
2741 gd->kernel_denoiseprofile_dist = dt_opencl_create_kernel(program, "denoiseprofile_dist");
2742 gd->kernel_denoiseprofile_horiz = dt_opencl_create_kernel(program, "denoiseprofile_horiz");
2743 gd->kernel_denoiseprofile_vert = dt_opencl_create_kernel(program, "denoiseprofile_vert");
2744 gd->kernel_denoiseprofile_accu = dt_opencl_create_kernel(program, "denoiseprofile_accu");
2745 gd->kernel_denoiseprofile_finish = dt_opencl_create_kernel(program, "denoiseprofile_finish");
2746 gd->kernel_denoiseprofile_finish_v2 = dt_opencl_create_kernel(program, "denoiseprofile_finish_v2");
2747 gd->kernel_denoiseprofile_backtransform = dt_opencl_create_kernel(program, "denoiseprofile_backtransform");
2748 gd->kernel_denoiseprofile_backtransform_v2 = dt_opencl_create_kernel(program, "denoiseprofile_backtransform_v2");
2749 gd->kernel_denoiseprofile_backtransform_Y0U0V0 = dt_opencl_create_kernel(program, "denoiseprofile_backtransform_Y0U0V0");
2750 gd->kernel_denoiseprofile_decompose = dt_opencl_create_kernel(program, "denoiseprofile_decompose");
2751 gd->kernel_denoiseprofile_synthesize = dt_opencl_create_kernel(program, "denoiseprofile_synthesize");
2752 gd->kernel_denoiseprofile_reduce_first = dt_opencl_create_kernel(program, "denoiseprofile_reduce_first");
2753 gd->kernel_denoiseprofile_reduce_second = dt_opencl_create_kernel(program, "denoiseprofile_reduce_second");
2754}
2755
2776
2778{
2779 GList *profiles = dt_noiseprofile_get_matching(&self->dev->image_storage);
2780 dt_noiseprofile_t interpolated = dt_noiseprofile_generic; // default to generic poissonian
2781
2782 const int iso = self->dev->image_storage.exif_iso;
2783 dt_noiseprofile_t *last = NULL;
2784 for(GList *iter = profiles; iter; iter = g_list_next(iter))
2785 {
2786 dt_noiseprofile_t *current = (dt_noiseprofile_t *)iter->data;
2787 if(current->iso == iso)
2788 {
2789 interpolated = *current;
2790 break;
2791 }
2792 if(last && last->iso < iso && current->iso > iso)
2793 {
2794 interpolated.iso = iso;
2795 dt_noiseprofile_interpolate(last, current, &interpolated);
2796 break;
2797 }
2798 last = current;
2799 }
2800 g_list_free_full(profiles, dt_noiseprofile_free);
2801 profiles = NULL;
2802 return interpolated;
2803}
2804
2808{
2811
2812 d->nbhood = p->nbhood;
2813 d->central_pixel_weight = p->central_pixel_weight;
2814 d->strength = p->strength;
2815 d->overshooting = p->overshooting;
2816 for(int i = 0; i < 3; i++)
2817 {
2818 d->a[i] = p->a[i];
2819 d->b[i] = p->b[i];
2820 }
2821 d->mode = p->mode;
2822 d->wavelet_color_mode = p->wavelet_color_mode;
2823
2824 // compare if a[0] in params is set to "magic value" -1.0 for autodetection
2825 if(p->a[0] == -1.0)
2826 {
2827 // autodetect matching profile again, the same way as detecting their names,
2828 // this is partially duplicated code and data because we are not allowed to access
2829 // gui_data here ..
2831 for(int k = 0; k < 3; k++)
2832 {
2833 d->a[k] = interpolated.a[k];
2834 d->b[k] = interpolated.b[k];
2835 }
2836 }
2837
2838 if((p->mode == MODE_NLMEANS_AUTO) || (p->mode == MODE_WAVELETS_AUTO))
2839 {
2840 const float gain = p->overshooting;
2841 d->radius = infer_radius_from_profile(d->a[1] * gain);
2842 d->scattering = infer_scattering_from_profile(d->a[1] * gain);
2843 d->shadows = infer_shadows_from_profile(d->a[1] * gain);
2844 d->bias = infer_bias_from_profile(d->a[1] * gain);
2845 }
2846 else
2847 {
2848 d->radius = p->radius;
2849 d->scattering = p->scattering;
2850 d->shadows = p->shadows;
2851 d->bias = p->bias;
2852 }
2853
2854 for(int ch = 0; ch < DT_DENOISE_PROFILE_NONE; ch++)
2855 {
2856 dt_draw_curve_set_point(d->curve[ch], 0, p->x[ch][DT_IOP_DENOISE_PROFILE_BANDS - 2] - 1.f, p->y[ch][0]);
2857 for(int k = 0; k < DT_IOP_DENOISE_PROFILE_BANDS; k++)
2858 dt_draw_curve_set_point(d->curve[ch], k, p->x[ch][k], p->y[ch][k]);
2859 dt_draw_curve_set_point(d->curve[ch], DT_IOP_DENOISE_PROFILE_BANDS + 1, p->x[ch][1] + 1.f,
2861 dt_draw_curve_calc_values(d->curve[ch], 0.0, 1.0, DT_IOP_DENOISE_PROFILE_BANDS, NULL, d->force[ch]);
2862 }
2863
2864 d->wb_adaptive_anscombe = p->wb_adaptive_anscombe;
2865 d->fix_anscombe_and_nlmeans_norm = p->fix_anscombe_and_nlmeans_norm;
2866 d->use_new_vst = p->use_new_vst;
2867 piece->cache_output_on_ram = TRUE;
2868}
2869
2871{
2874
2875 piece->data = (void *)d;
2876 piece->data_size = sizeof(dt_iop_denoiseprofile_data_t);
2877 for(int ch = 0; ch < DT_DENOISE_PROFILE_NONE; ch++)
2878 {
2879 d->curve[ch] = dt_draw_curve_new(0.0, 1.0, CATMULL_ROM);
2880 for(int k = 0; k < DT_IOP_DENOISE_PROFILE_BANDS; k++)
2881 (void)dt_draw_curve_add_point(d->curve[ch], default_params->x[ch][k], default_params->y[ch][k]);
2882 }
2883}
2884
2886{
2888 for(int ch = 0; ch < DT_DENOISE_PROFILE_NONE; ch++) dt_draw_curve_destroy(d->curve[ch]);
2889 dt_free_align(piece->data);
2890 piece->data = NULL;
2891}
2892
2894{
2895 int i = dt_bauhaus_combobox_get(w);
2898 const dt_noiseprofile_t *profile = &(g->interpolated);
2899 if(i > 0) profile = (dt_noiseprofile_t *)g_list_nth_data(g->profiles, i - 1);
2900 for(int k = 0; k < 3; k++)
2901 {
2902 p->a[k] = profile->a[k];
2903 p->b[k] = profile->b[k];
2904 }
2906}
2907
2909{
2912 const unsigned mode = dt_bauhaus_combobox_get(w);
2913 switch(mode)
2914 {
2915 case 0:
2916 p->mode = MODE_NLMEANS;
2917 gtk_widget_hide(g->box_wavelets);
2918 gtk_widget_hide(g->box_variance);
2919 gtk_widget_show_all(g->box_nlm);
2920 break;
2921 case 1:
2922 p->mode = MODE_NLMEANS_AUTO;
2923 gtk_widget_hide(g->box_wavelets);
2924 gtk_widget_hide(g->box_variance);
2925 gtk_widget_show_all(g->box_nlm);
2926 gtk_widget_set_visible(g->radius, FALSE);
2927 gtk_widget_set_visible(g->nbhood, FALSE);
2928 gtk_widget_set_visible(g->scattering, FALSE);
2929 break;
2930 case 2:
2931 p->mode = MODE_WAVELETS;
2932 gtk_widget_hide(g->box_nlm);
2933 gtk_widget_hide(g->box_variance);
2934 gtk_widget_show_all(g->box_wavelets);
2935 gtk_widget_set_visible(GTK_WIDGET(g->wavelet_color_mode), p->use_new_vst);
2936 gtk_widget_set_visible(GTK_WIDGET(g->channel_tabs), p->use_new_vst && (p->wavelet_color_mode == MODE_RGB));
2937 gtk_widget_set_visible(GTK_WIDGET(g->channel_tabs_Y0U0V0), p->use_new_vst && (p->wavelet_color_mode == MODE_Y0U0V0));
2938 break;
2939 case 3:
2940 p->mode = MODE_WAVELETS_AUTO;
2941 gtk_widget_hide(g->box_nlm);
2942 gtk_widget_hide(g->box_variance);
2943 gtk_widget_show_all(g->box_wavelets);
2944 gtk_widget_set_visible(GTK_WIDGET(g->wavelet_color_mode), p->use_new_vst);
2945 gtk_widget_set_visible(GTK_WIDGET(g->channel_tabs), p->use_new_vst && (p->wavelet_color_mode == MODE_RGB));
2946 gtk_widget_set_visible(GTK_WIDGET(g->channel_tabs_Y0U0V0), p->use_new_vst && (p->wavelet_color_mode == MODE_Y0U0V0));
2947 break;
2948 case 4:
2949 p->mode = MODE_VARIANCE;
2950 gtk_widget_hide(g->box_wavelets);
2951 gtk_widget_hide(g->box_nlm);
2952 gtk_widget_show_all(g->box_variance);
2953 break;
2954 }
2955 const gboolean auto_mode = (p->mode == MODE_NLMEANS_AUTO) || (p->mode == MODE_WAVELETS_AUTO);
2956 gtk_widget_set_visible(g->shadows, p->use_new_vst && !auto_mode);
2957 gtk_widget_set_visible(g->bias, p->use_new_vst && !auto_mode);
2958 gtk_widget_set_visible(g->overshooting, auto_mode);
2960}
2961
2962void gui_changed(dt_iop_module_t *self, GtkWidget *w, void *previous)
2963{
2966
2967 if(w == g->wavelet_color_mode)
2968 {
2969 gtk_widget_set_visible(GTK_WIDGET(g->channel_tabs), (p->wavelet_color_mode == MODE_RGB));
2970 gtk_widget_set_visible(GTK_WIDGET(g->channel_tabs_Y0U0V0), (p->wavelet_color_mode == MODE_Y0U0V0));
2971 if(p->wavelet_color_mode == MODE_RGB)
2972 g->channel = DT_DENOISE_PROFILE_ALL;
2973 else
2974 g->channel = DT_DENOISE_PROFILE_Y0;
2975 }
2976 else if(w == g->overshooting)
2977 {
2978 const float gain = p->overshooting;
2979 float a = p->a[1];
2980 if(p->a[0] == -1.0)
2981 {
2983 a = interpolated.a[1];
2984 }
2985 // set the sliders as visible while we are setting their values
2986 // otherwise a log message appears
2987 if(p->mode == MODE_NLMEANS_AUTO)
2988 {
2989 gtk_widget_set_visible(g->radius, TRUE);
2990 gtk_widget_set_visible(g->scattering, TRUE);
2993 gtk_widget_set_visible(g->radius, FALSE);
2994 gtk_widget_set_visible(g->scattering, FALSE);
2995 }
2996 else
2997 {
2998 // we are in wavelets mode.
2999 // we need to show the box_nlm, setting the sliders to visible is not enough
3000 gtk_widget_show_all(g->box_nlm);
3003 gtk_widget_hide(g->box_nlm);
3004 }
3005 gtk_widget_set_visible(g->shadows, TRUE);
3006 gtk_widget_set_visible(g->bias, TRUE);
3009 gtk_widget_set_visible(g->shadows, FALSE);
3010 gtk_widget_set_visible(g->bias, FALSE);
3011 }
3012 else if(w == g->use_new_vst)
3013 {
3014 const gboolean auto_mode = (p->mode == MODE_NLMEANS_AUTO) || (p->mode == MODE_WAVELETS_AUTO);
3015 gtk_widget_set_visible(g->shadows, p->use_new_vst && !auto_mode);
3016 gtk_widget_set_visible(g->bias, p->use_new_vst && !auto_mode);
3017 gtk_widget_set_visible(g->wavelet_color_mode, p->use_new_vst);
3018 if(!p->use_new_vst && p->wavelet_color_mode == MODE_Y0U0V0)
3019 p->wavelet_color_mode = MODE_RGB;
3020 }
3021}
3022
3024{
3027
3028 // Matching noise profiles + the interpolated default profile and the profile combobox, formerly
3029 // built in reload_defaults(). They need live widgets and feed the profile picker below, so they
3030 // belong here on the GUI thread. Keep g->interpolated in sync (used by the profile callback).
3031 dt_bauhaus_combobox_clear(g->profile);
3032 char name[512];
3033 if(g->profiles)
3034 {
3035 g_list_free_full(g->profiles, dt_noiseprofile_free);
3036 g->profiles = NULL;
3037 }
3038 g->profiles = dt_noiseprofile_get_matching(&self->dev->image_storage);
3039 g->interpolated = dt_noiseprofile_generic; // default to generic poissonian
3040 g_strlcpy(name, _(g->interpolated.name), sizeof(name));
3041
3042 const int iso = self->dev->image_storage.exif_iso;
3043 dt_noiseprofile_t *last = NULL;
3044 for(GList *iter = g->profiles; iter; iter = g_list_next(iter))
3045 {
3046 dt_noiseprofile_t *current = (dt_noiseprofile_t *)iter->data;
3047 if(current->iso == iso)
3048 {
3049 g->interpolated = *current;
3050 g->interpolated.a[0] = -1.0f; // signal later autodetection in commit_params
3051 snprintf(name, sizeof(name), _("found match for ISO %d"), iso);
3052 break;
3053 }
3054 if(last && last->iso < iso && current->iso > iso)
3055 {
3056 g->interpolated.iso = iso;
3057 dt_noiseprofile_interpolate(last, current, &g->interpolated);
3058 g->interpolated.a[0] = -1.0f;
3059 snprintf(name, sizeof(name), _("interpolated from ISO %d and %d"), last->iso, current->iso);
3060 break;
3061 }
3062 last = current;
3063 }
3064 dt_bauhaus_combobox_add(g->profile, name);
3065 for(GList *iter = g->profiles; iter; iter = g_list_next(iter))
3066 {
3067 dt_noiseprofile_t *profile = (dt_noiseprofile_t *)iter->data;
3068 dt_bauhaus_combobox_add(g->profile, profile->name);
3069 }
3070
3071 // slider defaults from the (image-dependent) default params computed in reload_defaults()
3073 dt_bauhaus_slider_set_default(g->radius, d->radius);
3074 dt_bauhaus_slider_set_default(g->scattering, d->scattering);
3075 dt_bauhaus_slider_set_default(g->shadows, d->shadows);
3076 dt_bauhaus_slider_set_default(g->bias, d->bias);
3077
3078 dt_bauhaus_combobox_set(g->profile, -1);
3079 unsigned combobox_index = 0;
3080 switch (p->mode)
3081 {
3082 case MODE_NLMEANS:
3083 combobox_index = 0;
3084 gtk_widget_hide(g->box_wavelets);
3085 gtk_widget_hide(g->box_variance);
3086 gtk_widget_show_all(g->box_nlm);
3087 break;
3088 case MODE_NLMEANS_AUTO:
3089 combobox_index = 1;
3090 gtk_widget_hide(g->box_wavelets);
3091 gtk_widget_hide(g->box_variance);
3092 gtk_widget_show_all(g->box_nlm);
3093 gtk_widget_set_visible(g->radius, FALSE);
3094 gtk_widget_set_visible(g->nbhood, FALSE);
3095 gtk_widget_set_visible(g->scattering, FALSE);
3096 break;
3097 case MODE_WAVELETS:
3098 combobox_index = 2;
3099 gtk_widget_hide(g->box_nlm);
3100 gtk_widget_hide(g->box_variance);
3101 gtk_widget_show_all(g->box_wavelets);
3102 break;
3103 case MODE_WAVELETS_AUTO:
3104 combobox_index = 3;
3105 gtk_widget_hide(g->box_nlm);
3106 gtk_widget_hide(g->box_variance);
3107 gtk_widget_show_all(g->box_wavelets);
3108 break;
3109 case MODE_VARIANCE:
3110 combobox_index = 4;
3111 gtk_widget_hide(g->box_wavelets);
3112 gtk_widget_hide(g->box_nlm);
3113 gtk_widget_show_all(g->box_variance);
3114 if(dt_bauhaus_combobox_length(g->mode) == 4)
3115 {
3116 dt_bauhaus_combobox_add(g->mode, _("compute variance"));
3117 }
3118 break;
3119 }
3120 float a = p->a[1];
3121 if(p->a[0] == -1.0)
3122 {
3124 a = interpolated.a[1];
3125 }
3126 if((p->mode == MODE_NLMEANS_AUTO) || (p->mode == MODE_WAVELETS_AUTO))
3127 {
3128 const float gain = p->overshooting;
3133 }
3134 dt_bauhaus_combobox_set(g->mode, combobox_index);
3135 if(p->a[0] == -1.0)
3136 {
3137 dt_bauhaus_combobox_set(g->profile, 0);
3138 }
3139 else
3140 {
3141 int i = 1;
3142 for(GList *iter = g->profiles; iter; iter = g_list_next(iter), i++)
3143 {
3144 dt_noiseprofile_t *profile = (dt_noiseprofile_t *)iter->data;
3145 if(!memcmp(profile->a, p->a, sizeof(float) * 3)
3146 && !memcmp(profile->b, p->b, sizeof(float) * 3))
3147 {
3148 dt_bauhaus_combobox_set(g->profile, i);
3149 break;
3150 }
3151 }
3152 }
3153 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(g->wb_adaptive_anscombe), p->wb_adaptive_anscombe);
3154 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(g->fix_anscombe_and_nlmeans_norm), p->fix_anscombe_and_nlmeans_norm);
3155 gtk_widget_set_visible(g->fix_anscombe_and_nlmeans_norm, !p->fix_anscombe_and_nlmeans_norm);
3156 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(g->use_new_vst), p->use_new_vst);
3157 gtk_widget_set_visible(g->use_new_vst, !p->use_new_vst);
3158 const gboolean auto_mode = (p->mode == MODE_NLMEANS_AUTO) || (p->mode == MODE_WAVELETS_AUTO);
3159 const gboolean wavelet_mode = (p->mode == MODE_WAVELETS) || (p->mode == MODE_WAVELETS_AUTO);
3160 gtk_widget_set_visible(g->overshooting, auto_mode);
3161 gtk_widget_set_visible(g->wavelet_color_mode, p->use_new_vst && wavelet_mode);
3162 gtk_widget_set_visible(g->shadows, p->use_new_vst && !auto_mode);
3163 gtk_widget_set_visible(g->bias, p->use_new_vst && !auto_mode);
3164 gtk_widget_set_visible(GTK_WIDGET(g->channel_tabs), (p->wavelet_color_mode == MODE_RGB));
3165 gtk_widget_set_visible(GTK_WIDGET(g->channel_tabs_Y0U0V0), (p->wavelet_color_mode == MODE_Y0U0V0));
3166 if((p->wavelet_color_mode == MODE_Y0U0V0) && (g->channel < DT_DENOISE_PROFILE_Y0))
3167 {
3168 g->channel = DT_DENOISE_PROFILE_Y0;
3169 gtk_notebook_set_current_page(GTK_NOTEBOOK(g->channel_tabs_Y0U0V0), g->channel - DT_DENOISE_PROFILE_Y0);
3170 }
3171 if((p->wavelet_color_mode == MODE_RGB) && (g->channel > DT_DENOISE_PROFILE_B))
3172 {
3173 g->channel = DT_DENOISE_PROFILE_ALL;
3174 gtk_notebook_set_current_page(GTK_NOTEBOOK(g->channel_tabs), g->channel);
3175 }
3176}
3177
3179{
3182 if(p->wavelet_color_mode == MODE_Y0U0V0)
3183 {
3184 g->channel = DT_DENOISE_PROFILE_Y0;
3185 gtk_notebook_set_current_page(GTK_NOTEBOOK(g->channel_tabs_Y0U0V0), g->channel - DT_DENOISE_PROFILE_Y0);
3186 }
3187 else
3188 {
3189 g->channel = DT_DENOISE_PROFILE_ALL;
3190 gtk_notebook_set_current_page(GTK_NOTEBOOK(g->channel_tabs), g->channel);
3191 }
3192 gtk_widget_set_visible(g->fix_anscombe_and_nlmeans_norm, !p->fix_anscombe_and_nlmeans_norm);
3193 gtk_widget_set_visible(g->use_new_vst, !p->use_new_vst);
3194}
3195
3196static void dt_iop_denoiseprofile_get_params(dt_iop_denoiseprofile_params_t *p, const int ch, const double mouse_x,
3197 const double mouse_y, const float rad)
3198{
3199 for(int k = 0; k < DT_IOP_DENOISE_PROFILE_BANDS; k++)
3200 {
3201 const float f = expf(-(mouse_x - p->x[ch][k]) * (mouse_x - p->x[ch][k]) / (rad * rad));
3202 p->y[ch][k] = (1 - f) * p->y[ch][k] + f * mouse_y;
3203 }
3204}
3205
3206static gboolean denoiseprofile_draw_variance(GtkWidget *widget, cairo_t *crf, gpointer user_data)
3207{
3208 if(dt_gui_widgets_suppressed()) return FALSE;
3209
3210 dt_iop_module_t *self = (dt_iop_module_t *)user_data;
3212
3213 if(!isnan(c->variance_R))
3214 {
3215 gchar *str = g_strdup_printf("%.2f", c->variance_R);
3217 gtk_label_set_text(c->label_var_R, str);
3219 dt_free(str);
3220 }
3221 if(!isnan(c->variance_G))
3222 {
3223 gchar *str = g_strdup_printf("%.2f", c->variance_G);
3225 gtk_label_set_text(c->label_var_G, str);
3227 dt_free(str);
3228 }
3229 if(!isnan(c->variance_B))
3230 {
3231 gchar *str = g_strdup_printf("%.2f", c->variance_B);
3233 gtk_label_set_text(c->label_var_B, str);
3235 dt_free(str);
3236 }
3237 return FALSE;
3238}
3239
3240static gboolean denoiseprofile_draw(GtkWidget *widget, cairo_t *crf, gpointer user_data)
3241{
3242 dt_iop_module_t *self = (dt_iop_module_t *)user_data;
3245
3246 int ch = (int)c->channel;
3247 dt_draw_curve_set_point(c->transition_curve, 0, p.x[ch][DT_IOP_DENOISE_PROFILE_BANDS - 2] - 1.f, p.y[ch][0]);
3248 for(int k = 0; k < DT_IOP_DENOISE_PROFILE_BANDS; k++)
3249 dt_draw_curve_set_point(c->transition_curve, k + 1, p.x[ch][k], p.y[ch][k]);
3250 dt_draw_curve_set_point(c->transition_curve, DT_IOP_DENOISE_PROFILE_BANDS + 1, p.x[ch][1] + 1.f,
3252
3253 const int inset = DT_IOP_DENOISE_PROFILE_INSET;
3254 GtkAllocation allocation;
3255 gtk_widget_get_allocation(widget, &allocation);
3256 int width = allocation.width, height = allocation.height;
3257 cairo_surface_t *cst = dt_cairo_image_surface_create(CAIRO_FORMAT_ARGB32, width, height);
3258 cairo_t *cr = cairo_create(cst);
3259 cairo_set_source_rgb(cr, .2, .2, .2);
3260
3261 cairo_paint(cr);
3262
3263 cairo_translate(cr, inset, inset);
3264 width -= 2 * inset;
3265 height -= 2 * inset;
3266
3267 cairo_set_line_width(cr, DT_PIXEL_APPLY_DPI(1.0));
3268 cairo_set_source_rgb(cr, .1, .1, .1);
3269 cairo_rectangle(cr, 0, 0, width, height);
3270 cairo_stroke(cr);
3271
3272 cairo_set_source_rgb(cr, .3, .3, .3);
3273 cairo_rectangle(cr, 0, 0, width, height);
3274 cairo_fill(cr);
3275
3276 // draw grid
3277 cairo_set_line_width(cr, DT_PIXEL_APPLY_DPI(.4));
3278 cairo_set_source_rgb(cr, .1, .1, .1);
3279 dt_draw_grid(cr, 8, 0, 0, width, height);
3280
3281 if(c->mouse_y > 0 || c->dragging)
3282 {
3283 // draw min/max curves:
3284 dt_iop_denoiseprofile_get_params(&p, c->channel, c->mouse_x, 1., c->mouse_radius);
3285 dt_draw_curve_set_point(c->transition_curve, 0, p.x[ch][DT_IOP_DENOISE_PROFILE_BANDS - 2] - 1.f, p.y[ch][0]);
3286 for(int k = 0; k < DT_IOP_DENOISE_PROFILE_BANDS; k++)
3287 dt_draw_curve_set_point(c->transition_curve, k + 1, p.x[ch][k], p.y[ch][k]);
3288 dt_draw_curve_set_point(c->transition_curve, DT_IOP_DENOISE_PROFILE_BANDS + 1, p.x[ch][1] + 1.f,
3290 dt_draw_curve_calc_values(c->transition_curve, 0.0, 1.0, DT_IOP_DENOISE_PROFILE_RES, c->draw_min_xs,
3291 c->draw_min_ys);
3292
3294 dt_iop_denoiseprofile_get_params(&p, c->channel, c->mouse_x, .0, c->mouse_radius);
3295 dt_draw_curve_set_point(c->transition_curve, 0, p.x[ch][DT_IOP_DENOISE_PROFILE_BANDS - 2] - 1.f, p.y[ch][0]);
3296 for(int k = 0; k < DT_IOP_DENOISE_PROFILE_BANDS; k++)
3297 dt_draw_curve_set_point(c->transition_curve, k + 1, p.x[ch][k], p.y[ch][k]);
3298 dt_draw_curve_set_point(c->transition_curve, DT_IOP_DENOISE_PROFILE_BANDS + 1, p.x[ch][1] + 1.f,
3300 dt_draw_curve_calc_values(c->transition_curve, 0.0, 1.0, DT_IOP_DENOISE_PROFILE_RES, c->draw_max_xs,
3301 c->draw_max_ys);
3302 }
3303
3304 cairo_save(cr);
3305
3306 // draw selected cursor
3307 cairo_translate(cr, 0, height);
3308
3309 cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
3310 cairo_set_line_width(cr, DT_PIXEL_APPLY_DPI(2.));
3311
3312 for(int i = 0; i < DT_DENOISE_PROFILE_NONE; i++)
3313 {
3314 // draw curves, selected last
3315 ch = ((int)c->channel + i + 1) % DT_DENOISE_PROFILE_NONE;
3316 float alpha = 0.3;
3317 if(i == DT_DENOISE_PROFILE_NONE - 1) alpha = 1.0;
3318 if(p.wavelet_color_mode == MODE_RGB)
3319 {
3320 switch(ch)
3321 {
3323 cairo_set_source_rgba(cr, .7, .7, .7, alpha);
3324 break;
3326 cairo_set_source_rgba(cr, .7, .1, .1, alpha);
3327 break;
3329 cairo_set_source_rgba(cr, .1, .7, .1, alpha);
3330 break;
3332 cairo_set_source_rgba(cr, .1, .1, .7, alpha);
3333 break;
3334 default:
3335 cairo_set_source_rgba(cr, 7, .7, .7, 0.0f);
3336 break;
3337 }
3338 }
3339 else
3340 {
3341 switch(ch)
3342 {
3344 cairo_set_source_rgba(cr, .7, .7, .7, alpha);
3345 break;
3347 cairo_set_source_rgba(cr, .8, .4, .0, alpha);
3348 break;
3349 default:
3350 cairo_set_source_rgba(cr, .7, .7, .7, 0.0f);
3351 break;
3352 }
3353 }
3354
3356 dt_draw_curve_set_point(c->transition_curve, 0, p.x[ch][DT_IOP_DENOISE_PROFILE_BANDS - 2] - 1.0f, p.y[ch][0]);
3357 for(int k = 0; k < DT_IOP_DENOISE_PROFILE_BANDS; k++)
3358 dt_draw_curve_set_point(c->transition_curve, k + 1, p.x[ch][k], p.y[ch][k]);
3359 dt_draw_curve_set_point(c->transition_curve, DT_IOP_DENOISE_PROFILE_BANDS + 1, p.x[ch][1] + 1.0f,
3361 dt_draw_curve_calc_values(c->transition_curve, 0.0, 1.0, DT_IOP_DENOISE_PROFILE_RES, c->draw_xs, c->draw_ys);
3362 cairo_move_to(cr, 0 * width / (float)(DT_IOP_DENOISE_PROFILE_RES - 1), -height * c->draw_ys[0]);
3363 for(int k = 1; k < DT_IOP_DENOISE_PROFILE_RES; k++)
3364 cairo_line_to(cr, k * width / (float)(DT_IOP_DENOISE_PROFILE_RES - 1), -height * c->draw_ys[k]);
3365 cairo_stroke(cr);
3366 }
3367
3368 ch = c->channel;
3369 // draw dots on knots
3370 cairo_set_source_rgb(cr, 0.7, 0.7, 0.7);
3371 cairo_set_line_width(cr, DT_PIXEL_APPLY_DPI(1.));
3372 for(int k = 0; k < DT_IOP_DENOISE_PROFILE_BANDS; k++)
3373 {
3374 cairo_arc(cr, width * p.x[ch][k], -height * p.y[ch][k], DT_PIXEL_APPLY_DPI(3.0), 0.0, 2.0 * M_PI);
3375 if(c->x_move == k)
3376 cairo_fill(cr);
3377 else
3378 cairo_stroke(cr);
3379 }
3380
3381 if(c->mouse_y > 0 || c->dragging)
3382 {
3383 // draw min/max, if selected
3384 cairo_set_source_rgba(cr, .7, .7, .7, .6);
3385 cairo_move_to(cr, 0, -height * c->draw_min_ys[0]);
3386 for(int k = 1; k < DT_IOP_DENOISE_PROFILE_RES; k++)
3387 cairo_line_to(cr, k * width / (float)(DT_IOP_DENOISE_PROFILE_RES - 1), -height * c->draw_min_ys[k]);
3388 for(int k = DT_IOP_DENOISE_PROFILE_RES - 1; k >= 0; k--)
3389 cairo_line_to(cr, k * width / (float)(DT_IOP_DENOISE_PROFILE_RES - 1), -height * c->draw_max_ys[k]);
3390 cairo_close_path(cr);
3391 cairo_fill(cr);
3392 // draw mouse focus circle
3393 cairo_set_source_rgba(cr, .9, .9, .9, .5);
3394 const float pos = DT_IOP_DENOISE_PROFILE_RES * c->mouse_x;
3395 int k = (int)pos;
3396 const float f = k - pos;
3398 float ht = -height * (f * c->draw_ys[k] + (1 - f) * c->draw_ys[k + 1]);
3399 cairo_arc(cr, c->mouse_x * width, ht, c->mouse_radius * width, 0, 2. * M_PI);
3400 cairo_stroke(cr);
3401 }
3402
3403 cairo_restore(cr);
3404
3405 cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
3406
3407 // draw labels:
3408 PangoLayout *layout;
3409 PangoRectangle ink;
3410 PangoFontDescription *desc = pango_font_description_copy_static(darktable.bauhaus->pango_font_desc);
3411 pango_font_description_set_weight(desc, PANGO_WEIGHT_BOLD);
3412 pango_font_description_set_absolute_size(desc, (.08 * height) * PANGO_SCALE);
3413 layout = pango_cairo_create_layout(cr);
3414 pango_layout_set_font_description(layout, desc);
3415 cairo_set_source_rgb(cr, .1, .1, .1);
3416
3417 pango_layout_set_text(layout, _("coarse"), -1);
3418 pango_layout_get_pixel_extents(layout, &ink, NULL);
3419 cairo_move_to(cr, .02 * width - ink.y, .5 * (height + ink.width));
3420 cairo_save(cr);
3421 cairo_rotate(cr, -M_PI * .5f);
3422 pango_cairo_show_layout(cr, layout);
3423 cairo_restore(cr);
3424
3425 pango_layout_set_text(layout, _("fine"), -1);
3426 pango_layout_get_pixel_extents(layout, &ink, NULL);
3427 cairo_move_to(cr, .98 * width - ink.height, .5 * (height + ink.width));
3428 cairo_save(cr);
3429 cairo_rotate(cr, -M_PI * .5f);
3430 pango_cairo_show_layout(cr, layout);
3431 cairo_restore(cr);
3432
3433
3434 pango_layout_set_text(layout, _("smooth"), -1);
3435 pango_layout_get_pixel_extents(layout, &ink, NULL);
3436 cairo_move_to(cr, .5 * (width - ink.width), .08 * height - ink.height);
3437 pango_cairo_show_layout(cr, layout);
3438
3439 pango_layout_set_text(layout, _("noisy"), -1);
3440 pango_layout_get_pixel_extents(layout, &ink, NULL);
3441 cairo_move_to(cr, .5 * (width - ink.width), .97 * height - ink.height);
3442 pango_cairo_show_layout(cr, layout);
3443
3444 pango_font_description_free(desc);
3445 g_object_unref(layout);
3446 cairo_destroy(cr);
3447 cairo_set_source_surface(crf, cst, 0, 0);
3448 cairo_paint(crf);
3449 cairo_surface_destroy(cst);
3450 return TRUE;
3451}
3452
3453static gboolean denoiseprofile_motion_notify(GtkWidget *widget, GdkEventMotion *event, gpointer user_data)
3454{
3455 dt_iop_module_t *self = (dt_iop_module_t *)user_data;
3458 const int inset = DT_IOP_DENOISE_PROFILE_INSET;
3459 GtkAllocation allocation;
3460 gtk_widget_get_allocation(widget, &allocation);
3461 int height = allocation.height - 2 * inset, width = allocation.width - 2 * inset;
3462 if(!c->dragging) c->mouse_x = CLAMP(event->x - inset, 0, width) / (float)width;
3463 c->mouse_y = 1.0 - CLAMP(event->y - inset, 0, height) / (float)height;
3464 if(c->dragging)
3465 {
3466 *p = c->drag_params;
3467 if(c->x_move < 0)
3468 {
3469 dt_iop_denoiseprofile_get_params(p, c->channel, c->mouse_x, c->mouse_y + c->mouse_pick, c->mouse_radius);
3470 }
3472 }
3473 else
3474 {
3475 c->x_move = -1;
3476 }
3477 gtk_widget_queue_draw(widget);
3478 return TRUE;
3479}
3480
3481static gboolean denoiseprofile_button_press(GtkWidget *widget, GdkEventButton *event, gpointer user_data)
3482{
3483 dt_iop_module_t *self = (dt_iop_module_t *)user_data;
3485 const int ch = c->channel;
3486 if(event->button == 1 && event->type == GDK_2BUTTON_PRESS)
3487 {
3488 // reset current curve
3491 /* dt_iop_denoiseprofile_gui_data_t *c = (dt_iop_denoiseprofile_gui_data_t *)self->gui_data; */
3492 for(int k = 0; k < DT_IOP_DENOISE_PROFILE_BANDS; k++)
3493 {
3494 p->x[ch][k] = d->x[ch][k];
3495 p->y[ch][k] = d->y[ch][k];
3496 }
3498 gtk_widget_queue_draw(self->widget);
3499 }
3500 else if(event->button == 1)
3501 {
3502 c->drag_params = *(dt_iop_denoiseprofile_params_t *)self->params;
3503 const int inset = DT_IOP_DENOISE_PROFILE_INSET;
3504 GtkAllocation allocation;
3505 gtk_widget_get_allocation(widget, &allocation);
3506 int height = allocation.height - 2 * inset, width = allocation.width - 2 * inset;
3507 c->mouse_pick
3508 = dt_draw_curve_calc_value(c->transition_curve, CLAMP(event->x - inset, 0, width) / (float)width);
3509 c->mouse_pick -= 1.0 - CLAMP(event->y - inset, 0, height) / (float)height;
3510 c->dragging = 1;
3511 return TRUE;
3512 }
3513 return FALSE;
3514}
3515
3516static gboolean denoiseprofile_button_release(GtkWidget *widget, GdkEventButton *event, gpointer user_data)
3517{
3518 if(event->button == 1)
3519 {
3520 dt_iop_module_t *self = (dt_iop_module_t *)user_data;
3522 c->dragging = 0;
3523 return TRUE;
3524 }
3525 return FALSE;
3526}
3527
3528static gboolean denoiseprofile_leave_notify(GtkWidget *widget, GdkEventCrossing *event, gpointer user_data)
3529{
3530 dt_iop_module_t *self = (dt_iop_module_t *)user_data;
3532 if(!c->dragging) c->mouse_y = -1.0;
3533 gtk_widget_queue_draw(widget);
3534 return TRUE;
3535}
3536
3537static gboolean denoiseprofile_scrolled(GtkWidget *widget, GdkEventScroll *event, gpointer user_data)
3538{
3539 dt_iop_module_t *self = (dt_iop_module_t *)user_data;
3541
3542 int delta_y;
3543 if(dt_gui_get_scroll_unit_deltas(event, NULL, &delta_y))
3544 {
3545 c->mouse_radius = CLAMP(c->mouse_radius * (1.f + 0.1f * delta_y), 0.2f / DT_IOP_DENOISE_PROFILE_BANDS, 1.f);
3546 gtk_widget_queue_draw(widget);
3547 }
3548
3549 return TRUE;
3550}
3551
3552static void denoiseprofile_tab_switch(GtkNotebook *notebook, GtkWidget *page, guint page_num, gpointer user_data)
3553{
3554 dt_iop_module_t *self = (dt_iop_module_t *)user_data;
3556 if(dt_gui_widgets_suppressed()) return;
3558 if(p->wavelet_color_mode == MODE_Y0U0V0)
3560 else
3561 c->channel = (dt_iop_denoiseprofile_channel_t)page_num;
3562 gtk_widget_queue_draw(self->widget);
3563}
3564
3566{
3569
3570 g->profiles = NULL;
3571
3572 g->channel = 0;
3573
3574 // First build sub-level boxes
3575 g->box_nlm = self->widget = gtk_box_new(GTK_ORIENTATION_VERTICAL, DT_GUI_BOX_SPACING);
3576
3577 g->radius = dt_bauhaus_slider_from_params(self, "radius");
3578 dt_bauhaus_slider_set_soft_range(g->radius, 0.0, 8.0);
3579 dt_bauhaus_slider_set_digits(g->radius, 0);
3580 g->nbhood = dt_bauhaus_slider_from_params(self, "nbhood");
3581 dt_bauhaus_slider_set_digits(g->nbhood, 0);
3582 g->scattering = dt_bauhaus_slider_from_params(self, "scattering");
3583 dt_bauhaus_slider_set_soft_max(g->scattering, 1.0f);
3584 g->central_pixel_weight = dt_bauhaus_slider_from_params(self, "central_pixel_weight");
3585 dt_bauhaus_slider_set_soft_max(g->central_pixel_weight, 1.0f);
3586
3587 g->box_wavelets = self->widget = gtk_box_new(GTK_ORIENTATION_VERTICAL, DT_GUI_BOX_SPACING);
3588
3589 g->wavelet_color_mode = dt_bauhaus_combobox_from_params(self, "wavelet_color_mode");
3590
3591 g->channel_tabs = GTK_NOTEBOOK(gtk_notebook_new());
3592 dt_ui_notebook_page(g->channel_tabs, N_("all"), NULL);
3593 dt_ui_notebook_page(g->channel_tabs, N_("R"), NULL);
3594 dt_ui_notebook_page(g->channel_tabs, N_("G"), NULL);
3595 dt_ui_notebook_page(g->channel_tabs, N_("B"), NULL);
3596 g_signal_connect(G_OBJECT(g->channel_tabs), "switch_page", G_CALLBACK(denoiseprofile_tab_switch), self);
3597 gtk_box_pack_start(GTK_BOX(g->box_wavelets), GTK_WIDGET(g->channel_tabs), FALSE, FALSE, 0);
3598
3599 g->channel_tabs_Y0U0V0 = GTK_NOTEBOOK(gtk_notebook_new());
3600 dt_ui_notebook_page(g->channel_tabs_Y0U0V0, N_("Y0"), NULL);
3601 dt_ui_notebook_page(g->channel_tabs_Y0U0V0, N_("U0V0"), NULL);
3602 g_signal_connect(G_OBJECT(g->channel_tabs_Y0U0V0), "switch_page", G_CALLBACK(denoiseprofile_tab_switch), self);
3603 gtk_box_pack_start(GTK_BOX(g->box_wavelets), GTK_WIDGET(g->channel_tabs_Y0U0V0), FALSE, FALSE, 0);
3604
3605 const int ch = (int)g->channel;
3606 g->transition_curve = dt_draw_curve_new(0.0, 1.0, CATMULL_ROM);
3607 (void)dt_draw_curve_add_point(g->transition_curve, p->x[ch][DT_IOP_DENOISE_PROFILE_BANDS - 2] - 1.0f,
3609 for(int k = 0; k < DT_IOP_DENOISE_PROFILE_BANDS; k++)
3610 (void)dt_draw_curve_add_point(g->transition_curve, p->x[ch][k], p->y[ch][k]);
3611 (void)dt_draw_curve_add_point(g->transition_curve, p->x[ch][1] + 1.0f, p->y[ch][1]);
3612
3613 g->mouse_x = g->mouse_y = g->mouse_pick = -1.0;
3614 g->dragging = 0;
3615 g->x_move = -1;
3616 g->mouse_radius = 1.0f / (DT_IOP_DENOISE_PROFILE_BANDS * 2);
3617
3618 g->area = GTK_DRAWING_AREA(gtk_drawing_area_new());
3619 gtk_widget_set_hexpand(GTK_WIDGET(g->area), TRUE);
3620
3621 gtk_widget_add_events(GTK_WIDGET(g->area), GDK_POINTER_MOTION_MASK
3622 | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK
3623 | GDK_LEAVE_NOTIFY_MASK | darktable.gui->scroll_mask);
3624 g_signal_connect(G_OBJECT(g->area), "draw", G_CALLBACK(denoiseprofile_draw), self);
3625 g_signal_connect(G_OBJECT(g->area), "button-press-event", G_CALLBACK(denoiseprofile_button_press), self);
3626 g_signal_connect(G_OBJECT(g->area), "button-release-event", G_CALLBACK(denoiseprofile_button_release), self);
3627 g_signal_connect(G_OBJECT(g->area), "motion-notify-event", G_CALLBACK(denoiseprofile_motion_notify), self);
3628 g_signal_connect(G_OBJECT(g->area), "leave-notify-event", G_CALLBACK(denoiseprofile_leave_notify), self);
3629 g_signal_connect(G_OBJECT(g->area), "scroll-event", G_CALLBACK(denoiseprofile_scrolled), self);
3630 gtk_box_pack_start(GTK_BOX(g->box_wavelets),
3631 dt_ui_resizable_drawing_area(GTK_WIDGET(g->area),
3632 "plugins/darkroom/denoiseprofile/graphheight", 280, 100),
3633 FALSE, FALSE, 0);
3634
3635 g->box_variance = gtk_box_new(GTK_ORIENTATION_VERTICAL, DT_GUI_BOX_SPACING);
3636
3637 g->label_var = GTK_LABEL(dt_ui_label_new(_("use only with a perfectly\n"
3638 "uniform image if you want to\n"
3639 "estimate the noise variance.")));
3640 gtk_box_pack_start(GTK_BOX(g->box_variance), GTK_WIDGET(g->label_var), TRUE, TRUE, 0);
3641
3642 GtkBox *hboxR = GTK_BOX(gtk_box_new(GTK_ORIENTATION_HORIZONTAL, DT_GUI_BOX_SPACING));
3643 GtkLabel *labelR = GTK_LABEL(dt_ui_label_new(_("variance red: ")));
3644 gtk_box_pack_start(GTK_BOX(hboxR), GTK_WIDGET(labelR), FALSE, FALSE, 0);
3645 g->label_var_R = GTK_LABEL(dt_ui_label_new("")); // This gets filled in by process
3646 gtk_widget_set_tooltip_text(GTK_WIDGET(g->label_var_R), _("variance computed on the red channel"));
3647 gtk_box_pack_start(GTK_BOX(hboxR), GTK_WIDGET(g->label_var_R), FALSE, FALSE, 0);
3648 gtk_box_pack_start(GTK_BOX(g->box_variance), GTK_WIDGET(hboxR), TRUE, TRUE, 0);
3649
3650 GtkBox *hboxG = GTK_BOX(gtk_box_new(GTK_ORIENTATION_HORIZONTAL, DT_GUI_BOX_SPACING));
3651 GtkLabel *labelG = GTK_LABEL(dt_ui_label_new(_("variance green: ")));
3652 gtk_box_pack_start(GTK_BOX(hboxG), GTK_WIDGET(labelG), FALSE, FALSE, 0);
3653 g->label_var_G = GTK_LABEL(dt_ui_label_new("")); // This gets filled in by process
3654 gtk_widget_set_tooltip_text(GTK_WIDGET(g->label_var_G), _("variance computed on the green channel"));
3655 gtk_box_pack_start(GTK_BOX(hboxG), GTK_WIDGET(g->label_var_G), FALSE, FALSE, 0);
3656 gtk_box_pack_start(GTK_BOX(g->box_variance), GTK_WIDGET(hboxG), TRUE, TRUE, 0);
3657
3658 GtkBox *hboxB = GTK_BOX(gtk_box_new(GTK_ORIENTATION_HORIZONTAL, DT_GUI_BOX_SPACING));
3659 GtkLabel *labelB = GTK_LABEL(dt_ui_label_new(_("variance blue: ")));
3660 gtk_box_pack_start(GTK_BOX(hboxB), GTK_WIDGET(labelB), FALSE, FALSE, 0);
3661 g->label_var_B = GTK_LABEL(dt_ui_label_new("")); // This gets filled in by process
3662 gtk_widget_set_tooltip_text(GTK_WIDGET(g->label_var_B), _("variance computed on the blue channel"));
3663 gtk_box_pack_start(GTK_BOX(hboxB), GTK_WIDGET(g->label_var_B), FALSE, FALSE, 0);
3664 gtk_box_pack_start(GTK_BOX(g->box_variance), GTK_WIDGET(hboxB), TRUE, TRUE, 0);
3665
3666 g_signal_connect(G_OBJECT(g->box_variance), "draw", G_CALLBACK(denoiseprofile_draw_variance), self);
3667
3668 // start building top level widget
3669 self->widget = gtk_box_new(GTK_ORIENTATION_VERTICAL, DT_GUI_BOX_SPACING);
3670
3672 dt_bauhaus_widget_set_label(g->profile, N_("profile"));
3673 g_signal_connect(G_OBJECT(g->profile), "value-changed", G_CALLBACK(profile_callback), self);
3674 gtk_box_pack_start(GTK_BOX(self->widget), g->profile, TRUE, TRUE, 0);
3675
3676 g->wb_adaptive_anscombe = dt_bauhaus_toggle_from_params(self, "wb_adaptive_anscombe");
3677
3679 dt_bauhaus_widget_set_label(g->mode, N_("mode"));
3680 dt_bauhaus_combobox_add(g->mode, _("non-local means"));
3681 dt_bauhaus_combobox_add(g->mode, _("non-local means auto"));
3682 dt_bauhaus_combobox_add(g->mode, _("wavelets"));
3683 dt_bauhaus_combobox_add(g->mode, _("wavelets auto"));
3684 const gboolean compute_variance = dt_conf_get_bool("plugins/darkroom/denoiseprofile/show_compute_variance_mode");
3685 if(compute_variance) dt_bauhaus_combobox_add(g->mode, _("compute variance"));
3686 g_signal_connect(G_OBJECT(g->mode), "value-changed", G_CALLBACK(mode_callback), self);
3687 gtk_box_pack_start(GTK_BOX(self->widget), g->mode, TRUE, TRUE, 0);
3688
3689 gtk_box_pack_start(GTK_BOX(self->widget), g->box_nlm, TRUE, TRUE, 0);
3690 gtk_box_pack_start(GTK_BOX(self->widget), g->box_wavelets, TRUE, TRUE, 0);
3691
3692 g->overshooting = dt_bauhaus_slider_from_params(self, "overshooting");
3693 g->strength = dt_bauhaus_slider_from_params(self, N_("strength"));
3694 dt_bauhaus_slider_set_soft_max(g->strength, 4.0f);
3695 dt_bauhaus_slider_set_digits(g->strength, 3);
3696 g->shadows = dt_bauhaus_slider_from_params(self, "shadows");
3697 g->bias = dt_bauhaus_slider_from_params(self, "bias");
3698 dt_bauhaus_slider_set_soft_range(g->bias, -10.0f, 10.0f);
3699
3700 // Set the overshooting soft range only once the sliders that its
3701 // value-changed handler (gui_changed) touches exist: changing a slider's
3702 // soft bound re-applies the value and emits "value-changed", which calls
3703 // gui_changed(overshooting) -> dt_bauhaus_slider_set(g->shadows / g->bias).
3704 // Those are created just above, so doing this earlier dereferenced NULL.
3705 dt_bauhaus_slider_set_soft_max(g->overshooting, 4.0f);
3706
3707 gtk_box_pack_start(GTK_BOX(self->widget), g->box_variance, TRUE, TRUE, 0);
3708
3709 g->fix_anscombe_and_nlmeans_norm = dt_bauhaus_toggle_from_params(self, "fix_anscombe_and_nlmeans_norm");
3710
3711 g->use_new_vst = dt_bauhaus_toggle_from_params(self, "use_new_vst");
3712
3713 gtk_widget_set_tooltip_text(g->wb_adaptive_anscombe, _("adapt denoising according to the\n"
3714 "white balance coefficients.\n"
3715 "should be enabled on a first instance\n"
3716 "for better denoising.\n"
3717 "should be disabled if an earlier instance\n"
3718 "has been used with a color blending mode."));
3719 gtk_widget_set_tooltip_text(g->fix_anscombe_and_nlmeans_norm, _("fix bugs in anscombe transform resulting\n"
3720 "in undersmoothing of the green channel in\n"
3721 "wavelets mode, combined with a bad handling\n"
3722 "of white balance coefficients, and a bug in\n"
3723 "non local means normalization resulting in\n"
3724 "undersmoothing when patch size was increased.\n"
3725 "enabling this option will change the denoising\n"
3726 "you get. once enabled, you won't be able to\n"
3727 "return back to old algorithm."));
3728 gtk_widget_set_tooltip_text(g->profile, _("profile used for variance stabilization"));
3729 gtk_widget_set_tooltip_text(g->mode, _("method used in the denoising core.\n"
3730 "non-local means works best for `lightness' blending,\n"
3731 "wavelets work best for `color' blending"));
3732 gtk_widget_set_tooltip_text(g->wavelet_color_mode, _("color representation used within the algorithm.\n"
3733 "RGB keeps the RGB channels separated,\n"
3734 "while Y0U0V0 combine the channels to\n"
3735 "denoise chroma and luma separately."));
3736 gtk_widget_set_tooltip_text(g->radius, _("radius of the patches to match.\n"
3737 "increase for more sharpness on strong edges, and better denoising of smooth areas.\n"
3738 "if details are oversmoothed, reduce this value or increase the central pixel weight slider."));
3739 gtk_widget_set_tooltip_text(g->nbhood, _("emergency use only: radius of the neighbourhood to search patches in. "
3740 "increase for better denoising performance, but watch the long runtimes! "
3741 "large radii can be very slow. you have been warned"));
3742 gtk_widget_set_tooltip_text(g->scattering, _("scattering of the neighbourhood to search patches in.\n"
3743 "increase for better coarse-grain noise reduction.\n"
3744 "does not affect execution time."));
3745 gtk_widget_set_tooltip_text(g->central_pixel_weight, _("increase the weight of the central pixel\n"
3746 "of the patch in the patch comparison.\n"
3747 "useful to recover details when patch size\n"
3748 "is quite big."));
3749 gtk_widget_set_tooltip_text(g->strength, _("finetune denoising strength"));
3750 gtk_widget_set_tooltip_text(g->overshooting, _("controls the way parameters are autoset\n"
3751 "increase if shadows are not denoised enough\n"
3752 "or if chroma noise remains.\n"
3753 "this can happen if your picture is underexposed."));
3754 gtk_widget_set_tooltip_text(g->shadows, _("finetune shadows denoising.\n"
3755 "decrease to denoise more aggressively\n"
3756 "dark areas of the image."));
3757 gtk_widget_set_tooltip_text(g->bias, _("correct color cast in shadows.\n"
3758 "decrease if shadows are too purple.\n"
3759 "increase if shadows are too green."));
3760 gtk_widget_set_tooltip_text(g->use_new_vst, _("upgrade the variance stabilizing algorithm.\n"
3761 "new algorithm extends the current one.\n"
3762 "it is more flexible but could give small\n"
3763 "differences in the images already processed."));
3764
3765}
3766
3768{
3770 g_list_free_full(g->profiles, dt_noiseprofile_free);
3771 g->profiles = NULL;
3772 dt_draw_curve_destroy(g->transition_curve);
3773 // nothing else necessary, gtk will clean up the slider.
3774
3776}
3777
3778// clang-format off
3779// modelines: These editor modelines have been set for all relevant files by tools/update_modelines.py
3780// vim: shiftwidth=2 expandtab tabstop=2 cindent
3781// kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-spaces modified;
3782// 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_soft_range(GtkWidget *widget, float soft_min, float soft_max)
Definition bauhaus.c:1650
void dt_bauhaus_slider_set_digits(GtkWidget *widget, int val)
Definition bauhaus.c:3549
void dt_bauhaus_combobox_clear(GtkWidget *widget)
Definition bauhaus.c:2192
void dt_bauhaus_slider_set_default(GtkWidget *widget, float def)
Definition bauhaus.c:1643
int dt_bauhaus_combobox_get(GtkWidget *widget)
Definition bauhaus.c:2350
void dt_bauhaus_slider_set_soft_max(GtkWidget *widget, float val)
Definition bauhaus.c:1626
int dt_bauhaus_combobox_length(GtkWidget *widget)
Definition bauhaus.c:2158
void dt_bauhaus_slider_set(GtkWidget *widget, float pos)
Definition bauhaus.c:3537
void dt_bauhaus_combobox_set(GtkWidget *widget, const int pos)
Definition bauhaus.c:2304
void dt_bauhaus_widget_set_label(GtkWidget *widget, const char *label)
Definition bauhaus.c:1656
GtkWidget * dt_bauhaus_combobox_new(dt_bauhaus_t *bh, dt_gui_module_t *self)
Definition bauhaus.c:1845
void dt_bauhaus_combobox_add(GtkWidget *widget, const char *text)
Definition bauhaus.c:2019
int width
Definition bilateral.h:1
int height
Definition bilateral.h:1
@ DEVELOP_BLEND_CS_RGB_SCENE
Definition blend.h:60
const double thrs
Definition chart/main.c:54
static const dt_aligned_pixel_simd_t const dt_adaptation_t const float p
@ IOP_CS_RGB
static dt_aligned_pixel_t rgb
const dt_aligned_pixel_t f
const float max
const dt_colormatrix_t dt_aligned_pixel_t out
const float delta
typedef void((*dt_cache_allocate_t)(void *userdata, dt_cache_entry_t *entry))
#define P(V, params)
int dt_conf_get_bool(const char *name)
#define CATMULL_ROM
Definition curve_tools.h:34
darktable_t darktable
Definition darktable.c:183
void dt_print(dt_debug_thread_t thread, const char *msg,...)
Definition darktable.c:1600
#define dt_free_align(ptr)
Definition darktable.h:503
static void * dt_calloc_align(size_t size)
Definition darktable.h:510
#define __OMP_SIMD__(...)
Definition darktable.h:274
@ DT_DEBUG_OPENCL
Definition darktable.h:744
#define dt_gui_freeze_begin()
Definition darktable.h:900
#define dt_gui_freeze_end()
Definition darktable.h:901
#define for_each_channel(_var,...)
Definition darktable.h:684
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:546
#define dt_pixelpipe_cache_alloc_align(size, pipe)
Definition darktable.h:449
static const dt_aligned_pixel_simd_t sign
Definition darktable.h:573
#define dt_free(ptr)
Definition darktable.h:478
void void void gboolean dt_gui_widgets_suppressed(void)
Definition gtk.c:194
#define DT_MODULE_INTROSPECTION(MODVER, PARAMSTYPE)
Definition darktable.h:151
#define dt_pixelpipe_cache_free_align(mem)
Definition darktable.h:475
#define __DT_CLONE_TARGETS__
Definition darktable.h:379
#define __OMP_PARALLEL_FOR__(...)
Definition darktable.h:270
#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:293
#define DT_CACHELINE_BYTES
Definition darktable.h:392
static int bucket_next(unsigned int *state, unsigned int max)
#define DT_IOP_DENOISE_PROFILE_BANDS
dt_iop_denoiseprofile_channel_t
@ DT_DENOISE_PROFILE_G
@ DT_DENOISE_PROFILE_R
@ DT_DENOISE_PROFILE_NONE
@ DT_DENOISE_PROFILE_ALL
@ DT_DENOISE_PROFILE_U0V0
@ DT_DENOISE_PROFILE_B
@ DT_DENOISE_PROFILE_Y0
void init(dt_iop_module_t *module)
#define NUM_BUCKETS
static __DT_CLONE_TARGETS__ void set_up_conversion_matrices(dt_colormatrix_t toY0U0V0, dt_colormatrix_t toRGB, const dt_aligned_pixel_t wb)
const char ** description(struct dt_iop_module_t *self)
int default_group()
static float infer_bias_from_profile(const float a)
void gui_reset(dt_iop_module_t *self)
#define DT_DENOISE_PROFILE_NONE_V9
static void profile_callback(GtkWidget *w, dt_iop_module_t *self)
void reload_defaults(dt_iop_module_t *module)
static __DT_CLONE_TARGETS__ void compute_wb_factors(dt_aligned_pixel_t wb, const dt_iop_denoiseprofile_data_t *const d, const dt_dev_pixelpipe_iop_t *const piece, const dt_aligned_pixel_t weights)
static __DT_CLONE_TARGETS__ void backtransform_v2(float *const buf, const int wd, const int ht, const float a, const dt_aligned_pixel_t p, const float b, const float bias, const dt_aligned_pixel_t wb)
void commit_params(struct dt_iop_module_t *self, dt_iop_params_t *params, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece)
static int process_nlmeans_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, const dt_iop_roi_t *const roi_in, const dt_iop_roi_t *const roi_out)
static void dt_iop_denoiseprofile_get_params(dt_iop_denoiseprofile_params_t *p, const int ch, const double mouse_x, const double mouse_y, const float rad)
#define REDUCESIZE
void gui_update(dt_iop_module_t *self)
static __DT_CLONE_TARGETS__ void backtransform(float *const buf, const int wd, const int ht, const dt_aligned_pixel_t a, const dt_aligned_pixel_t b)
static int process_wavelets_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, const dt_iop_roi_t *const roi_in, const dt_iop_roi_t *const roi_out)
void init_pipe(struct dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece)
static dt_noiseprofile_t dt_iop_denoiseprofile_get_auto_profile(dt_iop_module_t *self)
#define debug_dump_PFM(p, n, b, w, h, s)
static gboolean denoiseprofile_button_release(GtkWidget *widget, GdkEventButton *event, gpointer user_data)
const char * name()
#define DT_IOP_DENOISE_PROFILE_INSET
#define MAX_MAX_SCALE
static float nlmeans_precondition_cl(const dt_iop_denoiseprofile_data_t *const d, const dt_dev_pixelpipe_iop_t *const piece, dt_aligned_pixel_t wb, float scale, dt_aligned_pixel_t aa, dt_aligned_pixel_t bb, dt_aligned_pixel_t p)
void gui_init(dt_iop_module_t *self)
void gui_changed(dt_iop_module_t *self, GtkWidget *w, void *previous)
#define DT_IOP_DENOISE_PROFILE_RES
dt_iop_denoiseprofile_wavelet_mode_t
@ MODE_RGB
@ MODE_Y0U0V0
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)
static __DT_CLONE_TARGETS__ void backtransform_Y0U0V0(float *const buf, const int wd, const int ht, const float a, const dt_aligned_pixel_t p, const float b, const float bias, const dt_aligned_pixel_t wb, const dt_colormatrix_t toRGB)
void gui_cleanup(dt_iop_module_t *self)
static gboolean denoiseprofile_draw_variance(GtkWidget *widget, cairo_t *crf, gpointer user_data)
static float infer_scattering_from_profile(const float a)
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)
static float infer_shadows_from_profile(const float a)
static __DT_CLONE_TARGETS__ int process_wavelets(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, const dt_iop_roi_t *const roi_in, const dt_iop_roi_t *const roi_out, const eaw_dn_decompose_t decompose, const eaw_synthesize_t synthesize)
#define DT_IOP_DENOISE_PROFILE_V8_BANDS
int flags()
static gboolean denoiseprofile_scrolled(GtkWidget *widget, GdkEventScroll *event, gpointer user_data)
static void denoiseprofile_tab_switch(GtkNotebook *notebook, GtkWidget *page, guint page_num, gpointer user_data)
dt_iop_denoiseprofile_mode_t
@ MODE_WAVELETS
@ MODE_NLMEANS_AUTO
@ MODE_WAVELETS_AUTO
@ MODE_NLMEANS
@ MODE_VARIANCE
#define DT_IOP_DENOISE_PROFILE_P_FULCRUM
static __DT_CLONE_TARGETS__ void precondition_v2(const float *const in, float *const buf, const int wd, const int ht, const float a, const dt_aligned_pixel_t p, const float b, const dt_aligned_pixel_t wb)
void init_presets(dt_iop_module_so_t *self)
static void mode_callback(GtkWidget *w, dt_iop_module_t *self)
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 __DT_CLONE_TARGETS__ void precondition_Y0U0V0(const float *const in, float *const buf, const int wd, const int ht, const float a, const dt_aligned_pixel_t p, const float b, const dt_colormatrix_t toY0U0V0)
static __DT_CLONE_TARGETS__ void sum_rec(const size_t npixels, const float *in, float *out)
static __DT_CLONE_TARGETS__ void variance_rec(const size_t npixels, const float *in, float *out, const dt_aligned_pixel_t mean)
void cleanup_pipe(struct dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece)
static gboolean denoiseprofile_leave_notify(GtkWidget *widget, GdkEventCrossing *event, gpointer user_data)
static gboolean denoiseprofile_motion_notify(GtkWidget *widget, GdkEventMotion *event, gpointer user_data)
static dt_noiseprofile_t _default_noise_profile(dt_iop_module_t *module)
static gboolean denoiseprofile_button_press(GtkWidget *widget, GdkEventButton *event, gpointer user_data)
void init_global(dt_iop_module_so_t *module)
static gboolean denoiseprofile_draw(GtkWidget *widget, cairo_t *crf, gpointer user_data)
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 __DT_CLONE_TARGETS__ void precondition(const float *const in, float *const buf, const int wd, const int ht, const dt_aligned_pixel_t a, const dt_aligned_pixel_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 unsigned infer_radius_from_profile(const float a)
#define dt_dev_add_history_item(dev, module, enable, redraw)
void dt_iop_params_t
Definition dev_history.h:42
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:414
@ DT_DEV_PIXELPIPE_DISPLAY_MASK
Definition develop.h:118
static void dt_draw_curve_calc_values(dt_draw_curve_t *c, const float min, const float max, const int res, float *x, float *y)
Definition draw.h:309
static void dt_draw_grid(cairo_t *cr, const int num, const int left, const int top, const int right, const int bottom)
Definition draw.h:143
static float dt_draw_curve_calc_value(dt_draw_curve_t *c, const float x)
Definition draw.h:345
static void dt_draw_curve_destroy(dt_draw_curve_t *c)
Definition draw.h:282
static void dt_draw_curve_set_point(dt_draw_curve_t *c, const int num, const float x, const float y)
Definition draw.h:288
static int dt_draw_curve_add_point(dt_draw_curve_t *c, const float x, const float y)
Definition draw.h:364
static dt_draw_curve_t * dt_draw_curve_new(const float min, const float max, unsigned int type)
Definition draw.h:266
void eaw_dn_decompose(float *const restrict out, const float *const restrict in, float *const restrict detail, dt_aligned_pixel_t sum_squared, const int scale, const float inv_sigma2, const int32_t width, const int32_t height)
Definition eaw.c:243
void eaw_synthesize(float *const out, const float *const in, const float *const restrict detail, const float *const restrict threshold, const float *const restrict boost, const int32_t width, const int32_t height)
Definition eaw.c:158
gboolean dt_gui_get_scroll_unit_deltas(const GdkEventScroll *event, int *delta_x, int *delta_y)
Definition gtk.c:300
GtkWidget * dt_ui_resizable_drawing_area(GtkWidget *area, char *config_str, int default_height, int min_height)
Make a self-drawing widget (typically a GtkDrawingArea graph or scope) vertically resizable.
Definition gtk.c:3068
GtkWidget * dt_ui_notebook_page(GtkNotebook *notebook, const char *text, const char *tooltip)
Definition gtk.c:2470
static cairo_surface_t * dt_cairo_image_surface_create(cairo_format_t format, int width, int height)
Definition gtk.h:322
#define DT_GUI_BOX_SPACING
Definition gtk.h:109
#define DT_PIXEL_APPLY_DPI(value)
Definition gtk.h:90
static GtkWidget * dt_ui_label_new(const gchar *str)
Definition gtk.h:479
void dt_gui_presets_add_generic(const char *name, dt_dev_operation_t op, const int32_t version, const void *params, const int32_t params_size, const int32_t enabled, const dt_develop_blend_colorspace_t blend_cst)
#define DT_GUI_MODULE(x)
int dt_iop_alloc_image_buffers(struct dt_iop_module_t *const module, const struct dt_iop_roi_t *const roi_in, const struct dt_iop_roi_t *const roi_out,...)
Definition imagebuf.c:31
void dt_iop_copy_image_roi(float *const __restrict__ out, const float *const __restrict__ in, const size_t ch, const dt_iop_roi_t *const __restrict__ roi_in, const dt_iop_roi_t *const __restrict__ roi_out, const int zero_pad)
Definition imagebuf.c:159
#define DT_IMGSZ_INPUT
Definition imagebuf.h:55
void dt_iop_default_init(dt_iop_module_t *module)
Definition imageop.c:321
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:3220
#define IOP_GUI_FREE
Definition imageop.h:641
@ IOP_FLAGS_SUPPORTS_BLENDING
Definition imageop.h:197
@ IOP_FLAGS_ALLOW_TILING
Definition imageop.h:199
@ IOP_GROUP_REPAIR
Definition imageop.h:170
#define IOP_GUI_ALLOC(module)
Definition imageop.h:638
GtkWidget * dt_bauhaus_toggle_from_params(dt_iop_module_t *self, const char *param)
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
const int t
float *const restrict const size_t k
float *const restrict const size_t const size_t ch
#define M_PI
Definition math.h:45
float DT_ALIGNED_ARRAY dt_colormatrix_t[4][4]
Definition matrices.h:33
__DT_CLONE_TARGETS__ void nlmeans_denoise(const float *const inbuf, float *const outbuf, const dt_iop_roi_t *const roi_in, const dt_iop_roi_t *const roi_out, const dt_nlmeans_param_t *const params)
int nlmeans_denoiseprofile_cl(const dt_nlmeans_param_t *const params, const int devid, cl_mem dev_in, cl_mem dev_out, const dt_iop_roi_t *const roi_in)
float dt_aligned_pixel_t[4]
const dt_noiseprofile_t dt_noiseprofile_generic
void dt_noiseprofile_interpolate(const dt_noiseprofile_t *const p1, const dt_noiseprofile_t *const p2, dt_noiseprofile_t *out)
void dt_noiseprofile_free(gpointer data)
GList * dt_noiseprofile_get_matching(const dt_image_t *cimg)
int dt_opencl_local_buffer_opt(const int devid, const int kernel, dt_opencl_local_buffer_t *factors)
Definition opencl.c:3286
int dt_opencl_enqueue_kernel_2d(const int dev, const int kernel, const size_t *sizes)
Definition opencl.c:2164
void * dt_opencl_alloc_device_buffer(const int devid, const size_t size)
Definition opencl.c:2580
void * dt_opencl_alloc_device(const int devid, const int width, const int height, const int bpp)
Definition opencl.c:2504
int dt_opencl_create_kernel(const int prog, const char *name)
Definition opencl.c:2058
void * dt_opencl_copy_host_to_device_constant(const int devid, const size_t size, void *host)
Definition opencl.c:2360
int dt_opencl_enqueue_copy_image(const int devid, cl_mem src, cl_mem dst, size_t *orig_src, size_t *orig_dst, size_t *region)
Definition opencl.c:2289
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:2337
void dt_opencl_free_kernel(const int kernel)
Definition opencl.c:2101
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:2155
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:2170
void dt_opencl_release_mem_object(cl_mem mem)
Definition opencl.c:2415
#define ROUNDUP(a, n)
Definition opencl.h:78
#define ROUNDUPDHT(a, b)
Definition opencl.h:82
#define ROUNDUPDWD(a, b)
Definition opencl.h:81
@ DT_DEV_PIXELPIPE_THUMBNAIL
Definition pixelpipe.h:41
Pixelpipe cache for storing intermediate results in the pixelpipe.
struct _GtkWidget GtkWidget
Definition splash.h:29
const float uint32_t state[4]
const float sigma
struct dt_gui_gtk_t * gui
Definition darktable.h:803
struct dt_bauhaus_t * bauhaus
Definition darktable.h:806
struct dt_develop_t * develop
Definition darktable.h:798
PangoFontDescription * pango_font_desc
Definition bauhaus.h:274
dt_iop_buffer_dsc_t dsc_in
struct dt_iop_module_t *void * data
dt_dev_pixelpipe_type_t type
dt_image_t image_storage
Definition develop.h:259
gint scroll_mask
Definition gtk.h:230
float exif_iso
Definition image.h:288
struct dt_iop_buffer_dsc_t::@33 temperature
dt_aligned_pixel_t coeffs
Definition format.h:81
unsigned int channels
Definition format.h:54
dt_aligned_pixel_t processed_maximum
Definition format.h:85
dt_draw_curve_t * curve[DT_DENOISE_PROFILE_NONE]
dt_iop_denoiseprofile_wavelet_mode_t wavelet_color_mode
dt_iop_denoiseprofile_channel_t channel
float force[DT_DENOISE_PROFILE_NONE][7]
dt_iop_denoiseprofile_mode_t mode
dt_iop_denoiseprofile_params_t drag_params
dt_iop_denoiseprofile_channel_t channel
float x[DT_DENOISE_PROFILE_NONE][7]
dt_iop_denoiseprofile_mode_t mode
float y[DT_DENOISE_PROFILE_NONE][7]
dt_iop_denoiseprofile_wavelet_mode_t wavelet_color_mode
dt_iop_denoiseprofile_mode_t mode
float x[DT_DENOISE_PROFILE_NONE][7]
float y[DT_DENOISE_PROFILE_NONE][7]
dt_iop_denoiseprofile_wavelet_mode_t wavelet_color_mode
dt_iop_denoiseprofile_mode_t mode
dt_iop_denoiseprofile_mode_t mode
dt_iop_denoiseprofile_mode_t mode
dt_iop_denoiseprofile_mode_t mode
dt_iop_denoiseprofile_mode_t mode
dt_iop_denoiseprofile_mode_t mode
dt_iop_denoiseprofile_mode_t mode
GModule *dt_dev_operation_t op
Definition imageop.h:260
dt_iop_global_data_t * data
Definition imageop.h:263
dt_iop_params_t * default_params
Definition imageop.h:344
GtkWidget * widget
Definition imageop.h:374
struct dt_develop_t * dev
Definition imageop.h:333
dt_iop_gui_data_t * gui_data
Definition imageop.h:348
dt_iop_global_data_t * global_data
Definition imageop.h:351
dt_iop_params_t * params
Definition imageop.h:344
Region of interest passed through the pixelpipe.
Definition imageop.h:72
double scale
Definition imageop.h:74
dt_aligned_pixel_t a
dt_aligned_pixel_t b
GtkWidget * notebook
#define MIN(a, b)
Definition thinplate.c:32
#define MAX(a, b)
Definition thinplate.c:29