Ansel 0.0
A darktable fork - bloat + design vision
Loading...
Searching...
No Matches
demosaic.c
Go to the documentation of this file.
1/*
2 This file is part of darktable,
3 Copyright (C) 2010-2011 Bruce Guenter.
4 Copyright (C) 2010-2016 johannes hanika.
5 Copyright (C) 2011 Antony Dovgal.
6 Copyright (C) 2011 Henrik Andersson.
7 Copyright (C) 2011 Kaminsky Andrey.
8 Copyright (C) 2011 Olivier Tribout.
9 Copyright (C) 2011 Robert Bieber.
10 Copyright (C) 2011 Sergey Pavlov.
11 Copyright (C) 2011-2014, 2016, 2019 Tobias Ellinghaus.
12 Copyright (C) 2011-2012, 2014-2017 Ulrich Pegelow.
13 Copyright (C) 2012, 2015, 2020-2022 Aldric Renaudin.
14 Copyright (C) 2012, 2014-2015 Pascal de Bruijn.
15 Copyright (C) 2012 Richard Wonka.
16 Copyright (C) 2013-2016, 2019 Roman Lebedev.
17 Copyright (C) 2013 Thomas Pryds.
18 Copyright (C) 2014-2017 Dan Torop.
19 Copyright (C) 2015-2016 Pedro Côrte-Real.
20 Copyright (C) 2017-2019 Heiko Bauke.
21 Copyright (C) 2017, 2019 Ingo Liebhardt.
22 Copyright (C) 2017, 2019, 2021 luzpaz.
23 Copyright (C) 2017 Peter Budai.
24 Copyright (C) 2018-2020, 2023-2026 Aurélien PIERRE.
25 Copyright (C) 2018-2019 Edgardo Hoszowski.
26 Copyright (C) 2018 Kelvie Wong.
27 Copyright (C) 2018 Maurizio Paglia.
28 Copyright (C) 2018, 2020-2022 Pascal Obry.
29 Copyright (C) 2018 rawfiner.
30 Copyright (C) 2019 Andreas Schneider.
31 Copyright (C) 2019-2022 Hanno Schwalm.
32 Copyright (C) 2020 Chris Elston.
33 Copyright (C) 2020, 2022 Diederik Ter Rahe.
34 Copyright (C) 2020 Felipe Contreras.
35 Copyright (C) 2020-2021 Hubert Kowalski.
36 Copyright (C) 2020-2021 Ralf Brown.
37 Copyright (C) 2022 Martin Bařinka.
38 Copyright (C) 2022 Philipp Lutz.
39 Copyright (C) 2022 Victor Forsiuk.
40 Copyright (C) 2023 Alynx Zhou.
41 Copyright (C) 2025 Guillaume Stutin.
42
43 darktable is free software: you can redistribute it and/or modify
44 it under the terms of the GNU General Public License as published by
45 the Free Software Foundation, either version 3 of the License, or
46 (at your option) any later version.
47
48 darktable is distributed in the hope that it will be useful,
49 but WITHOUT ANY WARRANTY; without even the implied warranty of
50 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
51 GNU General Public License for more details.
52
53 You should have received a copy of the GNU General Public License
54 along with darktable. If not, see <http://www.gnu.org/licenses/>.
55*/
56
57#ifdef HAVE_CONFIG_H
58#include "config.h"
59#endif
60
61#include "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
291 const dt_dev_pixelpipe_iop_t *piece,
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
336{
337 return IOP_CS_RAW;
338}
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
378{
379 default_input_format(self, pipe, piece, dsc);
380 dsc->channels = 1;
382}
383
386{
387 dsc->channels = 4;
388 dsc->datatype = TYPE_FLOAT;
389 dsc->cst = IOP_CS_RGB;
390}
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
928void distort_mask(struct dt_iop_module_t *self, const struct dt_dev_pixelpipe_t *pipe, struct dt_dev_pixelpipe_iop_t *piece,
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{
989 const dt_iop_demosaic_params_t *const p = (const dt_iop_demosaic_params_t *)params;
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,
1006 struct dt_dev_pixelpipe_iop_t *piece,
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
1043int process(struct dt_iop_module_t *self, const dt_dev_pixelpipe_t *pipe, const dt_dev_pixelpipe_iop_t *piece,
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))
1057 && (pipe->type == DT_DEV_PIXELPIPE_FULL));
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;
1079 if(self->dev->gui_attached && pipe->type == DT_DEV_PIXELPIPE_FULL)
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
1256static int process_default_cl(struct dt_iop_module_t *self, const dt_dev_pixelpipe_t *pipe,
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
1691int process_cl(struct dt_iop_module_t *self, const dt_dev_pixelpipe_t *pipe, const dt_dev_pixelpipe_iop_t *piece,
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))
1698 && (pipe->type == DT_DEV_PIXELPIPE_FULL));
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;
1712 if(self->dev->gui_attached && pipe->type == DT_DEV_PIXELPIPE_FULL)
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
1916void 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)
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 tiling->overlap = 10;
1981 tiling->factor_cl = tiling->factor + 3.0f;
1982 }
1983 else if(demosaicing_method == DT_IOP_DEMOSAIC_LMMSE)
1984 {
1985 tiling->factor = 1.0f + ioratio;
1986 tiling->factor += fmax(1.0f + greeneq, smooth); // + tmp + geeneq | + smooth
1987 tiling->maxbuf = 1.0f;
1988 tiling->overhead = sizeof(float) * LMMSE_GRP * LMMSE_GRP * 6 * MAX(1, dt_get_num_openmp_threads());
1989 tiling->xalign = 2;
1990 tiling->yalign = 2;
1991 tiling->overlap = 10;
1992 }
1993 else
1994 {
1995 // VNG
1996 tiling->factor = 1.0f + ioratio;
1997 tiling->factor += fmax(1.0f + greeneq, smooth);
1998 tiling->maxbuf = 1.0f;
1999 tiling->overhead = 0;
2000 tiling->xalign = 6; // covering Bayer pattern for VNG4 as well as xtrans for VNG
2001 tiling->yalign = 6; // covering Bayer pattern for VNG4 as well as xtrans for VNG
2002 tiling->overlap = 6;
2003 }
2005 {
2006 // make sure VNG4 is also possible
2007 tiling->factor += 1.0f;
2008 tiling->xalign = MAX(6, tiling->xalign);
2009 tiling->yalign = MAX(6, tiling->yalign);
2010 tiling->overlap = MAX(6, tiling->overlap);
2011 }
2012 return;
2013}
2014#undef LMMSE_GRP
2015
2017{
2018 const int program = 0; // from programs.conf
2021 module->data = gd;
2022 gd->kernel_zoom_half_size = dt_opencl_create_kernel(program, "clip_and_zoom_demosaic_half_size");
2023 gd->kernel_ppg_green = dt_opencl_create_kernel(program, "ppg_demosaic_green");
2024 gd->kernel_green_eq_lavg = dt_opencl_create_kernel(program, "green_equilibration_lavg");
2025 gd->kernel_green_eq_favg_reduce_first = dt_opencl_create_kernel(program, "green_equilibration_favg_reduce_first");
2026 gd->kernel_green_eq_favg_reduce_second = dt_opencl_create_kernel(program, "green_equilibration_favg_reduce_second");
2027 gd->kernel_green_eq_favg_apply = dt_opencl_create_kernel(program, "green_equilibration_favg_apply");
2028 gd->kernel_pre_median = dt_opencl_create_kernel(program, "pre_median");
2029 gd->kernel_ppg_redblue = dt_opencl_create_kernel(program, "ppg_demosaic_redblue");
2030 gd->kernel_downsample = dt_opencl_create_kernel(program, "clip_and_zoom");
2031 gd->kernel_border_interpolate = dt_opencl_create_kernel(program, "border_interpolate");
2032 gd->kernel_color_smoothing = dt_opencl_create_kernel(program, "color_smoothing");
2033
2034 const int other = 14; // from programs.conf
2035 gd->kernel_passthrough_monochrome = dt_opencl_create_kernel(other, "passthrough_monochrome");
2036 gd->kernel_passthrough_color = dt_opencl_create_kernel(other, "passthrough_color");
2037 gd->kernel_zoom_passthrough_monochrome = dt_opencl_create_kernel(other, "clip_and_zoom_demosaic_passthrough_monochrome");
2038
2039 const int vng = 15; // from programs.conf
2040 gd->kernel_vng_border_interpolate = dt_opencl_create_kernel(vng, "vng_border_interpolate");
2041 gd->kernel_vng_lin_interpolate = dt_opencl_create_kernel(vng, "vng_lin_interpolate");
2042 gd->kernel_zoom_third_size = dt_opencl_create_kernel(vng, "clip_and_zoom_demosaic_third_size_xtrans");
2043 gd->kernel_zoom_half_size_xtrans = dt_opencl_create_kernel(vng, "clip_and_zoom_demosaic_half_size_xtrans");
2044 gd->kernel_vng_green_equilibrate = dt_opencl_create_kernel(vng, "vng_green_equilibrate");
2045 gd->kernel_vng_interpolate = dt_opencl_create_kernel(vng, "vng_interpolate");
2046
2047 const int markesteijn = 16; // from programs.conf
2048 gd->kernel_markesteijn_initial_copy = dt_opencl_create_kernel(markesteijn, "markesteijn_initial_copy");
2049 gd->kernel_markesteijn_green_minmax = dt_opencl_create_kernel(markesteijn, "markesteijn_green_minmax");
2050 gd->kernel_markesteijn_interpolate_green = dt_opencl_create_kernel(markesteijn, "markesteijn_interpolate_green");
2051 gd->kernel_markesteijn_solitary_green = dt_opencl_create_kernel(markesteijn, "markesteijn_solitary_green");
2052 gd->kernel_markesteijn_recalculate_green = dt_opencl_create_kernel(markesteijn, "markesteijn_recalculate_green");
2053 gd->kernel_markesteijn_red_and_blue = dt_opencl_create_kernel(markesteijn, "markesteijn_red_and_blue");
2054 gd->kernel_markesteijn_interpolate_twoxtwo = dt_opencl_create_kernel(markesteijn, "markesteijn_interpolate_twoxtwo");
2055 gd->kernel_markesteijn_convert_yuv = dt_opencl_create_kernel(markesteijn, "markesteijn_convert_yuv");
2056 gd->kernel_markesteijn_differentiate = dt_opencl_create_kernel(markesteijn, "markesteijn_differentiate");
2057 gd->kernel_markesteijn_homo_threshold = dt_opencl_create_kernel(markesteijn, "markesteijn_homo_threshold");
2058 gd->kernel_markesteijn_homo_set = dt_opencl_create_kernel(markesteijn, "markesteijn_homo_set");
2059 gd->kernel_markesteijn_homo_sum = dt_opencl_create_kernel(markesteijn, "markesteijn_homo_sum");
2060 gd->kernel_markesteijn_homo_max = dt_opencl_create_kernel(markesteijn, "markesteijn_homo_max");
2061 gd->kernel_markesteijn_homo_max_corr = dt_opencl_create_kernel(markesteijn, "markesteijn_homo_max_corr");
2062 gd->kernel_markesteijn_homo_quench = dt_opencl_create_kernel(markesteijn, "markesteijn_homo_quench");
2063 gd->kernel_markesteijn_zero = dt_opencl_create_kernel(markesteijn, "markesteijn_zero");
2064 gd->kernel_markesteijn_accu = dt_opencl_create_kernel(markesteijn, "markesteijn_accu");
2065 gd->kernel_markesteijn_final = dt_opencl_create_kernel(markesteijn, "markesteijn_final");
2066
2067 const int rcd = 31; // from programs.conf
2068 gd->kernel_rcd_populate = dt_opencl_create_kernel(rcd, "rcd_populate");
2069 gd->kernel_rcd_write_output = dt_opencl_create_kernel(rcd, "rcd_write_output");
2070 gd->kernel_rcd_step_1 = dt_opencl_create_kernel(rcd, "rcd_step_1");
2071 gd->kernel_rcd_step_2_1 = dt_opencl_create_kernel(rcd, "rcd_step_2_1");
2072 gd->kernel_rcd_step_3_1 = dt_opencl_create_kernel(rcd, "rcd_step_3_1");
2073 gd->kernel_rcd_step_4_1 = dt_opencl_create_kernel(rcd, "rcd_step_4_1");
2074 gd->kernel_rcd_step_4_2 = dt_opencl_create_kernel(rcd, "rcd_step_4_2");
2075 gd->kernel_rcd_step_5_1 = dt_opencl_create_kernel(rcd, "rcd_step_5_1");
2076 gd->kernel_rcd_step_5_2 = dt_opencl_create_kernel(rcd, "rcd_step_5_2");
2077 gd->kernel_rcd_border_redblue = dt_opencl_create_kernel(rcd, "rcd_border_redblue");
2078 gd->kernel_rcd_border_green = dt_opencl_create_kernel(rcd, "rcd_border_green");
2079 gd->kernel_write_blended_dual = dt_opencl_create_kernel(rcd, "write_blended_dual");
2080
2081 const int wavelets = 35; // bspline.cl, from programs.conf
2082 gd->kernel_guided_laplacian_coefficients = dt_opencl_create_kernel(wavelets, "guided_laplacian_coefficients");
2083 gd->kernel_guided_laplacian_normalize = dt_opencl_create_kernel(wavelets, "guided_laplacian_normalize");
2084 gd->kernel_guided_laplacian_apply = dt_opencl_create_kernel(wavelets, "guided_laplacian_apply");
2085 gd->kernel_guided_laplacian_finalize = dt_opencl_create_kernel(wavelets, "guided_laplacian_finalize");
2086 gd->kernel_bspline_horizontal = dt_opencl_create_kernel(wavelets, "blur_2D_Bspline_horizontal");
2087 gd->kernel_bspline_vertical = dt_opencl_create_kernel(wavelets, "blur_2D_Bspline_vertical");
2088 gd->kernel_bspline_horizontal_local = dt_opencl_create_kernel(wavelets, "blur_2D_Bspline_horizontal_local");
2089 gd->kernel_bspline_vertical_local = dt_opencl_create_kernel(wavelets, "blur_2D_Bspline_vertical_local");
2090 gd->lmmse_gamma_in = NULL;
2091 gd->lmmse_gamma_out = NULL;
2092}
2093
2095{
2157 dt_free(module->data);
2158}
2159
2160
2161gboolean force_enable(struct dt_iop_module_t *self, const gboolean current_state)
2162{
2163 // Demosaicing applies if and only if the buffer carries a CFA mosaic. Gate on the mosaic
2164 // axis, not on the historical "raw" flag: an already-demosaiced raw (sRAW / linear DNG) must
2165 // not be demosaiced, while a mosaiced raw that the RAW flag missed still must.
2166 // Mandatory module: the decision is purely image metadata and ignores current_state.
2167 const gboolean state = dt_image_needs_demosaic(&self->dev->image_storage);
2168 dt_iop_fmt_log(self, "force_enable: class=%s needs_demosaic=%d current=%d -> %d",
2170 state, current_state, state);
2171 return state;
2172}
2173
2174
2177{
2180
2181 d->green_eq = p->green_eq;
2182 d->color_smoothing = p->color_smoothing;
2183 d->median_thrs = p->median_thrs;
2184 d->dual_thrs = p->dual_thrs;
2185 d->lmmse_refine = p->lmmse_refine;
2186 dt_iop_demosaic_method_t use_method = p->demosaicing_method;
2187 const gboolean xmethod = use_method & DEMOSAIC_XTRANS;
2188 const gboolean bayer = (self->dev->image_storage.dsc.filters != 9u);
2189 const gboolean downsample = _is_downsample_method(use_method);
2190
2191 if(!downsample)
2192 {
2193 if(bayer && xmethod) use_method = DT_IOP_DEMOSAIC_RCD;
2194 if(!bayer && !xmethod) use_method = DT_IOP_DEMOSAIC_MARKESTEIJN;
2195 }
2196
2199 if(use_method == DT_IOP_DEMOSAIC_PASSTHROUGH_COLOR || use_method == DT_IOP_DEMOSAIC_PASSTHR_COLORX)
2201
2202 const gboolean passing = (use_method == DT_IOP_DEMOSAIC_PASSTHROUGH_MONOCHROME
2203 || use_method == DT_IOP_DEMOSAIC_PASSTHROUGH_COLOR);
2204
2205 if(!(use_method == DT_IOP_DEMOSAIC_PPG))
2206 d->median_thrs = 0.0f;
2207
2208 if(passing)
2209 {
2210 d->green_eq = DT_IOP_GREEN_EQ_NO;
2211 d->color_smoothing = 0;
2212 d->dual_thrs = 0.0f;
2213 }
2214 else if(downsample)
2215 {
2216 d->green_eq = DT_IOP_GREEN_EQ_NO;
2217 d->dual_thrs = 0.0f;
2218 }
2219
2220 if(use_method & DEMOSAIC_DUAL)
2221 d->color_smoothing = 0;
2222
2223 d->demosaicing_method = use_method;
2224
2225 // OpenCL only supported by some of the demosaicing methods
2226 switch(d->demosaicing_method)
2227 {
2229 piece->process_cl_ready = 1;
2230 break;
2232 piece->process_cl_ready = 0;
2233 break;
2235 piece->process_cl_ready = 1;
2236 break;
2238 piece->process_cl_ready = 1;
2239 break;
2241 piece->process_cl_ready = 1;
2242 break;
2244 piece->process_cl_ready = 1;
2245 break;
2247 piece->process_cl_ready = 0;
2248 break;
2250 piece->process_cl_ready = 1;
2251 break;
2253 piece->process_cl_ready = 0;
2254 break;
2256 piece->process_cl_ready = 1;
2257 break;
2259 piece->process_cl_ready = 1;
2260 break;
2262 piece->process_cl_ready = 1;
2263 break;
2265 piece->process_cl_ready = 1;
2266 break;
2268 piece->process_cl_ready = 0;
2269 break;
2271 piece->process_cl_ready = 1;
2272 break;
2273 default:
2274 piece->process_cl_ready = 0;
2275 }
2276
2277 // green-equilibrate over full image excludes tiling
2278 if((d->green_eq == DT_IOP_GREEN_EQ_FULL
2279 || d->green_eq == DT_IOP_GREEN_EQ_BOTH)
2280 || ((use_method & DEMOSAIC_DUAL) && (d->dual_thrs > 0.0f)))
2281 {
2282 piece->process_tiling_ready = 0;
2283 }
2284
2286 {
2287 // 4Bayer images only support the dedicated half-size downsample path in OpenCL.
2288 if(d->demosaicing_method != DT_IOP_DEMOSAIC_DOWNSAMPLE) piece->process_cl_ready = 0;
2289
2290 // Get and store the matrix to go from camera to RGB for 4Bayer images
2292 NULL, d->CAM_to_RGB,
2293 self->dev->image_storage.d65_color_matrix, NULL))
2294 {
2295 const char *camera = self->dev->image_storage.camera_makermodel;
2296 fprintf(stderr, "[colorspaces] `%s' color matrix not found for 4bayer image!\n", camera);
2297 dt_control_log(_("`%s' color matrix not found for 4bayer image!"), camera);
2298 }
2299 }
2300
2301 dt_iop_fmt_log(self, "commit: class=%s in(filters=%u ch=%i) method=%d passthrough=%d -> enabled=%d cl_ready=%d",
2303 piece->dsc_in.filters, piece->dsc_in.channels, d->demosaicing_method, passing,
2304 piece->enabled, piece->process_cl_ready);
2305}
2306
2308{
2310 piece->data_size = sizeof(dt_iop_demosaic_data_t);
2311}
2312
2314{
2315 dt_free_align(piece->data);
2316 piece->data = NULL;
2317}
2318
2320{
2322
2324 d->demosaicing_method = DT_IOP_DEMOSAIC_PASSTHROUGH_MONOCHROME;
2325 else if(module->dev->image_storage.dsc.filters == 9u)
2326 d->demosaicing_method = DT_IOP_DEMOSAIC_MARKESTEIJN;
2327 else
2328 d->demosaicing_method = DT_IOP_DEMOSAIC_RCD;
2329
2330 module->hide_enable_button = 1;
2331
2332 // Enabled iff the buffer is mosaiced (see force_enable): keeps demosaic off for
2333 // already-demosaiced sRAW / linear DNG and on for monochrome Bayer sensors.
2334 module->default_enabled = dt_image_needs_demosaic(&module->dev->image_storage);
2335 dt_iop_fmt_log(module, "reload_defaults: class=%s needs_demosaic=%d filters=%u mono=%d method=%d -> default_enabled=%d",
2338 module->dev->image_storage.dsc.filters,
2340 d->demosaicing_method, module->default_enabled);
2341 // Stack visibility (raw vs non_raw) is set from default_enabled in gui_update() (which already
2342 // does exactly this), so reload_defaults() stays params-only.
2343}
2344
2345void gui_changed(dt_iop_module_t *self, GtkWidget *w, void *previous)
2346{
2349
2350 const gboolean bayer = (self->dev->image_storage.dsc.filters != 9u);
2351 dt_iop_demosaic_method_t use_method = p->demosaicing_method;
2352 const gboolean xmethod = use_method & DEMOSAIC_XTRANS;
2353
2354 if(!_is_downsample_method(use_method))
2355 {
2356 if(bayer && xmethod) use_method = DT_IOP_DEMOSAIC_RCD;
2357 if(!bayer && !xmethod) use_method = DT_IOP_DEMOSAIC_MARKESTEIJN;
2358 }
2359
2360 const gboolean isppg = (use_method == DT_IOP_DEMOSAIC_PPG);
2361 const gboolean isdownsample = _is_downsample_method(use_method);
2362 const gboolean isdual = !isdownsample && (use_method & DEMOSAIC_DUAL);
2363 const gboolean islmmse = (use_method == DT_IOP_DEMOSAIC_LMMSE);
2364 const gboolean passing = ((use_method == DT_IOP_DEMOSAIC_PASSTHROUGH_MONOCHROME) ||
2365 (use_method == DT_IOP_DEMOSAIC_PASSTHROUGH_COLOR) ||
2366 (use_method == DT_IOP_DEMOSAIC_PASSTHR_MONOX) ||
2367 (use_method == DT_IOP_DEMOSAIC_PASSTHR_COLORX));
2368
2369 gtk_widget_set_visible(g->demosaic_method_bayer, bayer);
2370 gtk_widget_set_visible(g->demosaic_method_xtrans, !bayer);
2371 if(bayer)
2372 dt_bauhaus_combobox_set_from_value(g->demosaic_method_bayer, p->demosaicing_method);
2373 else
2374 dt_bauhaus_combobox_set_from_value(g->demosaic_method_xtrans, p->demosaicing_method);
2375
2376 gtk_widget_set_visible(g->median_thrs, bayer && isppg);
2377 gtk_widget_set_visible(g->greeneq, !passing && !isdownsample);
2378 gtk_widget_set_visible(g->color_smoothing, !passing && !isdual);
2379 gtk_widget_set_visible(g->dual_thrs, isdual);
2380 gtk_widget_set_visible(g->lmmse_refine, islmmse);
2381
2382 dt_image_t *img = dt_image_cache_get(self->dev->image_storage.id, 'w');
2383 if(!img) return;
2384 if((p->demosaicing_method == DT_IOP_DEMOSAIC_PASSTHROUGH_MONOCHROME) ||
2385 (p->demosaicing_method == DT_IOP_DEMOSAIC_PASSTHR_MONOX))
2387 else
2388 img->flags &= ~DT_IMAGE_MONOCHROME_BAYER;
2390}
2391void gui_update(struct dt_iop_module_t *self)
2392{
2395
2396 g->visual_mask = FALSE;
2397 gui_changed(self, NULL, NULL);
2398
2399 gtk_stack_set_visible_child_name(GTK_STACK(self->gui->widget), self->default_enabled ? "raw" : "non_raw");
2400}
2401
2402static void _visualize_callback(GtkWidget *quad, gpointer user_data)
2403{
2404 if(dt_gui_widgets_suppressed()) return;
2405 dt_iop_module_t *self = (dt_iop_module_t *)user_data;
2407
2408 g->visual_mask = dt_bauhaus_widget_get_quad_active(quad);
2410}
2411
2412void gui_focus(struct dt_iop_module_t *self, gboolean in)
2413{
2415 if(!in)
2416 {
2417 const gboolean was_dualmask = g->visual_mask;
2419 g->visual_mask = FALSE;
2420 if(was_dualmask) dt_dev_pixelpipe_update_history_main(self->dev);
2421 }
2422}
2423
2424void gui_init(struct dt_iop_module_t *self)
2425{
2427
2428 GtkWidget *box_raw = self->gui->widget = gtk_box_new(GTK_ORIENTATION_VERTICAL, DT_GUI_BOX_SPACING);
2429
2430 g->demosaic_method_bayer = dt_bauhaus_combobox_from_params(self, "demosaicing_method");
2431 for(int i=0;i<7;i++) dt_bauhaus_combobox_remove_at(g->demosaic_method_bayer, 9);
2432 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."));
2433
2434 g->demosaic_method_xtrans = dt_bauhaus_combobox_from_params(self, "demosaicing_method");
2435 for(int i=0;i<9;i++) dt_bauhaus_combobox_remove_at(g->demosaic_method_xtrans, 0);
2436 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."));
2437
2438 g->median_thrs = dt_bauhaus_slider_from_params(self, "median_thrs");
2439 dt_bauhaus_slider_set_digits(g->median_thrs, 3);
2440 gtk_widget_set_tooltip_text(g->median_thrs, _("threshold for edge-aware median.\nset to 0.0 to switch off\n"
2441 "set to 1.0 to ignore edges"));
2442
2443 g->dual_thrs = dt_bauhaus_slider_from_params(self, "dual_thrs");
2444 dt_bauhaus_slider_set_digits(g->dual_thrs, 2);
2445 gtk_widget_set_tooltip_text(g->dual_thrs, _("contrast threshold for dual demosaic.\nset to 0.0 for high frequency content\n"
2446 "set to 1.0 for flat content\ntoggle to visualize the mask"));
2450 g_signal_connect(G_OBJECT(g->dual_thrs), "quad-pressed", G_CALLBACK(_visualize_callback), self);
2451
2452 g->lmmse_refine = dt_bauhaus_combobox_from_params(self, "lmmse_refine");
2453 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"));
2454
2455 g->color_smoothing = dt_bauhaus_combobox_from_params(self, "color_smoothing");
2456 gtk_widget_set_tooltip_text(g->color_smoothing, _("how many post-demosaic smoothing passes.\nin downsample mode this sets the guided detail equalization iterations"));
2457
2458 g->greeneq = dt_bauhaus_combobox_from_params(self, "green_eq");
2459 gtk_widget_set_tooltip_text(g->greeneq, _("green channels matching method"));
2460
2461 // start building top level widget
2462 self->gui->widget = gtk_stack_new();
2463 gtk_stack_set_homogeneous(GTK_STACK(self->gui->widget), FALSE);
2464
2465 GtkWidget *label_non_raw = dt_ui_label_new(_("not applicable"));
2466 gtk_widget_set_tooltip_text(label_non_raw, _("demosaicing is only used for color raw images"));
2467
2468 gtk_stack_add_named(GTK_STACK(self->gui->widget), label_non_raw, "non_raw");
2469 gtk_stack_add_named(GTK_STACK(self->gui->widget), box_raw, "raw");
2470}
2471
2472// clang-format off
2473// modelines: These editor modelines have been set for all relevant files by tools/update_modelines.py
2474// vim: shiftwidth=2 expandtab tabstop=2 cindent
2475// kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-spaces modified;
2476// 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:3343
gboolean dt_bauhaus_combobox_set_from_value(GtkWidget *widget, int value)
Definition bauhaus.c:2119
void dt_bauhaus_widget_set_quad_toggle(GtkWidget *widget, int toggle)
Definition bauhaus.c:1572
void dt_bauhaus_widget_set_quad_active(GtkWidget *widget, int active)
Definition bauhaus.c:1578
int dt_bauhaus_widget_get_quad_active(GtkWidget *widget)
Definition bauhaus.c:1595
void dt_bauhaus_combobox_remove_at(GtkWidget *widget, int pos)
Definition bauhaus.c:1909
void dt_bauhaus_widget_set_quad_paint(GtkWidget *widget, dt_bauhaus_quad_paint_f f, int paint_flags, void *paint_data)
Definition bauhaus.c:1554
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:137
#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_log(const char *msg,...)
Definition control.c:824
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:518
#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:2319
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:2175
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:2412
void init_pipe(struct dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece)
Definition demosaic.c:2307
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:2391
#define DEMOSAIC_XTRANS
Definition demosaic.c:109
void gui_init(struct dt_iop_module_t *self)
Definition demosaic.c:2424
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:2345
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:2094
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:2161
void cleanup_pipe(struct dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece)
Definition demosaic.c:2313
static void _visualize_callback(GtkWidget *quad, gpointer user_data)
Definition demosaic.c:2402
void init_global(dt_iop_module_so_t *module)
Definition demosaic.c:2016
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:115
@ TYPE_FLOAT
Definition format.h:56
Where things are on the image, answered without a pipeline.
@ DT_IMAGE_MONOCHROME_BAYER
Definition image.h:151
@ DT_IMAGE_4BAYER
Definition image.h:140
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:1893
#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
#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:2085
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 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:3713
int dt_opencl_enqueue_kernel_2d(const int dev, const int kernel, const size_t *sizes)
Definition opencl.c:2554
void * dt_opencl_alloc_device_buffer(const int devid, const size_t size)
Definition opencl.c:2970
void * dt_opencl_alloc_device(const int devid, const int width, const int height, const int bpp)
Definition opencl.c:2894
int dt_opencl_create_kernel(const int prog, const char *name)
Definition opencl.c:2448
void * dt_opencl_copy_host_to_device_constant(const int devid, const size_t size, void *host)
Definition opencl.c:2750
void dt_opencl_free_kernel(const int kernel)
Definition opencl.c:2491
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:2545
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:2560
void dt_opencl_release_mem_object(cl_mem mem)
Definition opencl.c:2805
#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:568
#define RCD_TILESIZE
Definition rcd.c:54
#define eps
Definition rcd.c:81
static void rcd_demosaic(const dt_dev_pixelpipe_iop_t *piece, float *const restrict out, const float *const restrict in, dt_iop_roi_t *const roi_out, const dt_iop_roi_t *const roi_in, const uint32_t filters)
Definition rcd.c:274
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_iop_buffer_dsc_t dsc_in
struct dt_iop_module_t *void * data
dt_dev_pixelpipe_type_t type
int32_t gui_attached
Definition develop.h: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:382
float exif_iso
Definition image.h:370
int32_t flags
Definition image.h:401
float d65_color_matrix[9]
Definition image.h:421
dt_iop_buffer_dsc_t dsc
Definition image.h:419
float adobe_XYZ_to_CAM[4][3]
Definition image.h:449
int32_t id
Definition image.h:401
uint32_t filters
Definition format.h:89
unsigned int channels
Definition format.h:83
uint8_t xtrans[6][6]
Definition format.h:99
dt_iop_buffer_type_t datatype
Definition format.h:85
dt_aligned_pixel_t processed_maximum
Definition format.h:114
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)