Ansel 0.0
A darktable fork - bloat + design vision
Loading...
Searching...
No Matches
laplacian.c
Go to the documentation of this file.
1/*
2 This file is part of darktable,
3 Copyright (C) 2010 Bruce Guenter.
4 Copyright (C) 2010-2011 Henrik Andersson.
5 Copyright (C) 2010-2014, 2016 johannes hanika.
6 Copyright (C) 2010 Stuart Henderson.
7 Copyright (C) 2011 Antony Dovgal.
8 Copyright (C) 2011 Robert Bieber.
9 Copyright (C) 2011-2014, 2016, 2019 Tobias Ellinghaus.
10 Copyright (C) 2011-2012, 2014, 2016-2017 Ulrich Pegelow.
11 Copyright (C) 2012, 2015 Edouard Gomez.
12 Copyright (C) 2012 Jérémy Rosen.
13 Copyright (C) 2012 Richard Wonka.
14 Copyright (C) 2013, 2020 Aldric Renaudin.
15 Copyright (C) 2014, 2016 Dan Torop.
16 Copyright (C) 2014-2016 Roman Lebedev.
17 Copyright (C) 2015-2016 Pedro Côrte-Real.
18 Copyright (C) 2017 Heiko Bauke.
19 Copyright (C) 2017 luzpaz.
20 Copyright (C) 2018, 2020-2026 Aurélien PIERRE.
21 Copyright (C) 2018 Edgardo Hoszowski.
22 Copyright (C) 2018 Maurizio Paglia.
23 Copyright (C) 2018-2020, 2022 Pascal Obry.
24 Copyright (C) 2018 rawfiner.
25 Copyright (C) 2019 Andreas Schneider.
26 Copyright (C) 2019 Diederik ter Rahe.
27 Copyright (C) 2019-2020, 2022 Hanno Schwalm.
28 Copyright (C) 2020 Chris Elston.
29 Copyright (C) 2020, 2022 Diederik Ter Rahe.
30 Copyright (C) 2020-2021 Ralf Brown.
31 Copyright (C) 2021 Hubert Kowalski.
32 Copyright (C) 2022 Martin Bařinka.
33 Copyright (C) 2022 Philipp Lutz.
34 Copyright (C) 2022 Victor Forsiuk.
35 Copyright (C) 2023 Alynx Zhou.
36 Copyright (C) 2023 Guillaume Stutin.
37 Copyright (C) 2023 Luca Zulberti.
38
39 darktable is free software: you can redistribute it and/or modify
40 it under the terms of the GNU General Public License as published by
41 the Free Software Foundation, either version 3 of the License, or
42 (at your option) any later version.
43
44 darktable is distributed in the hope that it will be useful,
45 but WITHOUT ANY WARRANTY; without even the implied warranty of
46 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
47 GNU General Public License for more details.
48
49 You should have received a copy of the GNU General Public License
50 along with darktable. If not, see <http://www.gnu.org/licenses/>.
51 */
52
53// Guided-laplacian (2021 a-trous) highlight reconstruction, CPU + OpenCL. (implementation; see laplacian.h for the
54// public API.)
55
56#include "common/box_filters.h"
57#include "common/bspline.h"
58#include "common/darktable.h"
59#include "common/dwt.h"
61#include "common/opencl.h"
66#include <math.h>
67#include <string.h>
68
69static inline __attribute__((always_inline)) uint8_t scale_type(const int s, const int scales)
70{
71 uint8_t scale = ANY_SCALE;
72 if(s == 0) scale |= FIRST_SCALE;
73 if(s == scales - 1) scale |= LAST_SCALE;
74 return scale;
75}
76
78static inline void guide_laplacians(const float *const restrict high_freq, const float *const restrict low_freq,
79 const float *const restrict clipping_mask, float *const restrict output,
80 const size_t width, const size_t height, const int mult,
81 const float noise_level, const int salt, const uint8_t scale,
82 const float radius_sq)
83{
84 float *const restrict out = DT_IS_ALIGNED(output);
85 const float *const restrict LF = DT_IS_ALIGNED(low_freq);
86 const float *const restrict HF = DT_IS_ALIGNED(high_freq);
87 const dt_aligned_pixel_simd_t zero = dt_simd_set1(0.f);
88 const dt_aligned_pixel_simd_t ones = dt_simd_set1(1.f);
89 const dt_aligned_pixel_simd_t inv_patch = dt_simd_set1(1.f / 9.f);
90 const dt_aligned_pixel_simd_t scale_multiplier = dt_simd_set1(1.f / radius_sq);
91 const float eps = 1e-12f;
93 for(size_t row = 0; row < height; ++row)
94 {
95 // interleave the order in which we process the rows so that we minimize cache misses
96 const int i = dwt_interleave_rows(row, height, mult);
97 const float *const row0 = HF + 4 * ((size_t)MAX(i - mult, 0) * width);
98 const float *const row1 = HF + 4 * ((size_t)i * width);
99 const float *const row2 = HF + 4 * ((size_t)MIN(i + mult, (int)height - 1) * width);
100 const float *const rows[3] = { row0, row1, row2 };
101 const int max_col = (int)width - 1;
102
103 for(int j = 0; j < width; ++j)
104 {
105 const size_t idx = (i * width + j);
106 const size_t index = idx * 4;
107 const float alpha = clipping_mask[index + ALPHA];
108 const float alpha_comp = 1.f - alpha;
109 dt_aligned_pixel_simd_t high_frequency = dt_load_simd_aligned(HF + index);
110
111 if(alpha > 0.f) // reconstruct
112 {
113 const int col_offsets[3] = { 4 * MAX(j - mult, 0), 4 * j, 4 * MIN(j + mult, max_col) };
114 dt_aligned_pixel_simd_t sum = zero;
115 dt_aligned_pixel_simd_t sum_sq = zero;
116 dt_aligned_pixel_simd_t prod_r = zero;
117 dt_aligned_pixel_simd_t prod_g = zero;
118 dt_aligned_pixel_simd_t prod_b = zero;
119
120 // Walk the dense 3x3 neighbourhood as counted loops so GCC keeps the
121 // fit as a regular reduction instead of fully unrolling all 9 taps and
122 // spilling the intermediate moments to the stack.
123#if defined(__GNUC__) && !defined(__clang__)
124#pragma GCC unroll 1
125#endif
126 for(int jj = 0; jj < 3; ++jj)
127 {
128 const float *const row_ptr = rows[jj];
129#if defined(__GNUC__) && !defined(__clang__)
130#pragma GCC unroll 1
131#endif
132 for(int ii = 0; ii < 3; ++ii)
133 {
134 const dt_aligned_pixel_simd_t sample = dt_load_simd_aligned(row_ptr + col_offsets[ii]);
135
136 sum += sample;
137 sum_sq += sample * sample;
138 prod_r += sample * dt_simd_set1(sample[RED]);
139 prod_g += sample * dt_simd_set1(sample[GREEN]);
140 prod_b += sample * dt_simd_set1(sample[BLUE]);
141 }
142 }
143
144 dt_aligned_pixel_simd_t means = sum * inv_patch;
145 dt_aligned_pixel_simd_t variance = sum_sq * inv_patch - means * means;
146 variance = dt_simd_max_zero(variance);
147 variance[ALPHA] = 0.f;
148
149 size_t guiding_channel = RED;
150 float guide_variance = variance[RED];
151 if(variance[GREEN] > guide_variance)
152 {
153 guiding_channel = GREEN;
154 guide_variance = variance[GREEN];
155 }
156 if(variance[BLUE] > guide_variance)
157 {
158 guiding_channel = BLUE;
159 guide_variance = variance[BLUE];
160 }
161
162 if(guide_variance > eps)
163 {
164 const float guide_mean = means[guiding_channel];
165 dt_aligned_pixel_simd_t covariance
166 = (guiding_channel == RED ? prod_r : (guiding_channel == GREEN ? prod_g : prod_b)) * inv_patch
167 - means * dt_simd_set1(guide_mean);
168 dt_aligned_pixel_simd_t slope = covariance / dt_simd_set1(guide_variance);
169 slope = dt_simd_max_zero(slope);
170 dt_aligned_pixel_simd_t intercept = means - slope * dt_simd_set1(guide_mean);
171 const dt_aligned_pixel_simd_t blend = dt_load_simd_aligned(clipping_mask + index) * scale_multiplier;
172 const dt_aligned_pixel_simd_t guide = dt_simd_set1(high_frequency[guiding_channel]);
173 high_frequency = blend * (slope * guide + intercept) + (ones - blend) * high_frequency;
174 }
175 }
176
177 dt_aligned_pixel_simd_t out_pixel = high_frequency;
178 if((scale & FIRST_SCALE))
179 {
180 // out is not inited yet
181 }
182 else
183 {
184 // just accumulate HF
185 out_pixel += dt_load_simd_aligned(out + index);
186 }
187
188 if((scale & LAST_SCALE))
189 {
190 // add the residual and clamp
191 out_pixel = dt_simd_max_zero(out_pixel + dt_load_simd_aligned(LF + index));
192 }
193
194 // Last step of RGB reconstruct : add noise
195 if((scale & LAST_SCALE) && salt && alpha > 0.f)
196 {
197 // Init random number generator
198 uint32_t DT_ALIGNED_ARRAY state[4]
199 = { splitmix32(j + 1), splitmix32((j + 1) * (i + 3)), splitmix32(1337), splitmix32(666) };
204
205 dt_aligned_pixel_t noise = { 0.f };
206 dt_aligned_pixel_t sigma = { 0.20f };
207 const int DT_ALIGNED_ARRAY flip[4] = { TRUE, FALSE, TRUE, FALSE };
208
209 sigma[RED] = out_pixel[RED] * noise_level;
210 sigma[GREEN] = out_pixel[GREEN] * noise_level;
211 sigma[BLUE] = out_pixel[BLUE] * noise_level;
212 sigma[ALPHA] = out_pixel[ALPHA] * noise_level;
213
214 // create statistical noise
215 dt_aligned_pixel_t current = { out_pixel[RED], out_pixel[GREEN], out_pixel[BLUE], out_pixel[ALPHA] };
217
218 // Save the noisy interpolated image
219 for_each_channel(c, aligned(noise, current)) noise[c] = current[c] + fabsf(noise[c] - current[c]);
220
221 out_pixel[RED] = fmaxf(alpha * noise[RED] + alpha_comp * current[RED], 0.f);
222 out_pixel[GREEN] = fmaxf(alpha * noise[GREEN] + alpha_comp * current[GREEN], 0.f);
223 out_pixel[BLUE] = fmaxf(alpha * noise[BLUE] + alpha_comp * current[BLUE], 0.f);
224 out_pixel[ALPHA] = fmaxf(alpha * noise[ALPHA] + alpha_comp * current[ALPHA], 0.f);
225 }
226
227 if((scale & LAST_SCALE))
228 {
229 // Break the RGB channels into ratios/norm for the next step of reconstruction
230 const float norm = fmaxf(sqrtf(sqf(out_pixel[RED]) + sqf(out_pixel[GREEN]) + sqf(out_pixel[BLUE])), 1e-6f);
231 out_pixel /= dt_simd_set1(norm);
232 out_pixel[ALPHA] = norm;
233 }
234
235 dt_store_simd_aligned(out + index, out_pixel);
236 }
237 }
238}
239
241static inline void heat_PDE_diffusion(const float *const restrict high_freq, const float *const restrict low_freq,
242 const float *const restrict clipping_mask, float *const restrict output,
243 const size_t width, const size_t height, const int mult, const uint8_t scale,
244 const float first_order_factor)
245{
246 // Simultaneous inpainting for image structure and texture using anisotropic heat transfer model
247 // https://www.researchgate.net/publication/220663968
248 // modified as follow :
249 // * apply it in a multi-scale wavelet setup : we basically solve it twice, on the wavelets LF and HF layers.
250 // * replace the manual texture direction/distance selection by an automatic detection similar to the structure
251 // one,
252 // * generalize the framework for isotropic diffusion and anisotropic weighted on the isophote direction
253 // * add a variance regularization to better avoid edges.
254 // The sharpness setting mimics the contrast equalizer effect by simply multiplying the HF by some gain.
255
256 float *const restrict out = DT_IS_ALIGNED(output);
257 const float *const restrict LF = DT_IS_ALIGNED(low_freq);
258 const float *const restrict HF = DT_IS_ALIGNED(high_freq);
260 for(size_t row = 0; row < height; ++row)
261 {
262 // interleave the order in which we process the rows so that we minimize cache misses
263 const size_t i = dwt_interleave_rows(row, height, mult);
264 // compute the 'above' and 'below' coordinates, clamping them to the image, once for the entire row
265 const size_t i_neighbours[3] = { MAX((int)(i - mult), (int)0) * width, // x - mult
266 i * width, // x
267 MIN((int)(i + mult), (int)height - 1) * width }; // x + mult
268
269 static const float DT_ALIGNED_ARRAY anisotropic_kernel_isophote[9]
270 = { 0.25f, 0.5f, 0.25f, 0.5f, -3.f, 0.5f, 0.25f, 0.5f, 0.25f };
271
272 for(size_t j = 0; j < width; ++j)
273 {
274 const size_t idx = (i * width + j);
275 const size_t index = idx * 4;
276
277 // fetch the clipping mask opacity : opaque (alpha = 100 %) where clipped
278 const dt_aligned_pixel_t alpha = { clipping_mask[index + RED], clipping_mask[index + GREEN],
279 clipping_mask[index + BLUE], clipping_mask[index + ALPHA] };
280
281 dt_aligned_pixel_t high_frequency = { HF[index + 0], HF[index + 1], HF[index + 2], HF[index + 3] };
282
283 // The for_each_channel macro uses 4 floats SIMD instructions or 3 float regular ops,
284 // depending on system. Since we don't want to diffuse the norm, make sure to store and restore it later.
285 // This is not much of an issue when processing image at full-res, but more harmful since
286 // we reconstruct highlights on a downscaled variant
287 const float norm_backup = high_frequency[3];
288
289 if(alpha[ALPHA] > 0.f) // reconstruct
290 {
291 // non-local neighbours coordinates
292 const size_t j_neighbours[3] = { MAX((int)(j - mult), (int)0), // y - mult
293 j, // y
294 MIN((int)(j + mult), (int)width - 1) }; // y + mult
295
296 // fetch non-local pixels and store them locally and contiguously
297 dt_aligned_pixel_t neighbour_pixel_HF[9];
298 for_four_channels(c, aligned(neighbour_pixel_HF, HF : 16))
299 {
300 neighbour_pixel_HF[3 * 0 + 0][c] = HF[4 * (i_neighbours[0] + j_neighbours[0]) + c];
301 neighbour_pixel_HF[3 * 0 + 1][c] = HF[4 * (i_neighbours[0] + j_neighbours[1]) + c];
302 neighbour_pixel_HF[3 * 0 + 2][c] = HF[4 * (i_neighbours[0] + j_neighbours[2]) + c];
303
304 neighbour_pixel_HF[3 * 1 + 0][c] = HF[4 * (i_neighbours[1] + j_neighbours[0]) + c];
305 neighbour_pixel_HF[3 * 1 + 1][c] = HF[4 * (i_neighbours[1] + j_neighbours[1]) + c];
306 neighbour_pixel_HF[3 * 1 + 2][c] = HF[4 * (i_neighbours[1] + j_neighbours[2]) + c];
307
308 neighbour_pixel_HF[3 * 2 + 0][c] = HF[4 * (i_neighbours[2] + j_neighbours[0]) + c];
309 neighbour_pixel_HF[3 * 2 + 1][c] = HF[4 * (i_neighbours[2] + j_neighbours[1]) + c];
310 neighbour_pixel_HF[3 * 2 + 2][c] = HF[4 * (i_neighbours[2] + j_neighbours[2]) + c];
311 }
312
313 // Compute the laplacian in the direction parallel to the steepest gradient on the norm
314 // Convolve the filter to get the laplacian
315 dt_aligned_pixel_t laplacian_HF = { 0.f, 0.f, 0.f, 0.f };
316 for(int k = 0; k < 9; k++)
317 {
318 for_each_channel(c, aligned(laplacian_HF, neighbour_pixel_HF : 16)
319 aligned(anisotropic_kernel_isophote : 64)) laplacian_HF[c]
320 += neighbour_pixel_HF[k][c] * anisotropic_kernel_isophote[k];
321 }
322
323 // Diffuse
324 const dt_aligned_pixel_t multipliers_HF
326 for_each_channel(c, aligned(high_frequency, multipliers_HF, laplacian_HF, alpha)) high_frequency[c]
327 += alpha[c] * multipliers_HF[c] * (laplacian_HF[c] - first_order_factor * high_frequency[c]);
328
329 // Restore. See above.
330 high_frequency[3] = norm_backup;
331 }
332
333 if((scale & FIRST_SCALE))
334 {
335 // out is not inited yet
336 for_each_channel(c, aligned(out, high_frequency : 64)) out[index + c] = high_frequency[c];
337 }
338 else
339 {
340 // just accumulate HF
341 for_each_channel(c, aligned(out, high_frequency : 64)) out[index + c] += high_frequency[c];
342 }
343
344 if((scale & LAST_SCALE))
345 {
346 // add the residual and clamp
347 for_each_channel(c, aligned(out, LF, high_frequency : 64)) out[index + c]
348 = fmaxf(out[index + c] + LF[index + c], 0.f);
349
350 // renormalize ratios
351 if(alpha[ALPHA] > 0.f)
352 {
353 const float norm = sqrtf(sqf(out[index + RED]) + sqf(out[index + GREEN]) + sqf(out[index + BLUE]));
354 for_each_channel(c, aligned(out, LF, high_frequency : 64)) out[index + c]
355 /= (c != ALPHA && norm > 1e-4f) ? norm : 1.f;
356 }
357
358 // Last scale : reconstruct RGB from ratios and norm - norm stays in the 4th channel
359 // we need it to evaluate the gradient
360 for_four_channels(c, aligned(out)) out[index + c]
361 = (c == ALPHA) ? out[index + ALPHA] : out[index + c] * out[index + ALPHA];
362 }
363 }
364 }
365}
366
367static inline int wavelets_process(const float *const restrict in, float *const restrict reconstructed,
368 const float *const restrict clipping_mask, const size_t width,
369 const size_t height, const int scales, float *const restrict HF,
370 float *const restrict LF_odd, float *const restrict LF_even,
371 const diffuse_reconstruct_variant_t variant, const float noise_level,
372 const int salt, const float first_order_factor)
373{
374 // À trous decimated wavelet decompose
375 // there is a paper from a guy we know that explains it : https://jo.dreggn.org/home/2010_atrous.pdf
376 // the wavelets decomposition here is the same as the equalizer/atrous module,
377
378 // allocate a one-row temporary buffer for the decomposition
379 size_t padded_size;
380 float *const tempbuf = dt_pixelpipe_cache_alloc_perthread_float(4 * width, &padded_size); // TODO: alloc in caller
381 if(IS_NULL_PTR(tempbuf)) return 1;
382
383 for(int s = 0; s < scales; ++s)
384 {
385 // fprintf(stderr, "CPU Wavelet decompose : scale %i\n", s);
386 const int mult = 1 << s;
387
388 const float *restrict buffer_in;
389 float *restrict buffer_out;
390
391 if(s == 0)
392 {
393 buffer_in = in;
394 buffer_out = LF_odd;
395 }
396 else if(s % 2 != 0)
397 {
398 buffer_in = LF_odd;
399 buffer_out = LF_even;
400 }
401 else
402 {
403 buffer_in = LF_even;
404 buffer_out = LF_odd;
405 }
406
407 decompose_2D_Bspline(buffer_in, HF, buffer_out, width, height, mult, tempbuf, padded_size);
408
409 uint8_t current_scale_type = scale_type(s, scales);
410 const float radius = sqf(equivalent_sigma_at_step(B_SPLINE_SIGMA, s * DS_FACTOR));
411
412 if(variant == DIFFUSE_RECONSTRUCT_RGB)
413 guide_laplacians(HF, buffer_out, clipping_mask, reconstructed, width, height, mult, noise_level, salt,
414 current_scale_type, radius);
415 else
416 heat_PDE_diffusion(HF, buffer_out, clipping_mask, reconstructed, width, height, mult, current_scale_type,
417 first_order_factor);
418
419 }
421
422 return 0;
423}
424
427 const dt_dev_pixelpipe_iop_t *piece, const void *const restrict ivoid,
428 void *const restrict ovoid, const dt_iop_roi_t *const roi_in,
429 const dt_iop_roi_t *const roi_out, const dt_aligned_pixel_t clips)
430{
432 int err = 0;
433
434 // Every helper below (normalization, gather, remosaic) reads FC(row, col, filters) with
435 // tile-local row/col (0-based within this buffer, no roi offset added), so filters must be
436 // pre-shifted for roi_in's crop position here -- mirrors demosaic.c's tile-local algorithms.
437 const uint32_t filters = dt_dev_get_roi_filters(piece, roi_in);
438
439 const size_t height = roi_in->height;
440 const size_t width = roi_in->width;
441 const size_t size = roi_in->width * roi_in->height;
442
443 const size_t ds_height = height / DS_FACTOR;
444 const size_t ds_width = width / DS_FACTOR;
445 const size_t ds_size = ds_height * ds_width;
446
447 float *const restrict interpolated
448 = dt_pixelpipe_cache_alloc_align_float(size * 4, pipe); // [R, G, B, norm] for each pixel
449 float *const restrict clipping_mask
450 = dt_pixelpipe_cache_alloc_align_float(size * 4, pipe); // [R, G, B, norm] for each pixel
451
452 // temp buffer for blurs. We will need to cycle between them for memory efficiency
453 float *const restrict LF_odd = dt_pixelpipe_cache_alloc_align_float(ds_size * 4, pipe);
454 float *const restrict LF_even = dt_pixelpipe_cache_alloc_align_float(ds_size * 4, pipe);
455 float *const restrict temp = dt_pixelpipe_cache_alloc_align_float(ds_size * 4, pipe);
456
457 const float scale = DS_FACTOR * dt_dev_get_module_scale(pipe, roi_in);
458 const float final_radius = (float)((int)(1 << data->scales)) / scale;
459 const int scales = CLAMP((int)ceilf(log2f(final_radius)), 1, MAX_NUM_SCALES);
460
461 const float noise_level = data->noise_level / scale;
462
463 // wavelets scales buffers
464 float *restrict HF = dt_pixelpipe_cache_alloc_align_float(ds_size * 4, pipe);
465 float *restrict ds_interpolated = dt_pixelpipe_cache_alloc_align_float(ds_size * 4, pipe);
466 float *restrict ds_clipping_mask = dt_pixelpipe_cache_alloc_align_float(ds_size * 4, pipe);
467
468 if(IS_NULL_PTR(interpolated) || IS_NULL_PTR(clipping_mask) || IS_NULL_PTR(LF_odd) || IS_NULL_PTR(LF_even)
469 || IS_NULL_PTR(temp) || IS_NULL_PTR(HF) || IS_NULL_PTR(ds_interpolated) || IS_NULL_PTR(ds_clipping_mask))
470 {
471 err = 1;
472 goto error;
473 }
474
475 const float *const restrict input = (const float *const restrict)ivoid;
476 float *const restrict output = (float *const restrict)ovoid;
477 dt_aligned_pixel_t normalization = { 1.f, 1.f, 1.f, 1.f };
478 _compute_laplacian_normalization(input, roi_in, filters, NULL, normalization);
479
480 const dt_aligned_pixel_t det_unit = { 1.f, 1.f, 1.f, 1.f };
481 _interpolate_and_mask(input, interpolated, clipping_mask, clips, det_unit, normalization, filters, width, height);
482 if(dt_box_mean(clipping_mask, height, width, 4, 2, 1) != 0)
483 {
484 err = 1;
485 goto error;
486 }
487
488 // Downsample
489 interpolate_bilinear(clipping_mask, width, height, ds_clipping_mask, ds_width, ds_height, 4);
490 interpolate_bilinear(interpolated, width, height, ds_interpolated, ds_width, ds_height, 4);
491
492 for(int i = 0; i < data->iterations; i++)
493 {
494 const int salt = (i == data->iterations - 1); // add noise on the last iteration only
495 if(wavelets_process(ds_interpolated, temp, ds_clipping_mask, ds_width, ds_height, scales, HF, LF_odd, LF_even,
496 DIFFUSE_RECONSTRUCT_RGB, noise_level, salt, data->solid_color))
497 {
498 err = 1;
499 goto error;
500 }
501 if(wavelets_process(temp, ds_interpolated, ds_clipping_mask, ds_width, ds_height, scales, HF, LF_odd, LF_even,
502 DIFFUSE_RECONSTRUCT_CHROMA, noise_level, salt, data->solid_color))
503 {
504 err = 1;
505 goto error;
506 }
507 }
508
509 // Upsample
510 interpolate_bilinear(ds_interpolated, ds_width, ds_height, interpolated, width, height, 4);
511 _remosaic_and_replace(input, input, interpolated, clipping_mask, output, normalization, clips, FALSE, filters,
512 width, height);
513
514error:;
515 dt_pixelpipe_cache_free_align(interpolated);
516 dt_pixelpipe_cache_free_align(clipping_mask);
521 dt_pixelpipe_cache_free_align(ds_interpolated);
522 dt_pixelpipe_cache_free_align(ds_clipping_mask);
523 return err;
524}
525
528 const dt_dev_pixelpipe_iop_t *piece, const void *const restrict ivoid,
529 void *const restrict ovoid, const dt_iop_roi_t *const roi_in,
530 const dt_iop_roi_t *const roi_out, const dt_aligned_pixel_t clips)
531{
533 int err = 0;
534 (void)roi_out;
535
536 const uint8_t(*const xtrans)[6] = (const uint8_t(*const)[6])piece->dsc_in.xtrans;
537
538 const size_t height = roi_in->height;
539 const size_t width = roi_in->width;
540 const size_t size = roi_in->width * roi_in->height;
541
542 const size_t ds_height = height / DS_FACTOR;
543 const size_t ds_width = width / DS_FACTOR;
544 const size_t ds_size = ds_height * ds_width;
545
546 float *const restrict interpolated = dt_pixelpipe_cache_alloc_align_float(size * 4, pipe);
547 float *const restrict clipping_mask = dt_pixelpipe_cache_alloc_align_float(size * 4, pipe);
548 float *const restrict LF_odd = dt_pixelpipe_cache_alloc_align_float(ds_size * 4, pipe);
549 float *const restrict LF_even = dt_pixelpipe_cache_alloc_align_float(ds_size * 4, pipe);
550 float *const restrict temp = dt_pixelpipe_cache_alloc_align_float(ds_size * 4, pipe);
551
552 const float scale = DS_FACTOR * dt_dev_get_module_scale(pipe, roi_in);
553 const float final_radius = (float)((int)(1 << data->scales)) / scale;
554 const int scales = CLAMP((int)ceilf(log2f(final_radius)), 1, MAX_NUM_SCALES);
555 const float noise_level = data->noise_level / scale;
556
557 float *restrict HF = dt_pixelpipe_cache_alloc_align_float(ds_size * 4, pipe);
558 float *restrict ds_interpolated = dt_pixelpipe_cache_alloc_align_float(ds_size * 4, pipe);
559 float *restrict ds_clipping_mask = dt_pixelpipe_cache_alloc_align_float(ds_size * 4, pipe);
560
561 if(IS_NULL_PTR(interpolated) || IS_NULL_PTR(clipping_mask) || IS_NULL_PTR(LF_odd) || IS_NULL_PTR(LF_even)
562 || IS_NULL_PTR(temp) || IS_NULL_PTR(HF) || IS_NULL_PTR(ds_interpolated) || IS_NULL_PTR(ds_clipping_mask))
563 {
564 err = 1;
565 goto error;
566 }
567
568 const float *const restrict input = (const float *const restrict)ivoid;
569 float *const restrict output = (float *const restrict)ovoid;
570 dt_aligned_pixel_t normalization = { 1.f, 1.f, 1.f, 1.f };
571 int32_t lookup[6][6][32] = { { { 0 } } };
572
573 _compute_laplacian_normalization(input, roi_in, 9u, xtrans, normalization);
574 _build_xtrans_bilinear_lookup(lookup, roi_in, xtrans);
575 _interpolate_and_mask_xtrans(input, interpolated, clipping_mask, clips, normalization, roi_in, lookup, xtrans,
576 width, height);
577 if(dt_box_mean(clipping_mask, height, width, 4, 2, 1) != 0)
578 {
579 err = 1;
580 goto error;
581 }
582
583 interpolate_bilinear(clipping_mask, width, height, ds_clipping_mask, ds_width, ds_height, 4);
584 interpolate_bilinear(interpolated, width, height, ds_interpolated, ds_width, ds_height, 4);
585
586 for(int i = 0; i < data->iterations; i++)
587 {
588 const int salt = (i == data->iterations - 1);
589 if(wavelets_process(ds_interpolated, temp, ds_clipping_mask, ds_width, ds_height, scales, HF, LF_odd, LF_even,
590 DIFFUSE_RECONSTRUCT_RGB, noise_level, salt, data->solid_color))
591 {
592 err = 1;
593 goto error;
594 }
595 if(wavelets_process(temp, ds_interpolated, ds_clipping_mask, ds_width, ds_height, scales, HF, LF_odd, LF_even,
596 DIFFUSE_RECONSTRUCT_CHROMA, noise_level, salt, data->solid_color))
597 {
598 err = 1;
599 goto error;
600 }
601 }
602
603 interpolate_bilinear(ds_interpolated, ds_width, ds_height, interpolated, width, height, 4);
604 _remosaic_and_replace_xtrans(input, input, interpolated, clipping_mask, output, normalization, clips, FALSE,
605 roi_in, xtrans, width, height);
606
607error:
608 dt_pixelpipe_cache_free_align(interpolated);
609 dt_pixelpipe_cache_free_align(clipping_mask);
614 dt_pixelpipe_cache_free_align(ds_interpolated);
615 dt_pixelpipe_cache_free_align(ds_clipping_mask);
616 return err;
617}
618
619#ifdef HAVE_OPENCL
620static inline cl_int wavelets_process_cl(const int devid, cl_mem in, cl_mem reconstructed,
621 cl_mem reconstructed_scratch, cl_mem clipping_mask, const size_t sizes[3],
622 const int width, const int height,
623 dt_iop_highlights_global_data_t *const gd, const int scales, cl_mem HF,
624 cl_mem LF_odd, cl_mem LF_even,
625 const diffuse_reconstruct_variant_t variant, const float noise_level,
626 const int salt, const float solid_color)
627{
628 cl_int err = DT_OPENCL_DEFAULT_ERROR;
629 cl_mem reconstruct_read = reconstructed_scratch;
630
631 // À trous wavelet decompose
632 // there is a paper from a guy we know that explains it : https://jo.dreggn.org/home/2010_atrous.pdf
633 // the wavelets decomposition here is the same as the equalizer/atrous module,
634 for(int s = 0; s < scales; ++s)
635 {
636 // fprintf(stderr, "GPU Wavelet decompose : scale %i\n", s);
637 const int mult = 1 << s;
638
639 cl_mem buffer_in;
640 cl_mem buffer_out;
641
642 if(s == 0)
643 {
644 buffer_in = in;
645 buffer_out = LF_odd;
646 }
647 else if(s % 2 != 0)
648 {
649 buffer_in = LF_odd;
650 buffer_out = LF_even;
651 }
652 else
653 {
654 buffer_in = LF_even;
655 buffer_out = LF_odd;
656 }
657
658 // Compute wavelets low-frequency scales
659 const int clamp_lf = 1;
660 int hblocksize;
662 .xfactor = 1,
663 .yoffset = 0,
664 .yfactor = 1,
665 .cellsize = 4 * sizeof(float),
666 .overhead = 0,
667 .sizex = 1 << 16,
668 .sizey = 1 };
670 hblocksize = hlocopt.sizex;
671 else
672 hblocksize = 1;
673
674 if(hblocksize > 1)
675 {
676 const size_t horizontal_sizes[3] = { ROUNDUP(width, hblocksize), ROUNDUPDHT(height, devid), 1 };
677 const size_t horizontal_local[3] = { hblocksize, 1, 1 };
679 (void *)&buffer_in);
680 dt_opencl_set_kernel_arg(devid, gd->kernel_filmic_bspline_horizontal_local, 1, sizeof(cl_mem), (void *)&HF);
681 dt_opencl_set_kernel_arg(devid, gd->kernel_filmic_bspline_horizontal_local, 2, sizeof(int), (void *)&width);
682 dt_opencl_set_kernel_arg(devid, gd->kernel_filmic_bspline_horizontal_local, 3, sizeof(int), (void *)&height);
683 dt_opencl_set_kernel_arg(devid, gd->kernel_filmic_bspline_horizontal_local, 4, sizeof(int), (void *)&mult);
685 (void *)&clamp_lf);
687 (hblocksize + 4 * mult) * 4 * sizeof(float), NULL);
689 horizontal_sizes, horizontal_local);
690 }
691 else
692 {
693 dt_opencl_set_kernel_arg(devid, gd->kernel_filmic_bspline_horizontal, 0, sizeof(cl_mem), (void *)&buffer_in);
694 dt_opencl_set_kernel_arg(devid, gd->kernel_filmic_bspline_horizontal, 1, sizeof(cl_mem), (void *)&HF);
695 dt_opencl_set_kernel_arg(devid, gd->kernel_filmic_bspline_horizontal, 2, sizeof(int), (void *)&width);
696 dt_opencl_set_kernel_arg(devid, gd->kernel_filmic_bspline_horizontal, 3, sizeof(int), (void *)&height);
697 dt_opencl_set_kernel_arg(devid, gd->kernel_filmic_bspline_horizontal, 4, sizeof(int), (void *)&mult);
698 dt_opencl_set_kernel_arg(devid, gd->kernel_filmic_bspline_horizontal, 5, sizeof(int), (void *)&clamp_lf);
700 }
701 if(err != CL_SUCCESS) return err;
702
703 int vblocksize;
705 .xfactor = 1,
706 .yoffset = 2 * mult,
707 .yfactor = 1,
708 .cellsize = 4 * sizeof(float),
709 .overhead = 0,
710 .sizex = 1,
711 .sizey = 1 << 16 };
713 vblocksize = vlocopt.sizey;
714 else
715 vblocksize = 1;
716
717 if(vblocksize > 1)
718 {
719 const size_t vertical_sizes[3] = { ROUNDUPDWD(width, devid), ROUNDUP(height, vblocksize), 1 };
720 const size_t vertical_local[3] = { 1, vblocksize, 1 };
721 dt_opencl_set_kernel_arg(devid, gd->kernel_filmic_bspline_vertical_local, 0, sizeof(cl_mem), (void *)&HF);
723 (void *)&buffer_out);
724 dt_opencl_set_kernel_arg(devid, gd->kernel_filmic_bspline_vertical_local, 2, sizeof(int), (void *)&width);
725 dt_opencl_set_kernel_arg(devid, gd->kernel_filmic_bspline_vertical_local, 3, sizeof(int), (void *)&height);
726 dt_opencl_set_kernel_arg(devid, gd->kernel_filmic_bspline_vertical_local, 4, sizeof(int), (void *)&mult);
727 dt_opencl_set_kernel_arg(devid, gd->kernel_filmic_bspline_vertical_local, 5, sizeof(int), (void *)&clamp_lf);
729 (vblocksize + 4 * mult) * 4 * sizeof(float), NULL);
731 vertical_local);
732 }
733 else
734 {
735 dt_opencl_set_kernel_arg(devid, gd->kernel_filmic_bspline_vertical, 0, sizeof(cl_mem), (void *)&HF);
736 dt_opencl_set_kernel_arg(devid, gd->kernel_filmic_bspline_vertical, 1, sizeof(cl_mem), (void *)&buffer_out);
737 dt_opencl_set_kernel_arg(devid, gd->kernel_filmic_bspline_vertical, 2, sizeof(int), (void *)&width);
738 dt_opencl_set_kernel_arg(devid, gd->kernel_filmic_bspline_vertical, 3, sizeof(int), (void *)&height);
739 dt_opencl_set_kernel_arg(devid, gd->kernel_filmic_bspline_vertical, 4, sizeof(int), (void *)&mult);
740 dt_opencl_set_kernel_arg(devid, gd->kernel_filmic_bspline_vertical, 5, sizeof(int), (void *)&clamp_lf);
742 }
743 if(err != CL_SUCCESS) return err;
744
745 uint8_t current_scale_type = scale_type(s, scales);
746 const float radius = sqf(equivalent_sigma_at_step(B_SPLINE_SIGMA, s * DS_FACTOR));
747 cl_mem reconstruct_write = (s == scales - 1)
748 ? reconstructed
749 : (reconstruct_read == reconstructed ? reconstructed_scratch : reconstructed);
750
751 // Keep the accumulation image read/write handles distinct at each scale.
752 // Some AMD OpenCL drivers get unstable when the same image is bound for both roles.
753 if(variant == DIFFUSE_RECONSTRUCT_RGB)
754 {
756 (void *)&buffer_in);
758 (void *)&buffer_out);
760 (void *)&clipping_mask);
762 (void *)&reconstruct_read);
764 (void *)&reconstruct_write);
765 dt_opencl_set_kernel_arg(devid, gd->kernel_highlights_guide_laplacians, 5, sizeof(int), (void *)&width);
766 dt_opencl_set_kernel_arg(devid, gd->kernel_highlights_guide_laplacians, 6, sizeof(int), (void *)&height);
767 dt_opencl_set_kernel_arg(devid, gd->kernel_highlights_guide_laplacians, 7, sizeof(int), (void *)&mult);
769 (void *)&noise_level);
770 dt_opencl_set_kernel_arg(devid, gd->kernel_highlights_guide_laplacians, 9, sizeof(int), (void *)&salt);
771 dt_opencl_set_kernel_arg(devid, gd->kernel_highlights_guide_laplacians, 10, sizeof(uint8_t),
772 (void *)&current_scale_type);
773 dt_opencl_set_kernel_arg(devid, gd->kernel_highlights_guide_laplacians, 11, sizeof(float), (void *)&radius);
775 if(err != CL_SUCCESS) return err;
776 }
777 else // DIFFUSE_RECONSTRUCT_CHROMA
778 {
779 dt_opencl_set_kernel_arg(devid, gd->kernel_highlights_diffuse_color, 0, sizeof(cl_mem), (void *)&buffer_in);
780 dt_opencl_set_kernel_arg(devid, gd->kernel_highlights_diffuse_color, 1, sizeof(cl_mem), (void *)&buffer_out);
781 dt_opencl_set_kernel_arg(devid, gd->kernel_highlights_diffuse_color, 2, sizeof(cl_mem),
782 (void *)&clipping_mask);
783 dt_opencl_set_kernel_arg(devid, gd->kernel_highlights_diffuse_color, 3, sizeof(cl_mem),
784 (void *)&reconstruct_read);
785 dt_opencl_set_kernel_arg(devid, gd->kernel_highlights_diffuse_color, 4, sizeof(cl_mem),
786 (void *)&reconstruct_write);
787 dt_opencl_set_kernel_arg(devid, gd->kernel_highlights_diffuse_color, 5, sizeof(int), (void *)&width);
788 dt_opencl_set_kernel_arg(devid, gd->kernel_highlights_diffuse_color, 6, sizeof(int), (void *)&height);
789 dt_opencl_set_kernel_arg(devid, gd->kernel_highlights_diffuse_color, 7, sizeof(int), (void *)&mult);
790 dt_opencl_set_kernel_arg(devid, gd->kernel_highlights_diffuse_color, 8, sizeof(uint8_t),
791 (void *)&current_scale_type);
792 dt_opencl_set_kernel_arg(devid, gd->kernel_highlights_diffuse_color, 9, sizeof(float), (void *)&solid_color);
794 if(err != CL_SUCCESS) return err;
795 }
796
797 reconstruct_read = reconstruct_write;
798 }
799
800 return err;
801}
802#endif // HAVE_OPENCL
803
804#ifdef HAVE_OPENCL
805
807 const dt_dev_pixelpipe_iop_t *piece, cl_mem dev_in, cl_mem dev_out,
808 const dt_iop_roi_t *const roi_in, const dt_iop_roi_t *const roi_out,
809 const dt_aligned_pixel_t clips)
810{
813
814 cl_int err = DT_OPENCL_DEFAULT_ERROR;
815
816 const int devid = pipe->devid;
817 const int width = roi_in->width;
818 const int height = roi_in->height;
819
820 const int ds_height = height / DS_FACTOR;
821 const int ds_width = width / DS_FACTOR;
822
823 size_t sizes[] = { ROUNDUPDWD(width, devid), ROUNDUPDHT(height, devid), 1 };
824 size_t ds_sizes[] = { ROUNDUPDWD(ds_width, devid), ROUNDUPDHT(ds_height, devid), 1 };
825
826 // kernel_highlights_normalize_reduce_first is self-correcting: it takes the raw filters
827 // below PLUS roi_in->x/y as separate kernel args and adds them itself. interpolate_and_mask
828 // and remosaic_and_replace have no roi offset args at all -- they need filters pre-shifted
829 // for roi_in's crop position instead (mirrors the CPU process_laplacian_bayer fix).
830 const uint32_t filters = piece->dsc_in.filters;
831 const uint32_t filters_shifted = dt_dev_get_roi_filters(piece, roi_in);
832
833 cl_mem interpolated
834 = dt_opencl_alloc_device(devid, sizes[0], sizes[1], sizeof(float) * 4); // [R, G, B, norm] for each pixel
835 cl_mem clipping_mask
836 = dt_opencl_alloc_device(devid, sizes[0], sizes[1], sizeof(float) * 4); // [R, G, B, norm] for each pixel
837 cl_mem normalization = NULL;
838 cl_mem normalization_tmp = NULL;
839 cl_mem normalization_partials = NULL;
840 cl_mem normalization_final = NULL;
841
842 // temp buffer for blurs. We will need to cycle between them for memory efficiency
843 cl_mem LF_odd = dt_opencl_alloc_device(devid, ds_sizes[0], ds_sizes[1], sizeof(float) * 4);
844 cl_mem LF_even = dt_opencl_alloc_device(devid, ds_sizes[0], ds_sizes[1], sizeof(float) * 4);
845 cl_mem temp
846 = dt_opencl_alloc_device(devid, sizes[0], sizes[1], sizeof(float) * 4); // need full size here for blurring
847
848 const float scale = DS_FACTOR * dt_dev_get_module_scale(pipe, roi_in);
849 const float final_radius = (float)((int)(1 << data->scales)) / scale;
850 const int scales = CLAMP((int)ceilf(log2f(final_radius)), 1, MAX_NUM_SCALES);
851
852 const float noise_level = data->noise_level / scale;
853
854 // wavelets scales buffers
855 cl_mem HF = dt_opencl_alloc_device(devid, ds_sizes[0], ds_sizes[1], sizeof(float) * 4);
856 cl_mem ds_interpolated = dt_opencl_alloc_device(devid, ds_sizes[0], ds_sizes[1], sizeof(float) * 4);
857 cl_mem ds_clipping_mask = dt_opencl_alloc_device(devid, ds_sizes[0], ds_sizes[1], sizeof(float) * 4);
858 cl_mem reconstructed_scratch = dt_opencl_alloc_device(devid, ds_sizes[0], ds_sizes[1], sizeof(float) * 4);
859 cl_mem clips_cl = dt_opencl_copy_host_to_device_constant(devid, 4 * sizeof(float), (float *)clips);
860
861 if(IS_NULL_PTR(interpolated) || IS_NULL_PTR(clipping_mask) || IS_NULL_PTR(LF_odd) || IS_NULL_PTR(LF_even)
862 || IS_NULL_PTR(temp) || IS_NULL_PTR(HF) || IS_NULL_PTR(ds_interpolated) || IS_NULL_PTR(ds_clipping_mask)
863 || IS_NULL_PTR(reconstructed_scratch) || IS_NULL_PTR(clips_cl))
864 goto error;
865
866 {
868 .xfactor = 1,
869 .yoffset = 0,
870 .yfactor = 1,
871 .cellsize = 4 * sizeof(float),
872 .overhead = 0,
873 .sizex = 1 << 4,
874 .sizey = 1 << 4 };
875
877
878 const size_t bwidth = ROUNDUP(width, flocopt.sizex);
879 const size_t bheight = ROUNDUP(height, flocopt.sizey);
880 const int bufsize = (int)((bwidth / flocopt.sizex) * (bheight / flocopt.sizey));
881
882 normalization_partials = dt_opencl_alloc_device_buffer(devid, sizeof(float) * 4 * (size_t)bufsize);
883 normalization = dt_opencl_alloc_device_buffer(devid, sizeof(float) * 4 * REDUCESIZE);
884 normalization_tmp = dt_opencl_alloc_device_buffer(devid, sizeof(float) * 4 * REDUCESIZE);
885 if(!normalization_partials || !normalization || !normalization_tmp) goto error;
886
887 size_t fsizes[3] = { bwidth, bheight, 1 };
888 size_t flocal[3] = { flocopt.sizex, flocopt.sizey, 1 };
889 dt_opencl_set_kernel_arg(devid, gd->kernel_highlights_normalize_reduce_first, 0, sizeof(cl_mem), &dev_in);
893 &normalization_partials);
894 dt_opencl_set_kernel_arg(devid, gd->kernel_highlights_normalize_reduce_first, 4, sizeof(uint32_t), &filters);
895 dt_opencl_set_kernel_arg(devid, gd->kernel_highlights_normalize_reduce_first, 5, sizeof(int), &roi_in->x);
896 dt_opencl_set_kernel_arg(devid, gd->kernel_highlights_normalize_reduce_first, 6, sizeof(int), &roi_in->y);
898 sizeof(float) * 4 * flocopt.sizex * flocopt.sizey, NULL);
900 flocal);
901 if(err != CL_SUCCESS) goto error;
902
904 .xfactor = 1,
905 .yoffset = 0,
906 .yfactor = 1,
907 .cellsize = 4 * sizeof(float),
908 .overhead = 0,
909 .sizex = 1 << 16,
910 .sizey = 1 };
911
913
914 int current_length = bufsize;
915 cl_mem reduce_in = normalization_partials;
916 cl_mem reduce_out = normalization;
917
918 while(TRUE)
919 {
920 const int reducesize = MIN(REDUCESIZE, ROUNDUP(current_length, slocopt.sizex) / slocopt.sizex);
921 size_t ssizes[3] = { (size_t)reducesize * slocopt.sizex, 1, 1 };
922 size_t slocal[3] = { slocopt.sizex, 1, 1 };
923 dt_opencl_set_kernel_arg(devid, gd->kernel_highlights_normalize_reduce_second, 0, sizeof(cl_mem), &reduce_in);
925 &reduce_out);
927 &current_length);
929 sizeof(float) * 4 * slocopt.sizex, NULL);
931 slocal);
932 if(err != CL_SUCCESS) goto error;
933
934 if(reducesize == 1) break;
935 current_length = reducesize;
936 cl_mem swap = reduce_in;
937 reduce_in = reduce_out;
938 reduce_out = (swap == normalization_partials) ? normalization_tmp : normalization;
939 }
940
941 normalization_final = reduce_out;
942 }
943
944 dt_opencl_set_kernel_arg(devid, gd->kernel_highlights_bilinear_and_mask, 0, sizeof(cl_mem), (void *)&dev_in);
946 (void *)&interpolated);
947 dt_opencl_set_kernel_arg(devid, gd->kernel_highlights_bilinear_and_mask, 2, sizeof(cl_mem), (void *)&temp);
948 dt_opencl_set_kernel_arg(devid, gd->kernel_highlights_bilinear_and_mask, 3, sizeof(cl_mem), (void *)&clips_cl);
950 (void *)&normalization_final);
952 (void *)&filters_shifted);
953 dt_opencl_set_kernel_arg(devid, gd->kernel_highlights_bilinear_and_mask, 6, sizeof(int), (void *)&roi_out->width);
955 (void *)&roi_out->height);
957 if(err != CL_SUCCESS) goto error;
958
959 dt_opencl_set_kernel_arg(devid, gd->kernel_highlights_box_blur, 0, sizeof(cl_mem), (void *)&temp);
960 dt_opencl_set_kernel_arg(devid, gd->kernel_highlights_box_blur, 1, sizeof(cl_mem), (void *)&clipping_mask);
961 dt_opencl_set_kernel_arg(devid, gd->kernel_highlights_box_blur, 2, sizeof(int), (void *)&roi_out->width);
962 dt_opencl_set_kernel_arg(devid, gd->kernel_highlights_box_blur, 3, sizeof(int), (void *)&roi_out->height);
964 if(err != CL_SUCCESS) goto error;
965
966 // Downsample
967 const int RGBa = TRUE;
968 dt_opencl_set_kernel_arg(devid, gd->kernel_interpolate_bilinear, 0, sizeof(cl_mem), (void *)&clipping_mask);
969 dt_opencl_set_kernel_arg(devid, gd->kernel_interpolate_bilinear, 1, sizeof(int), (void *)&width);
970 dt_opencl_set_kernel_arg(devid, gd->kernel_interpolate_bilinear, 2, sizeof(int), (void *)&height);
971 dt_opencl_set_kernel_arg(devid, gd->kernel_interpolate_bilinear, 3, sizeof(cl_mem), (void *)&ds_clipping_mask);
972 dt_opencl_set_kernel_arg(devid, gd->kernel_interpolate_bilinear, 4, sizeof(int), (void *)&ds_width);
973 dt_opencl_set_kernel_arg(devid, gd->kernel_interpolate_bilinear, 5, sizeof(int), (void *)&ds_height);
974 dt_opencl_set_kernel_arg(devid, gd->kernel_interpolate_bilinear, 6, sizeof(int), (void *)&RGBa);
976 if(err != CL_SUCCESS) goto error;
977
978 dt_opencl_set_kernel_arg(devid, gd->kernel_interpolate_bilinear, 0, sizeof(cl_mem), (void *)&interpolated);
979 dt_opencl_set_kernel_arg(devid, gd->kernel_interpolate_bilinear, 1, sizeof(int), (void *)&width);
980 dt_opencl_set_kernel_arg(devid, gd->kernel_interpolate_bilinear, 2, sizeof(int), (void *)&height);
981 dt_opencl_set_kernel_arg(devid, gd->kernel_interpolate_bilinear, 3, sizeof(cl_mem), (void *)&ds_interpolated);
982 dt_opencl_set_kernel_arg(devid, gd->kernel_interpolate_bilinear, 4, sizeof(int), (void *)&ds_width);
983 dt_opencl_set_kernel_arg(devid, gd->kernel_interpolate_bilinear, 5, sizeof(int), (void *)&ds_height);
984 dt_opencl_set_kernel_arg(devid, gd->kernel_interpolate_bilinear, 6, sizeof(int), (void *)&RGBa);
986 if(err != CL_SUCCESS) goto error;
987
988 for(int i = 0; i < data->iterations; i++)
989 {
990 const int salt = (i == data->iterations - 1); // add noise on the last iteration only
991 err = wavelets_process_cl(devid, ds_interpolated, temp, reconstructed_scratch, ds_clipping_mask, ds_sizes,
992 ds_width, ds_height, gd, scales, HF, LF_odd, LF_even, DIFFUSE_RECONSTRUCT_RGB,
993 noise_level, salt, data->solid_color);
994 if(err != CL_SUCCESS) goto error;
995
996 err = wavelets_process_cl(devid, temp, ds_interpolated, reconstructed_scratch, ds_clipping_mask, ds_sizes,
997 ds_width, ds_height, gd, scales, HF, LF_odd, LF_even, DIFFUSE_RECONSTRUCT_CHROMA,
998 noise_level, salt, data->solid_color);
999 if(err != CL_SUCCESS) goto error;
1000 }
1001
1002 // Upsample
1003 dt_opencl_set_kernel_arg(devid, gd->kernel_interpolate_bilinear, 0, sizeof(cl_mem), (void *)&ds_interpolated);
1004 dt_opencl_set_kernel_arg(devid, gd->kernel_interpolate_bilinear, 1, sizeof(int), (void *)&ds_width);
1005 dt_opencl_set_kernel_arg(devid, gd->kernel_interpolate_bilinear, 2, sizeof(int), (void *)&ds_height);
1006 dt_opencl_set_kernel_arg(devid, gd->kernel_interpolate_bilinear, 3, sizeof(cl_mem), (void *)&interpolated);
1007 dt_opencl_set_kernel_arg(devid, gd->kernel_interpolate_bilinear, 4, sizeof(int), (void *)&width);
1008 dt_opencl_set_kernel_arg(devid, gd->kernel_interpolate_bilinear, 5, sizeof(int), (void *)&height);
1010 if(err != CL_SUCCESS) goto error;
1011
1012 // Remosaic
1013 const int clip_floor_off = FALSE; // 2021 mode keeps the historical blend
1014 dt_opencl_set_kernel_arg(devid, gd->kernel_highlights_remosaic_and_replace, 0, sizeof(cl_mem), (void *)&dev_in);
1015 dt_opencl_set_kernel_arg(devid, gd->kernel_highlights_remosaic_and_replace, 1, sizeof(cl_mem), (void *)&dev_in);
1017 (void *)&interpolated);
1019 (void *)&clipping_mask);
1020 dt_opencl_set_kernel_arg(devid, gd->kernel_highlights_remosaic_and_replace, 4, sizeof(cl_mem), (void *)&dev_out);
1022 (void *)&normalization_final);
1023 dt_opencl_set_kernel_arg(devid, gd->kernel_highlights_remosaic_and_replace, 6, sizeof(cl_mem), (void *)&clips_cl);
1025 (void *)&clip_floor_off);
1027 (void *)&filters_shifted);
1028 dt_opencl_set_kernel_arg(devid, gd->kernel_highlights_remosaic_and_replace, 9, sizeof(int), (void *)&width);
1029 dt_opencl_set_kernel_arg(devid, gd->kernel_highlights_remosaic_and_replace, 10, sizeof(int), (void *)&height);
1031 if(err != CL_SUCCESS) goto error;
1032
1033 // cleanup and exit on success
1035 dt_opencl_release_mem_object(normalization_partials);
1036 if(normalization_tmp != normalization_final) dt_opencl_release_mem_object(normalization_tmp);
1037 if(normalization != normalization_final) dt_opencl_release_mem_object(normalization);
1038 dt_opencl_release_mem_object(normalization_final);
1039 dt_opencl_release_mem_object(interpolated);
1040 dt_opencl_release_mem_object(clipping_mask);
1045 dt_opencl_release_mem_object(ds_clipping_mask);
1046 dt_opencl_release_mem_object(ds_interpolated);
1047 dt_opencl_release_mem_object(reconstructed_scratch);
1048 return err;
1049
1050error:
1052 dt_opencl_release_mem_object(normalization_partials);
1053 if(normalization_tmp != normalization_final) dt_opencl_release_mem_object(normalization_tmp);
1054 if(normalization != normalization_final) dt_opencl_release_mem_object(normalization);
1055 dt_opencl_release_mem_object(normalization_final);
1056 dt_opencl_release_mem_object(interpolated);
1057 dt_opencl_release_mem_object(clipping_mask);
1062 dt_opencl_release_mem_object(ds_clipping_mask);
1063 dt_opencl_release_mem_object(ds_interpolated);
1064 dt_opencl_release_mem_object(reconstructed_scratch);
1065
1066 dt_print(DT_DEBUG_OPENCL, "[opencl_highlights] couldn't enqueue kernel! %i\n", err);
1067 return err;
1068}
1069#endif // HAVE_OPENCL
1070
1071#ifdef HAVE_OPENCL
1072
1074 const dt_dev_pixelpipe_iop_t *piece, cl_mem dev_in, cl_mem dev_out,
1075 const dt_iop_roi_t *const roi_in, const dt_iop_roi_t *const roi_out,
1076 const dt_aligned_pixel_t clips)
1077{
1080
1081 cl_int err = DT_OPENCL_DEFAULT_ERROR;
1082
1083 const uint8_t(*const xtrans)[6] = (const uint8_t(*const)[6])piece->dsc_in.xtrans;
1084 const int devid = pipe->devid;
1085 const int width = roi_in->width;
1086 const int height = roi_in->height;
1087
1088 const int ds_height = height / DS_FACTOR;
1089 const int ds_width = width / DS_FACTOR;
1090
1091 size_t sizes[] = { ROUNDUPDWD(width, devid), ROUNDUPDHT(height, devid), 1 };
1092 size_t ds_sizes[] = { ROUNDUPDWD(ds_width, devid), ROUNDUPDHT(ds_height, devid), 1 };
1093
1094 cl_mem interpolated = dt_opencl_alloc_device(devid, sizes[0], sizes[1], sizeof(float) * 4);
1095 cl_mem clipping_mask = dt_opencl_alloc_device(devid, sizes[0], sizes[1], sizeof(float) * 4);
1096 cl_mem normalization = NULL;
1097 cl_mem normalization_tmp = NULL;
1098 cl_mem normalization_partials = NULL;
1099 cl_mem normalization_final = NULL;
1100 cl_mem LF_odd = dt_opencl_alloc_device(devid, ds_sizes[0], ds_sizes[1], sizeof(float) * 4);
1101 cl_mem LF_even = dt_opencl_alloc_device(devid, ds_sizes[0], ds_sizes[1], sizeof(float) * 4);
1102 cl_mem temp = dt_opencl_alloc_device(devid, sizes[0], sizes[1], sizeof(float) * 4);
1103
1104 const float scale = DS_FACTOR * dt_dev_get_module_scale(pipe, roi_in);
1105 const float final_radius = (float)((int)(1 << data->scales)) / scale;
1106 const int scales = CLAMP((int)ceilf(log2f(final_radius)), 1, MAX_NUM_SCALES);
1107 const float noise_level = data->noise_level / scale;
1108
1109 cl_mem HF = dt_opencl_alloc_device(devid, ds_sizes[0], ds_sizes[1], sizeof(float) * 4);
1110 cl_mem ds_interpolated = dt_opencl_alloc_device(devid, ds_sizes[0], ds_sizes[1], sizeof(float) * 4);
1111 cl_mem ds_clipping_mask = dt_opencl_alloc_device(devid, ds_sizes[0], ds_sizes[1], sizeof(float) * 4);
1112 cl_mem reconstructed_scratch = dt_opencl_alloc_device(devid, ds_sizes[0], ds_sizes[1], sizeof(float) * 4);
1113
1114 cl_mem clips_cl = dt_opencl_copy_host_to_device_constant(devid, 4 * sizeof(float), (float *)clips);
1115 cl_mem dev_xtrans
1116 = dt_opencl_copy_host_to_device_constant(devid, sizeof(piece->dsc_in.xtrans), (void *)piece->dsc_in.xtrans);
1117 int32_t lookup[6][6][32] = { { { 0 } } };
1118 _build_xtrans_bilinear_lookup(lookup, roi_in, xtrans);
1119 cl_mem lookup_cl = dt_opencl_copy_host_to_device_constant(devid, sizeof(lookup), lookup);
1120
1121 if(IS_NULL_PTR(interpolated) || IS_NULL_PTR(clipping_mask) || IS_NULL_PTR(LF_odd) || IS_NULL_PTR(LF_even)
1122 || IS_NULL_PTR(temp) || IS_NULL_PTR(HF) || IS_NULL_PTR(ds_interpolated) || IS_NULL_PTR(ds_clipping_mask)
1123 || IS_NULL_PTR(reconstructed_scratch) || IS_NULL_PTR(clips_cl) || IS_NULL_PTR(dev_xtrans)
1124 || IS_NULL_PTR(lookup_cl))
1125 goto error;
1126
1127 {
1129 .xfactor = 1,
1130 .yoffset = 0,
1131 .yfactor = 1,
1132 .cellsize = 4 * sizeof(float),
1133 .overhead = 0,
1134 .sizex = 1 << 4,
1135 .sizey = 1 << 4 };
1136
1138 goto error;
1139
1140 const size_t bwidth = ROUNDUP(width, flocopt.sizex);
1141 const size_t bheight = ROUNDUP(height, flocopt.sizey);
1142 const int bufsize = (int)((bwidth / flocopt.sizex) * (bheight / flocopt.sizey));
1143
1144 normalization_partials = dt_opencl_alloc_device_buffer(devid, sizeof(float) * 4 * (size_t)bufsize);
1145 normalization = dt_opencl_alloc_device_buffer(devid, sizeof(float) * 4 * REDUCESIZE);
1146 normalization_tmp = dt_opencl_alloc_device_buffer(devid, sizeof(float) * 4 * REDUCESIZE);
1147 if(!normalization_partials || !normalization || !normalization_tmp) goto error;
1148
1149 size_t fsizes[3] = { bwidth, bheight, 1 };
1150 size_t flocal[3] = { flocopt.sizex, flocopt.sizey, 1 };
1152 &dev_in);
1156 &normalization_partials);
1158 &roi_in->x);
1160 &roi_in->y);
1162 &dev_xtrans);
1164 sizeof(float) * 4 * flocopt.sizex * flocopt.sizey, NULL);
1166 fsizes, flocal);
1167 if(err != CL_SUCCESS) goto error;
1168
1170 .xfactor = 1,
1171 .yoffset = 0,
1172 .yfactor = 1,
1173 .cellsize = 4 * sizeof(float),
1174 .overhead = 0,
1175 .sizex = 1 << 16,
1176 .sizey = 1 };
1177
1179
1180 int current_length = bufsize;
1181 cl_mem reduce_in = normalization_partials;
1182 cl_mem reduce_out = normalization;
1183
1184 while(TRUE)
1185 {
1186 const int reducesize = MIN(REDUCESIZE, ROUNDUP(current_length, slocopt.sizex) / slocopt.sizex);
1187 size_t ssizes[3] = { (size_t)reducesize * slocopt.sizex, 1, 1 };
1188 size_t slocal[3] = { slocopt.sizex, 1, 1 };
1189 dt_opencl_set_kernel_arg(devid, gd->kernel_highlights_normalize_reduce_second, 0, sizeof(cl_mem), &reduce_in);
1191 &reduce_out);
1193 &current_length);
1195 sizeof(float) * 4 * slocopt.sizex, NULL);
1197 slocal);
1198 if(err != CL_SUCCESS) goto error;
1199
1200 if(reducesize == 1) break;
1201 current_length = reducesize;
1202 cl_mem swap = reduce_in;
1203 reduce_in = reduce_out;
1204 reduce_out = (swap == normalization_partials) ? normalization_tmp : normalization;
1205 }
1206
1207 normalization_final = reduce_out;
1208 }
1209
1211 (void *)&dev_in);
1213 (void *)&interpolated);
1214 dt_opencl_set_kernel_arg(devid, gd->kernel_highlights_bilinear_and_mask_xtrans, 2, sizeof(cl_mem), (void *)&temp);
1216 (void *)&clips_cl);
1218 (void *)&normalization_final);
1222 (void *)&roi_in->x);
1224 (void *)&roi_in->y);
1226 (void *)&dev_xtrans);
1228 (void *)&lookup_cl);
1230 if(err != CL_SUCCESS) goto error;
1231
1232 dt_opencl_set_kernel_arg(devid, gd->kernel_highlights_box_blur, 0, sizeof(cl_mem), (void *)&temp);
1233 dt_opencl_set_kernel_arg(devid, gd->kernel_highlights_box_blur, 1, sizeof(cl_mem), (void *)&clipping_mask);
1234 dt_opencl_set_kernel_arg(devid, gd->kernel_highlights_box_blur, 2, sizeof(int), (void *)&roi_out->width);
1235 dt_opencl_set_kernel_arg(devid, gd->kernel_highlights_box_blur, 3, sizeof(int), (void *)&roi_out->height);
1237 if(err != CL_SUCCESS) goto error;
1238
1239 const int RGBa = TRUE;
1240 dt_opencl_set_kernel_arg(devid, gd->kernel_interpolate_bilinear, 0, sizeof(cl_mem), (void *)&clipping_mask);
1241 dt_opencl_set_kernel_arg(devid, gd->kernel_interpolate_bilinear, 1, sizeof(int), (void *)&width);
1242 dt_opencl_set_kernel_arg(devid, gd->kernel_interpolate_bilinear, 2, sizeof(int), (void *)&height);
1243 dt_opencl_set_kernel_arg(devid, gd->kernel_interpolate_bilinear, 3, sizeof(cl_mem), (void *)&ds_clipping_mask);
1244 dt_opencl_set_kernel_arg(devid, gd->kernel_interpolate_bilinear, 4, sizeof(int), (void *)&ds_width);
1245 dt_opencl_set_kernel_arg(devid, gd->kernel_interpolate_bilinear, 5, sizeof(int), (void *)&ds_height);
1246 dt_opencl_set_kernel_arg(devid, gd->kernel_interpolate_bilinear, 6, sizeof(int), (void *)&RGBa);
1247 err = dt_opencl_enqueue_kernel_2d(devid, gd->kernel_interpolate_bilinear, ds_sizes);
1248 if(err != CL_SUCCESS) goto error;
1249
1250 dt_opencl_set_kernel_arg(devid, gd->kernel_interpolate_bilinear, 0, sizeof(cl_mem), (void *)&interpolated);
1251 dt_opencl_set_kernel_arg(devid, gd->kernel_interpolate_bilinear, 1, sizeof(int), (void *)&width);
1252 dt_opencl_set_kernel_arg(devid, gd->kernel_interpolate_bilinear, 2, sizeof(int), (void *)&height);
1253 dt_opencl_set_kernel_arg(devid, gd->kernel_interpolate_bilinear, 3, sizeof(cl_mem), (void *)&ds_interpolated);
1254 dt_opencl_set_kernel_arg(devid, gd->kernel_interpolate_bilinear, 4, sizeof(int), (void *)&ds_width);
1255 dt_opencl_set_kernel_arg(devid, gd->kernel_interpolate_bilinear, 5, sizeof(int), (void *)&ds_height);
1256 dt_opencl_set_kernel_arg(devid, gd->kernel_interpolate_bilinear, 6, sizeof(int), (void *)&RGBa);
1257 err = dt_opencl_enqueue_kernel_2d(devid, gd->kernel_interpolate_bilinear, ds_sizes);
1258 if(err != CL_SUCCESS) goto error;
1259
1260 for(int i = 0; i < data->iterations; i++)
1261 {
1262 const int salt = (i == data->iterations - 1);
1263 err = wavelets_process_cl(devid, ds_interpolated, temp, reconstructed_scratch, ds_clipping_mask, ds_sizes,
1264 ds_width, ds_height, gd, scales, HF, LF_odd, LF_even, DIFFUSE_RECONSTRUCT_RGB,
1265 noise_level, salt, data->solid_color);
1266 if(err != CL_SUCCESS) goto error;
1267
1268 err = wavelets_process_cl(devid, temp, ds_interpolated, reconstructed_scratch, ds_clipping_mask, ds_sizes,
1269 ds_width, ds_height, gd, scales, HF, LF_odd, LF_even, DIFFUSE_RECONSTRUCT_CHROMA,
1270 noise_level, salt, data->solid_color);
1271 if(err != CL_SUCCESS) goto error;
1272 }
1273
1274 dt_opencl_set_kernel_arg(devid, gd->kernel_interpolate_bilinear, 0, sizeof(cl_mem), (void *)&ds_interpolated);
1275 dt_opencl_set_kernel_arg(devid, gd->kernel_interpolate_bilinear, 1, sizeof(int), (void *)&ds_width);
1276 dt_opencl_set_kernel_arg(devid, gd->kernel_interpolate_bilinear, 2, sizeof(int), (void *)&ds_height);
1277 dt_opencl_set_kernel_arg(devid, gd->kernel_interpolate_bilinear, 3, sizeof(cl_mem), (void *)&interpolated);
1278 dt_opencl_set_kernel_arg(devid, gd->kernel_interpolate_bilinear, 4, sizeof(int), (void *)&width);
1279 dt_opencl_set_kernel_arg(devid, gd->kernel_interpolate_bilinear, 5, sizeof(int), (void *)&height);
1281 if(err != CL_SUCCESS) goto error;
1282
1283 const int clip_floor_off = FALSE; // 2021 mode keeps the historical blend
1285 (void *)&dev_in);
1287 (void *)&dev_in);
1289 (void *)&interpolated);
1291 (void *)&clipping_mask);
1293 (void *)&dev_out);
1295 (void *)&normalization_final);
1297 (void *)&clips_cl);
1299 (void *)&clip_floor_off);
1301 (void *)&width);
1303 (void *)&height);
1305 (void *)&roi_in->x);
1307 (void *)&roi_in->y);
1309 (void *)&dev_xtrans);
1311 if(err != CL_SUCCESS) goto error;
1314 dt_opencl_release_mem_object(dev_xtrans);
1315 dt_opencl_release_mem_object(normalization_partials);
1316 if(normalization_tmp != normalization_final) dt_opencl_release_mem_object(normalization_tmp);
1317 if(normalization != normalization_final) dt_opencl_release_mem_object(normalization);
1318 dt_opencl_release_mem_object(normalization_final);
1319 dt_opencl_release_mem_object(interpolated);
1320 dt_opencl_release_mem_object(clipping_mask);
1325 dt_opencl_release_mem_object(ds_clipping_mask);
1326 dt_opencl_release_mem_object(ds_interpolated);
1327 dt_opencl_release_mem_object(reconstructed_scratch);
1328 return err;
1329
1330error:
1333 dt_opencl_release_mem_object(dev_xtrans);
1334 dt_opencl_release_mem_object(normalization_partials);
1335 if(normalization_tmp != normalization_final) dt_opencl_release_mem_object(normalization_tmp);
1336 if(normalization != normalization_final) dt_opencl_release_mem_object(normalization);
1337 dt_opencl_release_mem_object(normalization_final);
1338 dt_opencl_release_mem_object(interpolated);
1339 dt_opencl_release_mem_object(clipping_mask);
1344 dt_opencl_release_mem_object(ds_clipping_mask);
1345 dt_opencl_release_mem_object(ds_interpolated);
1346 dt_opencl_release_mem_object(reconstructed_scratch);
1347
1348 dt_print(DT_DEBUG_OPENCL, "[opencl_highlights] couldn't enqueue kernel! %i\n", err);
1349 return err;
1350}
1351#endif // HAVE_OPENCL
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 MAX_NUM_SCALES
Definition atrous.c:90
int width
Definition bilateral.h:1
int height
Definition bilateral.h:1
int dt_box_mean(float *const buf, const size_t height, const size_t width, const int ch, const int radius, const unsigned iterations)
#define B_SPLINE_SIGMA
Definition bspline.h:34
static float equivalent_sigma_at_step(const float sigma, const unsigned int s)
Definition bspline.h:48
#define B_SPLINE_TO_LAPLACIAN
Definition bspline.h:45
static void decompose_2D_Bspline(const float *const restrict in, float *const restrict HF, float *const restrict LF, const size_t width, const size_t height, const int mult, float *const tempbuf, size_t padded_size)
Definition bspline.h:347
return vector dt_simd_set1(valid ?(scaling+NORM_MIN) :NORM_MIN)
static float lookup(read_only image2d_t lut, const float x)
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)))
static const int row
typedef void((*dt_cache_allocate_t)(void *userdata, dt_cache_entry_t *entry))
void dt_print(dt_debug_thread_t thread, const char *msg,...)
Definition darktable.c:1600
static const dt_aligned_pixel_simd_t const dt_aligned_pixel_simd_t row1
Definition darktable.h:645
@ DT_DEBUG_OPENCL
Definition darktable.h:744
#define DT_ALIGNED_ARRAY
Definition darktable.h:400
#define DT_IS_ALIGNED(x)
Definition darktable.h:383
#define for_each_channel(_var,...)
Definition darktable.h:684
float dt_aligned_pixel_simd_t __attribute__((vector_size(16), aligned(16)))
Enable aggressive floating-point arithmetic optimizations, in denormals handling. Set through user pr...
Definition darktable.h:546
#define dt_pixelpipe_cache_free_align(mem)
Definition darktable.h:475
#define dt_pixelpipe_cache_alloc_align_float(pixels, pipe)
Definition darktable.h:454
#define __DT_CLONE_TARGETS__
Definition darktable.h:379
static const dt_aligned_pixel_simd_t const dt_aligned_pixel_simd_t const dt_aligned_pixel_simd_t row2
Definition darktable.h:646
#define for_four_channels(_var,...)
Definition darktable.h:686
#define __OMP_PARALLEL_FOR__(...)
Definition darktable.h:270
#define dt_pixelpipe_cache_alloc_perthread_float(n, padded_size)
Definition darktable.h:1092
static const dt_aligned_pixel_simd_t row0
Definition darktable.h:644
#define IS_NULL_PTR(p)
C is way too permissive with !=, == and if(var) checks, which can mean too many things depending on w...
Definition darktable.h:293
#define BLUE
#define RED
#define GREEN
#define ALPHA
static float4 dt_noise_generator_simd(const dt_noise_distribution_t distribution, const float4 mu, const float4 param, uint state[4])
static unsigned int splitmix32(const unsigned long seed)
static float xoshiro128plus(uint state[4])
#define REDUCESIZE
Definition demosaic.c:102
static int dwt_interleave_rows(const int rowid, const int height, const int stride)
Definition dwt.h:93
static __DT_CLONE_TARGETS__ void interpolate_bilinear(const float *const restrict in, const size_t width_in, const size_t height_in, float *const restrict out, const size_t width_out, const size_t height_out, const size_t ch)
__DT_CLONE_TARGETS__ void _compute_laplacian_normalization(const float *const restrict input, const dt_iop_roi_t *const roi_in, const uint32_t filters, const uint8_t(*const xtrans)[6], dt_aligned_pixel_t normalization)
Definition gather.c:222
__DT_CLONE_TARGETS__ void _remosaic_and_replace_xtrans(const float *const restrict input, const float *const restrict input_raw, const float *const restrict interpolated, const float *const restrict clipping_mask, float *const restrict output, const dt_aligned_pixel_t white_balance, const dt_aligned_pixel_t clips, const int clip_is_floor, const dt_iop_roi_t *const roi_in, const uint8_t(*const xtrans)[6], const size_t width, const size_t height)
Definition gather.c:436
__DT_CLONE_TARGETS__ void _interpolate_and_mask(const float *const restrict input, float *const restrict interpolated, float *const restrict clipping_mask, const dt_aligned_pixel_t clips_in, const dt_aligned_pixel_t det_scale, const dt_aligned_pixel_t white_balance, const uint32_t filters, const size_t width, const size_t height)
Definition gather.c:66
__DT_CLONE_TARGETS__ void _remosaic_and_replace(const float *const restrict input, const float *const restrict input_raw, const float *const restrict interpolated, const float *const restrict clipping_mask, float *const restrict output, const dt_aligned_pixel_t white_balance, const dt_aligned_pixel_t clips, const int clip_is_floor, const uint32_t filters, const size_t width, const size_t height)
Definition gather.c:405
__DT_CLONE_TARGETS__ void _interpolate_and_mask_xtrans(const float *const restrict input, float *const restrict interpolated, float *const restrict clipping_mask, const dt_aligned_pixel_t clips, const dt_aligned_pixel_t white_balance, const dt_iop_roi_t *const roi_in, const int32_t lookup[6][6][32], const uint8_t(*const xtrans)[6], const size_t width, const size_t height)
Definition gather.c:298
__DT_CLONE_TARGETS__ void _build_xtrans_bilinear_lookup(int32_t lookup[6][6][32], const dt_iop_roi_t *const roi_in, const uint8_t(*const xtrans)[6])
Definition gather.c:258
uint32_t dt_dev_get_roi_filters(const dt_dev_pixelpipe_iop_t *const piece, const dt_iop_roi_t *const roi_in)
Definition imageop.c:136
float dt_dev_get_module_scale(const dt_dev_pixelpipe_t *const pipe, const dt_iop_roi_t *const roi_in)
Definition imageop.c:131
void *const ovoid
__DT_CLONE_TARGETS__ int process_laplacian_bayer(struct dt_iop_module_t *self, const dt_dev_pixelpipe_t *pipe, const dt_dev_pixelpipe_iop_t *piece, const void *const restrict ivoid, void *const restrict ovoid, const dt_iop_roi_t *const roi_in, const dt_iop_roi_t *const roi_out, const dt_aligned_pixel_t clips)
Definition laplacian.c:426
static __DT_CLONE_TARGETS__ void heat_PDE_diffusion(const float *const restrict high_freq, const float *const restrict low_freq, const float *const restrict clipping_mask, float *const restrict output, const size_t width, const size_t height, const int mult, const uint8_t scale, const float first_order_factor)
Definition laplacian.c:241
static int wavelets_process(const float *const restrict in, float *const restrict reconstructed, const float *const restrict clipping_mask, const size_t width, const size_t height, const int scales, float *const restrict HF, float *const restrict LF_odd, float *const restrict LF_even, const diffuse_reconstruct_variant_t variant, const float noise_level, const int salt, const float first_order_factor)
Definition laplacian.c:367
cl_int process_laplacian_bayer_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 dt_aligned_pixel_t clips)
Definition laplacian.c:806
cl_int process_laplacian_xtrans_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 dt_aligned_pixel_t clips)
Definition laplacian.c:1073
__DT_CLONE_TARGETS__ int process_laplacian_xtrans(struct dt_iop_module_t *self, const dt_dev_pixelpipe_t *pipe, const dt_dev_pixelpipe_iop_t *piece, const void *const restrict ivoid, void *const restrict ovoid, const dt_iop_roi_t *const roi_in, const dt_iop_roi_t *const roi_out, const dt_aligned_pixel_t clips)
Definition laplacian.c:527
static __DT_CLONE_TARGETS__ void guide_laplacians(const float *const restrict high_freq, const float *const restrict low_freq, const float *const restrict clipping_mask, float *const restrict output, const size_t width, const size_t height, const int mult, const float noise_level, const int salt, const uint8_t scale, const float radius_sq)
Definition laplacian.c:78
static cl_int wavelets_process_cl(const int devid, cl_mem in, cl_mem reconstructed, cl_mem reconstructed_scratch, cl_mem clipping_mask, const size_t sizes[3], const int width, const int height, dt_iop_highlights_global_data_t *const gd, const int scales, cl_mem HF, cl_mem LF_odd, cl_mem LF_even, const diffuse_reconstruct_variant_t variant, const float noise_level, const int salt, const float solid_color)
Definition laplacian.c:620
static void swap(float *x, float *y)
Definition lightroom.c:1022
float *const restrict const size_t k
size_t size
Definition mipmap_cache.c:3
float dt_aligned_pixel_t[4]
int dt_opencl_local_buffer_opt(const int devid, const int kernel, dt_opencl_local_buffer_t *factors)
Definition opencl.c:3286
int dt_opencl_enqueue_kernel_2d(const int dev, const int kernel, const size_t *sizes)
Definition opencl.c:2164
void * dt_opencl_alloc_device_buffer(const int devid, const size_t size)
Definition opencl.c:2580
void * dt_opencl_alloc_device(const int devid, const int width, const int height, const int bpp)
Definition opencl.c:2504
void * dt_opencl_copy_host_to_device_constant(const int devid, const size_t size, void *host)
Definition opencl.c:2360
int dt_opencl_set_kernel_arg(const int dev, const int kernel, const int num, const size_t size, const void *arg)
Definition opencl.c:2155
int dt_opencl_enqueue_kernel_2d_with_local(const int dev, const int kernel, const size_t *sizes, const size_t *local)
Definition opencl.c:2170
void dt_opencl_release_mem_object(cl_mem mem)
Definition opencl.c:2415
#define DT_OPENCL_DEFAULT_ERROR
Definition opencl.h:57
#define ROUNDUP(a, n)
Definition opencl.h:78
#define ROUNDUPDHT(a, b)
Definition opencl.h:82
#define ROUNDUPDWD(a, b)
Definition opencl.h:81
#define eps
Definition rcd.c:81
const float uint32_t state[4]
const float sigma
const float const int flip
const float noise
diffuse_reconstruct_variant_t
@ DIFFUSE_RECONSTRUCT_RGB
@ DIFFUSE_RECONSTRUCT_CHROMA
#define DS_FACTOR
dt_iop_buffer_dsc_t dsc_in
struct dt_iop_module_t *void * data
uint32_t filters
Definition format.h:60
uint8_t xtrans[6][6]
Definition format.h:70
dt_atrous_wavelets_scales_t scales
dt_iop_global_data_t * global_data
Definition imageop.h:351
Region of interest passed through the pixelpipe.
Definition imageop.h:72
#define MIN(a, b)
Definition thinplate.c:32
#define MAX(a, b)
Definition thinplate.c:29