Ansel 0.0
A darktable fork - bloat + design vision
Loading...
Searching...
No Matches
rawdenoise.c
Go to the documentation of this file.
1/*
2 This file is part of darktable,
3 Copyright (C) 2011 Bruce Guenter.
4 Copyright (C) 2011-2012 Henrik Andersson.
5 Copyright (C) 2011-2013, 2016 johannes hanika.
6 Copyright (C) 2011 Jérémy Rosen.
7 Copyright (C) 2011 Robert Bieber.
8 Copyright (C) 2011 Rostyslav Pidgornyi.
9 Copyright (C) 2011-2014, 2016, 2019 Tobias Ellinghaus.
10 Copyright (C) 2012 parafin.
11 Copyright (C) 2012 Pascal de Bruijn.
12 Copyright (C) 2012 Richard Wonka.
13 Copyright (C) 2014, 2021 Dan Torop.
14 Copyright (C) 2014-2016 Roman Lebedev.
15 Copyright (C) 2014, 2019 Ulrich Pegelow.
16 Copyright (C) 2015-2016 Pedro Côrte-Real.
17 Copyright (C) 2017 Heiko Bauke.
18 Copyright (C) 2018, 2020, 2022-2023, 2025-2026 Aurélien PIERRE.
19 Copyright (C) 2018 Edgardo Hoszowski.
20 Copyright (C) 2018 Maurizio Paglia.
21 Copyright (C) 2018, 2020-2022 Pascal Obry.
22 Copyright (C) 2018-2019 rawfiner.
23 Copyright (C) 2019 Andreas Schneider.
24 Copyright (C) 2019 emeikei.
25 Copyright (C) 2019 Hanno Schwalm.
26 Copyright (C) 2020 Aldric Renaudin.
27 Copyright (C) 2020-2022 Diederik Ter Rahe.
28 Copyright (C) 2020-2021 Hubert Kowalski.
29 Copyright (C) 2020 Ralf Brown.
30 Copyright (C) 2021 Chris Elston.
31 Copyright (C) 2022 Martin Bařinka.
32 Copyright (C) 2022 Philipp Lutz.
33
34 darktable is free software: you can redistribute it and/or modify
35 it under the terms of the GNU General Public License as published by
36 the Free Software Foundation, either version 3 of the License, or
37 (at your option) any later version.
38
39 darktable is distributed in the hope that it will be useful,
40 but WITHOUT ANY WARRANTY; without even the implied warranty of
41 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
42 GNU General Public License for more details.
43
44 You should have received a copy of the GNU General Public License
45 along with darktable. If not, see <http://www.gnu.org/licenses/>.
46*/
47#ifdef HAVE_CONFIG_H
48#include "config.h"
49#endif
50#include "bauhaus/bauhaus.h"
51#include "common/darktable.h"
52#include "common/imagebuf.h"
53#include "common/dwt.h"
54#include "control/control.h"
55#include "develop/imageop.h"
57#include "develop/imageop_gui.h"
59#include "dtgtk/drawingarea.h"
60
61#include "gui/draw.h"
62#include "gui/gtk.h"
63#include "iop/iop_api.h"
64
65#include <gtk/gtk.h>
66#include <stdlib.h>
67#include <strings.h>
68
70
71#define DT_IOP_RAWDENOISE_INSET DT_PIXEL_APPLY_DPI(5)
72#define DT_IOP_RAWDENOISE_RES 64
73#define DT_IOP_RAWDENOISE_BANDS 5
74
83
85{
86 float threshold; // $MIN: 0.0 $MAX: 1.0 $DEFAULT: 0.01 $DESCRIPTION: "noise threshold"
90
108
116
120
121int legacy_params(dt_iop_module_t *self, const void *const old_params, const int old_version, void *new_params,
122 const int new_version)
123{
124 if(old_version == 1 && new_version == 2)
125 {
126 // Since first version, the dt_iop_params_t struct have new members
127 // at the end of the struct.
128 // Yet, the beginning of the struct is exactly the same:
129 // threshold is still the first member of the struct.
130 // This allows to define the variable o with dt_iop_rawdenoise_params_t
131 // as long as we don't try to access new members on o.
132 // In other words, o can be seen as a dt_iop_rawdenoise_params_t
133 // with no allocated space for the new member.
136 n->threshold = o->threshold;
137 for(int k = 0; k < DT_IOP_RAWDENOISE_BANDS; k++)
138 {
139 for(int ch = 0; ch < DT_RAWDENOISE_NONE; ch++)
140 {
141 n->x[ch][k] = k / (DT_IOP_RAWDENOISE_BANDS - 1.0);
142 n->y[ch][k] = 0.5f;
143 }
144 }
145 return 0;
146 }
147 return 1;
148}
149
150
151const char *name()
152{
153 return _("raw denoise");
154}
155
156const char **description(struct dt_iop_module_t *self)
157{
158 return dt_iop_set_description(self, _("denoise the raw picture early in the pipeline"),
159 _("corrective"),
160 _("linear, raw, scene-referred"),
161 _("linear, raw"),
162 _("linear, raw, scene-referred"));
163}
164
169
171{
172 return IOP_GROUP_REPAIR;
173}
174
176{
177 return IOP_CS_RAW;
178}
179
182{
183 default_input_format(self, pipe, piece, dsc);
184 dsc->channels = 1;
186}
187
188#define BIT16 65536.0
189
191static void compute_channel_noise(float *const noise, int color, const dt_iop_rawdenoise_data_t *const data)
192{
193 // note that these constants are the same for X-Trans and Bayer, as they are proportional to image detail on
194 // each channel, not the sensor pattern
195 static const float noise_all[] = { 0.8002, 0.2735, 0.1202, 0.0585, 0.0291, 0.0152, 0.0080, 0.0044 };
196 for(int i = 0; i < DT_IOP_RAWDENOISE_BANDS; i++)
197 {
198 // scale the value from [0,1] to [0,16],
199 // and makes the "0.5" neutral value become 1
200 float chan_threshold_exp_4;
201 switch(color)
202 {
203 case 0:
204 chan_threshold_exp_4 = data->force[DT_RAWDENOISE_R][DT_IOP_RAWDENOISE_BANDS - i - 1];
205 break;
206 case 2:
207 chan_threshold_exp_4 = data->force[DT_RAWDENOISE_B][DT_IOP_RAWDENOISE_BANDS - i - 1];
208 break;
209 default:
210 chan_threshold_exp_4 = data->force[DT_RAWDENOISE_G][DT_IOP_RAWDENOISE_BANDS - i - 1];
211 break;
212 }
213 chan_threshold_exp_4 *= chan_threshold_exp_4;
214 chan_threshold_exp_4 *= chan_threshold_exp_4;
215 // repeat for the overall all-channels thresholds
216 float all_threshold_exp_4 = data->force[DT_RAWDENOISE_ALL][DT_IOP_RAWDENOISE_BANDS - i - 1];
217 all_threshold_exp_4 *= all_threshold_exp_4;
218 all_threshold_exp_4 *= all_threshold_exp_4;
219 noise[i] = noise_all[i] * all_threshold_exp_4 * chan_threshold_exp_4 * 16.0f * 16.0f;
220 // the following multiplication needs to stay separate from the above line, because merging the two changes
221 // the results on the integration test!
222 noise[i] *= data->threshold;
223 }
224}
225
227static int wavelet_denoise(const float *const restrict in, float *const restrict out, const dt_iop_roi_t *const roi,
228 const dt_iop_rawdenoise_data_t * const data, const uint32_t filters)
229{
230 const size_t size = (size_t)(roi->width / 2 + 1) * (roi->height / 2 + 1);
231 float *const restrict fimg = dt_pixelpipe_cache_alloc_align_float_cache(size, 0);
232 if (IS_NULL_PTR(fimg))
233 return 1;
234
235 const int nc = 4;
236 for(int c = 0; c < nc; c++) /* denoise R,G1,B,G3 individually */
237 {
238 const int color = FC(c % 2, c / 2, filters);
240 compute_channel_noise(noise,color,data);
241
242 // adjust for odd width and height
243 const int halfwidth = roi->width / 2 + (roi->width & (~(c >> 1)) & 1);
244 const int halfheight = roi->height / 2 + (roi->height & (~c) & 1);
245
246 // collect one of the R/G1/G2/B channels into a monochrome image, applying sqrt() to the values as a
247 // variance-stabilizing transform
249 for(int row = c & 1; row < roi->height; row += 2)
250 {
251 float *const restrict fimgp = fimg + (size_t)row / 2 * halfwidth;
252 const int offset = (c & 2) >> 1;
253 const float *const restrict inp = in + (size_t)row * roi->width + offset;
254 const int senselwidth = (roi->width-offset+1)/2;
255 for(int col = 0; col < senselwidth; col++)
256 fimgp[col] = sqrtf(MAX(0.0f, inp[2*col]));
257 }
258
259 // perform the wavelet decomposition and denoising
260 if(dwt_denoise(fimg, halfwidth, halfheight, DT_IOP_RAWDENOISE_BANDS, noise) != 0)
261 {
263 return 1;
264 }
265
266 // distribute the denoised data back out to the original R/G1/G2/B channel, squaring the resulting values to
267 // undo the original transform
269 for(int row = c & 1; row < roi->height; row += 2)
270 {
271 const float *const restrict fimgp = fimg + (size_t)row / 2 * halfwidth;
272 const int offset = (c & 2) >> 1;
273 float *const restrict outp = out + (size_t)row * roi->width + offset;
274 const int senselwidth = (roi->width-offset+1)/2;
275 for(int col = 0; col < senselwidth; col++)
276 {
277 float d = fimgp[col];
278 outp[2*col] = d * d;
279 }
280 }
281 }
282#if 0
283 /* FIXME: Haven't ported this part yet */
284 float maximum = 1.0; /* FIXME */
285 float black = 0.0; /* FIXME */
286 maximum *= BIT16;
287 black *= BIT16;
288 for (c=0; c<4; c++)
289 cblack[c] *= BIT16;
290 if (filters && colors == 3) /* pull G1 and G3 closer together */
291 {
292 float *window[4];
293 int wlast, blk[2];
294 float mul[2];
295 float thold = threshold/512;
296 for (row=0; row < 2; row++)
297 {
298 mul[row] = 0.125 * pre_mul[FC(row+1,0) | 1] / pre_mul[FC(row,0) | 1];
299 blk[row] = cblack[FC(row,0) | 1];
300 }
301 for (i=0; i < 4; i++)
302 window[i] = fimg + width*i;
303 for (wlast=-1, row=1; row < height-1; row++)
304 {
305 while (wlast < row+1)
306 {
307 for (wlast++, i=0; i < 4; i++)
308 window[(i+3) & 3] = window[i];
309 for (col = FC(wlast,1) & 1; col < width; col+=2)
310 window[2][col] = BAYER(wlast,col);
311 }
312 for (col = (FC(row,0) & 1)+1; col < width-1; col+=2)
313 {
314 float avg = ( window[0][col-1] + window[0][col+1] +
315 window[2][col-1] + window[2][col+1] - blk[~row & 1]*4 )
316 * mul[row & 1] + (window[1][col] + blk[row & 1]) * 0.5;
317 avg = avg > 0 ? sqrtf(avg) : 0;
318 float diff = sqrtf(BAYER(row,col)) - avg;
319 if (diff < -thold) diff += thold;
320 else if (diff > thold) diff -= thold;
321 else diff = 0;
322 BAYER(row,col) = SQR(avg+diff);
323 }
324 }
325 }
326#endif
328 return 0;
329}
330
331static inline float vstransform(const float value)
332{
333 return sqrtf(MAX(0.0f, value));
334}
335
337static int wavelet_denoise_xtrans(const float *const restrict in, float *const restrict out,
338 const dt_iop_roi_t *const restrict roi,
339 const dt_iop_rawdenoise_data_t *const data, const uint8_t (*const xtrans)[6])
340{
341 const int width = roi->width;
342 const int height = roi->height;
343 const size_t size = (size_t)width * height;
344 // allocate a buffer for the particular color channel to be denoise; we add two rows to simplify the
345 // channel-extraction code (no special case for top/bottom row)
346 float *const img = dt_pixelpipe_cache_alloc_align_float_cache((size_t)width * (height+2), 0);
347 if (IS_NULL_PTR(img))
348 {
349 // we ran out of memory, so just pass through the image without denoising
350 memcpy(out, in, sizeof(float) * size);
351 return 1;
352 }
353 float *const fimg = img + width; // point at the actual color channel contents in the buffer
354
355 for(int c = 0; c < 3; c++)
356 {
359
360 // ensure a defined value for every pixel in the top and bottom rows, even if they are more than
361 // one pixel away from the nearest neighbor of the same color and thus the simple interpolation
362 // used in the following loop does not set them
363 for (size_t col = 0; col < width; col++)
364 {
365 fimg[col] = 0.5f;
366 fimg[(size_t)(height-1)*width + col] = 0.5f;
367 }
368 const size_t nthreads = darktable.num_openmp_threads; // go direct, darktable.num_openmp_threads always returns numprocs
369 const size_t chunksize = (height + nthreads - 1) / nthreads;
370 __OMP_PARALLEL_FOR__(num_threads(nthreads) )
371 for(size_t chunk = 0; chunk < nthreads; chunk++)
372 {
373 const size_t start = chunk * chunksize;
374 const size_t pastend = MIN(start + chunksize,height);
375 for(size_t row = start; row < pastend; row++)
376 {
377 const float *const restrict inp = in + row * width;
378 float *const restrict fimgp = fimg + row * width;
379 // handle red/blue pixel in first column
380 if (c != 1 && FCxtrans(row, 0, roi, xtrans) == c)
381 {
382 // copy to neighbors above and right
383 const float d = vstransform(inp[0]);
384 fimgp[0] = fimgp[-width] = fimgp[-width+1] = d;
385 }
386 for(size_t col = (c != 1); col < width-1; col++)
387 {
388 if (FCxtrans(row, col, roi, xtrans) == c)
389 {
390 // the pixel at the current location has the desired color, so apply sqrt() as a variance-stablizing
391 // transform, and then do cheap nearest-neighbor interpolation by copying it to appropriate neighbors
392 const float d = vstransform(inp[col]);
393 fimgp[col] = d;
394 if (c == 1) // green pixel
395 {
396 // Copy to the right and down. The X-Trans color layout is such that copying to those two neighbors
397 // results in all positions being filled except in the left-most and right-most columns and sometimes
398 // the topmost and bottom-most rows (depending on how the ROI aligns with the CFA).
399 fimgp[col+1] = fimgp[col+width] = d;
400 }
401 else // red or blue pixel
402 {
403 // Copy value to all eight neighbors; it's OK to copy to the row above even when we're in row 0 (or
404 // the row below when in the last row) because the destination is sandwiched between other buffers
405 // that will be overwritten afterwards anyway. We need to copy to all adjacent positions because
406 // there may be two green pixels between nearest red/red or blue/blue, so each will cover one of the
407 // greens.
408 fimgp[col-width-1] = fimgp[col-width] = fimgp[col-width+1] = d; // row above
409 fimgp[col-1] = fimgp[col+1] = d; // left and right
410 if (row < pastend-1)
411 fimgp[col+width-1] = fimgp[col+width] = fimgp[col+width+1] = d; // row below
412 }
413 }
414 }
415 // leftmost and rightmost pixel in the row may still need to be filled in from a neighbor
416 if (FCxtrans(row, 0, roi, xtrans) != c)
417 {
418 int src = 0; // fallback is current sensel even if it has the wrong color
419 if (row > 1 && FCxtrans(row-1, 0, roi, xtrans) == c)
420 src = -width;
421 else if (FCxtrans(row, 1, roi, xtrans) == c)
422 src = 1;
423 else if (row > 1 && FCxtrans(row-1, 1, roi, xtrans) == c)
424 src = -width + 1;
425 fimgp[0] = vstransform(inp[src]);
426 }
427 // check the right-most pixel; if it's the desired color and not green, copy it to the neighbors
428 if (c != 1 && FCxtrans(row, width-1, roi, xtrans) == c)
429 {
430 // copy to neighbors above and left
431 const float d = vstransform(inp[width-1]);
432 fimgp[width-2] = fimgp[width-1] = fimgp[-1] = d;
433 }
434 else if (FCxtrans(row, width-1, roi, xtrans) != c)
435 {
436 int src = width-1; // fallback is current sensel even if it has the wrong color
437 if (FCxtrans(row, width-2, roi, xtrans) == c)
438 src = width-2;
439 else if (row > 1 && FCxtrans(row-1, width-1, roi, xtrans) == c)
440 src = -1;
441 else if (row > 1 && FCxtrans(row-1, width-2, roi, xtrans) == c)
442 src = -2;
443 fimgp[width-1] = vstransform(inp[src]);
444 }
445 }
446 if (pastend < height)
447 {
448 // Another slice follows us, and by updating the last row of our slice, we've clobbered values that
449 // were previously written by the other thread. Restore them.
450 const float *const restrict inp = in + pastend * width;
451 float *const restrict fimgp = fimg + pastend * width;
452 for (size_t col = 0; col < width-1; col++)
453 {
454 if (FCxtrans(pastend, col, roi, xtrans) == c)
455 {
456 const float d = vstransform(inp[col]);
457 if (c == 1) // green pixel
458 {
459 if (FCxtrans(pastend, col+1, roi, xtrans) != c)
460 fimgp[col] = fimgp[col+1] = d; // copy to the right
461 }
462 else // red/blue pixel
463 {
464 // copy the pixel's adjusted value to the prior row and left and right (if not at edge)
465 fimgp[col-width] = fimgp[col-width+1] = d;
466 if (col > 0) fimgp[col-width-1] = d;
467 }
468 }
469 // some red and blue values may need to be restored from the row TWO past the end of our slice
470 if (c != 1 && pastend+1 < height && FCxtrans(pastend+1, col, roi, xtrans) == c)
471 {
472 const float d = vstransform(inp[col+width]);
473 fimgp[col] = fimgp[col+1] = d;
474 if (col > 0) fimgp[col-1] = d;
475 }
476 }
477 }
478 }
479
480 // perform the wavelet decomposition and denoising
482 {
484 return 1;
485 }
486
487 // distribute the denoised data back out to the original R/G/B channel, squaring the resulting values to
488 // undo the original transform
490 for(int row = 0; row < height; row++)
491 {
492 const float *const restrict fimgp = fimg + (size_t)row * width;
493 float *const restrict outp = out + (size_t)row * width;
494 for(int col = 0; col < width; col++)
495 if(FCxtrans(row, col, roi, xtrans) == c)
496 {
497 float d = fimgp[col];
498 outp[col] = d * d;
499 }
500 }
501 }
502
504 return 0;
505}
506
507int process(struct dt_iop_module_t *self, const dt_dev_pixelpipe_t *pipe, const dt_dev_pixelpipe_iop_t *piece, const void *const ivoid,
508 void *const ovoid)
509{
510 const dt_iop_roi_t *const roi_in = &piece->roi_in;
511 const dt_iop_rawdenoise_data_t *const restrict d = (dt_iop_rawdenoise_data_t *)piece->data;
512
513 if(!(d->threshold > 0.0f))
514 {
515 dt_iop_image_copy_by_size(ovoid, ivoid, roi_in->width, roi_in->height, 1);
516 }
517 else
518 {
519 const uint32_t filters = piece->dsc_in.filters;
520 const uint8_t(*const xtrans)[6] = (const uint8_t(*const)[6])piece->dsc_in.xtrans;
521 if (filters != 9u)
522 return wavelet_denoise(ivoid, ovoid, roi_in, d, filters);
523 else
524 return wavelet_denoise_xtrans(ivoid, ovoid, roi_in, d, xtrans);
525 }
526 return 0;
527}
528
530{
531 dt_iop_default_init(module);
532
533 dt_iop_rawdenoise_params_t *d = module->default_params;
534
535 for(int k = 0; k < DT_IOP_RAWDENOISE_BANDS; k++)
536 {
537 for(int ch = 0; ch < DT_RAWDENOISE_NONE; ch++)
538 {
539 d->x[ch][k] = k / (DT_IOP_RAWDENOISE_BANDS - 1.f);
540 }
541 }
542}
543
544/* Single source of truth: raw CFA wavelet denoise operates on the mosaiced buffer, so it needs a
545 * CFA (needs_demosaic), not merely the RAW flag. An already-demosaiced raw (sRAW / linear DNG) has
546 * no mosaic to denoise. Shared by reload_defaults() and force_enable(). */
547static gboolean _rawdenoise_supported(const dt_image_t *img)
548{
549 return dt_image_needs_demosaic(img);
550}
551
553{
554 const dt_image_t *const img = &module->dev->image_storage;
555 module->hide_enable_button = !_rawdenoise_supported(img);
556 dt_iop_fmt_log(module, "reload_defaults: class=%s needs_demosaic=%d -> hide_enable=%d",
558 module->hide_enable_button);
559
560 // Stack visibility (raw vs non_raw) is applied from hide_enable_button in gui_update(), on the GUI
561 // thread when the widget exists; reload_defaults() stays params-only.
562 module->default_enabled = 0;
563}
564
565gboolean force_enable(struct dt_iop_module_t *self, const gboolean current_state)
566{
567 // History sanitization: a rawdenoise entry pasted onto a non-mosaic image is forced off here,
568 // at history-read time, rather than patched on the pipeline node in commit_params().
569 const gboolean active = _rawdenoise_supported(&self->dev->image_storage);
570 const gboolean state = current_state && active;
571 dt_iop_fmt_log(self, "force_enable: class=%s supported=%d current=%d -> %d",
573 active, current_state, state);
574 return state;
575}
576
579{
582
583 d->threshold = p->threshold;
584
585 for(int ch = 0; ch < DT_RAWDENOISE_NONE; ch++)
586 {
587 dt_draw_curve_set_point(d->curve[ch], 0, p->x[ch][DT_IOP_RAWDENOISE_BANDS - 2] - 1.0, p->y[ch][0]);
588 for(int k = 0; k < DT_IOP_RAWDENOISE_BANDS; k++)
589 dt_draw_curve_set_point(d->curve[ch], k, p->x[ch][k], p->y[ch][k]);
590 dt_draw_curve_set_point(d->curve[ch], DT_IOP_RAWDENOISE_BANDS + 1, p->x[ch][1] + 1.0,
591 p->y[ch][DT_IOP_RAWDENOISE_BANDS - 1]);
592 dt_draw_curve_calc_values(d->curve[ch], 0.0, 1.0, DT_IOP_RAWDENOISE_BANDS, NULL, d->force[ch]);
593 }
594
595 // Image-type gating handled at history level by force_enable()/reload_defaults().
596 dt_iop_fmt_log(self, "commit: class=%s enabled=%d",
598}
599
601{
604
605 piece->data = (void *)d;
606 piece->data_size = sizeof(dt_iop_rawdenoise_data_t);
607 for(int ch = 0; ch < DT_RAWDENOISE_NONE; ch++)
608 {
609 d->curve[ch] = dt_draw_curve_new(0.0, 1.0, CATMULL_ROM);
610 for(int k = 0; k < DT_IOP_RAWDENOISE_BANDS; k++)
611 (void)dt_draw_curve_add_point(d->curve[ch], default_params->x[ch][k], default_params->y[ch][k]);
612 }
613}
614
616{
618 for(int ch = 0; ch < DT_RAWDENOISE_NONE; ch++) dt_draw_curve_destroy(d->curve[ch]);
619 dt_free_align(piece->data);
620 piece->data = NULL;
621}
622
624{
626 // raw vs non_raw page, from the per-image support flag computed by reload_defaults()
627 gtk_stack_set_visible_child_name(GTK_STACK(self->widget), self->hide_enable_button ? "non_raw" : "raw");
628 gtk_widget_queue_draw(self->widget);
629}
630
631static void dt_iop_rawdenoise_get_params(dt_iop_rawdenoise_params_t *p, const int ch, const double mouse_x,
632 const double mouse_y, const float rad)
633{
634 for(int k = 0; k < DT_IOP_RAWDENOISE_BANDS; k++)
635 {
636 const float f = expf(-(mouse_x - p->x[ch][k]) * (mouse_x - p->x[ch][k]) / (rad * rad));
637 p->y[ch][k] = (1 - f) * p->y[ch][k] + f * mouse_y;
638 }
639}
640
641static gboolean rawdenoise_draw(GtkWidget *widget, cairo_t *crf, gpointer user_data)
642{
643 dt_iop_module_t *self = (dt_iop_module_t *)user_data;
646
647 int ch = (int)c->channel;
648 dt_draw_curve_set_point(c->transition_curve, 0, p.x[ch][DT_IOP_RAWDENOISE_BANDS - 2] - 1.0, p.y[ch][0]);
649 for(int k = 0; k < DT_IOP_RAWDENOISE_BANDS; k++)
650 dt_draw_curve_set_point(c->transition_curve, k + 1, p.x[ch][k], p.y[ch][k]);
651 dt_draw_curve_set_point(c->transition_curve, DT_IOP_RAWDENOISE_BANDS + 1, p.x[ch][1] + 1.0,
653
654 const int inset = DT_IOP_RAWDENOISE_INSET;
655 GtkAllocation allocation;
656 gtk_widget_get_allocation(widget, &allocation);
657 int width = allocation.width, height = allocation.height;
658 cairo_surface_t *cst = dt_cairo_image_surface_create(CAIRO_FORMAT_ARGB32, width, height);
659 cairo_t *cr = cairo_create(cst);
660 cairo_set_source_rgb(cr, .2, .2, .2);
661
662 cairo_paint(cr);
663
664 cairo_translate(cr, inset, inset);
665 width -= 2 * inset;
666 height -= 2 * inset;
667
668 cairo_set_line_width(cr, DT_PIXEL_APPLY_DPI(1.0));
669 cairo_set_source_rgb(cr, .1, .1, .1);
670 cairo_rectangle(cr, 0, 0, width, height);
671 cairo_stroke(cr);
672
673 cairo_set_source_rgb(cr, .3, .3, .3);
674 cairo_rectangle(cr, 0, 0, width, height);
675 cairo_fill(cr);
676
677 // draw grid
678 cairo_set_line_width(cr, DT_PIXEL_APPLY_DPI(.4));
679 cairo_set_source_rgb(cr, .1, .1, .1);
680 dt_draw_grid(cr, 8, 0, 0, width, height);
681
682 if(c->mouse_y > 0 || c->dragging)
683 {
684 // draw min/max curves:
685 dt_iop_rawdenoise_get_params(&p, c->channel, c->mouse_x, 1., c->mouse_radius);
686 dt_draw_curve_set_point(c->transition_curve, 0, p.x[ch][DT_IOP_RAWDENOISE_BANDS - 2] - 1.0, p.y[ch][0]);
687 for(int k = 0; k < DT_IOP_RAWDENOISE_BANDS; k++)
688 dt_draw_curve_set_point(c->transition_curve, k + 1, p.x[ch][k], p.y[ch][k]);
689 dt_draw_curve_set_point(c->transition_curve, DT_IOP_RAWDENOISE_BANDS + 1, p.x[ch][1] + 1.0,
691 dt_draw_curve_calc_values(c->transition_curve, 0.0, 1.0, DT_IOP_RAWDENOISE_RES, c->draw_min_xs, c->draw_min_ys);
692
694 dt_iop_rawdenoise_get_params(&p, c->channel, c->mouse_x, .0, c->mouse_radius);
695 dt_draw_curve_set_point(c->transition_curve, 0, p.x[ch][DT_IOP_RAWDENOISE_BANDS - 2] - 1.0, p.y[ch][0]);
696 for(int k = 0; k < DT_IOP_RAWDENOISE_BANDS; k++)
697 dt_draw_curve_set_point(c->transition_curve, k + 1, p.x[ch][k], p.y[ch][k]);
698 dt_draw_curve_set_point(c->transition_curve, DT_IOP_RAWDENOISE_BANDS + 1, p.x[ch][1] + 1.0,
700 dt_draw_curve_calc_values(c->transition_curve, 0.0, 1.0, DT_IOP_RAWDENOISE_RES, c->draw_max_xs, c->draw_max_ys);
701 }
702
703 cairo_save(cr);
704
705 // draw selected cursor
706 cairo_translate(cr, 0, height);
707
708 cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
709 cairo_set_line_width(cr, DT_PIXEL_APPLY_DPI(2.));
710
711 for(int i = 0; i < DT_RAWDENOISE_NONE; i++)
712 {
713 // draw curves, selected last
714 ch = ((int)c->channel + i + 1) % DT_RAWDENOISE_NONE;
715 float alpha = 0.3;
716 if(i == DT_RAWDENOISE_NONE - 1) alpha = 1.0;
717 switch(ch)
718 {
719 case 0:
720 cairo_set_source_rgba(cr, .7, .7, .7, alpha);
721 break;
722 case 1:
723 cairo_set_source_rgba(cr, .7, .1, .1, alpha);
724 break;
725 case 2:
726 cairo_set_source_rgba(cr, .1, .7, .1, alpha);
727 break;
728 case 3:
729 cairo_set_source_rgba(cr, .1, .1, .7, alpha);
730 break;
731 }
732
734 dt_draw_curve_set_point(c->transition_curve, 0, p.x[ch][DT_IOP_RAWDENOISE_BANDS - 2] - 1.0, p.y[ch][0]);
735 for(int k = 0; k < DT_IOP_RAWDENOISE_BANDS; k++)
736 dt_draw_curve_set_point(c->transition_curve, k + 1, p.x[ch][k], p.y[ch][k]);
737 dt_draw_curve_set_point(c->transition_curve, DT_IOP_RAWDENOISE_BANDS + 1, p.x[ch][1] + 1.0,
739 dt_draw_curve_calc_values(c->transition_curve, 0.0, 1.0, DT_IOP_RAWDENOISE_RES, c->draw_xs, c->draw_ys);
740 cairo_move_to(cr, 0 * width / (float)(DT_IOP_RAWDENOISE_RES - 1), -height * c->draw_ys[0]);
741 for(int k = 1; k < DT_IOP_RAWDENOISE_RES; k++)
742 cairo_line_to(cr, k * width / (float)(DT_IOP_RAWDENOISE_RES - 1), -height * c->draw_ys[k]);
743 cairo_stroke(cr);
744 }
745
746 ch = c->channel;
747 // draw dots on knots
748 cairo_set_source_rgb(cr, 0.7, 0.7, 0.7);
749 cairo_set_line_width(cr, DT_PIXEL_APPLY_DPI(1.));
750 for(int k = 0; k < DT_IOP_RAWDENOISE_BANDS; k++)
751 {
752 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);
753 if(c->x_move == k)
754 cairo_fill(cr);
755 else
756 cairo_stroke(cr);
757 }
758
759 if(c->mouse_y > 0 || c->dragging)
760 {
761 // draw min/max, if selected
762 cairo_set_source_rgba(cr, .7, .7, .7, .6);
763 cairo_move_to(cr, 0, -height * c->draw_min_ys[0]);
764 for(int k = 1; k < DT_IOP_RAWDENOISE_RES; k++)
765 cairo_line_to(cr, k * width / (float)(DT_IOP_RAWDENOISE_RES - 1), -height * c->draw_min_ys[k]);
766 for(int k = DT_IOP_RAWDENOISE_RES - 1; k >= 0; k--)
767 cairo_line_to(cr, k * width / (float)(DT_IOP_RAWDENOISE_RES - 1), -height * c->draw_max_ys[k]);
768 cairo_close_path(cr);
769 cairo_fill(cr);
770 // draw mouse focus circle
771 cairo_set_source_rgba(cr, .9, .9, .9, .5);
772 const float pos = DT_IOP_RAWDENOISE_RES * c->mouse_x;
773 int k = (int)pos;
774 const float f = k - pos;
776 float ht = -height * (f * c->draw_ys[k] + (1 - f) * c->draw_ys[k + 1]);
777 cairo_arc(cr, c->mouse_x * width, ht, c->mouse_radius * width, 0, 2. * M_PI);
778 cairo_stroke(cr);
779 }
780
781 cairo_restore(cr);
782
783 cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
784
785 // draw labels:
786 PangoLayout *layout;
787 PangoRectangle ink;
788 PangoFontDescription *desc = pango_font_description_copy_static(darktable.bauhaus->pango_font_desc);
789 pango_font_description_set_weight(desc, PANGO_WEIGHT_BOLD);
790 pango_font_description_set_absolute_size(desc, (.08 * height) * PANGO_SCALE);
791 layout = pango_cairo_create_layout(cr);
792 pango_layout_set_font_description(layout, desc);
793 cairo_set_source_rgb(cr, .1, .1, .1);
794
795 pango_layout_set_text(layout, _("coarse"), -1);
796 pango_layout_get_pixel_extents(layout, &ink, NULL);
797 cairo_move_to(cr, .02 * width - ink.y, .5 * (height + ink.width));
798 cairo_save(cr);
799 cairo_rotate(cr, -M_PI * .5f);
800 pango_cairo_show_layout(cr, layout);
801 cairo_restore(cr);
802
803 pango_layout_set_text(layout, _("fine"), -1);
804 pango_layout_get_pixel_extents(layout, &ink, NULL);
805 cairo_move_to(cr, .98 * width - ink.height, .5 * (height + ink.width));
806 cairo_save(cr);
807 cairo_rotate(cr, -M_PI * .5f);
808 pango_cairo_show_layout(cr, layout);
809 cairo_restore(cr);
810
811
812 pango_layout_set_text(layout, _("smooth"), -1);
813 pango_layout_get_pixel_extents(layout, &ink, NULL);
814 cairo_move_to(cr, .5 * (width - ink.width), .08 * height - ink.height);
815 pango_cairo_show_layout(cr, layout);
816
817 pango_layout_set_text(layout, _("noisy"), -1);
818 pango_layout_get_pixel_extents(layout, &ink, NULL);
819 cairo_move_to(cr, .5 * (width - ink.width), .97 * height - ink.height);
820 pango_cairo_show_layout(cr, layout);
821
822 pango_font_description_free(desc);
823 g_object_unref(layout);
824 cairo_destroy(cr);
825 cairo_set_source_surface(crf, cst, 0, 0);
826 cairo_paint(crf);
827 cairo_surface_destroy(cst);
828 return TRUE;
829}
830
831static gboolean rawdenoise_motion_notify(GtkWidget *widget, GdkEventMotion *event, gpointer user_data)
832{
833 dt_iop_module_t *self = (dt_iop_module_t *)user_data;
836 const int inset = DT_IOP_RAWDENOISE_INSET;
837 GtkAllocation allocation;
838 gtk_widget_get_allocation(widget, &allocation);
839 int height = allocation.height - 2 * inset, width = allocation.width - 2 * inset;
840 if(!c->dragging) c->mouse_x = CLAMP(event->x - inset, 0, width) / (float)width;
841 c->mouse_y = 1.0 - CLAMP(event->y - inset, 0, height) / (float)height;
842 if(c->dragging)
843 {
844 *p = c->drag_params;
845 if(c->x_move < 0)
846 {
847 dt_iop_rawdenoise_get_params(p, c->channel, c->mouse_x, c->mouse_y + c->mouse_pick, c->mouse_radius);
848 }
849 gtk_widget_queue_draw(widget);
851 }
852 else
853 {
854 c->x_move = -1;
855 gtk_widget_queue_draw(widget);
856 }
857 return TRUE;
858}
859
860static gboolean rawdenoise_button_press(GtkWidget *widget, GdkEventButton *event, gpointer user_data)
861{
862 dt_iop_module_t *self = (dt_iop_module_t *)user_data;
864 const int ch = c->channel;
865 if(event->button == 1 && event->type == GDK_2BUTTON_PRESS)
866 {
867 // reset current curve
870 for(int k = 0; k < DT_IOP_RAWDENOISE_BANDS; k++)
871 {
872 p->x[ch][k] = d->x[ch][k];
873 p->y[ch][k] = d->y[ch][k];
874 }
876 gtk_widget_queue_draw(self->widget);
877 }
878 else if(event->button == 1)
879 {
880 c->drag_params = *(dt_iop_rawdenoise_params_t *)self->params;
881 const int inset = DT_IOP_RAWDENOISE_INSET;
882 GtkAllocation allocation;
883 gtk_widget_get_allocation(widget, &allocation);
884 int height = allocation.height - 2 * inset, width = allocation.width - 2 * inset;
885 c->mouse_pick
886 = dt_draw_curve_calc_value(c->transition_curve, CLAMP(event->x - inset, 0, width) / (float)width);
887 c->mouse_pick -= 1.0 - CLAMP(event->y - inset, 0, height) / (float)height;
888 c->dragging = 1;
889 return TRUE;
890 }
891 return FALSE;
892}
893
894static gboolean rawdenoise_button_release(GtkWidget *widget, GdkEventButton *event, gpointer user_data)
895{
896 if(event->button == 1)
897 {
898 dt_iop_module_t *self = (dt_iop_module_t *)user_data;
900 c->dragging = 0;
901 return TRUE;
902 }
903 return FALSE;
904}
905
906static gboolean rawdenoise_leave_notify(GtkWidget *widget, GdkEventCrossing *event, gpointer user_data)
907{
908 dt_iop_module_t *self = (dt_iop_module_t *)user_data;
910 if(!c->dragging) c->mouse_y = -1.0;
911 gtk_widget_queue_draw(widget);
912 return TRUE;
913}
914
915static gboolean rawdenoise_scrolled(GtkWidget *widget, GdkEventScroll *event, gpointer user_data)
916{
917 dt_iop_module_t *self = (dt_iop_module_t *)user_data;
919
920 int delta_y;
921 if(dt_gui_get_scroll_unit_deltas(event, NULL, &delta_y))
922 {
923 c->mouse_radius = CLAMP(c->mouse_radius * (1.0 + 0.1 * delta_y), 0.2 / DT_IOP_RAWDENOISE_BANDS, 1.0);
924 gtk_widget_queue_draw(widget);
925 }
926
927 return TRUE;
928}
929
930static void rawdenoise_tab_switch(GtkNotebook *notebook, GtkWidget *page, guint page_num, gpointer user_data)
931{
932 dt_iop_module_t *self = (dt_iop_module_t *)user_data;
933 if(dt_gui_widgets_suppressed()) return;
935 c->channel = (dt_iop_rawdenoise_channel_t)page_num;
936 gtk_widget_queue_draw(self->widget);
937}
938
940{
943
944 c->channel = dt_conf_get_int("plugins/darkroom/rawdenoise/gui_channel");
945 c->channel_tabs = GTK_NOTEBOOK(gtk_notebook_new());
946
947 dt_ui_notebook_page(c->channel_tabs, N_("all"), NULL);
948 dt_ui_notebook_page(c->channel_tabs, N_("R"), NULL);
949 dt_ui_notebook_page(c->channel_tabs, N_("G"), NULL);
950 dt_ui_notebook_page(c->channel_tabs, N_("B"), NULL);
951
952 gtk_widget_show(gtk_notebook_get_nth_page(c->channel_tabs, c->channel));
953 gtk_notebook_set_current_page(c->channel_tabs, c->channel);
954 g_signal_connect(G_OBJECT(c->channel_tabs), "switch_page", G_CALLBACK(rawdenoise_tab_switch), self);
955
956 const int ch = (int)c->channel;
957 c->transition_curve = dt_draw_curve_new(0.0, 1.0, CATMULL_ROM);
958 (void)dt_draw_curve_add_point(c->transition_curve, p->x[ch][DT_IOP_RAWDENOISE_BANDS - 2] - 1.0,
959 p->y[ch][DT_IOP_RAWDENOISE_BANDS - 2]);
960 for(int k = 0; k < DT_IOP_RAWDENOISE_BANDS; k++)
961 (void)dt_draw_curve_add_point(c->transition_curve, p->x[ch][k], p->y[ch][k]);
962 (void)dt_draw_curve_add_point(c->transition_curve, p->x[ch][1] + 1.0, p->y[ch][1]);
963
964 c->mouse_x = c->mouse_y = c->mouse_pick = -1.0;
965 c->dragging = 0;
966 c->x_move = -1;
967 self->timeout_handle = 0;
968 c->mouse_radius = 1.0 / (DT_IOP_RAWDENOISE_BANDS * 2);
969
970 GtkWidget *box_raw = self->widget = gtk_box_new(GTK_ORIENTATION_VERTICAL, DT_GUI_BOX_SPACING);
971
972 c->area = GTK_DRAWING_AREA(gtk_drawing_area_new());
973 gtk_widget_set_hexpand(GTK_WIDGET(c->area), TRUE);
974 g_object_set_data(G_OBJECT(c->area), "iop-instance", self);
975
976 gtk_box_pack_start(GTK_BOX(box_raw), GTK_WIDGET(c->channel_tabs), FALSE, FALSE, 0);
977 gtk_box_pack_start(GTK_BOX(box_raw),
978 dt_ui_resizable_drawing_area(GTK_WIDGET(c->area),
979 "plugins/darkroom/rawdenoise/graphheight", 280, 100),
980 FALSE, FALSE, 0);
981
982 gtk_widget_add_events(GTK_WIDGET(c->area), GDK_POINTER_MOTION_MASK | darktable.gui->scroll_mask
983 | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK
984 | GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK);
985 g_signal_connect(G_OBJECT(c->area), "draw", G_CALLBACK(rawdenoise_draw), self);
986 g_signal_connect(G_OBJECT(c->area), "button-press-event", G_CALLBACK(rawdenoise_button_press), self);
987 g_signal_connect(G_OBJECT(c->area), "button-release-event", G_CALLBACK(rawdenoise_button_release), self);
988 g_signal_connect(G_OBJECT(c->area), "motion-notify-event", G_CALLBACK(rawdenoise_motion_notify), self);
989 g_signal_connect(G_OBJECT(c->area), "leave-notify-event", G_CALLBACK(rawdenoise_leave_notify), self);
990 g_signal_connect(G_OBJECT(c->area), "scroll-event", G_CALLBACK(rawdenoise_scrolled), self);
991
992 c->threshold = dt_bauhaus_slider_from_params(self, "threshold");
993 dt_bauhaus_slider_set_soft_max(c->threshold, 0.1);
994 dt_bauhaus_slider_set_digits(c->threshold, 3);
995
996 // start building top level widget
997 self->widget = gtk_stack_new();
998 gtk_stack_set_homogeneous(GTK_STACK(self->widget), FALSE);
999
1000 GtkWidget *label_non_raw = dt_ui_label_new(_("raw denoising\nonly works for raw images."));
1001
1002 gtk_stack_add_named(GTK_STACK(self->widget), label_non_raw, "non_raw");
1003 gtk_stack_add_named(GTK_STACK(self->widget), box_raw, "raw");
1004}
1005
1007{
1009 dt_conf_set_int("plugins/darkroom/rawdenoise/gui_channel", c->channel);
1010 dt_draw_curve_destroy(c->transition_curve);
1012
1014}
1015// clang-format off
1016// modelines: These editor modelines have been set for all relevant files by tools/update_modelines.py
1017// vim: shiftwidth=2 expandtab tabstop=2 cindent
1018// kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-spaces modified;
1019// clang-format on
#define SQR(a)
Definition ashift.c:128
#define TRUE
Definition ashift_lsd.c:162
#define FALSE
Definition ashift_lsd.c:158
void dt_bauhaus_slider_set_digits(GtkWidget *widget, int val)
Definition bauhaus.c:3549
void dt_bauhaus_slider_set_soft_max(GtkWidget *widget, float val)
Definition bauhaus.c:1626
int width
Definition bilateral.h:1
int height
Definition bilateral.h:1
static const dt_aligned_pixel_simd_t const dt_adaptation_t const float p
@ IOP_CS_RAW
const dt_aligned_pixel_t f
const float threshold
const dt_colormatrix_t dt_aligned_pixel_t out
static const int row
typedef void((*dt_cache_allocate_t)(void *userdata, dt_cache_entry_t *entry))
dt_image_pipe_class_t dt_image_pipe_class(const dt_image_t *img)
const char * dt_image_pipe_class_name(const dt_image_pipe_class_t klass)
gboolean dt_image_needs_demosaic(const dt_image_t *img)
void dt_conf_set_int(const char *name, int val)
int dt_conf_get_int(const char *name)
#define CATMULL_ROM
Definition curve_tools.h:34
darktable_t darktable
Definition darktable.c:183
#define dt_free_align(ptr)
Definition darktable.h:503
static void * dt_calloc_align(size_t size)
Definition darktable.h:510
#define dt_pixelpipe_cache_alloc_align_float_cache(pixels, id)
Definition darktable.h:459
void void void gboolean dt_gui_widgets_suppressed(void)
Definition gtk.c:194
#define DT_MODULE_INTROSPECTION(MODVER, PARAMSTYPE)
Definition darktable.h:151
#define dt_pixelpipe_cache_free_align(mem)
Definition darktable.h:475
#define __DT_CLONE_TARGETS__
Definition darktable.h:379
#define __OMP_PARALLEL_FOR__(...)
Definition darktable.h:270
static const dt_aligned_pixel_simd_t value
Definition darktable.h:599
#define IS_NULL_PTR(p)
C is way too permissive with !=, == and if(var) checks, which can mean too many things depending on w...
Definition darktable.h:293
static int FCxtrans(const int row, const int col, global const unsigned char(*const xtrans)[6])
static int FC(const int row, const int col, const unsigned int filters)
#define dt_dev_add_history_item(dev, module, enable, redraw)
void dt_iop_params_t
Definition dev_history.h:42
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
__DT_CLONE_TARGETS__ int dwt_denoise(float *const img, const int width, const int height, const int bands, const float *const noise)
Definition dwt.c:513
const dt_collection_filter_flag_t colors[6]
Definition filter.c:295
void default_input_format(dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece, dt_iop_buffer_dsc_t *dsc)
Definition format.c:57
void dt_iop_buffer_dsc_update_bpp(dt_iop_buffer_dsc_t *dsc)
Definition format.c:28
gboolean dt_gui_get_scroll_unit_deltas(const GdkEventScroll *event, int *delta_x, int *delta_y)
Definition gtk.c:300
GtkWidget * dt_ui_resizable_drawing_area(GtkWidget *area, char *config_str, int default_height, int min_height)
Make a self-drawing widget (typically a GtkDrawingArea graph or scope) vertically resizable.
Definition gtk.c:3068
GtkWidget * dt_ui_notebook_page(GtkNotebook *notebook, const char *text, const char *tooltip)
Definition gtk.c:2470
static cairo_surface_t * dt_cairo_image_surface_create(cairo_format_t format, int width, int height)
Definition gtk.h:322
#define DT_GUI_BOX_SPACING
Definition gtk.h:109
#define DT_PIXEL_APPLY_DPI(value)
Definition gtk.h:90
static GtkWidget * dt_ui_label_new(const gchar *str)
Definition gtk.h:479
void dt_gui_throttle_cancel(gpointer source)
void dt_gui_throttle_queue(gpointer source, dt_gui_throttle_callback_t callback, gpointer user_data)
static void dt_iop_image_copy_by_size(float *const __restrict__ out, const float *const __restrict__ in, const size_t width, const size_t height, const size_t ch)
Definition imagebuf.h:87
void dt_iop_throttled_history_update(gpointer data)
Definition imageop.c:3214
void dt_iop_default_init(dt_iop_module_t *module)
Definition imageop.c:321
const char ** dt_iop_set_description(dt_iop_module_t *module, const char *main_text, const char *purpose, const char *input, const char *process, const char *output)
Definition imageop.c:3220
#define IOP_GUI_FREE
Definition imageop.h:641
#define dt_iop_fmt_log(module, fmt,...)
Debug helper to trace a module's input-format-driven decisions on the -d pipe channel (DT_DEBUG_PIPE)...
Definition imageop.h:490
@ IOP_FLAGS_DEPRECATED
Definition imageop.h:198
@ IOP_FLAGS_SUPPORTS_BLENDING
Definition imageop.h:197
@ IOP_GROUP_REPAIR
Definition imageop.h:170
#define IOP_GUI_ALLOC(module)
Definition imageop.h:638
GtkWidget * dt_bauhaus_slider_from_params(dt_iop_module_t *self, const char *param)
Definition imageop_gui.c:77
void *const ovoid
float *const restrict const size_t k
float *const restrict const size_t const size_t ch
#define M_PI
Definition math.h:45
size_t size
Definition mipmap_cache.c:3
#define DT_IOP_RAWDENOISE_RES
Definition rawdenoise.c:72
void init(dt_iop_module_t *module)
Definition rawdenoise.c:529
static void rawdenoise_tab_switch(GtkNotebook *notebook, GtkWidget *page, guint page_num, gpointer user_data)
Definition rawdenoise.c:930
const char ** description(struct dt_iop_module_t *self)
Definition rawdenoise.c:156
int default_group()
Definition rawdenoise.c:170
static gboolean rawdenoise_button_release(GtkWidget *widget, GdkEventButton *event, gpointer user_data)
Definition rawdenoise.c:894
static __DT_CLONE_TARGETS__ void compute_channel_noise(float *const noise, int color, const dt_iop_rawdenoise_data_t *const data)
Definition rawdenoise.c:191
void reload_defaults(dt_iop_module_t *module)
Definition rawdenoise.c:552
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)
Definition rawdenoise.c:577
static gboolean rawdenoise_leave_notify(GtkWidget *widget, GdkEventCrossing *event, gpointer user_data)
Definition rawdenoise.c:906
void gui_update(dt_iop_module_t *self)
Refresh GUI controls from current params and configuration.
Definition rawdenoise.c:623
#define DT_IOP_RAWDENOISE_INSET
Definition rawdenoise.c:71
void init_pipe(struct dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece)
Definition rawdenoise.c:600
const char * name()
Definition rawdenoise.c:151
static gboolean _rawdenoise_supported(const dt_image_t *img)
Definition rawdenoise.c:547
void gui_init(dt_iop_module_t *self)
Definition rawdenoise.c:939
#define BIT16
Definition rawdenoise.c:188
void gui_cleanup(dt_iop_module_t *self)
static __DT_CLONE_TARGETS__ int wavelet_denoise(const float *const restrict in, float *const restrict out, const dt_iop_roi_t *const roi, const dt_iop_rawdenoise_data_t *const data, const uint32_t filters)
Definition rawdenoise.c:227
int default_colorspace(dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, const dt_dev_pixelpipe_iop_t *piece)
Definition rawdenoise.c:175
void input_format(dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece, dt_iop_buffer_dsc_t *dsc)
Definition rawdenoise.c:180
int flags()
Definition rawdenoise.c:165
static __DT_CLONE_TARGETS__ int wavelet_denoise_xtrans(const float *const restrict in, float *const restrict out, const dt_iop_roi_t *const restrict roi, const dt_iop_rawdenoise_data_t *const data, const uint8_t(*const xtrans)[6])
Definition rawdenoise.c:337
#define DT_IOP_RAWDENOISE_BANDS
Definition rawdenoise.c:73
static gboolean rawdenoise_scrolled(GtkWidget *widget, GdkEventScroll *event, gpointer user_data)
Definition rawdenoise.c:915
static gboolean rawdenoise_draw(GtkWidget *widget, cairo_t *crf, gpointer user_data)
Definition rawdenoise.c:641
static gboolean rawdenoise_button_press(GtkWidget *widget, GdkEventButton *event, gpointer user_data)
Definition rawdenoise.c:860
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)
Definition rawdenoise.c:507
gboolean force_enable(struct dt_iop_module_t *self, const gboolean current_state)
Definition rawdenoise.c:565
static float vstransform(const float value)
Definition rawdenoise.c:331
void cleanup_pipe(struct dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece)
Definition rawdenoise.c:615
static gboolean rawdenoise_motion_notify(GtkWidget *widget, GdkEventMotion *event, gpointer user_data)
Definition rawdenoise.c:831
static void dt_iop_rawdenoise_get_params(dt_iop_rawdenoise_params_t *p, const int ch, const double mouse_x, const double mouse_y, const float rad)
Definition rawdenoise.c:631
dt_iop_rawdenoise_channel_t
Definition rawdenoise.c:76
@ DT_RAWDENOISE_G
Definition rawdenoise.c:79
@ DT_RAWDENOISE_R
Definition rawdenoise.c:78
@ DT_RAWDENOISE_B
Definition rawdenoise.c:80
@ DT_RAWDENOISE_ALL
Definition rawdenoise.c:77
@ DT_RAWDENOISE_NONE
Definition rawdenoise.c:81
int legacy_params(dt_iop_module_t *self, const void *const old_params, const int old_version, void *new_params, const int new_version)
Definition rawdenoise.c:121
struct _GtkWidget GtkWidget
Definition splash.h:29
const float uint32_t state[4]
const float noise
int32_t num_openmp_threads
Definition darktable.h:786
struct dt_gui_gtk_t * gui
Definition darktable.h:803
struct dt_bauhaus_t * bauhaus
Definition darktable.h:806
struct dt_develop_t * develop
Definition darktable.h:798
PangoFontDescription * pango_font_desc
Definition bauhaus.h:274
dt_iop_buffer_dsc_t dsc_in
struct dt_iop_module_t *void * data
struct dt_develop_t * dev
dt_image_t image_storage
Definition develop.h:259
gint scroll_mask
Definition gtk.h:230
uint32_t filters
Definition format.h:60
unsigned int channels
Definition format.h:54
uint8_t xtrans[6][6]
Definition format.h:70
int32_t hide_enable_button
Definition imageop.h:292
dt_iop_params_t * default_params
Definition imageop.h:344
GtkWidget * widget
Definition imageop.h:374
struct dt_develop_t * dev
Definition imageop.h:333
dt_iop_gui_data_t * gui_data
Definition imageop.h:348
guint timeout_handle
Definition imageop.h:408
dt_iop_params_t * params
Definition imageop.h:344
float force[DT_RAWDENOISE_NONE][5]
Definition rawdenoise.c:114
dt_draw_curve_t * curve[DT_RAWDENOISE_NONE]
Definition rawdenoise.c:112
dt_iop_rawdenoise_channel_t channel
Definition rawdenoise.c:113
dt_iop_rawdenoise_channel_t channel
Definition rawdenoise.c:103
dt_iop_rawdenoise_params_t drag_params
Definition rawdenoise.c:100
dt_draw_curve_t * transition_curve
Definition rawdenoise.c:93
float x[DT_RAWDENOISE_NONE][5]
Definition rawdenoise.c:87
float y[DT_RAWDENOISE_NONE][5]
Definition rawdenoise.c:88
Region of interest passed through the pixelpipe.
Definition imageop.h:72
GtkWidget * window
GtkWidget * notebook
#define MIN(a, b)
Definition thinplate.c:32
#define MAX(a, b)
Definition thinplate.c:29