Ansel 0.0
A darktable fork - bloat + design vision
Loading...
Searching...
No Matches
demosaic.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-2016 johannes hanika.
5 Copyright (C) 2011 Antony Dovgal.
6 Copyright (C) 2011 Henrik Andersson.
7 Copyright (C) 2011 Kaminsky Andrey.
8 Copyright (C) 2011 Olivier Tribout.
9 Copyright (C) 2011 Robert Bieber.
10 Copyright (C) 2011 Sergey Pavlov.
11 Copyright (C) 2011-2014, 2016, 2019 Tobias Ellinghaus.
12 Copyright (C) 2011-2012, 2014-2017 Ulrich Pegelow.
13 Copyright (C) 2012, 2015, 2020-2022 Aldric Renaudin.
14 Copyright (C) 2012, 2014-2015 Pascal de Bruijn.
15 Copyright (C) 2012 Richard Wonka.
16 Copyright (C) 2013-2016, 2019 Roman Lebedev.
17 Copyright (C) 2013 Thomas Pryds.
18 Copyright (C) 2014-2017 Dan Torop.
19 Copyright (C) 2015-2016 Pedro Côrte-Real.
20 Copyright (C) 2017-2019 Heiko Bauke.
21 Copyright (C) 2017, 2019 Ingo Liebhardt.
22 Copyright (C) 2017, 2019, 2021 luzpaz.
23 Copyright (C) 2017 Peter Budai.
24 Copyright (C) 2018-2020, 2023-2026 Aurélien PIERRE.
25 Copyright (C) 2018-2019 Edgardo Hoszowski.
26 Copyright (C) 2018 Kelvie Wong.
27 Copyright (C) 2018 Maurizio Paglia.
28 Copyright (C) 2018, 2020-2022 Pascal Obry.
29 Copyright (C) 2018 rawfiner.
30 Copyright (C) 2019 Andreas Schneider.
31 Copyright (C) 2019-2022 Hanno Schwalm.
32 Copyright (C) 2020 Chris Elston.
33 Copyright (C) 2020, 2022 Diederik Ter Rahe.
34 Copyright (C) 2020 Felipe Contreras.
35 Copyright (C) 2020-2021 Hubert Kowalski.
36 Copyright (C) 2020-2021 Ralf Brown.
37 Copyright (C) 2022 Martin Bařinka.
38 Copyright (C) 2022 Philipp Lutz.
39 Copyright (C) 2022 Victor Forsiuk.
40 Copyright (C) 2023 Alynx Zhou.
41 Copyright (C) 2025 Guillaume Stutin.
42
43 darktable is free software: you can redistribute it and/or modify
44 it under the terms of the GNU General Public License as published by
45 the Free Software Foundation, either version 3 of the License, or
46 (at your option) any later version.
47
48 darktable is distributed in the hope that it will be useful,
49 but WITHOUT ANY WARRANTY; without even the implied warranty of
50 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
51 GNU General Public License for more details.
52
53 You should have received a copy of the GNU General Public License
54 along with darktable. If not, see <http://www.gnu.org/licenses/>.
55*/
56
57#ifdef HAVE_CONFIG_H
58#include "config.h"
59#endif
60
61#include "common/darktable.h"
62#include "common/imagebuf.h"
63#include "common/image_cache.h"
65#include "common/math.h"
66#include "common/opencl.h"
67#include "control/control.h"
68#include "develop/blend.h"
69#include "develop/develop.h"
70#include "develop/format.h"
71#include "develop/imageop.h"
73#include "develop/imageop_gui.h"
74#include "develop/masks.h"
76#include "develop/tiling.h"
77
78#include "bauhaus/bauhaus.h"
79#include "common/colorspaces.h"
80#include "control/conf.h"
82#include "common/bspline.h"
83
84#include "gui/gtk.h"
85#include "iop/iop_api.h"
86
87#include <memory.h>
88#include <stdlib.h>
89#include <string.h>
90#include <time.h>
91#include <complex.h>
92#include <glib.h>
93
94#ifdef __GNUC__
95 #define INLINE __inline
96#else
97 #define INLINE inline
98#endif
99
100#define DEMOSAIC_XTRANS 1024 // masks for non-Bayer demosaic ops
101#define DEMOSAIC_DUAL 2048 // masks for dual demosaicing methods
102#define REDUCESIZE 64
103
104// X-Trans CFA tile period, still needed to align tile boundaries in tiling_callback()
105// (unrelated to the ROI crop position, which no longer needs to be pattern-aligned:
106// process() rebuilds the exact phase for any roi_in->x/y, see the top of process()).
107#define XTRANS_SNAPPER 6
108#define DOWNSAMPLE_GUIDED_SCALES 1
109
111
113{
114 // methods for Bayer images
115 DT_IOP_DEMOSAIC_PPG = 0, // $DESCRIPTION: "PPG"
116 DT_IOP_DEMOSAIC_AMAZE = 1, // $DESCRIPTION: "AMaZE"
117 DT_IOP_DEMOSAIC_VNG4 = 2, // $DESCRIPTION: "VNG4"
118 DT_IOP_DEMOSAIC_RCD = 5, // $DESCRIPTION: "RCD"
119 DT_IOP_DEMOSAIC_LMMSE = 6, // $DESCRIPTION: "LMMSE"
122 DT_IOP_DEMOSAIC_PASSTHROUGH_MONOCHROME = 3, // $DESCRIPTION: "passthrough (monochrome)"
123 DT_IOP_DEMOSAIC_PASSTHROUGH_COLOR = 4, // $DESCRIPTION: "photosite color (debug)"
124 // methods for x-trans images
125 DT_IOP_DEMOSAIC_VNG = DEMOSAIC_XTRANS | 0, // $DESCRIPTION: "VNG"
126 DT_IOP_DEMOSAIC_MARKESTEIJN = DEMOSAIC_XTRANS | 1, // $DESCRIPTION: "Markesteijn 1-pass"
127 DT_IOP_DEMOSAIC_MARKESTEIJN_3 = DEMOSAIC_XTRANS | 2, // $DESCRIPTION: "Markesteijn 3-pass"
128 DT_IOP_DEMOSAIC_FDC = DEMOSAIC_XTRANS | 4, // $DESCRIPTION: "frequency domain chroma"
129 DT_IOP_DEMOSAIC_MARKEST3_VNG = DEMOSAIC_DUAL | DT_IOP_DEMOSAIC_MARKESTEIJN_3, // $DESCRIPTION: "Markesteijn 3-pass + VNG"
130 DT_IOP_DEMOSAIC_PASSTHR_MONOX = DEMOSAIC_XTRANS | 3, // $DESCRIPTION: "passthrough (monochrome)"
131 DT_IOP_DEMOSAIC_PASSTHR_COLORX = DEMOSAIC_XTRANS | 5, // $DESCRIPTION: "photosite color (debug)"
132 DT_IOP_DEMOSAIC_DOWNSAMPLE = 7, // $DESCRIPTION: "downsample"
134
136{
137 DT_IOP_GREEN_EQ_NO = 0, // $DESCRIPTION: "disabled"
138 DT_IOP_GREEN_EQ_LOCAL = 1, // $DESCRIPTION: "local average"
139 DT_IOP_GREEN_EQ_FULL = 2, // $DESCRIPTION: "full average"
140 DT_IOP_GREEN_EQ_BOTH = 3 // $DESCRIPTION: "full and local average"
142
143
145{
146 DEMOSAIC_SMOOTH_OFF = 0, // $DESCRIPTION: "disabled"
147 DEMOSAIC_SMOOTH_1 = 1, // $DESCRIPTION: "once"
148 DEMOSAIC_SMOOTH_2 = 2, // $DESCRIPTION: "twice"
149 DEMOSAIC_SMOOTH_3 = 3, // $DESCRIPTION: "three times"
150 DEMOSAIC_SMOOTH_4 = 4, // $DESCRIPTION: "four times"
151 DEMOSAIC_SMOOTH_5 = 5, // $DESCRIPTION: "five times"
153
155{
156 LMMSE_REFINE_0 = 0, // $DESCRIPTION: "basic"
157 LMMSE_REFINE_1 = 1, // $DESCRIPTION: "median"
158 LMMSE_REFINE_2 = 2, // $DESCRIPTION: "3x median"
159 LMMSE_REFINE_3 = 3, // $DESCRIPTION: "refine & medians"
160 LMMSE_REFINE_4 = 4, // $DESCRIPTION: "2x refine + medians"
162
164{
165 // demosaic pattern
227
228
239
240
241static inline __attribute__((always_inline)) float intp(float a, float b, float c)
242{ // taken from rt code
243 // calculate a * b + (1 - a) * c
244 // following is valid:
245 // intp(a, b+x, c+x) = intp(a, b, c) + x
246 // intp(a, b*x, c*x) = intp(a, b, c) * x
247 return a * (b - c) + c;
248}
249
256
257
259{
260 dt_iop_demosaic_greeneq_t green_eq; // $DEFAULT: DT_IOP_GREEN_EQ_NO $DESCRIPTION: "match greens"
261 float median_thrs; // $MIN: 0.0 $MAX: 1.0 $DEFAULT: 0.0 $DESCRIPTION: "edge threshold"
262 dt_iop_demosaic_smooth_t color_smoothing; // $DEFAULT: DEMOSAIC_SMOOTH_OFF $DESCRIPTION: "color smoothing"
263 dt_iop_demosaic_method_t demosaicing_method; // $DEFAULT: DT_IOP_DEMOSAIC_RCD $DESCRIPTION: "demosaicing method"
264 dt_iop_demosaic_lmmse_t lmmse_refine; // $DEFAULT: LMMSE_REFINE_1 $DESCRIPTION: "LMMSE refine"
265 float dual_thrs; // $MIN: 0.0 $MAX: 1.0 $DEFAULT: 0.20 $DESCRIPTION: "dual threshold"
267
279
280// Implemented on amaze_demosaic_RT.cc
282 const dt_dev_pixelpipe_iop_t *piece,
283 const float *const in,
284 float *out,
285 const dt_iop_roi_t *const roi_in,
286 const dt_iop_roi_t *const roi_out,
287 const uint32_t filters);
288
289
290// Mind the order of includes, there are internal dependencies
291// FIXME: handle all the branching uniformingly
292#include "demosaic/basic.c"
293#include "demosaic/passthrough.c"
294#include "demosaic/rcd.c"
295#include "demosaic/lmmse.c"
296#include "demosaic/ppg.c"
297#include "demosaic/vng.c"
298#include "demosaic/markesteijn.c"
299#include "demosaic/dual.c"
300
301
302const char *name()
303{
304 return _("demosaic");
305}
306
307const char **description(struct dt_iop_module_t *self)
308{
309 return dt_iop_set_description(self, _("reconstruct full RGB pixels from a sensor color filter array reading"),
310 _("mandatory"),
311 _("linear, raw, scene-referred"),
312 _("linear, raw"),
313 _("linear, RGB, scene-referred"));
314}
315
317{
318 return IOP_GROUP_TECHNICAL;
319}
320
325
327{
328 return IOP_CS_RAW;
329}
330
331int legacy_params(dt_iop_module_t *self, const void *const old_params, const int old_version,
332 void *new_params, const int new_version)
333{
334 typedef struct dt_iop_demosaic_params_t dt_iop_demosaic_params_v4_t;
335 typedef struct dt_iop_demosaic_params_v3_t
336 {
338 float median_thrs;
339 uint32_t color_smoothing;
340 dt_iop_demosaic_method_t demosaicing_method;
341 dt_iop_demosaic_lmmse_t lmmse_refine;
342 } dt_iop_demosaic_params_v3_t;
343
344 if(old_version == 3 && new_version == 4)
345 {
346 dt_iop_demosaic_params_v3_t *o = (dt_iop_demosaic_params_v3_t *)old_params;
347 dt_iop_demosaic_params_v4_t *n = (dt_iop_demosaic_params_v4_t *)new_params;
348 memcpy(n, o, sizeof *o);
349 n->dual_thrs = 0.20f;
350 return 0;
351 }
352
353 if(old_version == 2 && new_version == 3)
354 {
357 n->green_eq = o->green_eq;
358 n->median_thrs = o->median_thrs;
359 n->color_smoothing = 0;
360 n->demosaicing_method = DT_IOP_DEMOSAIC_PPG;
361 n->lmmse_refine = LMMSE_REFINE_1;
362 return 0;
363 }
364 return 1;
365}
366
369{
370 default_input_format(self, pipe, piece, dsc);
371 dsc->channels = 1;
373}
374
377{
378 dsc->channels = 4;
379 dsc->datatype = TYPE_FLOAT;
380 dsc->cst = IOP_CS_RGB;
381}
382
383static inline __attribute__((always_inline)) const char* method2string(dt_iop_demosaic_method_t method)
384{
385 const char *string;
386
387 switch(method)
388 {
390 string = "PPG";
391 break;
393 string = "AMaZE";
394 break;
396 string = "VNG4";
397 break;
399 string = "passthrough monochrome";
400 break;
402 string = "photosites";
403 break;
405 string = "RCD";
406 break;
408 string = "LMMSE";
409 break;
411 string = "RCD + VNG4";
412 break;
414 string = "AMaZE + VNG4";
415 break;
417 string = "VNG (xtrans)";
418 break;
420 string = "Markesteijn-1 (XTrans)";
421 break;
423 string = "Markesteijn-3 (XTrans)";
424 break;
426 string = "Markesteijn 3-pass + VNG";
427 break;
429 string = "Frequency Domain Chroma (XTrans)";
430 break;
432 string = "passthrough monochrome (XTrans)";
433 break;
435 string = "photosites (XTrans)";
436 break;
438 string = "downsample";
439 break;
440 default:
441 string = "(unknown method)";
442 }
443 return string;
444}
445
446static inline gboolean _is_downsample_method(const dt_iop_demosaic_method_t method)
447{
448 return method == DT_IOP_DEMOSAIC_DOWNSAMPLE;
449}
450
451static inline __attribute__((always_inline)) const char *_pipe_type_string(dt_dev_pixelpipe_type_t type)
452{
453 switch(type)
454 {
455 case DT_DEV_PIXELPIPE_EXPORT: return "export";
456 case DT_DEV_PIXELPIPE_FULL: return "full";
457 case DT_DEV_PIXELPIPE_PREVIEW: return "preview";
458 case DT_DEV_PIXELPIPE_THUMBNAIL: return "thumbnail";
459 default: return "none";
460 }
461}
462
472static void _downsample_bayer_half_size(float *const out, const float *const in,
473 const dt_iop_roi_t *const roi_out,
474 const dt_iop_roi_t *const roi_in, const uint32_t filters,
475 const gboolean is_4bayer, const double CAM_to_RGB[3][4])
476{
477 __OMP_PARALLEL_FOR__(collapse(2))
478 for(int y = 0; y < roi_out->height; y++)
479 {
480 for(int x = 0; x < roi_out->width; x++)
481 {
482 float *const outc = out + 4 * ((size_t)y * roi_out->width + x);
483 dt_aligned_pixel_t cam = { 0.0f };
484 int samples[4] = { 0 };
485 const int px = MIN(2 * x, roi_in->width - 1);
486 const int py = MIN(2 * y, roi_in->height - 1);
487
488 // Collect the 2x2 source block feeding this output pixel and average only
489 // same-colour photosites together so we do not invent new chroma detail.
490 for(int j = 0; j < 2; j++)
491 {
492 for(int i = 0; i < 2; i++)
493 {
494 const int xx = MIN(px + i, roi_in->width - 1);
495 const int yy = MIN(py + j, roi_in->height - 1);
496 const int c = FC(yy, xx, filters);
497 cam[c] += in[(size_t)yy * roi_in->width + xx];
498 samples[c]++;
499 }
500 }
501
502 for(int c = 0; c < 4; c++)
503 if(samples[c] > 0) cam[c] /= (float)samples[c];
504
505 if(is_4bayer)
506 {
507 for(int c = 0; c < 3; c++)
508 {
509 outc[c] = 0.0f;
510 for(int k = 0; k < 4; k++) outc[c] += CAM_to_RGB[c][k] * cam[k];
511 }
512 }
513 else
514 {
515 outc[0] = cam[RED];
516 outc[1] = cam[GREEN];
517 outc[2] = cam[BLUE];
518 }
519
520 outc[3] = 0.0f;
521 }
522 }
523}
524
534static float _downsample_xtrans_missing_colour(const float *const in, const dt_iop_roi_t *const roi_in,
535 const int px, const int py,
536 const uint8_t (*const xtrans)[6], const int colour)
537{
538 const float cx = px + 0.5f;
539 const float cy = py + 0.5f;
540 const int xmin = MAX(0, px - 3);
541 const int xmax = MIN(roi_in->width - 1, px + 4);
542 const int ymin = MAX(0, py - 3);
543 const int ymax = MIN(roi_in->height - 1, py + 4);
544
545 float quadrant_value[4] = { 0.0f };
546 float quadrant_dist[4] = { INFINITY, INFINITY, INFINITY, INFINITY };
547 int quadrant_x[4] = { 0 };
548 int quadrant_y[4] = { 0 };
549 gboolean quadrant_valid[4] = { FALSE, FALSE, FALSE, FALSE };
550
551 float nearest_value = 0.0f;
552 float nearest_dist = INFINITY;
553
554 // Search the local 7x7 neighbourhood because X-Trans can place the next sample
555 // of a given colour two pixels away from the 2x2 block feeding the output.
556 for(int yy = ymin; yy <= ymax; yy++)
557 {
558 for(int xx = xmin; xx <= xmax; xx++)
559 {
560 if(FCxtrans(yy, xx, roi_in, xtrans) != colour) continue;
561
562 const float dx = xx - cx;
563 const float dy = yy - cy;
564 const float dist2 = dx * dx + dy * dy;
565 if(dist2 < nearest_dist)
566 {
567 nearest_dist = dist2;
568 nearest_value = in[(size_t)yy * roi_in->width + xx];
569 }
570
571 const int quadrant = ((yy > cy) ? 2 : 0) + ((xx > cx) ? 1 : 0);
572 if(dist2 < quadrant_dist[quadrant])
573 {
574 quadrant_dist[quadrant] = dist2;
575 quadrant_value[quadrant] = in[(size_t)yy * roi_in->width + xx];
576 quadrant_x[quadrant] = xx;
577 quadrant_y[quadrant] = yy;
578 quadrant_valid[quadrant] = TRUE;
579 }
580 }
581 }
582
583 if(quadrant_valid[0] && quadrant_valid[1] && quadrant_valid[2] && quadrant_valid[3])
584 {
585 const float x_left = 0.5f * (quadrant_x[0] + quadrant_x[2]);
586 const float x_right = 0.5f * (quadrant_x[1] + quadrant_x[3]);
587 const float y_top = 0.5f * (quadrant_y[0] + quadrant_y[1]);
588 const float y_bottom = 0.5f * (quadrant_y[2] + quadrant_y[3]);
589 const float tx = CLAMP((cx - x_left) / MAX(x_right - x_left, 1e-6f), 0.0f, 1.0f);
590 const float ty = CLAMP((cy - y_top) / MAX(y_bottom - y_top, 1e-6f), 0.0f, 1.0f);
591 const float top = quadrant_value[0] + tx * (quadrant_value[1] - quadrant_value[0]);
592 const float bottom = quadrant_value[2] + tx * (quadrant_value[3] - quadrant_value[2]);
593 return top + ty * (bottom - top);
594 }
595
596 float sum = 0.0f;
597 int count = 0;
598 for(int q = 0; q < 4; q++)
599 {
600 if(!quadrant_valid[q]) continue;
601 sum += quadrant_value[q];
602 count++;
603 }
604
605 return (count > 0) ? sum / (float)count : nearest_value;
606}
607
616static void _downsample_xtrans_half_size(float *const out, const float *const in,
617 const dt_iop_roi_t *const roi_out,
618 const dt_iop_roi_t *const roi_in,
619 const uint8_t (*const xtrans)[6])
620{
621 __OMP_PARALLEL_FOR__(collapse(2))
622 for(int y = 0; y < roi_out->height; y++)
623 {
624 for(int x = 0; x < roi_out->width; x++)
625 {
626 float *const outc = out + 4 * ((size_t)y * roi_out->width + x);
627 dt_aligned_pixel_t rgb = { 0.0f };
628 int samples[3] = { 0 };
629 const int px = MIN(2 * x, roi_in->width - 1);
630 const int py = MIN(2 * y, roi_in->height - 1);
631
632 // Reuse the real X-Trans photosites that already fall inside the source 2x2 block
633 // and only interpolate the colours that the local pattern does not sample there.
634 for(int j = 0; j < 2; j++)
635 {
636 for(int i = 0; i < 2; i++)
637 {
638 const int xx = MIN(px + i, roi_in->width - 1);
639 const int yy = MIN(py + j, roi_in->height - 1);
640 const int c = FCxtrans(yy, xx, roi_in, xtrans);
641 rgb[c] += in[(size_t)yy * roi_in->width + xx];
642 samples[c]++;
643 }
644 }
645
646 for(int c = 0; c < 3; c++)
647 {
648 if(samples[c] > 0)
649 outc[c] = rgb[c] / (float)samples[c];
650 else
651 outc[c] = _downsample_xtrans_missing_colour(in, roi_in, px, py, xtrans, c);
652 }
653
654 outc[3] = 0.0f;
655 }
656 }
657}
658
672static void _downsample_guided_laplacian_fit(const float *const restrict HF,
673 float *const restrict coeff,
674 float *const restrict bias,
675 const size_t width, const size_t height)
676{
677 const float eps = 1e-12f;
678 const dt_aligned_pixel_simd_t zero = dt_simd_set1(0.f);
679 const dt_aligned_pixel_simd_t inv_patch = dt_simd_set1(1.f / 25.f);
681 for(size_t row = 0; row < height; ++row)
682 {
683 const float *const row0 = HF + 4 * ((size_t)CLAMP((int)row - 2, 0, (int)height - 1) * width);
684 const float *const row1 = HF + 4 * ((size_t)CLAMP((int)row - 1, 0, (int)height - 1) * width);
685 const float *const row2 = HF + 4 * (row * width);
686 const float *const row3 = HF + 4 * ((size_t)CLAMP((int)row + 1, 0, (int)height - 1) * width);
687 const float *const row4 = HF + 4 * ((size_t)CLAMP((int)row + 2, 0, (int)height - 1) * width);
688 const float *const rows[BSPLINE_FSIZE] = { row0, row1, row2, row3, row4 };
689 const int max_col = (int)width - 1;
690
691 for(size_t col = 0; col < width; ++col)
692 {
693 dt_aligned_pixel_simd_t sum_rgb = zero;
694 dt_aligned_pixel_simd_t sum_rgb_guide = zero;
695 float sum_guide = 0.f;
696 float sum_guide_sq = 0.f;
697 const int col_offsets[BSPLINE_FSIZE]
698 = { 4 * CLAMP((int)col - 2, 0, max_col),
699 4 * CLAMP((int)col - 1, 0, max_col),
700 4 * (int)col,
701 4 * CLAMP((int)col + 1, 0, max_col),
702 4 * CLAMP((int)col + 2, 0, max_col) };
703
704 // Walk the dense 5x5 neighbourhood once. The 5 clamped column offsets are
705 // hoisted once per output pixel so the inner loop keeps only loads and moment
706 // accumulation for the RGB channels and their shared guide. Keep those loops
707 // as counted loops so GCC does not fully unroll all 25 taps and spill the
708 // intermediate guide terms to the stack.
709#if defined(__GNUC__) && !defined(__clang__)
710#pragma GCC unroll 1
711#endif
712 for(int jj = 0; jj < BSPLINE_FSIZE; ++jj)
713 {
714 const float *const row_ptr = rows[jj];
715#if defined(__GNUC__) && !defined(__clang__)
716#pragma GCC unroll 1
717#endif
718 for(int ii = 0; ii < BSPLINE_FSIZE; ++ii)
719 {
720 const dt_aligned_pixel_simd_t sample = dt_load_simd_aligned(row_ptr + col_offsets[ii]);
721 const float guide = (sample[RED] + sample[GREEN] + sample[BLUE]) / 3.f;
722
723 sum_rgb += sample;
724 sum_guide += guide;
725 sum_guide_sq += guide * guide;
726 sum_rgb_guide += sample * dt_simd_set1(guide);
727 }
728 }
729
730 dt_aligned_pixel_simd_t means = sum_rgb * inv_patch;
731 const float guide_mean = sum_guide * (1.f / 25.f);
732 float variance = sum_guide_sq * (1.f / 25.f) - sqf(guide_mean);
733 dt_aligned_pixel_simd_t covariance = sum_rgb_guide * inv_patch - means * dt_simd_set1(guide_mean);
734 means[ALPHA] = 0.f;
735 covariance[ALPHA] = 0.f;
736
737 if(variance < 0.f) variance = 0.f;
738
739 dt_aligned_pixel_simd_t slope = zero;
740 if(variance > eps) slope = covariance / dt_simd_set1(variance);
741 slope[ALPHA] = 0.f;
742
743 dt_aligned_pixel_simd_t intercept = means - slope * dt_simd_set1(guide_mean);
744 intercept[ALPHA] = 0.f;
745
746 dt_store_simd_aligned(coeff + 4 * (row * width + col), slope);
747 dt_store_simd_aligned(bias + 4 * (row * width + col), intercept);
748 }
749 }
750}
751
761static void _downsample_guided_laplacian_apply(const float *const restrict HF,
762 const float *const restrict coeff,
763 const float *const restrict bias,
764 const float *const restrict LF,
765 float *const restrict reconstructed,
766 const size_t width, const size_t height,
767 const gboolean reset)
768{
770 for(size_t row = 0; row < height; ++row)
771 {
772 for(size_t col = 0; col < width; ++col)
773 {
774 const size_t index = 4 * (row * width + col);
775 const dt_aligned_pixel_simd_t hf = dt_load_simd_aligned(HF + index);
776 const dt_aligned_pixel_simd_t guide = dt_simd_set1((hf[RED] + hf[GREEN] + hf[BLUE]) / 3.f);
777 dt_aligned_pixel_simd_t filtered = (dt_load_simd_aligned(coeff + index) * guide
778 + dt_load_simd_aligned(bias + index))
779 * dt_load_simd_aligned(LF + index);
780
781 if(!reset) filtered += dt_load_simd_aligned(reconstructed + index);
782 filtered[ALPHA] = 0.f;
783 dt_store_simd_aligned(reconstructed + index, filtered);
784 }
785 }
786}
787
802 const size_t width, const size_t height,
803 const int iterations)
804{
805 if(iterations <= 0) return 0;
806
807 const size_t pixels = width * height;
808 float *const restrict LF_even = dt_pixelpipe_cache_alloc_align_float_cache(4 * pixels, 0);
809 float *const restrict LF_odd = dt_pixelpipe_cache_alloc_align_float_cache(4 * pixels, 0);
810 float *const restrict HF = dt_pixelpipe_cache_alloc_align_float_cache(4 * pixels, 0);
811 float *const restrict reconstructed = dt_pixelpipe_cache_alloc_align_float_cache(4 * pixels, 0);
812 float *const restrict coeff = dt_pixelpipe_cache_alloc_align_float_cache(4 * pixels, 0);
813 float *const restrict bias = dt_pixelpipe_cache_alloc_align_float_cache(4 * pixels, 0);
814 float *const restrict coeff_tmp = dt_pixelpipe_cache_alloc_align_float_cache(4 * pixels, 0);
815 size_t padded_size;
816 float *const restrict tempbuf = dt_pixelpipe_cache_alloc_perthread_float(4 * width, &padded_size);
817 const float *restrict residual = out;
818 int err = 0;
819
820 if(IS_NULL_PTR(LF_even) || IS_NULL_PTR(LF_odd) || IS_NULL_PTR(HF) || IS_NULL_PTR(reconstructed) || IS_NULL_PTR(coeff) || IS_NULL_PTR(bias) || IS_NULL_PTR(coeff_tmp) || IS_NULL_PTR(tempbuf))
821 {
822 err = 1;
823 goto cleanup;
824 }
825
826 for(int iteration = 0; iteration < iterations; ++iteration)
827 {
828 residual = out;
829
830 for(int s = 0; s < DOWNSAMPLE_GUIDED_SCALES; ++s)
831 {
832 const int mult = 1 << s;
833 const float *restrict buffer_in;
834 float *restrict buffer_out;
835
836 if(s == 0)
837 {
838 buffer_in = out;
839 buffer_out = LF_odd;
840 }
841 else if(s % 2 != 0)
842 {
843 buffer_in = LF_odd;
844 buffer_out = LF_even;
845 }
846 else
847 {
848 buffer_in = LF_even;
849 buffer_out = LF_odd;
850 }
851
852 decompose_2D_Bspline(buffer_in, HF, buffer_out, width, height, mult, tempbuf, padded_size);
853 // Express the current wavelet band as relative detail over the blur that created it
854 // so the linear RGB model follows local chroma ratios instead of absolute amplitudes.
856 for(size_t row = 0; row < height; ++row)
857 {
858 for(size_t col = 0; col < width; ++col)
859 {
860 const size_t index = 4 * (row * width + col);
861 dt_aligned_pixel_simd_t lf = dt_load_simd_aligned(buffer_out + index);
862 lf[RED] = fmaxf(lf[RED], 1e-8f);
863 lf[GREEN] = fmaxf(lf[GREEN], 1e-8f);
864 lf[BLUE] = fmaxf(lf[BLUE], 1e-8f);
865 lf[ALPHA] = 1.f;
866
867 dt_aligned_pixel_simd_t normalized = dt_load_simd_aligned(HF + index) / lf;
868 normalized[ALPHA] = 0.f;
869 dt_store_simd_aligned(HF + index, normalized);
870 }
871 }
872
874
875 blur_2D_Bspline(coeff, coeff_tmp, tempbuf, width, height, 1, FALSE);
877 blur_2D_Bspline(bias, coeff_tmp, tempbuf, width, height, 1, FALSE);
878 dt_iop_image_copy_by_size(bias, coeff_tmp, width, height, 4);
879
880 _downsample_guided_laplacian_apply(HF, coeff, bias, buffer_out, reconstructed,
881 width, height, s == 0);
882 residual = buffer_out;
883 }
884
885 const gboolean last_iteration = (iteration == iterations - 1);
887 for(size_t row = 0; row < height; ++row)
888 {
889 for(size_t col = 0; col < width; ++col)
890 {
891 const size_t index = 4 * (row * width + col);
892 dt_aligned_pixel_simd_t pixel
893 = dt_simd_max_zero(dt_load_simd_aligned(reconstructed + index)
894 + dt_load_simd_aligned(residual + index));
895 pixel[ALPHA] = 0.f;
896
897 if(last_iteration)
898 dt_store_simd_nontemporal(out + index, pixel);
899 else
900 dt_store_simd_aligned(out + index, pixel);
901 }
902 }
903 }
904 dt_omploop_sfence(); // ensure the final nontemporal writeback completes before the caller reads out
905
906cleanup:
911 dt_pixelpipe_cache_free_align(reconstructed);
915 return err;
916}
917
918
919void distort_mask(struct dt_iop_module_t *self, const struct dt_dev_pixelpipe_t *pipe, struct dt_dev_pixelpipe_iop_t *piece,
920 const float *const in, float *const out, const dt_iop_roi_t *const roi_in,
921 const dt_iop_roi_t *const roi_out)
922{
923 (void)pipe;
925 dt_interpolation_resample_roi_1c(itor, out, roi_out, in, roi_in);
926}
927
928void modify_roi_out(struct dt_iop_module_t *self, const struct dt_dev_pixelpipe_t *pipe,
929 struct dt_dev_pixelpipe_iop_t *piece, dt_iop_roi_t *roi_out,
930 const dt_iop_roi_t *const roi_in)
931{
932 *roi_out = *roi_in;
933
936 {
937 roi_out->width = (roi_in->width + 1) / 2;
938 roi_out->height = (roi_in->height + 1) / 2;
939 }
940
941 // snap to start of mosaic block:
942 roi_out->x = 0; // MAX(0, roi_out->x & ~1);
943 roi_out->y = 0; // MAX(0, roi_out->y & ~1);
944}
945
946// which roi input is needed to process to this output?
947// roi_out is unchanged, full buffer in is full buffer out.
948// see ../../doc/resizing-scaling.md for details
949void modify_roi_in(struct dt_iop_module_t *self, const struct dt_dev_pixelpipe_t *pipe,
950 struct dt_dev_pixelpipe_iop_t *piece,
951 const dt_iop_roi_t *roi_out, dt_iop_roi_t *roi_in)
952{
953 // this op is disabled for filters == 0
954 *roi_in = *roi_out;
955
957 const int method = data->demosaicing_method;
958 const gboolean passthrough = (method == DT_IOP_DEMOSAIC_PASSTHROUGH_MONOCHROME) ||
960 const gboolean downsample = _is_downsample_method(method);
961
962 if(downsample)
963 {
964 roi_in->x *= 2;
965 roi_in->y *= 2;
966 roi_in->width *= 2;
967 roi_in->height *= 2;
968
969 // Half-size mode maps each output pixel to one 2x2 raw block, so keep the exact
970 // 2x addressing and clamp only the tail block against the available input buffer.
971 roi_in->x = CLAMP(roi_in->x, 0, MAX(0, piece->buf_in.width - 1));
972 roi_in->y = CLAMP(roi_in->y, 0, MAX(0, piece->buf_in.height - 1));
973 roi_in->width = CLAMP(roi_in->width, 1, piece->buf_in.width - roi_in->x);
974 roi_in->height = CLAMP(roi_in->height, 1, piece->buf_in.height - roi_in->y);
975 return;
976 }
977
978 // No snapping needed: process() rebuilds the exact CFA/xtrans phase for the
979 // actual roi_in->x/y on every call (see the top of process()), so demosaic can
980 // request any pixel-precise crop position from upstream instead of rounding to
981 // the nearest sensor-pattern-aligned one.
982 (void)passthrough;
983}
984
985
987int process(struct dt_iop_module_t *self, const dt_dev_pixelpipe_t *pipe, const dt_dev_pixelpipe_iop_t *piece,
988 const void *const i, void *const o)
989{
990 const dt_iop_roi_t *const roi_in = &piece->roi_in;
991 const dt_iop_roi_t *const roi_out = &piece->roi_out;
992 const dt_image_t *img = &self->dev->image_storage;
993 const float threshold = 0.0001f * img->exif_iso;
994 dt_times_t start_time = { 0 }, end_time = { 0 };
995
996 dt_iop_roi_t roi = *roi_in;
997 dt_iop_roi_t roo = *roi_out;
998 roo.x = roo.y = 0;
999 // roi_out->scale = global scale: (iscale == 1.0, always when demosaic is on)
1000 const gboolean info = ((darktable.unmuted & (DT_DEBUG_DEMOSAIC | DT_DEBUG_PERF))
1001 && (pipe->type == DT_DEV_PIXELPIPE_FULL));
1002
1003 // piece->dsc_in.filters/xtrans only carries the fixed sensor-border-trim phase shift
1004 // (applied once by rawprepare, which owns that constant, and never rewritten here —
1005 // process() must treat piece->dsc_in as immutable). The dynamic, ROI-dependent part of
1006 // the shift (panning/zooming/cropping downstream) is NOT baked into a table here: most
1007 // algorithms below (markesteijn, passthrough, VNG, the xtrans downsample path,
1008 // green-equilibration, dual demosaic) already add roi_in->x/y themselves at their own
1009 // call sites, working directly off the unshifted piece->dsc_in.xtrans/filters. Only the
1010 // handful of algorithms that work in tile-local coordinates with no roi awareness at all
1011 // (RCD, LMMSE, PPG, AMaZE, the bayer downsample path) need the dynamic offset folded into
1012 // `filters` up front — passing a locally pre-shifted table to the roi-aware algorithms
1013 // instead would double their own correction on top of it.
1014 const uint8_t(*const xtrans_raw)[6] = (const uint8_t(*const)[6])piece->dsc_in.xtrans;
1015 const uint32_t filters = dt_dev_get_roi_filters(piece, roi_in);
1016
1019
1020 int demosaicing_method = data->demosaicing_method;
1021
1022 gboolean showmask = FALSE;
1023 if(self->dev->gui_attached && pipe->type == DT_DEV_PIXELPIPE_FULL)
1024 {
1026 if(g) showmask = (g->visual_mask);
1027 // take care of passthru modes
1029 demosaicing_method = (filters != 9u) ? DT_IOP_DEMOSAIC_RCD : DT_IOP_DEMOSAIC_MARKESTEIJN;
1031 demosaicing_method = DT_IOP_DEMOSAIC_PASSTHROUGH_MONOCHROME;
1032 }
1033
1035 "[demosaic] CPU pipe %p (%s) thread %lu piece %p roi_in=(%d,%d) %dx%d dsc_in.filters=0x%x -> filters=0x%x xtrans_raw=%p method=%s\n",
1036 (void *)pipe, _pipe_type_string(pipe->type), (unsigned long)pthread_self(), (void *)piece,
1037 roi_in->x, roi_in->y, roi_in->width, roi_in->height,
1038 piece->dsc_in.filters, filters, (void *)xtrans_raw, method2string(demosaicing_method));
1039
1040 const float *const pixels = (float *)i;
1041
1042 // Full demosaic and then scaling if needed
1043 if(info) dt_get_times(&start_time);
1044
1045 if(_is_downsample_method(demosaicing_method))
1046 {
1047 if(filters == 9u)
1048 _downsample_xtrans_half_size(o, pixels, &roo, &roi, xtrans_raw);
1049 else
1050 _downsample_bayer_half_size(o, pixels, &roo, &roi, filters,
1051 img->flags & DT_IMAGE_4BAYER, data->CAM_to_RGB);
1052
1054 }
1055 else if(demosaicing_method == DT_IOP_DEMOSAIC_PASSTHROUGH_MONOCHROME)
1056 {
1057 passthrough_monochrome(o, pixels, &roo, &roi);
1058 }
1059 else if(demosaicing_method == DT_IOP_DEMOSAIC_PASSTHROUGH_COLOR)
1060 {
1061 passthrough_color(o, pixels, &roo, &roi, piece->dsc_in.filters, xtrans_raw);
1062 }
1063 else if(filters == 9u)
1064 {
1065 const int passes = (demosaicing_method == DT_IOP_DEMOSAIC_MARKESTEIJN) ? 1 : 3;
1066 if(demosaicing_method == DT_IOP_DEMOSAIC_MARKEST3_VNG)
1067 xtrans_markesteijn_interpolate(o, pixels, &roo, &roi, xtrans_raw, passes);
1068 else if(demosaicing_method == DT_IOP_DEMOSAIC_FDC)
1069 xtrans_fdc_interpolate(self, o, pixels, &roo, &roi, xtrans_raw);
1070 else if(demosaicing_method >= DT_IOP_DEMOSAIC_MARKESTEIJN)
1071 xtrans_markesteijn_interpolate(o, pixels, &roo, &roi, xtrans_raw, passes);
1072 else
1073 if(vng_interpolate(o, pixels, &roo, &roi, piece->dsc_in.filters, xtrans_raw, FALSE))
1074 return 1;
1075 }
1076 else
1077 {
1078 float *in = (float *)pixels;
1079 float *aux;
1080
1081 if(!(img->flags & DT_IMAGE_4BAYER) && data->green_eq != DT_IOP_GREEN_EQ_NO)
1082 {
1083 in = dt_pixelpipe_cache_alloc_align_float((size_t)roi_in->height * roi_in->width, pipe);
1084 if(!IS_NULL_PTR(in))
1085 {
1086 switch(data->green_eq)
1087 {
1089 green_equilibration_favg(in, pixels, roi_in->width, roi_in->height, piece->dsc_in.filters,
1090 roi_in->x, roi_in->y);
1091 break;
1093 green_equilibration_lavg(in, pixels, roi_in->width, roi_in->height, piece->dsc_in.filters,
1094 roi_in->x, roi_in->y, threshold);
1095 break;
1097 aux = dt_pixelpipe_cache_alloc_align_float((size_t)roi_in->height * roi_in->width, pipe);
1098 if(IS_NULL_PTR(aux))
1099 {
1101 return 1;
1102 }
1103 green_equilibration_favg(aux, pixels, roi_in->width, roi_in->height, piece->dsc_in.filters,
1104 roi_in->x, roi_in->y);
1105 green_equilibration_lavg(in, aux, roi_in->width, roi_in->height, piece->dsc_in.filters, roi_in->x,
1106 roi_in->y, threshold);
1108 break;
1109 }
1110 }
1111 else
1112 {
1113 return 1;
1114 }
1115 }
1116
1117 if(demosaicing_method == DT_IOP_DEMOSAIC_VNG4 || (img->flags & DT_IMAGE_4BAYER))
1118 {
1119 if(vng_interpolate(o, in, &roo, &roi, piece->dsc_in.filters, xtrans_raw, FALSE))
1120 return 1;
1121 if(img->flags & DT_IMAGE_4BAYER)
1122 {
1124 dt_aligned_pixel_t processed_maximum = { piece->dsc_in.processed_maximum[0], piece->dsc_in.processed_maximum[1],
1125 piece->dsc_in.processed_maximum[2], 0.0f };
1126 dt_colorspaces_cygm_to_rgb(processed_maximum, 1, data->CAM_to_RGB);
1127 }
1128 }
1129 else if((demosaicing_method & ~DEMOSAIC_DUAL) == DT_IOP_DEMOSAIC_RCD)
1130 {
1131 rcd_demosaic(piece, o, in, &roo, &roi, filters);
1132 }
1133 else if(demosaicing_method == DT_IOP_DEMOSAIC_LMMSE)
1134 {
1136 {
1140 {
1143 gd->lmmse_gamma_in = NULL;
1144 gd->lmmse_gamma_out = NULL;
1145 if(!(img->flags & DT_IMAGE_4BAYER) && data->green_eq != DT_IOP_GREEN_EQ_NO)
1147 return 1;
1148 }
1149#ifdef _OPENMP
1150 #pragma omp for
1151#endif
1152 for(int j = 0; j < 65536; j++)
1153 {
1154 const double x = (double)j / 65535.0;
1155 gd->lmmse_gamma_in[j] = (x <= 0.001867) ? x * 17.0 : 1.044445 * exp(log(x) / 2.4) - 0.044445;
1156 gd->lmmse_gamma_out[j] = (x <= 0.031746) ? x / 17.0 : exp(log((x + 0.044445) / 1.044445) * 2.4);
1157 }
1158 }
1159 lmmse_demosaic(piece, o, in, &roo, &roi, filters, data->lmmse_refine, gd->lmmse_gamma_in, gd->lmmse_gamma_out);
1160 }
1161 else if((demosaicing_method & ~DEMOSAIC_DUAL) != DT_IOP_DEMOSAIC_AMAZE)
1162 {
1163 if(demosaic_ppg(o, in, &roo, &roi, filters, data->median_thrs))
1164 {
1165 if(!(img->flags & DT_IMAGE_4BAYER) && data->green_eq != DT_IOP_GREEN_EQ_NO)
1167 return 1;
1168 }
1169 } // wanted ppg or zoomed out a lot and quality is limited to 1
1170 else
1171 amaze_demosaic_RT(piece, in, o, &roi, &roo, filters);
1172
1173 if(!(img->flags & DT_IMAGE_4BAYER) && data->green_eq != DT_IOP_GREEN_EQ_NO)
1175 }
1176
1177 if(info)
1178 {
1179 const float mpixels = (roo.width * roo.height) / 1.0e6;
1180 dt_get_times(&end_time);
1181 const float tclock = end_time.clock - start_time.clock;
1182 const float uclock = end_time.user - start_time.user;
1183 fprintf(stderr," [demosaic] process CPU `%s' did %.2fmpix, %.4f secs (%.4f CPU), %.2f pix/us\n",
1184 method2string(demosaicing_method & ~DEMOSAIC_DUAL), mpixels, tclock, uclock, mpixels / tclock);
1185 }
1186
1187 if((demosaicing_method & DEMOSAIC_DUAL))
1188 {
1189 if(dual_demosaic(pipe, piece, o, pixels, &roo, &roi, piece->dsc_in.filters, xtrans_raw, showmask, data->dual_thrs))
1190 return 1;
1191 }
1192
1193 if(data->color_smoothing && !_is_downsample_method(demosaicing_method))
1194 color_smoothing(o, roi_out, data->color_smoothing);
1195
1196 return 0;
1197}
1198
1199#ifdef HAVE_OPENCL
1200static int process_default_cl(struct dt_iop_module_t *self, const dt_dev_pixelpipe_t *pipe,
1201 const dt_dev_pixelpipe_iop_t *piece, cl_mem dev_in,
1202 cl_mem dev_out, const dt_iop_roi_t *const roi_in,
1203 const dt_iop_roi_t *const roi_out, const int demosaicing_method)
1204{
1207
1208 const int devid = pipe->devid;
1209
1210 cl_mem dev_aux = NULL;
1211 cl_mem dev_tmp = NULL;
1212 cl_mem dev_med = NULL;
1213 cl_mem dev_green_eq = NULL;
1214 cl_int err = -999;
1215
1216 int width = roi_out->width;
1217 int height = roi_out->height;
1218
1219 // The PPG kernels below work in tile-local coordinates with no roi awareness, so they
1220 // need the dynamic ROI offset folded into filters up front (see process()'s comment).
1221 const uint32_t filters = dt_dev_get_roi_filters(piece, roi_in);
1222
1223 // green equilibration
1224 if(data->green_eq != DT_IOP_GREEN_EQ_NO)
1225 {
1226 dev_green_eq = dt_opencl_alloc_device(devid, roi_in->width, roi_in->height, sizeof(float));
1227 if(IS_NULL_PTR(dev_green_eq)) goto error;
1228
1229 if(!green_equilibration_cl(self, pipe, piece, dev_in, dev_green_eq, roi_in))
1230 goto error;
1231
1232 dev_in = dev_green_eq;
1233 }
1234
1235 // need to reserve scaled auxiliary buffer or use dev_out
1236 dev_aux = dev_out;
1237
1238 if(demosaicing_method == DT_IOP_DEMOSAIC_PASSTHROUGH_MONOCHROME)
1239 {
1240 size_t sizes[3] = { ROUNDUPDWD(width, devid), ROUNDUPDHT(height, devid), 1 };
1241 dt_opencl_set_kernel_arg(devid, gd->kernel_passthrough_monochrome, 0, sizeof(cl_mem), &dev_in);
1242 dt_opencl_set_kernel_arg(devid, gd->kernel_passthrough_monochrome, 1, sizeof(cl_mem), &dev_aux);
1243 dt_opencl_set_kernel_arg(devid, gd->kernel_passthrough_monochrome, 2, sizeof(int), &width);
1246 if(err != CL_SUCCESS) goto error;
1247 }
1248 else if(demosaicing_method == DT_IOP_DEMOSAIC_PASSTHROUGH_COLOR)
1249 {
1250 size_t sizes[3] = { ROUNDUPDWD(width, devid), ROUNDUPDHT(height, devid), 1 };
1251 dt_opencl_set_kernel_arg(devid, gd->kernel_passthrough_color, 0, sizeof(cl_mem), &dev_in);
1252 dt_opencl_set_kernel_arg(devid, gd->kernel_passthrough_color, 1, sizeof(cl_mem), &dev_aux);
1253 dt_opencl_set_kernel_arg(devid, gd->kernel_passthrough_color, 2, sizeof(int), &width);
1254 dt_opencl_set_kernel_arg(devid, gd->kernel_passthrough_color, 3, sizeof(int), &height);
1255 dt_opencl_set_kernel_arg(devid, gd->kernel_passthrough_color, 4, sizeof(int), (void *)&roi_in->x);
1256 dt_opencl_set_kernel_arg(devid, gd->kernel_passthrough_color, 5, sizeof(int), (void *)&roi_in->y);
1257 dt_opencl_set_kernel_arg(devid, gd->kernel_passthrough_color, 6, sizeof(uint32_t), (void *)&piece->dsc_in.filters);
1258
1260 if(err != CL_SUCCESS) goto error;
1261 }
1262 else if(demosaicing_method == DT_IOP_DEMOSAIC_PPG)
1263 {
1264 dev_tmp = dt_opencl_alloc_device(devid, roi_in->width, roi_in->height, sizeof(float) * 4);
1265 if(IS_NULL_PTR(dev_tmp)) goto error;
1266
1267 {
1268 const int myborder = 3;
1269 // manage borders
1270 size_t sizes[3] = { ROUNDUPDWD(width, devid), ROUNDUPDHT(height, devid), 1 };
1271 dt_opencl_set_kernel_arg(devid, gd->kernel_border_interpolate, 0, sizeof(cl_mem), &dev_in);
1272 dt_opencl_set_kernel_arg(devid, gd->kernel_border_interpolate, 1, sizeof(cl_mem), &dev_tmp);
1273 dt_opencl_set_kernel_arg(devid, gd->kernel_border_interpolate, 2, sizeof(int), (void *)&width);
1274 dt_opencl_set_kernel_arg(devid, gd->kernel_border_interpolate, 3, sizeof(int), (void *)&height);
1275 dt_opencl_set_kernel_arg(devid, gd->kernel_border_interpolate, 4, sizeof(uint32_t), (void *)&filters);
1276 dt_opencl_set_kernel_arg(devid, gd->kernel_border_interpolate, 5, sizeof(int), (void *)&myborder);
1278 if(err != CL_SUCCESS) goto error;
1279 }
1280
1281 if(data->median_thrs > 0.0f)
1282 {
1283 dev_med = dt_opencl_alloc_device(devid, roi_in->width, roi_in->height, sizeof(float) * 4);
1284 if(IS_NULL_PTR(dev_med)) goto error;
1285
1287 = (dt_opencl_local_buffer_t){ .xoffset = 2*2, .xfactor = 1, .yoffset = 2*2, .yfactor = 1,
1288 .cellsize = 1 * sizeof(float), .overhead = 0,
1289 .sizex = 1 << 8, .sizey = 1 << 8 };
1290
1291 if(!dt_opencl_local_buffer_opt(devid, gd->kernel_pre_median, &locopt))
1292 goto error;
1293
1294 size_t sizes[3] = { ROUNDUP(width, locopt.sizex), ROUNDUP(height, locopt.sizey), 1 };
1295 size_t local[3] = { locopt.sizex, locopt.sizey, 1 };
1296 dt_opencl_set_kernel_arg(devid, gd->kernel_pre_median, 0, sizeof(cl_mem), &dev_in);
1297 dt_opencl_set_kernel_arg(devid, gd->kernel_pre_median, 1, sizeof(cl_mem), &dev_med);
1298 dt_opencl_set_kernel_arg(devid, gd->kernel_pre_median, 2, sizeof(int), &width);
1299 dt_opencl_set_kernel_arg(devid, gd->kernel_pre_median, 3, sizeof(int), &height);
1300 dt_opencl_set_kernel_arg(devid, gd->kernel_pre_median, 4, sizeof(uint32_t),
1301 (void *)&filters);
1302 dt_opencl_set_kernel_arg(devid, gd->kernel_pre_median, 5, sizeof(float), (void *)&data->median_thrs);
1304 sizeof(float) * (locopt.sizex + 4) * (locopt.sizey + 4), NULL);
1305 err = dt_opencl_enqueue_kernel_2d_with_local(devid, gd->kernel_pre_median, sizes, local);
1306 if(err != CL_SUCCESS) goto error;
1307 dev_in = dev_aux;
1308 }
1309 else
1310 dev_med = dev_in;
1311
1312 {
1314 = (dt_opencl_local_buffer_t){ .xoffset = 2*3, .xfactor = 1, .yoffset = 2*3, .yfactor = 1,
1315 .cellsize = sizeof(float) * 1, .overhead = 0,
1316 .sizex = 1 << 8, .sizey = 1 << 8 };
1317
1318 if(!dt_opencl_local_buffer_opt(devid, gd->kernel_ppg_green, &locopt))
1319 goto error;
1320
1321 size_t sizes[3] = { ROUNDUP(width, locopt.sizex), ROUNDUP(height, locopt.sizey), 1 };
1322 size_t local[3] = { locopt.sizex, locopt.sizey, 1 };
1323 dt_opencl_set_kernel_arg(devid, gd->kernel_ppg_green, 0, sizeof(cl_mem), &dev_med);
1324 dt_opencl_set_kernel_arg(devid, gd->kernel_ppg_green, 1, sizeof(cl_mem), &dev_tmp);
1325 dt_opencl_set_kernel_arg(devid, gd->kernel_ppg_green, 2, sizeof(int), &width);
1326 dt_opencl_set_kernel_arg(devid, gd->kernel_ppg_green, 3, sizeof(int), &height);
1327 dt_opencl_set_kernel_arg(devid, gd->kernel_ppg_green, 4, sizeof(uint32_t),
1328 (void *)&filters);
1330 sizeof(float) * (locopt.sizex + 2*3) * (locopt.sizey + 2*3), NULL);
1331
1332 err = dt_opencl_enqueue_kernel_2d_with_local(devid, gd->kernel_ppg_green, sizes, local);
1333 if(err != CL_SUCCESS) goto error;
1334 }
1335
1336 {
1338 = (dt_opencl_local_buffer_t){ .xoffset = 2*1, .xfactor = 1, .yoffset = 2*1, .yfactor = 1,
1339 .cellsize = 4 * sizeof(float), .overhead = 0,
1340 .sizex = 1 << 8, .sizey = 1 << 8 };
1341
1342 if(!dt_opencl_local_buffer_opt(devid, gd->kernel_ppg_redblue, &locopt))
1343 goto error;
1344
1345 size_t sizes[3] = { ROUNDUP(width, locopt.sizex), ROUNDUP(height, locopt.sizey), 1 };
1346 size_t local[3] = { locopt.sizex, locopt.sizey, 1 };
1347 dt_opencl_set_kernel_arg(devid, gd->kernel_ppg_redblue, 0, sizeof(cl_mem), &dev_tmp);
1348 dt_opencl_set_kernel_arg(devid, gd->kernel_ppg_redblue, 1, sizeof(cl_mem), &dev_aux);
1349 dt_opencl_set_kernel_arg(devid, gd->kernel_ppg_redblue, 2, sizeof(int), &width);
1350 dt_opencl_set_kernel_arg(devid, gd->kernel_ppg_redblue, 3, sizeof(int), &height);
1351 dt_opencl_set_kernel_arg(devid, gd->kernel_ppg_redblue, 4, sizeof(uint32_t),
1352 (void *)&filters);
1354 sizeof(float) * 4 * (locopt.sizex + 2) * (locopt.sizey + 2), NULL);
1355
1356 err = dt_opencl_enqueue_kernel_2d_with_local(devid, gd->kernel_ppg_redblue, sizes, local);
1357 if(err != CL_SUCCESS) goto error;
1358 }
1359 }
1360
1361 if(dev_aux != dev_out) dt_opencl_release_mem_object(dev_aux);
1362 if(dev_med != dev_in) dt_opencl_release_mem_object(dev_med);
1363 dt_opencl_release_mem_object(dev_green_eq);
1365 dev_aux = dev_green_eq = dev_tmp = dev_med = NULL;
1366
1367 // color smoothing
1368 if(data->color_smoothing)
1369 {
1370 if(!color_smoothing_cl(self, pipe, piece, dev_out, dev_out, roi_out, data->color_smoothing))
1371 goto error;
1372 }
1373
1374 return TRUE;
1375
1376error:
1377 if(dev_aux != dev_out) dt_opencl_release_mem_object(dev_aux);
1378 if(dev_med != dev_in) dt_opencl_release_mem_object(dev_med);
1379 dt_opencl_release_mem_object(dev_green_eq);
1381 dt_print(DT_DEBUG_OPENCL, "[opencl_demosaic] couldn't enqueue kernel! %d\n", err);
1382 return FALSE;
1383}
1384
1386 const dt_dev_pixelpipe_t *pipe,
1387 cl_mem dev_out,
1388 const dt_iop_roi_t *const roi_out,
1389 const int iterations)
1390{
1391 if(iterations <= 0) return TRUE;
1392
1394 const int devid = pipe->devid;
1395 const int width = roi_out->width;
1396 const int height = roi_out->height;
1397 const int clip_negatives = 1;
1398 const int keep_signed = 0;
1399 const int dense_mult = 1;
1400 size_t sizes[] = { ROUNDUPDWD(width, devid), ROUNDUPDHT(height, devid), 1 };
1401
1402 cl_mem LF_even = NULL;
1403 cl_mem LF_odd = NULL;
1404 cl_mem temp = NULL;
1405 cl_mem coeff = NULL;
1406 cl_mem bias = NULL;
1407 cl_mem coeff_tmp = NULL;
1408 cl_mem reconstructed_a = NULL;
1409 cl_mem reconstructed_b = NULL;
1410 cl_mem residual = NULL;
1411 cl_mem reconstructed_read = NULL;
1412 cl_mem reconstructed_write = NULL;
1413 cl_mem reconstructed_final = NULL;
1414 cl_int err = DT_OPENCL_DEFAULT_ERROR;
1415
1416 LF_even = dt_opencl_alloc_device(devid, width, height, sizeof(float) * 4);
1417 LF_odd = dt_opencl_alloc_device(devid, width, height, sizeof(float) * 4);
1418 temp = dt_opencl_alloc_device(devid, width, height, sizeof(float) * 4);
1419 coeff = dt_opencl_alloc_device(devid, width, height, sizeof(float) * 4);
1420 bias = dt_opencl_alloc_device(devid, width, height, sizeof(float) * 4);
1421 coeff_tmp = dt_opencl_alloc_device(devid, width, height, sizeof(float) * 4);
1422 reconstructed_a = dt_opencl_alloc_device(devid, width, height, sizeof(float) * 4);
1423 reconstructed_b = dt_opencl_alloc_device(devid, width, height, sizeof(float) * 4);
1424 if(IS_NULL_PTR(LF_even) || IS_NULL_PTR(LF_odd) || IS_NULL_PTR(temp) || IS_NULL_PTR(coeff) || IS_NULL_PTR(bias) || IS_NULL_PTR(coeff_tmp)
1425 || IS_NULL_PTR(reconstructed_a) || IS_NULL_PTR(reconstructed_b))
1426 goto error;
1427
1428 for(int iteration = 0; iteration < iterations; ++iteration)
1429 {
1430 reconstructed_read = reconstructed_a;
1431 reconstructed_write = reconstructed_b;
1432 reconstructed_final = NULL;
1433 residual = dev_out;
1434
1435 for(int s = 0; s < DOWNSAMPLE_GUIDED_SCALES; ++s)
1436 {
1437 const int mult = 1 << s;
1438 const int first_scale = (s == 0);
1439 cl_mem buffer_in;
1440 cl_mem buffer_out;
1441
1442 if(s == 0)
1443 {
1444 buffer_in = dev_out;
1445 buffer_out = LF_odd;
1446 }
1447 else if(s % 2 != 0)
1448 {
1449 buffer_in = LF_odd;
1450 buffer_out = LF_even;
1451 }
1452 else
1453 {
1454 buffer_in = LF_even;
1455 buffer_out = LF_odd;
1456 }
1457
1458 int hblocksize;
1459 dt_opencl_local_buffer_t hlocopt = (dt_opencl_local_buffer_t){ .xoffset = 2 * mult, .xfactor = 1,
1460 .yoffset = 0, .yfactor = 1,
1461 .cellsize = 4 * sizeof(float), .overhead = 0,
1462 .sizex = 1 << 16, .sizey = 1 };
1464 hblocksize = hlocopt.sizex;
1465 else
1466 hblocksize = 1;
1467
1468 if(hblocksize > 1)
1469 {
1470 const size_t horizontal_sizes[3] = { ROUNDUP(width, hblocksize), ROUNDUPDHT(height, devid), 1 };
1471 const size_t horizontal_local[3] = { hblocksize, 1, 1 };
1472 dt_opencl_set_kernel_arg(devid, gd->kernel_bspline_horizontal_local, 0, sizeof(cl_mem), &buffer_in);
1473 dt_opencl_set_kernel_arg(devid, gd->kernel_bspline_horizontal_local, 1, sizeof(cl_mem), &temp);
1476 dt_opencl_set_kernel_arg(devid, gd->kernel_bspline_horizontal_local, 4, sizeof(int), &mult);
1477 dt_opencl_set_kernel_arg(devid, gd->kernel_bspline_horizontal_local, 5, sizeof(int), &clip_negatives);
1479 (hblocksize + 4 * mult) * 4 * sizeof(float), NULL);
1481 horizontal_sizes, horizontal_local);
1482 }
1483 else
1484 {
1485 dt_opencl_set_kernel_arg(devid, gd->kernel_bspline_horizontal, 0, sizeof(cl_mem), &buffer_in);
1486 dt_opencl_set_kernel_arg(devid, gd->kernel_bspline_horizontal, 1, sizeof(cl_mem), &temp);
1487 dt_opencl_set_kernel_arg(devid, gd->kernel_bspline_horizontal, 2, sizeof(int), &width);
1488 dt_opencl_set_kernel_arg(devid, gd->kernel_bspline_horizontal, 3, sizeof(int), &height);
1489 dt_opencl_set_kernel_arg(devid, gd->kernel_bspline_horizontal, 4, sizeof(int), &mult);
1490 dt_opencl_set_kernel_arg(devid, gd->kernel_bspline_horizontal, 5, sizeof(int), &clip_negatives);
1492 }
1493 if(err != CL_SUCCESS) goto error;
1494
1495 int vblocksize;
1496 dt_opencl_local_buffer_t vlocopt = (dt_opencl_local_buffer_t){ .xoffset = 0, .xfactor = 1,
1497 .yoffset = 2 * mult, .yfactor = 1,
1498 .cellsize = 4 * sizeof(float), .overhead = 0,
1499 .sizex = 1, .sizey = 1 << 16 };
1501 vblocksize = vlocopt.sizey;
1502 else
1503 vblocksize = 1;
1504
1505 if(vblocksize > 1)
1506 {
1507 const size_t vertical_sizes[3] = { ROUNDUPDWD(width, devid), ROUNDUP(height, vblocksize), 1 };
1508 const size_t vertical_local[3] = { 1, vblocksize, 1 };
1509 dt_opencl_set_kernel_arg(devid, gd->kernel_bspline_vertical_local, 0, sizeof(cl_mem), &temp);
1510 dt_opencl_set_kernel_arg(devid, gd->kernel_bspline_vertical_local, 1, sizeof(cl_mem), &buffer_out);
1511 dt_opencl_set_kernel_arg(devid, gd->kernel_bspline_vertical_local, 2, sizeof(int), &width);
1513 dt_opencl_set_kernel_arg(devid, gd->kernel_bspline_vertical_local, 4, sizeof(int), &mult);
1514 dt_opencl_set_kernel_arg(devid, gd->kernel_bspline_vertical_local, 5, sizeof(int), &clip_negatives);
1516 (vblocksize + 4 * mult) * 4 * sizeof(float), NULL);
1518 vertical_sizes, vertical_local);
1519 }
1520 else
1521 {
1522 dt_opencl_set_kernel_arg(devid, gd->kernel_bspline_vertical, 0, sizeof(cl_mem), &temp);
1523 dt_opencl_set_kernel_arg(devid, gd->kernel_bspline_vertical, 1, sizeof(cl_mem), &buffer_out);
1524 dt_opencl_set_kernel_arg(devid, gd->kernel_bspline_vertical, 2, sizeof(int), &width);
1525 dt_opencl_set_kernel_arg(devid, gd->kernel_bspline_vertical, 3, sizeof(int), &height);
1526 dt_opencl_set_kernel_arg(devid, gd->kernel_bspline_vertical, 4, sizeof(int), &mult);
1527 dt_opencl_set_kernel_arg(devid, gd->kernel_bspline_vertical, 5, sizeof(int), &clip_negatives);
1529 }
1530 if(err != CL_SUCCESS) goto error;
1531
1532 dt_opencl_set_kernel_arg(devid, gd->kernel_guided_laplacian_normalize, 0, sizeof(cl_mem), &buffer_in);
1533 dt_opencl_set_kernel_arg(devid, gd->kernel_guided_laplacian_normalize, 1, sizeof(cl_mem), &buffer_out);
1534 dt_opencl_set_kernel_arg(devid, gd->kernel_guided_laplacian_normalize, 2, sizeof(cl_mem), &temp);
1538 if(err != CL_SUCCESS) goto error;
1539
1540 dt_opencl_set_kernel_arg(devid, gd->kernel_guided_laplacian_coefficients, 0, sizeof(cl_mem), &temp);
1542 dt_opencl_set_kernel_arg(devid, gd->kernel_guided_laplacian_coefficients, 2, sizeof(cl_mem), &bias);
1546 if(err != CL_SUCCESS) goto error;
1547
1548 dt_opencl_set_kernel_arg(devid, gd->kernel_bspline_horizontal, 0, sizeof(cl_mem), &coeff);
1549 dt_opencl_set_kernel_arg(devid, gd->kernel_bspline_horizontal, 1, sizeof(cl_mem), &coeff_tmp);
1550 dt_opencl_set_kernel_arg(devid, gd->kernel_bspline_horizontal, 2, sizeof(int), &width);
1551 dt_opencl_set_kernel_arg(devid, gd->kernel_bspline_horizontal, 3, sizeof(int), &height);
1552 dt_opencl_set_kernel_arg(devid, gd->kernel_bspline_horizontal, 4, sizeof(int), &dense_mult);
1553 dt_opencl_set_kernel_arg(devid, gd->kernel_bspline_horizontal, 5, sizeof(int), &keep_signed);
1555 if(err != CL_SUCCESS) goto error;
1556
1557 dt_opencl_set_kernel_arg(devid, gd->kernel_bspline_vertical, 0, sizeof(cl_mem), &coeff_tmp);
1558 dt_opencl_set_kernel_arg(devid, gd->kernel_bspline_vertical, 1, sizeof(cl_mem), &coeff);
1559 dt_opencl_set_kernel_arg(devid, gd->kernel_bspline_vertical, 2, sizeof(int), &width);
1560 dt_opencl_set_kernel_arg(devid, gd->kernel_bspline_vertical, 3, sizeof(int), &height);
1561 dt_opencl_set_kernel_arg(devid, gd->kernel_bspline_vertical, 4, sizeof(int), &dense_mult);
1562 dt_opencl_set_kernel_arg(devid, gd->kernel_bspline_vertical, 5, sizeof(int), &keep_signed);
1564 if(err != CL_SUCCESS) goto error;
1565
1566 dt_opencl_set_kernel_arg(devid, gd->kernel_bspline_horizontal, 0, sizeof(cl_mem), &bias);
1567 dt_opencl_set_kernel_arg(devid, gd->kernel_bspline_horizontal, 1, sizeof(cl_mem), &coeff_tmp);
1568 dt_opencl_set_kernel_arg(devid, gd->kernel_bspline_horizontal, 2, sizeof(int), &width);
1569 dt_opencl_set_kernel_arg(devid, gd->kernel_bspline_horizontal, 3, sizeof(int), &height);
1570 dt_opencl_set_kernel_arg(devid, gd->kernel_bspline_horizontal, 4, sizeof(int), &dense_mult);
1571 dt_opencl_set_kernel_arg(devid, gd->kernel_bspline_horizontal, 5, sizeof(int), &keep_signed);
1573 if(err != CL_SUCCESS) goto error;
1574
1575 dt_opencl_set_kernel_arg(devid, gd->kernel_bspline_vertical, 0, sizeof(cl_mem), &coeff_tmp);
1576 dt_opencl_set_kernel_arg(devid, gd->kernel_bspline_vertical, 1, sizeof(cl_mem), &bias);
1577 dt_opencl_set_kernel_arg(devid, gd->kernel_bspline_vertical, 2, sizeof(int), &width);
1578 dt_opencl_set_kernel_arg(devid, gd->kernel_bspline_vertical, 3, sizeof(int), &height);
1579 dt_opencl_set_kernel_arg(devid, gd->kernel_bspline_vertical, 4, sizeof(int), &dense_mult);
1580 dt_opencl_set_kernel_arg(devid, gd->kernel_bspline_vertical, 5, sizeof(int), &keep_signed);
1582 if(err != CL_SUCCESS) goto error;
1583
1584 dt_opencl_set_kernel_arg(devid, gd->kernel_guided_laplacian_apply, 0, sizeof(cl_mem), &temp);
1585 dt_opencl_set_kernel_arg(devid, gd->kernel_guided_laplacian_apply, 1, sizeof(cl_mem), &coeff);
1586 dt_opencl_set_kernel_arg(devid, gd->kernel_guided_laplacian_apply, 2, sizeof(cl_mem), &bias);
1587 dt_opencl_set_kernel_arg(devid, gd->kernel_guided_laplacian_apply, 3, sizeof(cl_mem), &buffer_out);
1588 dt_opencl_set_kernel_arg(devid, gd->kernel_guided_laplacian_apply, 4, sizeof(cl_mem), &reconstructed_read);
1589 dt_opencl_set_kernel_arg(devid, gd->kernel_guided_laplacian_apply, 5, sizeof(cl_mem), &reconstructed_write);
1590 dt_opencl_set_kernel_arg(devid, gd->kernel_guided_laplacian_apply, 6, sizeof(int), &width);
1592 dt_opencl_set_kernel_arg(devid, gd->kernel_guided_laplacian_apply, 8, sizeof(int), &first_scale);
1594 if(err != CL_SUCCESS) goto error;
1595
1596 residual = buffer_out;
1597 reconstructed_final = reconstructed_write;
1598 cl_mem tmp = reconstructed_read;
1599 reconstructed_read = reconstructed_write;
1600 reconstructed_write = tmp;
1601 }
1602
1603 dt_opencl_set_kernel_arg(devid, gd->kernel_guided_laplacian_finalize, 0, sizeof(cl_mem), &reconstructed_final);
1604 dt_opencl_set_kernel_arg(devid, gd->kernel_guided_laplacian_finalize, 1, sizeof(cl_mem), &residual);
1605 dt_opencl_set_kernel_arg(devid, gd->kernel_guided_laplacian_finalize, 2, sizeof(cl_mem), &dev_out);
1609 if(err != CL_SUCCESS) goto error;
1610 }
1611
1612 dt_opencl_release_mem_object(reconstructed_b);
1613 dt_opencl_release_mem_object(reconstructed_a);
1620 return TRUE;
1621
1622error:
1623 dt_opencl_release_mem_object(reconstructed_b);
1624 dt_opencl_release_mem_object(reconstructed_a);
1631 dt_print(DT_DEBUG_OPENCL, "[opencl_demosaic] guided laplacian postfilter failed: %d\n", err);
1632 return FALSE;
1633}
1634
1635int process_cl(struct dt_iop_module_t *self, const dt_dev_pixelpipe_t *pipe, const dt_dev_pixelpipe_iop_t *piece,
1636 cl_mem dev_in, cl_mem dev_out)
1637{
1638 const dt_iop_roi_t *const roi_in = &piece->roi_in;
1639 const dt_iop_roi_t *const roi_out = &piece->roi_out;
1640 dt_times_t start_time = { 0 }, end_time = { 0 };
1641 const gboolean info = ((darktable.unmuted & (DT_DEBUG_DEMOSAIC | DT_DEBUG_PERF))
1642 && (pipe->type == DT_DEV_PIXELPIPE_FULL));
1643
1644 // See the matching comment in process(): most kernels below take roi_in->x/y as an
1645 // explicit argument and self-correct against the unshifted piece->dsc_in.filters/xtrans.
1646 // Only the bayer downsample kernel works in tile-local coordinates with no roi awareness,
1647 // so it alone needs the dynamic offset folded in up front.
1648 const uint32_t filters = dt_dev_get_roi_filters(piece, roi_in);
1649
1652
1653 int demosaicing_method = data->demosaicing_method;
1654
1655 gboolean showmask = FALSE;
1656 if(self->dev->gui_attached && pipe->type == DT_DEV_PIXELPIPE_FULL)
1657 {
1659 if(g) showmask = (g->visual_mask);
1660 // take care of passthru modes
1662 demosaicing_method = (piece->dsc_in.filters != 9u) ? DT_IOP_DEMOSAIC_RCD : DT_IOP_DEMOSAIC_MARKESTEIJN;
1664 demosaicing_method = DT_IOP_DEMOSAIC_PASSTHROUGH_MONOCHROME;
1665 }
1666
1668 "[demosaic] CL pipe %p (%s) thread %lu piece %p devid=%d roi_in=(%d,%d) %dx%d dsc_in.filters=0x%x -> filters=0x%x method=%s\n",
1669 (void *)pipe, _pipe_type_string(pipe->type), (unsigned long)pthread_self(), (void *)piece,
1670 pipe->devid, roi_in->x, roi_in->y, roi_in->width, roi_in->height,
1671 piece->dsc_in.filters, filters, method2string(demosaicing_method));
1672
1673 cl_mem high_image = NULL;
1674 cl_mem low_image = NULL;
1675 cl_mem blend = NULL;
1676 cl_mem details = NULL;
1677 cl_mem dev_aux = NULL;
1678 cl_mem dev_xtrans = NULL;
1679 const gboolean dual = ((demosaicing_method & DEMOSAIC_DUAL) && (data->dual_thrs > 0.0f));
1680 const int devid = pipe->devid;
1681 gboolean retval = FALSE;
1682
1683 if(info) dt_get_times(&start_time);
1684
1685 if(demosaicing_method == DT_IOP_DEMOSAIC_PASSTHROUGH_MONOCHROME ||
1686 demosaicing_method == DT_IOP_DEMOSAIC_PPG ||
1687 demosaicing_method == DT_IOP_DEMOSAIC_PASSTHROUGH_COLOR )
1688 {
1689 if(!process_default_cl(self, pipe, piece, dev_in, dev_out, roi_in, roi_out, demosaicing_method)) return FALSE;
1690 }
1691 else if(_is_downsample_method(demosaicing_method))
1692 {
1693 const int width = roi_out->width;
1694 const int height = roi_out->height;
1695 size_t sizes[3] = { ROUNDUPDWD(width, devid), ROUNDUPDHT(height, devid), 1 };
1696
1697 if(piece->dsc_in.filters == 9u)
1698 {
1699 dev_xtrans = dt_opencl_copy_host_to_device_constant(devid, sizeof(piece->dsc_in.xtrans),
1700 (void *)piece->dsc_in.xtrans);
1701 if(IS_NULL_PTR(dev_xtrans)) goto finish;
1702
1703 dt_opencl_set_kernel_arg(devid, gd->kernel_zoom_half_size_xtrans, 0, sizeof(cl_mem), &dev_in);
1704 dt_opencl_set_kernel_arg(devid, gd->kernel_zoom_half_size_xtrans, 1, sizeof(cl_mem), &dev_out);
1705 dt_opencl_set_kernel_arg(devid, gd->kernel_zoom_half_size_xtrans, 2, sizeof(int), &width);
1706 dt_opencl_set_kernel_arg(devid, gd->kernel_zoom_half_size_xtrans, 3, sizeof(int), &height);
1707 dt_opencl_set_kernel_arg(devid, gd->kernel_zoom_half_size_xtrans, 4, sizeof(int), (void *)&roi_in->x);
1708 dt_opencl_set_kernel_arg(devid, gd->kernel_zoom_half_size_xtrans, 5, sizeof(int), (void *)&roi_in->y);
1709 dt_opencl_set_kernel_arg(devid, gd->kernel_zoom_half_size_xtrans, 6, sizeof(int), (void *)&roi_in->width);
1710 dt_opencl_set_kernel_arg(devid, gd->kernel_zoom_half_size_xtrans, 7, sizeof(int), (void *)&roi_in->height);
1711 dt_opencl_set_kernel_arg(devid, gd->kernel_zoom_half_size_xtrans, 8, sizeof(cl_mem), (void *)&dev_xtrans);
1712 const cl_int err = dt_opencl_enqueue_kernel_2d(devid, gd->kernel_zoom_half_size_xtrans, sizes);
1713 if(err != CL_SUCCESS) goto finish;
1714 }
1715 else
1716 {
1717 const int zero = 0;
1718 const int is_4bayer = self->dev->image_storage.flags & DT_IMAGE_4BAYER;
1719 const float cam_to_rgb_0[4] = { data->CAM_to_RGB[0][0], data->CAM_to_RGB[0][1],
1720 data->CAM_to_RGB[0][2], data->CAM_to_RGB[0][3] };
1721 const float cam_to_rgb_1[4] = { data->CAM_to_RGB[1][0], data->CAM_to_RGB[1][1],
1722 data->CAM_to_RGB[1][2], data->CAM_to_RGB[1][3] };
1723 const float cam_to_rgb_2[4] = { data->CAM_to_RGB[2][0], data->CAM_to_RGB[2][1],
1724 data->CAM_to_RGB[2][2], data->CAM_to_RGB[2][3] };
1725
1726 dt_opencl_set_kernel_arg(devid, gd->kernel_zoom_half_size, 0, sizeof(cl_mem), &dev_in);
1727 dt_opencl_set_kernel_arg(devid, gd->kernel_zoom_half_size, 1, sizeof(cl_mem), &dev_out);
1728 dt_opencl_set_kernel_arg(devid, gd->kernel_zoom_half_size, 2, sizeof(int), &width);
1729 dt_opencl_set_kernel_arg(devid, gd->kernel_zoom_half_size, 3, sizeof(int), &height);
1730 dt_opencl_set_kernel_arg(devid, gd->kernel_zoom_half_size, 4, sizeof(int), &zero);
1731 dt_opencl_set_kernel_arg(devid, gd->kernel_zoom_half_size, 5, sizeof(int), &zero);
1732 dt_opencl_set_kernel_arg(devid, gd->kernel_zoom_half_size, 6, sizeof(int), (void *)&roi_in->width);
1733 dt_opencl_set_kernel_arg(devid, gd->kernel_zoom_half_size, 7, sizeof(int), (void *)&roi_in->height);
1734 dt_opencl_set_kernel_arg(devid, gd->kernel_zoom_half_size, 8, sizeof(float), (void *)&roi_out->scale);
1735 dt_opencl_set_kernel_arg(devid, gd->kernel_zoom_half_size, 9, sizeof(uint32_t), (void *)&filters);
1736 dt_opencl_set_kernel_arg(devid, gd->kernel_zoom_half_size, 10, sizeof(int), &is_4bayer);
1737 dt_opencl_set_kernel_arg(devid, gd->kernel_zoom_half_size, 11, sizeof(cam_to_rgb_0), cam_to_rgb_0);
1738 dt_opencl_set_kernel_arg(devid, gd->kernel_zoom_half_size, 12, sizeof(cam_to_rgb_1), cam_to_rgb_1);
1739 dt_opencl_set_kernel_arg(devid, gd->kernel_zoom_half_size, 13, sizeof(cam_to_rgb_2), cam_to_rgb_2);
1740 const cl_int err = dt_opencl_enqueue_kernel_2d(devid, gd->kernel_zoom_half_size, sizes);
1741 if(err != CL_SUCCESS) goto finish;
1742 }
1743
1744 if(!_downsample_guided_laplacian_postfilter_cl(self, pipe, dev_out, roi_out, data->color_smoothing)) goto finish;
1745
1746 retval = TRUE;
1747 goto finish;
1748 }
1749 else if((demosaicing_method & ~DEMOSAIC_DUAL) == DT_IOP_DEMOSAIC_RCD)
1750 {
1751 if(dual)
1752 {
1753 high_image = dt_opencl_alloc_device(devid, roi_in->width, roi_in->height, sizeof(float) * 4);
1754 if(IS_NULL_PTR(high_image)) return FALSE;
1755 if(!process_rcd_cl(self, pipe, piece, dev_in, high_image, roi_in, roi_in, FALSE)) goto finish;
1756 }
1757 else
1758 {
1759 if(!process_rcd_cl(self, pipe, piece, dev_in, dev_out, roi_in, roi_out, TRUE)) return FALSE;
1760 }
1761 }
1762 else if(demosaicing_method == DT_IOP_DEMOSAIC_VNG4 || demosaicing_method == DT_IOP_DEMOSAIC_VNG)
1763 {
1764 if(!process_vng_cl(self, pipe, piece, dev_in, dev_out, roi_in, roi_out, TRUE, FALSE)) return FALSE;
1765 }
1766 else if(((demosaicing_method & ~DEMOSAIC_DUAL) == DT_IOP_DEMOSAIC_MARKESTEIJN ) ||
1767 ((demosaicing_method & ~DEMOSAIC_DUAL) == DT_IOP_DEMOSAIC_MARKESTEIJN_3))
1768 {
1769 if(dual)
1770 {
1771 high_image = dt_opencl_alloc_device(devid, roi_in->width, roi_in->height, sizeof(float) * 4);
1772 if(IS_NULL_PTR(high_image)) return FALSE;
1773 if(!process_markesteijn_cl(self, pipe, piece, dev_in, high_image, roi_in, roi_in, FALSE)) return FALSE;
1774 }
1775 else
1776 {
1777 if(!process_markesteijn_cl(self, pipe, piece, dev_in, dev_out, roi_in, roi_out, TRUE)) return FALSE;
1778 }
1779 }
1780 else
1781 {
1782 dt_print(DT_DEBUG_OPENCL, "[opencl_demosaic] demosaicing method '%s' not yet supported by opencl code\n", method2string(demosaicing_method));
1783 return FALSE;
1784 }
1785
1786 if(info)
1787 {
1788 const float mpixels = (roi_in->width * roi_in->height) / 1.0e6;
1789 dt_get_times(&end_time);
1790 const float tclock = end_time.clock - start_time.clock;
1791 const float uclock = end_time.user - start_time.user;
1792 fprintf(stderr," [demosaic] process GPU `%s' did %.2fmpix, %.4f secs (%.4f CPU), %.2f pix/us\n",
1793 method2string(demosaicing_method & ~DEMOSAIC_DUAL), mpixels, tclock, uclock, mpixels / tclock);
1794 }
1795 if(!dual)
1796 {
1797 retval = TRUE;
1798 goto finish;
1799 }
1800
1801 // This is dual demosaicing only stuff
1802 const int scaled = (roi_out->width != roi_in->width || roi_out->height != roi_in->height);
1803
1804 int width = roi_out->width;
1805 int height = roi_out->height;
1806 // need to reserve scaled auxiliary buffer or use dev_out
1807 if(scaled)
1808 {
1809 dev_aux = dt_opencl_alloc_device(devid, roi_in->width, roi_in->height, sizeof(float) * 4);
1810 if(IS_NULL_PTR(dev_aux)) goto finish;
1811 width = roi_in->width;
1812 height = roi_in->height;
1813 }
1814 else
1815 dev_aux = dev_out;
1816
1817 // here we have work to be done only for dual demosaicers
1818 blend = dt_opencl_alloc_device_buffer(devid, sizeof(float) * width * height);
1819 details = dt_opencl_alloc_device_buffer(devid, sizeof(float) * width * height);
1820 low_image = dt_opencl_alloc_device(devid, width, height, sizeof(float) * 4);
1821 if((IS_NULL_PTR(blend)) || (IS_NULL_PTR(low_image)) || (IS_NULL_PTR(details))) goto finish;
1822
1823 if(info) dt_get_times(&start_time);
1824 if(process_vng_cl(self, pipe, piece, dev_in, low_image, roi_in, roi_in, FALSE, FALSE))
1825 {
1826 if(!color_smoothing_cl(self, pipe, piece, low_image, low_image, roi_in, 2))
1827 {
1828 retval = FALSE;
1829 goto finish;
1830 }
1831 retval = dual_demosaic_cl(self, pipe, piece, details, blend, high_image, low_image, dev_aux, width, height, showmask);
1832 }
1833
1834 if(info)
1835 {
1836 dt_get_times(&end_time);
1837 fprintf(stderr," [demosaic] GPU dual blending %.4f secs (%.4f CPU)\n", end_time.clock - start_time.clock, end_time.user - start_time.user);
1838 }
1839
1840 if(scaled)
1841 {
1842 // scale aux buffer to output buffer
1843 const int err = dt_iop_clip_and_zoom_roi_cl(devid, dev_out, dev_aux, roi_out, roi_in);
1844 if(err != CL_SUCCESS)
1845 retval = FALSE;
1846 }
1847
1848 finish:
1849 dt_opencl_release_mem_object(high_image);
1853 if(dev_aux != dev_out) dt_opencl_release_mem_object(dev_aux);
1854 dt_opencl_release_mem_object(dev_xtrans);
1855 if(!retval && dual) dt_control_log(_("[dual demosaic_cl] internal problem"));
1856 return retval;
1857}
1858#endif
1859
1860void 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)
1861{
1862 const dt_iop_roi_t *const roi_in = &piece->roi_in;
1863 const dt_iop_roi_t *const roi_out = &piece->roi_out;
1865
1866 const float ioratio = (float)roi_out->width * roi_out->height / ((float)roi_in->width * roi_in->height);
1867 const float smooth = data->color_smoothing ? ioratio : 0.0f;
1868 const float greeneq
1869 = ((piece->dsc_in.filters != 9u) && (data->green_eq != DT_IOP_GREEN_EQ_NO)) ? 0.25f : 0.0f;
1870 const dt_iop_demosaic_method_t demosaicing_method = data->demosaicing_method & ~DEMOSAIC_DUAL;
1871
1872 if(demosaicing_method == DT_IOP_DEMOSAIC_DOWNSAMPLE)
1873 {
1874 tiling->factor = 1.0f + ioratio + (data->color_smoothing ? 7.0f * ioratio : 0.0f);
1875 tiling->maxbuf = 1.0f;
1876 tiling->overhead = 0;
1877 tiling->xalign = 1;
1878 tiling->yalign = 1;
1879 tiling->overlap = (piece->dsc_in.filters == 9u) ? 18 : 16;
1880 }
1881 else if((demosaicing_method == DT_IOP_DEMOSAIC_PPG) ||
1882 (demosaicing_method == DT_IOP_DEMOSAIC_PASSTHROUGH_MONOCHROME) ||
1883 (demosaicing_method == DT_IOP_DEMOSAIC_PASSTHROUGH_COLOR) ||
1884 (demosaicing_method == DT_IOP_DEMOSAIC_AMAZE))
1885 {
1886 // Bayer pattern with PPG, Passthrough or Amaze
1887 tiling->factor = 1.0f + ioratio; // in + out
1888 tiling->factor += fmax(1.0f + greeneq, smooth); // + tmp + geeneq | + smooth
1889 tiling->maxbuf = 1.0f;
1890 tiling->overhead = 0;
1891 tiling->xalign = 2;
1892 tiling->yalign = 2;
1893 tiling->overlap = 5; // take care of border handling
1894 }
1895 else if(((demosaicing_method == DT_IOP_DEMOSAIC_MARKESTEIJN) ||
1896 (demosaicing_method == DT_IOP_DEMOSAIC_MARKESTEIJN_3) ||
1897 (demosaicing_method == DT_IOP_DEMOSAIC_FDC)))
1898 {
1899 // X-Trans pattern full Markesteijn processing
1900 const int ndir = (demosaicing_method == DT_IOP_DEMOSAIC_MARKESTEIJN_3) ? 8 : 4;
1901 const int overlap = (demosaicing_method == DT_IOP_DEMOSAIC_MARKESTEIJN_3) ? 18 : 12;
1902
1903 tiling->factor = 1.0f + ioratio;
1904 tiling->factor += ndir * 1.0f // rgb
1905 + ndir * 0.25f // drv
1906 + ndir * 0.125f // homo + homosum
1907 + 1.0f; // aux
1908
1909 tiling->factor += fmax(1.0f + greeneq, smooth);
1910 tiling->maxbuf = 1.0f;
1911 tiling->overhead = 0;
1912 tiling->xalign = XTRANS_SNAPPER;
1913 tiling->yalign = XTRANS_SNAPPER;
1914 tiling->overlap = overlap;
1915 }
1916 else if(demosaicing_method == DT_IOP_DEMOSAIC_RCD)
1917 {
1918 tiling->factor = 1.0f + ioratio;
1919 tiling->factor += fmax(1.0f + greeneq, smooth); // + tmp + geeneq | + smooth
1920 tiling->maxbuf = 1.0f;
1921 tiling->overhead = sizeof(float) * RCD_TILESIZE * RCD_TILESIZE * 8 * MAX(1, darktable.num_openmp_threads);
1922 tiling->xalign = 2;
1923 tiling->yalign = 2;
1924 tiling->overlap = 10;
1925 tiling->factor_cl = tiling->factor + 3.0f;
1926 }
1927 else if(demosaicing_method == DT_IOP_DEMOSAIC_LMMSE)
1928 {
1929 tiling->factor = 1.0f + ioratio;
1930 tiling->factor += fmax(1.0f + greeneq, smooth); // + tmp + geeneq | + smooth
1931 tiling->maxbuf = 1.0f;
1932 tiling->overhead = sizeof(float) * LMMSE_GRP * LMMSE_GRP * 6 * MAX(1, darktable.num_openmp_threads);
1933 tiling->xalign = 2;
1934 tiling->yalign = 2;
1935 tiling->overlap = 10;
1936 }
1937 else
1938 {
1939 // VNG
1940 tiling->factor = 1.0f + ioratio;
1941 tiling->factor += fmax(1.0f + greeneq, smooth);
1942 tiling->maxbuf = 1.0f;
1943 tiling->overhead = 0;
1944 tiling->xalign = 6; // covering Bayer pattern for VNG4 as well as xtrans for VNG
1945 tiling->yalign = 6; // covering Bayer pattern for VNG4 as well as xtrans for VNG
1946 tiling->overlap = 6;
1947 }
1949 {
1950 // make sure VNG4 is also possible
1951 tiling->factor += 1.0f;
1952 tiling->xalign = MAX(6, tiling->xalign);
1953 tiling->yalign = MAX(6, tiling->yalign);
1954 tiling->overlap = MAX(6, tiling->overlap);
1955 }
1956 return;
1957}
1958#undef LMMSE_GRP
1959
1961{
1962 const int program = 0; // from programs.conf
1965 module->data = gd;
1966 gd->kernel_zoom_half_size = dt_opencl_create_kernel(program, "clip_and_zoom_demosaic_half_size");
1967 gd->kernel_ppg_green = dt_opencl_create_kernel(program, "ppg_demosaic_green");
1968 gd->kernel_green_eq_lavg = dt_opencl_create_kernel(program, "green_equilibration_lavg");
1969 gd->kernel_green_eq_favg_reduce_first = dt_opencl_create_kernel(program, "green_equilibration_favg_reduce_first");
1970 gd->kernel_green_eq_favg_reduce_second = dt_opencl_create_kernel(program, "green_equilibration_favg_reduce_second");
1971 gd->kernel_green_eq_favg_apply = dt_opencl_create_kernel(program, "green_equilibration_favg_apply");
1972 gd->kernel_pre_median = dt_opencl_create_kernel(program, "pre_median");
1973 gd->kernel_ppg_redblue = dt_opencl_create_kernel(program, "ppg_demosaic_redblue");
1974 gd->kernel_downsample = dt_opencl_create_kernel(program, "clip_and_zoom");
1975 gd->kernel_border_interpolate = dt_opencl_create_kernel(program, "border_interpolate");
1976 gd->kernel_color_smoothing = dt_opencl_create_kernel(program, "color_smoothing");
1977
1978 const int other = 14; // from programs.conf
1979 gd->kernel_passthrough_monochrome = dt_opencl_create_kernel(other, "passthrough_monochrome");
1980 gd->kernel_passthrough_color = dt_opencl_create_kernel(other, "passthrough_color");
1981 gd->kernel_zoom_passthrough_monochrome = dt_opencl_create_kernel(other, "clip_and_zoom_demosaic_passthrough_monochrome");
1982
1983 const int vng = 15; // from programs.conf
1984 gd->kernel_vng_border_interpolate = dt_opencl_create_kernel(vng, "vng_border_interpolate");
1985 gd->kernel_vng_lin_interpolate = dt_opencl_create_kernel(vng, "vng_lin_interpolate");
1986 gd->kernel_zoom_third_size = dt_opencl_create_kernel(vng, "clip_and_zoom_demosaic_third_size_xtrans");
1987 gd->kernel_zoom_half_size_xtrans = dt_opencl_create_kernel(vng, "clip_and_zoom_demosaic_half_size_xtrans");
1988 gd->kernel_vng_green_equilibrate = dt_opencl_create_kernel(vng, "vng_green_equilibrate");
1989 gd->kernel_vng_interpolate = dt_opencl_create_kernel(vng, "vng_interpolate");
1990
1991 const int markesteijn = 16; // from programs.conf
1992 gd->kernel_markesteijn_initial_copy = dt_opencl_create_kernel(markesteijn, "markesteijn_initial_copy");
1993 gd->kernel_markesteijn_green_minmax = dt_opencl_create_kernel(markesteijn, "markesteijn_green_minmax");
1994 gd->kernel_markesteijn_interpolate_green = dt_opencl_create_kernel(markesteijn, "markesteijn_interpolate_green");
1995 gd->kernel_markesteijn_solitary_green = dt_opencl_create_kernel(markesteijn, "markesteijn_solitary_green");
1996 gd->kernel_markesteijn_recalculate_green = dt_opencl_create_kernel(markesteijn, "markesteijn_recalculate_green");
1997 gd->kernel_markesteijn_red_and_blue = dt_opencl_create_kernel(markesteijn, "markesteijn_red_and_blue");
1998 gd->kernel_markesteijn_interpolate_twoxtwo = dt_opencl_create_kernel(markesteijn, "markesteijn_interpolate_twoxtwo");
1999 gd->kernel_markesteijn_convert_yuv = dt_opencl_create_kernel(markesteijn, "markesteijn_convert_yuv");
2000 gd->kernel_markesteijn_differentiate = dt_opencl_create_kernel(markesteijn, "markesteijn_differentiate");
2001 gd->kernel_markesteijn_homo_threshold = dt_opencl_create_kernel(markesteijn, "markesteijn_homo_threshold");
2002 gd->kernel_markesteijn_homo_set = dt_opencl_create_kernel(markesteijn, "markesteijn_homo_set");
2003 gd->kernel_markesteijn_homo_sum = dt_opencl_create_kernel(markesteijn, "markesteijn_homo_sum");
2004 gd->kernel_markesteijn_homo_max = dt_opencl_create_kernel(markesteijn, "markesteijn_homo_max");
2005 gd->kernel_markesteijn_homo_max_corr = dt_opencl_create_kernel(markesteijn, "markesteijn_homo_max_corr");
2006 gd->kernel_markesteijn_homo_quench = dt_opencl_create_kernel(markesteijn, "markesteijn_homo_quench");
2007 gd->kernel_markesteijn_zero = dt_opencl_create_kernel(markesteijn, "markesteijn_zero");
2008 gd->kernel_markesteijn_accu = dt_opencl_create_kernel(markesteijn, "markesteijn_accu");
2009 gd->kernel_markesteijn_final = dt_opencl_create_kernel(markesteijn, "markesteijn_final");
2010
2011 const int rcd = 31; // from programs.conf
2012 gd->kernel_rcd_populate = dt_opencl_create_kernel(rcd, "rcd_populate");
2013 gd->kernel_rcd_write_output = dt_opencl_create_kernel(rcd, "rcd_write_output");
2014 gd->kernel_rcd_step_1 = dt_opencl_create_kernel(rcd, "rcd_step_1");
2015 gd->kernel_rcd_step_2_1 = dt_opencl_create_kernel(rcd, "rcd_step_2_1");
2016 gd->kernel_rcd_step_3_1 = dt_opencl_create_kernel(rcd, "rcd_step_3_1");
2017 gd->kernel_rcd_step_4_1 = dt_opencl_create_kernel(rcd, "rcd_step_4_1");
2018 gd->kernel_rcd_step_4_2 = dt_opencl_create_kernel(rcd, "rcd_step_4_2");
2019 gd->kernel_rcd_step_5_1 = dt_opencl_create_kernel(rcd, "rcd_step_5_1");
2020 gd->kernel_rcd_step_5_2 = dt_opencl_create_kernel(rcd, "rcd_step_5_2");
2021 gd->kernel_rcd_border_redblue = dt_opencl_create_kernel(rcd, "rcd_border_redblue");
2022 gd->kernel_rcd_border_green = dt_opencl_create_kernel(rcd, "rcd_border_green");
2023 gd->kernel_write_blended_dual = dt_opencl_create_kernel(rcd, "write_blended_dual");
2024
2025 const int wavelets = 35; // bspline.cl, from programs.conf
2026 gd->kernel_guided_laplacian_coefficients = dt_opencl_create_kernel(wavelets, "guided_laplacian_coefficients");
2027 gd->kernel_guided_laplacian_normalize = dt_opencl_create_kernel(wavelets, "guided_laplacian_normalize");
2028 gd->kernel_guided_laplacian_apply = dt_opencl_create_kernel(wavelets, "guided_laplacian_apply");
2029 gd->kernel_guided_laplacian_finalize = dt_opencl_create_kernel(wavelets, "guided_laplacian_finalize");
2030 gd->kernel_bspline_horizontal = dt_opencl_create_kernel(wavelets, "blur_2D_Bspline_horizontal");
2031 gd->kernel_bspline_vertical = dt_opencl_create_kernel(wavelets, "blur_2D_Bspline_vertical");
2032 gd->kernel_bspline_horizontal_local = dt_opencl_create_kernel(wavelets, "blur_2D_Bspline_horizontal_local");
2033 gd->kernel_bspline_vertical_local = dt_opencl_create_kernel(wavelets, "blur_2D_Bspline_vertical_local");
2034 gd->lmmse_gamma_in = NULL;
2035 gd->lmmse_gamma_out = NULL;
2036}
2037
2039{
2101 dt_free(module->data);
2102}
2103
2104
2105gboolean force_enable(struct dt_iop_module_t *self, const gboolean current_state)
2106{
2107 // Demosaicing applies if and only if the buffer carries a CFA mosaic. Gate on the mosaic
2108 // axis, not on the historical "raw" flag: an already-demosaiced raw (sRAW / linear DNG) must
2109 // not be demosaiced, while a mosaiced raw that the RAW flag missed still must.
2110 // Mandatory module: the decision is purely image metadata and ignores current_state.
2111 const gboolean state = dt_image_needs_demosaic(&self->dev->image_storage);
2112 dt_iop_fmt_log(self, "force_enable: class=%s needs_demosaic=%d current=%d -> %d",
2114 state, current_state, state);
2115 return state;
2116}
2117
2118
2121{
2124
2125 d->green_eq = p->green_eq;
2126 d->color_smoothing = p->color_smoothing;
2127 d->median_thrs = p->median_thrs;
2128 d->dual_thrs = p->dual_thrs;
2129 d->lmmse_refine = p->lmmse_refine;
2130 dt_iop_demosaic_method_t use_method = p->demosaicing_method;
2131 const gboolean xmethod = use_method & DEMOSAIC_XTRANS;
2132 const gboolean bayer = (self->dev->image_storage.dsc.filters != 9u);
2133 const gboolean downsample = _is_downsample_method(use_method);
2134
2135 if(!downsample)
2136 {
2137 if(bayer && xmethod) use_method = DT_IOP_DEMOSAIC_RCD;
2138 if(!bayer && !xmethod) use_method = DT_IOP_DEMOSAIC_MARKESTEIJN;
2139 }
2140
2143 if(use_method == DT_IOP_DEMOSAIC_PASSTHROUGH_COLOR || use_method == DT_IOP_DEMOSAIC_PASSTHR_COLORX)
2145
2146 const gboolean passing = (use_method == DT_IOP_DEMOSAIC_PASSTHROUGH_MONOCHROME
2147 || use_method == DT_IOP_DEMOSAIC_PASSTHROUGH_COLOR);
2148
2149 if(!(use_method == DT_IOP_DEMOSAIC_PPG))
2150 d->median_thrs = 0.0f;
2151
2152 if(passing)
2153 {
2154 d->green_eq = DT_IOP_GREEN_EQ_NO;
2155 d->color_smoothing = 0;
2156 d->dual_thrs = 0.0f;
2157 }
2158 else if(downsample)
2159 {
2160 d->green_eq = DT_IOP_GREEN_EQ_NO;
2161 d->dual_thrs = 0.0f;
2162 }
2163
2164 if(use_method & DEMOSAIC_DUAL)
2165 d->color_smoothing = 0;
2166
2167 d->demosaicing_method = use_method;
2168
2169 // OpenCL only supported by some of the demosaicing methods
2170 switch(d->demosaicing_method)
2171 {
2173 piece->process_cl_ready = 1;
2174 break;
2176 piece->process_cl_ready = 0;
2177 break;
2179 piece->process_cl_ready = 1;
2180 break;
2182 piece->process_cl_ready = 1;
2183 break;
2185 piece->process_cl_ready = 1;
2186 break;
2188 piece->process_cl_ready = 1;
2189 break;
2191 piece->process_cl_ready = 0;
2192 break;
2194 piece->process_cl_ready = 1;
2195 break;
2197 piece->process_cl_ready = 0;
2198 break;
2200 piece->process_cl_ready = 1;
2201 break;
2203 piece->process_cl_ready = 1;
2204 break;
2206 piece->process_cl_ready = 1;
2207 break;
2209 piece->process_cl_ready = 1;
2210 break;
2212 piece->process_cl_ready = 0;
2213 break;
2215 piece->process_cl_ready = 1;
2216 break;
2217 default:
2218 piece->process_cl_ready = 0;
2219 }
2220
2221 // green-equilibrate over full image excludes tiling
2222 if((d->green_eq == DT_IOP_GREEN_EQ_FULL
2223 || d->green_eq == DT_IOP_GREEN_EQ_BOTH)
2224 || ((use_method & DEMOSAIC_DUAL) && (d->dual_thrs > 0.0f)))
2225 {
2226 piece->process_tiling_ready = 0;
2227 }
2228
2230 {
2231 // 4Bayer images only support the dedicated half-size downsample path in OpenCL.
2232 if(d->demosaicing_method != DT_IOP_DEMOSAIC_DOWNSAMPLE) piece->process_cl_ready = 0;
2233
2234 // Get and store the matrix to go from camera to RGB for 4Bayer images
2236 NULL, d->CAM_to_RGB,
2237 self->dev->image_storage.d65_color_matrix, NULL))
2238 {
2239 const char *camera = self->dev->image_storage.camera_makermodel;
2240 fprintf(stderr, "[colorspaces] `%s' color matrix not found for 4bayer image!\n", camera);
2241 dt_control_log(_("`%s' color matrix not found for 4bayer image!"), camera);
2242 }
2243 }
2244
2245 dt_iop_fmt_log(self, "commit: class=%s in(filters=%u ch=%i) method=%d passthrough=%d -> enabled=%d cl_ready=%d",
2247 piece->dsc_in.filters, piece->dsc_in.channels, d->demosaicing_method, passing,
2248 piece->enabled, piece->process_cl_ready);
2249}
2250
2252{
2254 piece->data_size = sizeof(dt_iop_demosaic_data_t);
2255}
2256
2258{
2259 dt_free_align(piece->data);
2260 piece->data = NULL;
2261}
2262
2264{
2266
2268 d->demosaicing_method = DT_IOP_DEMOSAIC_PASSTHROUGH_MONOCHROME;
2269 else if(module->dev->image_storage.dsc.filters == 9u)
2270 d->demosaicing_method = DT_IOP_DEMOSAIC_MARKESTEIJN;
2271 else
2272 d->demosaicing_method = DT_IOP_DEMOSAIC_RCD;
2273
2274 module->hide_enable_button = 1;
2275
2276 // Enabled iff the buffer is mosaiced (see force_enable): keeps demosaic off for
2277 // already-demosaiced sRAW / linear DNG and on for monochrome Bayer sensors.
2278 module->default_enabled = dt_image_needs_demosaic(&module->dev->image_storage);
2279 dt_iop_fmt_log(module, "reload_defaults: class=%s needs_demosaic=%d filters=%u mono=%d method=%d -> default_enabled=%d",
2282 module->dev->image_storage.dsc.filters,
2284 d->demosaicing_method, module->default_enabled);
2285 // Stack visibility (raw vs non_raw) is set from default_enabled in gui_update() (which already
2286 // does exactly this), so reload_defaults() stays params-only.
2287}
2288
2289void gui_changed(dt_iop_module_t *self, GtkWidget *w, void *previous)
2290{
2293
2294 const gboolean bayer = (self->dev->image_storage.dsc.filters != 9u);
2295 dt_iop_demosaic_method_t use_method = p->demosaicing_method;
2296 const gboolean xmethod = use_method & DEMOSAIC_XTRANS;
2297
2298 if(!_is_downsample_method(use_method))
2299 {
2300 if(bayer && xmethod) use_method = DT_IOP_DEMOSAIC_RCD;
2301 if(!bayer && !xmethod) use_method = DT_IOP_DEMOSAIC_MARKESTEIJN;
2302 }
2303
2304 const gboolean isppg = (use_method == DT_IOP_DEMOSAIC_PPG);
2305 const gboolean isdownsample = _is_downsample_method(use_method);
2306 const gboolean isdual = !isdownsample && (use_method & DEMOSAIC_DUAL);
2307 const gboolean islmmse = (use_method == DT_IOP_DEMOSAIC_LMMSE);
2308 const gboolean passing = ((use_method == DT_IOP_DEMOSAIC_PASSTHROUGH_MONOCHROME) ||
2309 (use_method == DT_IOP_DEMOSAIC_PASSTHROUGH_COLOR) ||
2310 (use_method == DT_IOP_DEMOSAIC_PASSTHR_MONOX) ||
2311 (use_method == DT_IOP_DEMOSAIC_PASSTHR_COLORX));
2312
2313 gtk_widget_set_visible(g->demosaic_method_bayer, bayer);
2314 gtk_widget_set_visible(g->demosaic_method_xtrans, !bayer);
2315 if(bayer)
2316 dt_bauhaus_combobox_set_from_value(g->demosaic_method_bayer, p->demosaicing_method);
2317 else
2318 dt_bauhaus_combobox_set_from_value(g->demosaic_method_xtrans, p->demosaicing_method);
2319
2320 gtk_widget_set_visible(g->median_thrs, bayer && isppg);
2321 gtk_widget_set_visible(g->greeneq, !passing && !isdownsample);
2322 gtk_widget_set_visible(g->color_smoothing, !passing && !isdual);
2323 gtk_widget_set_visible(g->dual_thrs, isdual);
2324 gtk_widget_set_visible(g->lmmse_refine, islmmse);
2325
2327 if((p->demosaicing_method == DT_IOP_DEMOSAIC_PASSTHROUGH_MONOCHROME) ||
2328 (p->demosaicing_method == DT_IOP_DEMOSAIC_PASSTHR_MONOX))
2330 else
2331 img->flags &= ~DT_IMAGE_MONOCHROME_BAYER;
2333}
2334void gui_update(struct dt_iop_module_t *self)
2335{
2338
2339 g->visual_mask = FALSE;
2340 gui_changed(self, NULL, NULL);
2341
2342 gtk_stack_set_visible_child_name(GTK_STACK(self->widget), self->default_enabled ? "raw" : "non_raw");
2343}
2344
2345static void _visualize_callback(GtkWidget *quad, gpointer user_data)
2346{
2347 if(dt_gui_widgets_suppressed()) return;
2348 dt_iop_module_t *self = (dt_iop_module_t *)user_data;
2350
2351 g->visual_mask = dt_bauhaus_widget_get_quad_active(quad);
2353}
2354
2355void gui_focus(struct dt_iop_module_t *self, gboolean in)
2356{
2358 if(!in)
2359 {
2360 const gboolean was_dualmask = g->visual_mask;
2362 g->visual_mask = FALSE;
2363 if(was_dualmask) dt_dev_pixelpipe_update_history_main(self->dev);
2364 }
2365}
2366
2367void gui_init(struct dt_iop_module_t *self)
2368{
2370
2371 GtkWidget *box_raw = self->widget = gtk_box_new(GTK_ORIENTATION_VERTICAL, DT_GUI_BOX_SPACING);
2372
2373 g->demosaic_method_bayer = dt_bauhaus_combobox_from_params(self, "demosaicing_method");
2374 for(int i=0;i<7;i++) dt_bauhaus_combobox_remove_at(g->demosaic_method_bayer, 9);
2375 gtk_widget_set_tooltip_text(g->demosaic_method_bayer, _("Bayer sensor demosaicing method, PPG and RCD are fast, AMaZE and LMMSE are slow.\nLMMSE is suited best for high ISO images.\ndual demosaicers double processing time."));
2376
2377 g->demosaic_method_xtrans = dt_bauhaus_combobox_from_params(self, "demosaicing_method");
2378 for(int i=0;i<9;i++) dt_bauhaus_combobox_remove_at(g->demosaic_method_xtrans, 0);
2379 gtk_widget_set_tooltip_text(g->demosaic_method_xtrans, _("X-Trans sensor demosaicing method, Markesteijn 3-pass and frequency domain chroma are slow.\ndual demosaicers double processing time."));
2380
2381 g->median_thrs = dt_bauhaus_slider_from_params(self, "median_thrs");
2382 dt_bauhaus_slider_set_digits(g->median_thrs, 3);
2383 gtk_widget_set_tooltip_text(g->median_thrs, _("threshold for edge-aware median.\nset to 0.0 to switch off\n"
2384 "set to 1.0 to ignore edges"));
2385
2386 g->dual_thrs = dt_bauhaus_slider_from_params(self, "dual_thrs");
2387 dt_bauhaus_slider_set_digits(g->dual_thrs, 2);
2388 gtk_widget_set_tooltip_text(g->dual_thrs, _("contrast threshold for dual demosaic.\nset to 0.0 for high frequency content\n"
2389 "set to 1.0 for flat content\ntoggle to visualize the mask"));
2393 g_signal_connect(G_OBJECT(g->dual_thrs), "quad-pressed", G_CALLBACK(_visualize_callback), self);
2394
2395 g->lmmse_refine = dt_bauhaus_combobox_from_params(self, "lmmse_refine");
2396 gtk_widget_set_tooltip_text(g->lmmse_refine, _("LMMSE refinement steps. the median steps average the output,\nrefine adds some recalculation of red & blue channels"));
2397
2398 g->color_smoothing = dt_bauhaus_combobox_from_params(self, "color_smoothing");
2399 gtk_widget_set_tooltip_text(g->color_smoothing, _("how many post-demosaic smoothing passes.\nin downsample mode this sets the guided detail equalization iterations"));
2400
2401 g->greeneq = dt_bauhaus_combobox_from_params(self, "green_eq");
2402 gtk_widget_set_tooltip_text(g->greeneq, _("green channels matching method"));
2403
2404 // start building top level widget
2405 self->widget = gtk_stack_new();
2406 gtk_stack_set_homogeneous(GTK_STACK(self->widget), FALSE);
2407
2408 GtkWidget *label_non_raw = dt_ui_label_new(_("not applicable"));
2409 gtk_widget_set_tooltip_text(label_non_raw, _("demosaicing is only used for color raw images"));
2410
2411 gtk_stack_add_named(GTK_STACK(self->widget), label_non_raw, "non_raw");
2412 gtk_stack_add_named(GTK_STACK(self->widget), box_raw, "raw");
2413}
2414
2415// clang-format off
2416// modelines: These editor modelines have been set for all relevant files by tools/update_modelines.py
2417// vim: shiftwidth=2 expandtab tabstop=2 cindent
2418// kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-spaces modified;
2419// 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
void cleanup(dt_imageio_module_format_t *self)
Definition avif.c:164
static __DT_CLONE_TARGETS__ void green_equilibration_lavg(float *out, const float *const in, const int width, const int height, const uint32_t filters, const int x, const int y, const float thr)
Definition basic.c:248
static int green_equilibration_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)
Definition basic.c:400
static __DT_CLONE_TARGETS__ void green_equilibration_favg(float *out, const float *const in, const int width, const int height, const uint32_t filters, const int x, const int y)
Definition basic.c:296
static __DT_CLONE_TARGETS__ void color_smoothing(float *out, const dt_iop_roi_t *const roi_out, const int num_passes)
Definition basic.c:192
static int color_smoothing_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_out, const int passes)
Definition basic.c:334
void dt_bauhaus_slider_set_digits(GtkWidget *widget, int val)
Definition bauhaus.c:3549
gboolean dt_bauhaus_combobox_set_from_value(GtkWidget *widget, int value)
Definition bauhaus.c:2333
void dt_bauhaus_widget_set_quad_toggle(GtkWidget *widget, int toggle)
Definition bauhaus.c:1723
void dt_bauhaus_widget_set_quad_active(GtkWidget *widget, int active)
Definition bauhaus.c:1729
int dt_bauhaus_widget_get_quad_active(GtkWidget *widget)
Definition bauhaus.c:1746
void dt_bauhaus_combobox_remove_at(GtkWidget *widget, int pos)
Definition bauhaus.c:2104
void dt_bauhaus_widget_set_quad_paint(GtkWidget *widget, dt_bauhaus_quad_paint_f f, int paint_flags, void *paint_data)
Definition bauhaus.c:1705
int width
Definition bilateral.h:1
int height
Definition bilateral.h:1
static void blur_2D_Bspline(const float *const restrict in, float *const restrict out, float *const restrict tempbuf, const size_t width, const size_t height, const int mult, const gboolean clip_negatives)
Definition bspline.h:325
#define BSPLINE_FSIZE
Definition bspline.h:30
static void decompose_2D_Bspline(const float *const restrict in, float *const restrict HF, float *const restrict LF, const size_t width, const size_t height, const int mult, float *const tempbuf, size_t padded_size)
Definition bspline.h:347
static float intp(const float a, const float b, const float c)
Definition cacorrect.c:180
static const dt_aligned_pixel_simd_t const dt_adaptation_t const float p
return vector dt_simd_set1(valid ?(scaling+NORM_MIN) :NORM_MIN)
@ IOP_CS_RAW
@ IOP_CS_RGB
__DT_CLONE_TARGETS__ int dt_colorspaces_conversion_matrices_rgb(const float adobe_XYZ_to_CAM[4][3], double out_RGB_to_CAM[4][3], double out_CAM_to_RGB[3][4], const float *embedded_matrix, double mul[4])
__DT_CLONE_TARGETS__ void dt_colorspaces_cygm_to_rgb(float *out, int num, double CAM_to_RGB[3][4])
static dt_aligned_pixel_t rgb
const float threshold
const dt_colormatrix_t dt_aligned_pixel_t out
dt_store_simd_aligned(out, dt_mat3x4_mul_vec4(vin, dt_colormatrix_row_to_simd(matrix, 0), dt_colormatrix_row_to_simd(matrix, 1), dt_colormatrix_row_to_simd(matrix, 2)))
const float top
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_is_monochrome(const dt_image_t *img)
gboolean dt_image_needs_demosaic(const dt_image_t *img)
int type
void dt_control_log(const char *msg,...)
Definition control.c:777
void reset(dt_view_t *self)
Definition darkroom.c:1062
darktable_t darktable
Definition darktable.c:183
void dt_print(dt_debug_thread_t thread, const char *msg,...)
Definition darktable.c:1600
static const dt_aligned_pixel_simd_t const dt_aligned_pixel_simd_t row1
Definition darktable.h:645
#define dt_free_align(ptr)
Definition darktable.h:503
static void * dt_calloc_align(size_t size)
Definition darktable.h:510
@ DT_DEBUG_OPENCL
Definition darktable.h:744
@ DT_DEBUG_DEMOSAIC
Definition darktable.h:759
@ DT_DEBUG_PERF
Definition darktable.h:741
#define dt_pixelpipe_cache_alloc_align_float_cache(pixels, id)
Definition darktable.h:459
float dt_aligned_pixel_simd_t __attribute__((vector_size(16), aligned(16)))
Enable aggressive floating-point arithmetic optimizations, in denormals handling. Set through user pr...
Definition darktable.h:546
#define dt_free(ptr)
Definition darktable.h:478
void void void gboolean dt_gui_widgets_suppressed(void)
Definition gtk.c:194
static void dt_get_times(dt_times_t *t)
Definition darktable.h:983
#define DT_MODULE_INTROSPECTION(MODVER, PARAMSTYPE)
Definition darktable.h:151
#define dt_pixelpipe_cache_free_align(mem)
Definition darktable.h:475
#define dt_pixelpipe_cache_alloc_align_float(pixels, pipe)
Definition darktable.h:454
#define __DT_CLONE_TARGETS__
Definition darktable.h:379
static const dt_aligned_pixel_simd_t const dt_aligned_pixel_simd_t const dt_aligned_pixel_simd_t row2
Definition darktable.h:646
#define __OMP_PARALLEL_FOR__(...)
Definition darktable.h:270
#define dt_pixelpipe_cache_alloc_perthread_float(n, padded_size)
Definition darktable.h:1092
static const dt_aligned_pixel_simd_t row0
Definition darktable.h:644
#define IS_NULL_PTR(p)
C is way too permissive with !=, == and if(var) checks, which can mean too many things depending on w...
Definition darktable.h:293
#define BLUE
#define RED
#define GREEN
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 ALPHA
dt_iop_demosaic_method_t
Definition demosaic.c:113
@ DT_IOP_DEMOSAIC_MARKESTEIJN_3
Definition demosaic.c:127
@ DT_IOP_DEMOSAIC_RCD
Definition demosaic.c:118
@ DT_IOP_DEMOSAIC_RCD_VNG
Definition demosaic.c:120
@ DT_IOP_DEMOSAIC_MARKESTEIJN
Definition demosaic.c:126
@ DT_IOP_DEMOSAIC_PASSTHROUGH_COLOR
Definition demosaic.c:123
@ DT_IOP_DEMOSAIC_PASSTHR_MONOX
Definition demosaic.c:130
@ DT_IOP_DEMOSAIC_VNG4
Definition demosaic.c:117
@ DT_IOP_DEMOSAIC_PPG
Definition demosaic.c:115
@ DT_IOP_DEMOSAIC_MARKEST3_VNG
Definition demosaic.c:129
@ DT_IOP_DEMOSAIC_LMMSE
Definition demosaic.c:119
@ DT_IOP_DEMOSAIC_VNG
Definition demosaic.c:125
@ DT_IOP_DEMOSAIC_AMAZE_VNG
Definition demosaic.c:121
@ DT_IOP_DEMOSAIC_DOWNSAMPLE
Definition demosaic.c:132
@ DT_IOP_DEMOSAIC_PASSTHR_COLORX
Definition demosaic.c:131
@ DT_IOP_DEMOSAIC_FDC
Definition demosaic.c:128
@ DT_IOP_DEMOSAIC_PASSTHROUGH_MONOCHROME
Definition demosaic.c:122
@ DT_IOP_DEMOSAIC_AMAZE
Definition demosaic.c:116
void distort_mask(struct dt_iop_module_t *self, const struct dt_dev_pixelpipe_t *pipe, struct dt_dev_pixelpipe_iop_t *piece, const float *const in, float *const out, const dt_iop_roi_t *const roi_in, const dt_iop_roi_t *const roi_out)
Definition demosaic.c:919
static int process_default_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, const int demosaicing_method)
Definition demosaic.c:1200
const char ** description(struct dt_iop_module_t *self)
Definition demosaic.c:307
int default_group()
Definition demosaic.c:316
void modify_roi_out(struct dt_iop_module_t *self, const struct dt_dev_pixelpipe_t *pipe, struct dt_dev_pixelpipe_iop_t *piece, dt_iop_roi_t *roi_out, const dt_iop_roi_t *const roi_in)
Definition demosaic.c:928
static gboolean _downsample_guided_laplacian_postfilter_cl(struct dt_iop_module_t *self, const dt_dev_pixelpipe_t *pipe, cl_mem dev_out, const dt_iop_roi_t *const roi_out, const int iterations)
Definition demosaic.c:1385
static __DT_CLONE_TARGETS__ void _downsample_bayer_half_size(float *const out, const float *const in, const dt_iop_roi_t *const roi_out, const dt_iop_roi_t *const roi_in, const uint32_t filters, const gboolean is_4bayer, const double CAM_to_RGB[3][4])
Build one half-size RGB pixel from the 2x2 CFA block backing it.
Definition demosaic.c:472
void reload_defaults(dt_iop_module_t *module)
Definition demosaic.c:2263
void amaze_demosaic_RT(const dt_dev_pixelpipe_iop_t *piece, const float *const in, float *out, const dt_iop_roi_t *const roi_in, const dt_iop_roi_t *const roi_out, const uint32_t filters)
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 demosaic.c:2119
dt_iop_demosaic_smooth_t
Definition demosaic.c:145
@ DEMOSAIC_SMOOTH_2
Definition demosaic.c:148
@ DEMOSAIC_SMOOTH_3
Definition demosaic.c:149
@ DEMOSAIC_SMOOTH_1
Definition demosaic.c:147
@ DEMOSAIC_SMOOTH_OFF
Definition demosaic.c:146
@ DEMOSAIC_SMOOTH_4
Definition demosaic.c:150
@ DEMOSAIC_SMOOTH_5
Definition demosaic.c:151
static __DT_CLONE_TARGETS__ void _downsample_xtrans_half_size(float *const out, const float *const in, const dt_iop_roi_t *const roi_out, const dt_iop_roi_t *const roi_in, const uint8_t(*const xtrans)[6])
Build one half-size RGB pixel from a 2x2 X-Trans block.
Definition demosaic.c:616
dt_iop_demosaic_lmmse_t
Definition demosaic.c:155
@ LMMSE_REFINE_0
Definition demosaic.c:156
@ LMMSE_REFINE_3
Definition demosaic.c:159
@ LMMSE_REFINE_4
Definition demosaic.c:160
@ LMMSE_REFINE_2
Definition demosaic.c:158
@ LMMSE_REFINE_1
Definition demosaic.c:157
#define DEMOSAIC_DUAL
Definition demosaic.c:101
void gui_focus(struct dt_iop_module_t *self, gboolean in)
Definition demosaic.c:2355
void init_pipe(struct dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece)
Definition demosaic.c:2251
static __DT_CLONE_TARGETS__ int _downsample_guided_laplacian_postfilter(float *const out, const size_t width, const size_t height, const int iterations)
Denoise the half-size demosaic result by filtering its wavelet details.
Definition demosaic.c:801
dt_iop_demosaic_greeneq_t
Definition demosaic.c:136
@ DT_IOP_GREEN_EQ_LOCAL
Definition demosaic.c:138
@ DT_IOP_GREEN_EQ_FULL
Definition demosaic.c:139
@ DT_IOP_GREEN_EQ_BOTH
Definition demosaic.c:140
@ DT_IOP_GREEN_EQ_NO
Definition demosaic.c:137
const char * name()
Definition demosaic.c:302
void output_format(dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece, dt_iop_buffer_dsc_t *dsc)
Definition demosaic.c:375
void gui_update(struct dt_iop_module_t *self)
Definition demosaic.c:2334
#define DEMOSAIC_XTRANS
Definition demosaic.c:100
void gui_init(struct dt_iop_module_t *self)
Definition demosaic.c:2367
static gboolean _is_downsample_method(const dt_iop_demosaic_method_t method)
Definition demosaic.c:446
void gui_changed(dt_iop_module_t *self, GtkWidget *w, void *previous)
Definition demosaic.c:2289
dt_iop_demosaic_quality_t
Definition demosaic.c:251
@ DT_DEMOSAIC_BEST
Definition demosaic.c:254
@ DT_DEMOSAIC_FAIR
Definition demosaic.c:253
@ DT_DEMOSAIC_FAST
Definition demosaic.c:252
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)
Definition demosaic.c:1860
static __DT_CLONE_TARGETS__ void _downsample_guided_laplacian_apply(const float *const restrict HF, const float *const restrict coeff, const float *const restrict bias, const float *const restrict LF, float *const restrict reconstructed, const size_t width, const size_t height, const gboolean reset)
Apply the locally averaged affine RGB model to one high-frequency layer.
Definition demosaic.c:761
void cleanup_global(dt_iop_module_so_t *module)
Definition demosaic.c:2038
static __DT_CLONE_TARGETS__ void _downsample_guided_laplacian_fit(const float *const restrict HF, float *const restrict coeff, float *const restrict bias, const size_t width, const size_t height)
Fit one local affine RGB model for the current high-frequency scale.
Definition demosaic.c:672
int default_colorspace(dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, const dt_dev_pixelpipe_iop_t *piece)
Definition demosaic.c:326
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 demosaic.c:367
int flags()
Definition demosaic.c:321
static __DT_CLONE_TARGETS__ float _downsample_xtrans_missing_colour(const float *const in, const dt_iop_roi_t *const roi_in, const int px, const int py, const uint8_t(*const xtrans)[6], const int colour)
Reconstruct one missing X-Trans colour from the nearest surrounding photosites.
Definition demosaic.c:534
#define DOWNSAMPLE_GUIDED_SCALES
Definition demosaic.c:108
__DT_CLONE_TARGETS__ int process(struct dt_iop_module_t *self, const dt_dev_pixelpipe_t *pipe, const dt_dev_pixelpipe_iop_t *piece, const void *const i, void *const o)
Definition demosaic.c:987
gboolean force_enable(struct dt_iop_module_t *self, const gboolean current_state)
Definition demosaic.c:2105
void cleanup_pipe(struct dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece)
Definition demosaic.c:2257
static void _visualize_callback(GtkWidget *quad, gpointer user_data)
Definition demosaic.c:2345
void init_global(dt_iop_module_so_t *module)
Definition demosaic.c:1960
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)
Definition demosaic.c:1635
void modify_roi_in(struct dt_iop_module_t *self, const struct dt_dev_pixelpipe_t *pipe, struct dt_dev_pixelpipe_iop_t *piece, const dt_iop_roi_t *roi_out, dt_iop_roi_t *roi_in)
Definition demosaic.c:949
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 demosaic.c:331
#define XTRANS_SNAPPER
Definition demosaic.c:107
void dt_iop_params_t
Definition dev_history.h:42
#define dt_dev_pixelpipe_update_history_main(dev)
@ DT_DEV_PIXELPIPE_DISPLAY_PASSTHRU_MONO
Definition develop.h:137
@ DT_DEV_PIXELPIPE_DISPLAY_PASSTHRU
Definition develop.h:136
void dtgtk_cairo_paint_showmask(cairo_t *cr, gint x, gint y, gint w, gint h, gint flags, void *data)
static __DT_CLONE_TARGETS__ int dual_demosaic(const dt_dev_pixelpipe_t *pipe, const dt_dev_pixelpipe_iop_t *piece, float *const restrict rgb_data, const float *const restrict raw_data, dt_iop_roi_t *const roi_out, const dt_iop_roi_t *const roi_in, const uint32_t filters, const uint8_t(*const xtrans)[6], const gboolean dual_mask, float dual_threshold)
Definition dual.c:39
gboolean dual_demosaic_cl(struct dt_iop_module_t *self, const dt_dev_pixelpipe_t *pipe, const dt_dev_pixelpipe_iop_t *piece, cl_mem detail, cl_mem blend, cl_mem high_image, cl_mem low_image, cl_mem out, const int width, const int height, const int showmask)
Definition dual.c:115
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
@ TYPE_FLOAT
Definition format.h:46
#define DT_GUI_BOX_SPACING
Definition gtk.h:109
static GtkWidget * dt_ui_label_new(const gchar *str)
Definition gtk.h:479
@ DT_IMAGE_MONOCHROME_BAYER
Definition image.h:138
@ DT_IMAGE_4BAYER
Definition image.h:127
dt_image_t * dt_image_cache_get(dt_image_cache_t *cache, const int32_t imgid, char mode)
void dt_image_cache_write_release(dt_image_cache_t *cache, dt_image_t *img, dt_image_cache_write_mode_t mode)
@ DT_IMAGE_CACHE_RELAXED
Definition image_cache.h:51
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
uint32_t dt_dev_get_roi_filters(const dt_dev_pixelpipe_iop_t *const piece, const dt_iop_roi_t *const roi_in)
Definition imageop.c:136
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 dt_omploop_sfence()
Definition imageop.h:747
#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_ALLOW_TILING
Definition imageop.h:199
@ IOP_FLAGS_ONE_INSTANCE
Definition imageop.h:202
@ IOP_GROUP_TECHNICAL
Definition imageop.h:173
#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
GtkWidget * dt_bauhaus_combobox_from_params(dt_iop_module_t *self, const char *param)
int dt_iop_clip_and_zoom_roi_cl(int devid, cl_mem dev_out, cl_mem dev_in, const dt_iop_roi_t *const roi_out, const dt_iop_roi_t *const roi_in)
const struct dt_interpolation * dt_interpolation_new(enum dt_interpolation_type type)
void dt_interpolation_resample_roi_1c(const struct dt_interpolation *itor, float *out, const dt_iop_roi_t *const roi_out, const float *const in, const dt_iop_roi_t *const roi_in)
@ DT_INTERPOLATION_USERPREF
static const float x
const float *const const float coeff[3]
#define LMMSE_GRP
Definition lmmse.c:52
static void lmmse_demosaic(const dt_dev_pixelpipe_iop_t *piece, float *const restrict out, const float *const restrict in, dt_iop_roi_t *const roi_out, const dt_iop_roi_t *const roi_in, const uint32_t filters, const uint32_t mode, float *const restrict gamma_in, float *const restrict gamma_out)
Definition lmmse.c:129
float *const restrict const size_t k
static int process_markesteijn_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, const gboolean smooth)
static __DT_CLONE_TARGETS__ void xtrans_markesteijn_interpolate(float *out, const float *const in, const dt_iop_roi_t *const roi_out, const dt_iop_roi_t *const roi_in, const uint8_t(*const xtrans)[6], const int passes)
Definition markesteijn.c:47
static __DT_CLONE_TARGETS__ void xtrans_fdc_interpolate(struct dt_iop_module_t *self, float *out, const float *const in, const dt_iop_roi_t *const roi_out, const dt_iop_roi_t *const roi_in, const uint8_t(*const xtrans)[6])
float dt_aligned_pixel_t[4]
int dt_opencl_local_buffer_opt(const int devid, const int kernel, dt_opencl_local_buffer_t *factors)
Definition opencl.c:3286
int dt_opencl_enqueue_kernel_2d(const int dev, const int kernel, const size_t *sizes)
Definition opencl.c:2164
void * dt_opencl_alloc_device_buffer(const int devid, const size_t size)
Definition opencl.c:2580
void * dt_opencl_alloc_device(const int devid, const int width, const int height, const int bpp)
Definition opencl.c:2504
int dt_opencl_create_kernel(const int prog, const char *name)
Definition opencl.c:2058
void * dt_opencl_copy_host_to_device_constant(const int devid, const size_t size, void *host)
Definition opencl.c:2360
void dt_opencl_free_kernel(const int kernel)
Definition opencl.c:2101
int dt_opencl_set_kernel_arg(const int dev, const int kernel, const int num, const size_t size, const void *arg)
Definition opencl.c:2155
int dt_opencl_enqueue_kernel_2d_with_local(const int dev, const int kernel, const size_t *sizes, const size_t *local)
Definition opencl.c:2170
void dt_opencl_release_mem_object(cl_mem mem)
Definition opencl.c:2415
#define DT_OPENCL_DEFAULT_ERROR
Definition opencl.h:57
#define ROUNDUP(a, n)
Definition opencl.h:78
#define ROUNDUPDHT(a, b)
Definition opencl.h:82
#define ROUNDUPDWD(a, b)
Definition opencl.h:81
static __DT_CLONE_TARGETS__ void passthrough_monochrome(float *out, const float *const in, dt_iop_roi_t *const roi_out, const dt_iop_roi_t *const roi_in)
Definition passthrough.c:22
static __DT_CLONE_TARGETS__ void passthrough_color(float *out, const float *const in, dt_iop_roi_t *const roi_out, const dt_iop_roi_t *const roi_in, const uint32_t filters, const uint8_t(*const xtrans)[6])
Definition passthrough.c:44
dt_dev_pixelpipe_type_t
Definition pixelpipe.h:36
@ DT_DEV_PIXELPIPE_THUMBNAIL
Definition pixelpipe.h:41
@ DT_DEV_PIXELPIPE_EXPORT
Definition pixelpipe.h:38
@ DT_DEV_PIXELPIPE_PREVIEW
Definition pixelpipe.h:40
@ DT_DEV_PIXELPIPE_FULL
Definition pixelpipe.h:39
static __DT_CLONE_TARGETS__ int demosaic_ppg(float *const out, const float *const in, const dt_iop_roi_t *const roi_out, const dt_iop_roi_t *const roi_in, const uint32_t filters, const float thrs)
Definition ppg.c:22
static int process_rcd_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, const gboolean smooth)
Definition rcd.c:568
#define RCD_TILESIZE
Definition rcd.c:54
#define eps
Definition rcd.c:81
static void rcd_demosaic(const dt_dev_pixelpipe_iop_t *piece, float *const restrict out, const float *const restrict in, dt_iop_roi_t *const roi_out, const dt_iop_roi_t *const roi_in, const uint32_t filters)
Definition rcd.c:274
struct _GtkWidget GtkWidget
Definition splash.h:29
const float uint32_t state[4]
int32_t num_openmp_threads
Definition darktable.h:786
int32_t unmuted
Definition darktable.h:788
struct dt_image_cache_t * image_cache
Definition darktable.h:805
dt_iop_buffer_dsc_t dsc_in
struct dt_iop_module_t *void * data
dt_dev_pixelpipe_type_t type
int32_t gui_attached
Definition develop.h:162
dt_image_t image_storage
Definition develop.h:259
char camera_makermodel[128]
Definition image.h:300
float exif_iso
Definition image.h:288
int32_t flags
Definition image.h:319
float d65_color_matrix[9]
Definition image.h:339
dt_iop_buffer_dsc_t dsc
Definition image.h:337
float adobe_XYZ_to_CAM[4][3]
Definition image.h:362
int32_t id
Definition image.h:319
uint32_t filters
Definition format.h:60
unsigned int channels
Definition format.h:54
uint8_t xtrans[6][6]
Definition format.h:70
dt_iop_buffer_type_t datatype
Definition format.h:56
dt_aligned_pixel_t processed_maximum
Definition format.h:85
uint32_t demosaicing_method
Definition demosaic.c:233
double CAM_to_RGB[3][4]
Definition demosaic.c:236
GtkWidget * demosaic_method_xtrans
Definition demosaic.c:274
GtkWidget * color_smoothing
Definition demosaic.c:272
GtkWidget * demosaic_method_bayer
Definition demosaic.c:273
dt_iop_demosaic_method_t demosaicing_method
Definition demosaic.c:263
dt_iop_demosaic_greeneq_t green_eq
Definition demosaic.c:260
dt_iop_demosaic_smooth_t color_smoothing
Definition demosaic.c:262
dt_iop_demosaic_lmmse_t lmmse_refine
Definition demosaic.c:264
dt_iop_global_data_t * data
Definition imageop.h:263
dt_iop_params_t * default_params
Definition imageop.h:344
GtkWidget * widget
Definition imageop.h:374
struct dt_develop_t * dev
Definition imageop.h:333
dt_iop_gui_data_t * gui_data
Definition imageop.h:348
gboolean default_enabled
Definition imageop.h:340
dt_iop_global_data_t * global_data
Definition imageop.h:351
dt_iop_params_t * params
Definition imageop.h:344
Region of interest passed through the pixelpipe.
Definition imageop.h:72
double scale
Definition imageop.h:74
double clock
Definition darktable.h:870
double user
Definition darktable.h:871
GQueue * log
Definition supervisor.c:117
typedef double((*spd)(unsigned long int wavelength, double TempK))
#define MIN(a, b)
Definition thinplate.c:32
#define MAX(a, b)
Definition thinplate.c:29
static int process_vng_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, const gboolean smooth, const int only_vng_linear)
Definition vng.c:206
static __DT_CLONE_TARGETS__ int vng_interpolate(float *out, const float *const in, const dt_iop_roi_t *const roi_out, const dt_iop_roi_t *const roi_in, const uint32_t filters, const uint8_t(*const xtrans)[6], const int only_vng_linear)
Definition vng.c:34