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
2676{
2678 if(!IS_NULL_PTR(g))
2679 {
2680 dt_bauhaus_combobox_clear(g->profile);
2681
2682 // get matching profiles:
2683 char name[512];
2684 if(g->profiles)
2685 {
2686 g_list_free_full(g->profiles, dt_noiseprofile_free);
2687 g->profiles = NULL;
2688 }
2689 g->profiles = dt_noiseprofile_get_matching(&module->dev->image_storage);
2690 g->interpolated = dt_noiseprofile_generic; // default to generic poissonian
2691 g_strlcpy(name, _(g->interpolated.name), sizeof(name));
2692
2693 const int iso = module->dev->image_storage.exif_iso;
2694 dt_noiseprofile_t *last = NULL;
2695 for(GList *iter = g->profiles; iter; iter = g_list_next(iter))
2696 {
2697 dt_noiseprofile_t *current = (dt_noiseprofile_t *)iter->data;
2698
2699 if(current->iso == iso)
2700 {
2701 g->interpolated = *current;
2702 // signal later autodetection in commit_params:
2703 g->interpolated.a[0] = -1.0f;
2704 snprintf(name, sizeof(name), _("found match for ISO %d"), iso);
2705 break;
2706 }
2707 if(last && last->iso < iso && current->iso > iso)
2708 {
2709 g->interpolated.iso = iso;
2710 dt_noiseprofile_interpolate(last, current, &g->interpolated);
2711 // signal later autodetection in commit_params:
2712 g->interpolated.a[0] = -1.0f;
2713 snprintf(name, sizeof(name), _("interpolated from ISO %d and %d"), last->iso, current->iso);
2714 break;
2715 }
2716 last = current;
2717 }
2718
2719 dt_bauhaus_combobox_add(g->profile, name);
2720 for(GList *iter = g->profiles; iter; iter = g_list_next(iter))
2721 {
2722 dt_noiseprofile_t *profile = (dt_noiseprofile_t *)iter->data;
2723 dt_bauhaus_combobox_add(g->profile, profile->name);
2724 }
2725
2726 // set defaults depending on the profile
2727 // all these formulas were "guessed" and are completely empirical
2728 const float a = g->interpolated.a[1];
2729 dt_iop_denoiseprofile_params_t *d = module->default_params;
2730
2732 d->scattering = infer_scattering_from_profile(a);
2733 d->shadows = infer_shadows_from_profile(a);
2734 d->bias = infer_bias_from_profile(a);
2735
2736 dt_bauhaus_slider_set_default(g->radius, d->radius);
2737 dt_bauhaus_slider_set_default(g->scattering, d->scattering);
2738 dt_bauhaus_slider_set_default(g->shadows, d->shadows);
2739 dt_bauhaus_slider_set_default(g->bias, d->bias);
2740
2741 for(int k = 0; k < 3; k++)
2742 {
2743 d->a[k] = g->interpolated.a[k];
2744 d->b[k] = g->interpolated.b[k];
2745 }
2746 }
2747}
2748
2750{
2751 const int program = 11; // denoiseprofile.cl, from programs.conf
2754 module->data = gd;
2755 gd->kernel_denoiseprofile_precondition = dt_opencl_create_kernel(program, "denoiseprofile_precondition");
2756 gd->kernel_denoiseprofile_precondition_v2 = dt_opencl_create_kernel(program, "denoiseprofile_precondition_v2");
2757 gd->kernel_denoiseprofile_precondition_Y0U0V0 = dt_opencl_create_kernel(program, "denoiseprofile_precondition_Y0U0V0");
2758 gd->kernel_denoiseprofile_init = dt_opencl_create_kernel(program, "denoiseprofile_init");
2759 gd->kernel_denoiseprofile_dist = dt_opencl_create_kernel(program, "denoiseprofile_dist");
2760 gd->kernel_denoiseprofile_horiz = dt_opencl_create_kernel(program, "denoiseprofile_horiz");
2761 gd->kernel_denoiseprofile_vert = dt_opencl_create_kernel(program, "denoiseprofile_vert");
2762 gd->kernel_denoiseprofile_accu = dt_opencl_create_kernel(program, "denoiseprofile_accu");
2763 gd->kernel_denoiseprofile_finish = dt_opencl_create_kernel(program, "denoiseprofile_finish");
2764 gd->kernel_denoiseprofile_finish_v2 = dt_opencl_create_kernel(program, "denoiseprofile_finish_v2");
2765 gd->kernel_denoiseprofile_backtransform = dt_opencl_create_kernel(program, "denoiseprofile_backtransform");
2766 gd->kernel_denoiseprofile_backtransform_v2 = dt_opencl_create_kernel(program, "denoiseprofile_backtransform_v2");
2767 gd->kernel_denoiseprofile_backtransform_Y0U0V0 = dt_opencl_create_kernel(program, "denoiseprofile_backtransform_Y0U0V0");
2768 gd->kernel_denoiseprofile_decompose = dt_opencl_create_kernel(program, "denoiseprofile_decompose");
2769 gd->kernel_denoiseprofile_synthesize = dt_opencl_create_kernel(program, "denoiseprofile_synthesize");
2770 gd->kernel_denoiseprofile_reduce_first = dt_opencl_create_kernel(program, "denoiseprofile_reduce_first");
2771 gd->kernel_denoiseprofile_reduce_second = dt_opencl_create_kernel(program, "denoiseprofile_reduce_second");
2772}
2773
2794
2796{
2797 GList *profiles = dt_noiseprofile_get_matching(&self->dev->image_storage);
2798 dt_noiseprofile_t interpolated = dt_noiseprofile_generic; // default to generic poissonian
2799
2800 const int iso = self->dev->image_storage.exif_iso;
2801 dt_noiseprofile_t *last = NULL;
2802 for(GList *iter = profiles; iter; iter = g_list_next(iter))
2803 {
2804 dt_noiseprofile_t *current = (dt_noiseprofile_t *)iter->data;
2805 if(current->iso == iso)
2806 {
2807 interpolated = *current;
2808 break;
2809 }
2810 if(last && last->iso < iso && current->iso > iso)
2811 {
2812 interpolated.iso = iso;
2813 dt_noiseprofile_interpolate(last, current, &interpolated);
2814 break;
2815 }
2816 last = current;
2817 }
2818 g_list_free_full(profiles, dt_noiseprofile_free);
2819 profiles = NULL;
2820 return interpolated;
2821}
2822
2826{
2829
2830 d->nbhood = p->nbhood;
2831 d->central_pixel_weight = p->central_pixel_weight;
2832 d->strength = p->strength;
2833 d->overshooting = p->overshooting;
2834 for(int i = 0; i < 3; i++)
2835 {
2836 d->a[i] = p->a[i];
2837 d->b[i] = p->b[i];
2838 }
2839 d->mode = p->mode;
2840 d->wavelet_color_mode = p->wavelet_color_mode;
2841
2842 // compare if a[0] in params is set to "magic value" -1.0 for autodetection
2843 if(p->a[0] == -1.0)
2844 {
2845 // autodetect matching profile again, the same way as detecting their names,
2846 // this is partially duplicated code and data because we are not allowed to access
2847 // gui_data here ..
2849 for(int k = 0; k < 3; k++)
2850 {
2851 d->a[k] = interpolated.a[k];
2852 d->b[k] = interpolated.b[k];
2853 }
2854 }
2855
2856 if((p->mode == MODE_NLMEANS_AUTO) || (p->mode == MODE_WAVELETS_AUTO))
2857 {
2858 const float gain = p->overshooting;
2859 d->radius = infer_radius_from_profile(d->a[1] * gain);
2860 d->scattering = infer_scattering_from_profile(d->a[1] * gain);
2861 d->shadows = infer_shadows_from_profile(d->a[1] * gain);
2862 d->bias = infer_bias_from_profile(d->a[1] * gain);
2863 }
2864 else
2865 {
2866 d->radius = p->radius;
2867 d->scattering = p->scattering;
2868 d->shadows = p->shadows;
2869 d->bias = p->bias;
2870 }
2871
2872 for(int ch = 0; ch < DT_DENOISE_PROFILE_NONE; ch++)
2873 {
2874 dt_draw_curve_set_point(d->curve[ch], 0, p->x[ch][DT_IOP_DENOISE_PROFILE_BANDS - 2] - 1.f, p->y[ch][0]);
2875 for(int k = 0; k < DT_IOP_DENOISE_PROFILE_BANDS; k++)
2876 dt_draw_curve_set_point(d->curve[ch], k, p->x[ch][k], p->y[ch][k]);
2877 dt_draw_curve_set_point(d->curve[ch], DT_IOP_DENOISE_PROFILE_BANDS + 1, p->x[ch][1] + 1.f,
2879 dt_draw_curve_calc_values(d->curve[ch], 0.0, 1.0, DT_IOP_DENOISE_PROFILE_BANDS, NULL, d->force[ch]);
2880 }
2881
2882 d->wb_adaptive_anscombe = p->wb_adaptive_anscombe;
2883 d->fix_anscombe_and_nlmeans_norm = p->fix_anscombe_and_nlmeans_norm;
2884 d->use_new_vst = p->use_new_vst;
2885 piece->cache_output_on_ram = TRUE;
2886}
2887
2889{
2892
2893 piece->data = (void *)d;
2894 piece->data_size = sizeof(dt_iop_denoiseprofile_data_t);
2895 for(int ch = 0; ch < DT_DENOISE_PROFILE_NONE; ch++)
2896 {
2897 d->curve[ch] = dt_draw_curve_new(0.0, 1.0, CATMULL_ROM);
2898 for(int k = 0; k < DT_IOP_DENOISE_PROFILE_BANDS; k++)
2899 (void)dt_draw_curve_add_point(d->curve[ch], default_params->x[ch][k], default_params->y[ch][k]);
2900 }
2901}
2902
2904{
2906 for(int ch = 0; ch < DT_DENOISE_PROFILE_NONE; ch++) dt_draw_curve_destroy(d->curve[ch]);
2907 dt_free_align(piece->data);
2908 piece->data = NULL;
2909}
2910
2912{
2913 int i = dt_bauhaus_combobox_get(w);
2916 const dt_noiseprofile_t *profile = &(g->interpolated);
2917 if(i > 0) profile = (dt_noiseprofile_t *)g_list_nth_data(g->profiles, i - 1);
2918 for(int k = 0; k < 3; k++)
2919 {
2920 p->a[k] = profile->a[k];
2921 p->b[k] = profile->b[k];
2922 }
2924}
2925
2927{
2930 const unsigned mode = dt_bauhaus_combobox_get(w);
2931 switch(mode)
2932 {
2933 case 0:
2934 p->mode = MODE_NLMEANS;
2935 gtk_widget_hide(g->box_wavelets);
2936 gtk_widget_hide(g->box_variance);
2937 gtk_widget_show_all(g->box_nlm);
2938 break;
2939 case 1:
2940 p->mode = MODE_NLMEANS_AUTO;
2941 gtk_widget_hide(g->box_wavelets);
2942 gtk_widget_hide(g->box_variance);
2943 gtk_widget_show_all(g->box_nlm);
2944 gtk_widget_set_visible(g->radius, FALSE);
2945 gtk_widget_set_visible(g->nbhood, FALSE);
2946 gtk_widget_set_visible(g->scattering, FALSE);
2947 break;
2948 case 2:
2949 p->mode = MODE_WAVELETS;
2950 gtk_widget_hide(g->box_nlm);
2951 gtk_widget_hide(g->box_variance);
2952 gtk_widget_show_all(g->box_wavelets);
2953 gtk_widget_set_visible(GTK_WIDGET(g->wavelet_color_mode), p->use_new_vst);
2954 gtk_widget_set_visible(GTK_WIDGET(g->channel_tabs), p->use_new_vst && (p->wavelet_color_mode == MODE_RGB));
2955 gtk_widget_set_visible(GTK_WIDGET(g->channel_tabs_Y0U0V0), p->use_new_vst && (p->wavelet_color_mode == MODE_Y0U0V0));
2956 break;
2957 case 3:
2958 p->mode = MODE_WAVELETS_AUTO;
2959 gtk_widget_hide(g->box_nlm);
2960 gtk_widget_hide(g->box_variance);
2961 gtk_widget_show_all(g->box_wavelets);
2962 gtk_widget_set_visible(GTK_WIDGET(g->wavelet_color_mode), p->use_new_vst);
2963 gtk_widget_set_visible(GTK_WIDGET(g->channel_tabs), p->use_new_vst && (p->wavelet_color_mode == MODE_RGB));
2964 gtk_widget_set_visible(GTK_WIDGET(g->channel_tabs_Y0U0V0), p->use_new_vst && (p->wavelet_color_mode == MODE_Y0U0V0));
2965 break;
2966 case 4:
2967 p->mode = MODE_VARIANCE;
2968 gtk_widget_hide(g->box_wavelets);
2969 gtk_widget_hide(g->box_nlm);
2970 gtk_widget_show_all(g->box_variance);
2971 break;
2972 }
2973 const gboolean auto_mode = (p->mode == MODE_NLMEANS_AUTO) || (p->mode == MODE_WAVELETS_AUTO);
2974 gtk_widget_set_visible(g->shadows, p->use_new_vst && !auto_mode);
2975 gtk_widget_set_visible(g->bias, p->use_new_vst && !auto_mode);
2976 gtk_widget_set_visible(g->overshooting, auto_mode);
2978}
2979
2980void gui_changed(dt_iop_module_t *self, GtkWidget *w, void *previous)
2981{
2984
2985 if(w == g->wavelet_color_mode)
2986 {
2987 gtk_widget_set_visible(GTK_WIDGET(g->channel_tabs), (p->wavelet_color_mode == MODE_RGB));
2988 gtk_widget_set_visible(GTK_WIDGET(g->channel_tabs_Y0U0V0), (p->wavelet_color_mode == MODE_Y0U0V0));
2989 if(p->wavelet_color_mode == MODE_RGB)
2990 g->channel = DT_DENOISE_PROFILE_ALL;
2991 else
2992 g->channel = DT_DENOISE_PROFILE_Y0;
2993 }
2994 else if(w == g->overshooting)
2995 {
2996 const float gain = p->overshooting;
2997 float a = p->a[1];
2998 if(p->a[0] == -1.0)
2999 {
3001 a = interpolated.a[1];
3002 }
3003 // set the sliders as visible while we are setting their values
3004 // otherwise a log message appears
3005 if(p->mode == MODE_NLMEANS_AUTO)
3006 {
3007 gtk_widget_set_visible(g->radius, TRUE);
3008 gtk_widget_set_visible(g->scattering, TRUE);
3011 gtk_widget_set_visible(g->radius, FALSE);
3012 gtk_widget_set_visible(g->scattering, FALSE);
3013 }
3014 else
3015 {
3016 // we are in wavelets mode.
3017 // we need to show the box_nlm, setting the sliders to visible is not enough
3018 gtk_widget_show_all(g->box_nlm);
3021 gtk_widget_hide(g->box_nlm);
3022 }
3023 gtk_widget_set_visible(g->shadows, TRUE);
3024 gtk_widget_set_visible(g->bias, TRUE);
3027 gtk_widget_set_visible(g->shadows, FALSE);
3028 gtk_widget_set_visible(g->bias, FALSE);
3029 }
3030 else if(w == g->use_new_vst)
3031 {
3032 const gboolean auto_mode = (p->mode == MODE_NLMEANS_AUTO) || (p->mode == MODE_WAVELETS_AUTO);
3033 gtk_widget_set_visible(g->shadows, p->use_new_vst && !auto_mode);
3034 gtk_widget_set_visible(g->bias, p->use_new_vst && !auto_mode);
3035 gtk_widget_set_visible(g->wavelet_color_mode, p->use_new_vst);
3036 if(!p->use_new_vst && p->wavelet_color_mode == MODE_Y0U0V0)
3037 p->wavelet_color_mode = MODE_RGB;
3038 }
3039}
3040
3042{
3045
3046 dt_bauhaus_combobox_set(g->profile, -1);
3047 unsigned combobox_index = 0;
3048 switch (p->mode)
3049 {
3050 case MODE_NLMEANS:
3051 combobox_index = 0;
3052 gtk_widget_hide(g->box_wavelets);
3053 gtk_widget_hide(g->box_variance);
3054 gtk_widget_show_all(g->box_nlm);
3055 break;
3056 case MODE_NLMEANS_AUTO:
3057 combobox_index = 1;
3058 gtk_widget_hide(g->box_wavelets);
3059 gtk_widget_hide(g->box_variance);
3060 gtk_widget_show_all(g->box_nlm);
3061 gtk_widget_set_visible(g->radius, FALSE);
3062 gtk_widget_set_visible(g->nbhood, FALSE);
3063 gtk_widget_set_visible(g->scattering, FALSE);
3064 break;
3065 case MODE_WAVELETS:
3066 combobox_index = 2;
3067 gtk_widget_hide(g->box_nlm);
3068 gtk_widget_hide(g->box_variance);
3069 gtk_widget_show_all(g->box_wavelets);
3070 break;
3071 case MODE_WAVELETS_AUTO:
3072 combobox_index = 3;
3073 gtk_widget_hide(g->box_nlm);
3074 gtk_widget_hide(g->box_variance);
3075 gtk_widget_show_all(g->box_wavelets);
3076 break;
3077 case MODE_VARIANCE:
3078 combobox_index = 4;
3079 gtk_widget_hide(g->box_wavelets);
3080 gtk_widget_hide(g->box_nlm);
3081 gtk_widget_show_all(g->box_variance);
3082 if(dt_bauhaus_combobox_length(g->mode) == 4)
3083 {
3084 dt_bauhaus_combobox_add(g->mode, _("compute variance"));
3085 }
3086 break;
3087 }
3088 float a = p->a[1];
3089 if(p->a[0] == -1.0)
3090 {
3092 a = interpolated.a[1];
3093 }
3094 if((p->mode == MODE_NLMEANS_AUTO) || (p->mode == MODE_WAVELETS_AUTO))
3095 {
3096 const float gain = p->overshooting;
3101 }
3102 dt_bauhaus_combobox_set(g->mode, combobox_index);
3103 if(p->a[0] == -1.0)
3104 {
3105 dt_bauhaus_combobox_set(g->profile, 0);
3106 }
3107 else
3108 {
3109 int i = 1;
3110 for(GList *iter = g->profiles; iter; iter = g_list_next(iter), i++)
3111 {
3112 dt_noiseprofile_t *profile = (dt_noiseprofile_t *)iter->data;
3113 if(!memcmp(profile->a, p->a, sizeof(float) * 3)
3114 && !memcmp(profile->b, p->b, sizeof(float) * 3))
3115 {
3116 dt_bauhaus_combobox_set(g->profile, i);
3117 break;
3118 }
3119 }
3120 }
3121 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(g->wb_adaptive_anscombe), p->wb_adaptive_anscombe);
3122 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(g->fix_anscombe_and_nlmeans_norm), p->fix_anscombe_and_nlmeans_norm);
3123 gtk_widget_set_visible(g->fix_anscombe_and_nlmeans_norm, !p->fix_anscombe_and_nlmeans_norm);
3124 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(g->use_new_vst), p->use_new_vst);
3125 gtk_widget_set_visible(g->use_new_vst, !p->use_new_vst);
3126 const gboolean auto_mode = (p->mode == MODE_NLMEANS_AUTO) || (p->mode == MODE_WAVELETS_AUTO);
3127 const gboolean wavelet_mode = (p->mode == MODE_WAVELETS) || (p->mode == MODE_WAVELETS_AUTO);
3128 gtk_widget_set_visible(g->overshooting, auto_mode);
3129 gtk_widget_set_visible(g->wavelet_color_mode, p->use_new_vst && wavelet_mode);
3130 gtk_widget_set_visible(g->shadows, p->use_new_vst && !auto_mode);
3131 gtk_widget_set_visible(g->bias, p->use_new_vst && !auto_mode);
3132 gtk_widget_set_visible(GTK_WIDGET(g->channel_tabs), (p->wavelet_color_mode == MODE_RGB));
3133 gtk_widget_set_visible(GTK_WIDGET(g->channel_tabs_Y0U0V0), (p->wavelet_color_mode == MODE_Y0U0V0));
3134 if((p->wavelet_color_mode == MODE_Y0U0V0) && (g->channel < DT_DENOISE_PROFILE_Y0))
3135 {
3136 g->channel = DT_DENOISE_PROFILE_Y0;
3137 gtk_notebook_set_current_page(GTK_NOTEBOOK(g->channel_tabs_Y0U0V0), g->channel - DT_DENOISE_PROFILE_Y0);
3138 }
3139 if((p->wavelet_color_mode == MODE_RGB) && (g->channel > DT_DENOISE_PROFILE_B))
3140 {
3141 g->channel = DT_DENOISE_PROFILE_ALL;
3142 gtk_notebook_set_current_page(GTK_NOTEBOOK(g->channel_tabs), g->channel);
3143 }
3144}
3145
3147{
3150 if(p->wavelet_color_mode == MODE_Y0U0V0)
3151 {
3152 g->channel = DT_DENOISE_PROFILE_Y0;
3153 gtk_notebook_set_current_page(GTK_NOTEBOOK(g->channel_tabs_Y0U0V0), g->channel - DT_DENOISE_PROFILE_Y0);
3154 }
3155 else
3156 {
3157 g->channel = DT_DENOISE_PROFILE_ALL;
3158 gtk_notebook_set_current_page(GTK_NOTEBOOK(g->channel_tabs), g->channel);
3159 }
3160 gtk_widget_set_visible(g->fix_anscombe_and_nlmeans_norm, !p->fix_anscombe_and_nlmeans_norm);
3161 gtk_widget_set_visible(g->use_new_vst, !p->use_new_vst);
3162}
3163
3164static void dt_iop_denoiseprofile_get_params(dt_iop_denoiseprofile_params_t *p, const int ch, const double mouse_x,
3165 const double mouse_y, const float rad)
3166{
3167 for(int k = 0; k < DT_IOP_DENOISE_PROFILE_BANDS; k++)
3168 {
3169 const float f = expf(-(mouse_x - p->x[ch][k]) * (mouse_x - p->x[ch][k]) / (rad * rad));
3170 p->y[ch][k] = (1 - f) * p->y[ch][k] + f * mouse_y;
3171 }
3172}
3173
3174static gboolean denoiseprofile_draw_variance(GtkWidget *widget, cairo_t *crf, gpointer user_data)
3175{
3176 if(darktable.gui->reset) return FALSE;
3177
3178 dt_iop_module_t *self = (dt_iop_module_t *)user_data;
3180
3181 if(!isnan(c->variance_R))
3182 {
3183 gchar *str = g_strdup_printf("%.2f", c->variance_R);
3184 ++darktable.gui->reset;
3185 gtk_label_set_text(c->label_var_R, str);
3186 --darktable.gui->reset;
3187 dt_free(str);
3188 }
3189 if(!isnan(c->variance_G))
3190 {
3191 gchar *str = g_strdup_printf("%.2f", c->variance_G);
3192 ++darktable.gui->reset;
3193 gtk_label_set_text(c->label_var_G, str);
3194 --darktable.gui->reset;
3195 dt_free(str);
3196 }
3197 if(!isnan(c->variance_B))
3198 {
3199 gchar *str = g_strdup_printf("%.2f", c->variance_B);
3200 ++darktable.gui->reset;
3201 gtk_label_set_text(c->label_var_B, str);
3202 --darktable.gui->reset;
3203 dt_free(str);
3204 }
3205 return FALSE;
3206}
3207
3208static gboolean denoiseprofile_draw(GtkWidget *widget, cairo_t *crf, gpointer user_data)
3209{
3210 dt_iop_module_t *self = (dt_iop_module_t *)user_data;
3213
3214 int ch = (int)c->channel;
3215 dt_draw_curve_set_point(c->transition_curve, 0, p.x[ch][DT_IOP_DENOISE_PROFILE_BANDS - 2] - 1.f, p.y[ch][0]);
3216 for(int k = 0; k < DT_IOP_DENOISE_PROFILE_BANDS; k++)
3217 dt_draw_curve_set_point(c->transition_curve, k + 1, p.x[ch][k], p.y[ch][k]);
3218 dt_draw_curve_set_point(c->transition_curve, DT_IOP_DENOISE_PROFILE_BANDS + 1, p.x[ch][1] + 1.f,
3220
3221 const int inset = DT_IOP_DENOISE_PROFILE_INSET;
3222 GtkAllocation allocation;
3223 gtk_widget_get_allocation(widget, &allocation);
3224 int width = allocation.width, height = allocation.height;
3225 cairo_surface_t *cst = dt_cairo_image_surface_create(CAIRO_FORMAT_ARGB32, width, height);
3226 cairo_t *cr = cairo_create(cst);
3227 cairo_set_source_rgb(cr, .2, .2, .2);
3228
3229 cairo_paint(cr);
3230
3231 cairo_translate(cr, inset, inset);
3232 width -= 2 * inset;
3233 height -= 2 * inset;
3234
3235 cairo_set_line_width(cr, DT_PIXEL_APPLY_DPI(1.0));
3236 cairo_set_source_rgb(cr, .1, .1, .1);
3237 cairo_rectangle(cr, 0, 0, width, height);
3238 cairo_stroke(cr);
3239
3240 cairo_set_source_rgb(cr, .3, .3, .3);
3241 cairo_rectangle(cr, 0, 0, width, height);
3242 cairo_fill(cr);
3243
3244 // draw grid
3245 cairo_set_line_width(cr, DT_PIXEL_APPLY_DPI(.4));
3246 cairo_set_source_rgb(cr, .1, .1, .1);
3247 dt_draw_grid(cr, 8, 0, 0, width, height);
3248
3249 if(c->mouse_y > 0 || c->dragging)
3250 {
3251 // draw min/max curves:
3252 dt_iop_denoiseprofile_get_params(&p, c->channel, c->mouse_x, 1., c->mouse_radius);
3253 dt_draw_curve_set_point(c->transition_curve, 0, p.x[ch][DT_IOP_DENOISE_PROFILE_BANDS - 2] - 1.f, p.y[ch][0]);
3254 for(int k = 0; k < DT_IOP_DENOISE_PROFILE_BANDS; k++)
3255 dt_draw_curve_set_point(c->transition_curve, k + 1, p.x[ch][k], p.y[ch][k]);
3256 dt_draw_curve_set_point(c->transition_curve, DT_IOP_DENOISE_PROFILE_BANDS + 1, p.x[ch][1] + 1.f,
3258 dt_draw_curve_calc_values(c->transition_curve, 0.0, 1.0, DT_IOP_DENOISE_PROFILE_RES, c->draw_min_xs,
3259 c->draw_min_ys);
3260
3262 dt_iop_denoiseprofile_get_params(&p, c->channel, c->mouse_x, .0, c->mouse_radius);
3263 dt_draw_curve_set_point(c->transition_curve, 0, p.x[ch][DT_IOP_DENOISE_PROFILE_BANDS - 2] - 1.f, p.y[ch][0]);
3264 for(int k = 0; k < DT_IOP_DENOISE_PROFILE_BANDS; k++)
3265 dt_draw_curve_set_point(c->transition_curve, k + 1, p.x[ch][k], p.y[ch][k]);
3266 dt_draw_curve_set_point(c->transition_curve, DT_IOP_DENOISE_PROFILE_BANDS + 1, p.x[ch][1] + 1.f,
3268 dt_draw_curve_calc_values(c->transition_curve, 0.0, 1.0, DT_IOP_DENOISE_PROFILE_RES, c->draw_max_xs,
3269 c->draw_max_ys);
3270 }
3271
3272 cairo_save(cr);
3273
3274 // draw selected cursor
3275 cairo_translate(cr, 0, height);
3276
3277 cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
3278 cairo_set_line_width(cr, DT_PIXEL_APPLY_DPI(2.));
3279
3280 for(int i = 0; i < DT_DENOISE_PROFILE_NONE; i++)
3281 {
3282 // draw curves, selected last
3283 ch = ((int)c->channel + i + 1) % DT_DENOISE_PROFILE_NONE;
3284 float alpha = 0.3;
3285 if(i == DT_DENOISE_PROFILE_NONE - 1) alpha = 1.0;
3286 if(p.wavelet_color_mode == MODE_RGB)
3287 {
3288 switch(ch)
3289 {
3291 cairo_set_source_rgba(cr, .7, .7, .7, alpha);
3292 break;
3294 cairo_set_source_rgba(cr, .7, .1, .1, alpha);
3295 break;
3297 cairo_set_source_rgba(cr, .1, .7, .1, alpha);
3298 break;
3300 cairo_set_source_rgba(cr, .1, .1, .7, alpha);
3301 break;
3302 default:
3303 cairo_set_source_rgba(cr, 7, .7, .7, 0.0f);
3304 break;
3305 }
3306 }
3307 else
3308 {
3309 switch(ch)
3310 {
3312 cairo_set_source_rgba(cr, .7, .7, .7, alpha);
3313 break;
3315 cairo_set_source_rgba(cr, .8, .4, .0, alpha);
3316 break;
3317 default:
3318 cairo_set_source_rgba(cr, .7, .7, .7, 0.0f);
3319 break;
3320 }
3321 }
3322
3324 dt_draw_curve_set_point(c->transition_curve, 0, p.x[ch][DT_IOP_DENOISE_PROFILE_BANDS - 2] - 1.0f, p.y[ch][0]);
3325 for(int k = 0; k < DT_IOP_DENOISE_PROFILE_BANDS; k++)
3326 dt_draw_curve_set_point(c->transition_curve, k + 1, p.x[ch][k], p.y[ch][k]);
3327 dt_draw_curve_set_point(c->transition_curve, DT_IOP_DENOISE_PROFILE_BANDS + 1, p.x[ch][1] + 1.0f,
3329 dt_draw_curve_calc_values(c->transition_curve, 0.0, 1.0, DT_IOP_DENOISE_PROFILE_RES, c->draw_xs, c->draw_ys);
3330 cairo_move_to(cr, 0 * width / (float)(DT_IOP_DENOISE_PROFILE_RES - 1), -height * c->draw_ys[0]);
3331 for(int k = 1; k < DT_IOP_DENOISE_PROFILE_RES; k++)
3332 cairo_line_to(cr, k * width / (float)(DT_IOP_DENOISE_PROFILE_RES - 1), -height * c->draw_ys[k]);
3333 cairo_stroke(cr);
3334 }
3335
3336 ch = c->channel;
3337 // draw dots on knots
3338 cairo_set_source_rgb(cr, 0.7, 0.7, 0.7);
3339 cairo_set_line_width(cr, DT_PIXEL_APPLY_DPI(1.));
3340 for(int k = 0; k < DT_IOP_DENOISE_PROFILE_BANDS; k++)
3341 {
3342 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);
3343 if(c->x_move == k)
3344 cairo_fill(cr);
3345 else
3346 cairo_stroke(cr);
3347 }
3348
3349 if(c->mouse_y > 0 || c->dragging)
3350 {
3351 // draw min/max, if selected
3352 cairo_set_source_rgba(cr, .7, .7, .7, .6);
3353 cairo_move_to(cr, 0, -height * c->draw_min_ys[0]);
3354 for(int k = 1; k < DT_IOP_DENOISE_PROFILE_RES; k++)
3355 cairo_line_to(cr, k * width / (float)(DT_IOP_DENOISE_PROFILE_RES - 1), -height * c->draw_min_ys[k]);
3356 for(int k = DT_IOP_DENOISE_PROFILE_RES - 1; k >= 0; k--)
3357 cairo_line_to(cr, k * width / (float)(DT_IOP_DENOISE_PROFILE_RES - 1), -height * c->draw_max_ys[k]);
3358 cairo_close_path(cr);
3359 cairo_fill(cr);
3360 // draw mouse focus circle
3361 cairo_set_source_rgba(cr, .9, .9, .9, .5);
3362 const float pos = DT_IOP_DENOISE_PROFILE_RES * c->mouse_x;
3363 int k = (int)pos;
3364 const float f = k - pos;
3366 float ht = -height * (f * c->draw_ys[k] + (1 - f) * c->draw_ys[k + 1]);
3367 cairo_arc(cr, c->mouse_x * width, ht, c->mouse_radius * width, 0, 2. * M_PI);
3368 cairo_stroke(cr);
3369 }
3370
3371 cairo_restore(cr);
3372
3373 cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
3374
3375 // draw labels:
3376 PangoLayout *layout;
3377 PangoRectangle ink;
3378 PangoFontDescription *desc = pango_font_description_copy_static(darktable.bauhaus->pango_font_desc);
3379 pango_font_description_set_weight(desc, PANGO_WEIGHT_BOLD);
3380 pango_font_description_set_absolute_size(desc, (.08 * height) * PANGO_SCALE);
3381 layout = pango_cairo_create_layout(cr);
3382 pango_layout_set_font_description(layout, desc);
3383 cairo_set_source_rgb(cr, .1, .1, .1);
3384
3385 pango_layout_set_text(layout, _("coarse"), -1);
3386 pango_layout_get_pixel_extents(layout, &ink, NULL);
3387 cairo_move_to(cr, .02 * width - ink.y, .5 * (height + ink.width));
3388 cairo_save(cr);
3389 cairo_rotate(cr, -M_PI * .5f);
3390 pango_cairo_show_layout(cr, layout);
3391 cairo_restore(cr);
3392
3393 pango_layout_set_text(layout, _("fine"), -1);
3394 pango_layout_get_pixel_extents(layout, &ink, NULL);
3395 cairo_move_to(cr, .98 * width - ink.height, .5 * (height + ink.width));
3396 cairo_save(cr);
3397 cairo_rotate(cr, -M_PI * .5f);
3398 pango_cairo_show_layout(cr, layout);
3399 cairo_restore(cr);
3400
3401
3402 pango_layout_set_text(layout, _("smooth"), -1);
3403 pango_layout_get_pixel_extents(layout, &ink, NULL);
3404 cairo_move_to(cr, .5 * (width - ink.width), .08 * height - ink.height);
3405 pango_cairo_show_layout(cr, layout);
3406
3407 pango_layout_set_text(layout, _("noisy"), -1);
3408 pango_layout_get_pixel_extents(layout, &ink, NULL);
3409 cairo_move_to(cr, .5 * (width - ink.width), .97 * height - ink.height);
3410 pango_cairo_show_layout(cr, layout);
3411
3412 pango_font_description_free(desc);
3413 g_object_unref(layout);
3414 cairo_destroy(cr);
3415 cairo_set_source_surface(crf, cst, 0, 0);
3416 cairo_paint(crf);
3417 cairo_surface_destroy(cst);
3418 return TRUE;
3419}
3420
3421static gboolean denoiseprofile_motion_notify(GtkWidget *widget, GdkEventMotion *event, gpointer user_data)
3422{
3423 dt_iop_module_t *self = (dt_iop_module_t *)user_data;
3426 const int inset = DT_IOP_DENOISE_PROFILE_INSET;
3427 GtkAllocation allocation;
3428 gtk_widget_get_allocation(widget, &allocation);
3429 int height = allocation.height - 2 * inset, width = allocation.width - 2 * inset;
3430 if(!c->dragging) c->mouse_x = CLAMP(event->x - inset, 0, width) / (float)width;
3431 c->mouse_y = 1.0 - CLAMP(event->y - inset, 0, height) / (float)height;
3432 if(c->dragging)
3433 {
3434 *p = c->drag_params;
3435 if(c->x_move < 0)
3436 {
3437 dt_iop_denoiseprofile_get_params(p, c->channel, c->mouse_x, c->mouse_y + c->mouse_pick, c->mouse_radius);
3438 }
3440 }
3441 else
3442 {
3443 c->x_move = -1;
3444 }
3445 gtk_widget_queue_draw(widget);
3446 return TRUE;
3447}
3448
3449static gboolean denoiseprofile_button_press(GtkWidget *widget, GdkEventButton *event, gpointer user_data)
3450{
3451 dt_iop_module_t *self = (dt_iop_module_t *)user_data;
3453 const int ch = c->channel;
3454 if(event->button == 1 && event->type == GDK_2BUTTON_PRESS)
3455 {
3456 // reset current curve
3459 /* dt_iop_denoiseprofile_gui_data_t *c = (dt_iop_denoiseprofile_gui_data_t *)self->gui_data; */
3460 for(int k = 0; k < DT_IOP_DENOISE_PROFILE_BANDS; k++)
3461 {
3462 p->x[ch][k] = d->x[ch][k];
3463 p->y[ch][k] = d->y[ch][k];
3464 }
3466 gtk_widget_queue_draw(self->widget);
3467 }
3468 else if(event->button == 1)
3469 {
3470 c->drag_params = *(dt_iop_denoiseprofile_params_t *)self->params;
3471 const int inset = DT_IOP_DENOISE_PROFILE_INSET;
3472 GtkAllocation allocation;
3473 gtk_widget_get_allocation(widget, &allocation);
3474 int height = allocation.height - 2 * inset, width = allocation.width - 2 * inset;
3475 c->mouse_pick
3476 = dt_draw_curve_calc_value(c->transition_curve, CLAMP(event->x - inset, 0, width) / (float)width);
3477 c->mouse_pick -= 1.0 - CLAMP(event->y - inset, 0, height) / (float)height;
3478 c->dragging = 1;
3479 return TRUE;
3480 }
3481 return FALSE;
3482}
3483
3484static gboolean denoiseprofile_button_release(GtkWidget *widget, GdkEventButton *event, gpointer user_data)
3485{
3486 if(event->button == 1)
3487 {
3488 dt_iop_module_t *self = (dt_iop_module_t *)user_data;
3490 c->dragging = 0;
3491 return TRUE;
3492 }
3493 return FALSE;
3494}
3495
3496static gboolean denoiseprofile_leave_notify(GtkWidget *widget, GdkEventCrossing *event, gpointer user_data)
3497{
3498 dt_iop_module_t *self = (dt_iop_module_t *)user_data;
3500 if(!c->dragging) c->mouse_y = -1.0;
3501 gtk_widget_queue_draw(widget);
3502 return TRUE;
3503}
3504
3505static gboolean denoiseprofile_scrolled(GtkWidget *widget, GdkEventScroll *event, gpointer user_data)
3506{
3507 dt_iop_module_t *self = (dt_iop_module_t *)user_data;
3509
3510 int delta_y;
3511 if(dt_gui_get_scroll_unit_deltas(event, NULL, &delta_y))
3512 {
3513 c->mouse_radius = CLAMP(c->mouse_radius * (1.f + 0.1f * delta_y), 0.2f / DT_IOP_DENOISE_PROFILE_BANDS, 1.f);
3514 gtk_widget_queue_draw(widget);
3515 }
3516
3517 return TRUE;
3518}
3519
3520static void denoiseprofile_tab_switch(GtkNotebook *notebook, GtkWidget *page, guint page_num, gpointer user_data)
3521{
3522 dt_iop_module_t *self = (dt_iop_module_t *)user_data;
3524 if(darktable.gui->reset) return;
3526 if(p->wavelet_color_mode == MODE_Y0U0V0)
3528 else
3529 c->channel = (dt_iop_denoiseprofile_channel_t)page_num;
3530 gtk_widget_queue_draw(self->widget);
3531}
3532
3534{
3537
3538 g->profiles = NULL;
3539
3540 g->channel = 0;
3541
3542 // First build sub-level boxes
3543 g->box_nlm = self->widget = gtk_box_new(GTK_ORIENTATION_VERTICAL, DT_GUI_BOX_SPACING);
3544
3545 g->radius = dt_bauhaus_slider_from_params(self, "radius");
3546 dt_bauhaus_slider_set_soft_range(g->radius, 0.0, 8.0);
3547 dt_bauhaus_slider_set_digits(g->radius, 0);
3548 g->nbhood = dt_bauhaus_slider_from_params(self, "nbhood");
3549 dt_bauhaus_slider_set_digits(g->nbhood, 0);
3550 g->scattering = dt_bauhaus_slider_from_params(self, "scattering");
3551 dt_bauhaus_slider_set_soft_max(g->scattering, 1.0f);
3552 g->central_pixel_weight = dt_bauhaus_slider_from_params(self, "central_pixel_weight");
3553 dt_bauhaus_slider_set_soft_max(g->central_pixel_weight, 1.0f);
3554
3555 g->box_wavelets = self->widget = gtk_box_new(GTK_ORIENTATION_VERTICAL, DT_GUI_BOX_SPACING);
3556
3557 g->wavelet_color_mode = dt_bauhaus_combobox_from_params(self, "wavelet_color_mode");
3558
3559 g->channel_tabs = GTK_NOTEBOOK(gtk_notebook_new());
3560 dt_ui_notebook_page(g->channel_tabs, N_("all"), NULL);
3561 dt_ui_notebook_page(g->channel_tabs, N_("R"), NULL);
3562 dt_ui_notebook_page(g->channel_tabs, N_("G"), NULL);
3563 dt_ui_notebook_page(g->channel_tabs, N_("B"), NULL);
3564 g_signal_connect(G_OBJECT(g->channel_tabs), "switch_page", G_CALLBACK(denoiseprofile_tab_switch), self);
3565 gtk_box_pack_start(GTK_BOX(g->box_wavelets), GTK_WIDGET(g->channel_tabs), FALSE, FALSE, 0);
3566
3567 g->channel_tabs_Y0U0V0 = GTK_NOTEBOOK(gtk_notebook_new());
3568 dt_ui_notebook_page(g->channel_tabs_Y0U0V0, N_("Y0"), NULL);
3569 dt_ui_notebook_page(g->channel_tabs_Y0U0V0, N_("U0V0"), NULL);
3570 g_signal_connect(G_OBJECT(g->channel_tabs_Y0U0V0), "switch_page", G_CALLBACK(denoiseprofile_tab_switch), self);
3571 gtk_box_pack_start(GTK_BOX(g->box_wavelets), GTK_WIDGET(g->channel_tabs_Y0U0V0), FALSE, FALSE, 0);
3572
3573 const int ch = (int)g->channel;
3574 g->transition_curve = dt_draw_curve_new(0.0, 1.0, CATMULL_ROM);
3575 (void)dt_draw_curve_add_point(g->transition_curve, p->x[ch][DT_IOP_DENOISE_PROFILE_BANDS - 2] - 1.0f,
3577 for(int k = 0; k < DT_IOP_DENOISE_PROFILE_BANDS; k++)
3578 (void)dt_draw_curve_add_point(g->transition_curve, p->x[ch][k], p->y[ch][k]);
3579 (void)dt_draw_curve_add_point(g->transition_curve, p->x[ch][1] + 1.0f, p->y[ch][1]);
3580
3581 g->mouse_x = g->mouse_y = g->mouse_pick = -1.0;
3582 g->dragging = 0;
3583 g->x_move = -1;
3584 g->mouse_radius = 1.0f / (DT_IOP_DENOISE_PROFILE_BANDS * 2);
3585
3586 g->area = GTK_DRAWING_AREA(gtk_drawing_area_new());
3587 gtk_widget_set_hexpand(GTK_WIDGET(g->area), TRUE);
3588
3589 gtk_widget_add_events(GTK_WIDGET(g->area), GDK_POINTER_MOTION_MASK
3590 | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK
3591 | GDK_LEAVE_NOTIFY_MASK | darktable.gui->scroll_mask);
3592 g_signal_connect(G_OBJECT(g->area), "draw", G_CALLBACK(denoiseprofile_draw), self);
3593 g_signal_connect(G_OBJECT(g->area), "button-press-event", G_CALLBACK(denoiseprofile_button_press), self);
3594 g_signal_connect(G_OBJECT(g->area), "button-release-event", G_CALLBACK(denoiseprofile_button_release), self);
3595 g_signal_connect(G_OBJECT(g->area), "motion-notify-event", G_CALLBACK(denoiseprofile_motion_notify), self);
3596 g_signal_connect(G_OBJECT(g->area), "leave-notify-event", G_CALLBACK(denoiseprofile_leave_notify), self);
3597 g_signal_connect(G_OBJECT(g->area), "scroll-event", G_CALLBACK(denoiseprofile_scrolled), self);
3598 gtk_box_pack_start(GTK_BOX(g->box_wavelets),
3599 dt_ui_resizable_drawing_area(GTK_WIDGET(g->area),
3600 "plugins/darkroom/denoiseprofile/graphheight", 280, 100),
3601 FALSE, FALSE, 0);
3602
3603 g->box_variance = gtk_box_new(GTK_ORIENTATION_VERTICAL, DT_GUI_BOX_SPACING);
3604
3605 g->label_var = GTK_LABEL(dt_ui_label_new(_("use only with a perfectly\n"
3606 "uniform image if you want to\n"
3607 "estimate the noise variance.")));
3608 gtk_box_pack_start(GTK_BOX(g->box_variance), GTK_WIDGET(g->label_var), TRUE, TRUE, 0);
3609
3610 GtkBox *hboxR = GTK_BOX(gtk_box_new(GTK_ORIENTATION_HORIZONTAL, DT_GUI_BOX_SPACING));
3611 GtkLabel *labelR = GTK_LABEL(dt_ui_label_new(_("variance red: ")));
3612 gtk_box_pack_start(GTK_BOX(hboxR), GTK_WIDGET(labelR), FALSE, FALSE, 0);
3613 g->label_var_R = GTK_LABEL(dt_ui_label_new("")); // This gets filled in by process
3614 gtk_widget_set_tooltip_text(GTK_WIDGET(g->label_var_R), _("variance computed on the red channel"));
3615 gtk_box_pack_start(GTK_BOX(hboxR), GTK_WIDGET(g->label_var_R), FALSE, FALSE, 0);
3616 gtk_box_pack_start(GTK_BOX(g->box_variance), GTK_WIDGET(hboxR), TRUE, TRUE, 0);
3617
3618 GtkBox *hboxG = GTK_BOX(gtk_box_new(GTK_ORIENTATION_HORIZONTAL, DT_GUI_BOX_SPACING));
3619 GtkLabel *labelG = GTK_LABEL(dt_ui_label_new(_("variance green: ")));
3620 gtk_box_pack_start(GTK_BOX(hboxG), GTK_WIDGET(labelG), FALSE, FALSE, 0);
3621 g->label_var_G = GTK_LABEL(dt_ui_label_new("")); // This gets filled in by process
3622 gtk_widget_set_tooltip_text(GTK_WIDGET(g->label_var_G), _("variance computed on the green channel"));
3623 gtk_box_pack_start(GTK_BOX(hboxG), GTK_WIDGET(g->label_var_G), FALSE, FALSE, 0);
3624 gtk_box_pack_start(GTK_BOX(g->box_variance), GTK_WIDGET(hboxG), TRUE, TRUE, 0);
3625
3626 GtkBox *hboxB = GTK_BOX(gtk_box_new(GTK_ORIENTATION_HORIZONTAL, DT_GUI_BOX_SPACING));
3627 GtkLabel *labelB = GTK_LABEL(dt_ui_label_new(_("variance blue: ")));
3628 gtk_box_pack_start(GTK_BOX(hboxB), GTK_WIDGET(labelB), FALSE, FALSE, 0);
3629 g->label_var_B = GTK_LABEL(dt_ui_label_new("")); // This gets filled in by process
3630 gtk_widget_set_tooltip_text(GTK_WIDGET(g->label_var_B), _("variance computed on the blue channel"));
3631 gtk_box_pack_start(GTK_BOX(hboxB), GTK_WIDGET(g->label_var_B), FALSE, FALSE, 0);
3632 gtk_box_pack_start(GTK_BOX(g->box_variance), GTK_WIDGET(hboxB), TRUE, TRUE, 0);
3633
3634 g_signal_connect(G_OBJECT(g->box_variance), "draw", G_CALLBACK(denoiseprofile_draw_variance), self);
3635
3636 // start building top level widget
3637 self->widget = gtk_box_new(GTK_ORIENTATION_VERTICAL, DT_GUI_BOX_SPACING);
3638
3640 dt_bauhaus_widget_set_label(g->profile, N_("profile"));
3641 g_signal_connect(G_OBJECT(g->profile), "value-changed", G_CALLBACK(profile_callback), self);
3642 gtk_box_pack_start(GTK_BOX(self->widget), g->profile, TRUE, TRUE, 0);
3643
3644 g->wb_adaptive_anscombe = dt_bauhaus_toggle_from_params(self, "wb_adaptive_anscombe");
3645
3647 dt_bauhaus_widget_set_label(g->mode, N_("mode"));
3648 dt_bauhaus_combobox_add(g->mode, _("non-local means"));
3649 dt_bauhaus_combobox_add(g->mode, _("non-local means auto"));
3650 dt_bauhaus_combobox_add(g->mode, _("wavelets"));
3651 dt_bauhaus_combobox_add(g->mode, _("wavelets auto"));
3652 const gboolean compute_variance = dt_conf_get_bool("plugins/darkroom/denoiseprofile/show_compute_variance_mode");
3653 if(compute_variance) dt_bauhaus_combobox_add(g->mode, _("compute variance"));
3654 g_signal_connect(G_OBJECT(g->mode), "value-changed", G_CALLBACK(mode_callback), self);
3655 gtk_box_pack_start(GTK_BOX(self->widget), g->mode, TRUE, TRUE, 0);
3656
3657 gtk_box_pack_start(GTK_BOX(self->widget), g->box_nlm, TRUE, TRUE, 0);
3658 gtk_box_pack_start(GTK_BOX(self->widget), g->box_wavelets, TRUE, TRUE, 0);
3659
3660 g->overshooting = dt_bauhaus_slider_from_params(self, "overshooting");
3661 dt_bauhaus_slider_set_soft_max(g->overshooting, 4.0f);
3662 g->strength = dt_bauhaus_slider_from_params(self, N_("strength"));
3663 dt_bauhaus_slider_set_soft_max(g->strength, 4.0f);
3664 dt_bauhaus_slider_set_digits(g->strength, 3);
3665 g->shadows = dt_bauhaus_slider_from_params(self, "shadows");
3666 g->bias = dt_bauhaus_slider_from_params(self, "bias");
3667 dt_bauhaus_slider_set_soft_range(g->bias, -10.0f, 10.0f);
3668
3669 gtk_box_pack_start(GTK_BOX(self->widget), g->box_variance, TRUE, TRUE, 0);
3670
3671 g->fix_anscombe_and_nlmeans_norm = dt_bauhaus_toggle_from_params(self, "fix_anscombe_and_nlmeans_norm");
3672
3673 g->use_new_vst = dt_bauhaus_toggle_from_params(self, "use_new_vst");
3674
3675 gtk_widget_set_tooltip_text(g->wb_adaptive_anscombe, _("adapt denoising according to the\n"
3676 "white balance coefficients.\n"
3677 "should be enabled on a first instance\n"
3678 "for better denoising.\n"
3679 "should be disabled if an earlier instance\n"
3680 "has been used with a color blending mode."));
3681 gtk_widget_set_tooltip_text(g->fix_anscombe_and_nlmeans_norm, _("fix bugs in anscombe transform resulting\n"
3682 "in undersmoothing of the green channel in\n"
3683 "wavelets mode, combined with a bad handling\n"
3684 "of white balance coefficients, and a bug in\n"
3685 "non local means normalization resulting in\n"
3686 "undersmoothing when patch size was increased.\n"
3687 "enabling this option will change the denoising\n"
3688 "you get. once enabled, you won't be able to\n"
3689 "return back to old algorithm."));
3690 gtk_widget_set_tooltip_text(g->profile, _("profile used for variance stabilization"));
3691 gtk_widget_set_tooltip_text(g->mode, _("method used in the denoising core.\n"
3692 "non-local means works best for `lightness' blending,\n"
3693 "wavelets work best for `color' blending"));
3694 gtk_widget_set_tooltip_text(g->wavelet_color_mode, _("color representation used within the algorithm.\n"
3695 "RGB keeps the RGB channels separated,\n"
3696 "while Y0U0V0 combine the channels to\n"
3697 "denoise chroma and luma separately."));
3698 gtk_widget_set_tooltip_text(g->radius, _("radius of the patches to match.\n"
3699 "increase for more sharpness on strong edges, and better denoising of smooth areas.\n"
3700 "if details are oversmoothed, reduce this value or increase the central pixel weight slider."));
3701 gtk_widget_set_tooltip_text(g->nbhood, _("emergency use only: radius of the neighbourhood to search patches in. "
3702 "increase for better denoising performance, but watch the long runtimes! "
3703 "large radii can be very slow. you have been warned"));
3704 gtk_widget_set_tooltip_text(g->scattering, _("scattering of the neighbourhood to search patches in.\n"
3705 "increase for better coarse-grain noise reduction.\n"
3706 "does not affect execution time."));
3707 gtk_widget_set_tooltip_text(g->central_pixel_weight, _("increase the weight of the central pixel\n"
3708 "of the patch in the patch comparison.\n"
3709 "useful to recover details when patch size\n"
3710 "is quite big."));
3711 gtk_widget_set_tooltip_text(g->strength, _("finetune denoising strength"));
3712 gtk_widget_set_tooltip_text(g->overshooting, _("controls the way parameters are autoset\n"
3713 "increase if shadows are not denoised enough\n"
3714 "or if chroma noise remains.\n"
3715 "this can happen if your picture is underexposed."));
3716 gtk_widget_set_tooltip_text(g->shadows, _("finetune shadows denoising.\n"
3717 "decrease to denoise more aggressively\n"
3718 "dark areas of the image."));
3719 gtk_widget_set_tooltip_text(g->bias, _("correct color cast in shadows.\n"
3720 "decrease if shadows are too purple.\n"
3721 "increase if shadows are too green."));
3722 gtk_widget_set_tooltip_text(g->use_new_vst, _("upgrade the variance stabilizing algorithm.\n"
3723 "new algorithm extends the current one.\n"
3724 "it is more flexible but could give small\n"
3725 "differences in the images already processed."));
3726
3727}
3728
3730{
3732 g_list_free_full(g->profiles, dt_noiseprofile_free);
3733 g->profiles = NULL;
3734 dt_draw_curve_destroy(g->transition_curve);
3735 // nothing else necessary, gtk will clean up the slider.
3736
3738}
3739
3740// clang-format off
3741// modelines: These editor modelines have been set for all relevant files by tools/update_modelines.py
3742// vim: shiftwidth=2 expandtab tabstop=2 cindent
3743// kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-spaces modified;
3744// 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:1647
void dt_bauhaus_slider_set_digits(GtkWidget *widget, int val)
Definition bauhaus.c:3534
void dt_bauhaus_combobox_clear(GtkWidget *widget)
Definition bauhaus.c:2189
void dt_bauhaus_slider_set_default(GtkWidget *widget, float def)
Definition bauhaus.c:1640
int dt_bauhaus_combobox_get(GtkWidget *widget)
Definition bauhaus.c:2347
void dt_bauhaus_slider_set_soft_max(GtkWidget *widget, float val)
Definition bauhaus.c:1624
int dt_bauhaus_combobox_length(GtkWidget *widget)
Definition bauhaus.c:2155
void dt_bauhaus_slider_set(GtkWidget *widget, float pos)
Definition bauhaus.c:3506
void dt_bauhaus_combobox_set(GtkWidget *widget, const int pos)
Definition bauhaus.c:2301
void dt_bauhaus_widget_set_label(GtkWidget *widget, const char *label)
Definition bauhaus.c:1653
GtkWidget * dt_bauhaus_combobox_new(dt_bauhaus_t *bh, dt_gui_module_t *self)
Definition bauhaus.c:1842
void dt_bauhaus_combobox_add(GtkWidget *widget, const char *text)
Definition bauhaus.c:2016
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:181
void dt_print(dt_debug_thread_t thread, const char *msg,...)
Definition darktable.c:1542
#define dt_free_align(ptr)
Definition darktable.h:481
static void * dt_calloc_align(size_t size)
Definition darktable.h:488
#define __OMP_SIMD__(...)
Definition darktable.h:262
@ DT_DEBUG_OPENCL
Definition darktable.h:722
#define for_each_channel(_var,...)
Definition darktable.h:662
float dt_aligned_pixel_simd_t __attribute__((vector_size(16), aligned(16)))
Enable aggressive floating-point arithmetic optimizations, in denormals handling. Set through user pr...
Definition darktable.h:524
#define dt_pixelpipe_cache_alloc_align(size, pipe)
Definition darktable.h:437
static const dt_aligned_pixel_simd_t sign
Definition darktable.h:551
#define dt_free(ptr)
Definition darktable.h:456
#define DT_MODULE_INTROSPECTION(MODVER, PARAMSTYPE)
Definition darktable.h:151
#define dt_pixelpipe_cache_free_align(mem)
Definition darktable.h:453
#define __DT_CLONE_TARGETS__
Definition darktable.h:367
#define __OMP_PARALLEL_FOR__(...)
Definition darktable.h:258
#define IS_NULL_PTR(p)
C is way too permissive with !=, == and if(var) checks, which can mean too many things depending on w...
Definition darktable.h:281
#define DT_CACHELINE_BYTES
Definition darktable.h:380
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 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:41
gboolean dt_dev_pixelpipe_has_preview_output(const dt_develop_t *dev, const dt_dev_pixelpipe_t *pipe, const dt_iop_roi_t *roi)
Definition develop.c:367
@ 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:219
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:2836
GtkWidget * dt_ui_notebook_page(GtkNotebook *notebook, const char *text, const char *tooltip)
Definition gtk.c:2259
static cairo_surface_t * dt_cairo_image_surface_create(cairo_format_t format, int width, int height)
Definition gtk.h:316
#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:461
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:316
const char ** dt_iop_set_description(dt_iop_module_t *module, const char *main_text, const char *purpose, const char *input, const char *process, const char *output)
Definition imageop.c:3141
#define IOP_GUI_FREE
Definition imageop.h:602
@ IOP_FLAGS_SUPPORTS_BLENDING
Definition imageop.h:167
@ IOP_FLAGS_ALLOW_TILING
Definition imageop.h:169
@ IOP_GROUP_REPAIR
Definition imageop.h:140
#define IOP_GUI_ALLOC(module)
Definition imageop.h:599
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:3156
int dt_opencl_enqueue_kernel_2d(const int dev, const int kernel, const size_t *sizes)
Definition opencl.c:2136
void * dt_opencl_alloc_device_buffer(const int devid, const size_t size)
Definition opencl.c:2544
void * dt_opencl_alloc_device(const int devid, const int width, const int height, const int bpp)
Definition opencl.c:2471
int dt_opencl_create_kernel(const int prog, const char *name)
Definition opencl.c:2030
void * dt_opencl_copy_host_to_device_constant(const int devid, const size_t size, void *host)
Definition opencl.c:2332
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:2261
int dt_opencl_read_buffer_from_device(const int devid, void *host, void *device, const size_t offset, const size_t size, const int blocking)
Definition opencl.c:2309
void dt_opencl_free_kernel(const int kernel)
Definition opencl.c:2073
int dt_opencl_set_kernel_arg(const int dev, const int kernel, const int num, const size_t size, const void *arg)
Definition opencl.c:2127
int dt_opencl_enqueue_kernel_2d_with_local(const int dev, const int kernel, const size_t *sizes, const size_t *local)
Definition opencl.c:2142
void dt_opencl_release_mem_object(cl_mem mem)
Definition opencl.c:2383
#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:775
struct dt_bauhaus_t * bauhaus
Definition darktable.h:778
struct dt_develop_t * develop
Definition darktable.h:770
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:224
int32_t reset
Definition gtk.h:172
float exif_iso
Definition image.h:288
dt_aligned_pixel_t coeffs
Definition format.h:81
struct dt_iop_buffer_dsc_t::@30 temperature
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:230
dt_iop_global_data_t * data
Definition imageop.h:233
dt_iop_params_t * default_params
Definition imageop.h:307
GtkWidget * widget
Definition imageop.h:337
struct dt_develop_t * dev
Definition imageop.h:296
dt_iop_gui_data_t * gui_data
Definition imageop.h:311
dt_iop_global_data_t * global_data
Definition imageop.h:314
dt_iop_params_t * params
Definition imageop.h:307
Region of interest passed through the pixelpipe.
Definition imageop.h:72
double scale
Definition imageop.h:74
dt_aligned_pixel_t a
dt_aligned_pixel_t b
#define MIN(a, b)
Definition thinplate.c:32
#define MAX(a, b)
Definition thinplate.c:29