Ansel 0.0
A darktable fork - bloat + design vision
Loading...
Searching...
No Matches
rcd.c
Go to the documentation of this file.
1/*
2 This file is part of darktable,
3 Copyright (C) 2020-2021 Hanno Schwalm.
4 Copyright (C) 2021 Marco.
5 Copyright (C) 2021 Pascal Obry.
6 Copyright (C) 2021 Ralf Brown.
7 Copyright (C) 2022 Martin Bařinka.
8 Copyright (C) 2023, 2025-2026 Aurélien PIERRE.
9 Copyright (C) 2026 Guillaume Stutin.
10
11 darktable is free software: you can redistribute it and/or modify
12 it under the terms of the GNU General Public License as published by
13 the Free Software Foundation, either version 3 of the License, or
14 (at your option) any later version.
15
16 darktable is distributed in the hope that it will be useful,
17 but WITHOUT ANY WARRANTY; without even the implied warranty of
18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 GNU General Public License for more details.
20
21 You should have received a copy of the GNU General Public License
22 along with darktable. If not, see <http://www.gnu.org/licenses/>.
23*/
24
25
26/*
27* RATIO CORRECTED DEMOSAICING
28* Luis Sanz Rodríguez (luis.sanz.rodriguez(at)gmail(dot)com)
29*
30* Release 2.3 @ 171125
31*
32* Original code from https://github.com/LuisSR/RCD-Demosaicing
33* Licensed under the GNU GPL version 3
34*/
35
36/*
37* The tiling coding was done by Ingo Weyrich (heckflosse67@gmx.de) from rawtherapee
38*/
39
40/*
41* Luis Sanz Rodríguez significantly optimised the v 2.3 code and simplified the directional
42* coefficients in an exact, shorter and more performant formula.
43* In cooperation with Ingo Weyrich and Luis Sanz Rodríguez this has been tuned for performance.
44* Hanno Schwalm (hanno@schwalm-bremen.de)
45*/
46
47/* Some notes about the algorithm
48* 1. A pixel is exact only RCD_BORDER = 10 pixels inside a tile, image edges included. VH_Dir cannot be
49* computed in the outer 4 rows and columns of a tile, yet the green interpolation reads it one pixel
50* further out, in row 3; that green feeds the red/blue at diagonals 2 rows further in, which feed the
51* red/blue at green sites 3 rows further in: row 9, and symmetrically the last row. What is read
52* there is whatever the scratch holds (zeroes, or another tile's values): with a 9-pixel border,
53* the seams would depend on where the tile grid falls, which pixel-pipe tiling moves.
54* 2. The outer RCD_BORDER pixels of the image are interpolated otherwise (rcd_ppg_border()).
55* 3. The tilesize has a significant influence on performance, the default is a good guess for modern
56* x86/64 machines, tested on Xeon E-2288G, i5-8250U.
57*/
58
59#ifndef RCD_TILESIZE
60 #define RCD_TILESIZE 112
61#endif
62
63/* We don't want to use the -Ofast option in dt as it effects are not well specified and there have been issues
64 leading to crashes.
65 But we can use the 'fast-math' option in code sections if input data and algorithms are well defined.
66
67 We have defined input data and make sure there are no divide-by-zero or overflows by chosen eps
68 Reordering of instructions might lead to a slight loss of presision whigh is not significant here.
69 Not necessary in this code section
70 threadsafe handling of errno
71 signed zero handling
72 handling of math interrupts
73 handling of rounding
74 handling of overflows
75
76 The 'fp-contract=fast' option enables fused multiply&add if available
77*/
78
79// Left defined for demosaic.c: a pixel-pipe tile must overlap its neighbours by at least this much.
80#define RCD_BORDER 10
81#define RCD_TILEVALID (RCD_TILESIZE - 2 * RCD_BORDER)
82#define w1 RCD_TILESIZE
83#define w2 (2 * RCD_TILESIZE)
84#define w3 (3 * RCD_TILESIZE)
85#define w4 (4 * RCD_TILESIZE)
86
87#define eps 1e-5f // Tolerance to avoid dividing by zero
88#define epssq 1e-10f
89
90// We might have negative data in input and also want to normalise
91static INLINE float safe_in(float a, float scale)
92{
93 return fmaxf(0.0f, a) * scale;
94}
95
98static void rcd_ppg_border(float *const out, const float *const in, const int width, const int height, const uint32_t filters, const int margin)
99{
100 const int border = margin + 3;
101 // write approximatad 3-pixel border region to out
102 float sum[8];
104 for(int j = 0; j < height; j++)
105 {
106 for(int i = 0; i < width; i++)
107 {
108 if(i == 3 && j >= 3 && j < height - 3) i = width - 3;
109 memset(sum, 0, sizeof(float) * 8);
110 for(int y = j - 1; y != j + 2; y++)
111 {
112 for(int x = i - 1; x != i + 2; x++)
113 {
114 if((y >= 0) && (x >= 0) && (y < height) && (x < width))
115 {
116 const int f = FC(y, x, filters);
117 sum[f] += fmaxf(0.0f, in[(size_t)y * width + x]);
118 sum[f + 4]++;
119 }
120 }
121 }
122 const int f = FC(j, i, filters);
123 for(int c = 0; c < 3; c++)
124 {
125 if(c != f && sum[c + 4] > 0.0f)
126 out[4 * ((size_t)j * width + i) + c] = sum[c] / sum[c + 4];
127 else
128 out[4 * ((size_t)j * width + i) + c] = fmaxf(0.0f, in[(size_t)j * width + i]);
129 }
130 // the passes below copy it along: left unwritten, it is whatever the buffer held
131 out[4 * ((size_t)j * width + i) + 3] = 0.0f;
132 }
133 }
134
135
136 const float *input = in;
138 for(int j = 3; j < height - 3; j++)
139 {
140 float *buf = out + (size_t)4 * width * j + 4 * 3;
141 const float *buf_in = input + (size_t)width * j + 3;
142 for(int i = 3; i < width - 3; i++)
143 {
144 if(i == border && j >= border && j < height - border)
145 {
146 i = width - border;
147 buf = out + (size_t)4 * width * j + 4 * i;
148 buf_in = input + (size_t)width * j + i;
149 }
150 if(i == width) break;
151
152 const int c = FC(j, i, filters);
154 const float pc = fmaxf(0.0f, buf_in[0]);
155 if(c == 0 || c == 2)
156 {
157 color[c] = pc;
158 const float pym = fmaxf(0.0f, buf_in[-width * 1]);
159 const float pym2 = fmaxf(0.0f, buf_in[-width * 2]);
160 const float pym3 = fmaxf(0.0f, buf_in[-width * 3]);
161 const float pyM = fmaxf(0.0f, buf_in[+width * 1]);
162 const float pyM2 = fmaxf(0.0f, buf_in[+width * 2]);
163 const float pyM3 = fmaxf(0.0f, buf_in[+width * 3]);
164 const float pxm = fmaxf(0.0f, buf_in[-1]);
165 const float pxm2 = fmaxf(0.0f, buf_in[-2]);
166 const float pxm3 = fmaxf(0.0f, buf_in[-3]);
167 const float pxM = fmaxf(0.0f, buf_in[+1]);
168 const float pxM2 = fmaxf(0.0f, buf_in[+2]);
169 const float pxM3 = fmaxf(0.0f, buf_in[+3]);
170
171 const float guessx = (pxm + pc + pxM) * 2.0f - pxM2 - pxm2;
172 const float diffx = (fabsf(pxm2 - pc) + fabsf(pxM2 - pc) + fabsf(pxm - pxM)) * 3.0f
173 + (fabsf(pxM3 - pxM) + fabsf(pxm3 - pxm)) * 2.0f;
174 const float guessy = (pym + pc + pyM) * 2.0f - pyM2 - pym2;
175 const float diffy = (fabsf(pym2 - pc) + fabsf(pyM2 - pc) + fabsf(pym - pyM)) * 3.0f
176 + (fabsf(pyM3 - pyM) + fabsf(pym3 - pym)) * 2.0f;
177 if(diffx > diffy)
178 {
179 // use guessy
180 const float m = fminf(pym, pyM);
181 const float M = fmaxf(pym, pyM);
182 color[1] = fmaxf(fminf(guessy * .25f, M), m);
183 }
184 else
185 {
186 const float m = fminf(pxm, pxM);
187 const float M = fmaxf(pxm, pxM);
188 color[1] = fmaxf(fminf(guessx * .25f, M), m);
189 }
190 }
191 else
192 color[1] = pc;
193
194 color[3] = 0.0f;
195 memcpy(buf, color, sizeof(float) * 4);
196 buf += 4;
197 buf_in++;
198 }
199 }
200
201
202 // for all pixels: interpolate colors into float array
204 for(int j = 1; j < height - 1; j++)
205 {
206 float *buf = out + (size_t)4 * width * j + 4;
207 for(int i = 1; i < width - 1; i++)
208 {
209 if(i == margin && j >= margin && j < height - margin)
210 {
211 i = width - margin;
212 buf = out + (size_t)4 * (width * j + i);
213 }
214 const int c = FC(j, i, filters);
215 dt_aligned_pixel_t color = { buf[0], buf[1], buf[2], buf[3] };
216 const int linesize = 4 * width;
217 // fill all four pixels with correctly interpolated stuff: r/b for green1/2
218 // b for r and r for b
219 if(__builtin_expect(c & 1, 1)) // c == 1 || c == 3)
220 {
221 // calculate red and blue for green pixels:
222 // need 4-nbhood:
223 const float *nt = buf - linesize;
224 const float *nb = buf + linesize;
225 const float *nl = buf - 4;
226 const float *nr = buf + 4;
227 if(FC(j, i + 1, filters) == 0) // red nb in same row
228 {
229 color[2] = (nt[2] + nb[2] + 2.0f * color[1] - nt[1] - nb[1]) * .5f;
230 color[0] = (nl[0] + nr[0] + 2.0f * color[1] - nl[1] - nr[1]) * .5f;
231 }
232 else
233 {
234 // blue nb
235 color[0] = (nt[0] + nb[0] + 2.0f * color[1] - nt[1] - nb[1]) * .5f;
236 color[2] = (nl[2] + nr[2] + 2.0f * color[1] - nl[1] - nr[1]) * .5f;
237 }
238 }
239 else
240 {
241 // get 4-star-nbhood:
242 const float *ntl = buf - 4 - linesize;
243 const float *ntr = buf + 4 - linesize;
244 const float *nbl = buf - 4 + linesize;
245 const float *nbr = buf + 4 + linesize;
246
247 if(c == 0)
248 {
249 // red pixel, fill blue:
250 const float diff1 = fabsf(ntl[2] - nbr[2]) + fabsf(ntl[1] - color[1]) + fabsf(nbr[1] - color[1]);
251 const float guess1 = ntl[2] + nbr[2] + 2.0f * color[1] - ntl[1] - nbr[1];
252 const float diff2 = fabsf(ntr[2] - nbl[2]) + fabsf(ntr[1] - color[1]) + fabsf(nbl[1] - color[1]);
253 const float guess2 = ntr[2] + nbl[2] + 2.0f * color[1] - ntr[1] - nbl[1];
254 if(diff1 > diff2)
255 color[2] = guess2 * .5f;
256 else if(diff1 < diff2)
257 color[2] = guess1 * .5f;
258 else
259 color[2] = (guess1 + guess2) * .25f;
260 }
261 else // c == 2, blue pixel, fill red:
262 {
263 const float diff1 = fabsf(ntl[0] - nbr[0]) + fabsf(ntl[1] - color[1]) + fabsf(nbr[1] - color[1]);
264 const float guess1 = ntl[0] + nbr[0] + 2.0f * color[1] - ntl[1] - nbr[1];
265 const float diff2 = fabsf(ntr[0] - nbl[0]) + fabsf(ntr[1] - color[1]) + fabsf(nbl[1] - color[1]);
266 const float guess2 = ntr[0] + nbl[0] + 2.0f * color[1] - ntr[1] - nbl[1];
267 if(diff1 > diff2)
268 color[0] = guess2 * .5f;
269 else if(diff1 < diff2)
270 color[0] = guess1 * .5f;
271 else
272 color[0] = (guess1 + guess2) * .25f;
273 }
274 }
275 memcpy(buf, color, sizeof(float) * 4);
276 buf += 4;
277 }
278 }
279
280}
281
282static 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,
283 const dt_iop_roi_t *const roi_in, const uint32_t filters)
284{
285 const int width = roi_in->width;
286 const int height = roi_in->height;
287
288 if((width < 16) || (height < 16))
289 {
290 dt_control_log(_("[rcd_demosaic] too small area"));
291 return;
292 }
293
294 rcd_ppg_border(out, in, width, height, filters, RCD_BORDER);
295
296 const float scaler = fmaxf(piece->dsc_in.processed_maximum[0], fmaxf(piece->dsc_in.processed_maximum[1], piece->dsc_in.processed_maximum[2]));
297 const float revscaler = 1.0f / scaler;
298
299 const int num_vertical = 1 + (height - 2 * RCD_BORDER -1) / RCD_TILEVALID;
300 const int num_horizontal = 1 + (width - 2 * RCD_BORDER -1) / RCD_TILEVALID;
301
302#ifdef _OPENMP
303 #pragma omp parallel
304#endif
305 {
306 // FIXME: CRITICAL: need to handle the case where we couldn't alloc the memory,
307 // but in a parallel section, that's not going to be trivial.
308 dt_fp_init(DT_FP_MODE_FAST);
309
310 // VH_Dir and rgb have elements no step writes and a later one reads (note 1). Zeroed once, so
311 // that those reads stay finite whatever the arena handed back; RCD_BORDER keeps them out of
312 // the output, whatever they hold by the time a tile reads them.
314 memset(VH_Dir, 0, sizeof(*VH_Dir) * RCD_TILESIZE * RCD_TILESIZE);
317 float *P_CDiff_Hpf = dt_pixelpipe_cache_alloc_align_float_cache((size_t) RCD_TILESIZE * RCD_TILESIZE / 2, 0);
318 float *Q_CDiff_Hpf = dt_pixelpipe_cache_alloc_align_float_cache((size_t) RCD_TILESIZE * RCD_TILESIZE / 2, 0);
319
320 float (*rgb)[RCD_TILESIZE * RCD_TILESIZE] =
322 memset(rgb, 0, sizeof(float) * 3 * RCD_TILESIZE * RCD_TILESIZE);
323
324 // No overlapping use so re-use same buffer
325 float *lpf = PQ_Dir;
326
327 // There has been a discussion about the schedule strategy, at least on the tested machines the
328 // dynamic scheduling seems to be slightly faster.
329#ifdef _OPENMP
330 #pragma omp for schedule(simd:dynamic, 6) collapse(2) nowait
331#endif
332 for(int tile_vertical = 0; tile_vertical < num_vertical; tile_vertical++)
333 {
334 for(int tile_horizontal = 0; tile_horizontal < num_horizontal; tile_horizontal++)
335 {
336 const int rowStart = tile_vertical * RCD_TILEVALID;
337 const int rowEnd = MIN(rowStart + RCD_TILESIZE, height);
338
339 const int colStart = tile_horizontal * RCD_TILEVALID;
340 const int colEnd = MIN(colStart + RCD_TILESIZE, width);
341
342 const int tileRows = MIN(rowEnd - rowStart, RCD_TILESIZE);
343 const int tileCols = MIN(colEnd - colStart, RCD_TILESIZE);
344
345 // Step 0: fill data and make sure data are not negative.
346 for(int row = rowStart; row < rowEnd; row++)
347 {
348 const int c0 = FC(row, colStart, filters);
349 const int c1 = FC(row, colStart + 1, filters);
350 for(int col = colStart, indx = (row - rowStart) * RCD_TILESIZE, in_indx = row * width + colStart; col < colEnd; col++, indx++, in_indx++)
351 {
352 cfa[indx] = rgb[c0][indx] = rgb[c1][indx] = safe_in(in[in_indx], revscaler);
353 }
354 }
355
356 // STEP 1: Find vertical and horizontal interpolation directions
357 float bufferV[3][RCD_TILESIZE - 8];
358 // Step 1.1: Calculate the square of the vertical and horizontal color difference high pass filter
359 for(int row = 3; row < MIN(tileRows - 3, 5); row++ )
360 {
361 for(int col = 4, indx = row * RCD_TILESIZE + col; col < tileCols - 4; col++, indx++ )
362 {
363 bufferV[row - 3][col - 4] = sqf((cfa[indx - w3] - cfa[indx - w1] - cfa[indx + w1] + cfa[indx + w3]) - 3.0f * (cfa[indx - w2] + cfa[indx + w2]) + 6.0f * cfa[indx]);
364 }
365 }
366
367 // Step 1.2: Obtain the vertical and horizontal directional discrimination strength
368 float DT_ALIGNED_PIXEL bufferH[RCD_TILESIZE];
369 // We start with V0, V1 and V2 pointing to row -1, row and row +1
370 // After row is processed V0 must point to the old V1, V1 must point to the old V2 and V2 must point to the old V0
371 // because the old V0 is not used anymore and will be filled with row + 1 data in next iteration
372 float* V0 = bufferV[0];
373 float* V1 = bufferV[1];
374 float* V2 = bufferV[2];
375 for(int row = 4; row < tileRows - 4; row++ )
376 {
377 for(int col = 3, indx = row * RCD_TILESIZE + col; col < tileCols - 3; col++, indx++)
378 {
379 bufferH[col - 3] = sqf((cfa[indx - 3] - cfa[indx - 1] - cfa[indx + 1] + cfa[indx + 3]) - 3.0f * (cfa[indx - 2] + cfa[indx + 2]) + 6.0f * cfa[indx]);
380 }
381 for(int col = 4, indx = (row + 1) * RCD_TILESIZE + col; col < tileCols - 4; col++, indx++)
382 {
383 V2[col - 4] = sqf((cfa[indx - w3] - cfa[indx - w1] - cfa[indx + w1] + cfa[indx + w3]) - 3.0f * (cfa[indx - w2] + cfa[indx + w2]) + 6.0f * cfa[indx]);
384 }
385 for(int col = 4, indx = row * RCD_TILESIZE + col; col < tileCols - 4; col++, indx++ )
386 {
387 const float V_Stat = fmaxf(epssq, V0[col - 4] + V1[col - 4] + V2[col - 4]);
388 const float H_Stat = fmaxf(epssq, bufferH[col - 4] + bufferH[col - 3] + bufferH[col - 2]);
389 VH_Dir[indx] = V_Stat / ( V_Stat + H_Stat );
390 }
391 // rolling the line pointers
392 float* tmp = V0; V0 = V1; V1 = V2; V2 = tmp;
393 }
394
395 // STEP 2: Calculate the low pass filter
396 // Step 2.1: Low pass filter incorporating green, red and blue local samples from the raw data
397 for(int row = 2; row < tileRows - 2; row++)
398 {
399 for(int col = 2 + (FC(row, 0, filters) & 1), indx = row * RCD_TILESIZE + col, lp_indx = indx / 2; col < tileCols - 2; col += 2, indx +=2, lp_indx++)
400 {
401 lpf[lp_indx] = cfa[indx]
402 + 0.5f * (cfa[indx - w1] + cfa[indx + w1] + cfa[indx - 1] + cfa[indx + 1])
403 + 0.25f * (cfa[indx - w1 - 1] + cfa[indx - w1 + 1] + cfa[indx + w1 - 1] + cfa[indx + w1 + 1]);
404 }
405 }
406
407 // STEP 3: Populate the green channel
408 // Step 3.1: Populate the green channel at blue and red CFA positions
409 for(int row = 4; row < tileRows - 4; row++)
410 {
411 for(int col = 4 + (FC(row, 0, filters) & 1), indx = row * RCD_TILESIZE + col, lpindx = indx / 2; col < tileCols - 4; col += 2, indx += 2, lpindx++)
412 {
413 const float cfai = cfa[indx];
414
415 // Cardinal gradients
416 const float N_Grad = eps + fabs(cfa[indx - w1] - cfa[indx + w1]) + fabs(cfai - cfa[indx - w2]) + fabs(cfa[indx - w1] - cfa[indx - w3]) + fabs(cfa[indx - w2] - cfa[indx - w4]);
417 const float S_Grad = eps + fabs(cfa[indx - w1] - cfa[indx + w1]) + fabs(cfai - cfa[indx + w2]) + fabs(cfa[indx + w1] - cfa[indx + w3]) + fabs(cfa[indx + w2] - cfa[indx + w4]);
418 const float W_Grad = eps + fabs(cfa[indx - 1] - cfa[indx + 1]) + fabs(cfai - cfa[indx - 2]) + fabs(cfa[indx - 1] - cfa[indx - 3]) + fabs(cfa[indx - 2] - cfa[indx - 4]);
419 const float E_Grad = eps + fabs(cfa[indx - 1] - cfa[indx + 1]) + fabs(cfai - cfa[indx + 2]) + fabs(cfa[indx + 1] - cfa[indx + 3]) + fabs(cfa[indx + 2] - cfa[indx + 4]);
420
421 // Cardinal pixel estimations
422 const float lpfi = lpf[lpindx];
423 const float N_Est = cfa[indx - w1] * (lpfi + lpfi) / (eps + lpfi + lpf[lpindx - w1]);
424 const float S_Est = cfa[indx + w1] * (lpfi + lpfi) / (eps + lpfi + lpf[lpindx + w1]);
425 const float W_Est = cfa[indx - 1] * (lpfi + lpfi) / (eps + lpfi + lpf[lpindx - 1]);
426 const float E_Est = cfa[indx + 1] * (lpfi + lpfi) / (eps + lpfi + lpf[lpindx + 1]);
427
428 // Vertical and horizontal estimations
429 const float V_Est = (S_Grad * N_Est + N_Grad * S_Est) / (N_Grad + S_Grad);
430 const float H_Est = (W_Grad * E_Est + E_Grad * W_Est) / (E_Grad + W_Grad);
431
432 // G@B and G@R interpolation
433 // Refined vertical and horizontal local discrimination
434 const float VH_Central_Value = VH_Dir[indx];
435 const float VH_Neighbourhood_Value = 0.25f * (VH_Dir[indx - w1 - 1] + VH_Dir[indx - w1 + 1] + VH_Dir[indx + w1 - 1] + VH_Dir[indx + w1 + 1]);
436 const float VH_Disc = (fabs(0.5f - VH_Central_Value) < fabs(0.5f - VH_Neighbourhood_Value)) ? VH_Neighbourhood_Value : VH_Central_Value;
437
438 rgb[1][indx] = intp(VH_Disc, H_Est, V_Est);
439 }
440 }
441
442 // STEP 4: Populate the red and blue channels
443
444 // Step 4.0: Calculate the square of the P/Q diagonals color difference high pass filter
445 for(int row = 3; row < tileRows - 3; row++)
446 {
447 for(int col = 3, indx = row * RCD_TILESIZE + col, indx2 = indx / 2; col < tileCols - 3; col+=2, indx+=2, indx2++)
448 {
449 P_CDiff_Hpf[indx2] = sqf((cfa[indx - w3 - 3] - cfa[indx - w1 - 1] - cfa[indx + w1 + 1] + cfa[indx + w3 + 3]) - 3.0f * (cfa[indx - w2 - 2] + cfa[indx + w2 + 2]) + 6.0f * cfa[indx]);
450 Q_CDiff_Hpf[indx2] = sqf((cfa[indx - w3 + 3] - cfa[indx - w1 + 1] - cfa[indx + w1 - 1] + cfa[indx + w3 - 3]) - 3.0f * (cfa[indx - w2 + 2] + cfa[indx + w2 - 2]) + 6.0f * cfa[indx]);
451 }
452 }
453 // Step 4.1: Obtain the P/Q diagonals directional discrimination strength
454 for(int row = 4; row < tileRows - 4; row++)
455 {
456 for(int col = 4 + (FC(row, 0, filters) & 1), indx = row * RCD_TILESIZE + col, indx2 = indx / 2, indx3 = (indx - w1 - 1) / 2, indx4 = (indx + w1 - 1) / 2; col < tileCols - 4; col += 2, indx += 2, indx2++, indx3++, indx4++ )
457 {
458 const float P_Stat = fmaxf(epssq, P_CDiff_Hpf[indx3] + P_CDiff_Hpf[indx2] + P_CDiff_Hpf[indx4 + 1]);
459 const float Q_Stat = fmaxf(epssq, Q_CDiff_Hpf[indx3 + 1] + Q_CDiff_Hpf[indx2] + Q_CDiff_Hpf[indx4]);
460 PQ_Dir[indx2] = P_Stat / (P_Stat + Q_Stat);
461 }
462 }
463
464 // Step 4.2: Populate the red and blue channels at blue and red CFA positions
465 for(int row = 4; row < tileRows - 4; row++)
466 {
467 for(int col = 4 + (FC(row, 0, filters) & 1), indx = row * RCD_TILESIZE + col, c = 2 - FC(row, col, filters), pqindx = indx / 2, pqindx2 = (indx - w1 - 1) / 2, pqindx3 = (indx + w1 - 1) / 2; col < tileCols - 4; col += 2, indx += 2, pqindx++, pqindx2++, pqindx3++)
468 {
469 // Refined P/Q diagonal local discrimination
470 const float PQ_Central_Value = PQ_Dir[pqindx];
471 const float PQ_Neighbourhood_Value = 0.25f * (PQ_Dir[pqindx2] + PQ_Dir[pqindx2 + 1] + PQ_Dir[pqindx3] + PQ_Dir[pqindx3 + 1]);
472
473 const float PQ_Disc = (fabs(0.5f - PQ_Central_Value) < fabs(0.5f - PQ_Neighbourhood_Value)) ? PQ_Neighbourhood_Value : PQ_Central_Value;
474
475 // Diagonal gradients
476 const float NW_Grad = eps + fabs(rgb[c][indx - w1 - 1] - rgb[c][indx + w1 + 1]) + fabs(rgb[c][indx - w1 - 1] - rgb[c][indx - w3 - 3]) + fabs(rgb[1][indx] - rgb[1][indx - w2 - 2]);
477 const float NE_Grad = eps + fabs(rgb[c][indx - w1 + 1] - rgb[c][indx + w1 - 1]) + fabs(rgb[c][indx - w1 + 1] - rgb[c][indx - w3 + 3]) + fabs(rgb[1][indx] - rgb[1][indx - w2 + 2]);
478 const float SW_Grad = eps + fabs(rgb[c][indx - w1 + 1] - rgb[c][indx + w1 - 1]) + fabs(rgb[c][indx + w1 - 1] - rgb[c][indx + w3 - 3]) + fabs(rgb[1][indx] - rgb[1][indx + w2 - 2]);
479 const float SE_Grad = eps + fabs(rgb[c][indx - w1 - 1] - rgb[c][indx + w1 + 1]) + fabs(rgb[c][indx + w1 + 1] - rgb[c][indx + w3 + 3]) + fabs(rgb[1][indx] - rgb[1][indx + w2 + 2]);
480
481 // Diagonal colour differences
482 const float NW_Est = rgb[c][indx - w1 - 1] - rgb[1][indx - w1 - 1];
483 const float NE_Est = rgb[c][indx - w1 + 1] - rgb[1][indx - w1 + 1];
484 const float SW_Est = rgb[c][indx + w1 - 1] - rgb[1][indx + w1 - 1];
485 const float SE_Est = rgb[c][indx + w1 + 1] - rgb[1][indx + w1 + 1];
486
487 // P/Q estimations
488 const float P_Est = (NW_Grad * SE_Est + SE_Grad * NW_Est) / (NW_Grad + SE_Grad);
489 const float Q_Est = (NE_Grad * SW_Est + SW_Grad * NE_Est) / (NE_Grad + SW_Grad);
490
491 // R@B and B@R interpolation
492 rgb[c][indx] = rgb[1][indx] + intp(PQ_Disc, Q_Est, P_Est);
493 }
494 }
495
496 // Step 4.3: Populate the red and blue channels at green CFA positions
497 for(int row = 4; row < tileRows - 4; row++)
498 {
499 for(int col = 4 + (FC(row, 1, filters) & 1), indx = row * RCD_TILESIZE + col; col < tileCols - 4; col += 2, indx +=2)
500 {
501 // Refined vertical and horizontal local discrimination
502 const float VH_Central_Value = VH_Dir[indx];
503 const float VH_Neighbourhood_Value = 0.25f * (VH_Dir[indx - w1 - 1] + VH_Dir[indx - w1 + 1] + VH_Dir[indx + w1 - 1] + VH_Dir[indx + w1 + 1]);
504 const float VH_Disc = (fabs(0.5f - VH_Central_Value) < fabs(0.5f - VH_Neighbourhood_Value) ) ? VH_Neighbourhood_Value : VH_Central_Value;
505 const float rgb1 = rgb[1][indx];
506 const float N1 = eps + fabs(rgb1 - rgb[1][indx - w2]);
507 const float S1 = eps + fabs(rgb1 - rgb[1][indx + w2]);
508 const float W1 = eps + fabs(rgb1 - rgb[1][indx - 2]);
509 const float E1 = eps + fabs(rgb1 - rgb[1][indx + 2]);
510
511 const float rgb1mw1 = rgb[1][indx - w1];
512 const float rgb1pw1 = rgb[1][indx + w1];
513 const float rgb1m1 = rgb[1][indx - 1];
514 const float rgb1p1 = rgb[1][indx + 1];
515
516 for(int c = 0; c <= 2; c += 2)
517 {
518 const float SNabs = fabs(rgb[c][indx - w1] - rgb[c][indx + w1]);
519 const float EWabs = fabs(rgb[c][indx - 1] - rgb[c][indx + 1]);
520
521 // Cardinal gradients
522 const float N_Grad = N1 + SNabs + fabs(rgb[c][indx - w1] - rgb[c][indx - w3]);
523 const float S_Grad = S1 + SNabs + fabs(rgb[c][indx + w1] - rgb[c][indx + w3]);
524 const float W_Grad = W1 + EWabs + fabs(rgb[c][indx - 1] - rgb[c][indx - 3]);
525 const float E_Grad = E1 + EWabs + fabs(rgb[c][indx + 1] - rgb[c][indx + 3]);
526
527 // Cardinal colour differences
528 const float N_Est = rgb[c][indx - w1] - rgb1mw1;
529 const float S_Est = rgb[c][indx + w1] - rgb1pw1;
530 const float W_Est = rgb[c][indx - 1] - rgb1m1;
531 const float E_Est = rgb[c][indx + 1] - rgb1p1;
532
533 // Vertical and horizontal estimations
534 const float V_Est = (N_Grad * S_Est + S_Grad * N_Est) / (N_Grad + S_Grad);
535 const float H_Est = (E_Grad * W_Est + W_Grad * E_Est) / (E_Grad + W_Grad);
536
537 // R@G and B@G interpolation
538 rgb[c][indx] = rgb1 + intp(VH_Disc, H_Est, V_Est);
539 }
540 }
541 }
542
543 // The outermost tiles need the same border: the image edge is a tile edge like any other
544 const int first_vertical = rowStart + RCD_BORDER;
545 const int last_vertical = rowEnd - RCD_BORDER;
546 const int first_horizontal = colStart + RCD_BORDER;
547 const int last_horizontal = colEnd - RCD_BORDER;
548 for(int row = first_vertical; row < last_vertical; row++)
549 {
550 for(int col = first_horizontal, idx = (row - rowStart) * RCD_TILESIZE + col - colStart, o_idx = (row * width + col) * 4; col < last_horizontal; col++, o_idx += 4, idx++)
551 {
552 out[o_idx] = scaler * fmaxf(0.0f, rgb[0][idx]);
553 out[o_idx+1] = scaler * fmaxf(0.0f, rgb[1][idx]);
554 out[o_idx+2] = scaler * fmaxf(0.0f, rgb[2][idx]);
555 out[o_idx+3] = 0.0f;
556 }
557 }
558 }
559 }
566 }
567}
568
569#ifdef HAVE_OPENCL
570
572 const dt_dev_pixelpipe_iop_t *piece, cl_mem dev_in, cl_mem dev_out,
573 const dt_iop_roi_t *const roi_in, const dt_iop_roi_t *const roi_out,
574 const gboolean smooth)
575{
578
579 const int devid = pipe->devid;
580
581 cl_mem dev_aux = NULL;
582 cl_mem dev_tmp = NULL;
583 cl_mem dev_green_eq = NULL;
584 cl_mem cfa = NULL;
585 cl_mem rgb0 = NULL;
586 cl_mem rgb1 = NULL;
587 cl_mem rgb2 = NULL;
588 cl_mem VH_dir = NULL;
589 cl_mem PQ_dir = NULL;
590 cl_mem VP_diff = NULL;
591 cl_mem HQ_diff = NULL;
592
593 cl_int err = -999;
594
595 int width = roi_out->width;
596 int height = roi_out->height;
597
598 // RCD works in tile-local coordinates with no roi awareness, so it needs the dynamic
599 // ROI offset folded into filters up front (see demosaic.c process()'s matching comment).
600 const uint32_t filters = dt_dev_get_roi_filters(piece, roi_in);
601
602 // green equilibration
603 if(data->green_eq != DT_IOP_GREEN_EQ_NO)
604 {
605 dev_green_eq = dt_opencl_alloc_device(devid, roi_in->width, roi_in->height, sizeof(float));
606 if(IS_NULL_PTR(dev_green_eq)) goto error;
607 if(!green_equilibration_cl(self, pipe, piece, dev_in, dev_green_eq, roi_in)) goto error;
608 dev_in = dev_green_eq;
609 }
610
611 // need to reserve scaled auxiliary buffer or use dev_out
612 dev_aux = dev_out;
613
614 dev_tmp = dt_opencl_alloc_device(devid, roi_in->width, roi_in->height, sizeof(float) * 4);
615 if(IS_NULL_PTR(dev_tmp)) goto error;
616
617 {
618 const int myborder = 3;
619 size_t sizes[3] = { ROUNDUPDWD(width, devid), ROUNDUPDHT(height, devid), 1 };
620 dt_opencl_set_kernel_arg(devid, gd->kernel_border_interpolate, 0, sizeof(cl_mem), &dev_in);
621 dt_opencl_set_kernel_arg(devid, gd->kernel_border_interpolate, 1, sizeof(cl_mem), &dev_tmp);
622 dt_opencl_set_kernel_arg(devid, gd->kernel_border_interpolate, 2, sizeof(int), (void *)&width);
623 dt_opencl_set_kernel_arg(devid, gd->kernel_border_interpolate, 3, sizeof(int), (void *)&height);
624 dt_opencl_set_kernel_arg(devid, gd->kernel_border_interpolate, 4, sizeof(uint32_t), (void *)&filters);
625 dt_opencl_set_kernel_arg(devid, gd->kernel_border_interpolate, 5, sizeof(int), (void *)&myborder);
627 if(err != CL_SUCCESS) goto error;
628 }
629
630 {
632 = (dt_opencl_local_buffer_t){ .xoffset = 2*3, .xfactor = 1, .yoffset = 2*3, .yfactor = 1,
633 .cellsize = sizeof(float) * 1, .overhead = 0,
634 .sizex = 64, .sizey = 64 };
635
636 if(!dt_opencl_local_buffer_opt(devid, gd->kernel_rcd_border_green, &locopt)) goto error;
637 const int myborder = 32;
638 size_t sizes[3] = { ROUNDUP(width, locopt.sizex), ROUNDUP(height, locopt.sizey), 1 };
639 size_t local[3] = { locopt.sizex, locopt.sizey, 1 };
640 dt_opencl_set_kernel_arg(devid, gd->kernel_rcd_border_green, 0, sizeof(cl_mem), &dev_in);
641 dt_opencl_set_kernel_arg(devid, gd->kernel_rcd_border_green, 1, sizeof(cl_mem), &dev_tmp);
642 dt_opencl_set_kernel_arg(devid, gd->kernel_rcd_border_green, 2, sizeof(int), &width);
643 dt_opencl_set_kernel_arg(devid, gd->kernel_rcd_border_green, 3, sizeof(int), &height);
644 dt_opencl_set_kernel_arg(devid, gd->kernel_rcd_border_green, 4, sizeof(uint32_t), (void *)&filters);
645 dt_opencl_set_kernel_arg(devid, gd->kernel_rcd_border_green, 5, sizeof(float) * (locopt.sizex + 2*3) * (locopt.sizey + 2*3), NULL);
646 dt_opencl_set_kernel_arg(devid, gd->kernel_rcd_border_green, 6, sizeof(int), (void *)&myborder);
648 if(err != CL_SUCCESS) goto error;
649 }
650
651 {
653 = (dt_opencl_local_buffer_t){ .xoffset = 2*1, .xfactor = 1, .yoffset = 2*1, .yfactor = 1,
654 .cellsize = 4 * sizeof(float), .overhead = 0,
655 .sizex = 64, .sizey = 64 };
656
657 if(!dt_opencl_local_buffer_opt(devid, gd->kernel_rcd_border_redblue, &locopt)) goto error;
658 const int myborder = 16;
659 size_t sizes[3] = { ROUNDUP(width, locopt.sizex), ROUNDUP(height, locopt.sizey), 1 };
660 size_t local[3] = { locopt.sizex, locopt.sizey, 1 };
661 dt_opencl_set_kernel_arg(devid, gd->kernel_rcd_border_redblue, 0, sizeof(cl_mem), &dev_tmp);
662 dt_opencl_set_kernel_arg(devid, gd->kernel_rcd_border_redblue, 1, sizeof(cl_mem), &dev_aux);
663 dt_opencl_set_kernel_arg(devid, gd->kernel_rcd_border_redblue, 2, sizeof(int), &width);
664 dt_opencl_set_kernel_arg(devid, gd->kernel_rcd_border_redblue, 3, sizeof(int), &height);
665 dt_opencl_set_kernel_arg(devid, gd->kernel_rcd_border_redblue, 4, sizeof(uint32_t), (void *)&filters);
666 dt_opencl_set_kernel_arg(devid, gd->kernel_rcd_border_redblue, 5, sizeof(float) * 4 * (locopt.sizex + 2) * (locopt.sizey + 2), NULL);
667 dt_opencl_set_kernel_arg(devid, gd->kernel_rcd_border_redblue, 6, sizeof(int), (void *)&myborder);
669 if(err != CL_SUCCESS) goto error;
670 }
672 dev_tmp = NULL;
673
674 cfa = dt_opencl_alloc_device_buffer(devid, sizeof(float) * roi_in->width * roi_in->height);
675 if(IS_NULL_PTR(cfa)) goto error;
676 VH_dir = dt_opencl_alloc_device_buffer(devid, sizeof(float) * roi_in->width * roi_in->height);
677 if(IS_NULL_PTR(VH_dir)) goto error;
678 PQ_dir = dt_opencl_alloc_device_buffer(devid, sizeof(float) * roi_in->width * roi_in->height);
679 if(IS_NULL_PTR(PQ_dir)) goto error;
680 VP_diff = dt_opencl_alloc_device_buffer(devid, sizeof(float) * roi_in->width * roi_in->height);
681 if(IS_NULL_PTR(VP_diff)) goto error;
682 HQ_diff = dt_opencl_alloc_device_buffer(devid, sizeof(float) * roi_in->width * roi_in->height);
683 if(IS_NULL_PTR(HQ_diff)) goto error;
684 rgb0 = dt_opencl_alloc_device_buffer(devid, sizeof(float) * roi_in->width * roi_in->height);
685 if(IS_NULL_PTR(rgb0)) goto error;
686 rgb1 = dt_opencl_alloc_device_buffer(devid, sizeof(float) * roi_in->width * roi_in->height);
687 if(IS_NULL_PTR(rgb1)) goto error;
688 rgb2 = dt_opencl_alloc_device_buffer(devid, sizeof(float) * roi_in->width * roi_in->height);
689 if(IS_NULL_PTR(rgb2)) goto error;
690
691 {
692 // populate data
693 size_t sizes[3] = { ROUNDUPDWD(width, devid), ROUNDUPDHT(height, devid), 1 };
694 const float scaler = 1.0f / fmaxf(piece->dsc_in.processed_maximum[0], fmaxf(piece->dsc_in.processed_maximum[1], piece->dsc_in.processed_maximum[2]));
695 dt_opencl_set_kernel_arg(devid, gd->kernel_rcd_populate, 0, sizeof(cl_mem), &dev_in);
696 dt_opencl_set_kernel_arg(devid, gd->kernel_rcd_populate, 1, sizeof(cl_mem), &cfa);
697 dt_opencl_set_kernel_arg(devid, gd->kernel_rcd_populate, 2, sizeof(cl_mem), &rgb0);
698 dt_opencl_set_kernel_arg(devid, gd->kernel_rcd_populate, 3, sizeof(cl_mem), &rgb1);
699 dt_opencl_set_kernel_arg(devid, gd->kernel_rcd_populate, 4, sizeof(cl_mem), &rgb2);
700 dt_opencl_set_kernel_arg(devid, gd->kernel_rcd_populate, 5, sizeof(int), &width);
701 dt_opencl_set_kernel_arg(devid, gd->kernel_rcd_populate, 6, sizeof(int), &height);
702 dt_opencl_set_kernel_arg(devid, gd->kernel_rcd_populate, 7, sizeof(uint32_t), (void *)&filters);
703 dt_opencl_set_kernel_arg(devid, gd->kernel_rcd_populate, 8, sizeof(float), &scaler);
704 err = dt_opencl_enqueue_kernel_2d(devid, gd->kernel_rcd_populate, sizes);
705 if(err != CL_SUCCESS) goto error;
706 }
707
708 {
709 // Fuse the vertical/horizontal high-pass and discrimination stages so the intermediate
710 // directional filters stay tile-local instead of being written to global memory.
712 = (dt_opencl_local_buffer_t){ .xoffset = 8, .xfactor = 1, .yoffset = 8, .yfactor = 1,
713 .cellsize = sizeof(float), .overhead = 0,
714 .sizex = 64, .sizey = 64 };
715
716 if(!dt_opencl_local_buffer_opt(devid, gd->kernel_rcd_step_1, &locopt)) goto error;
717 size_t sizes[3] = { ROUNDUP(width, locopt.sizex), ROUNDUP(height, locopt.sizey), 1 };
718 size_t local[3] = { locopt.sizex, locopt.sizey, 1 };
719 dt_opencl_set_kernel_arg(devid, gd->kernel_rcd_step_1, 0, sizeof(cl_mem), &cfa);
720 dt_opencl_set_kernel_arg(devid, gd->kernel_rcd_step_1, 1, sizeof(cl_mem), &VH_dir);
721 dt_opencl_set_kernel_arg(devid, gd->kernel_rcd_step_1, 2, sizeof(int), &width);
722 dt_opencl_set_kernel_arg(devid, gd->kernel_rcd_step_1, 3, sizeof(int), &height);
724 sizeof(float) * (locopt.sizex + 8) * (locopt.sizey + 8), NULL);
725 err = dt_opencl_enqueue_kernel_2d_with_local(devid, gd->kernel_rcd_step_1, sizes, local);
726 if(err != CL_SUCCESS) goto error;
727 }
728
729 {
730 // Reuse PQ_dir as the packed low-pass field consumed by step 3, then overwrite the same
731 // half-width buffer later with the diagonal discriminator used by step 5.
732 size_t sizes[3] = { ROUNDUPDWD(width / 2, devid), ROUNDUPDHT(height, devid), 1 };
733 dt_opencl_set_kernel_arg(devid, gd->kernel_rcd_step_2_1, 0, sizeof(cl_mem), &PQ_dir);
734 dt_opencl_set_kernel_arg(devid, gd->kernel_rcd_step_2_1, 1, sizeof(cl_mem), &cfa);
735 dt_opencl_set_kernel_arg(devid, gd->kernel_rcd_step_2_1, 2, sizeof(int), &width);
736 dt_opencl_set_kernel_arg(devid, gd->kernel_rcd_step_2_1, 3, sizeof(int), &height);
737 dt_opencl_set_kernel_arg(devid, gd->kernel_rcd_step_2_1, 4, sizeof(uint32_t), (void *)&filters);
738 err = dt_opencl_enqueue_kernel_2d(devid, gd->kernel_rcd_step_2_1, sizes);
739 if(err != CL_SUCCESS) goto error;
740 }
741
742 {
743 size_t sizes[3] = { ROUNDUPDWD(width / 2, devid), ROUNDUPDHT(height, devid), 1 };
744 dt_opencl_set_kernel_arg(devid, gd->kernel_rcd_step_3_1, 0, sizeof(cl_mem), &PQ_dir);
745 dt_opencl_set_kernel_arg(devid, gd->kernel_rcd_step_3_1, 1, sizeof(cl_mem), &cfa);
746 dt_opencl_set_kernel_arg(devid, gd->kernel_rcd_step_3_1, 2, sizeof(cl_mem), &rgb1);
747 dt_opencl_set_kernel_arg(devid, gd->kernel_rcd_step_3_1, 3, sizeof(cl_mem), &VH_dir);
748 dt_opencl_set_kernel_arg(devid, gd->kernel_rcd_step_3_1, 4, sizeof(int), &width);
749 dt_opencl_set_kernel_arg(devid, gd->kernel_rcd_step_3_1, 5, sizeof(int), &height);
750 dt_opencl_set_kernel_arg(devid, gd->kernel_rcd_step_3_1, 6, sizeof(uint32_t), (void *)&filters);
751 err = dt_opencl_enqueue_kernel_2d(devid, gd->kernel_rcd_step_3_1, sizes);
752 if(err != CL_SUCCESS) goto error;
753 }
754
755 {
756 size_t sizes[3] = { ROUNDUPDWD(width / 2, devid), ROUNDUPDHT(height, devid), 1 };
757 // Reuse the full-size scratch pair for the diagonal high-pass fields, then collapse
758 // them into PQ_dir before the red/blue reconstruction reads that compact field.
759 dt_opencl_set_kernel_arg(devid, gd->kernel_rcd_step_4_1, 0, sizeof(cl_mem), &cfa);
760 dt_opencl_set_kernel_arg(devid, gd->kernel_rcd_step_4_1, 1, sizeof(cl_mem), &VP_diff);
761 dt_opencl_set_kernel_arg(devid, gd->kernel_rcd_step_4_1, 2, sizeof(cl_mem), &HQ_diff);
762 dt_opencl_set_kernel_arg(devid, gd->kernel_rcd_step_4_1, 3, sizeof(int), &width);
763 dt_opencl_set_kernel_arg(devid, gd->kernel_rcd_step_4_1, 4, sizeof(int), &height);
764 dt_opencl_set_kernel_arg(devid, gd->kernel_rcd_step_4_1, 5, sizeof(uint32_t), (void *)&filters);
765 err = dt_opencl_enqueue_kernel_2d(devid, gd->kernel_rcd_step_4_1, sizes);
766 if(err != CL_SUCCESS) goto error;
767
768 dt_opencl_set_kernel_arg(devid, gd->kernel_rcd_step_4_2, 0, sizeof(cl_mem), &PQ_dir);
769 dt_opencl_set_kernel_arg(devid, gd->kernel_rcd_step_4_2, 1, sizeof(cl_mem), &VP_diff);
770 dt_opencl_set_kernel_arg(devid, gd->kernel_rcd_step_4_2, 2, sizeof(cl_mem), &HQ_diff);
771 dt_opencl_set_kernel_arg(devid, gd->kernel_rcd_step_4_2, 3, sizeof(int), &width);
772 dt_opencl_set_kernel_arg(devid, gd->kernel_rcd_step_4_2, 4, sizeof(int), &height);
773 dt_opencl_set_kernel_arg(devid, gd->kernel_rcd_step_4_2, 5, sizeof(uint32_t), (void *)&filters);
774 err = dt_opencl_enqueue_kernel_2d(devid, gd->kernel_rcd_step_4_2, sizes);
775 if(err != CL_SUCCESS) goto error;
776
777 dt_opencl_set_kernel_arg(devid, gd->kernel_rcd_step_5_1, 0, sizeof(cl_mem), &PQ_dir);
778 dt_opencl_set_kernel_arg(devid, gd->kernel_rcd_step_5_1, 1, sizeof(cl_mem), &rgb0);
779 dt_opencl_set_kernel_arg(devid, gd->kernel_rcd_step_5_1, 2, sizeof(cl_mem), &rgb1);
780 dt_opencl_set_kernel_arg(devid, gd->kernel_rcd_step_5_1, 3, sizeof(cl_mem), &rgb2);
781 dt_opencl_set_kernel_arg(devid, gd->kernel_rcd_step_5_1, 4, sizeof(int), &width);
782 dt_opencl_set_kernel_arg(devid, gd->kernel_rcd_step_5_1, 5, sizeof(int), &height);
783 dt_opencl_set_kernel_arg(devid, gd->kernel_rcd_step_5_1, 6, sizeof(uint32_t), (void *)&filters);
784 err = dt_opencl_enqueue_kernel_2d(devid, gd->kernel_rcd_step_5_1, sizes);
785 if(err != CL_SUCCESS) goto error;
786
787 dt_opencl_set_kernel_arg(devid, gd->kernel_rcd_step_5_2, 0, sizeof(cl_mem), &VH_dir);
788 dt_opencl_set_kernel_arg(devid, gd->kernel_rcd_step_5_2, 1, sizeof(cl_mem), &rgb0);
789 dt_opencl_set_kernel_arg(devid, gd->kernel_rcd_step_5_2, 2, sizeof(cl_mem), &rgb1);
790 dt_opencl_set_kernel_arg(devid, gd->kernel_rcd_step_5_2, 3, sizeof(cl_mem), &rgb2);
791 dt_opencl_set_kernel_arg(devid, gd->kernel_rcd_step_5_2, 4, sizeof(int), &width);
792 dt_opencl_set_kernel_arg(devid, gd->kernel_rcd_step_5_2, 5, sizeof(int), &height);
793 dt_opencl_set_kernel_arg(devid, gd->kernel_rcd_step_5_2, 6, sizeof(uint32_t), (void *)&filters);
794 err = dt_opencl_enqueue_kernel_2d(devid, gd->kernel_rcd_step_5_2, sizes);
795 if(err != CL_SUCCESS) goto error;
796 }
797 const float scaler = fmaxf(piece->dsc_in.processed_maximum[0], fmaxf(piece->dsc_in.processed_maximum[1], piece->dsc_in.processed_maximum[2]));
798
799 {
800 // write output, where every value it reads was computed: the kernels leave the outer rows and
801 // columns of their scratch buffers unwritten, as the CPU tiles do
802 const int myborder = RCD_BORDER;
803 size_t sizes[3] = { ROUNDUPDWD(width, devid), ROUNDUPDHT(height, devid), 1 };
804 dt_opencl_set_kernel_arg(devid, gd->kernel_rcd_write_output, 0, sizeof(cl_mem), &dev_aux);
805 dt_opencl_set_kernel_arg(devid, gd->kernel_rcd_write_output, 1, sizeof(cl_mem), &rgb0);
806 dt_opencl_set_kernel_arg(devid, gd->kernel_rcd_write_output, 2, sizeof(cl_mem), &rgb1);
807 dt_opencl_set_kernel_arg(devid, gd->kernel_rcd_write_output, 3, sizeof(cl_mem), &rgb2);
808 dt_opencl_set_kernel_arg(devid, gd->kernel_rcd_write_output, 4, sizeof(int), &width);
809 dt_opencl_set_kernel_arg(devid, gd->kernel_rcd_write_output, 5, sizeof(int), &height);
810 dt_opencl_set_kernel_arg(devid, gd->kernel_rcd_write_output, 6, sizeof(float), &scaler);
811 dt_opencl_set_kernel_arg(devid, gd->kernel_rcd_write_output, 7, sizeof(int), (void *)&myborder);
813 if(err != CL_SUCCESS) goto error;
814 }
815
824 dt_opencl_release_mem_object(dev_green_eq);
825 dev_green_eq = cfa = rgb0 = rgb1 = rgb2 = VH_dir = PQ_dir = VP_diff = HQ_diff = NULL;
826
827 if(dev_aux != dev_out) dt_opencl_release_mem_object(dev_aux);
828 dev_aux = NULL;
829
830 // color smoothing
831 if((data->color_smoothing) && smooth)
832 {
833 if(!color_smoothing_cl(self, pipe, piece, dev_out, dev_out, roi_out, data->color_smoothing))
834 goto error;
835 }
836
837 return TRUE;
838
839error:
840 if(dev_aux != dev_out) dt_opencl_release_mem_object(dev_aux);
841 dt_opencl_release_mem_object(dev_green_eq);
851 dev_aux = dev_green_eq = dev_tmp = cfa = rgb0 = rgb1 = rgb2 = VH_dir = PQ_dir = VP_diff = HQ_diff = NULL;
852 dt_print(DT_DEBUG_OPENCL, "[opencl_demosaic] rcd couldn't enqueue kernel! %d\n", err);
853 return FALSE;
854}
855#endif //HAVE_OPENCL
856
857#undef RCD_TILEVALID
858#undef w1
859#undef w2
860#undef w3
861#undef w4
862#undef eps
863#undef epssq
864//#undef RCD_TILESIZE
865
866
867// clang-format off
868// modelines: These editor modelines have been set for all relevant files by tools/update_modelines.py
869// vim: shiftwidth=2 expandtab tabstop=2 cindent
870// kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-spaces modified;
871// clang-format on
static void error(char *msg)
Definition ashift_lsd.c:202
#define TRUE
Definition ashift_lsd.c:162
#define FALSE
Definition ashift_lsd.c:158
#define m
Definition basecurve.c:283
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 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
#define INLINE
Definition cacorrect.c:172
static float intp(const float a, const float b, const float c)
Definition cacorrect.c:185
static const float x
const float f
static dt_aligned_pixel_t rgb
const dt_colormatrix_t dt_aligned_pixel_t out
static const int row
static const dt_colormatrix_t M
void dt_control_log(const char *msg,...)
Definition control.c:839
static int FC(const int row, const int col, const unsigned int filters)
@ DT_IOP_GREEN_EQ_NO
Definition demosaic.c:146
@ DT_FP_MODE_FAST
Definition fp_mode.h:40
GdkRGBA color[]
Definition geotagging.c:541
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
static const dt_dev_pixelpipe_t * pipe
Definition layers.h:49
static const dt_dev_pixelpipe_t const dt_dev_pixelpipe_iop_t * piece
Definition layers.h:50
@ DT_DEBUG_OPENCL
Definition logging.h:57
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.
#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
#define DT_ALIGNED_PIXEL
Align a 4-float pixel on 16 bytes, enough for SSE. Same struct-member caveat as DT_ALIGNED_ARRAY,...
Definition mem_alloc.h:85
uint32_t width
Definition mipmap_cache.c:0
uint32_t height
Definition mipmap_cache.c:1
int dt_opencl_local_buffer_opt(const int devid, const int kernel, dt_opencl_local_buffer_t *factors)
Definition opencl.c:3786
int dt_opencl_enqueue_kernel_2d(const int dev, const int kernel, const size_t *sizes)
Definition opencl.c:2612
void * dt_opencl_alloc_device_buffer(const int devid, const size_t size)
Definition opencl.c:3028
void * dt_opencl_alloc_device(const int devid, const int width, const int height, const int bpp)
Definition opencl.c:2952
int dt_opencl_set_kernel_arg(const int dev, const int kernel, const int num, const size_t size, const void *arg)
Definition opencl.c:2603
int dt_opencl_enqueue_kernel_2d_with_local(const int dev, const int kernel, const size_t *sizes, const size_t *local)
Definition opencl.c:2618
void dt_opencl_release_mem_object(cl_mem mem)
Definition opencl.c:2863
#define ROUNDUP(a, n)
Definition opencl.h:82
#define ROUNDUPDHT(a, b)
Definition opencl.h:86
#define ROUNDUPDWD(a, b)
Definition opencl.h:85
#define __OMP_PARALLEL_FOR__(...)
Definition openmp.h:95
#define dt_pixelpipe_cache_alloc_align_float_cache(pixels, id)
#define dt_pixelpipe_cache_free_align(mem)
static int process_rcd_cl(struct dt_iop_module_t *self, const dt_dev_pixelpipe_t *pipe, const dt_dev_pixelpipe_iop_t *piece, cl_mem dev_in, cl_mem dev_out, const dt_iop_roi_t *const roi_in, const dt_iop_roi_t *const roi_out, const gboolean smooth)
Definition rcd.c:571
static __DT_CLONE_TARGETS__ void rcd_ppg_border(float *const out, const float *const in, const int width, const int height, const uint32_t filters, const int margin)
Definition rcd.c:98
static INLINE float safe_in(float a, float scale)
Definition rcd.c:91
#define w2
Definition rcd.c:83
#define RCD_TILESIZE
Definition rcd.c:60
#define w1
Definition rcd.c:82
#define w4
Definition rcd.c:85
#define eps
Definition rcd.c:87
#define w3
Definition rcd.c:84
#define epssq
Definition rcd.c:88
static void rcd_demosaic(const dt_dev_pixelpipe_iop_t *piece, float *const restrict out, const float *const restrict in, dt_iop_roi_t *const roi_out, const dt_iop_roi_t *const roi_in, const uint32_t filters)
Definition rcd.c:282
#define RCD_BORDER
Definition rcd.c:80
#define RCD_TILEVALID
Definition rcd.c:81
DT_ALIGNED_PIXEL float dt_aligned_pixel_t[4]
Definition simd.h:53
dt_iop_global_data_t * global_data
Definition imageop.h:337
Region of interest passed through the pixelpipe.
Definition format.h:49
int width
Definition format.h:50
int height
Definition format.h:50
#define __DT_CLONE_TARGETS__
#define MIN(a, b)
Definition thinplate.c:32