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-2026 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 "system/macros.h"
62#include "system/openmp.h"
64#include "system/mem_alloc.h"
65#include "system/simd.h"
66#include "common/logging.h"
67#include "common/times.h"
69#include "system/fp_mode.h"
71#include "common/imagebuf.h"
72#include "caches/image_cache.h"
73#include "pixel/interpolation.h"
74#include "math/math.h"
75#include "common/opencl.h"
77#include "develop/blend.h"
78#include "develop/develop.h"
79#include "pixel/format.h"
81#include "develop/imageop.h"
83#include "develop/imageop_gui.h"
85#include "math/openmp_maths.h"
86#include "develop/tiling.h"
87
88#include "widgets/bauhaus.h"
91#include "pixel/bspline.h"
92
93#include "iop/iop_api.h"
94
95#include <memory.h>
96#include <stdlib.h>
97#include <string.h>
98#include <time.h>
99#include <complex.h>
100#include <glib.h>
101#include "widgets/label.h"
102
103#ifdef __GNUC__
104 #define INLINE __inline
105#else
106 #define INLINE inline
107#endif
108
109#define DEMOSAIC_XTRANS 1024 // masks for non-Bayer demosaic ops
110#define DEMOSAIC_DUAL 2048 // masks for dual demosaicing methods
111#define REDUCESIZE 64
112
113// X-Trans CFA tile period, still needed to align tile boundaries in tiling_callback()
114// (unrelated to the ROI crop position, which no longer needs to be pattern-aligned:
115// process() rebuilds the exact phase for any roi_in->x/y, see the top of process()).
116#define XTRANS_SNAPPER 6
117#define DOWNSAMPLE_GUIDED_SCALES 1
118
120
122{
123 // methods for Bayer images
124 DT_IOP_DEMOSAIC_PPG = 0, // $DESCRIPTION: "PPG"
125 DT_IOP_DEMOSAIC_AMAZE = 1, // $DESCRIPTION: "AMaZE"
126 DT_IOP_DEMOSAIC_VNG4 = 2, // $DESCRIPTION: "VNG4"
127 DT_IOP_DEMOSAIC_RCD = 5, // $DESCRIPTION: "RCD"
128 DT_IOP_DEMOSAIC_LMMSE = 6, // $DESCRIPTION: "LMMSE"
131 DT_IOP_DEMOSAIC_PASSTHROUGH_MONOCHROME = 3, // $DESCRIPTION: "passthrough (monochrome)"
132 DT_IOP_DEMOSAIC_PASSTHROUGH_COLOR = 4, // $DESCRIPTION: "photosite color (debug)"
133 // methods for x-trans images
134 DT_IOP_DEMOSAIC_VNG = DEMOSAIC_XTRANS | 0, // $DESCRIPTION: "VNG"
135 DT_IOP_DEMOSAIC_MARKESTEIJN = DEMOSAIC_XTRANS | 1, // $DESCRIPTION: "Markesteijn 1-pass"
136 DT_IOP_DEMOSAIC_MARKESTEIJN_3 = DEMOSAIC_XTRANS | 2, // $DESCRIPTION: "Markesteijn 3-pass"
137 DT_IOP_DEMOSAIC_FDC = DEMOSAIC_XTRANS | 4, // $DESCRIPTION: "frequency domain chroma"
138 DT_IOP_DEMOSAIC_MARKEST3_VNG = DEMOSAIC_DUAL | DT_IOP_DEMOSAIC_MARKESTEIJN_3, // $DESCRIPTION: "Markesteijn 3-pass + VNG"
139 DT_IOP_DEMOSAIC_PASSTHR_MONOX = DEMOSAIC_XTRANS | 3, // $DESCRIPTION: "passthrough (monochrome)"
140 DT_IOP_DEMOSAIC_PASSTHR_COLORX = DEMOSAIC_XTRANS | 5, // $DESCRIPTION: "photosite color (debug)"
141 DT_IOP_DEMOSAIC_DOWNSAMPLE = 7, // $DESCRIPTION: "downsample"
143
145{
146 DT_IOP_GREEN_EQ_NO = 0, // $DESCRIPTION: "disabled"
147 DT_IOP_GREEN_EQ_LOCAL = 1, // $DESCRIPTION: "local average"
148 DT_IOP_GREEN_EQ_FULL = 2, // $DESCRIPTION: "full average"
149 DT_IOP_GREEN_EQ_BOTH = 3 // $DESCRIPTION: "full and local average"
151
152
154{
155 DEMOSAIC_SMOOTH_OFF = 0, // $DESCRIPTION: "disabled"
156 DEMOSAIC_SMOOTH_1 = 1, // $DESCRIPTION: "once"
157 DEMOSAIC_SMOOTH_2 = 2, // $DESCRIPTION: "twice"
158 DEMOSAIC_SMOOTH_3 = 3, // $DESCRIPTION: "three times"
159 DEMOSAIC_SMOOTH_4 = 4, // $DESCRIPTION: "four times"
160 DEMOSAIC_SMOOTH_5 = 5, // $DESCRIPTION: "five times"
162
164{
165 LMMSE_REFINE_0 = 0, // $DESCRIPTION: "basic"
166 LMMSE_REFINE_1 = 1, // $DESCRIPTION: "median"
167 LMMSE_REFINE_2 = 2, // $DESCRIPTION: "3x median"
168 LMMSE_REFINE_3 = 3, // $DESCRIPTION: "refine & medians"
169 LMMSE_REFINE_4 = 4, // $DESCRIPTION: "2x refine + medians"
171
173{
174 // demosaic pattern
236
237
248
249
250static inline __attribute__((always_inline)) float intp(float a, float b, float c)
251{ // taken from rt code
252 // calculate a * b + (1 - a) * c
253 // following is valid:
254 // intp(a, b+x, c+x) = intp(a, b, c) + x
255 // intp(a, b*x, c*x) = intp(a, b, c) * x
256 return a * (b - c) + c;
257}
258
265
266
268{
269 dt_iop_demosaic_greeneq_t green_eq; // $DEFAULT: DT_IOP_GREEN_EQ_NO $DESCRIPTION: "match greens"
270 float median_thrs; // $MIN: 0.0 $MAX: 1.0 $DEFAULT: 0.0 $DESCRIPTION: "edge threshold"
271 dt_iop_demosaic_smooth_t color_smoothing; // $DEFAULT: DEMOSAIC_SMOOTH_OFF $DESCRIPTION: "color smoothing"
272 dt_iop_demosaic_method_t demosaicing_method; // $DEFAULT: DT_IOP_DEMOSAIC_RCD $DESCRIPTION: "demosaicing method"
273 dt_iop_demosaic_lmmse_t lmmse_refine; // $DEFAULT: LMMSE_REFINE_1 $DESCRIPTION: "LMMSE refine"
274 float dual_thrs; // $MIN: 0.0 $MAX: 1.0 $DEFAULT: 0.20 $DESCRIPTION: "dual threshold"
276
288
289// Implemented on amaze_demosaic_RT.cc
292 const float *const in,
293 float *out,
294 const dt_iop_roi_t *const roi_in,
295 const dt_iop_roi_t *const roi_out,
296 const uint32_t filters);
297
298
299// Mind the order of includes, there are internal dependencies
300// FIXME: handle all the branching uniformingly
301#include "demosaic/basic.c"
302#include "demosaic/passthrough.c"
303#include "demosaic/rcd.c"
304#include "demosaic/lmmse.c"
305#include "demosaic/ppg.c"
306#include "demosaic/vng.c"
307#include "demosaic/markesteijn.c"
308#include "demosaic/dual.c"
309
310
311const char *name()
312{
313 return _("demosaic");
314}
315
316const char **description(struct dt_iop_module_t *self)
317{
318 return dt_iop_set_description(self, _("reconstruct full RGB pixels from a sensor color filter array reading"),
319 _("mandatory"),
320 _("linear, raw, scene-referred"),
321 _("linear, raw"),
322 _("linear, RGB, scene-referred"));
323}
324
326{
327 return IOP_GROUP_TECHNICAL;
328}
329
334
339
340int legacy_params(dt_iop_module_t *self, const void *const old_params, const int old_version,
341 void *new_params, const int new_version)
342{
343 typedef struct dt_iop_demosaic_params_t dt_iop_demosaic_params_v4_t;
344 typedef struct dt_iop_demosaic_params_v3_t
345 {
347 float median_thrs;
348 uint32_t color_smoothing;
349 dt_iop_demosaic_method_t demosaicing_method;
350 dt_iop_demosaic_lmmse_t lmmse_refine;
351 } dt_iop_demosaic_params_v3_t;
352
353 if(old_version == 3 && new_version == 4)
354 {
355 dt_iop_demosaic_params_v3_t *o = (dt_iop_demosaic_params_v3_t *)old_params;
356 dt_iop_demosaic_params_v4_t *n = (dt_iop_demosaic_params_v4_t *)new_params;
357 memcpy(n, o, sizeof *o);
358 n->dual_thrs = 0.20f;
359 return 0;
360 }
361
362 if(old_version == 2 && new_version == 3)
363 {
366 n->green_eq = o->green_eq;
367 n->median_thrs = o->median_thrs;
368 n->color_smoothing = 0;
369 n->demosaicing_method = DT_IOP_DEMOSAIC_PPG;
370 n->lmmse_refine = LMMSE_REFINE_1;
371 return 0;
372 }
373 return 1;
374}
375
383
391
392static inline __attribute__((always_inline)) const char* method2string(dt_iop_demosaic_method_t method)
393{
394 const char *string;
395
396 switch(method)
397 {
399 string = "PPG";
400 break;
402 string = "AMaZE";
403 break;
405 string = "VNG4";
406 break;
408 string = "passthrough monochrome";
409 break;
411 string = "photosites";
412 break;
414 string = "RCD";
415 break;
417 string = "LMMSE";
418 break;
420 string = "RCD + VNG4";
421 break;
423 string = "AMaZE + VNG4";
424 break;
426 string = "VNG (xtrans)";
427 break;
429 string = "Markesteijn-1 (XTrans)";
430 break;
432 string = "Markesteijn-3 (XTrans)";
433 break;
435 string = "Markesteijn 3-pass + VNG";
436 break;
438 string = "Frequency Domain Chroma (XTrans)";
439 break;
441 string = "passthrough monochrome (XTrans)";
442 break;
444 string = "photosites (XTrans)";
445 break;
447 string = "downsample";
448 break;
449 default:
450 string = "(unknown method)";
451 }
452 return string;
453}
454
455static inline gboolean _is_downsample_method(const dt_iop_demosaic_method_t method)
456{
457 return method == DT_IOP_DEMOSAIC_DOWNSAMPLE;
458}
459
460static inline __attribute__((always_inline)) const char *_pipe_type_string(dt_dev_pixelpipe_type_t type)
461{
462 switch(type)
463 {
464 case DT_DEV_PIXELPIPE_EXPORT: return "export";
465 case DT_DEV_PIXELPIPE_FULL: return "full";
466 case DT_DEV_PIXELPIPE_PREVIEW: return "preview";
467 case DT_DEV_PIXELPIPE_THUMBNAIL: return "thumbnail";
468 default: return "none";
469 }
470}
471
481static void _downsample_bayer_half_size(float *const out, const float *const in,
482 const dt_iop_roi_t *const roi_out,
483 const dt_iop_roi_t *const roi_in, const uint32_t filters,
484 const gboolean is_4bayer, const double CAM_to_RGB[3][4])
485{
486 __OMP_PARALLEL_FOR__(collapse(2))
487 for(int y = 0; y < roi_out->height; y++)
488 {
489 for(int x = 0; x < roi_out->width; x++)
490 {
491 float *const outc = out + 4 * ((size_t)y * roi_out->width + x);
492 dt_aligned_pixel_t cam = { 0.0f };
493 int samples[4] = { 0 };
494 const int px = MIN(2 * x, roi_in->width - 1);
495 const int py = MIN(2 * y, roi_in->height - 1);
496
497 // Collect the 2x2 source block feeding this output pixel and average only
498 // same-colour photosites together so we do not invent new chroma detail.
499 for(int j = 0; j < 2; j++)
500 {
501 for(int i = 0; i < 2; i++)
502 {
503 const int xx = MIN(px + i, roi_in->width - 1);
504 const int yy = MIN(py + j, roi_in->height - 1);
505 const int c = FC(yy, xx, filters);
506 cam[c] += in[(size_t)yy * roi_in->width + xx];
507 samples[c]++;
508 }
509 }
510
511 for(int c = 0; c < 4; c++)
512 if(samples[c] > 0) cam[c] /= (float)samples[c];
513
514 if(is_4bayer)
515 {
516 for(int c = 0; c < 3; c++)
517 {
518 outc[c] = 0.0f;
519 for(int k = 0; k < 4; k++) outc[c] += CAM_to_RGB[c][k] * cam[k];
520 }
521 }
522 else
523 {
524 outc[0] = cam[RED];
525 outc[1] = cam[GREEN];
526 outc[2] = cam[BLUE];
527 }
528
529 outc[3] = 0.0f;
530 }
531 }
532}
533
543static float _downsample_xtrans_missing_colour(const float *const in, const dt_iop_roi_t *const roi_in,
544 const int px, const int py,
545 const uint8_t (*const xtrans)[6], const int colour)
546{
547 const float cx = px + 0.5f;
548 const float cy = py + 0.5f;
549 const int xmin = MAX(0, px - 3);
550 const int xmax = MIN(roi_in->width - 1, px + 4);
551 const int ymin = MAX(0, py - 3);
552 const int ymax = MIN(roi_in->height - 1, py + 4);
553
554 float quadrant_value[4] = { 0.0f };
555 float quadrant_dist[4] = { INFINITY, INFINITY, INFINITY, INFINITY };
556 int quadrant_x[4] = { 0 };
557 int quadrant_y[4] = { 0 };
558 gboolean quadrant_valid[4] = { FALSE, FALSE, FALSE, FALSE };
559
560 float nearest_value = 0.0f;
561 float nearest_dist = INFINITY;
562
563 // Search the local 7x7 neighbourhood because X-Trans can place the next sample
564 // of a given colour two pixels away from the 2x2 block feeding the output.
565 for(int yy = ymin; yy <= ymax; yy++)
566 {
567 for(int xx = xmin; xx <= xmax; xx++)
568 {
569 if(FCxtrans(yy, xx, roi_in, xtrans) != colour) continue;
570
571 const float dx = xx - cx;
572 const float dy = yy - cy;
573 const float dist2 = dx * dx + dy * dy;
574 if(dist2 < nearest_dist)
575 {
576 nearest_dist = dist2;
577 nearest_value = in[(size_t)yy * roi_in->width + xx];
578 }
579
580 const int quadrant = ((yy > cy) ? 2 : 0) + ((xx > cx) ? 1 : 0);
581 if(dist2 < quadrant_dist[quadrant])
582 {
583 quadrant_dist[quadrant] = dist2;
584 quadrant_value[quadrant] = in[(size_t)yy * roi_in->width + xx];
585 quadrant_x[quadrant] = xx;
586 quadrant_y[quadrant] = yy;
587 quadrant_valid[quadrant] = TRUE;
588 }
589 }
590 }
591
592 if(quadrant_valid[0] && quadrant_valid[1] && quadrant_valid[2] && quadrant_valid[3])
593 {
594 const float x_left = 0.5f * (quadrant_x[0] + quadrant_x[2]);
595 const float x_right = 0.5f * (quadrant_x[1] + quadrant_x[3]);
596 const float y_top = 0.5f * (quadrant_y[0] + quadrant_y[1]);
597 const float y_bottom = 0.5f * (quadrant_y[2] + quadrant_y[3]);
598 const float tx = CLAMP((cx - x_left) / MAX(x_right - x_left, 1e-6f), 0.0f, 1.0f);
599 const float ty = CLAMP((cy - y_top) / MAX(y_bottom - y_top, 1e-6f), 0.0f, 1.0f);
600 const float top = quadrant_value[0] + tx * (quadrant_value[1] - quadrant_value[0]);
601 const float bottom = quadrant_value[2] + tx * (quadrant_value[3] - quadrant_value[2]);
602 return top + ty * (bottom - top);
603 }
604
605 float sum = 0.0f;
606 int count = 0;
607 for(int q = 0; q < 4; q++)
608 {
609 if(!quadrant_valid[q]) continue;
610 sum += quadrant_value[q];
611 count++;
612 }
613
614 return (count > 0) ? sum / (float)count : nearest_value;
615}
616
625static void _downsample_xtrans_half_size(float *const out, const float *const in,
626 const dt_iop_roi_t *const roi_out,
627 const dt_iop_roi_t *const roi_in,
628 const uint8_t (*const xtrans)[6])
629{
630 __OMP_PARALLEL_FOR__(collapse(2))
631 for(int y = 0; y < roi_out->height; y++)
632 {
633 for(int x = 0; x < roi_out->width; x++)
634 {
635 float *const outc = out + 4 * ((size_t)y * roi_out->width + x);
636 dt_aligned_pixel_t rgb = { 0.0f };
637 int samples[3] = { 0 };
638 const int px = MIN(2 * x, roi_in->width - 1);
639 const int py = MIN(2 * y, roi_in->height - 1);
640
641 // Reuse the real X-Trans photosites that already fall inside the source 2x2 block
642 // and only interpolate the colours that the local pattern does not sample there.
643 for(int j = 0; j < 2; j++)
644 {
645 for(int i = 0; i < 2; i++)
646 {
647 const int xx = MIN(px + i, roi_in->width - 1);
648 const int yy = MIN(py + j, roi_in->height - 1);
649 const int c = FCxtrans(yy, xx, roi_in, xtrans);
650 rgb[c] += in[(size_t)yy * roi_in->width + xx];
651 samples[c]++;
652 }
653 }
654
655 for(int c = 0; c < 3; c++)
656 {
657 if(samples[c] > 0)
658 outc[c] = rgb[c] / (float)samples[c];
659 else
660 outc[c] = _downsample_xtrans_missing_colour(in, roi_in, px, py, xtrans, c);
661 }
662
663 outc[3] = 0.0f;
664 }
665 }
666}
667
681static void _downsample_guided_laplacian_fit(const float *const restrict HF,
682 float *const restrict coeff,
683 float *const restrict bias,
684 const size_t width, const size_t height)
685{
686 const float eps = 1e-12f;
687 const dt_aligned_pixel_simd_t zero = dt_simd_set1(0.f);
688 const dt_aligned_pixel_simd_t inv_patch = dt_simd_set1(1.f / 25.f);
690 for(size_t row = 0; row < height; ++row)
691 {
692 const float *const row0 = HF + 4 * ((size_t)CLAMP((int)row - 2, 0, (int)height - 1) * width);
693 const float *const row1 = HF + 4 * ((size_t)CLAMP((int)row - 1, 0, (int)height - 1) * width);
694 const float *const row2 = HF + 4 * (row * width);
695 const float *const row3 = HF + 4 * ((size_t)CLAMP((int)row + 1, 0, (int)height - 1) * width);
696 const float *const row4 = HF + 4 * ((size_t)CLAMP((int)row + 2, 0, (int)height - 1) * width);
697 const float *const rows[BSPLINE_FSIZE] = { row0, row1, row2, row3, row4 };
698 const int max_col = (int)width - 1;
699
700 for(size_t col = 0; col < width; ++col)
701 {
702 dt_aligned_pixel_simd_t sum_rgb = zero;
703 dt_aligned_pixel_simd_t sum_rgb_guide = zero;
704 float sum_guide = 0.f;
705 float sum_guide_sq = 0.f;
706 const int col_offsets[BSPLINE_FSIZE]
707 = { 4 * CLAMP((int)col - 2, 0, max_col),
708 4 * CLAMP((int)col - 1, 0, max_col),
709 4 * (int)col,
710 4 * CLAMP((int)col + 1, 0, max_col),
711 4 * CLAMP((int)col + 2, 0, max_col) };
712
713 // Walk the dense 5x5 neighbourhood once. The 5 clamped column offsets are
714 // hoisted once per output pixel so the inner loop keeps only loads and moment
715 // accumulation for the RGB channels and their shared guide. Keep those loops
716 // as counted loops so GCC does not fully unroll all 25 taps and spill the
717 // intermediate guide terms to the stack.
718#if defined(__GNUC__) && !defined(__clang__)
719#pragma GCC unroll 1
720#endif
721 for(int jj = 0; jj < BSPLINE_FSIZE; ++jj)
722 {
723 const float *const row_ptr = rows[jj];
724#if defined(__GNUC__) && !defined(__clang__)
725#pragma GCC unroll 1
726#endif
727 for(int ii = 0; ii < BSPLINE_FSIZE; ++ii)
728 {
729 const dt_aligned_pixel_simd_t sample = dt_load_simd_aligned(row_ptr + col_offsets[ii]);
730 const float guide = (sample[RED] + sample[GREEN] + sample[BLUE]) / 3.f;
731
732 sum_rgb += sample;
733 sum_guide += guide;
734 sum_guide_sq += guide * guide;
735 sum_rgb_guide += sample * dt_simd_set1(guide);
736 }
737 }
738
739 dt_aligned_pixel_simd_t means = sum_rgb * inv_patch;
740 const float guide_mean = sum_guide * (1.f / 25.f);
741 float variance = sum_guide_sq * (1.f / 25.f) - sqf(guide_mean);
742 dt_aligned_pixel_simd_t covariance = sum_rgb_guide * inv_patch - means * dt_simd_set1(guide_mean);
743 means[ALPHA] = 0.f;
744 covariance[ALPHA] = 0.f;
745
746 if(variance < 0.f) variance = 0.f;
747
748 dt_aligned_pixel_simd_t slope = zero;
749 if(variance > eps) slope = covariance / dt_simd_set1(variance);
750 slope[ALPHA] = 0.f;
751
752 dt_aligned_pixel_simd_t intercept = means - slope * dt_simd_set1(guide_mean);
753 intercept[ALPHA] = 0.f;
754
755 dt_store_simd_aligned(coeff + 4 * (row * width + col), slope);
756 dt_store_simd_aligned(bias + 4 * (row * width + col), intercept);
757 }
758 }
759}
760
770static void _downsample_guided_laplacian_apply(const float *const restrict HF,
771 const float *const restrict coeff,
772 const float *const restrict bias,
773 const float *const restrict LF,
774 float *const restrict reconstructed,
775 const size_t width, const size_t height,
776 const gboolean reset)
777{
779 for(size_t row = 0; row < height; ++row)
780 {
781 for(size_t col = 0; col < width; ++col)
782 {
783 const size_t index = 4 * (row * width + col);
784 const dt_aligned_pixel_simd_t hf = dt_load_simd_aligned(HF + index);
785 const dt_aligned_pixel_simd_t guide = dt_simd_set1((hf[RED] + hf[GREEN] + hf[BLUE]) / 3.f);
786 dt_aligned_pixel_simd_t filtered = (dt_load_simd_aligned(coeff + index) * guide
787 + dt_load_simd_aligned(bias + index))
788 * dt_load_simd_aligned(LF + index);
789
790 if(!reset) filtered += dt_load_simd_aligned(reconstructed + index);
791 filtered[ALPHA] = 0.f;
792 dt_store_simd_aligned(reconstructed + index, filtered);
793 }
794 }
795}
796
811 const size_t width, const size_t height,
812 const int iterations)
813{
814 if(iterations <= 0) return 0;
815
816 const size_t pixels = width * height;
817 float *const restrict LF_even = dt_pixelpipe_cache_alloc_align_float_cache(4 * pixels, 0);
818 float *const restrict LF_odd = dt_pixelpipe_cache_alloc_align_float_cache(4 * pixels, 0);
819 float *const restrict HF = dt_pixelpipe_cache_alloc_align_float_cache(4 * pixels, 0);
820 float *const restrict reconstructed = dt_pixelpipe_cache_alloc_align_float_cache(4 * pixels, 0);
821 float *const restrict coeff = dt_pixelpipe_cache_alloc_align_float_cache(4 * pixels, 0);
822 float *const restrict bias = dt_pixelpipe_cache_alloc_align_float_cache(4 * pixels, 0);
823 float *const restrict coeff_tmp = dt_pixelpipe_cache_alloc_align_float_cache(4 * pixels, 0);
824 size_t padded_size;
825 float *const restrict tempbuf = dt_pixelpipe_cache_alloc_perthread_float(4 * width, &padded_size);
826 const float *restrict residual = out;
827 int err = 0;
828
829 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))
830 {
831 err = 1;
832 goto cleanup;
833 }
834
835 for(int iteration = 0; iteration < iterations; ++iteration)
836 {
837 residual = out;
838
839 for(int s = 0; s < DOWNSAMPLE_GUIDED_SCALES; ++s)
840 {
841 const int mult = 1 << s;
842 const float *restrict buffer_in;
843 float *restrict buffer_out;
844
845 if(s == 0)
846 {
847 buffer_in = out;
848 buffer_out = LF_odd;
849 }
850 else if(s % 2 != 0)
851 {
852 buffer_in = LF_odd;
853 buffer_out = LF_even;
854 }
855 else
856 {
857 buffer_in = LF_even;
858 buffer_out = LF_odd;
859 }
860
861 decompose_2D_Bspline(buffer_in, HF, buffer_out, width, height, mult, tempbuf, padded_size);
862 // Express the current wavelet band as relative detail over the blur that created it
863 // so the linear RGB model follows local chroma ratios instead of absolute amplitudes.
865 for(size_t row = 0; row < height; ++row)
866 {
867 for(size_t col = 0; col < width; ++col)
868 {
869 const size_t index = 4 * (row * width + col);
870 dt_aligned_pixel_simd_t lf = dt_load_simd_aligned(buffer_out + index);
871 lf[RED] = fmaxf(lf[RED], 1e-8f);
872 lf[GREEN] = fmaxf(lf[GREEN], 1e-8f);
873 lf[BLUE] = fmaxf(lf[BLUE], 1e-8f);
874 lf[ALPHA] = 1.f;
875
876 dt_aligned_pixel_simd_t normalized = dt_load_simd_aligned(HF + index) / lf;
877 normalized[ALPHA] = 0.f;
878 dt_store_simd_aligned(HF + index, normalized);
879 }
880 }
881
883
884 blur_2D_Bspline(coeff, coeff_tmp, tempbuf, width, height, 1, FALSE);
886 blur_2D_Bspline(bias, coeff_tmp, tempbuf, width, height, 1, FALSE);
887 dt_iop_image_copy_by_size(bias, coeff_tmp, width, height, 4);
888
889 _downsample_guided_laplacian_apply(HF, coeff, bias, buffer_out, reconstructed,
890 width, height, s == 0);
891 residual = buffer_out;
892 }
893
894 const gboolean last_iteration = (iteration == iterations - 1);
896 for(size_t row = 0; row < height; ++row)
897 {
898 for(size_t col = 0; col < width; ++col)
899 {
900 const size_t index = 4 * (row * width + col);
901 dt_aligned_pixel_simd_t pixel
902 = dt_simd_max_zero(dt_load_simd_aligned(reconstructed + index)
903 + dt_load_simd_aligned(residual + index));
904 pixel[ALPHA] = 0.f;
905
906 if(last_iteration)
907 dt_store_simd_nontemporal(out + index, pixel);
908 else
909 dt_store_simd_aligned(out + index, pixel);
910 }
911 }
912 }
913 dt_omploop_sfence(); // ensure the final nontemporal writeback completes before the caller reads out
914
915cleanup:
920 dt_pixelpipe_cache_free_align(reconstructed);
924 return err;
925}
926
927
929 const float *const in, float *const out, const dt_iop_roi_t *const roi_in,
930 const dt_iop_roi_t *const roi_out)
931{
932 (void)pipe;
934 dt_interpolation_resample_roi_1c(itor, out, roi_out, in, roi_in);
935}
936
939static void _demosaic_map_size(const gboolean downsamples, const dt_iop_roi_t *const roi_in,
940 dt_iop_roi_t *roi_out)
941{
942 *roi_out = *roi_in;
943
944 if(downsamples)
945 {
946 roi_out->width = (roi_in->width + 1) / 2;
947 roi_out->height = (roi_in->height + 1) / 2;
948 }
949
950 // snap to start of mosaic block:
951 roi_out->x = 0; // MAX(0, roi_out->x & ~1);
952 roi_out->y = 0; // MAX(0, roi_out->y & ~1);
953}
954
955void modify_roi_out(struct dt_iop_module_t *self, const struct dt_dev_pixelpipe_t *pipe,
956 struct dt_dev_pixelpipe_iop_t *piece, dt_iop_roi_t *roi_out,
957 const dt_iop_roi_t *const roi_in)
958{
961}
962
963/* --- the geometry service's view of this module (develop/geometry/geometry.h) ---------
964 *
965 * Demosaic is on the roster for its SIZE only: the downsampling methods halve the frame, and
966 * nothing downstream would otherwise know. It publishes no point transform because it has none
967 * -- consumers of coordinates work in the post-demosaic frame and normalise against the
968 * per-module dimensions the chain's fold records.
969 */
970
975
976static void _demosaic_geometry_map_size(const void *data, const dt_iop_roi_t *const in, dt_iop_roi_t *out)
977{
978 _demosaic_map_size(((const dt_iop_demosaic_geometry_t *)data)->downsamples, in, out);
979}
980
983 .transform = NULL,
984 .backtransform = NULL,
985};
986
987gboolean geometry_record(dt_iop_module_t *self, const void *params, dt_geometry_record_t *record)
988{
990
993 if(IS_NULL_PTR(data)) return FALSE;
994 data->downsamples = _is_downsample_method(p->demosaicing_method);
995
996 record->data = data;
997 record->free_data = dt_free_gpointer;
999 return TRUE;
1000}
1001
1002// which roi input is needed to process to this output?
1003// roi_out is unchanged, full buffer in is full buffer out.
1004// see ../../doc/resizing-scaling.md for details
1005void modify_roi_in(struct dt_iop_module_t *self, const struct dt_dev_pixelpipe_t *pipe,
1007 const dt_iop_roi_t *roi_out, dt_iop_roi_t *roi_in)
1008{
1009 // this op is disabled for filters == 0
1010 *roi_in = *roi_out;
1011
1013 const int method = data->demosaicing_method;
1014 const gboolean passthrough = (method == DT_IOP_DEMOSAIC_PASSTHROUGH_MONOCHROME) ||
1016 const gboolean downsample = _is_downsample_method(method);
1017
1018 if(downsample)
1019 {
1020 roi_in->x *= 2;
1021 roi_in->y *= 2;
1022 roi_in->width *= 2;
1023 roi_in->height *= 2;
1024
1025 // Half-size mode maps each output pixel to one 2x2 raw block, so keep the exact
1026 // 2x addressing and clamp only the tail block against the available input buffer.
1027 roi_in->x = CLAMP(roi_in->x, 0, MAX(0, piece->buf_in.width - 1));
1028 roi_in->y = CLAMP(roi_in->y, 0, MAX(0, piece->buf_in.height - 1));
1029 roi_in->width = CLAMP(roi_in->width, 1, piece->buf_in.width - roi_in->x);
1030 roi_in->height = CLAMP(roi_in->height, 1, piece->buf_in.height - roi_in->y);
1031 return;
1032 }
1033
1034 // No snapping needed: process() rebuilds the exact CFA/xtrans phase for the
1035 // actual roi_in->x/y on every call (see the top of process()), so demosaic can
1036 // request any pixel-precise crop position from upstream instead of rounding to
1037 // the nearest sensor-pattern-aligned one.
1038 (void)passthrough;
1039}
1040
1041
1044 const void *const i, void *const o)
1045{
1046 const dt_iop_roi_t *const roi_in = &piece->roi_in;
1047 const dt_iop_roi_t *const roi_out = &piece->roi_out;
1048 const dt_image_t *img = &self->dev->image_storage;
1049 const float threshold = 0.0001f * img->exif_iso;
1050 dt_times_t start_time = { 0 }, end_time = { 0 };
1051
1052 dt_iop_roi_t roi = *roi_in;
1053 dt_iop_roi_t roo = *roi_out;
1054 roo.x = roo.y = 0;
1055 // roi_out->scale = global scale: (iscale == 1.0, always when demosaic is on)
1056 const gboolean info = ((dt_get_debug_flags() & (DT_DEBUG_DEMOSAIC | DT_DEBUG_PERF))
1058
1059 // piece->dsc_in.filters/xtrans only carries the fixed sensor-border-trim phase shift
1060 // (applied once by rawprepare, which owns that constant, and never rewritten here —
1061 // process() must treat piece->dsc_in as immutable). The dynamic, ROI-dependent part of
1062 // the shift (panning/zooming/cropping downstream) is NOT baked into a table here: most
1063 // algorithms below (markesteijn, passthrough, VNG, the xtrans downsample path,
1064 // green-equilibration, dual demosaic) already add roi_in->x/y themselves at their own
1065 // call sites, working directly off the unshifted piece->dsc_in.xtrans/filters. Only the
1066 // handful of algorithms that work in tile-local coordinates with no roi awareness at all
1067 // (RCD, LMMSE, PPG, AMaZE, the bayer downsample path) need the dynamic offset folded into
1068 // `filters` up front — passing a locally pre-shifted table to the roi-aware algorithms
1069 // instead would double their own correction on top of it.
1070 const uint8_t(*const xtrans_raw)[6] = (const uint8_t(*const)[6])piece->dsc_in.xtrans;
1071 const uint32_t filters = dt_dev_get_roi_filters(piece, roi_in);
1072
1075
1076 int demosaicing_method = data->demosaicing_method;
1077
1078 gboolean showmask = FALSE;
1080 {
1082 if(g) showmask = (g->visual_mask);
1083 // take care of passthru modes
1085 demosaicing_method = (filters != 9u) ? DT_IOP_DEMOSAIC_RCD : DT_IOP_DEMOSAIC_MARKESTEIJN;
1087 demosaicing_method = DT_IOP_DEMOSAIC_PASSTHROUGH_MONOCHROME;
1088 }
1089
1091 "[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",
1092 (void *)pipe, _pipe_type_string(pipe->type), (unsigned long)pthread_self(), (void *)piece,
1093 roi_in->x, roi_in->y, roi_in->width, roi_in->height,
1094 piece->dsc_in.filters, filters, (void *)xtrans_raw, method2string(demosaicing_method));
1095
1096 const float *const pixels = (float *)i;
1097
1098 // Full demosaic and then scaling if needed
1099 if(info) dt_get_times(&start_time);
1100
1101 if(_is_downsample_method(demosaicing_method))
1102 {
1103 if(filters == 9u)
1104 _downsample_xtrans_half_size(o, pixels, &roo, &roi, xtrans_raw);
1105 else
1106 _downsample_bayer_half_size(o, pixels, &roo, &roi, filters,
1107 img->flags & DT_IMAGE_4BAYER, data->CAM_to_RGB);
1108
1110 }
1111 else if(demosaicing_method == DT_IOP_DEMOSAIC_PASSTHROUGH_MONOCHROME)
1112 {
1113 passthrough_monochrome(o, pixels, &roo, &roi);
1114 }
1115 else if(demosaicing_method == DT_IOP_DEMOSAIC_PASSTHROUGH_COLOR)
1116 {
1117 passthrough_color(o, pixels, &roo, &roi, piece->dsc_in.filters, xtrans_raw);
1118 }
1119 else if(filters == 9u)
1120 {
1121 const int passes = (demosaicing_method == DT_IOP_DEMOSAIC_MARKESTEIJN) ? 1 : 3;
1122 if(demosaicing_method == DT_IOP_DEMOSAIC_MARKEST3_VNG)
1123 xtrans_markesteijn_interpolate(o, pixels, &roo, &roi, xtrans_raw, passes);
1124 else if(demosaicing_method == DT_IOP_DEMOSAIC_FDC)
1125 xtrans_fdc_interpolate(self, o, pixels, &roo, &roi, xtrans_raw);
1126 else if(demosaicing_method >= DT_IOP_DEMOSAIC_MARKESTEIJN)
1127 xtrans_markesteijn_interpolate(o, pixels, &roo, &roi, xtrans_raw, passes);
1128 else
1129 if(vng_interpolate(o, pixels, &roo, &roi, piece->dsc_in.filters, xtrans_raw, FALSE))
1130 return 1;
1131 }
1132 else
1133 {
1134 float *in = (float *)pixels;
1135 float *aux;
1136
1137 if(!(img->flags & DT_IMAGE_4BAYER) && data->green_eq != DT_IOP_GREEN_EQ_NO)
1138 {
1139 in = dt_pixelpipe_cache_alloc_align_float((size_t)roi_in->height * roi_in->width, pipe);
1140 if(!IS_NULL_PTR(in))
1141 {
1142 switch(data->green_eq)
1143 {
1145 green_equilibration_favg(in, pixels, roi_in->width, roi_in->height, piece->dsc_in.filters,
1146 roi_in->x, roi_in->y);
1147 break;
1149 green_equilibration_lavg(in, pixels, roi_in->width, roi_in->height, piece->dsc_in.filters,
1150 roi_in->x, roi_in->y, threshold);
1151 break;
1153 aux = dt_pixelpipe_cache_alloc_align_float((size_t)roi_in->height * roi_in->width, pipe);
1154 if(IS_NULL_PTR(aux))
1155 {
1157 return 1;
1158 }
1159 green_equilibration_favg(aux, pixels, roi_in->width, roi_in->height, piece->dsc_in.filters,
1160 roi_in->x, roi_in->y);
1161 green_equilibration_lavg(in, aux, roi_in->width, roi_in->height, piece->dsc_in.filters, roi_in->x,
1162 roi_in->y, threshold);
1164 break;
1165 }
1166 }
1167 else
1168 {
1169 return 1;
1170 }
1171 }
1172
1173 if(demosaicing_method == DT_IOP_DEMOSAIC_VNG4 || (img->flags & DT_IMAGE_4BAYER))
1174 {
1175 if(vng_interpolate(o, in, &roo, &roi, piece->dsc_in.filters, xtrans_raw, FALSE))
1176 return 1;
1177 if(img->flags & DT_IMAGE_4BAYER)
1178 {
1180 dt_aligned_pixel_t processed_maximum = { piece->dsc_in.processed_maximum[0], piece->dsc_in.processed_maximum[1],
1181 piece->dsc_in.processed_maximum[2], 0.0f };
1182 dt_colorspaces_cygm_to_rgb(processed_maximum, 1, data->CAM_to_RGB);
1183 }
1184 }
1185 else if((demosaicing_method & ~DEMOSAIC_DUAL) == DT_IOP_DEMOSAIC_RCD)
1186 {
1187 rcd_demosaic(piece, o, in, &roo, &roi, filters);
1188 }
1189 else if(demosaicing_method == DT_IOP_DEMOSAIC_LMMSE)
1190 {
1192 {
1196 {
1199 gd->lmmse_gamma_in = NULL;
1200 gd->lmmse_gamma_out = NULL;
1201 if(!(img->flags & DT_IMAGE_4BAYER) && data->green_eq != DT_IOP_GREEN_EQ_NO)
1203 return 1;
1204 }
1205#ifdef _OPENMP
1206 #pragma omp for
1207#endif
1208 for(int j = 0; j < 65536; j++)
1209 {
1210 const double x = (double)j / 65535.0;
1211 gd->lmmse_gamma_in[j] = (x <= 0.001867) ? x * 17.0 : 1.044445 * exp(log(x) / 2.4) - 0.044445;
1212 gd->lmmse_gamma_out[j] = (x <= 0.031746) ? x / 17.0 : exp(log((x + 0.044445) / 1.044445) * 2.4);
1213 }
1214 }
1215 lmmse_demosaic(piece, o, in, &roo, &roi, filters, data->lmmse_refine, gd->lmmse_gamma_in, gd->lmmse_gamma_out);
1216 }
1217 else if((demosaicing_method & ~DEMOSAIC_DUAL) != DT_IOP_DEMOSAIC_AMAZE)
1218 {
1219 if(demosaic_ppg(o, in, &roo, &roi, filters, data->median_thrs))
1220 {
1221 if(!(img->flags & DT_IMAGE_4BAYER) && data->green_eq != DT_IOP_GREEN_EQ_NO)
1223 return 1;
1224 }
1225 } // wanted ppg or zoomed out a lot and quality is limited to 1
1226 else
1227 amaze_demosaic_RT(piece, in, o, &roi, &roo, filters);
1228
1229 if(!(img->flags & DT_IMAGE_4BAYER) && data->green_eq != DT_IOP_GREEN_EQ_NO)
1231 }
1232
1233 if(info)
1234 {
1235 const float mpixels = (roo.width * roo.height) / 1.0e6;
1236 dt_get_times(&end_time);
1237 const float tclock = end_time.clock - start_time.clock;
1238 const float uclock = end_time.user - start_time.user;
1239 fprintf(stderr," [demosaic] process CPU `%s' did %.2fmpix, %.4f secs (%.4f CPU), %.2f pix/us\n",
1240 method2string(demosaicing_method & ~DEMOSAIC_DUAL), mpixels, tclock, uclock, mpixels / tclock);
1241 }
1242
1243 if((demosaicing_method & DEMOSAIC_DUAL))
1244 {
1245 if(dual_demosaic(pipe, piece, o, pixels, &roo, &roi, piece->dsc_in.filters, xtrans_raw, showmask, data->dual_thrs))
1246 return 1;
1247 }
1248
1249 if(data->color_smoothing && !_is_downsample_method(demosaicing_method))
1250 color_smoothing(o, roi_out, data->color_smoothing);
1251
1252 return 0;
1253}
1254
1255#ifdef HAVE_OPENCL
1257 const dt_dev_pixelpipe_iop_t *piece, cl_mem dev_in,
1258 cl_mem dev_out, const dt_iop_roi_t *const roi_in,
1259 const dt_iop_roi_t *const roi_out, const int demosaicing_method)
1260{
1263
1264 const int devid = pipe->devid;
1265
1266 cl_mem dev_aux = NULL;
1267 cl_mem dev_tmp = NULL;
1268 cl_mem dev_med = NULL;
1269 cl_mem dev_green_eq = NULL;
1270 cl_int err = -999;
1271
1272 int width = roi_out->width;
1273 int height = roi_out->height;
1274
1275 // The PPG kernels below work in tile-local coordinates with no roi awareness, so they
1276 // need the dynamic ROI offset folded into filters up front (see process()'s comment).
1277 const uint32_t filters = dt_dev_get_roi_filters(piece, roi_in);
1278
1279 // green equilibration
1280 if(data->green_eq != DT_IOP_GREEN_EQ_NO)
1281 {
1282 dev_green_eq = dt_opencl_alloc_device(devid, roi_in->width, roi_in->height, sizeof(float));
1283 if(IS_NULL_PTR(dev_green_eq)) goto error;
1284
1285 if(!green_equilibration_cl(self, pipe, piece, dev_in, dev_green_eq, roi_in))
1286 goto error;
1287
1288 dev_in = dev_green_eq;
1289 }
1290
1291 // need to reserve scaled auxiliary buffer or use dev_out
1292 dev_aux = dev_out;
1293
1294 if(demosaicing_method == DT_IOP_DEMOSAIC_PASSTHROUGH_MONOCHROME)
1295 {
1296 size_t sizes[3] = { ROUNDUPDWD(width, devid), ROUNDUPDHT(height, devid), 1 };
1297 dt_opencl_set_kernel_arg(devid, gd->kernel_passthrough_monochrome, 0, sizeof(cl_mem), &dev_in);
1298 dt_opencl_set_kernel_arg(devid, gd->kernel_passthrough_monochrome, 1, sizeof(cl_mem), &dev_aux);
1299 dt_opencl_set_kernel_arg(devid, gd->kernel_passthrough_monochrome, 2, sizeof(int), &width);
1302 if(err != CL_SUCCESS) goto error;
1303 }
1304 else if(demosaicing_method == DT_IOP_DEMOSAIC_PASSTHROUGH_COLOR)
1305 {
1306 size_t sizes[3] = { ROUNDUPDWD(width, devid), ROUNDUPDHT(height, devid), 1 };
1307 dt_opencl_set_kernel_arg(devid, gd->kernel_passthrough_color, 0, sizeof(cl_mem), &dev_in);
1308 dt_opencl_set_kernel_arg(devid, gd->kernel_passthrough_color, 1, sizeof(cl_mem), &dev_aux);
1309 dt_opencl_set_kernel_arg(devid, gd->kernel_passthrough_color, 2, sizeof(int), &width);
1310 dt_opencl_set_kernel_arg(devid, gd->kernel_passthrough_color, 3, sizeof(int), &height);
1311 dt_opencl_set_kernel_arg(devid, gd->kernel_passthrough_color, 4, sizeof(int), (void *)&roi_in->x);
1312 dt_opencl_set_kernel_arg(devid, gd->kernel_passthrough_color, 5, sizeof(int), (void *)&roi_in->y);
1313 dt_opencl_set_kernel_arg(devid, gd->kernel_passthrough_color, 6, sizeof(uint32_t), (void *)&piece->dsc_in.filters);
1314
1316 if(err != CL_SUCCESS) goto error;
1317 }
1318 else if(demosaicing_method == DT_IOP_DEMOSAIC_PPG)
1319 {
1320 dev_tmp = dt_opencl_alloc_device(devid, roi_in->width, roi_in->height, sizeof(float) * 4);
1321 if(IS_NULL_PTR(dev_tmp)) goto error;
1322
1323 {
1324 const int myborder = 3;
1325 // manage borders
1326 size_t sizes[3] = { ROUNDUPDWD(width, devid), ROUNDUPDHT(height, devid), 1 };
1327 dt_opencl_set_kernel_arg(devid, gd->kernel_border_interpolate, 0, sizeof(cl_mem), &dev_in);
1328 dt_opencl_set_kernel_arg(devid, gd->kernel_border_interpolate, 1, sizeof(cl_mem), &dev_tmp);
1329 dt_opencl_set_kernel_arg(devid, gd->kernel_border_interpolate, 2, sizeof(int), (void *)&width);
1330 dt_opencl_set_kernel_arg(devid, gd->kernel_border_interpolate, 3, sizeof(int), (void *)&height);
1331 dt_opencl_set_kernel_arg(devid, gd->kernel_border_interpolate, 4, sizeof(uint32_t), (void *)&filters);
1332 dt_opencl_set_kernel_arg(devid, gd->kernel_border_interpolate, 5, sizeof(int), (void *)&myborder);
1334 if(err != CL_SUCCESS) goto error;
1335 }
1336
1337 if(data->median_thrs > 0.0f)
1338 {
1339 dev_med = dt_opencl_alloc_device(devid, roi_in->width, roi_in->height, sizeof(float) * 4);
1340 if(IS_NULL_PTR(dev_med)) goto error;
1341
1343 = (dt_opencl_local_buffer_t){ .xoffset = 2*2, .xfactor = 1, .yoffset = 2*2, .yfactor = 1,
1344 .cellsize = 1 * sizeof(float), .overhead = 0,
1345 .sizex = 1 << 8, .sizey = 1 << 8 };
1346
1347 if(!dt_opencl_local_buffer_opt(devid, gd->kernel_pre_median, &locopt))
1348 goto error;
1349
1350 size_t sizes[3] = { ROUNDUP(width, locopt.sizex), ROUNDUP(height, locopt.sizey), 1 };
1351 size_t local[3] = { locopt.sizex, locopt.sizey, 1 };
1352 dt_opencl_set_kernel_arg(devid, gd->kernel_pre_median, 0, sizeof(cl_mem), &dev_in);
1353 dt_opencl_set_kernel_arg(devid, gd->kernel_pre_median, 1, sizeof(cl_mem), &dev_med);
1354 dt_opencl_set_kernel_arg(devid, gd->kernel_pre_median, 2, sizeof(int), &width);
1355 dt_opencl_set_kernel_arg(devid, gd->kernel_pre_median, 3, sizeof(int), &height);
1356 dt_opencl_set_kernel_arg(devid, gd->kernel_pre_median, 4, sizeof(uint32_t),
1357 (void *)&filters);
1358 dt_opencl_set_kernel_arg(devid, gd->kernel_pre_median, 5, sizeof(float), (void *)&data->median_thrs);
1360 sizeof(float) * (locopt.sizex + 4) * (locopt.sizey + 4), NULL);
1361 err = dt_opencl_enqueue_kernel_2d_with_local(devid, gd->kernel_pre_median, sizes, local);
1362 if(err != CL_SUCCESS) goto error;
1363 dev_in = dev_aux;
1364 }
1365 else
1366 dev_med = dev_in;
1367
1368 {
1370 = (dt_opencl_local_buffer_t){ .xoffset = 2*3, .xfactor = 1, .yoffset = 2*3, .yfactor = 1,
1371 .cellsize = sizeof(float) * 1, .overhead = 0,
1372 .sizex = 1 << 8, .sizey = 1 << 8 };
1373
1374 if(!dt_opencl_local_buffer_opt(devid, gd->kernel_ppg_green, &locopt))
1375 goto error;
1376
1377 size_t sizes[3] = { ROUNDUP(width, locopt.sizex), ROUNDUP(height, locopt.sizey), 1 };
1378 size_t local[3] = { locopt.sizex, locopt.sizey, 1 };
1379 dt_opencl_set_kernel_arg(devid, gd->kernel_ppg_green, 0, sizeof(cl_mem), &dev_med);
1380 dt_opencl_set_kernel_arg(devid, gd->kernel_ppg_green, 1, sizeof(cl_mem), &dev_tmp);
1381 dt_opencl_set_kernel_arg(devid, gd->kernel_ppg_green, 2, sizeof(int), &width);
1382 dt_opencl_set_kernel_arg(devid, gd->kernel_ppg_green, 3, sizeof(int), &height);
1383 dt_opencl_set_kernel_arg(devid, gd->kernel_ppg_green, 4, sizeof(uint32_t),
1384 (void *)&filters);
1386 sizeof(float) * (locopt.sizex + 2*3) * (locopt.sizey + 2*3), NULL);
1387
1388 err = dt_opencl_enqueue_kernel_2d_with_local(devid, gd->kernel_ppg_green, sizes, local);
1389 if(err != CL_SUCCESS) goto error;
1390 }
1391
1392 {
1394 = (dt_opencl_local_buffer_t){ .xoffset = 2*1, .xfactor = 1, .yoffset = 2*1, .yfactor = 1,
1395 .cellsize = 4 * sizeof(float), .overhead = 0,
1396 .sizex = 1 << 8, .sizey = 1 << 8 };
1397
1398 if(!dt_opencl_local_buffer_opt(devid, gd->kernel_ppg_redblue, &locopt))
1399 goto error;
1400
1401 size_t sizes[3] = { ROUNDUP(width, locopt.sizex), ROUNDUP(height, locopt.sizey), 1 };
1402 size_t local[3] = { locopt.sizex, locopt.sizey, 1 };
1403 dt_opencl_set_kernel_arg(devid, gd->kernel_ppg_redblue, 0, sizeof(cl_mem), &dev_tmp);
1404 dt_opencl_set_kernel_arg(devid, gd->kernel_ppg_redblue, 1, sizeof(cl_mem), &dev_aux);
1405 dt_opencl_set_kernel_arg(devid, gd->kernel_ppg_redblue, 2, sizeof(int), &width);
1406 dt_opencl_set_kernel_arg(devid, gd->kernel_ppg_redblue, 3, sizeof(int), &height);
1407 dt_opencl_set_kernel_arg(devid, gd->kernel_ppg_redblue, 4, sizeof(uint32_t),
1408 (void *)&filters);
1410 sizeof(float) * 4 * (locopt.sizex + 2) * (locopt.sizey + 2), NULL);
1411
1412 err = dt_opencl_enqueue_kernel_2d_with_local(devid, gd->kernel_ppg_redblue, sizes, local);
1413 if(err != CL_SUCCESS) goto error;
1414 }
1415 }
1416
1417 if(dev_aux != dev_out) dt_opencl_release_mem_object(dev_aux);
1418 if(dev_med != dev_in) dt_opencl_release_mem_object(dev_med);
1419 dt_opencl_release_mem_object(dev_green_eq);
1421 dev_aux = dev_green_eq = dev_tmp = dev_med = NULL;
1422
1423 // color smoothing
1424 if(data->color_smoothing)
1425 {
1426 if(!color_smoothing_cl(self, pipe, piece, dev_out, dev_out, roi_out, data->color_smoothing))
1427 goto error;
1428 }
1429
1430 return TRUE;
1431
1432error:
1433 if(dev_aux != dev_out) dt_opencl_release_mem_object(dev_aux);
1434 if(dev_med != dev_in) dt_opencl_release_mem_object(dev_med);
1435 dt_opencl_release_mem_object(dev_green_eq);
1437 dt_print(DT_DEBUG_OPENCL, "[opencl_demosaic] couldn't enqueue kernel! %d\n", err);
1438 return FALSE;
1439}
1440
1442 const dt_dev_pixelpipe_t *pipe,
1443 cl_mem dev_out,
1444 const dt_iop_roi_t *const roi_out,
1445 const int iterations)
1446{
1447 if(iterations <= 0) return TRUE;
1448
1450 const int devid = pipe->devid;
1451 const int width = roi_out->width;
1452 const int height = roi_out->height;
1453 const int clip_negatives = 1;
1454 const int keep_signed = 0;
1455 const int dense_mult = 1;
1456 size_t sizes[] = { ROUNDUPDWD(width, devid), ROUNDUPDHT(height, devid), 1 };
1457
1458 cl_mem LF_even = NULL;
1459 cl_mem LF_odd = NULL;
1460 cl_mem temp = NULL;
1461 cl_mem coeff = NULL;
1462 cl_mem bias = NULL;
1463 cl_mem coeff_tmp = NULL;
1464 cl_mem reconstructed_a = NULL;
1465 cl_mem reconstructed_b = NULL;
1466 cl_mem residual = NULL;
1467 cl_mem reconstructed_read = NULL;
1468 cl_mem reconstructed_write = NULL;
1469 cl_mem reconstructed_final = NULL;
1470 cl_int err = DT_OPENCL_DEFAULT_ERROR;
1471
1472 LF_even = dt_opencl_alloc_device(devid, width, height, sizeof(float) * 4);
1473 LF_odd = dt_opencl_alloc_device(devid, width, height, sizeof(float) * 4);
1474 temp = dt_opencl_alloc_device(devid, width, height, sizeof(float) * 4);
1475 coeff = dt_opencl_alloc_device(devid, width, height, sizeof(float) * 4);
1476 bias = dt_opencl_alloc_device(devid, width, height, sizeof(float) * 4);
1477 coeff_tmp = dt_opencl_alloc_device(devid, width, height, sizeof(float) * 4);
1478 reconstructed_a = dt_opencl_alloc_device(devid, width, height, sizeof(float) * 4);
1479 reconstructed_b = dt_opencl_alloc_device(devid, width, height, sizeof(float) * 4);
1480 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)
1481 || IS_NULL_PTR(reconstructed_a) || IS_NULL_PTR(reconstructed_b))
1482 goto error;
1483
1484 for(int iteration = 0; iteration < iterations; ++iteration)
1485 {
1486 reconstructed_read = reconstructed_a;
1487 reconstructed_write = reconstructed_b;
1488 reconstructed_final = NULL;
1489 residual = dev_out;
1490
1491 for(int s = 0; s < DOWNSAMPLE_GUIDED_SCALES; ++s)
1492 {
1493 const int mult = 1 << s;
1494 const int first_scale = (s == 0);
1495 cl_mem buffer_in;
1496 cl_mem buffer_out;
1497
1498 if(s == 0)
1499 {
1500 buffer_in = dev_out;
1501 buffer_out = LF_odd;
1502 }
1503 else if(s % 2 != 0)
1504 {
1505 buffer_in = LF_odd;
1506 buffer_out = LF_even;
1507 }
1508 else
1509 {
1510 buffer_in = LF_even;
1511 buffer_out = LF_odd;
1512 }
1513
1514 int hblocksize;
1515 dt_opencl_local_buffer_t hlocopt = (dt_opencl_local_buffer_t){ .xoffset = 2 * mult, .xfactor = 1,
1516 .yoffset = 0, .yfactor = 1,
1517 .cellsize = 4 * sizeof(float), .overhead = 0,
1518 .sizex = 1 << 16, .sizey = 1 };
1520 hblocksize = hlocopt.sizex;
1521 else
1522 hblocksize = 1;
1523
1524 if(hblocksize > 1)
1525 {
1526 const size_t horizontal_sizes[3] = { ROUNDUP(width, hblocksize), ROUNDUPDHT(height, devid), 1 };
1527 const size_t horizontal_local[3] = { hblocksize, 1, 1 };
1528 dt_opencl_set_kernel_arg(devid, gd->kernel_bspline_horizontal_local, 0, sizeof(cl_mem), &buffer_in);
1529 dt_opencl_set_kernel_arg(devid, gd->kernel_bspline_horizontal_local, 1, sizeof(cl_mem), &temp);
1532 dt_opencl_set_kernel_arg(devid, gd->kernel_bspline_horizontal_local, 4, sizeof(int), &mult);
1533 dt_opencl_set_kernel_arg(devid, gd->kernel_bspline_horizontal_local, 5, sizeof(int), &clip_negatives);
1535 (hblocksize + 4 * mult) * 4 * sizeof(float), NULL);
1537 horizontal_sizes, horizontal_local);
1538 }
1539 else
1540 {
1541 dt_opencl_set_kernel_arg(devid, gd->kernel_bspline_horizontal, 0, sizeof(cl_mem), &buffer_in);
1542 dt_opencl_set_kernel_arg(devid, gd->kernel_bspline_horizontal, 1, sizeof(cl_mem), &temp);
1543 dt_opencl_set_kernel_arg(devid, gd->kernel_bspline_horizontal, 2, sizeof(int), &width);
1544 dt_opencl_set_kernel_arg(devid, gd->kernel_bspline_horizontal, 3, sizeof(int), &height);
1545 dt_opencl_set_kernel_arg(devid, gd->kernel_bspline_horizontal, 4, sizeof(int), &mult);
1546 dt_opencl_set_kernel_arg(devid, gd->kernel_bspline_horizontal, 5, sizeof(int), &clip_negatives);
1548 }
1549 if(err != CL_SUCCESS) goto error;
1550
1551 int vblocksize;
1552 dt_opencl_local_buffer_t vlocopt = (dt_opencl_local_buffer_t){ .xoffset = 0, .xfactor = 1,
1553 .yoffset = 2 * mult, .yfactor = 1,
1554 .cellsize = 4 * sizeof(float), .overhead = 0,
1555 .sizex = 1, .sizey = 1 << 16 };
1557 vblocksize = vlocopt.sizey;
1558 else
1559 vblocksize = 1;
1560
1561 if(vblocksize > 1)
1562 {
1563 const size_t vertical_sizes[3] = { ROUNDUPDWD(width, devid), ROUNDUP(height, vblocksize), 1 };
1564 const size_t vertical_local[3] = { 1, vblocksize, 1 };
1565 dt_opencl_set_kernel_arg(devid, gd->kernel_bspline_vertical_local, 0, sizeof(cl_mem), &temp);
1566 dt_opencl_set_kernel_arg(devid, gd->kernel_bspline_vertical_local, 1, sizeof(cl_mem), &buffer_out);
1567 dt_opencl_set_kernel_arg(devid, gd->kernel_bspline_vertical_local, 2, sizeof(int), &width);
1569 dt_opencl_set_kernel_arg(devid, gd->kernel_bspline_vertical_local, 4, sizeof(int), &mult);
1570 dt_opencl_set_kernel_arg(devid, gd->kernel_bspline_vertical_local, 5, sizeof(int), &clip_negatives);
1572 (vblocksize + 4 * mult) * 4 * sizeof(float), NULL);
1574 vertical_sizes, vertical_local);
1575 }
1576 else
1577 {
1578 dt_opencl_set_kernel_arg(devid, gd->kernel_bspline_vertical, 0, sizeof(cl_mem), &temp);
1579 dt_opencl_set_kernel_arg(devid, gd->kernel_bspline_vertical, 1, sizeof(cl_mem), &buffer_out);
1580 dt_opencl_set_kernel_arg(devid, gd->kernel_bspline_vertical, 2, sizeof(int), &width);
1581 dt_opencl_set_kernel_arg(devid, gd->kernel_bspline_vertical, 3, sizeof(int), &height);
1582 dt_opencl_set_kernel_arg(devid, gd->kernel_bspline_vertical, 4, sizeof(int), &mult);
1583 dt_opencl_set_kernel_arg(devid, gd->kernel_bspline_vertical, 5, sizeof(int), &clip_negatives);
1585 }
1586 if(err != CL_SUCCESS) goto error;
1587
1588 dt_opencl_set_kernel_arg(devid, gd->kernel_guided_laplacian_normalize, 0, sizeof(cl_mem), &buffer_in);
1589 dt_opencl_set_kernel_arg(devid, gd->kernel_guided_laplacian_normalize, 1, sizeof(cl_mem), &buffer_out);
1590 dt_opencl_set_kernel_arg(devid, gd->kernel_guided_laplacian_normalize, 2, sizeof(cl_mem), &temp);
1594 if(err != CL_SUCCESS) goto error;
1595
1596 dt_opencl_set_kernel_arg(devid, gd->kernel_guided_laplacian_coefficients, 0, sizeof(cl_mem), &temp);
1598 dt_opencl_set_kernel_arg(devid, gd->kernel_guided_laplacian_coefficients, 2, sizeof(cl_mem), &bias);
1602 if(err != CL_SUCCESS) goto error;
1603
1604 dt_opencl_set_kernel_arg(devid, gd->kernel_bspline_horizontal, 0, sizeof(cl_mem), &coeff);
1605 dt_opencl_set_kernel_arg(devid, gd->kernel_bspline_horizontal, 1, sizeof(cl_mem), &coeff_tmp);
1606 dt_opencl_set_kernel_arg(devid, gd->kernel_bspline_horizontal, 2, sizeof(int), &width);
1607 dt_opencl_set_kernel_arg(devid, gd->kernel_bspline_horizontal, 3, sizeof(int), &height);
1608 dt_opencl_set_kernel_arg(devid, gd->kernel_bspline_horizontal, 4, sizeof(int), &dense_mult);
1609 dt_opencl_set_kernel_arg(devid, gd->kernel_bspline_horizontal, 5, sizeof(int), &keep_signed);
1611 if(err != CL_SUCCESS) goto error;
1612
1613 dt_opencl_set_kernel_arg(devid, gd->kernel_bspline_vertical, 0, sizeof(cl_mem), &coeff_tmp);
1614 dt_opencl_set_kernel_arg(devid, gd->kernel_bspline_vertical, 1, sizeof(cl_mem), &coeff);
1615 dt_opencl_set_kernel_arg(devid, gd->kernel_bspline_vertical, 2, sizeof(int), &width);
1616 dt_opencl_set_kernel_arg(devid, gd->kernel_bspline_vertical, 3, sizeof(int), &height);
1617 dt_opencl_set_kernel_arg(devid, gd->kernel_bspline_vertical, 4, sizeof(int), &dense_mult);
1618 dt_opencl_set_kernel_arg(devid, gd->kernel_bspline_vertical, 5, sizeof(int), &keep_signed);
1620 if(err != CL_SUCCESS) goto error;
1621
1622 dt_opencl_set_kernel_arg(devid, gd->kernel_bspline_horizontal, 0, sizeof(cl_mem), &bias);
1623 dt_opencl_set_kernel_arg(devid, gd->kernel_bspline_horizontal, 1, sizeof(cl_mem), &coeff_tmp);
1624 dt_opencl_set_kernel_arg(devid, gd->kernel_bspline_horizontal, 2, sizeof(int), &width);
1625 dt_opencl_set_kernel_arg(devid, gd->kernel_bspline_horizontal, 3, sizeof(int), &height);
1626 dt_opencl_set_kernel_arg(devid, gd->kernel_bspline_horizontal, 4, sizeof(int), &dense_mult);
1627 dt_opencl_set_kernel_arg(devid, gd->kernel_bspline_horizontal, 5, sizeof(int), &keep_signed);
1629 if(err != CL_SUCCESS) goto error;
1630
1631 dt_opencl_set_kernel_arg(devid, gd->kernel_bspline_vertical, 0, sizeof(cl_mem), &coeff_tmp);
1632 dt_opencl_set_kernel_arg(devid, gd->kernel_bspline_vertical, 1, sizeof(cl_mem), &bias);
1633 dt_opencl_set_kernel_arg(devid, gd->kernel_bspline_vertical, 2, sizeof(int), &width);
1634 dt_opencl_set_kernel_arg(devid, gd->kernel_bspline_vertical, 3, sizeof(int), &height);
1635 dt_opencl_set_kernel_arg(devid, gd->kernel_bspline_vertical, 4, sizeof(int), &dense_mult);
1636 dt_opencl_set_kernel_arg(devid, gd->kernel_bspline_vertical, 5, sizeof(int), &keep_signed);
1638 if(err != CL_SUCCESS) goto error;
1639
1640 dt_opencl_set_kernel_arg(devid, gd->kernel_guided_laplacian_apply, 0, sizeof(cl_mem), &temp);
1641 dt_opencl_set_kernel_arg(devid, gd->kernel_guided_laplacian_apply, 1, sizeof(cl_mem), &coeff);
1642 dt_opencl_set_kernel_arg(devid, gd->kernel_guided_laplacian_apply, 2, sizeof(cl_mem), &bias);
1643 dt_opencl_set_kernel_arg(devid, gd->kernel_guided_laplacian_apply, 3, sizeof(cl_mem), &buffer_out);
1644 dt_opencl_set_kernel_arg(devid, gd->kernel_guided_laplacian_apply, 4, sizeof(cl_mem), &reconstructed_read);
1645 dt_opencl_set_kernel_arg(devid, gd->kernel_guided_laplacian_apply, 5, sizeof(cl_mem), &reconstructed_write);
1646 dt_opencl_set_kernel_arg(devid, gd->kernel_guided_laplacian_apply, 6, sizeof(int), &width);
1648 dt_opencl_set_kernel_arg(devid, gd->kernel_guided_laplacian_apply, 8, sizeof(int), &first_scale);
1650 if(err != CL_SUCCESS) goto error;
1651
1652 residual = buffer_out;
1653 reconstructed_final = reconstructed_write;
1654 cl_mem tmp = reconstructed_read;
1655 reconstructed_read = reconstructed_write;
1656 reconstructed_write = tmp;
1657 }
1658
1659 dt_opencl_set_kernel_arg(devid, gd->kernel_guided_laplacian_finalize, 0, sizeof(cl_mem), &reconstructed_final);
1660 dt_opencl_set_kernel_arg(devid, gd->kernel_guided_laplacian_finalize, 1, sizeof(cl_mem), &residual);
1661 dt_opencl_set_kernel_arg(devid, gd->kernel_guided_laplacian_finalize, 2, sizeof(cl_mem), &dev_out);
1665 if(err != CL_SUCCESS) goto error;
1666 }
1667
1668 dt_opencl_release_mem_object(reconstructed_b);
1669 dt_opencl_release_mem_object(reconstructed_a);
1676 return TRUE;
1677
1678error:
1679 dt_opencl_release_mem_object(reconstructed_b);
1680 dt_opencl_release_mem_object(reconstructed_a);
1687 dt_print(DT_DEBUG_OPENCL, "[opencl_demosaic] guided laplacian postfilter failed: %d\n", err);
1688 return FALSE;
1689}
1690
1692 cl_mem dev_in, cl_mem dev_out)
1693{
1694 const dt_iop_roi_t *const roi_in = &piece->roi_in;
1695 const dt_iop_roi_t *const roi_out = &piece->roi_out;
1696 dt_times_t start_time = { 0 }, end_time = { 0 };
1697 const gboolean info = ((dt_get_debug_flags() & (DT_DEBUG_DEMOSAIC | DT_DEBUG_PERF))
1699
1700 // See the matching comment in process(): most kernels below take roi_in->x/y as an
1701 // explicit argument and self-correct against the unshifted piece->dsc_in.filters/xtrans.
1702 // Only the bayer downsample kernel works in tile-local coordinates with no roi awareness,
1703 // so it alone needs the dynamic offset folded in up front.
1704 const uint32_t filters = dt_dev_get_roi_filters(piece, roi_in);
1705
1708
1709 int demosaicing_method = data->demosaicing_method;
1710
1711 gboolean showmask = FALSE;
1713 {
1715 if(g) showmask = (g->visual_mask);
1716 // take care of passthru modes
1718 demosaicing_method = (piece->dsc_in.filters != 9u) ? DT_IOP_DEMOSAIC_RCD : DT_IOP_DEMOSAIC_MARKESTEIJN;
1720 demosaicing_method = DT_IOP_DEMOSAIC_PASSTHROUGH_MONOCHROME;
1721 }
1722
1724 "[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",
1725 (void *)pipe, _pipe_type_string(pipe->type), (unsigned long)pthread_self(), (void *)piece,
1726 pipe->devid, roi_in->x, roi_in->y, roi_in->width, roi_in->height,
1727 piece->dsc_in.filters, filters, method2string(demosaicing_method));
1728
1729 cl_mem high_image = NULL;
1730 cl_mem low_image = NULL;
1731 cl_mem blend = NULL;
1732 cl_mem details = NULL;
1733 cl_mem dev_aux = NULL;
1734 cl_mem dev_xtrans = NULL;
1735 const gboolean dual = ((demosaicing_method & DEMOSAIC_DUAL) && (data->dual_thrs > 0.0f));
1736 const int devid = pipe->devid;
1737 gboolean retval = FALSE;
1738
1739 if(info) dt_get_times(&start_time);
1740
1741 if(demosaicing_method == DT_IOP_DEMOSAIC_PASSTHROUGH_MONOCHROME ||
1742 demosaicing_method == DT_IOP_DEMOSAIC_PPG ||
1743 demosaicing_method == DT_IOP_DEMOSAIC_PASSTHROUGH_COLOR )
1744 {
1745 if(!process_default_cl(self, pipe, piece, dev_in, dev_out, roi_in, roi_out, demosaicing_method)) return FALSE;
1746 }
1747 else if(_is_downsample_method(demosaicing_method))
1748 {
1749 const int width = roi_out->width;
1750 const int height = roi_out->height;
1751 size_t sizes[3] = { ROUNDUPDWD(width, devid), ROUNDUPDHT(height, devid), 1 };
1752
1753 if(piece->dsc_in.filters == 9u)
1754 {
1755 dev_xtrans = dt_opencl_copy_host_to_device_constant(devid, sizeof(piece->dsc_in.xtrans),
1756 (void *)piece->dsc_in.xtrans);
1757 if(IS_NULL_PTR(dev_xtrans)) goto finish;
1758
1759 dt_opencl_set_kernel_arg(devid, gd->kernel_zoom_half_size_xtrans, 0, sizeof(cl_mem), &dev_in);
1760 dt_opencl_set_kernel_arg(devid, gd->kernel_zoom_half_size_xtrans, 1, sizeof(cl_mem), &dev_out);
1761 dt_opencl_set_kernel_arg(devid, gd->kernel_zoom_half_size_xtrans, 2, sizeof(int), &width);
1762 dt_opencl_set_kernel_arg(devid, gd->kernel_zoom_half_size_xtrans, 3, sizeof(int), &height);
1763 dt_opencl_set_kernel_arg(devid, gd->kernel_zoom_half_size_xtrans, 4, sizeof(int), (void *)&roi_in->x);
1764 dt_opencl_set_kernel_arg(devid, gd->kernel_zoom_half_size_xtrans, 5, sizeof(int), (void *)&roi_in->y);
1765 dt_opencl_set_kernel_arg(devid, gd->kernel_zoom_half_size_xtrans, 6, sizeof(int), (void *)&roi_in->width);
1766 dt_opencl_set_kernel_arg(devid, gd->kernel_zoom_half_size_xtrans, 7, sizeof(int), (void *)&roi_in->height);
1767 dt_opencl_set_kernel_arg(devid, gd->kernel_zoom_half_size_xtrans, 8, sizeof(cl_mem), (void *)&dev_xtrans);
1768 const cl_int err = dt_opencl_enqueue_kernel_2d(devid, gd->kernel_zoom_half_size_xtrans, sizes);
1769 if(err != CL_SUCCESS) goto finish;
1770 }
1771 else
1772 {
1773 const int zero = 0;
1774 const int is_4bayer = self->dev->image_storage.flags & DT_IMAGE_4BAYER;
1775 const float cam_to_rgb_0[4] = { data->CAM_to_RGB[0][0], data->CAM_to_RGB[0][1],
1776 data->CAM_to_RGB[0][2], data->CAM_to_RGB[0][3] };
1777 const float cam_to_rgb_1[4] = { data->CAM_to_RGB[1][0], data->CAM_to_RGB[1][1],
1778 data->CAM_to_RGB[1][2], data->CAM_to_RGB[1][3] };
1779 const float cam_to_rgb_2[4] = { data->CAM_to_RGB[2][0], data->CAM_to_RGB[2][1],
1780 data->CAM_to_RGB[2][2], data->CAM_to_RGB[2][3] };
1781
1782 dt_opencl_set_kernel_arg(devid, gd->kernel_zoom_half_size, 0, sizeof(cl_mem), &dev_in);
1783 dt_opencl_set_kernel_arg(devid, gd->kernel_zoom_half_size, 1, sizeof(cl_mem), &dev_out);
1784 dt_opencl_set_kernel_arg(devid, gd->kernel_zoom_half_size, 2, sizeof(int), &width);
1785 dt_opencl_set_kernel_arg(devid, gd->kernel_zoom_half_size, 3, sizeof(int), &height);
1786 dt_opencl_set_kernel_arg(devid, gd->kernel_zoom_half_size, 4, sizeof(int), &zero);
1787 dt_opencl_set_kernel_arg(devid, gd->kernel_zoom_half_size, 5, sizeof(int), &zero);
1788 dt_opencl_set_kernel_arg(devid, gd->kernel_zoom_half_size, 6, sizeof(int), (void *)&roi_in->width);
1789 dt_opencl_set_kernel_arg(devid, gd->kernel_zoom_half_size, 7, sizeof(int), (void *)&roi_in->height);
1790 dt_opencl_set_kernel_arg(devid, gd->kernel_zoom_half_size, 8, sizeof(float), (void *)&roi_out->scale);
1791 dt_opencl_set_kernel_arg(devid, gd->kernel_zoom_half_size, 9, sizeof(uint32_t), (void *)&filters);
1792 dt_opencl_set_kernel_arg(devid, gd->kernel_zoom_half_size, 10, sizeof(int), &is_4bayer);
1793 dt_opencl_set_kernel_arg(devid, gd->kernel_zoom_half_size, 11, sizeof(cam_to_rgb_0), cam_to_rgb_0);
1794 dt_opencl_set_kernel_arg(devid, gd->kernel_zoom_half_size, 12, sizeof(cam_to_rgb_1), cam_to_rgb_1);
1795 dt_opencl_set_kernel_arg(devid, gd->kernel_zoom_half_size, 13, sizeof(cam_to_rgb_2), cam_to_rgb_2);
1796 const cl_int err = dt_opencl_enqueue_kernel_2d(devid, gd->kernel_zoom_half_size, sizes);
1797 if(err != CL_SUCCESS) goto finish;
1798 }
1799
1800 if(!_downsample_guided_laplacian_postfilter_cl(self, pipe, dev_out, roi_out, data->color_smoothing)) goto finish;
1801
1802 retval = TRUE;
1803 goto finish;
1804 }
1805 else if((demosaicing_method & ~DEMOSAIC_DUAL) == DT_IOP_DEMOSAIC_RCD)
1806 {
1807 if(dual)
1808 {
1809 high_image = dt_opencl_alloc_device(devid, roi_in->width, roi_in->height, sizeof(float) * 4);
1810 if(IS_NULL_PTR(high_image)) return FALSE;
1811 if(!process_rcd_cl(self, pipe, piece, dev_in, high_image, roi_in, roi_in, FALSE)) goto finish;
1812 }
1813 else
1814 {
1815 if(!process_rcd_cl(self, pipe, piece, dev_in, dev_out, roi_in, roi_out, TRUE)) return FALSE;
1816 }
1817 }
1818 else if(demosaicing_method == DT_IOP_DEMOSAIC_VNG4 || demosaicing_method == DT_IOP_DEMOSAIC_VNG)
1819 {
1820 if(!process_vng_cl(self, pipe, piece, dev_in, dev_out, roi_in, roi_out, TRUE, FALSE)) return FALSE;
1821 }
1822 else if(((demosaicing_method & ~DEMOSAIC_DUAL) == DT_IOP_DEMOSAIC_MARKESTEIJN ) ||
1823 ((demosaicing_method & ~DEMOSAIC_DUAL) == DT_IOP_DEMOSAIC_MARKESTEIJN_3))
1824 {
1825 if(dual)
1826 {
1827 high_image = dt_opencl_alloc_device(devid, roi_in->width, roi_in->height, sizeof(float) * 4);
1828 if(IS_NULL_PTR(high_image)) return FALSE;
1829 if(!process_markesteijn_cl(self, pipe, piece, dev_in, high_image, roi_in, roi_in, FALSE)) return FALSE;
1830 }
1831 else
1832 {
1833 if(!process_markesteijn_cl(self, pipe, piece, dev_in, dev_out, roi_in, roi_out, TRUE)) return FALSE;
1834 }
1835 }
1836 else
1837 {
1838 dt_print(DT_DEBUG_OPENCL, "[opencl_demosaic] demosaicing method '%s' not yet supported by opencl code\n", method2string(demosaicing_method));
1839 return FALSE;
1840 }
1841
1842 if(info)
1843 {
1844 const float mpixels = (roi_in->width * roi_in->height) / 1.0e6;
1845 dt_get_times(&end_time);
1846 const float tclock = end_time.clock - start_time.clock;
1847 const float uclock = end_time.user - start_time.user;
1848 fprintf(stderr," [demosaic] process GPU `%s' did %.2fmpix, %.4f secs (%.4f CPU), %.2f pix/us\n",
1849 method2string(demosaicing_method & ~DEMOSAIC_DUAL), mpixels, tclock, uclock, mpixels / tclock);
1850 }
1851 if(!dual)
1852 {
1853 retval = TRUE;
1854 goto finish;
1855 }
1856
1857 // This is dual demosaicing only stuff
1858 const int scaled = (roi_out->width != roi_in->width || roi_out->height != roi_in->height);
1859
1860 int width = roi_out->width;
1861 int height = roi_out->height;
1862 // need to reserve scaled auxiliary buffer or use dev_out
1863 if(scaled)
1864 {
1865 dev_aux = dt_opencl_alloc_device(devid, roi_in->width, roi_in->height, sizeof(float) * 4);
1866 if(IS_NULL_PTR(dev_aux)) goto finish;
1867 width = roi_in->width;
1868 height = roi_in->height;
1869 }
1870 else
1871 dev_aux = dev_out;
1872
1873 // here we have work to be done only for dual demosaicers
1874 blend = dt_opencl_alloc_device_buffer(devid, sizeof(float) * width * height);
1875 details = dt_opencl_alloc_device_buffer(devid, sizeof(float) * width * height);
1876 low_image = dt_opencl_alloc_device(devid, width, height, sizeof(float) * 4);
1877 if((IS_NULL_PTR(blend)) || (IS_NULL_PTR(low_image)) || (IS_NULL_PTR(details))) goto finish;
1878
1879 if(info) dt_get_times(&start_time);
1880 if(process_vng_cl(self, pipe, piece, dev_in, low_image, roi_in, roi_in, FALSE, FALSE))
1881 {
1882 if(!color_smoothing_cl(self, pipe, piece, low_image, low_image, roi_in, 2))
1883 {
1884 retval = FALSE;
1885 goto finish;
1886 }
1887 retval = dual_demosaic_cl(self, pipe, piece, details, blend, high_image, low_image, dev_aux, width, height, showmask);
1888 }
1889
1890 if(info)
1891 {
1892 dt_get_times(&end_time);
1893 fprintf(stderr," [demosaic] GPU dual blending %.4f secs (%.4f CPU)\n", end_time.clock - start_time.clock, end_time.user - start_time.user);
1894 }
1895
1896 if(scaled)
1897 {
1898 // scale aux buffer to output buffer
1899 const int err = dt_iop_clip_and_zoom_roi_cl(devid, dev_out, dev_aux, roi_out, roi_in);
1900 if(err != CL_SUCCESS)
1901 retval = FALSE;
1902 }
1903
1904 finish:
1905 dt_opencl_release_mem_object(high_image);
1909 if(dev_aux != dev_out) dt_opencl_release_mem_object(dev_aux);
1910 dt_opencl_release_mem_object(dev_xtrans);
1911 if(!retval && dual) dt_control_log(_("[dual demosaic_cl] internal problem"));
1912 return retval;
1913}
1914#endif
1915
1917{
1918 const dt_iop_roi_t *const roi_in = &piece->roi_in;
1919 const dt_iop_roi_t *const roi_out = &piece->roi_out;
1921
1922 const float ioratio = (float)roi_out->width * roi_out->height / ((float)roi_in->width * roi_in->height);
1923 const float smooth = data->color_smoothing ? ioratio : 0.0f;
1924 const float greeneq
1925 = ((piece->dsc_in.filters != 9u) && (data->green_eq != DT_IOP_GREEN_EQ_NO)) ? 0.25f : 0.0f;
1926 const dt_iop_demosaic_method_t demosaicing_method = data->demosaicing_method & ~DEMOSAIC_DUAL;
1927
1928 if(demosaicing_method == DT_IOP_DEMOSAIC_DOWNSAMPLE)
1929 {
1930 tiling->factor = 1.0f + ioratio + (data->color_smoothing ? 7.0f * ioratio : 0.0f);
1931 tiling->maxbuf = 1.0f;
1932 tiling->overhead = 0;
1933 tiling->xalign = 1;
1934 tiling->yalign = 1;
1935 tiling->overlap = (piece->dsc_in.filters == 9u) ? 18 : 16;
1936 }
1937 else if((demosaicing_method == DT_IOP_DEMOSAIC_PPG) ||
1938 (demosaicing_method == DT_IOP_DEMOSAIC_PASSTHROUGH_MONOCHROME) ||
1939 (demosaicing_method == DT_IOP_DEMOSAIC_PASSTHROUGH_COLOR) ||
1940 (demosaicing_method == DT_IOP_DEMOSAIC_AMAZE))
1941 {
1942 // Bayer pattern with PPG, Passthrough or Amaze
1943 tiling->factor = 1.0f + ioratio; // in + out
1944 tiling->factor += fmax(1.0f + greeneq, smooth); // + tmp + geeneq | + smooth
1945 tiling->maxbuf = 1.0f;
1946 tiling->overhead = 0;
1947 tiling->xalign = 2;
1948 tiling->yalign = 2;
1949 tiling->overlap = 5; // take care of border handling
1950 }
1951 else if(((demosaicing_method == DT_IOP_DEMOSAIC_MARKESTEIJN) ||
1952 (demosaicing_method == DT_IOP_DEMOSAIC_MARKESTEIJN_3) ||
1953 (demosaicing_method == DT_IOP_DEMOSAIC_FDC)))
1954 {
1955 // X-Trans pattern full Markesteijn processing
1956 const int ndir = (demosaicing_method == DT_IOP_DEMOSAIC_MARKESTEIJN_3) ? 8 : 4;
1957 const int overlap = (demosaicing_method == DT_IOP_DEMOSAIC_MARKESTEIJN_3) ? 18 : 12;
1958
1959 tiling->factor = 1.0f + ioratio;
1960 tiling->factor += ndir * 1.0f // rgb
1961 + ndir * 0.25f // drv
1962 + ndir * 0.125f // homo + homosum
1963 + 1.0f; // aux
1964
1965 tiling->factor += fmax(1.0f + greeneq, smooth);
1966 tiling->maxbuf = 1.0f;
1967 tiling->overhead = 0;
1968 tiling->xalign = XTRANS_SNAPPER;
1969 tiling->yalign = XTRANS_SNAPPER;
1970 tiling->overlap = overlap;
1971 }
1972 else if(demosaicing_method == DT_IOP_DEMOSAIC_RCD)
1973 {
1974 tiling->factor = 1.0f + ioratio;
1975 tiling->factor += fmax(1.0f + greeneq, smooth); // + tmp + geeneq | + smooth
1976 tiling->maxbuf = 1.0f;
1977 tiling->overhead = sizeof(float) * RCD_TILESIZE * RCD_TILESIZE * 8 * MAX(1, dt_get_num_openmp_threads());
1978 tiling->xalign = 2;
1979 tiling->yalign = 2;
1980 // what RCD itself discards at a tile's edges (rcd.c): a pixel closer than that to a pixel-pipe
1981 // tile's edge would not be the one computed untiled
1982 tiling->overlap = RCD_BORDER;
1983 tiling->factor_cl = tiling->factor + 3.0f;
1984 }
1985 else if(demosaicing_method == DT_IOP_DEMOSAIC_LMMSE)
1986 {
1987 tiling->factor = 1.0f + ioratio;
1988 tiling->factor += fmax(1.0f + greeneq, smooth); // + tmp + geeneq | + smooth
1989 tiling->maxbuf = 1.0f;
1990 tiling->overhead = sizeof(float) * LMMSE_GRP * LMMSE_GRP * 6 * MAX(1, dt_get_num_openmp_threads());
1991 tiling->xalign = 2;
1992 tiling->yalign = 2;
1993 tiling->overlap = 10;
1994 }
1995 else
1996 {
1997 // VNG
1998 tiling->factor = 1.0f + ioratio;
1999 tiling->factor += fmax(1.0f + greeneq, smooth);
2000 tiling->maxbuf = 1.0f;
2001 tiling->overhead = 0;
2002 tiling->xalign = 6; // covering Bayer pattern for VNG4 as well as xtrans for VNG
2003 tiling->yalign = 6; // covering Bayer pattern for VNG4 as well as xtrans for VNG
2004 tiling->overlap = 6;
2005 }
2007 {
2008 // make sure VNG4 is also possible
2009 tiling->factor += 1.0f;
2010 tiling->xalign = MAX(6, tiling->xalign);
2011 tiling->yalign = MAX(6, tiling->yalign);
2012 tiling->overlap = MAX(6, tiling->overlap);
2013 }
2014 return;
2015}
2016#undef LMMSE_GRP
2017
2019{
2020 const int program = 0; // from programs.conf
2023 module->data = gd;
2024 gd->kernel_zoom_half_size = dt_opencl_create_kernel(program, "clip_and_zoom_demosaic_half_size");
2025 gd->kernel_ppg_green = dt_opencl_create_kernel(program, "ppg_demosaic_green");
2026 gd->kernel_green_eq_lavg = dt_opencl_create_kernel(program, "green_equilibration_lavg");
2027 gd->kernel_green_eq_favg_reduce_first = dt_opencl_create_kernel(program, "green_equilibration_favg_reduce_first");
2028 gd->kernel_green_eq_favg_reduce_second = dt_opencl_create_kernel(program, "green_equilibration_favg_reduce_second");
2029 gd->kernel_green_eq_favg_apply = dt_opencl_create_kernel(program, "green_equilibration_favg_apply");
2030 gd->kernel_pre_median = dt_opencl_create_kernel(program, "pre_median");
2031 gd->kernel_ppg_redblue = dt_opencl_create_kernel(program, "ppg_demosaic_redblue");
2032 gd->kernel_downsample = dt_opencl_create_kernel(program, "clip_and_zoom");
2033 gd->kernel_border_interpolate = dt_opencl_create_kernel(program, "border_interpolate");
2034 gd->kernel_color_smoothing = dt_opencl_create_kernel(program, "color_smoothing");
2035
2036 const int other = 14; // from programs.conf
2037 gd->kernel_passthrough_monochrome = dt_opencl_create_kernel(other, "passthrough_monochrome");
2038 gd->kernel_passthrough_color = dt_opencl_create_kernel(other, "passthrough_color");
2039 gd->kernel_zoom_passthrough_monochrome = dt_opencl_create_kernel(other, "clip_and_zoom_demosaic_passthrough_monochrome");
2040
2041 const int vng = 15; // from programs.conf
2042 gd->kernel_vng_border_interpolate = dt_opencl_create_kernel(vng, "vng_border_interpolate");
2043 gd->kernel_vng_lin_interpolate = dt_opencl_create_kernel(vng, "vng_lin_interpolate");
2044 gd->kernel_zoom_third_size = dt_opencl_create_kernel(vng, "clip_and_zoom_demosaic_third_size_xtrans");
2045 gd->kernel_zoom_half_size_xtrans = dt_opencl_create_kernel(vng, "clip_and_zoom_demosaic_half_size_xtrans");
2046 gd->kernel_vng_green_equilibrate = dt_opencl_create_kernel(vng, "vng_green_equilibrate");
2047 gd->kernel_vng_interpolate = dt_opencl_create_kernel(vng, "vng_interpolate");
2048
2049 const int markesteijn = 16; // from programs.conf
2050 gd->kernel_markesteijn_initial_copy = dt_opencl_create_kernel(markesteijn, "markesteijn_initial_copy");
2051 gd->kernel_markesteijn_green_minmax = dt_opencl_create_kernel(markesteijn, "markesteijn_green_minmax");
2052 gd->kernel_markesteijn_interpolate_green = dt_opencl_create_kernel(markesteijn, "markesteijn_interpolate_green");
2053 gd->kernel_markesteijn_solitary_green = dt_opencl_create_kernel(markesteijn, "markesteijn_solitary_green");
2054 gd->kernel_markesteijn_recalculate_green = dt_opencl_create_kernel(markesteijn, "markesteijn_recalculate_green");
2055 gd->kernel_markesteijn_red_and_blue = dt_opencl_create_kernel(markesteijn, "markesteijn_red_and_blue");
2056 gd->kernel_markesteijn_interpolate_twoxtwo = dt_opencl_create_kernel(markesteijn, "markesteijn_interpolate_twoxtwo");
2057 gd->kernel_markesteijn_convert_yuv = dt_opencl_create_kernel(markesteijn, "markesteijn_convert_yuv");
2058 gd->kernel_markesteijn_differentiate = dt_opencl_create_kernel(markesteijn, "markesteijn_differentiate");
2059 gd->kernel_markesteijn_homo_threshold = dt_opencl_create_kernel(markesteijn, "markesteijn_homo_threshold");
2060 gd->kernel_markesteijn_homo_set = dt_opencl_create_kernel(markesteijn, "markesteijn_homo_set");
2061 gd->kernel_markesteijn_homo_sum = dt_opencl_create_kernel(markesteijn, "markesteijn_homo_sum");
2062 gd->kernel_markesteijn_homo_max = dt_opencl_create_kernel(markesteijn, "markesteijn_homo_max");
2063 gd->kernel_markesteijn_homo_max_corr = dt_opencl_create_kernel(markesteijn, "markesteijn_homo_max_corr");
2064 gd->kernel_markesteijn_homo_quench = dt_opencl_create_kernel(markesteijn, "markesteijn_homo_quench");
2065 gd->kernel_markesteijn_zero = dt_opencl_create_kernel(markesteijn, "markesteijn_zero");
2066 gd->kernel_markesteijn_accu = dt_opencl_create_kernel(markesteijn, "markesteijn_accu");
2067 gd->kernel_markesteijn_final = dt_opencl_create_kernel(markesteijn, "markesteijn_final");
2068
2069 const int rcd = 31; // from programs.conf
2070 gd->kernel_rcd_populate = dt_opencl_create_kernel(rcd, "rcd_populate");
2071 gd->kernel_rcd_write_output = dt_opencl_create_kernel(rcd, "rcd_write_output");
2072 gd->kernel_rcd_step_1 = dt_opencl_create_kernel(rcd, "rcd_step_1");
2073 gd->kernel_rcd_step_2_1 = dt_opencl_create_kernel(rcd, "rcd_step_2_1");
2074 gd->kernel_rcd_step_3_1 = dt_opencl_create_kernel(rcd, "rcd_step_3_1");
2075 gd->kernel_rcd_step_4_1 = dt_opencl_create_kernel(rcd, "rcd_step_4_1");
2076 gd->kernel_rcd_step_4_2 = dt_opencl_create_kernel(rcd, "rcd_step_4_2");
2077 gd->kernel_rcd_step_5_1 = dt_opencl_create_kernel(rcd, "rcd_step_5_1");
2078 gd->kernel_rcd_step_5_2 = dt_opencl_create_kernel(rcd, "rcd_step_5_2");
2079 gd->kernel_rcd_border_redblue = dt_opencl_create_kernel(rcd, "rcd_border_redblue");
2080 gd->kernel_rcd_border_green = dt_opencl_create_kernel(rcd, "rcd_border_green");
2081 gd->kernel_write_blended_dual = dt_opencl_create_kernel(rcd, "write_blended_dual");
2082
2083 const int wavelets = 35; // bspline.cl, from programs.conf
2084 gd->kernel_guided_laplacian_coefficients = dt_opencl_create_kernel(wavelets, "guided_laplacian_coefficients");
2085 gd->kernel_guided_laplacian_normalize = dt_opencl_create_kernel(wavelets, "guided_laplacian_normalize");
2086 gd->kernel_guided_laplacian_apply = dt_opencl_create_kernel(wavelets, "guided_laplacian_apply");
2087 gd->kernel_guided_laplacian_finalize = dt_opencl_create_kernel(wavelets, "guided_laplacian_finalize");
2088 gd->kernel_bspline_horizontal = dt_opencl_create_kernel(wavelets, "blur_2D_Bspline_horizontal");
2089 gd->kernel_bspline_vertical = dt_opencl_create_kernel(wavelets, "blur_2D_Bspline_vertical");
2090 gd->kernel_bspline_horizontal_local = dt_opencl_create_kernel(wavelets, "blur_2D_Bspline_horizontal_local");
2091 gd->kernel_bspline_vertical_local = dt_opencl_create_kernel(wavelets, "blur_2D_Bspline_vertical_local");
2092 gd->lmmse_gamma_in = NULL;
2093 gd->lmmse_gamma_out = NULL;
2094}
2095
2097{
2159 dt_free(module->data);
2160}
2161
2162
2163gboolean force_enable(struct dt_iop_module_t *self, const gboolean current_state)
2164{
2165 // Demosaicing applies if and only if the buffer carries a CFA mosaic. Gate on the mosaic
2166 // axis, not on the historical "raw" flag: an already-demosaiced raw (sRAW / linear DNG) must
2167 // not be demosaiced, while a mosaiced raw that the RAW flag missed still must.
2168 // Mandatory module: the decision is purely image metadata and ignores current_state.
2169 const gboolean state = dt_image_needs_demosaic(&self->dev->image_storage);
2170 dt_iop_fmt_log(self, "force_enable: class=%s needs_demosaic=%d current=%d -> %d",
2172 state, current_state, state);
2173 return state;
2174}
2175
2176
2179{
2182
2183 d->green_eq = p->green_eq;
2184 d->color_smoothing = p->color_smoothing;
2185 d->median_thrs = p->median_thrs;
2186 d->dual_thrs = p->dual_thrs;
2187 d->lmmse_refine = p->lmmse_refine;
2188 dt_iop_demosaic_method_t use_method = p->demosaicing_method;
2189 const gboolean xmethod = use_method & DEMOSAIC_XTRANS;
2190 const gboolean bayer = (self->dev->image_storage.dsc.filters != 9u);
2191 const gboolean downsample = _is_downsample_method(use_method);
2192
2193 if(!downsample)
2194 {
2195 if(bayer && xmethod) use_method = DT_IOP_DEMOSAIC_RCD;
2196 if(!bayer && !xmethod) use_method = DT_IOP_DEMOSAIC_MARKESTEIJN;
2197 }
2198
2201 if(use_method == DT_IOP_DEMOSAIC_PASSTHROUGH_COLOR || use_method == DT_IOP_DEMOSAIC_PASSTHR_COLORX)
2203
2204 const gboolean passing = (use_method == DT_IOP_DEMOSAIC_PASSTHROUGH_MONOCHROME
2205 || use_method == DT_IOP_DEMOSAIC_PASSTHROUGH_COLOR);
2206
2207 if(!(use_method == DT_IOP_DEMOSAIC_PPG))
2208 d->median_thrs = 0.0f;
2209
2210 if(passing)
2211 {
2212 d->green_eq = DT_IOP_GREEN_EQ_NO;
2213 d->color_smoothing = 0;
2214 d->dual_thrs = 0.0f;
2215 }
2216 else if(downsample)
2217 {
2218 d->green_eq = DT_IOP_GREEN_EQ_NO;
2219 d->dual_thrs = 0.0f;
2220 }
2221
2222 if(use_method & DEMOSAIC_DUAL)
2223 d->color_smoothing = 0;
2224
2225 d->demosaicing_method = use_method;
2226
2227 // OpenCL only supported by some of the demosaicing methods
2228 switch(d->demosaicing_method)
2229 {
2231 piece->process_cl_ready = 1;
2232 break;
2234 piece->process_cl_ready = 0;
2235 break;
2237 piece->process_cl_ready = 1;
2238 break;
2240 piece->process_cl_ready = 1;
2241 break;
2243 piece->process_cl_ready = 1;
2244 break;
2246 piece->process_cl_ready = 1;
2247 break;
2249 piece->process_cl_ready = 0;
2250 break;
2252 piece->process_cl_ready = 1;
2253 break;
2255 piece->process_cl_ready = 0;
2256 break;
2258 piece->process_cl_ready = 1;
2259 break;
2261 piece->process_cl_ready = 1;
2262 break;
2264 piece->process_cl_ready = 1;
2265 break;
2267 piece->process_cl_ready = 1;
2268 break;
2270 piece->process_cl_ready = 0;
2271 break;
2273 piece->process_cl_ready = 1;
2274 break;
2275 default:
2276 piece->process_cl_ready = 0;
2277 }
2278
2279 // green-equilibrate over full image excludes tiling
2280 if((d->green_eq == DT_IOP_GREEN_EQ_FULL
2281 || d->green_eq == DT_IOP_GREEN_EQ_BOTH)
2282 || ((use_method & DEMOSAIC_DUAL) && (d->dual_thrs > 0.0f)))
2283 {
2284 piece->process_tiling_ready = 0;
2285 }
2286
2288 {
2289 // 4Bayer images only support the dedicated half-size downsample path in OpenCL.
2290 if(d->demosaicing_method != DT_IOP_DEMOSAIC_DOWNSAMPLE) piece->process_cl_ready = 0;
2291
2292 // Get and store the matrix to go from camera to RGB for 4Bayer images
2294 NULL, d->CAM_to_RGB,
2295 self->dev->image_storage.d65_color_matrix, NULL))
2296 {
2297 const char *camera = self->dev->image_storage.camera_makermodel;
2298 gchar *item = g_strdup_printf("%s (%s)", self->dev->image_storage.fullpath, camera);
2299 dt_control_alert(_("Camera data missing"), _("color matrix not found for 4bayer image!"), item);
2300 dt_free(item);
2301 }
2302 }
2303
2304 dt_iop_fmt_log(self, "commit: class=%s in(filters=%u ch=%i) method=%d passthrough=%d -> enabled=%d cl_ready=%d",
2306 piece->dsc_in.filters, piece->dsc_in.channels, d->demosaicing_method, passing,
2307 piece->enabled, piece->process_cl_ready);
2308}
2309
2315
2317{
2318 dt_free_align(piece->data);
2319 piece->data = NULL;
2320}
2321
2323{
2325
2327 d->demosaicing_method = DT_IOP_DEMOSAIC_PASSTHROUGH_MONOCHROME;
2328 else if(module->dev->image_storage.dsc.filters == 9u)
2329 d->demosaicing_method = DT_IOP_DEMOSAIC_MARKESTEIJN;
2330 else
2331 d->demosaicing_method = DT_IOP_DEMOSAIC_RCD;
2332
2333 module->hide_enable_button = 1;
2334
2335 // Enabled iff the buffer is mosaiced (see force_enable): keeps demosaic off for
2336 // already-demosaiced sRAW / linear DNG and on for monochrome Bayer sensors.
2337 module->default_enabled = dt_image_needs_demosaic(&module->dev->image_storage);
2338 dt_iop_fmt_log(module, "reload_defaults: class=%s needs_demosaic=%d filters=%u mono=%d method=%d -> default_enabled=%d",
2341 module->dev->image_storage.dsc.filters,
2343 d->demosaicing_method, module->default_enabled);
2344 // Stack visibility (raw vs non_raw) is set from default_enabled in gui_update() (which already
2345 // does exactly this), so reload_defaults() stays params-only.
2346}
2347
2348void gui_changed(dt_iop_module_t *self, GtkWidget *w, void *previous)
2349{
2352
2353 const gboolean bayer = (self->dev->image_storage.dsc.filters != 9u);
2354 dt_iop_demosaic_method_t use_method = p->demosaicing_method;
2355 const gboolean xmethod = use_method & DEMOSAIC_XTRANS;
2356
2357 if(!_is_downsample_method(use_method))
2358 {
2359 if(bayer && xmethod) use_method = DT_IOP_DEMOSAIC_RCD;
2360 if(!bayer && !xmethod) use_method = DT_IOP_DEMOSAIC_MARKESTEIJN;
2361 }
2362
2363 const gboolean isppg = (use_method == DT_IOP_DEMOSAIC_PPG);
2364 const gboolean isdownsample = _is_downsample_method(use_method);
2365 const gboolean isdual = !isdownsample && (use_method & DEMOSAIC_DUAL);
2366 const gboolean islmmse = (use_method == DT_IOP_DEMOSAIC_LMMSE);
2367 const gboolean passing = ((use_method == DT_IOP_DEMOSAIC_PASSTHROUGH_MONOCHROME) ||
2368 (use_method == DT_IOP_DEMOSAIC_PASSTHROUGH_COLOR) ||
2369 (use_method == DT_IOP_DEMOSAIC_PASSTHR_MONOX) ||
2370 (use_method == DT_IOP_DEMOSAIC_PASSTHR_COLORX));
2371
2372 gtk_widget_set_visible(g->demosaic_method_bayer, bayer);
2373 gtk_widget_set_visible(g->demosaic_method_xtrans, !bayer);
2374 if(bayer)
2375 dt_bauhaus_combobox_set_from_value(g->demosaic_method_bayer, p->demosaicing_method);
2376 else
2377 dt_bauhaus_combobox_set_from_value(g->demosaic_method_xtrans, p->demosaicing_method);
2378
2379 gtk_widget_set_visible(g->median_thrs, bayer && isppg);
2380 gtk_widget_set_visible(g->greeneq, !passing && !isdownsample);
2381 gtk_widget_set_visible(g->color_smoothing, !passing && !isdual);
2382 gtk_widget_set_visible(g->dual_thrs, isdual);
2383 gtk_widget_set_visible(g->lmmse_refine, islmmse);
2384
2385 dt_image_t *img = dt_image_cache_get(self->dev->image_storage.id, 'w');
2386 if(!img) return;
2387 if((p->demosaicing_method == DT_IOP_DEMOSAIC_PASSTHROUGH_MONOCHROME) ||
2388 (p->demosaicing_method == DT_IOP_DEMOSAIC_PASSTHR_MONOX))
2390 else
2391 img->flags &= ~DT_IMAGE_MONOCHROME_BAYER;
2393}
2394void gui_update(struct dt_iop_module_t *self)
2395{
2398
2399 g->visual_mask = FALSE;
2400 gui_changed(self, NULL, NULL);
2401
2402 gtk_stack_set_visible_child_name(GTK_STACK(self->gui->widget), self->default_enabled ? "raw" : "non_raw");
2403}
2404
2405static void _visualize_callback(GtkWidget *quad, gpointer user_data)
2406{
2407 if(dt_gui_widgets_suppressed()) return;
2408 dt_iop_module_t *self = (dt_iop_module_t *)user_data;
2410
2411 g->visual_mask = dt_bauhaus_widget_get_quad_active(quad);
2413}
2414
2415void gui_focus(struct dt_iop_module_t *self, gboolean in)
2416{
2418 if(!in)
2419 {
2420 const gboolean was_dualmask = g->visual_mask;
2422 g->visual_mask = FALSE;
2423 if(was_dualmask) dt_dev_pixelpipe_update_history_main(self->dev);
2424 }
2425}
2426
2427void gui_init(struct dt_iop_module_t *self)
2428{
2430
2431 GtkWidget *box_raw = self->gui->widget = gtk_box_new(GTK_ORIENTATION_VERTICAL, DT_GUI_BOX_SPACING);
2432
2433 g->demosaic_method_bayer = dt_bauhaus_combobox_from_params(self, "demosaicing_method");
2434 for(int i=0;i<7;i++) dt_bauhaus_combobox_remove_at(g->demosaic_method_bayer, 9);
2435 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."));
2436
2437 g->demosaic_method_xtrans = dt_bauhaus_combobox_from_params(self, "demosaicing_method");
2438 for(int i=0;i<9;i++) dt_bauhaus_combobox_remove_at(g->demosaic_method_xtrans, 0);
2439 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."));
2440
2441 g->median_thrs = dt_bauhaus_slider_from_params(self, "median_thrs");
2442 dt_bauhaus_slider_set_digits(g->median_thrs, 3);
2443 gtk_widget_set_tooltip_text(g->median_thrs, _("threshold for edge-aware median.\nset to 0.0 to switch off\n"
2444 "set to 1.0 to ignore edges"));
2445
2446 g->dual_thrs = dt_bauhaus_slider_from_params(self, "dual_thrs");
2447 dt_bauhaus_slider_set_digits(g->dual_thrs, 2);
2448 gtk_widget_set_tooltip_text(g->dual_thrs, _("contrast threshold for dual demosaic.\nset to 0.0 for high frequency content\n"
2449 "set to 1.0 for flat content\ntoggle to visualize the mask"));
2453 g_signal_connect(G_OBJECT(g->dual_thrs), "quad-pressed", G_CALLBACK(_visualize_callback), self);
2454
2455 g->lmmse_refine = dt_bauhaus_combobox_from_params(self, "lmmse_refine");
2456 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"));
2457
2458 g->color_smoothing = dt_bauhaus_combobox_from_params(self, "color_smoothing");
2459 gtk_widget_set_tooltip_text(g->color_smoothing, _("how many post-demosaic smoothing passes.\nin downsample mode this sets the guided detail equalization iterations"));
2460
2461 g->greeneq = dt_bauhaus_combobox_from_params(self, "green_eq");
2462 gtk_widget_set_tooltip_text(g->greeneq, _("green channels matching method"));
2463
2464 // start building top level widget
2465 self->gui->widget = gtk_stack_new();
2466 gtk_stack_set_homogeneous(GTK_STACK(self->gui->widget), FALSE);
2467
2468 GtkWidget *label_non_raw = dt_ui_label_new(_("not applicable"));
2469 gtk_widget_set_tooltip_text(label_non_raw, _("demosaicing is only used for color raw images"));
2470
2471 gtk_stack_add_named(GTK_STACK(self->gui->widget), label_non_raw, "non_raw");
2472 gtk_stack_add_named(GTK_STACK(self->gui->widget), box_raw, "raw");
2473}
2474
2475// clang-format off
2476// modelines: These editor modelines have been set for all relevant files by tools/update_modelines.py
2477// vim: shiftwidth=2 expandtab tabstop=2 cindent
2478// kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-spaces modified;
2479// 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:170
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:3346
gboolean dt_bauhaus_combobox_set_from_value(GtkWidget *widget, int value)
Definition bauhaus.c:2122
void dt_bauhaus_widget_set_quad_toggle(GtkWidget *widget, int toggle)
Definition bauhaus.c:1575
void dt_bauhaus_widget_set_quad_active(GtkWidget *widget, int active)
Definition bauhaus.c:1581
int dt_bauhaus_widget_get_quad_active(GtkWidget *widget)
Definition bauhaus.c:1598
void dt_bauhaus_combobox_remove_at(GtkWidget *widget, int pos)
Definition bauhaus.c:1912
void dt_bauhaus_widget_set_quad_paint(GtkWidget *widget, dt_bauhaus_quad_paint_f f, int paint_flags, void *paint_data)
Definition bauhaus.c:1557
static void blur_2D_Bspline(const float *const restrict in, float *const restrict out, const size_t width, const size_t height)
Definition blurs.c:138
#define BSPLINE_FSIZE
Definition bspline.h:34
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:351
typedef void((*dt_cache_allocate_t)(void *userdata, dt_cache_entry_t *entry))
static float intp(const float a, const float b, const float c)
Definition cacorrect.c:185
return vector dt_simd_set1(valid ?(scaling+NORM_MIN) :NORM_MIN)
@ IOP_CS_RAW
@ IOP_CS_RGB
static const float x
const float *const const float coeff[3]
__DT_CLONE_TARGETS__ int dt_colorspaces_conversion_matrices_rgb(const float *adobe_XYZ_to_CAM, double(*out_RGB_to_CAM)[3], double(*out_CAM_to_RGB)[4], const float *embedded_matrix, double *mul)
Compute the sRGB->camera and camera->sRGB matrices, and the default white balance multipliers.
__DT_CLONE_TARGETS__ void dt_colorspaces_cygm_to_rgb(float *out, int num, double CAM_to_RGB[3][4])
Convert a 4-channel CYGM buffer to RGB, in place.
The colour-profile module's API: which profiles exist, and how to apply one.
struct _GtkWidget GtkWidget
GtkWidget, opaque, spelled exactly as GTK spells it.
Definition colorspaces.h:98
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
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)
void dt_control_alert(const char *title, const char *message, const char *item)
Tell the user something that must not go unseen: what it says stays true after a toast would have fad...
Definition control.c:883
void dt_control_log(const char *msg,...)
Definition control.c:839
void reset(dt_view_t *self)
Definition darkroom.c:1222
int dt_get_num_openmp_threads(void)
Number of OpenMP threads the application decided to use.
Definition darktable.c:521
#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:122
@ DT_IOP_DEMOSAIC_MARKESTEIJN_3
Definition demosaic.c:136
@ DT_IOP_DEMOSAIC_RCD
Definition demosaic.c:127
@ DT_IOP_DEMOSAIC_RCD_VNG
Definition demosaic.c:129
@ DT_IOP_DEMOSAIC_MARKESTEIJN
Definition demosaic.c:135
@ DT_IOP_DEMOSAIC_PASSTHROUGH_COLOR
Definition demosaic.c:132
@ DT_IOP_DEMOSAIC_PASSTHR_MONOX
Definition demosaic.c:139
@ DT_IOP_DEMOSAIC_VNG4
Definition demosaic.c:126
@ DT_IOP_DEMOSAIC_PPG
Definition demosaic.c:124
@ DT_IOP_DEMOSAIC_MARKEST3_VNG
Definition demosaic.c:138
@ DT_IOP_DEMOSAIC_LMMSE
Definition demosaic.c:128
@ DT_IOP_DEMOSAIC_VNG
Definition demosaic.c:134
@ DT_IOP_DEMOSAIC_AMAZE_VNG
Definition demosaic.c:130
@ DT_IOP_DEMOSAIC_DOWNSAMPLE
Definition demosaic.c:141
@ DT_IOP_DEMOSAIC_PASSTHR_COLORX
Definition demosaic.c:140
@ DT_IOP_DEMOSAIC_FDC
Definition demosaic.c:137
@ DT_IOP_DEMOSAIC_PASSTHROUGH_MONOCHROME
Definition demosaic.c:131
@ DT_IOP_DEMOSAIC_AMAZE
Definition demosaic.c:125
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:928
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:1256
const char ** description(struct dt_iop_module_t *self)
Definition demosaic.c:316
int default_group()
Definition demosaic.c:325
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:955
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:1441
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:481
void reload_defaults(dt_iop_module_t *module)
Definition demosaic.c:2322
static const dt_geometry_vtable_t _demosaic_geometry_vtable
Definition demosaic.c:981
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:2177
dt_iop_demosaic_smooth_t
Definition demosaic.c:154
@ DEMOSAIC_SMOOTH_2
Definition demosaic.c:157
@ DEMOSAIC_SMOOTH_3
Definition demosaic.c:158
@ DEMOSAIC_SMOOTH_1
Definition demosaic.c:156
@ DEMOSAIC_SMOOTH_OFF
Definition demosaic.c:155
@ DEMOSAIC_SMOOTH_4
Definition demosaic.c:159
@ DEMOSAIC_SMOOTH_5
Definition demosaic.c:160
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:625
dt_iop_demosaic_lmmse_t
Definition demosaic.c:164
@ LMMSE_REFINE_0
Definition demosaic.c:165
@ LMMSE_REFINE_3
Definition demosaic.c:168
@ LMMSE_REFINE_4
Definition demosaic.c:169
@ LMMSE_REFINE_2
Definition demosaic.c:167
@ LMMSE_REFINE_1
Definition demosaic.c:166
#define DEMOSAIC_DUAL
Definition demosaic.c:110
void gui_focus(struct dt_iop_module_t *self, gboolean in)
Definition demosaic.c:2415
void init_pipe(struct dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece)
Definition demosaic.c:2310
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:810
dt_iop_demosaic_greeneq_t
Definition demosaic.c:145
@ DT_IOP_GREEN_EQ_LOCAL
Definition demosaic.c:147
@ DT_IOP_GREEN_EQ_FULL
Definition demosaic.c:148
@ DT_IOP_GREEN_EQ_BOTH
Definition demosaic.c:149
@ DT_IOP_GREEN_EQ_NO
Definition demosaic.c:146
const char * name()
Definition demosaic.c:311
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:384
gboolean geometry_record(dt_iop_module_t *self, const void *params, dt_geometry_record_t *record)
Definition demosaic.c:987
void gui_update(struct dt_iop_module_t *self)
Definition demosaic.c:2394
#define DEMOSAIC_XTRANS
Definition demosaic.c:109
void gui_init(struct dt_iop_module_t *self)
Definition demosaic.c:2427
static gboolean _is_downsample_method(const dt_iop_demosaic_method_t method)
Definition demosaic.c:455
void gui_changed(dt_iop_module_t *self, GtkWidget *w, void *previous)
Definition demosaic.c:2348
static void _demosaic_geometry_map_size(const void *data, const dt_iop_roi_t *const in, dt_iop_roi_t *out)
Definition demosaic.c:976
dt_iop_demosaic_quality_t
Definition demosaic.c:260
@ DT_DEMOSAIC_BEST
Definition demosaic.c:263
@ DT_DEMOSAIC_FAIR
Definition demosaic.c:262
@ DT_DEMOSAIC_FAST
Definition demosaic.c:261
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:1916
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:770
void cleanup_global(dt_iop_module_so_t *module)
Definition demosaic.c:2096
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:681
int default_colorspace(dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, const dt_dev_pixelpipe_iop_t *piece)
Definition demosaic.c:335
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:376
int flags()
Definition demosaic.c:330
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:543
#define DOWNSAMPLE_GUIDED_SCALES
Definition demosaic.c:117
__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:1043
gboolean force_enable(struct dt_iop_module_t *self, const gboolean current_state)
Definition demosaic.c:2163
void cleanup_pipe(struct dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece)
Definition demosaic.c:2316
static void _visualize_callback(GtkWidget *quad, gpointer user_data)
Definition demosaic.c:2405
void init_global(dt_iop_module_so_t *module)
Definition demosaic.c:2018
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:1691
static void _demosaic_map_size(const gboolean downsamples, const dt_iop_roi_t *const roi_in, dt_iop_roi_t *roi_out)
Input rect -> output rect. THE size evaluator: modify_roi_out() below and the geometry service's reco...
Definition demosaic.c:939
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:1005
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:340
#define XTRANS_SNAPPER
Definition demosaic.c:116
void dt_iop_params_t
Definition dev_history.h:43
#define dt_dev_pixelpipe_update_history_main(dev)
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)
@ DT_DEV_PIXELPIPE_DISPLAY_PASSTHRU_MONO
Definition develop.h:142
@ DT_DEV_PIXELPIPE_DISPLAY_PASSTHRU
Definition develop.h:141
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:116
@ TYPE_FLOAT
Definition format.h:56
Where things are on the image, answered without a pipeline.
@ DT_IMAGE_MONOCHROME_BAYER
Definition image.h:160
@ DT_IMAGE_4BAYER
Definition image.h:149
void dt_image_cache_write_release(dt_image_t *img, dt_image_cache_write_mode_t mode)
dt_image_t * dt_image_cache_get(const int32_t imgid, char mode)
@ DT_IMAGE_CACHE_RELAXED
Definition image_cache.h:50
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:91
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:139
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:1909
#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:535
@ IOP_FLAGS_ALLOW_TILING
Definition imageop.h:188
@ IOP_FLAGS_ONE_INSTANCE
Definition imageop.h:191
@ IOP_GROUP_TECHNICAL
Definition imageop.h:162
GtkWidget * dt_bauhaus_slider_from_params(dt_iop_module_t *self, const char *param)
GtkWidget * dt_bauhaus_combobox_from_params(dt_iop_module_t *self, const char *param)
static dt_iop_gui_data_t * dt_iop_gui_data(const struct dt_iop_module_t *m)
The module's GUI data blob, NULL-safe for headless callers: IOP process() implementations read it for...
Definition imageop_gui.h:81
#define IOP_GUI_ALLOC(module)
Definition imageop_gui.h:93
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
GtkWidget * dt_ui_label_new(const gchar *str)
Definition label.c:125
static const dt_dev_pixelpipe_t * pipe
Definition layers.h:49
static const dt_dev_pixelpipe_t const dt_dev_pixelpipe_iop_t * piece
Definition layers.h:50
static const dt_iop_drawlayer_params_t * params
Definition layers.h:80
#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
_lib_location_type_t type
Definition location.c:1
@ DT_DEBUG_OPENCL
Definition logging.h:57
@ DT_DEBUG_DEMOSAIC
Definition logging.h:72
@ DT_DEBUG_PERF
Definition logging.h:55
int32_t dt_get_debug_flags(void)
Definition darktable.c:2094
void dt_print(dt_debug_thread_t thread, const char *msg,...) __attribute__((format(printf
Print to stdout when thread is enabled, prefixed with seconds since startup.
float *const restrict const size_t k
#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 macros.h:96
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])
The detail-mask pixel math: a Scharr-style detail estimate over a raw or scene-referred buffer,...
#define dt_free_align(ptr)
Release memory from dt_alloc_align() and set ptr to NULL.
Definition mem_alloc.h:214
static void * dt_calloc_align(size_t size)
dt_alloc_align() followed by a zero fill.
Definition mem_alloc.h:225
static void dt_free_gpointer(gpointer ptr)
g_free() one pointer, with the signature GDestroyNotify wants.
Definition mem_alloc.h:184
#define dt_free(ptr)
g_free() ptr and set it to NULL, skipping both if it is already NULL.
Definition mem_alloc.h:171
uint32_t width
Definition mipmap_cache.c:0
uint32_t height
Definition mipmap_cache.c:1
#define o
#define DT_MODULE_INTROSPECTION(MODVER, PARAMSTYPE)
DT_MODULE() for a module whose params struct is introspected.
int dt_opencl_local_buffer_opt(const int devid, const int kernel, dt_opencl_local_buffer_t *factors)
Definition opencl.c:3786
int dt_opencl_enqueue_kernel_2d(const int dev, const int kernel, const size_t *sizes)
Definition opencl.c:2612
void * dt_opencl_alloc_device_buffer(const int devid, const size_t size)
Definition opencl.c:3028
void * dt_opencl_alloc_device(const int devid, const int width, const int height, const int bpp)
Definition opencl.c:2952
int dt_opencl_create_kernel(const int prog, const char *name)
Definition opencl.c:2506
void * dt_opencl_copy_host_to_device_constant(const int devid, const size_t size, void *host)
Definition opencl.c:2808
void dt_opencl_free_kernel(const int kernel)
Definition opencl.c:2549
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:2603
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:2618
void dt_opencl_release_mem_object(cl_mem mem)
Definition opencl.c:2863
#define DT_OPENCL_DEFAULT_ERROR
Definition opencl.h:61
#define ROUNDUP(a, n)
Definition opencl.h:82
#define ROUNDUPDHT(a, b)
Definition opencl.h:86
#define ROUNDUPDWD(a, b)
Definition opencl.h:85
#define dt_omploop_sfence()
Definition openmp.h:164
#define __OMP_PARALLEL_FOR__(...)
Definition openmp.h:95
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
void dt_iop_buffer_dsc_update_bpp(dt_iop_buffer_dsc_t *dsc)
dt_dev_pixelpipe_type_t
Definition pixelpipe.h:40
@ DT_DEV_PIXELPIPE_THUMBNAIL
Definition pixelpipe.h:45
@ DT_DEV_PIXELPIPE_EXPORT
Definition pixelpipe.h:42
@ DT_DEV_PIXELPIPE_PREVIEW
Definition pixelpipe.h:44
@ DT_DEV_PIXELPIPE_FULL
Definition pixelpipe.h:43
#define dt_pixelpipe_cache_alloc_align_float_cache(pixels, id)
#define dt_pixelpipe_cache_free_align(mem)
#define dt_pixelpipe_cache_alloc_align_float(pixels, pipe)
#define dt_pixelpipe_cache_alloc_perthread_float(n, padded_size)
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:571
#define RCD_TILESIZE
Definition rcd.c:60
#define eps
Definition rcd.c:87
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:282
#define RCD_BORDER
Definition rcd.c:80
static const dt_aligned_pixel_simd_t const dt_aligned_pixel_simd_t row1
Definition simd.h:190
DT_ALIGNED_PIXEL float dt_aligned_pixel_t[4]
Definition simd.h:53
float dt_aligned_pixel_simd_t __attribute__((vector_size(16), aligned(16)))
Apply one channel's tone curve to each of the three colour channels, or pass the channel through unto...
Definition simd.h:55
static const dt_aligned_pixel_simd_t const dt_aligned_pixel_simd_t const dt_aligned_pixel_simd_t row2
Definition simd.h:191
static const dt_aligned_pixel_simd_t row0
Definition simd.h:189
const float uint32_t state[4]
dt_dev_pixelpipe_type_t type
int32_t gui_attached
Definition develop.h:167
dt_image_t image_storage
Definition develop.h:225
One module instance's contribution, as data.
Definition geometry.h:99
const dt_geometry_vtable_t * vtable
Definition geometry.h:110
void(* free_data)(void *data)
Definition geometry.h:112
A module's geometry, evaluated. Pure functions of the record's own data.
Definition geometry.h:75
void(* map_size)(const void *data, const dt_iop_roi_t *const in, dt_iop_roi_t *out)
Full-resolution input rect -> output rect. Mirrors modify_roi_out() at scale 1.
Definition geometry.h:77
char camera_makermodel[128]
Definition image.h:391
float exif_iso
Definition image.h:379
int32_t flags
Definition image.h:410
float d65_color_matrix[9]
Definition image.h:430
dt_iop_buffer_dsc_t dsc
Definition image.h:428
float adobe_XYZ_to_CAM[4][3]
Definition image.h:458
int32_t id
Definition image.h:410
char fullpath[DT_PATH_MAX]
Definition image.h:396
uint32_t filters
Definition format.h:89
unsigned int channels
Definition format.h:83
dt_iop_buffer_type_t datatype
Definition format.h:85
uint32_t demosaicing_method
Definition demosaic.c:242
double CAM_to_RGB[3][4]
Definition demosaic.c:245
GtkWidget * demosaic_method_xtrans
Definition demosaic.c:283
GtkWidget * color_smoothing
Definition demosaic.c:281
GtkWidget * demosaic_method_bayer
Definition demosaic.c:282
dt_iop_demosaic_method_t demosaicing_method
Definition demosaic.c:272
dt_iop_demosaic_greeneq_t green_eq
Definition demosaic.c:269
dt_iop_demosaic_smooth_t color_smoothing
Definition demosaic.c:271
dt_iop_demosaic_lmmse_t lmmse_refine
Definition demosaic.c:273
GtkWidget * widget
Definition imageop_gui.h:47
dt_iop_global_data_t * data
Definition imageop.h:238
dt_iop_params_t * default_params
Definition imageop.h:333
struct dt_iop_module_gui_t * gui
Definition imageop.h:346
struct dt_develop_t * dev
Definition imageop.h:311
gboolean default_enabled
Definition imageop.h:318
dt_iop_global_data_t * global_data
Definition imageop.h:337
dt_iop_params_t * params
Definition imageop.h:333
Region of interest passed through the pixelpipe.
Definition format.h:49
double scale
Definition format.h:51
int width
Definition format.h:50
int height
Definition format.h:50
double clock
Definition times.h:39
double user
Definition times.h:40
GQueue * log
Definition supervisor.c:121
#define __DT_CLONE_TARGETS__
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 void dt_get_times(dt_times_t *t)
Definition times.h:50
Telling the user something happened.
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
gboolean dt_gui_widgets_suppressed(void)
#define DT_GUI_BOX_SPACING
void dtgtk_cairo_paint_showmask(cairo_t *cr, gint x, gint y, gint w, gint h, gint flags, void *data)